diff --git a/CodeiumVS/CodeLens/CodeLensTag.cs b/CodeiumVS/CodeLens/CodeLensTag.cs deleted file mode 100644 index 2fabfdd..0000000 --- a/CodeiumVS/CodeLens/CodeLensTag.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Microsoft.VisualStudio.Text; -using Microsoft.VisualStudio.Text.Tagging; - -namespace CodeiumVS -{ - - // the SpaceNegotiatingAdornmentTag is used to tell the editor to create an empty space - // they work like a more complicated version of
from html - internal class CodeLensTag : SpaceNegotiatingAdornmentTag - { - public CodeLensTag(double width, double topSpace, double baseline, double textHeight, - double bottomSpace, PositionAffinity affinity, object identityTag, - object providerTag) - : base(width, topSpace, baseline, textHeight, bottomSpace, affinity, identityTag, - providerTag) - { - } - } -} \ No newline at end of file diff --git a/CodeiumVS/CodeLens/CodeLensTagger.cs b/CodeiumVS/CodeLens/CodeLensTagger.cs deleted file mode 100644 index 1537ed3..0000000 --- a/CodeiumVS/CodeLens/CodeLensTagger.cs +++ /dev/null @@ -1,647 +0,0 @@ -using System.Collections.Generic; -using System.Windows.Controls; -using System.Windows.Media; -using System.Windows.Documents; -using System.Diagnostics; -using Microsoft.VisualStudio.Text; -using Microsoft.VisualStudio.Text.Editor; -using Microsoft.VisualStudio.Text.Tagging; -using System.Windows.Media.TextFormatting; -using Microsoft.VisualStudio.Utilities; -using System.ComponentModel.Composition; -using Microsoft.VisualStudio.Text.Projection; -using CodeiumVS.Languages; -using System.IO; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using System.Windows.Input; -using CodeiumVS.Packets; -using CodeiumVS.Utilities; -using System.Windows.Shapes; -using System; -using WebSocketSharp; -using System.Windows.Forms; - -namespace CodeiumVS -{ - - internal sealed class CodeLensTagger : ITagger, IDisposable - { - - /// used to set the colour of the grey text - private Brush greyBrush; - - /// contains the editor text and OnChange triggers on any text changes - ITextBuffer buffer; - - /// current editor display, immutable data - ITextSnapshot snapshot; - - /// the editor display object - IWpfTextView view; - - /// contains the grey text - private IAdornmentLayer adornmentLayer; - - private ITextDocument _document; - private LangInfo _language; - - private CancellationTokenSource? _requestTokenSource; - private CancellationTokenSource currentCancellTokenSource = null; - private CancellationToken currentCancellToken; - - List _functions; - List _classes; - List originalSpans = new List(); - List panels = new List(); - private double lastViewPortTop = 0; - - public CodeLensTagger(IWpfTextView view, ITextBuffer buffer, ITextDocument document) - { - this.buffer = buffer; - this.snapshot = buffer.CurrentSnapshot; - this.view = view; - this.adornmentLayer = view.GetAdornmentLayer("CodeiumCodeLensAdornmentLayer"); - _document = document; - this.greyBrush = new SolidColorBrush(Colors.Gray); - - RefreshLanguage(); - - view.TextBuffer.Changed += BufferChanged; - this.view.LayoutChanged += this.OnSizeChanged; - - if (_document != null) - { - _document.FileActionOccurred += OnFileActionOccurred; - _document.TextBuffer.ContentTypeChanged += OnContentTypeChanged; - } - Task.Run(() => Update()); - } - - private void OnContentTypeChanged(object sender, ContentTypeChangedEventArgs e) - { - RefreshLanguage(); - } - - private void OnFileActionOccurred(object sender, TextDocumentFileActionEventArgs e) - { - RefreshLanguage(); - } - - private void RefreshLanguage() - { - if (_document != null) - { - _language = Mapper.GetLanguage(_document.TextBuffer.ContentType, - System.IO.Path.GetExtension(_document.FilePath)?.Trim('.')); - } - } - - // This an iterator that is used to iterate through all of the test tags - // tags are like html tags they mark places in the view to modify how those sections look - // Testtag is a tag that tells the editor to add empty space - public IEnumerable> GetTags(NormalizedSnapshotSpanCollection spans) - { - ITextSnapshot currentSnapshot; - double height, lineHeight; - try - { - SnapshotSpan entire = new SnapshotSpan(spans[0].Start, spans[spans.Count - 1].End) - .TranslateTo(snapshot, SpanTrackingMode.EdgeExclusive); - currentSnapshot = spans[0].Snapshot; - - height = view.LineHeight; - - lineHeight = view.LineHeight; - - } - catch (Exception e) - { - yield break; - } - - int index = 0; - if (_functions != null) - { - foreach (FunctionInfo function in _functions) - { - int lineN = function.DefinitionLine; - SnapshotSpan line; - SnapshotSpan span; - - try - { - line = currentSnapshot.GetLineFromLineNumber(lineN).Extent; - span = new SnapshotSpan(line.Start, line.Start); - } - catch (ArgumentOutOfRangeException e) - { - yield break; - } - - yield return new TagSpan( - span, - new CodeLensTag( - 0, height, 0, 0, 0, PositionAffinity.Predecessor, panels[index], this)); - index++; - } - } - - if (_classes != null) - { - foreach (ClassInfo c in _classes) - { - int lineN = c.StartLine; - SnapshotSpan line; - SnapshotSpan span; - - try - { - line = currentSnapshot.GetLineFromLineNumber(lineN).Extent; - span = new SnapshotSpan(line.Start, line.Start); - } - catch (ArgumentOutOfRangeException e) - { - yield break; - } - - yield return new TagSpan( - span, - new CodeLensTag( - 0, height, 0, lineHeight, 0, PositionAffinity.Predecessor, panels[index], this)); - } - } - } - - public event EventHandler TagsChanged; - - // triggers when the editor text buffer changes - void BufferChanged(object sender, TextContentChangedEventArgs e) - { - // If this isn't the most up-to-date version of the buffer, then ignore it for now (we'll - // eventually get another change event). - if (e.After != buffer.CurrentSnapshot) return; - - Task.Run(() => Update()); - } - - TextBlock CreateTextBox(int i, bool needsGoDoc) - { - TextBlock textBlock = new TextBlock(); - - var RefactorRun = new Run("Refactor") { Foreground = greyBrush }; - RefactorRun.MouseUp += (object sender, MouseButtonEventArgs e) => ClickRefactor(i); - var ExplainRun = new Run("Explain") { Foreground = greyBrush }; - ExplainRun.MouseUp += (object sender, MouseButtonEventArgs e) => ClickExplain(i); - var DocRun = new Run("DocString") { Foreground = greyBrush }; - DocRun.MouseUp += (object sender, MouseButtonEventArgs e) => ClickDoc(i); - - textBlock.Inlines.Add(new Run("Codeium: ") { Foreground = greyBrush }); - textBlock.Inlines.Add(RefactorRun); - textBlock.Inlines.Add(new Run(" | ") { Foreground = greyBrush }); - textBlock.Inlines.Add(ExplainRun); - - if (needsGoDoc) - { - textBlock.Inlines.Add(new Run(" | ") { Foreground = greyBrush }); - textBlock.Inlines.Add(DocRun); - } - - return textBlock; - } - - FunctionInfo GetFunN(int n) - { - var funcLength = _functions == null ? 0 : _functions.Count; - - if (n >= funcLength) - { - return null; - } - else - { - return _functions[n]; - } - } - - ClassInfo GetClassN(int n) - { - var funcLength = _functions == null ? 0 : _functions.Count; - - if (n >= funcLength) - { - n -= funcLength; - if (_classes.Count <= n) return null; - return _classes[n]; - } - else - { - return null; - } - } - - async void ClickRefactor(int i) - { - try - { - LanguageServerController controller = - CodeiumVSPackage.Instance.LanguageServer.Controller; - - FunctionInfo functionInfo = GetFunN(i); - ClassInfo classInfo; - int lineN; - if (functionInfo == null) - { - classInfo = GetClassN(i); - lineN = classInfo.StartLine; - } - else - { - lineN = functionInfo.DefinitionLine; - } - var span = originalSpans[i].TranslateTo(view.TextSnapshot, SpanTrackingMode.EdgePositive); - ITextSnapshotLine snapshotLine = span.Start.GetContainingLine(); - var start = view.TextViewLines.GetCharacterBounds(snapshotLine.Start); - - // highlight the selected codeblock - TextHighlighter? highlighter = TextHighlighter.GetInstance(view); - highlighter?.AddHighlight(snapshotLine.Extent); - var dialog = RefactorCodeDialogWindow.GetOrCreate(); - var prompt = - await dialog.ShowAndGetPromptAsync(_language, start.Left - view.ViewportLeft, start.Top - view.ViewportTop); - - highlighter?.ClearAll(); - - // user did not select any of the prompt - if (prompt == null) return; - if (functionInfo != null) - { - controller.RefactorFunctionAsync( - prompt, _document.FilePath, functionInfo); - } - else - { - classInfo = GetClassN(i); - if (classInfo == null) return; - CodeBlockInfo codeBlockInfo = ClassToCodeBlock(classInfo); - - controller.ExplainCodeBlockAsync(_document.FilePath, _language.Type, codeBlockInfo); - } - - } - catch (Exception e) - { - - } - } - - void ClickExplain(int i) - { - LanguageServerController controller = - CodeiumVSPackage.Instance.LanguageServer.Controller; - - FunctionInfo functionInfo = GetFunN(i); - if (functionInfo != null) - { - controller.ExplainFunctionAsync(_document.FilePath, functionInfo); - } - else - { - ClassInfo classInfo = GetClassN(i); - if (classInfo == null) return; - CodeBlockInfo codeBlockInfo = ClassToCodeBlock(classInfo); - controller.ExplainCodeBlockAsync( - _document.FilePath, _language.Type, codeBlockInfo); - } - } - - CodeBlockInfo ClassToCodeBlock(ClassInfo classInfo) - { - CodeBlockInfo codeBlockInfo = new CodeBlockInfo(); - codeBlockInfo.start_line = classInfo.StartLine; - codeBlockInfo.end_line = classInfo.EndLine; - codeBlockInfo.start_col = classInfo.StartCol; - codeBlockInfo.end_col = classInfo.EndCol; - return codeBlockInfo; - } - - void ClickDoc(int i) - { - LanguageServerController controller = - CodeiumVSPackage.Instance.LanguageServer.Controller; - - FunctionInfo functionInfo = GetFunN(i); - - if (functionInfo != null) - { - controller.GenerateFunctionDocstringAsync(_document.FilePath, functionInfo); - } - } - - private void UpdatePanel(StackPanel panel, int index, bool needsGoDoc) - { - panel.Children.Clear(); - panel.Children.Add(CreateTextBox(index, needsGoDoc)); - } - private void AddPanel(bool needsGoDoc) - { - CreateStackPanel(needsGoDoc); - } - - private void RemovePanel(List panels) - { - panels.RemoveAt(panels.Count - 1); - } - - void CreateStackPanel(bool needsGoDoc) - { - var stackPanel = new StackPanel(); - stackPanel.Children.Add(CreateTextBox(panels.Count, needsGoDoc)); - panels.Add(stackPanel); - } - - private void SetPosition(SnapshotSpan orginalLine, StackPanel panel) - { - try - { - - var snapshotSpan = orginalLine.TranslateTo(view.TextSnapshot, SpanTrackingMode.EdgeExclusive); - ITextSnapshotLine snapshotLine = snapshotSpan.Start.GetContainingLine(); - if (view.TextViewLines.FirstVisibleLine.Start < snapshotLine.Start && - view.TextViewLines.LastVisibleLine.End >= snapshotLine.Start) - { - var text = snapshotLine.GetText(); - int emptySpaceLength = text.Length - text.TrimStart().Length; - - var start = view.TextViewLines.GetCharacterBounds(snapshotLine.Start.Add(emptySpaceLength)); - - if (panel.Children.Count > 0) - { - var span = snapshotLine.Extent; - // Place the image in the top left hand corner of the line - Canvas.SetLeft(panel, start.Left); - Canvas.SetTop(element: panel, start.TextTop - view.LineHeight); - - // Add the image to the adornment layer and make it relative to the viewport - this.adornmentLayer.AddAdornment( - AdornmentPositioningBehavior.TextRelative, span, null, panel, null); - } - } - } - catch (Exception e) { Debug.Write(e); } - } - - private void UpdateAdornments() - { - this.adornmentLayer.RemoveAllAdornments(); - int i = 0; - - if(originalSpans == null) return; - if (_functions != null) - { - foreach (var function in _functions) - { - if (originalSpans.Count > i) - { - SetPosition(originalSpans[i], panels[i]); - } - i++; - } - } - - if (_classes != null) - { - foreach (var c in _classes) - { - if (originalSpans.Count > i) - { - SetPosition(originalSpans[i], panels[i]); - } - i++; - } - } - - } - - // Adds grey text to display - private void OnSizeChanged(object sender, EventArgs e) - { - UpdateAdornments(); - if ( Math.Abs(lastViewPortTop - view.ViewportTop) > Double.Epsilon) - { - lastViewPortTop = view.ViewportTop; - MarkDirty(); - } - } - - // update multiline data - public async Task Update() - { - - while (CodeiumVSPackage.Instance == null || CodeiumVSPackage.Instance.LanguageServer == null) - { - await Task.Delay(100); - } - - try - { - lastViewPortTop = view.ViewportTop; - - string text = _document.TextBuffer.CurrentSnapshot.GetText(); - SnapshotPoint? caretPoint = view.Caret.Position.Point.GetPoint( - textBuffer => (!textBuffer.ContentType.IsOfType("projection")), - PositionAffinity.Successor); - if (!caretPoint.HasValue) - { - return false; - } - - var caretPosition = caretPoint.Value.Position; - - int cursorPosition = _document.Encoding.IsSingleByte - ? caretPosition - : Utf16OffsetToUtf8Offset(text, caretPosition); - - if (cursorPosition > text.Length) - { - Debug.Print("Error Caret past text position"); - return false; - } - - UpdateRequestTokenSource(new CancellationTokenSource()); - IList? functions = - await CodeiumVSPackage.Instance.LanguageServer.GetFunctionsAsync( - _document.FilePath, - text, - _language, - cursorPosition, - view.Options.GetOptionValue(DefaultOptions.NewLineCharacterOptionId), - currentCancellTokenSource.Token); - - IList? classes = await CodeiumVSPackage.Instance.LanguageServer.GetClassInfosAsync( - _document.FilePath, - text, - _language, - cursorPosition, - view.Options.GetOptionValue(DefaultOptions.NewLineCharacterOptionId), - currentCancellTokenSource.Token); - - await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); - - _functions = (List)functions; - _classes = (List)classes; - - originalSpans.Clear(); - int index = 0; - foreach (FunctionInfo function in _functions) - { - if (panels.Count > index) - { - var panel = panels[index]; - var needsGoDoc = function.Docstring.IsNullOrEmpty(); - var childrenCount = (panel.Children[0] as TextBlock).Inlines.Count; - if ((needsGoDoc && childrenCount < 5) || (!needsGoDoc && childrenCount > 5)) - { - UpdatePanel(panel, index, needsGoDoc); - } - } - originalSpans.Add(view.TextSnapshot.GetLineFromLineNumber(function.DefinitionLine).Extent); - index++; - } - - foreach (ClassInfo c in classes) - { - if (panels.Count > index) - { - var panel = panels[index]; - if((panel.Children[0] as TextBlock).Inlines.Count >= 5) - { - UpdatePanel(panel, index, false); - } - } - - originalSpans.Add(view.TextSnapshot.GetLineFromLineNumber(c.DefinitionLine).Extent); - index++; - } - - int panelDiff = (_functions.Count + _classes.Count) - panels.Count; - if (panelDiff > 0) - { - for (int i = 0; i < panelDiff; i++) - { - FunctionInfo function = GetFunN(i); - bool needsGoDoc = function != null && function.Docstring.IsNullOrEmpty(); - AddPanel(needsGoDoc); - } - } - else if (panelDiff < 0) - { - for (int i = 0; i < panelDiff; i++) - { - RemovePanel(panels); - } - } - - UpdateAdornments(); - MarkDirty(); - - return true; - } - catch (Exception e) - { - return false; - } - } - - private void UpdateRequestTokenSource(CancellationTokenSource newSource) - { - if (currentCancellTokenSource != null) - { - currentCancellTokenSource.Cancel(); - currentCancellTokenSource.Dispose(); - } - currentCancellTokenSource = newSource; - } - public static int Utf16OffsetToUtf8Offset(string str, int utf16Offset) - { - return Encoding.UTF8.GetByteCount(str.ToCharArray(), 0, utf16Offset); - } - - // triggers refresh of the screen - void MarkDirty() - { - try - { - ITextSnapshot newSnapshot = buffer.CurrentSnapshot; - this.snapshot = newSnapshot; - - if (view.TextViewLines == null) return; - if (!view.TextViewLines.IsValid) return; - - var changeStart = view.TextViewLines.FirstVisibleLine.Start; - var changeEnd = view.TextViewLines.LastVisibleLine.Start; - - var startLine = view.TextSnapshot.GetLineFromPosition(changeStart); - var endLine = view.TextSnapshot.GetLineFromPosition(changeEnd); - - var span = new SnapshotSpan(startLine.Start, endLine.EndIncludingLineBreak) - .TranslateTo(targetSnapshot: newSnapshot, SpanTrackingMode.EdgePositive); - - // lines we are marking dirty - // currently all of them for simplicity - if (this.TagsChanged != null) { TagsChanged(this, new SnapshotSpanEventArgs(span)); } - } - catch (Exception e) { Debug.Write(e); } - } - - public void Dispose() - { - _document.FileActionOccurred -= OnFileActionOccurred; - _document.TextBuffer.ContentTypeChanged -= OnContentTypeChanged; - UpdateRequestTokenSource(null); - } - - } - - [Export(typeof(IViewTaggerProvider))] - [TagType(typeof(CodeLensTag))] - [ContentType("text")] - internal sealed class CodeLensProvider : IViewTaggerProvider - { - - [Export(typeof(AdornmentLayerDefinition))] - [Name("CodeiumCodeLensAdornmentLayer")] - [Order(After = PredefinedAdornmentLayers.Caret)] - private AdornmentLayerDefinition codeLensAdornmentLayer; - -#pragma warning restore 649, 169 - - // document factory is used to get information about the current text document such as filepath, - // language, etc. - [Import] - internal ITextDocumentFactoryService documentFactory = null; - - // create a single tagger for each buffer. - // the MultilineGreyTextTagger displays the grey text in the editor. - public ITagger CreateTagger(ITextView textView, ITextBuffer buffer) - where T : ITag - { - var topBuffer = textView.BufferGraph.TopBuffer; - - var projectionBuffer = topBuffer as IProjectionBufferBase; - - ITextBuffer textBuffer = - projectionBuffer != null ? projectionBuffer.SourceBuffers[0] : topBuffer; - ITextDocument _document; - documentFactory.TryGetTextDocument(textBuffer, out _document); - if(_document == null) return null; - Func> sc = delegate () - { - return new CodeLensTagger((IWpfTextView)textView, buffer, _document) as ITagger; - }; - return buffer.Properties.GetOrCreateSingletonProperty>(typeof(CodeLensTagger), - sc); - } - } -} diff --git a/CodeiumVS/CodeLensConnection/CodeLensConnectionHandler.cs b/CodeiumVS/CodeLensConnection/CodeLensConnectionHandler.cs new file mode 100644 index 0000000..c8b4d8d --- /dev/null +++ b/CodeiumVS/CodeLensConnection/CodeLensConnectionHandler.cs @@ -0,0 +1,98 @@ +#nullable enable + +using System.Collections.Generic; +using CodeiumVS.Packets; + +namespace CodeiumVS +{ + using System; + using System.Diagnostics; + using System.IO.Pipes; + using System.Linq; + using System.Threading.Tasks; + + using StreamJsonRpc; + + using CodeLensConnections = System.Collections.Concurrent.ConcurrentDictionary; + using CodeLensDetails = System.Collections.Concurrent.ConcurrentDictionary; + + public class CodeLensConnectionHandler : IRemoteVisualStudio, IDisposable + { + private static readonly CodeLensConnections connections = new CodeLensConnections(); + private static readonly CodeLensDetails detailsData = new CodeLensDetails(); + + private JsonRpc? rpc; + private Guid? dataPointId; + + public static async Task AcceptCodeLensConnections() + { + try + { + while (true) + { + var stream = new NamedPipeServerStream( + PipeName.Get(Process.GetCurrentProcess().Id), + PipeDirection.InOut, + NamedPipeServerStream.MaxAllowedServerInstances, + PipeTransmissionMode.Byte, + PipeOptions.Asynchronous); + await stream.WaitForConnectionAsync().Caf(); + _ = HandleConnection(stream); + } + } + catch (Exception ex) + { + throw; + } + + static async Task HandleConnection(NamedPipeServerStream stream) + { + try + { + var handler = new CodeLensConnectionHandler(); + var rpc = JsonRpc.Attach(stream, handler); + handler.rpc = rpc; + await rpc.Completion; + handler.Dispose(); + stream.Dispose(); + } + catch (Exception ex) + { + CodeiumVSPackage.Instance.LogAsync("Handle Connection Error"); + } + } + } + + public void Dispose() + { + if (dataPointId.HasValue) + { + _ = connections.TryRemove(dataPointId.Value, out var _); + _ = detailsData.TryRemove(dataPointId.Value, out var _); + } + } + + // Called from each CodeLensDataPoint via JSON RPC. + public void RegisterCodeLensDataPoint(Guid id) + { + dataPointId = id; + connections[id] = this; + } + + public static void StoreDetailsData(Guid id, FunctionInfo closestFunction) => detailsData[id] = closestFunction; + + public static FunctionInfo GetDetailsData(Guid id) => detailsData[id]; + + public static async Task RefreshCodeLensDataPoint(Guid id) + { + if (!connections.TryGetValue(id, out var conn)) + throw new InvalidOperationException($"CodeLens data point {id} was not registered."); + + Debug.Assert(conn.rpc != null); + await conn.rpc!.InvokeAsync(nameof(IRemoteCodeLens.Refresh)).Caf(); + } + + public static async Task RefreshAllCodeLensDataPoints() + => await Task.WhenAll(connections.Keys.Select(RefreshCodeLensDataPoint)).Caf(); + } +} diff --git a/CodeiumVS/CodeLensConnection/CodeLensListener.cs b/CodeiumVS/CodeLensConnection/CodeLensListener.cs new file mode 100644 index 0000000..c11ced7 --- /dev/null +++ b/CodeiumVS/CodeLensConnection/CodeLensListener.cs @@ -0,0 +1,101 @@ +#nullable enable + +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.Language.CodeLens; +using Microsoft.VisualStudio.Utilities; +using CodeiumVS.Packets; +using Microsoft.VisualStudio.ComponentModelHost; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using System.Windows.Shapes; +using Microsoft.VisualStudio.Debugger.Interop; + +namespace CodeiumVS +{ + + [Export(typeof(ICodeLensCallbackListener))] + [ContentType("C/C++")] + [ContentType("CSharp")] + [ContentType("Basic")] + [ContentType("vbscript")] + [ContentType("TypeScript")] + [ContentType("JavaScript")] + public class CodeLensListener : ICodeLensCallbackListener, ICodeLensListener + { + + public int GetVisualStudioPid() => Process.GetCurrentProcess().Id; + + FunctionInfo GetClosestFunction(IList? functions, int line) + { + + FunctionInfo minFunction = null; + int minDistance = int.MaxValue; + foreach (var f in functions) + { + var distance = Math.Abs(f.DefinitionLine - line); + if (distance < minDistance) + { + minDistance = distance; + minFunction = f; + } + } + + return minFunction; + } + + public async Task LoadInstructions( + Guid dataPointId, + Guid projGuid, + string filePath, + int textStart, + int textLen, + CancellationToken ct) + { + try + { + + ITextDocument _document; + TextViewListener.Instance.documentDictionary.TryGetValue(filePath.ToLower(), out _document); + var key = typeof(CodeiumCompletionHandler); + var props = _document.TextBuffer.Properties; + CodeiumCompletionHandler handler; + if (props.ContainsProperty(key)) + { + handler = props.GetProperty(key); + } + else + { + handler = null; + return null; + } + + IList? functions = + await CodeiumVSPackage.Instance.LanguageServer.GetFunctionsAsync( + _document.FilePath, + _document.TextBuffer.CurrentSnapshot.GetText(), + handler.GetLanguage(), + 0, + ct); + + var line = new Span(textStart, textLen); + var snapshotLine = _document.TextBuffer.CurrentSnapshot.GetLineFromPosition(line.Start); + var lineN = snapshotLine.LineNumber; + FunctionInfo closestFunction = GetClosestFunction(functions, lineN); + CodeLensConnectionHandler.StoreDetailsData(dataPointId, closestFunction); + + return closestFunction; + } + catch (Exception ex) + { + CodeiumVSPackage.Instance.LogAsync(ex.ToString()); + + return null; + } + } + + } +} diff --git a/CodeiumVS/CodeiumVS.csproj b/CodeiumVS/CodeiumVS.csproj index e73cce1..6c42aaa 100644 --- a/CodeiumVS/CodeiumVS.csproj +++ b/CodeiumVS/CodeiumVS.csproj @@ -67,8 +67,12 @@ preview - - + + + + + + InlineDiffControl.xaml diff --git a/CodeiumVS/CodeiumVSPackage.cs b/CodeiumVS/CodeiumVSPackage.cs index 5c15651..0293b44 100644 --- a/CodeiumVS/CodeiumVSPackage.cs +++ b/CodeiumVS/CodeiumVSPackage.cs @@ -81,6 +81,18 @@ await VS.MessageBox.ShowErrorAsync( "Codeium might not work correctly. Please check the output window for more details."); } + try + { + await base.InitializeAsync(cancellationToken, progress); + await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + _ = CodeLensConnectionHandler.AcceptCodeLensConnections(); + } + catch (Exception ex) + { + await LogAsync("Codeium Error" + ex); + throw; + } + await LanguageServer.InitializeAsync(); await LogAsync("Codeium Extension for Visual Studio"); } diff --git a/CodeiumVS/Commands.cs b/CodeiumVS/Commands.cs index 5b6d3d8..f3b3dc3 100644 --- a/CodeiumVS/Commands.cs +++ b/CodeiumVS/Commands.cs @@ -9,6 +9,11 @@ using CodeiumVS.Packets; using CodeiumVS.Utilities; using System.Windows.Forms; +using System.Collections.Generic; +using System.Windows.Shapes; +using static System.Windows.Forms.LinkLabel; +using System.Threading; +using Microsoft.VisualStudio.Language.CodeLens; namespace CodeiumVS.Commands; @@ -343,6 +348,269 @@ await controller.RefactorCodeBlockAsync( } } +internal class BaseCommandCodeLens : BaseCommand + where T : class, new() +{ + internal static long lastQuery = 0; + + protected static DocumentView? docView; + protected static string text; // the selected text + protected static Languages.LangInfo languageInfo; + protected FunctionInfo functionInfo; + protected ClassInfo classInfo; + protected override void BeforeQueryStatus(EventArgs e) + { + // Derived menu commands will call this repeatedly upon openning + // so we only want to do it once, i can't find a better way to do it + long timeStamp = Stopwatch.GetTimestamp(); + if (lastQuery != 0 && timeStamp - lastQuery < 500) return; + lastQuery = timeStamp; + + ThreadHelper.JoinableTaskFactory.Run(async delegate { + + // any interactions with the `IVsTextView` should be done on the main thread + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + + try + { + docView = await VS.Documents.GetActiveDocumentViewAsync(); + text = docView.TextBuffer.CurrentSnapshot.GetText(); + if (docView?.TextView == null) return false; + } + catch (Exception ex) + { + await CodeiumVSPackage.Instance.LogAsync( + $"BaseCommandContextMenu: Failed to get the active document view; Exception: {ex}"); + return false; + } + + languageInfo = Languages.Mapper.GetLanguage(docView); + ITextSelection selection = docView.TextView.Selection; + + return true; + }); + } + + protected async Task ResolveCodeBlock(int startLine) + { + CancellationTokenSource cts = new CancellationTokenSource(); + IList? functions = + await CodeiumVSPackage.Instance.LanguageServer.GetFunctionsAsync( + docView.FilePath, + text, + languageInfo, + 0, + cts.Token); + + IList? classes = await CodeiumVSPackage.Instance.LanguageServer.GetClassInfosAsync( + docView.FilePath, + text, + languageInfo, + 0, + docView.TextView.Options.GetOptionValue(DefaultOptions.NewLineCharacterOptionId), + cts.Token); + + FunctionInfo minFunction = null; + int minDistance = int.MaxValue; + foreach (var f in functions) + { + var distance = Math.Abs(f.DefinitionLine - startLine); + if (distance < minDistance) + { + minDistance = distance; + functionInfo = f; + } + } + + foreach (var c in classes) + { + var distance = Math.Abs(c.StartLine - startLine); + if (distance < minDistance) + { + minDistance = distance; + functionInfo = null; + classInfo = c; + } + } + return true; + } + + protected CodeBlockInfo ClassToCodeBlock(ClassInfo c) + { + CodeBlockInfo codeBlockInfo = null; + try + { + var snapshotLineStart = docView.TextBuffer.CurrentSnapshot.GetLineFromLineNumber(c.StartLine); + var snapShotLineEnd = docView.TextBuffer.CurrentSnapshot.GetLineFromLineNumber(c.EndLine); + + var start_position = snapshotLineStart.Start; + var end_position = snapShotLineEnd.End; + + var start_line = snapshotLineStart.LineNumber + 1; + var end_line = snapShotLineEnd.LineNumber + 1; + var start_col = start_position - snapshotLineStart.Start.Position + 1; + var end_col = end_position - snapShotLineEnd.Start.Position + 1; + + text = docView.TextBuffer.CurrentSnapshot.GetText(start_position, + end_position - start_position); + + codeBlockInfo = new() + { + raw_source = text, + start_line = start_line, + end_line = end_line, + start_col = start_col, + end_col = end_col, + }; + } + catch (Exception ex) + { + Task.Run(async () => + { + return CodeiumVSPackage.Instance.LogAsync(ex.ToString()); + }); + } + return codeBlockInfo; + } + +} + + +[Command(PackageIds.RefactorSelectionCodeBlock)] +internal class CommandRefactorSelectionCodeBlock : BaseCommandCodeLens +{ + protected override async Task ExecuteAsync(OleMenuCmdEventArgs e) + { + + try + { + LanguageServerController controller = + (Package as CodeiumVSPackage).LanguageServer.Controller; + + CodeLensDescriptorContext ctx = null; + ITextSnapshotLine snapshotLine; + int startPos; + if (e.InValue != null) + { + await CodeiumVSPackage.Instance.LogAsync(e.InValue.ToString()); + ctx = e.InValue as CodeLensDescriptorContext; + startPos = ctx.ApplicableSpan.Value.Start; + snapshotLine = docView.TextBuffer.CurrentSnapshot.GetLineFromPosition(startPos); + int startLine = snapshotLine.LineNumber; + TextBounds selectionLine = docView.TextView.TextViewLines.GetCharacterBounds(snapshotLine.Start); + Point selectionScreenPos = docView.TextView.VisualElement.PointToScreen( + new Point(selectionLine.Left - docView.TextView.ViewportLeft, + selectionLine.Top - docView.TextView.ViewportTop)); + + var start = docView.TextView.TextViewLines.GetCharacterBounds(snapshotLine.Start); + + // highlight the selected codeblock + TextHighlighter? highlighter = TextHighlighter.GetInstance(docView.TextView); + highlighter?.AddHighlight(snapshotLine.Extent); + var dialog = RefactorCodeDialogWindow.GetOrCreate(); + string? prompt = + await dialog.ShowAndGetPromptAsync(languageInfo, selectionScreenPos.X, selectionScreenPos.Y); + + highlighter?.ClearAll(); + + await ResolveCodeBlock(startLine); + // user did not select any of the prompt + if (prompt == null) return; + if (functionInfo != null) + { + controller.RefactorFunctionAsync( + prompt, docView.FilePath, functionInfo); + } + else + { + if (classInfo == null) return; + CodeBlockInfo codeBlockInfo = ClassToCodeBlock(classInfo); + + await controller.RefactorCodeBlockAsync( + prompt, docView.FilePath, languageInfo.Type, codeBlockInfo); + } + } + } + catch (Exception ex) + { + await CodeiumVSPackage.Instance.LogAsync(ex.ToString()); + } + } +} + +[Command(PackageIds.ExplainSelectionCodeBlock)] +internal class ExplainSelectionCodeBlock : BaseCommandCodeLens +{ + protected override async Task ExecuteAsync(OleMenuCmdEventArgs e) + { + + try + { + LanguageServerController controller = + (Package as CodeiumVSPackage).LanguageServer.Controller; + + if (e.InValue != null) + { + await CodeiumVSPackage.Instance.LogAsync(e.InValue.ToString()); + var ctx = e.InValue as CodeLensDescriptorContext; + int startPos = ctx.ApplicableSpan.Value.Start; + ITextSnapshotLine line = docView.TextBuffer.CurrentSnapshot.GetLineFromPosition(startPos); + int startLine = line.LineNumber; + + await ResolveCodeBlock(startLine); + if (functionInfo != null) + { + controller.ExplainFunctionAsync(docView.FilePath, functionInfo); + } + else + { + if (classInfo == null) return; + CodeBlockInfo codeBlockInfo = ClassToCodeBlock(classInfo); + controller.ExplainCodeBlockAsync( + docView.FilePath, languageInfo.Type, codeBlockInfo); + } + } + } + catch (Exception ex) + { + await CodeiumVSPackage.Instance.LogAsync(ex.ToString()); + } + } +} + + +[Command(PackageIds.GenerateSelectionFunctionDocstring)] +internal class GenerateSelectionFunctionDocstring : BaseCommandCodeLens +{ + protected override async Task ExecuteAsync(OleMenuCmdEventArgs e) + { + + try + { + LanguageServerController controller = + (Package as CodeiumVSPackage).LanguageServer.Controller; + + if (e.InValue != null) + { + await CodeiumVSPackage.Instance.LogAsync(e.InValue.ToString()); + var ctx = e.InValue as CodeLensDescriptorContext; + int startPos = ctx.ApplicableSpan.Value.Start; + ITextSnapshotLine line = docView.TextBuffer.CurrentSnapshot.GetLineFromPosition(startPos); + int startLine = line.LineNumber; + + await ResolveCodeBlock(startLine); + if (functionInfo != null) + { + controller.GenerateFunctionDocstringAsync(docView.FilePath, functionInfo); + } + } + } + catch (Exception ex) + { + await CodeiumVSPackage.Instance.LogAsync(ex.ToString()); + } + } +} [Command(PackageIds.GenerateFunctionUnitTest)] internal class CommandGenerateFunctionUnitTest : BaseCommandContextMenu diff --git a/CodeiumVS/LanguageServer/LanguageServer.cs b/CodeiumVS/LanguageServer/LanguageServer.cs index 89056ed..511c59a 100644 --- a/CodeiumVS/LanguageServer/LanguageServer.cs +++ b/CodeiumVS/LanguageServer/LanguageServer.cs @@ -818,6 +818,7 @@ public Metadata GetMetadata() api_key = _metadata.api_key, ide_name = _metadata.ide_name, ide_version = _metadata.ide_version, + extension_name = _metadata.extension_name, extension_version = _metadata.extension_version, session_id = _metadata.session_id, @@ -827,7 +828,7 @@ public Metadata GetMetadata() public async Task?> GetFunctionsAsync(string absolutePath, string text, Languages.LangInfo language, - int cursorPosition, string lineEnding, CancellationToken token) + int cursorPosition, CancellationToken token) { if (!_initializedWorkspace) { diff --git a/CodeiumVS/SuggestionUI/SuggestionTagger.cs b/CodeiumVS/SuggestionUI/SuggestionTagger.cs index 9bfbd31..fd0a37a 100644 --- a/CodeiumVS/SuggestionUI/SuggestionTagger.cs +++ b/CodeiumVS/SuggestionUI/SuggestionTagger.cs @@ -68,49 +68,68 @@ private InlineGreyTextTagger GetTagger() public bool SetSuggestion(String newSuggestion, int caretPoint) { - newSuggestion = newSuggestion.TrimEnd(); - newSuggestion = newSuggestion.Replace("\r", ""); - ClearSuggestion(); + try + { + // If this isn't the most up-to-date version of the buffer, then ignore it for now (we'll + newSuggestion = newSuggestion.TrimEnd(); + newSuggestion = newSuggestion.Replace("\r", ""); + ClearSuggestion(); - int lineN = GetCurrentTextLine(); + int lineN = GetCurrentTextLine(); - if (lineN < 0) return false; + if (lineN < 0) return false; - String untrim = buffer.CurrentSnapshot.GetLineFromLineNumber(lineN).GetText(); + String untrim = buffer.CurrentSnapshot.GetLineFromLineNumber(lineN).GetText(); - virtualText = ""; - if (String.IsNullOrWhiteSpace(untrim) && untrim.Length < caretPoint) - { - virtualText = new string(' ', caretPoint - untrim.Length); - } - String line = untrim.TrimStart(); - int offset = untrim.Length - line.Length; + virtualText = ""; + if (String.IsNullOrWhiteSpace(untrim) && untrim.Length < caretPoint) + { + virtualText = new string(' ', caretPoint - untrim.Length); + } + String line = untrim.TrimStart(); + int offset = untrim.Length - line.Length; - caretPoint = Math.Max(0, caretPoint - offset); + caretPoint = Math.Max(0, caretPoint - offset); - String combineSuggestion = line + newSuggestion; - if (line.Length - caretPoint > 0) - { - String currentText = line.Substring(0, caretPoint); - combineSuggestion = currentText + newSuggestion; - userEndingText = line.Substring(caretPoint).Trim(); - var userIndex = newSuggestion.IndexOf(userEndingText); + String combineSuggestion = line + newSuggestion; + if (line.Length - caretPoint > 0) + { + String currentText = line.Substring(0, caretPoint); + combineSuggestion = currentText + newSuggestion; + userEndingText = line.Substring(caretPoint).Trim(); + var userIndex = newSuggestion.IndexOf(userEndingText); - if (userIndex < 0) { return false; } - userIndex += currentText.Length; + if (userIndex < 0) { return false; } + userIndex += currentText.Length; - this.userIndex = userIndex; - isTextInsertion = true; - insertionPoint = line.Length - caretPoint; + this.userIndex = userIndex; + isTextInsertion = true; + insertionPoint = line.Length - caretPoint; + } + else { isTextInsertion = false; } + var suggestionLines = combineSuggestion.Split('\n'); + suggestion = new Tuple(combineSuggestion, suggestionLines); + return Update(); + }catch (Exception ex) + { + CodeiumVSPackage.Instance?.LogAsync("Exception: " + ex.ToString()); + return false; } - else { isTextInsertion = false; } - var suggestionLines = combineSuggestion.Split('\n'); - suggestion = new Tuple(combineSuggestion, suggestionLines); - return Update(); } public bool OnSameLine() { return GetCurrentTextLine() == currentTextLineN; } - private void LostFocus(object sender, EventArgs e) { ClearSuggestion(); } + + private void LostFocus(object sender, EventArgs e) + { + try + { + ClearSuggestion(); + } + catch (Exception ex) + { + CodeiumVSPackage.Instance?.LogAsync("Exception: " + ex.ToString()); + } + } public SuggestionTagger(IWpfTextView view, ITextBuffer buffer) { @@ -173,11 +192,17 @@ public IEnumerable> GetTags(NormalizedSnapshotSpanCollec // triggers when the editor text buffer changes void BufferChanged(object sender, TextContentChangedEventArgs e) { - // If this isn't the most up-to-date version of the buffer, then ignore it for now (we'll - // eventually get another change event). - if (e.After != buffer.CurrentSnapshot) return; - - this.Update(); + try + { + // If this isn't the most up-to-date version of the buffer, then ignore it for now (we'll + // eventually get another change event). + if (e.After != buffer.CurrentSnapshot) return; + this.Update(); + } + catch (Exception ex) + { + CodeiumVSPackage.Instance?.LogAsync("Exception: " + ex.ToString()); + } } TextRunProperties GetTextFormat() @@ -253,88 +278,88 @@ void AddInsertionTextBlock(int start, int end, string line) // Updates the grey text public void UpdateAdornment(IWpfTextView view, string userText, int suggestionStart) { - stackPanel.Children.Clear(); - GetTagger().ClearAdornment(); - for (int i = suggestionStart; i < suggestion.Item2.Length; i++) - { - string line = suggestion.Item2[i]; - - if (i == 0) + try + { + stackPanel.Children.Clear(); + GetTagger().ClearAdornment(); + for (int i = suggestionStart; i < suggestion.Item2.Length; i++) { - int offset = line.Length - line.TrimStart().Length; + string line = suggestion.Item2[i]; - if (isTextInsertion && suggestionIndex < userIndex) + if (i == 0) { - if (suggestionIndex > 0 && suggestionIndex < line.Length && char.IsWhiteSpace(line[suggestionIndex - 1]) && - userText.Length > insertionPoint + 1 && - !char.IsWhiteSpace(userText[userText.Length - insertionPoint - 1])) - { - suggestionIndex--; - } - AddInsertionTextBlock(suggestionIndex + offset, userIndex, line); - if (line.Length > userIndex + 1) - { - AddSuffixTextBlocks( - userIndex + userEndingText.Trim().Length, line, userText); - } - else { stackPanel.Children.Add(CreateTextBox("", greyBrush)); } - } - else - { - if (String.IsNullOrEmpty(line)) + int offset = line.Length - line.TrimStart().Length; + + if (isTextInsertion && suggestionIndex < userIndex) { - stackPanel.Children.Add(CreateTextBox("", greyBrush)); + if (suggestionIndex > 0 && suggestionIndex < line.Length && char.IsWhiteSpace(line[suggestionIndex - 1]) && + userText.Length > insertionPoint + 1 && + !char.IsWhiteSpace(userText[userText.Length - insertionPoint - 1])) + { + suggestionIndex--; + } + AddInsertionTextBlock(suggestionIndex + offset, userIndex, line); + if (line.Length > userIndex + 1) + { + AddSuffixTextBlocks( + userIndex + userEndingText.Trim().Length, line, userText); + } + else { stackPanel.Children.Add(CreateTextBox("", greyBrush)); } } else { - String suggestedLine = - virtualText.Length > 0 ? virtualText + line.TrimStart() : line; - AddSuffixTextBlocks(userText.Length > 0 ? suggestionIndex + offset : 0, - suggestedLine, - userText); + if (String.IsNullOrEmpty(line)) + { + stackPanel.Children.Add(CreateTextBox("", greyBrush)); + } + else + { + String suggestedLine = + virtualText.Length > 0 ? virtualText + line.TrimStart() : line; + AddSuffixTextBlocks(userText.Length > 0 ? suggestionIndex + offset : 0, + suggestedLine, + userText); + } } } + else { stackPanel.Children.Add(CreateTextBox(line, greyBrush)); } } - else { stackPanel.Children.Add(CreateTextBox(line, greyBrush)); } - } - this.adornmentLayer.RemoveAllAdornments(); + this.adornmentLayer.RemoveAllAdornments(); - // usually only happens the moment a bunch of text has rentered such as an undo operation - try - { - ITextSnapshotLine snapshotLine = - view.TextSnapshot.GetLineFromLineNumber(currentTextLineN); - var start = view.TextViewLines.GetCharacterBounds(snapshotLine.Start); + // usually only happens the moment a bunch of text has rentered such as an undo operation + ITextSnapshotLine snapshotLine = + view.TextSnapshot.GetLineFromLineNumber(currentTextLineN); + var start = view.TextViewLines.GetCharacterBounds(snapshotLine.Start); - // Place the image in the top left hand corner of the line - Canvas.SetLeft(stackPanel, start.Left); - Canvas.SetTop(stackPanel, start.TextTop); - var span = snapshotLine.Extent; - // Add the image to the adornment layer and make it relative to the viewport - this.adornmentLayer.AddAdornment( - AdornmentPositioningBehavior.TextRelative, span, null, stackPanel, null); + // Place the image in the top left hand corner of the line + Canvas.SetLeft(stackPanel, start.Left); + Canvas.SetTop(stackPanel, start.TextTop); + var span = snapshotLine.Extent; + // Add the image to the adornment layer and make it relative to the viewport + this.adornmentLayer.AddAdornment( + AdornmentPositioningBehavior.TextRelative, span, null, stackPanel, null); } catch (Exception e) - { - Debug.Write(e); + { Debug.Write(e); } } // Adds grey text to display private void OnSizeChanged(object sender, EventArgs e) { - if (!showSuggestion) { return; } - foreach (TextBlock block in stackPanel.Children) + try { - FormatText(block); - } + if (!showSuggestion) { return; } - ITextSnapshotLine snapshotLine = view.TextSnapshot.GetLineFromLineNumber(currentTextLineN); + foreach (TextBlock block in stackPanel.Children) + { + FormatText(block); + } + + ITextSnapshotLine snapshotLine = view.TextSnapshot.GetLineFromLineNumber(currentTextLineN); - try - { var start = view.TextViewLines.GetCharacterBounds(snapshotLine.Start); InlineGreyTextTagger inlineTagger = GetTagger(); @@ -358,9 +383,9 @@ private void OnSizeChanged(object sender, EventArgs e) AdornmentPositioningBehavior.TextRelative, span, null, stackPanel, null); } } - catch (ArgumentOutOfRangeException) + catch (Exception ex) { - Debug.Print("Error argument out of range"); + CodeiumVSPackage.Instance?.LogAsync("Exception: " + ex.ToString()); } } @@ -398,12 +423,12 @@ public bool Update() // else // clear suggestions - int suggestionIndex = + int newIndex = StringCompare.CheckSuggestion(suggestion.Item1, line, isTextInsertion, insertionPoint); - if (suggestionIndex >= 0) + if (newIndex >= 0) { this.currentTextLineN = textLineN; - this.suggestionIndex = suggestionIndex; + this.suggestionIndex = newIndex; ShowSuggestion(untrimLine, 0); return true; } @@ -415,20 +440,31 @@ public bool Update() // Adds the grey text to the file replacing current line in the process public bool CompleteText() { - if (!showSuggestion || suggestion == null) { return false; } + try + { + if (!showSuggestion || suggestion == null) + { + return false; + } - String untrimLine = this.snapshot.GetLineFromLineNumber(currentTextLineN).GetText(); - String line = untrimLine.Trim(); + String untrimLine = this.snapshot.GetLineFromLineNumber(currentTextLineN).GetText(); + String line = untrimLine.Trim(); - int suggestionLineN = - StringCompare.CheckSuggestion(suggestion.Item1, line, isTextInsertion, insertionPoint); - if (suggestionLineN >= 0) + int suggestionLineN = + StringCompare.CheckSuggestion(suggestion.Item1, line, isTextInsertion, insertionPoint); + if (suggestionLineN >= 0) + { + int diff = untrimLine.Length - untrimLine.TrimStart().Length; + string whitespace = + String.IsNullOrWhiteSpace(untrimLine) ? "" : untrimLine.Substring(0, diff); + ReplaceText(whitespace + suggestion.Item1, currentTextLineN); + return true; + } + + } + catch (Exception e) { - int diff = untrimLine.Length - untrimLine.TrimStart().Length; - string whitespace = - String.IsNullOrWhiteSpace(untrimLine) ? "" : untrimLine.Substring(0, diff); - ReplaceText(whitespace + suggestion.Item1, currentTextLineN); - return true; + CodeiumVSPackage.Instance?.LogAsync("Exception: " + e.ToString()); } return false; @@ -476,15 +512,23 @@ void ShowSuggestion(String text, int suggestionLineStart) // removes the suggestion public void ClearSuggestion() { - if (!showSuggestion) return; - InlineGreyTextTagger inlineTagger = GetTagger(); - inlineTagger.ClearAdornment(); - inlineTagger.MarkDirty(); - suggestion = null; - adornmentLayer.RemoveAllAdornments(); - showSuggestion = false; + try + { + if (!showSuggestion) return; + InlineGreyTextTagger inlineTagger = GetTagger(); + inlineTagger.ClearAdornment(); + inlineTagger.MarkDirty(); + suggestion = null; + adornmentLayer.RemoveAllAdornments(); + showSuggestion = false; - MarkDirty(); + MarkDirty(); + + } + catch (Exception ex) + { + + } } // triggers refresh of the screen diff --git a/CodeiumVS/SuggestionUI/TextViewListener.cs b/CodeiumVS/SuggestionUI/TextViewListener.cs index 48d11f3..be4fad1 100644 --- a/CodeiumVS/SuggestionUI/TextViewListener.cs +++ b/CodeiumVS/SuggestionUI/TextViewListener.cs @@ -117,7 +117,7 @@ public async void GetCompletion() await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); suggestionIndex = 0; currentCompletionID = suggestions[0].Item2; - tagger.SetSuggestion(suggestions[0].Item1, characterN); + var valid = tagger.SetSuggestion(suggestions[0].Item1, characterN); } await package.LogAsync("Generated " + list.Count + $" proposals"); @@ -225,6 +225,11 @@ private void OnSuggestionAccepted(String proposalId) .FireAndForget(true); } + public LangInfo GetLanguage() + { + return _language; + } + private void UpdateRequestTokenSource(CancellationTokenSource newSource) { if (currentCancellTokenSource != null) @@ -263,6 +268,9 @@ internal CodeiumCompletionHandler(IVsTextView textViewAdapter, ITextView view, if (_document != null) { + CodeiumVSPackage.Instance.LogAsync("CodeiumCompletionHandler filepath = " + _document.FilePath); + + provider.documentDictionary.Add(_document.FilePath.ToLower(), _document); _document.FileActionOccurred += OnFileActionOccurred; _document.TextBuffer.ContentTypeChanged += OnContentTypeChanged; RefreshLanguage(); @@ -291,21 +299,36 @@ internal CodeiumCompletionHandler(IVsTextView textViewAdapter, ITextView view, private void CaretUpdate(object sender, CaretPositionChangedEventArgs e) { - var tagger = GetTagger(); - if(tagger == null) { return; } - if (CompleteSuggestionCommand != null && CompleteSuggestionCommand.Bindings is object[] bindings && bindings.Length > 0) + try { - tagger.ClearSuggestion(); - return; - } + var tagger = GetTagger(); + if (tagger == null) + { + return; + } - var key = GetAsyncKeyState(0x09); - if ((0x8000 & key) > 0) - { - CompleteSuggestion(false); - }else if (!tagger.OnSameLine()) + if (CompleteSuggestionCommand != null && CompleteSuggestionCommand.Bindings is object[] bindings && + bindings.Length > 0) + { + tagger.ClearSuggestion(); + return; + } + + var key = GetAsyncKeyState(0x09); + if ((0x8000 & key) > 0) + { + CompleteSuggestion(false); + } + else if (!tagger.OnSameLine()) + { + tagger.ClearSuggestion(); + } + } + catch (Exception ex) { - tagger.ClearSuggestion(); + ThreadHelper.JoinableTaskFactory + .RunAsync(async delegate { await CodeiumVSPackage.Instance.LogAsync(ex.ToString()); }) + .FireAndForget(true); } } @@ -327,36 +350,47 @@ private void RefreshLanguage() public async void ShowNextSuggestion() { - if (suggestions != null && suggestions.Count > 1) + try { + if (suggestions != null && suggestions.Count > 1) + { - await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); - var oldSuggestion = suggestionIndex; - suggestionIndex = (suggestionIndex + 1) % suggestions.Count; - currentCompletionID = suggestions[suggestionIndex].Item2; + var oldSuggestion = suggestionIndex; + suggestionIndex = (suggestionIndex + 1) % suggestions.Count; + currentCompletionID = suggestions[suggestionIndex].Item2; - SuggestionTagger tagger = GetTagger(); - - int lineN, characterN; - int res = _textViewAdapter.GetCaretPos(out lineN, out characterN); + SuggestionTagger tagger = GetTagger(); - if (res != VSConstants.S_OK) - { - suggestionIndex = oldSuggestion; - currentCompletionID = suggestions[suggestionIndex].Item2; - return; - } + int lineN, characterN; + int res = _textViewAdapter.GetCaretPos(out lineN, out characterN); - bool validSuggestion = tagger.SetSuggestion(suggestions[suggestionIndex].Item1, characterN); - if (!validSuggestion) - { - suggestionIndex = oldSuggestion; - currentCompletionID = suggestions[suggestionIndex].Item2; + if (res != VSConstants.S_OK) + { + suggestionIndex = oldSuggestion; + currentCompletionID = suggestions[suggestionIndex].Item2; + return; + } + + bool validSuggestion = tagger.SetSuggestion(suggestions[suggestionIndex].Item1, characterN); + if (!validSuggestion) + { + suggestionIndex = oldSuggestion; + currentCompletionID = suggestions[suggestionIndex].Item2; - tagger.SetSuggestion(suggestions[suggestionIndex].Item1, characterN); + tagger.SetSuggestion(suggestions[suggestionIndex].Item1, characterN); + } } } + catch (Exception ex) + { + ThreadHelper.JoinableTaskFactory + .RunAsync(async delegate { await CodeiumVSPackage.Instance.LogAsync(ex.ToString()); }) + .FireAndForget(true); + + } + } public bool CompleteSuggestion(bool checkLine = true) @@ -487,7 +521,6 @@ public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pv CheckSuggestionUpdate(nCmdID); - // make a copy of this so we can look at it after forwarding some commands uint commandID = nCmdID; char typedChar = char.MinValue; @@ -590,16 +623,20 @@ internal ICompletionBroker CompletionBroker { [Import] internal ITextDocumentFactoryService documentFactory = null; - public void VsTextViewCreated(IVsTextView textViewAdapter) - { - ITextView textView = AdapterService.GetWpfTextView(textViewAdapter); - if (textView == null) return; + internal static TextViewListener? Instance { get; private set; } - Func createCommandHandler = delegate() - { - return new CodeiumCompletionHandler(textViewAdapter, textView, this); - }; - textView.TextBuffer.Properties.GetOrCreateSingletonProperty(typeof(CodeiumCompletionHandler), createCommandHandler); - } + public Dictionary documentDictionary = new Dictionary(); + public void VsTextViewCreated(IVsTextView textViewAdapter) + { + Instance = this; + ITextView textView = AdapterService.GetWpfTextView(textViewAdapter); + if (textView == null) return; + + Func createCommandHandler = delegate() + { + return new CodeiumCompletionHandler(textViewAdapter, textView, this); + }; + textView.TextBuffer.Properties.GetOrCreateSingletonProperty(typeof(CodeiumCompletionHandler), createCommandHandler); } } +} diff --git a/CodeiumVS/VSCommandTable.cs b/CodeiumVS/VSCommandTable.cs index 3ad52fa..6e3afc2 100644 --- a/CodeiumVS/VSCommandTable.cs +++ b/CodeiumVS/VSCommandTable.cs @@ -36,6 +36,10 @@ internal sealed partial class PackageIds public const int GenerateFunctionUnitTest = 0x0107; public const int ShowNextSuggestion = 0x0108; public const int CompleteSuggestion = 0x0109; + public const int RefactorSelectionCodeBlock = 0x0110; + public const int ExplainSelectionCodeBlock = 0x0111; + public const int GenerateSelectionFunctionDocstring = 0x0112; public const int DebugButton = 0x0600; + } } diff --git a/CodeiumVS/VSCommandTable.vsct b/CodeiumVS/VSCommandTable.vsct index 2557689..631be90 100644 --- a/CodeiumVS/VSCommandTable.vsct +++ b/CodeiumVS/VSCommandTable.vsct @@ -157,6 +157,38 @@ Codeium.CompleteSuggestion + + + + + + + + +