static async Task <LSP.TextEdit> GenerateTextEdit( Document document, CompletionItem item, CompletionService completionService, SourceText?documentText, TextSpan?defaultSpan, LSP.Range?defaultRange, CancellationToken cancellationToken) { Contract.ThrowIfNull(documentText); Contract.ThrowIfNull(defaultSpan); Contract.ThrowIfNull(defaultRange); var completionChange = await completionService.GetChangeAsync( document, item, cancellationToken : cancellationToken).ConfigureAwait(false); var completionChangeSpan = completionChange.TextChange.Span; var textEdit = new LSP.TextEdit() { NewText = completionChange.TextChange.NewText ?? "", Range = completionChangeSpan == defaultSpan.Value ? defaultRange : ProtocolConversions.TextSpanToRange(completionChangeSpan, documentText), }; return(textEdit); }
static async Task AddTextEdit( Document document, CompletionItem item, LSP.CompletionItem lspItem, CompletionService completionService, SourceText documentText, TextSpan defaultSpan, bool itemDefaultsSupported, CancellationToken cancellationToken) { var completionChange = await completionService.GetChangeAsync( document, item, cancellationToken : cancellationToken).ConfigureAwait(false); var completionChangeSpan = completionChange.TextChange.Span; var newText = completionChange.TextChange.NewText ?? ""; if (itemDefaultsSupported && completionChangeSpan == defaultSpan) { // The span is the same as the default, we just need to store the new text as // the insert text so the client can create the text edit from it and the default range. lspItem.InsertText = newText; } else { var textEdit = new LSP.TextEdit() { NewText = newText, Range = ProtocolConversions.TextSpanToRange(completionChangeSpan, documentText), }; lspItem.TextEdit = textEdit; } }
public async System.Threading.Tasks.Task <object[]> TextDocumentFormattingName(JToken arg) { if (trace) { System.Console.Error.WriteLine("<-- TextDocumentFormatting"); System.Console.Error.WriteLine(arg.ToString()); } var request = arg.ToObject <DocumentFormattingParams>(); var document = CheckDoc(request.TextDocument.Uri); var new_list = new List <Microsoft.VisualStudio.LanguageServer.Protocol.TextEdit>(); var changes = LanguageServer.Module.Reformat(document); int count = 0; foreach (var delta in changes) { var new_edit = new Microsoft.VisualStudio.LanguageServer.Protocol.TextEdit(); new_edit.Range = new Microsoft.VisualStudio.LanguageServer.Protocol.Range(); var lcs = LanguageServer.Module.GetLineColumn(delta.range.Start.Value, document); var lce = LanguageServer.Module.GetLineColumn(delta.range.End.Value, document); new_edit.Range.Start = new Position(lcs.Item1, lcs.Item2); new_edit.Range.End = new Position(lce.Item1, lce.Item2); new_edit.NewText = delta.NewText; new_list.Add(new_edit); count++; } return(new_list.ToArray()); }
public async System.Threading.Tasks.Task <WorkspaceEdit> TextDocumentRenameName(JToken arg) { if (trace) { System.Console.Error.WriteLine("<-- TextDocumentRename"); System.Console.Error.WriteLine(arg.ToString()); } RenameParams request = arg.ToObject <RenameParams>(); Document document = CheckDoc(request.TextDocument.Uri); Position position = request.Position; int line = position.Line; int character = position.Character; int index = LanguageServer.Module.GetIndex(line, character, document); if (trace) { System.Console.Error.WriteLine("position index = " + index); (int, int)back = LanguageServer.Module.GetLineColumn(index, document); System.Console.Error.WriteLine("back to l,c = " + back.Item1 + "," + back.Item2); } string new_name = request.NewName; Dictionary <string, LanguageServer.TextEdit[]> changes = LanguageServer.Module.Rename(index, new_name, document); WorkspaceEdit edit = new WorkspaceEdit(); int count = 0; Dictionary <string, Microsoft.VisualStudio.LanguageServer.Protocol.TextEdit[]> edit_changes_array = new Dictionary <string, Microsoft.VisualStudio.LanguageServer.Protocol.TextEdit[]>(); foreach (KeyValuePair <string, LanguageServer.TextEdit[]> pair in changes) { string doc = pair.Key; Uri uri = new Uri(doc); LanguageServer.TextEdit[] val = pair.Value; List <Microsoft.VisualStudio.LanguageServer.Protocol.TextEdit> new_list = new List <Microsoft.VisualStudio.LanguageServer.Protocol.TextEdit>(); foreach (LanguageServer.TextEdit v in val) { Microsoft.VisualStudio.LanguageServer.Protocol.TextEdit new_edit = new Microsoft.VisualStudio.LanguageServer.Protocol.TextEdit { Range = new Microsoft.VisualStudio.LanguageServer.Protocol.Range() }; (int, int)lcs = LanguageServer.Module.GetLineColumn(v.range.Start.Value, document); (int, int)lce = LanguageServer.Module.GetLineColumn(v.range.End.Value, document); new_edit.Range.Start = new Position(lcs.Item1, lcs.Item2); new_edit.Range.End = new Position(lce.Item1, lce.Item2); new_edit.NewText = v.NewText; new_list.Add(new_edit); count++; } edit_changes_array.Add(uri.ToString(), new_list.ToArray()); } edit.Changes = edit_changes_array; return(edit); }
// Internal for testing internal static async Task <LSP.TextEdit> GenerateTextEditAsync( Document document, CompletionService completionService, CompletionItem selectedItem, bool snippetsSupported, CancellationToken cancellationToken ) { var documentText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); var completionChange = await completionService .GetChangeAsync(document, selectedItem, cancellationToken : cancellationToken) .ConfigureAwait(false); var completionChangeSpan = completionChange.TextChange.Span; var newText = completionChange.TextChange.NewText; Contract.ThrowIfNull(newText); // If snippets are supported, that means we can move the caret (represented by $0) to // a new location. if (snippetsSupported) { var caretPosition = completionChange.NewPosition; if (caretPosition.HasValue) { // caretPosition is the absolute position of the caret in the document. // We want the position relative to the start of the snippet. var relativeCaretPosition = caretPosition.Value - completionChangeSpan.Start; // The caret could technically be placed outside the bounds of the text // being inserted. This situation is currently unsupported in LSP, so in // these cases we won't move the caret. if (relativeCaretPosition >= 0 && relativeCaretPosition <= newText.Length) { newText = newText.Insert(relativeCaretPosition, "$0"); } } } var textEdit = new LSP.TextEdit() { NewText = newText, Range = ProtocolConversions.TextSpanToRange(completionChangeSpan, documentText), }; return(textEdit); }
public async Task TestCodeActionResolveHandlerAsync() { var initialMarkup = @"class A { void M() { {|caret:|}int i = 1; } }"; using var testLspServer = CreateTestLspServer(initialMarkup, out var locations); var unresolvedCodeAction = CodeActionsTests.CreateCodeAction( title: CSharpAnalyzersResources.Use_implicit_type, kind: CodeActionKind.Refactor, children: Array.Empty <LSP.VSCodeAction>(), data: CreateCodeActionResolveData( CSharpAnalyzersResources.Use_implicit_type, locations["caret"].Single() ), priority: PriorityLevel.Low, groupName: "Roslyn1", applicableRange: new LSP.Range { Start = new Position { Line = 4, Character = 8 }, End = new Position { Line = 4, Character = 11 } }, diagnostics: null ); // Expected text after edit: // class A // { // void M() // { // var i = 1; // } // } var expectedTextEdits = new LSP.TextEdit[] { GenerateTextEdit( "var", new LSP.Range { Start = new Position(4, 8), End = new Position(4, 11) } ) }; var expectedResolvedAction = CodeActionsTests.CreateCodeAction( title: CSharpAnalyzersResources.Use_implicit_type, kind: CodeActionKind.Refactor, children: Array.Empty <LSP.VSCodeAction>(), data: CreateCodeActionResolveData( CSharpAnalyzersResources.Use_implicit_type, locations["caret"].Single() ), priority: PriorityLevel.Low, groupName: "Roslyn1", diagnostics: null, applicableRange: new LSP.Range { Start = new Position { Line = 4, Character = 8 }, End = new Position { Line = 4, Character = 11 } }, edit: GenerateWorkspaceEdit(locations, expectedTextEdits) ); var actualResolvedAction = await RunGetCodeActionResolveAsync( testLspServer, unresolvedCodeAction ); AssertJsonEquals(expectedResolvedAction, actualResolvedAction); }
public async Task TestCodeActionResolveHandlerAsync_NestedAction() { var initialMarkup = @"class A { void M() { int {|caret:|}i = 1; } }"; using var testLspServer = CreateTestLspServer(initialMarkup, out var locations); var unresolvedCodeAction = CodeActionsTests.CreateCodeAction( title: string.Format(FeaturesResources.Introduce_constant_for_0, "1"), kind: CodeActionKind.Refactor, children: Array.Empty <LSP.VSCodeAction>(), data: CreateCodeActionResolveData( FeaturesResources.Introduce_constant + "|" + string.Format(FeaturesResources.Introduce_constant_for_0, "1"), locations["caret"].Single() ), priority: PriorityLevel.Normal, groupName: "Roslyn2", applicableRange: new LSP.Range { Start = new Position { Line = 4, Character = 8 }, End = new Position { Line = 4, Character = 11 } }, diagnostics: null ); // Expected text after edits: // class A // { // private const int V = 1; // // void M() // { // int i = V; // } // } var expectedTextEdits = new LSP.TextEdit[] { GenerateTextEdit( @"private const int V = 1; ", new LSP.Range { Start = new Position(2, 4), End = new Position(2, 4) } ), GenerateTextEdit( "V", new LSP.Range { Start = new Position(4, 16), End = new Position(4, 17) } ) }; var expectedResolvedAction = CodeActionsTests.CreateCodeAction( title: string.Format(FeaturesResources.Introduce_constant_for_0, "1"), kind: CodeActionKind.Refactor, children: Array.Empty <LSP.VSCodeAction>(), data: CreateCodeActionResolveData( FeaturesResources.Introduce_constant + "|" + string.Format(FeaturesResources.Introduce_constant_for_0, "1"), locations["caret"].Single() ), priority: PriorityLevel.Normal, groupName: "Roslyn2", applicableRange: new LSP.Range { Start = new Position { Line = 4, Character = 8 }, End = new Position { Line = 4, Character = 11 } }, diagnostics: null, edit: GenerateWorkspaceEdit(locations, expectedTextEdits) ); var actualResolvedAction = await RunGetCodeActionResolveAsync( testLspServer, unresolvedCodeAction ); AssertJsonEquals(expectedResolvedAction, actualResolvedAction); }