diff --git a/src/OneWare.Core/Views/DockViews/EditView.axaml.cs b/src/OneWare.Core/Views/DockViews/EditView.axaml.cs index f38da4d2..6e266cc5 100644 --- a/src/OneWare.Core/Views/DockViews/EditView.axaml.cs +++ b/src/OneWare.Core/Views/DockViews/EditView.axaml.cs @@ -103,9 +103,10 @@ private void Setup() { var completion = new CompletionWindow(CodeBox) { - CloseWhenCaretAtBeginning = false, + CloseWhenCaretAtBeginning = true, AdditionalOffset = new Vector(0, 3), - MaxHeight = 225 + MaxHeight = 225, + CloseAutomatically = true, }; _typeAssistance?.Attach(completion); } diff --git a/src/OneWare.Cpp/TypeAssistanceCpp.cs b/src/OneWare.Cpp/TypeAssistanceCpp.cs index b86639a0..62ce1468 100644 --- a/src/OneWare.Cpp/TypeAssistanceCpp.cs +++ b/src/OneWare.Cpp/TypeAssistanceCpp.cs @@ -60,7 +60,7 @@ protected override ICompletionData ConvertCompletionItem(CompletionItem comp, in void AfterComplete() { - _ = ShowOverloadProviderAsync(); + _ = ShowSignatureHelpAsync(); } var description = comp.Documentation != null ? (comp.Documentation.MarkupContent != null ? comp.Documentation.MarkupContent.Value : comp.Documentation.String) : null; @@ -69,10 +69,5 @@ void AfterComplete() return new CompletionData(comp.InsertText ?? "", newLabel, description, icon, 0, comp, offset, AfterComplete); } - - protected override bool CharAtNormalCompletion(char c) - { - return char.IsLetterOrDigit(c) || c is '_' || c is ':'; - } } } \ No newline at end of file diff --git a/src/OneWare.SDK/EditorExtensions/CompletionData.cs b/src/OneWare.SDK/EditorExtensions/CompletionData.cs index b637e500..429be611 100644 --- a/src/OneWare.SDK/EditorExtensions/CompletionData.cs +++ b/src/OneWare.SDK/EditorExtensions/CompletionData.cs @@ -13,7 +13,7 @@ public class CompletionData : ICompletionData private Action? AfterCompletion { get; } - private int CompletionOffset { get; } + public int CompletionOffset { get; } public IImage? Image { get; } @@ -55,6 +55,8 @@ public void Complete(TextArea textArea, ISegment completionSegment, EventArgs in var caretStop = new Regex(@"\$."); var placeHolder = new Regex(@"\${.*?}"); + + Console.WriteLine(CompletionOffset + " " + completionSegment.Offset + completionSegment.EndOffset); var newLine = TextUtilities.GetNewLineFromDocument(textArea.Document, segmentLine.LineNumber); diff --git a/src/OneWare.SDK/LanguageService/ILanguageService.cs b/src/OneWare.SDK/LanguageService/ILanguageService.cs index d479f169..9ca40aa3 100644 --- a/src/OneWare.SDK/LanguageService/ILanguageService.cs +++ b/src/OneWare.SDK/LanguageService/ILanguageService.cs @@ -25,9 +25,13 @@ public interface ILanguageService public void RefreshTextDocument(string fullPath, Container changes); public void RefreshTextDocument(string fullPath, string newText); + + public IEnumerable GetSignatureHelpTriggerChars(); + public IEnumerable GetSignatureHelpRetriggerChars(); + public IEnumerable GetCompletionTriggerChars(); + public IEnumerable GetCompletionCommitChars(); - public Task RequestCompletionAsync(string fullPath, Position pos, string triggerChar, - CompletionTriggerKind triggerKind); + public Task RequestCompletionAsync(string fullPath, Position pos, CompletionTriggerKind triggerKind, string? triggerChar); public Task ResolveCompletionItemAsync(CompletionItem completionItem); public Task PrepareRenameAsync(string fullPath, Position pos); public Task RequestRenameAsync(string fullPath, Position pos, string newName); diff --git a/src/OneWare.SDK/LanguageService/LanguageServiceBase.cs b/src/OneWare.SDK/LanguageService/LanguageServiceBase.cs index 52e5f55e..12f7e6bd 100644 --- a/src/OneWare.SDK/LanguageService/LanguageServiceBase.cs +++ b/src/OneWare.SDK/LanguageService/LanguageServiceBase.cs @@ -73,7 +73,27 @@ public virtual void RefreshTextDocument(string fullPath, string newText) } - public virtual Task RequestCompletionAsync(string fullPath, Position pos, string triggerChar, CompletionTriggerKind triggerKind) + public IEnumerable GetSignatureHelpTriggerChars() + { + return []; + } + + public IEnumerable GetSignatureHelpRetriggerChars() + { + return []; + } + + public virtual IEnumerable GetCompletionTriggerChars() + { + return []; + } + + public virtual IEnumerable GetCompletionCommitChars() + { + return []; + } + + public virtual Task RequestCompletionAsync(string fullPath, Position pos, CompletionTriggerKind triggerKind, string? triggerChar) { return Task.FromResult(null); } diff --git a/src/OneWare.SDK/LanguageService/LanguageServiceLsp.cs b/src/OneWare.SDK/LanguageService/LanguageServiceLsp.cs index 5d62d5a8..d2841d8e 100644 --- a/src/OneWare.SDK/LanguageService/LanguageServiceLsp.cs +++ b/src/OneWare.SDK/LanguageService/LanguageServiceLsp.cs @@ -25,12 +25,12 @@ namespace OneWare.SDK.LanguageService public abstract class LanguageServiceLsp : LanguageServiceBase { private readonly Dictionary _tokenRegister = new(); - protected LanguageClient? Client { get; private set; } + private LanguageClient? Client { get; set; } private CancellationTokenSource? _cancellation; private IChildProcess? _process; - protected string? Arguments { get; set; } - protected string? ExecutablePath { get; set; } + private string? Arguments { get; set; } + private string? ExecutablePath { get; set; } protected LanguageServiceLsp(string name, string? executablePath, string? arguments, string? workspace) : base(name, workspace) { @@ -101,9 +101,9 @@ public override async Task ActivateAsync() var reader = new StreamReader(_process.StandardError); _ = Task.Run(() => { - while (_process.HasStandardError) + while (_process.HasStandardError && !reader.EndOfStream && !_cancellation.IsCancellationRequested) { - Console.WriteLine(reader.ReadToEnd()); + Console.WriteLine("ERR:" + reader.ReadToEnd()); } }, _cancellation.Token); @@ -168,16 +168,16 @@ protected async Task InitAsync(Stream input, Stream output, Action ContainerLocator.Container.Resolve()?.Log(x.Message, ConsoleColor.DarkCyan)); options.OnTelemetryEvent(x => { ContainerLocator.Container.Resolve()?.Log(x, ConsoleColor.Magenta); }); - - // options.WithCapability(new SynchronizationCapability() - // { - // DidSave = true, - // WillSave = true, - // WillSaveWaitUntil = true - // }); + + options.WithCapability(new TextSynchronizationCapability() + { + DidSave = true, + WillSave = false, + WillSaveWaitUntil = false + }); options.WithCapability(new HoverCapability { - ContentFormat = new Container(MarkupKind.PlainText) + ContentFormat = new Container(MarkupKind.PlainText, MarkupKind.Markdown) }); options.WithCapability(new PublishDiagnosticsCapability { @@ -191,12 +191,25 @@ protected async Task InitAsync(Stream input, Stream output, Action(MarkupKind.PlainText) }, + { + DocumentationFormat = new Container(MarkupKind.PlainText), + ParameterInformation = new SignatureParameterInformationCapabilityOptions() + { + LabelOffsetSupport = true + }, + ActiveParameterSupport = true + }, ContextSupport = true }); options.WithCapability(new CodeActionCapability @@ -220,17 +233,23 @@ protected async Task InitAsync(Stream input, Stream output, Action(MarkupKind.PlainText), SnippetSupport = true, PreselectSupport = true, + InsertReplaceSupport = true, + InsertTextModeSupport = new CompletionItemInsertTextModeSupportCapabilityOptions() + { + ValueSet = new Container(InsertTextMode.AdjustIndentation, InsertTextMode.AsIs) + }, + ResolveAdditionalTextEditsSupport = true, TagSupport = new Supports { Value = new CompletionItemTagSupportCapabilityOptions { ValueSet = new Container(CompletionItemTag.Deprecated) } - } + }, }, CompletionItemKind = new CompletionItemKindCapabilityOptions { @@ -238,7 +257,8 @@ protected async Task InitAsync(Stream input, Stream output, Action( @@ -263,11 +291,6 @@ protected async Task InitAsync(Stream input, Stream output, Action RequestCompletionAsync(string fullPath, Position pos, string triggerChar, - CompletionTriggerKind triggerKind) + public override async Task RequestCompletionAsync(string fullPath, Position pos, CompletionTriggerKind triggerKind, string? triggerChar) { var cts = new CancellationTokenSource(); cts.CancelAfter(1000); @@ -454,7 +475,7 @@ public override Task ExecuteCommandAsync(Command cmd) Context = new CompletionContext { TriggerKind = triggerKind, - TriggerCharacter = triggerChar + TriggerCharacter = triggerChar, }, Position = pos }, cts.Token); @@ -861,5 +882,19 @@ public override Task ExecuteCommandAsync(Command cmd) return formatting; } + + #region Capability Check + + public override IEnumerable GetCompletionTriggerChars() + { + return Client?.ServerSettings.Capabilities.CompletionProvider?.TriggerCharacters ?? []; + } + + public override IEnumerable GetCompletionCommitChars() + { + return Client?.ServerSettings.Capabilities.CompletionProvider?.AllCommitCharacters ?? []; + } + + #endregion } } \ No newline at end of file diff --git a/src/OneWare.SDK/LanguageService/TypeAssistanceLanguageService.cs b/src/OneWare.SDK/LanguageService/TypeAssistanceLanguageService.cs index bda208a5..57c78ad7 100644 --- a/src/OneWare.SDK/LanguageService/TypeAssistanceLanguageService.cs +++ b/src/OneWare.SDK/LanguageService/TypeAssistanceLanguageService.cs @@ -8,6 +8,7 @@ using AvaloniaEdit; using AvaloniaEdit.CodeCompletion; using AvaloniaEdit.Document; +using AvaloniaEdit.Editing; using CommunityToolkit.Mvvm.Input; using DynamicData; using OmniSharp.Extensions.LanguageServer.Protocol.Models; @@ -30,7 +31,6 @@ namespace OneWare.SDK.LanguageService public abstract class TypeAssistanceLanguageService : TypeAssistanceBase { private bool _completionBusy; - private int _completionOffset; private DispatcherTimer? _dispatcherTimer; private TimeSpan _lastCompletionItemChangedTime = DateTime.Now.TimeOfDay; private TimeSpan _lastCompletionItemResolveTime = DateTime.Now.TimeOfDay; @@ -435,30 +435,31 @@ protected override async Task TextEnteredAsync(TextInputEventArgs args) if (!Service.IsLanguageServiceReady || args.Text == null) return; - var t = args.Text.Length > 0 ? args.Text[0] : ';'; - var b = CodeBox.CaretOffset > 1 ? CodeBox.Text[CodeBox.CaretOffset - 2] : ' '; - //var cLine = CodeBox.Document.GetText(CodeBox.Document.GetLineByOffset(CodeBox.CaretOffset)); - - if (t == '(' || b == '(' || - t == ',') //Function Parameter / Overload insight - if (SettingsService.GetSettingValue("TypeAssistance_EnableAutoCompletion") && - !_completionBusy) + if (SettingsService.GetSettingValue("TypeAssistance_EnableAutoCompletion") && !_completionBusy) + { + var triggerChar = args.Text!; + var beforeTriggerChar = CodeBox.CaretOffset > 1 ? CodeBox.Text[CodeBox.CaretOffset - 2] : ' '; + + if (Service.GetSignatureHelpTriggerChars().Contains(triggerChar)) //Function Parameter / Overload insight { Completion?.Close(); - await ShowOverloadProviderAsync(); + await ShowSignatureHelpAsync(); } - if (SettingsService.GetSettingValue("TypeAssistance_EnableAutoCompletion") && !_completionBusy && - (CharBeforeNormalCompletion(b) && CharAtNormalCompletion(t) || //Normal completion - (CharAtNormalCompletion(b) || b is ')') && t == '.')) //Child insight - { - _completionBusy = true; - _completionOffset = CodeBox.CaretOffset; - if (t == '.') _completionOffset++; - await ShowCompletionAsync(args.Text, t == '.' ? CompletionTriggerKind.Invoked : CompletionTriggerKind.TriggerCharacter); + if (Service.GetCompletionTriggerChars().Contains(triggerChar)) + { + _completionBusy = true; + await ShowCompletionAsync(CompletionTriggerKind.TriggerCharacter, triggerChar); + } + else if (CharBeforeNormalCompletion(beforeTriggerChar) && triggerChar.All(char.IsLetter)) + { + _completionBusy = true; + await ShowCompletionAsync(CompletionTriggerKind.Invoked, triggerChar); + } + _completionBusy = false; } - + await base.TextEnteredAsync(args); } catch (Exception e) @@ -527,7 +528,7 @@ protected virtual async Task UpdateSymbolsAsync() // } } - protected virtual async Task ShowOverloadProviderAsync() + protected virtual async Task ShowSignatureHelpAsync() { var signatureHelp = await Service.RequestSignatureHelpAsync(CurrentFile.FullPath, new Position(CodeBox.TextArea.Caret.Line - 1, CodeBox.TextArea.Caret.Column - 1)); @@ -545,38 +546,55 @@ protected virtual async Task ShowOverloadProviderAsync() } } - protected virtual async Task ShowCompletionAsync(string triggerChar, CompletionTriggerKind triggerKind) + protected virtual async Task ShowCompletionAsync(CompletionTriggerKind triggerKind, string? triggerChar) { - //var t = triggerChar.Length > 0 ? triggerChar[0] : ';'; + Console.WriteLine($"Completion request kind: {triggerKind} char: {triggerChar}"); - var completion = await Service.RequestCompletionAsync(CurrentFile.FullPath, - new Position(CodeBox.TextArea.Caret.Line - 1, CodeBox.TextArea.Caret.Column - 1), triggerChar, - triggerKind); - var custom = await GetCustomCompletionItemsAsync(); + var lspCompletionItems = await Service.RequestCompletionAsync(CurrentFile.FullPath, + new Position(CodeBox.TextArea.Caret.Line - 1, CodeBox.TextArea.Caret.Column - 1), + triggerKind, triggerKind == CompletionTriggerKind.Invoked ? null : triggerChar); + + var customCompletionItems = await GetCustomCompletionItemsAsync(); - if ((completion is not null || custom.Count > 0) && IsOpen && Completion != null) + if ((lspCompletionItems is not null || customCompletionItems.Count > 0) && IsOpen && Completion != null) { - Completion.EndOffset = CodeBox.CaretOffset; - Completion.StartOffset = CodeBox.CaretOffset; - if (triggerKind is CompletionTriggerKind.TriggerCharacter) Completion.StartOffset -= triggerChar.Length; Completion.CompletionList.Reset(); - Completion.CompletionList.CompletionData.AddRange(custom); - if (completion is not null) - Completion.CompletionList.CompletionData.AddRange(ConvertCompletionData(completion)); + Completion.CompletionList.ListBox.ClearSelection(); + Completion.StartOffset = CodeBox.CaretOffset; + Completion.EndOffset = CodeBox.CaretOffset; + + Completion.CompletionList.CompletionData.AddRange(customCompletionItems); + + if (lspCompletionItems is not null) + { + var completionOffset = CodeBox.CaretOffset; + if (triggerKind is CompletionTriggerKind.TriggerCharacter && triggerChar != null) + { + Completion.ExpectInsertionBeforeStart = true; + completionOffset++; + } + else + { + Completion.ExpectInsertionBeforeStart = false; + } + Completion.CompletionList.CompletionData.AddRange(ConvertCompletionData(lspCompletionItems, completionOffset)); + } - //Calculate completionwindow width + //Calculate CompletionWindow width var length = 0; - foreach (var dataprop in Completion.CompletionList.CompletionData) - if (dataprop.Content is string str && str.Length > length) + foreach (var data in Completion.CompletionList.CompletionData) + if (data.Content is string str && str.Length > length) length = str.Length; - var calwidth = length * SettingsService.GetSettingValue("Editor_FontSize") + 50; + var calculatedWith = length * SettingsService.GetSettingValue("Editor_FontSize") + 50; - Completion.Width = calwidth > 400 ? 500 : calwidth; + Completion.Width = calculatedWith > 400 ? 400 : calculatedWith; if (Completion.CompletionList.CompletionData.Count > 0) { - if (triggerChar.Length == 1 && char.IsLetter(triggerChar[0])) - Completion.Show(triggerChar); - else Completion.Show(); + //Show without starting filter after . + if(triggerKind is CompletionTriggerKind.TriggerCharacter) + Completion.Show(); + //Show with filter at normal invocation + else Completion.Show(triggerChar); } } } @@ -614,7 +632,7 @@ public virtual async Task ResolveCompletionAsync() var resolvedCi = await Service.ResolveCompletionItemAsync(completionLsp.CompletionItemLsp); if (resolvedCi != null && IsOpen && Completion.IsOpen) { - var cc = ConvertCompletionItem(resolvedCi, _completionOffset); + var cc = ConvertCompletionItem(resolvedCi, completionLsp.CompletionOffset); var cindex = Completion.CompletionList.CompletionData.IndexOf(completionLsp); if (cindex >= 0) { @@ -630,11 +648,6 @@ protected virtual bool CharBeforeNormalCompletion(char c) return char.IsWhiteSpace(c) || c is ';' or '#' or '(' or ':' or '+' or '-' or '=' or '*' or '/' or '&' or ','; } - protected virtual bool CharAtNormalCompletion(char c) - { - return char.IsLetterOrDigit(c) || c is '_'; - } - public virtual OverloadProvider ConvertOverloadProvider(SignatureHelp signatureHelp) { var overloadOptions = new List<(string, string?)>(); @@ -663,10 +676,10 @@ public virtual OverloadProvider ConvertOverloadProvider(SignatureHelp signatureH return new OverloadProvider(overloadOptions); } - public virtual IEnumerable ConvertCompletionData(CompletionList list) + protected virtual IEnumerable ConvertCompletionData(CompletionList list, int offset) { //Parse completionitem - foreach (var comp in list.Items) yield return ConvertCompletionItem(comp, _completionOffset); + foreach (var comp in list.Items) yield return ConvertCompletionItem(comp, offset); } protected virtual ICompletionData ConvertCompletionItem(CompletionItem comp, int offset) @@ -677,12 +690,12 @@ protected virtual ICompletionData ConvertCompletionItem(CompletionItem comp, int void AfterComplete() { - _ = ShowOverloadProviderAsync(); + _ = ShowSignatureHelpAsync(); } var description = comp.Documentation != null ? (comp.Documentation.MarkupContent != null ? comp.Documentation.MarkupContent.Value : comp.Documentation.String) : null; - return new CompletionData(comp.InsertText ?? "", comp.Label, description, icon, 0, + return new CompletionData(comp.InsertText ?? comp.FilterText ?? "", comp.Label, description, icon, 0, comp, offset, AfterComplete); }