public async Task <object> HandleAsync(LSP.TextDocumentPositionParams request, RequestContext <Solution> requestContext, CancellationToken cancellationToken) { var document = _solutionProvider.GetDocument(request.TextDocument); if (document == null) { return(Array.Empty <LSP.Location>()); } var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false); var locations = await GetDefinitionsWithFindUsagesServiceAsync(document, position, cancellationToken).ConfigureAwait(false); // No definition found - see if we can get metadata as source but that's only applicable for C#\VB. if ((locations.Length == 0) && document.SupportsSemanticModel && this._metadataAsSourceService != null) { var symbol = await SymbolFinder.FindSymbolAtPositionAsync(document, position, cancellationToken).ConfigureAwait(false); if (symbol?.Locations.FirstOrDefault().IsInMetadata == true) { var declarationFile = await this._metadataAsSourceService.GetGeneratedFileAsync(document.Project, symbol, false, cancellationToken).ConfigureAwait(false); var linePosSpan = declarationFile.IdentifierLocation.GetLineSpan().Span; return(new LSP.Location[] { new LSP.Location { Uri = new Uri(declarationFile.FilePath), Range = ProtocolConversions.LinePositionToRange(linePosSpan) } }); } } return(locations.ToArray()); }
public override async Task <CompletionList?> HandleRequestAsync(CompletionParams request, RequestContext context, CancellationToken cancellationToken) { if (request.Context is VSInternalCompletionContext completionContext && completionContext.InvokeKind == VSInternalCompletionInvokeKind.Deletion) { // Don't trigger completions on backspace. return(null); } var document = context.Document; if (document == null) { return(null); } var completionService = document.Project.LanguageServices.GetRequiredService <IXamlCompletionService>(); var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); var offset = text.Lines.GetPosition(ProtocolConversions.PositionToLinePosition(request.Position)); var completionResult = await completionService.GetCompletionsAsync(new XamlCompletionContext(document, offset, request.Context?.TriggerCharacter?.FirstOrDefault() ?? '\0'), cancellationToken : cancellationToken).ConfigureAwait(false); if (completionResult?.Completions == null) { return(null); } var commitCharactersCache = new Dictionary <XamlCompletionKind, ImmutableArray <VSInternalCommitCharacter> >(); return(new VSInternalCompletionList { Items = completionResult.Completions.Select(c => CreateCompletionItem(c, document.Id, text, request.Position, request.TextDocument, commitCharactersCache)).ToArray(), SuggestionMode = false, }); }
public override async Task <Hover?> HandleRequestAsync(TextDocumentPositionParams request, RequestContext context, CancellationToken cancellationToken) { var document = context.Document; if (document == null) { return(null); } var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false); var quickInfoService = document.Project.LanguageServices.GetRequiredService <QuickInfoService>(); var info = await quickInfoService.GetQuickInfoAsync(document, position, cancellationToken).ConfigureAwait(false); if (info == null) { return(null); } var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); // TODO - Switch to markup content once it supports classifications. // https://devdiv.visualstudio.com/DevDiv/_workitems/edit/918138 return(new VSHover { Range = ProtocolConversions.TextSpanToRange(info.Span, text), Contents = new SumType <SumType <string, MarkedString>, SumType <string, MarkedString>[], MarkupContent>(string.Empty), // Build the classified text without navigation actions - they are not serializable. RawContent = await IntellisenseQuickInfoBuilder.BuildContentWithoutNavigationActionsAsync(info, document, cancellationToken).ConfigureAwait(false) }); }
public override async Task <LSP.ReferenceItem[]?> HandleRequestAsync(ReferenceParams referenceParams, RequestContext context, CancellationToken cancellationToken) { Debug.Assert(context.ClientCapabilities.HasVisualStudioLspCapability()); var document = context.Document; if (document == null) { return(Array.Empty <LSP.VSReferenceItem>()); } using var progress = BufferedProgress.Create <VSReferenceItem>(referenceParams.PartialResultToken); var findUsagesService = document.GetRequiredLanguageService <IFindUsagesLSPService>(); var position = await document.GetPositionFromLinePositionAsync( ProtocolConversions.PositionToLinePosition(referenceParams.Position), cancellationToken).ConfigureAwait(false); var findUsagesContext = new FindUsagesLSPContext( progress, document, position, _metadataAsSourceFileService, cancellationToken); // Finds the references for the symbol at the specific position in the document, reporting them via streaming to the LSP client. await findUsagesService.FindReferencesAsync(document, position, findUsagesContext).ConfigureAwait(false); await findUsagesContext.OnCompletedAsync().ConfigureAwait(false); return(progress.GetValues()); }
public override async Task <LSP.VSReferenceItem[]> HandleRequestAsync( ReferenceParams referenceParams, ClientCapabilities clientCapabilities, string?clientName, CancellationToken cancellationToken) { Debug.Assert(clientCapabilities.HasVisualStudioLspCapability()); var document = SolutionProvider.GetDocument(referenceParams.TextDocument, clientName); if (document == null) { return(Array.Empty <LSP.VSReferenceItem>()); } var findUsagesService = document.GetRequiredLanguageService <IFindUsagesLSPService>(); var position = await document.GetPositionFromLinePositionAsync( ProtocolConversions.PositionToLinePosition(referenceParams.Position), cancellationToken).ConfigureAwait(false); var context = new FindUsagesLSPContext(document, position, _metadataAsSourceFileService, cancellationToken); // Finds the references for the symbol at the specific position in the document, reporting them via streaming to the LSP client. // TODO: Change back FAR to use streaming once the following LSP bug is fixed: // https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1094786/ await findUsagesService.FindReferencesAsync(document, position, context).ConfigureAwait(false); return(context.GetReferences().ToArray()); }
public override async Task <VSInternalInlineCompletionList?> HandleRequestAsync(VSInternalInlineCompletionRequest request, RequestContext context, CancellationToken cancellationToken) { Contract.ThrowIfNull(context.Document); // First get available snippets if any. var snippetInfoService = context.Document.Project.GetRequiredLanguageService <ISnippetInfoService>(); var snippetInfo = snippetInfoService.GetSnippetsIfAvailable(); if (!snippetInfo.Any()) { return(null); } // Then attempt to get the word at the requested position. var sourceText = await context.Document.GetTextAsync(cancellationToken).ConfigureAwait(false); var syntaxFactsService = context.Document.Project.GetRequiredLanguageService <ISyntaxFactsService>(); var linePosition = ProtocolConversions.PositionToLinePosition(request.Position); var position = sourceText.Lines.GetPosition(linePosition); if (!SnippetUtilities.TryGetWordOnLeft(position, sourceText, syntaxFactsService, out var wordOnLeft)) { return(null); } // Find the snippet with shortcut text that matches the typed word. var wordText = sourceText.GetSubText(wordOnLeft.Value).ToString(); if (!BuiltInSnippets.Contains(wordText, StringComparer.OrdinalIgnoreCase)) { return(null); } var matchingSnippetInfo = snippetInfo.First(s => wordText.Equals(s.Shortcut, StringComparison.OrdinalIgnoreCase)); var parsedSnippet = _xmlSnippetParser.GetParsedXmlSnippet(matchingSnippetInfo, context); if (parsedSnippet == null) { return(null); } // Use the formatting options specified by the client to format the snippet. var formattingOptions = await ProtocolConversions.GetFormattingOptionsAsync(request.Options, context.Document, cancellationToken).ConfigureAwait(false); var formattedLspSnippet = await GetFormattedLspSnippetAsync(parsedSnippet, wordOnLeft.Value, context.Document, sourceText, formattingOptions, cancellationToken).ConfigureAwait(false); return(new VSInternalInlineCompletionList { Items = new VSInternalInlineCompletionItem[] { new VSInternalInlineCompletionItem { Range = ProtocolConversions.TextSpanToRange(wordOnLeft.Value, sourceText), Text = formattedLspSnippet, TextFormat = InsertTextFormat.Snippet, } } }); }
public override async Task <CompletionList?> HandleRequestAsync(CompletionParams request, RequestContext context, CancellationToken cancellationToken) { var document = context.Document; if (document == null) { return(null); } var completionService = document.Project.LanguageServices.GetRequiredService <IXamlCompletionService>(); var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); var offset = text.Lines.GetPosition(ProtocolConversions.PositionToLinePosition(request.Position)); var completionResult = await completionService.GetCompletionsAsync(new XamlCompletionContext(document, offset, request.Context?.TriggerCharacter?.FirstOrDefault() ?? '\0'), cancellationToken : cancellationToken).ConfigureAwait(false); if (completionResult?.Completions == null) { return(null); } return(new VSCompletionList { Items = completionResult.Completions.Select(c => CreateCompletionItem(c, document.Id, text, request.Position)).ToArray(), SuggestionMode = false, }); }
public async Task <DocumentHighlight[]> HandleRequestAsync(Solution solution, TextDocumentPositionParams request, ClientCapabilities clientCapabilities, CancellationToken cancellationToken) { var document = solution.GetDocumentFromURI(request.TextDocument.Uri); if (document == null) { return(Array.Empty <DocumentHighlight>()); } var documentHighlightService = document.Project.LanguageServices.GetService <IDocumentHighlightsService>(); var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false); var highlights = await documentHighlightService.GetDocumentHighlightsAsync( document, position, ImmutableHashSet.Create(document), cancellationToken).ConfigureAwait(false); if (!highlights.IsDefaultOrEmpty) { // LSP requests are only for a single document. So just get the highlights for the requested document. var highlightsForDocument = highlights.FirstOrDefault(h => h.Document.Id == document.Id); var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); return(highlightsForDocument.HighlightSpans.Select(h => new DocumentHighlight { Range = ProtocolConversions.TextSpanToRange(h.TextSpan, text), Kind = ProtocolConversions.HighlightSpanKindToDocumentHighlightKind(h.Kind), }).ToArray()); } return(Array.Empty <DocumentHighlight>()); }
public override async Task <DocumentHighlight[]?> HandleRequestAsync(TextDocumentPositionParams request, RequestContext context, CancellationToken cancellationToken) { var document = context.Document; if (document == null) { return(null); } var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false); // First check if this is a keyword that needs highlighting. var keywordHighlights = await GetKeywordHighlightsAsync(document, text, position, cancellationToken).ConfigureAwait(false); if (keywordHighlights.Any()) { return(keywordHighlights.ToArray()); } // Not a keyword, check if it is a reference that needs highlighting. var referenceHighlights = await GetReferenceHighlightsAsync(document, text, position, cancellationToken).ConfigureAwait(false); if (referenceHighlights.Any()) { return(referenceHighlights.ToArray()); } // No keyword or references to highlight at this location. return(Array.Empty <DocumentHighlight>()); }
public async Task <Hover> HandleRequestAsync(Solution solution, TextDocumentPositionParams request, ClientCapabilities clientCapabilities, CancellationToken cancellationToken) { var document = solution.GetDocumentFromURI(request.TextDocument.Uri); if (document == null) { return(null); } var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false); var quickInfoService = document.Project.LanguageServices.GetService <QuickInfoService>(); var info = await quickInfoService.GetQuickInfoAsync(document, position, cancellationToken).ConfigureAwait(false); if (info == null) { return(null); } var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); return(new Hover { Range = ProtocolConversions.TextSpanToRange(info.Span, text), Contents = new MarkupContent { Kind = MarkupKind.Markdown, Value = GetMarkdownString(info) } });
public override async Task <TextEdit[]> HandleRequestAsync(DocumentOnTypeFormattingParams request, RequestContext context, CancellationToken cancellationToken) { var edits = new ArrayBuilder <TextEdit>(); if (string.IsNullOrEmpty(request.Character)) { return(edits.ToArrayAndFree()); } var document = context.Document; var formattingService = document?.Project.LanguageServices.GetService <IXamlFormattingService>(); if (document != null && formattingService != null) { var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false); var options = new XamlFormattingOptions { InsertSpaces = request.Options.InsertSpaces, TabSize = request.Options.TabSize, OtherOptions = request.Options.OtherOptions }; var textChanges = await formattingService.GetFormattingChangesAsync(document, options, request.Character[0], position, cancellationToken).ConfigureAwait(false); if (textChanges != null) { var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); edits.AddRange(textChanges.Select(change => ProtocolConversions.TextChangeToTextEdit(change, text))); } } return(edits.ToArrayAndFree()); }
public override async Task <LSP.CompletionItem[]> HandleRequestAsync(LSP.CompletionParams request, LSP.ClientCapabilities clientCapabilities, string?clientName, CancellationToken cancellationToken) { var document = SolutionProvider.GetDocument(request.TextDocument, clientName); if (document == null) { return(Array.Empty <LSP.CompletionItem>()); } var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false); // Filter out unimported types for now as there are two issues with providing them: // 1. LSP client does not currently provide a way to provide detail text on the completion item to show the namespace. // https://dev.azure.com/devdiv/DevDiv/_workitems/edit/1076759 // 2. We need to figure out how to provide the text edits along with the completion item or provide them in the resolve request. // https://devdiv.visualstudio.com/DevDiv/_workitems/edit/985860/ // 3. LSP client should support completion filters / expanders var documentOptions = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); var completionOptions = documentOptions .WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, false) .WithChangedOption(CompletionServiceOptions.IsExpandedCompletion, false); var completionService = document.Project.LanguageServices.GetRequiredService <CompletionService>(); var list = await completionService.GetCompletionsAsync(document, position, options : completionOptions, cancellationToken : cancellationToken).ConfigureAwait(false); if (list == null) { return(Array.Empty <LSP.CompletionItem>()); } var lspVSClientCapability = clientCapabilities?.HasVisualStudioLspCapability() == true; return(list.Items.Select(item => CreateLSPCompletionItem(request, item, lspVSClientCapability)).ToArray());
public override async Task <LSP.VSReferenceItem[]> HandleRequestAsync(ReferenceParams referenceParams, RequestContext context, CancellationToken cancellationToken) { Debug.Assert(context.ClientCapabilities.HasVisualStudioLspCapability()); var document = SolutionProvider.GetDocument(referenceParams.TextDocument, context.ClientName); if (document == null) { return(Array.Empty <LSP.VSReferenceItem>()); } var findUsagesService = document.GetRequiredLanguageService <IFindUsagesLSPService>(); var position = await document.GetPositionFromLinePositionAsync( ProtocolConversions.PositionToLinePosition(referenceParams.Position), cancellationToken).ConfigureAwait(false); var findUsagesContext = new FindUsagesLSPContext( referenceParams.PartialResultToken, document, position, _metadataAsSourceFileService, cancellationToken); // Finds the references for the symbol at the specific position in the document, reporting them via streaming to the LSP client. await findUsagesService.FindReferencesAsync(document, position, findUsagesContext).ConfigureAwait(false); await findUsagesContext.OnCompletedAsync().ConfigureAwait(false); // The results have already been reported to the client, so we don't need to return anything here. return(Array.Empty <LSP.VSReferenceItem>()); }
public override async Task <LSP.Location[]> HandleRequestAsync(LSP.TextDocumentPositionParams request, RequestContext context, CancellationToken cancellationToken) { var document = context.Document; Contract.ThrowIfNull(document); var locations = ArrayBuilder <LSP.Location> .GetInstance(); var findUsagesService = document.GetRequiredLanguageService <IFindUsagesService>(); var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false); var findUsagesContext = new SimpleFindUsagesContext(); await FindImplementationsAsync(findUsagesService, document, position, findUsagesContext, cancellationToken).ConfigureAwait(false); foreach (var definition in findUsagesContext.GetDefinitions()) { var text = definition.GetClassifiedText(); foreach (var sourceSpan in definition.SourceSpans) { if (context.ClientCapabilities?.HasVisualStudioLspCapability() == true) { locations.AddIfNotNull(await ProtocolConversions.DocumentSpanToLocationWithTextAsync(sourceSpan, text, cancellationToken).ConfigureAwait(false)); } else { locations.AddIfNotNull(await ProtocolConversions.DocumentSpanToLocationAsync(sourceSpan, cancellationToken).ConfigureAwait(false)); } } } return(locations.ToArrayAndFree()); }
public async Task <object[]> HandleAsync(LSP.ReferenceParams request, RequestContext <Solution> requestContext, CancellationToken cancellationToken) { var locations = ArrayBuilder <LSP.Location> .GetInstance(); var solution = requestContext.Context; var document = solution.GetDocumentFromURI(request.TextDocument.Uri); if (document == null) { return(locations.ToArrayAndFree()); } var findUsagesService = document.Project.LanguageServices.GetService <IFindUsagesService>(); var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false); var context = new SimpleFindUsagesContext(cancellationToken); // Roslyn calls into third party extensions to compute reference results and needs to be on the UI thread to compute results. // This is not great for us and ideally we should ask for a Roslyn API where we can make this call without blocking the UI. if (VsTaskLibraryHelper.ServiceInstance != null) { await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); } await findUsagesService.FindReferencesAsync(document, position, context).ConfigureAwait(false); if (requestContext?.ClientCapabilities?.HasVisualStudioLspCapability() == true) { return(await GetReferenceGroupsAsync(request, context, cancellationToken).ConfigureAwait(false)); } else { return(await GetLocationsAsync(request, context, cancellationToken).ConfigureAwait(false)); } }
public override async Task <LSP.CompletionItem> HandleRequestAsync(LSP.CompletionItem completionItem, RequestContext context, CancellationToken cancellationToken) { CompletionResolveData data; if (completionItem.Data is CompletionResolveData) { data = (CompletionResolveData)completionItem.Data; } else { data = ((JToken)completionItem.Data).ToObject <CompletionResolveData>(); } var document = SolutionProvider.GetDocument(data.TextDocument, context.ClientName); if (document == null) { return(completionItem); } var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(data.Position), cancellationToken).ConfigureAwait(false); var completionService = document.Project.LanguageServices.GetRequiredService <CompletionService>(); var list = await completionService.GetCompletionsAsync(document, position, cancellationToken : cancellationToken).ConfigureAwait(false); if (list == null) { return(completionItem); } var selectedItem = list.Items.FirstOrDefault(i => i.DisplayText == data.DisplayText); if (selectedItem == null) { return(completionItem); } var description = await completionService.GetDescriptionAsync(document, selectedItem, cancellationToken).ConfigureAwait(false); var lspVSClientCapability = context.ClientCapabilities?.HasVisualStudioLspCapability() == true; LSP.CompletionItem resolvedCompletionItem; if (lspVSClientCapability) { resolvedCompletionItem = CloneVSCompletionItem(completionItem); ((LSP.VSCompletionItem)resolvedCompletionItem).Description = new ClassifiedTextElement(description.TaggedParts .Select(tp => new ClassifiedTextRun(tp.Tag.ToClassificationTypeName(), tp.Text))); } else { resolvedCompletionItem = RoslynCompletionItem.From(completionItem); ((RoslynCompletionItem)resolvedCompletionItem).Description = description.TaggedParts.Select( tp => new RoslynTaggedText { Tag = tp.Tag, Text = tp.Text }).ToArray(); } resolvedCompletionItem.Detail = description.TaggedParts.GetFullText(); return(resolvedCompletionItem); }
private static async Task <LSP.VSInternalDocumentOnAutoInsertResponseItem?> GetDocumentationCommentResponseAsync( LSP.VSInternalDocumentOnAutoInsertParams autoInsertParams, Document document, IDocumentationCommentSnippetService service, DocumentationCommentOptions options, CancellationToken cancellationToken) { var syntaxTree = await document.GetRequiredSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var sourceText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); var linePosition = ProtocolConversions.PositionToLinePosition(autoInsertParams.Position); var position = sourceText.Lines.GetPosition(linePosition); var result = autoInsertParams.Character == "\n" ? service.GetDocumentationCommentSnippetOnEnterTyped(syntaxTree, sourceText, position, options, cancellationToken) : service.GetDocumentationCommentSnippetOnCharacterTyped(syntaxTree, sourceText, position, options, cancellationToken); if (result == null) { return(null); } return(new LSP.VSInternalDocumentOnAutoInsertResponseItem { TextEditFormat = LSP.InsertTextFormat.Snippet, TextEdit = new LSP.TextEdit { NewText = result.SnippetText.Insert(result.CaretOffset, "$0"), Range = ProtocolConversions.TextSpanToRange(result.SpanToReplace, sourceText) } }); }
public async Task <LSP.CompletionItem> HandleRequestAsync(LSP.CompletionItem completionItem, RequestContext context, CancellationToken cancellationToken) { if (!(completionItem.Data is CompletionResolveData data)) { data = ((JToken)completionItem.Data).ToObject <CompletionResolveData>(); } var documentId = DocumentId.CreateFromSerialized(ProjectId.CreateFromSerialized(data.ProjectGuid), data.DocumentGuid); var document = context.Solution.GetAdditionalDocument(documentId); if (document == null) { return(completionItem); } int offset = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(data.Position), cancellationToken).ConfigureAwait(false); var completionService = document.Project.LanguageServices.GetRequiredService <IXamlCompletionService>(); var symbol = await completionService.GetSymbolAsync(new XamlCompletionContext(document, offset), completionItem.Label, cancellationToken : cancellationToken).ConfigureAwait(false); if (symbol == null) { return(completionItem); } var description = await symbol.GetDescriptionAsync(document, offset, cancellationToken).ConfigureAwait(false); var vsCompletionItem = CloneVSCompletionItem(completionItem); vsCompletionItem.Description = new ClassifiedTextElement(description.Select(tp => new ClassifiedTextRun(tp.Tag.ToClassificationTypeName(), tp.Text))); return(vsCompletionItem); }
public override async Task <LinkedEditingRanges?> HandleRequestAsync(LinkedEditingRangeParams request, RequestContext context, CancellationToken cancellationToken) { var document = context.Document; if (document == null) { return(null); } var renameService = document.Project.LanguageServices.GetService <IXamlTypeRenameService>(); if (renameService == null) { return(null); } var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); var offset = text.Lines.GetPosition(ProtocolConversions.PositionToLinePosition(request.Position)); var result = await renameService.GetTypeRenameAsync(document, offset, cancellationToken).ConfigureAwait(false); if (result == null) { return(null); } Contract.ThrowIfTrue(result.Ranges.IsDefault); return(new LinkedEditingRanges { Ranges = result.Ranges.Select(s => ProtocolConversions.TextSpanToRange(s, text)).ToArray(), WordPattern = result.WordPattern }); }
protected static async Task <LSP.VSCompletionItem> CreateCompletionItemAsync( string label, LSP.CompletionItemKind kind, string[] tags, LSP.CompletionParams request, Document document, bool preselect = false, ImmutableArray <char>?commitCharacters = null, LSP.TextEdit?textEdit = null, string?insertText = null, string?sortText = null, string?filterText = null, long resultId = 0 ) { var position = await document .GetPositionFromLinePositionAsync( ProtocolConversions.PositionToLinePosition(request.Position), CancellationToken.None ) .ConfigureAwait(false); var completionTrigger = await ProtocolConversions .LSPToRoslynCompletionTriggerAsync( request.Context, document, position, CancellationToken.None ) .ConfigureAwait(false); var item = new LSP.VSCompletionItem() { TextEdit = textEdit, InsertText = insertText, FilterText = filterText ?? label, Label = label, SortText = sortText ?? label, InsertTextFormat = LSP.InsertTextFormat.Plaintext, Kind = kind, Data = JObject.FromObject(new CompletionResolveData() { ResultId = resultId, }), Preselect = preselect }; if (tags != null) { item.Icon = tags.ToImmutableArray().GetFirstGlyph().GetImageElement(); } if (commitCharacters != null) { item.CommitCharacters = commitCharacters.Value.Select(c => c.ToString()).ToArray(); } return(item); }
protected async Task <LSP.Location[]> GetDefinitionAsync(Solution solution, LSP.TextDocumentPositionParams request, bool typeOnly, CancellationToken cancellationToken) { var locations = ArrayBuilder <LSP.Location> .GetInstance(); var document = solution.GetDocumentFromURI(request.TextDocument.Uri); if (document == null) { return(locations.ToArrayAndFree()); } var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false); var definitionService = document.Project.LanguageServices.GetService <IGoToDefinitionService>(); var definitions = await definitionService.FindDefinitionsAsync(document, position, cancellationToken).ConfigureAwait(false); if (definitions != null && definitions.Count() > 0) { foreach (var definition in definitions) { if (!ShouldInclude(definition, typeOnly)) { continue; } var definitionText = await definition.Document.GetTextAsync(cancellationToken).ConfigureAwait(false); locations.Add(new LSP.Location { Uri = definition.Document.GetURI(), Range = ProtocolConversions.TextSpanToRange(definition.SourceSpan, definitionText), }); } } else if (document.SupportsSemanticModel && _metadataAsSourceFileService != null) { // No definition found - see if we can get metadata as source but that's only applicable for C#\VB. var symbol = await SymbolFinder.FindSymbolAtPositionAsync(document, position, cancellationToken).ConfigureAwait(false); if (symbol != null && symbol.Locations != null && !symbol.Locations.IsEmpty && symbol.Locations.First().IsInMetadata) { if (!typeOnly || symbol is ITypeSymbol) { var declarationFile = await _metadataAsSourceFileService.GetGeneratedFileAsync(document.Project, symbol, false, cancellationToken).ConfigureAwait(false); var linePosSpan = declarationFile.IdentifierLocation.GetLineSpan().Span; locations.Add(new LSP.Location { Uri = new Uri(declarationFile.FilePath), Range = ProtocolConversions.LinePositionToRange(linePosSpan), }); } } } return(locations.ToArrayAndFree());
public async Task <Hover?> HandleRequestAsync(TextDocumentPositionParams request, RequestContext context, CancellationToken cancellationToken) { var document = context.Document; if (document == null) { return(null); } var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false); var quickInfoService = document.Project.LanguageServices.GetService <IXamlQuickInfoService>(); if (quickInfoService == null) { return(null); } var info = await quickInfoService.GetQuickInfoAsync(document, position, cancellationToken).ConfigureAwait(false); if (info == null) { return(null); } var descriptionBuilder = new List <TaggedText>(info.Description); if (info.Symbol != null) { var options = _globalOptions.GetSymbolDescriptionOptions(document.Project.Language); var description = await info.Symbol.GetDescriptionAsync(document, options, cancellationToken).ConfigureAwait(false); if (description.Any()) { if (descriptionBuilder.Any()) { descriptionBuilder.AddLineBreak(); } descriptionBuilder.AddRange(description); } } var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); return(new VSInternalHover { Range = ProtocolConversions.TextSpanToRange(info.Span, text), Contents = new MarkupContent { Kind = MarkupKind.Markdown, Value = GetMarkdownString(descriptionBuilder) }, RawContent = new ClassifiedTextElement(descriptionBuilder.Select(tp => new ClassifiedTextRun(tp.Tag.ToClassificationTypeName(), tp.Text))) });
protected async Task <LSP.Location[]?> GetDefinitionAsync(LSP.TextDocumentPositionParams request, bool typeOnly, RequestContext context, CancellationToken cancellationToken) { var document = context.Document; if (document == null) { return(null); } var locations = ArrayBuilder <LSP.Location> .GetInstance(); var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false); var findDefinitionService = document.GetRequiredLanguageService <IFindDefinitionService>(); var definitions = await findDefinitionService.FindDefinitionsAsync(document, position, cancellationToken).ConfigureAwait(false); if (definitions.Any()) { foreach (var definition in definitions) { if (!ShouldInclude(definition, typeOnly)) { continue; } var location = await ProtocolConversions.TextSpanToLocationAsync( definition.Document, definition.SourceSpan, definition.IsStale, cancellationToken).ConfigureAwait(false); locations.AddIfNotNull(location); } } else if (document.SupportsSemanticModel && _metadataAsSourceFileService != null) { // No definition found - see if we can get metadata as source but that's only applicable for C#\VB. var symbol = await SymbolFinder.FindSymbolAtPositionAsync(document, position, cancellationToken).ConfigureAwait(false); if (symbol != null && _metadataAsSourceFileService.IsNavigableMetadataSymbol(symbol)) { if (!typeOnly || symbol is ITypeSymbol) { var options = _globalOptions.GetMetadataAsSourceOptions(document.Project.LanguageServices); var declarationFile = await _metadataAsSourceFileService.GetGeneratedFileAsync(document.Project, symbol, signaturesOnly : false, options, cancellationToken).ConfigureAwait(false); var linePosSpan = declarationFile.IdentifierLocation.GetLineSpan().Span; locations.Add(new LSP.Location { Uri = new Uri(declarationFile.FilePath), Range = ProtocolConversions.LinePositionToRange(linePosSpan), }); } } } return(locations.ToArrayAndFree());
public override async Task <DocumentOnAutoInsertResponseItem?> HandleRequestAsync( DocumentOnAutoInsertParams request, RequestContext context, CancellationToken cancellationToken ) { var document = context.Document; if (document == null) { return(null); } var insertService = document.Project.LanguageServices.GetService <IXamlAutoInsertService>(); if (insertService == null) { return(null); } var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); var offset = text.Lines.GetPosition( ProtocolConversions.PositionToLinePosition(request.Position) ); var result = await insertService .GetAutoInsertAsync(document, request.Character[0], offset, cancellationToken) .ConfigureAwait(false); if (result == null) { return(null); } Contract.ThrowIfNull(result.TextChange.NewText); var insertText = result.TextChange.NewText; var insertFormat = InsertTextFormat.Plaintext; if (result.CaretOffset.HasValue) { insertFormat = InsertTextFormat.Snippet; insertText = insertText.Insert(result.CaretOffset.Value, "$0"); } return(new DocumentOnAutoInsertResponseItem { TextEditFormat = insertFormat, TextEdit = new TextEdit { NewText = insertText, Range = ProtocolConversions.TextSpanToRange(result.TextChange.Span, text) } }); }
public async Task <WorkspaceEdit> HandleRequestAsync(Solution solution, RenameParams request, ClientCapabilities clientCapabilities, CancellationToken cancellationToken) { WorkspaceEdit workspaceEdit = null; var document = solution.GetDocumentFromURI(request.TextDocument.Uri); if (document != null) { var renameService = document.Project.LanguageServices.GetService <IEditorInlineRenameService>(); var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false); var renameInfo = await renameService.GetRenameInfoAsync(document, position, cancellationToken).ConfigureAwait(false); if (!renameInfo.CanRename) { return(workspaceEdit); } var renameLocationSet = await renameInfo.FindRenameLocationsAsync(solution.Workspace.Options, cancellationToken).ConfigureAwait(false); var renameReplacementInfo = await renameLocationSet.GetReplacementsAsync(request.NewName, solution.Workspace.Options, cancellationToken).ConfigureAwait(false); var newSolution = renameReplacementInfo.NewSolution; var solutionChanges = newSolution.GetChanges(solution); var changedDocuments = solutionChanges .GetProjectChanges() .SelectMany(p => p.GetChangedDocuments(onlyGetDocumentsWithTextChanges: true)); var documentEdits = new ArrayBuilder <TextDocumentEdit>(); foreach (var docId in changedDocuments) { var oldDoc = solution.GetDocument(docId); var newDoc = newSolution.GetDocument(docId); var textChanges = await newDoc.GetTextChangesAsync(oldDoc, cancellationToken).ConfigureAwait(false); var oldText = await oldDoc.GetTextAsync(cancellationToken).ConfigureAwait(false); var textDocumentEdit = new TextDocumentEdit { TextDocument = new VersionedTextDocumentIdentifier { Uri = newDoc.GetURI() }, Edits = textChanges.Select(tc => ProtocolConversions.TextChangeToTextEdit(tc, oldText)).ToArray() }; documentEdits.Add(textDocumentEdit); } workspaceEdit = new WorkspaceEdit { DocumentChanges = documentEdits.ToArrayAndFree() }; } return(workspaceEdit); }
public async Task <LSP.DocumentOnAutoInsertResponseItem[]> HandleRequestAsync(LSP.DocumentOnAutoInsertParams autoInsertParams, RequestContext context, CancellationToken cancellationToken) { using var _ = ArrayBuilder <LSP.DocumentOnAutoInsertResponseItem> .GetInstance(out var response); var document = context.Document; if (document == null) { return(response.ToArray()); } var insertService = document.Project.LanguageServices.GetService <IXamlAutoInsertService>(); if (insertService == null) { return(response.ToArray()); } var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); var offset = text.Lines.GetPosition(ProtocolConversions.PositionToLinePosition(autoInsertParams.Position)); var result = await insertService.GetAutoInsertAsync(document, autoInsertParams.Character[0], offset, cancellationToken).ConfigureAwait(false); if (result == null) { return(response.ToArray()); } Contract.ThrowIfNull(result.TextChange.NewText); var insertText = result.TextChange.NewText; var insertFormat = LSP.InsertTextFormat.Plaintext; if (result.CaretOffset.HasValue) { insertFormat = LSP.InsertTextFormat.Snippet; insertText = insertText.Insert(result.CaretOffset.Value, "$0"); } response.Add(new LSP.DocumentOnAutoInsertResponseItem { TextEditFormat = insertFormat, TextEdit = new LSP.TextEdit { NewText = insertText, Range = ProtocolConversions.TextSpanToRange(result.TextChange.Span, text) } }); return(response.ToArray()); }
public override async Task <LSP.DocumentOnAutoInsertResponseItem[]> HandleRequestAsync(LSP.DocumentOnAutoInsertParams autoInsertParams, RequestContext context, CancellationToken cancellationToken) { using var _ = ArrayBuilder <LSP.DocumentOnAutoInsertResponseItem> .GetInstance(out var response); var document = SolutionProvider.GetDocument(autoInsertParams.TextDocument, context.ClientName); if (document == null) { return(response.ToArray()); } var service = document.GetRequiredLanguageService <IDocumentationCommentSnippetService>(); // The editor calls this handler for C# and VB comment characters, but we only need to process the one for the language that matches the document if (autoInsertParams.Character != "\n" && autoInsertParams.Character != service.DocumentationCommentCharacter) { return(response.ToArray()); } var syntaxTree = await document.GetRequiredSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var sourceText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); var linePosition = ProtocolConversions.PositionToLinePosition(autoInsertParams.Position); var position = sourceText.Lines.GetPosition(linePosition); var result = autoInsertParams.Character == "\n" ? service.GetDocumentationCommentSnippetOnEnterTyped(syntaxTree, sourceText, position, options, cancellationToken) : service.GetDocumentationCommentSnippetOnCharacterTyped(syntaxTree, sourceText, position, options, cancellationToken); if (result == null) { return(response.ToArray()); } response.Add(new LSP.DocumentOnAutoInsertResponseItem { TextEditFormat = LSP.InsertTextFormat.Snippet, TextEdit = new LSP.TextEdit { NewText = result.SnippetText.Insert(result.CaretOffset, "$0"), Range = ProtocolConversions.TextSpanToRange(result.SpanToReplace, sourceText) } }); return(response.ToArray()); }
public async Task <WorkspaceEdit?> HandleRequestAsync(RenameParams request, RequestContext context, CancellationToken cancellationToken) { var document = context.Document; Contract.ThrowIfNull(document); var oldSolution = document.Project.Solution; var renameService = document.Project.LanguageServices.GetRequiredService <IEditorInlineRenameService>(); var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false); var renameInfo = await renameService.GetRenameInfoAsync(document, position, cancellationToken).ConfigureAwait(false); if (!renameInfo.CanRename) { return(null); } var options = new SymbolRenameOptions( RenameOverloads: false, RenameInStrings: false, RenameInComments: false, RenameFile: false); var renameLocationSet = await renameInfo.FindRenameLocationsAsync(options, cancellationToken).ConfigureAwait(false); var renameReplacementInfo = await renameLocationSet.GetReplacementsAsync(request.NewName, options, cancellationToken).ConfigureAwait(false); var renamedSolution = renameReplacementInfo.NewSolution; var solutionChanges = renamedSolution.GetChanges(oldSolution); // Linked files can correspond to multiple roslyn documents each with changes. Merge the changes in the linked files so that all linked documents have the same text. // Then we can just take the text changes from the first document to avoid returning duplicate edits. renamedSolution = await renamedSolution.WithMergedLinkedFileChangesAsync(oldSolution, solutionChanges, cancellationToken : cancellationToken).ConfigureAwait(false); solutionChanges = renamedSolution.GetChanges(oldSolution); var changedDocuments = solutionChanges .GetProjectChanges() .SelectMany(p => p.GetChangedDocuments(onlyGetDocumentsWithTextChanges: true)) .GroupBy(docId => renamedSolution.GetRequiredDocument(docId).FilePath, StringComparer.OrdinalIgnoreCase).Select(group => group.First()); var textDiffService = renamedSolution.Services.GetRequiredService <IDocumentTextDifferencingService>(); var documentEdits = await ProtocolConversions.ChangedDocumentsToTextDocumentEditsAsync(changedDocuments, renamedSolution.GetRequiredDocument, oldSolution.GetRequiredDocument, textDiffService, cancellationToken).ConfigureAwait(false); return(new WorkspaceEdit { DocumentChanges = documentEdits }); }
protected static string ApplyTextEdits(LSP.TextEdit[] edits, SourceText originalMarkup) { var text = originalMarkup; foreach (var edit in edits) { var lines = text.Lines; var startPosition = ProtocolConversions.PositionToLinePosition(edit.Range.Start); var endPosition = ProtocolConversions.PositionToLinePosition(edit.Range.End); var textSpan = lines.GetTextSpan(new LinePositionSpan(startPosition, endPosition)); text = text.Replace(textSpan, edit.NewText); } return(text.ToString()); }
public override async Task <TextEdit[]?> HandleRequestAsync( DocumentOnTypeFormattingParams request, RequestContext context, CancellationToken cancellationToken) { var document = context.Document; if (document == null) { return(null); } var edits = new ArrayBuilder <TextEdit>(); var formattingService = document.Project.LanguageServices.GetRequiredService <IFormattingInteractionService>(); var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false); if (string.IsNullOrEmpty(request.Character)) { return(edits.ToArrayAndFree()); } // We should use the options passed in by LSP instead of the document's options. var documentOptions = await ProtocolConversions.FormattingOptionsToDocumentOptionsAsync( request.Options, document, cancellationToken).ConfigureAwait(false); IList <TextChange>?textChanges; if (SyntaxFacts.IsNewLine(request.Character[0])) { textChanges = await GetFormattingChangesOnReturnAsync( formattingService, document, position, documentOptions, cancellationToken).ConfigureAwait(false); } else { textChanges = await GetFormattingChangesAsync( formattingService, document, request.Character[0], position, documentOptions, cancellationToken).ConfigureAwait(false); } var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); if (textChanges != null) { edits.AddRange(textChanges.Select(change => ProtocolConversions.TextChangeToTextEdit(change, text))); } return(edits.ToArrayAndFree()); }