From 26787bd75315d199cbcd3e38e200b08592caab82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Go=C5=82=C4=99biowski?= Date: Thu, 12 Dec 2024 11:28:32 +0100 Subject: [PATCH 01/19] Autocomplete --- src/Cody.Core/Agent/IAgentService.cs | 18 +- src/Cody.Core/Agent/NotificationHandlers.cs | 6 +- .../Agent/Protocol/AutocompleteItem.cs | 11 + .../Agent/Protocol/AutocompleteParams.cs | 23 ++ .../Agent/Protocol/AutocompleteResult.cs | 12 + .../Protocol/CompletionBookkeepingEvent.cs | 9 + .../Agent/Protocol/CompletionItemInfo.cs | 14 + .../Agent/Protocol/CompletionItemParams.cs | 9 + .../Agent/Protocol/GetDocumentsParams.cs | 10 + .../Agent/Protocol/GetDocumentsResult.cs | 10 + .../Agent/Protocol/SelectedCompletionInfo.cs | 12 + src/Cody.Core/Cody.Core.csproj | 10 + src/Cody.Core/Common/StringExtensions.cs | 23 ++ .../DocumentSync/DocumentSyncCallback.cs | 22 +- .../Settings/IUserSettingsService.cs | 1 + src/Cody.Core/Settings/UserSettingsService.cs | 10 + src/Cody.Core/Trace/FileTraceListener.cs | 3 +- src/Cody.Core/Trace/LogioTraceListener.cs | 3 +- .../Options/GeneralOptionsControl.xaml | 12 +- .../ViewModels/GeneralOptionsViewModel.cs | 17 +- src/Cody.VisualStudio/Client/AgentClient.cs | 6 +- src/Cody.VisualStudio/Client/TraceJsonRpc.cs | 24 ++ .../Cody.VisualStudio.csproj | 9 + src/Cody.VisualStudio/CodyPackage.cs | 5 +- .../Completions/CodyProposalCollection.cs | 13 + .../Completions/CodyProposalManager.cs | 38 +++ .../CodyProposalManagerProvider.cs | 27 ++ .../Completions/CodyProposalSource.cs | 261 ++++++++++++++++++ .../Completions/CodyProposalSourceProvider.cs | 118 ++++++++ .../Options/GeneralOptionsPage.cs | 14 +- .../Services/DocumentsSyncService.cs | 18 +- .../source.extension.vsixmanifest | 3 +- 32 files changed, 734 insertions(+), 37 deletions(-) create mode 100644 src/Cody.Core/Agent/Protocol/AutocompleteItem.cs create mode 100644 src/Cody.Core/Agent/Protocol/AutocompleteParams.cs create mode 100644 src/Cody.Core/Agent/Protocol/AutocompleteResult.cs create mode 100644 src/Cody.Core/Agent/Protocol/CompletionBookkeepingEvent.cs create mode 100644 src/Cody.Core/Agent/Protocol/CompletionItemInfo.cs create mode 100644 src/Cody.Core/Agent/Protocol/CompletionItemParams.cs create mode 100644 src/Cody.Core/Agent/Protocol/GetDocumentsParams.cs create mode 100644 src/Cody.Core/Agent/Protocol/GetDocumentsResult.cs create mode 100644 src/Cody.Core/Agent/Protocol/SelectedCompletionInfo.cs create mode 100644 src/Cody.Core/Common/StringExtensions.cs create mode 100644 src/Cody.VisualStudio/Client/TraceJsonRpc.cs create mode 100644 src/Cody.VisualStudio/Completions/CodyProposalCollection.cs create mode 100644 src/Cody.VisualStudio/Completions/CodyProposalManager.cs create mode 100644 src/Cody.VisualStudio/Completions/CodyProposalManagerProvider.cs create mode 100644 src/Cody.VisualStudio/Completions/CodyProposalSource.cs create mode 100644 src/Cody.VisualStudio/Completions/CodyProposalSourceProvider.cs diff --git a/src/Cody.Core/Agent/IAgentService.cs b/src/Cody.Core/Agent/IAgentService.cs index 717cb74a..eaf410cb 100644 --- a/src/Cody.Core/Agent/IAgentService.cs +++ b/src/Cody.Core/Agent/IAgentService.cs @@ -1,6 +1,7 @@ using Cody.Core.Agent.Protocol; -using System.Collections.Generic; using System; +using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; namespace Cody.Core.Agent @@ -61,6 +62,21 @@ public interface IAgentService [AgentCall("progress/cancel")] void CancelProgress(string id); + [AgentCall("autocomplete/execute")] + Task Autocomplete(AutocompleteParams autocomplete, CancellationToken cancellationToken); + + [AgentCall("autocomplete/clearLastCandidate")] + void ClearLastCandidate(); + + [AgentCall("autocomplete/completionSuggested")] + void CompletionSuggested(CompletionItemParams completionItem); + + [AgentCall("autocomplete/completionAccepted")] + void CompletionAccepted(CompletionItemParams completionItem); + + [AgentCall("testing/workspaceDocuments")] + Task GetWorkspaceDocuments(GetDocumentsParams documents); + //--------------------------------------------------------- // For notifications return type MUST be void! //--------------------------------------------------------- diff --git a/src/Cody.Core/Agent/NotificationHandlers.cs b/src/Cody.Core/Agent/NotificationHandlers.cs index 392c678d..9b449eb3 100644 --- a/src/Cody.Core/Agent/NotificationHandlers.cs +++ b/src/Cody.Core/Agent/NotificationHandlers.cs @@ -2,6 +2,7 @@ using Cody.Core.Infrastructure; using Cody.Core.Logging; using Cody.Core.Settings; +using Cody.Core.Trace; using Cody.Core.Workspace; using Newtonsoft.Json.Linq; using System; @@ -11,6 +12,8 @@ namespace Cody.Core.Agent { public class NotificationHandlers : INotificationHandler { + private static TraceLogger trace = new TraceLogger(nameof(NotificationHandlers)); + private readonly WebviewMessageHandler _messageFilter; private readonly IUserSettingsService _settingsService; private readonly IFileService _fileService; @@ -57,9 +60,10 @@ await agentClient.ReceiveMessageStringEncoded(new ReceiveMessageStringEncodedPar } [AgentCallback("debug/message")] - public void Debug(string channel, string message) + public void Debug(string channel, string message, string level) { _logger.Debug($"[{channel} {message}]"); + trace.TraceEvent("AgentDebug", message); } [AgentCallback("webview/registerWebview")] diff --git a/src/Cody.Core/Agent/Protocol/AutocompleteItem.cs b/src/Cody.Core/Agent/Protocol/AutocompleteItem.cs new file mode 100644 index 00000000..77785dce --- /dev/null +++ b/src/Cody.Core/Agent/Protocol/AutocompleteItem.cs @@ -0,0 +1,11 @@ +using System; + +namespace Cody.Core.Agent.Protocol +{ + public class AutocompleteItem + { + public string Id { get; set; } + public string InsertText { get; set; } + public Range Range { get; set; } + } +} diff --git a/src/Cody.Core/Agent/Protocol/AutocompleteParams.cs b/src/Cody.Core/Agent/Protocol/AutocompleteParams.cs new file mode 100644 index 00000000..c3d343f2 --- /dev/null +++ b/src/Cody.Core/Agent/Protocol/AutocompleteParams.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; + +namespace Cody.Core.Agent.Protocol +{ + public class AutocompleteParams + { + public string Uri { get; set; } + public string FilePath { get; set; } + + public Position Position { get; set; } + + public TriggerKind? TriggerKind { get; set; } + + public SelectedCompletionInfo SelectedCompletionInfo { get; set; } + } + + public enum TriggerKind + { + Automatic, + Invoke + } +} diff --git a/src/Cody.Core/Agent/Protocol/AutocompleteResult.cs b/src/Cody.Core/Agent/Protocol/AutocompleteResult.cs new file mode 100644 index 00000000..9e13305b --- /dev/null +++ b/src/Cody.Core/Agent/Protocol/AutocompleteResult.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; + +namespace Cody.Core.Agent.Protocol +{ + public class AutocompleteResult + { + public AutocompleteItem[] Items { get; set; } + + public CompletionBookkeepingEvent CompletionEvent { get; set; } + } +} diff --git a/src/Cody.Core/Agent/Protocol/CompletionBookkeepingEvent.cs b/src/Cody.Core/Agent/Protocol/CompletionBookkeepingEvent.cs new file mode 100644 index 00000000..0bedbbe3 --- /dev/null +++ b/src/Cody.Core/Agent/Protocol/CompletionBookkeepingEvent.cs @@ -0,0 +1,9 @@ +using System; + +namespace Cody.Core.Agent.Protocol +{ + public class CompletionBookkeepingEvent + { + public CompletionItemInfo[] Items { get; set; } + } +} diff --git a/src/Cody.Core/Agent/Protocol/CompletionItemInfo.cs b/src/Cody.Core/Agent/Protocol/CompletionItemInfo.cs new file mode 100644 index 00000000..7080b29d --- /dev/null +++ b/src/Cody.Core/Agent/Protocol/CompletionItemInfo.cs @@ -0,0 +1,14 @@ +using System; + +namespace Cody.Core.Agent.Protocol +{ + public class CompletionItemInfo + { + public int LineCount { get; set; } + public int CharCount { get; set; } + + public string InsertText { get; set; } + + public string StopReason { get; set; } + } +} diff --git a/src/Cody.Core/Agent/Protocol/CompletionItemParams.cs b/src/Cody.Core/Agent/Protocol/CompletionItemParams.cs new file mode 100644 index 00000000..f57fcd4a --- /dev/null +++ b/src/Cody.Core/Agent/Protocol/CompletionItemParams.cs @@ -0,0 +1,9 @@ +using System; + +namespace Cody.Core.Agent.Protocol +{ + public class CompletionItemParams + { + public string CompletionID { get; set; } + } +} diff --git a/src/Cody.Core/Agent/Protocol/GetDocumentsParams.cs b/src/Cody.Core/Agent/Protocol/GetDocumentsParams.cs new file mode 100644 index 00000000..d289e4ae --- /dev/null +++ b/src/Cody.Core/Agent/Protocol/GetDocumentsParams.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; + +namespace Cody.Core.Agent.Protocol +{ + public class GetDocumentsParams + { + public string[] Uris { get; set; } + } +} diff --git a/src/Cody.Core/Agent/Protocol/GetDocumentsResult.cs b/src/Cody.Core/Agent/Protocol/GetDocumentsResult.cs new file mode 100644 index 00000000..291831f4 --- /dev/null +++ b/src/Cody.Core/Agent/Protocol/GetDocumentsResult.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; + +namespace Cody.Core.Agent.Protocol +{ + public class GetDocumentsResult + { + public ProtocolTextDocument[] Documents { get; set; } + } +} diff --git a/src/Cody.Core/Agent/Protocol/SelectedCompletionInfo.cs b/src/Cody.Core/Agent/Protocol/SelectedCompletionInfo.cs new file mode 100644 index 00000000..39890864 --- /dev/null +++ b/src/Cody.Core/Agent/Protocol/SelectedCompletionInfo.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; + +namespace Cody.Core.Agent.Protocol +{ + public class SelectedCompletionInfo + { + public Range Range { get; set; } + + public string Text { get; set; } + } +} diff --git a/src/Cody.Core/Cody.Core.csproj b/src/Cody.Core/Cody.Core.csproj index bf20ab8b..4298746b 100644 --- a/src/Cody.Core/Cody.Core.csproj +++ b/src/Cody.Core/Cody.Core.csproj @@ -54,11 +54,20 @@ + + + + + + + + + @@ -81,6 +90,7 @@ + diff --git a/src/Cody.Core/Common/StringExtensions.cs b/src/Cody.Core/Common/StringExtensions.cs new file mode 100644 index 00000000..64dd8efe --- /dev/null +++ b/src/Cody.Core/Common/StringExtensions.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace Cody.Core.Common +{ + public static class StringExtensions + { + public static string ToUri(this string path) + { + var uri = new Uri(path).AbsoluteUri; + return Regex.Replace(uri, "(file:///)(\\D+)(:)", m => m.Groups[1].Value + m.Groups[2].Value.ToLower() + "%3A"); + } + + public static string ConvertLineBreaks(this string text, string lineBrakeChars) + { + return Regex.Replace(text, @"\r\n?|\n", lineBrakeChars); + } + } +} diff --git a/src/Cody.Core/DocumentSync/DocumentSyncCallback.cs b/src/Cody.Core/DocumentSync/DocumentSyncCallback.cs index 7f6ed6fc..4f613ddd 100644 --- a/src/Cody.Core/DocumentSync/DocumentSyncCallback.cs +++ b/src/Cody.Core/DocumentSync/DocumentSyncCallback.cs @@ -1,11 +1,11 @@ using Cody.Core.Agent; using Cody.Core.Agent.Protocol; +using Cody.Core.Common; using Cody.Core.Logging; using Cody.Core.Trace; using System; using System.Collections.Generic; using System.Linq; -using System.Text.RegularExpressions; namespace Cody.Core.DocumentSync { @@ -22,12 +22,6 @@ public DocumentSyncCallback(IAgentService agentService, ILog logger) this.logger = logger; } - private string ToUri(string path) - { - var uri = new Uri(path).AbsoluteUri; - return Regex.Replace(uri, "(file:///)(\\D+)(:)", m => m.Groups[1].Value + m.Groups[2].Value.ToLower() + "%3A"); - } - public void OnChanged(string fullPath, DocumentRange visibleRange, DocumentRange selection, IEnumerable changes) { trace.TraceEvent("DidChange", "ch: '{0}', sel:{1}, vr:{2}, path:{3}", string.Join("", changes), selection, visibleRange, fullPath); @@ -52,7 +46,7 @@ public void OnChanged(string fullPath, DocumentRange visibleRange, DocumentRange var docState = new ProtocolTextDocument { - Uri = ToUri(fullPath), + Uri = fullPath.ToUri(), VisibleRange = vRange, Selection = new Range { @@ -69,7 +63,7 @@ public void OnChanged(string fullPath, DocumentRange visibleRange, DocumentRange }, ContentChanges = changes.Select(x => new ProtocolTextDocumentContentChangeEvent { - Text = x.Text, + Text = x.Text.ConvertLineBreaks("\n"), Range = new Range { Start = new Position @@ -95,7 +89,7 @@ public void OnClosed(string fullPath) var docState = new ProtocolTextDocument { - Uri = ToUri(fullPath), + Uri = fullPath.ToUri(), }; // Only the 'uri' property is required, other properties are ignored. @@ -105,7 +99,7 @@ public void OnClosed(string fullPath) public void OnFocus(string fullPath) { trace.TraceEvent("DidFocus", "{0}", fullPath); - agentService.DidFocus(new CodyFilePath { Uri = ToUri(fullPath) }); + agentService.DidFocus(new CodyFilePath { Uri = fullPath.ToUri() }); } @@ -133,8 +127,8 @@ public void OnOpened(string fullPath, string content, DocumentRange visibleRange var docState = new ProtocolTextDocument { - Uri = ToUri(fullPath), - Content = content, + Uri = fullPath.ToUri(), + Content = content.ConvertLineBreaks("\n"), VisibleRange = vRange, Selection = new Range { @@ -157,7 +151,7 @@ public void OnOpened(string fullPath, string content, DocumentRange visibleRange public void OnSaved(string fullPath) { trace.TraceEvent("DidSave", "{0}", fullPath); - agentService.DidSave(new CodyFilePath { Uri = ToUri(fullPath) }); + agentService.DidSave(new CodyFilePath { Uri = fullPath.ToUri() }); } } } diff --git a/src/Cody.Core/Settings/IUserSettingsService.cs b/src/Cody.Core/Settings/IUserSettingsService.cs index f629682b..ad8125ae 100644 --- a/src/Cody.Core/Settings/IUserSettingsService.cs +++ b/src/Cody.Core/Settings/IUserSettingsService.cs @@ -10,6 +10,7 @@ public interface IUserSettingsService string ServerEndpoint { get; set; } string CustomConfiguration { get; set; } bool AcceptNonTrustedCert { get; set; } + bool AutomaticallyTriggerCompletions { get; set; } event EventHandler AuthorizationDetailsChanged; diff --git a/src/Cody.Core/Settings/UserSettingsService.cs b/src/Cody.Core/Settings/UserSettingsService.cs index 09d2b136..8d0e1c33 100644 --- a/src/Cody.Core/Settings/UserSettingsService.cs +++ b/src/Cody.Core/Settings/UserSettingsService.cs @@ -110,5 +110,15 @@ public bool AcceptNonTrustedCert } set => Set(nameof(AcceptNonTrustedCert), value.ToString()); } + + public bool AutomaticallyTriggerCompletions + { + get + { + var value = GetOrDefault(nameof(AutomaticallyTriggerCompletions), true.ToString()); + return bool.Parse(value); + } + set => Set(nameof(AutomaticallyTriggerCompletions), value.ToString()); + } } } diff --git a/src/Cody.Core/Trace/FileTraceListener.cs b/src/Cody.Core/Trace/FileTraceListener.cs index 506fcf3f..f5b68acf 100644 --- a/src/Cody.Core/Trace/FileTraceListener.cs +++ b/src/Cody.Core/Trace/FileTraceListener.cs @@ -34,7 +34,8 @@ protected string FormatTraceEvent(TraceEvent traceEvent) if (!string.IsNullOrEmpty(traceEvent.Message)) { - sb.AppendFormat(traceEvent.Message, traceEvent.MessageArgs); + if (traceEvent.MessageArgs != null) sb.AppendFormat(traceEvent.Message, traceEvent.MessageArgs); + else sb.Append(traceEvent.Message); } if (traceEvent.Data != null) diff --git a/src/Cody.Core/Trace/LogioTraceListener.cs b/src/Cody.Core/Trace/LogioTraceListener.cs index fe6c303f..8f0e106b 100644 --- a/src/Cody.Core/Trace/LogioTraceListener.cs +++ b/src/Cody.Core/Trace/LogioTraceListener.cs @@ -49,7 +49,8 @@ protected override void Write(TraceEvent traceEvent) if (!string.IsNullOrEmpty(traceEvent.Message)) { sb.Append(" "); - sb.AppendFormat(traceEvent.Message, traceEvent.MessageArgs); + if (traceEvent.MessageArgs != null) sb.AppendFormat(traceEvent.Message, traceEvent.MessageArgs); + else sb.Append(traceEvent.Message); } if (traceEvent.Data != null) diff --git a/src/Cody.UI/Controls/Options/GeneralOptionsControl.xaml b/src/Cody.UI/Controls/Options/GeneralOptionsControl.xaml index 0f540316..91fc4fe7 100644 --- a/src/Cody.UI/Controls/Options/GeneralOptionsControl.xaml +++ b/src/Cody.UI/Controls/Options/GeneralOptionsControl.xaml @@ -17,6 +17,7 @@ + @@ -76,10 +77,19 @@ Content="Accept non-trusted certificates (requires restart)" /> + +