// 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 FindReferencesAsync(Document document, int position, IFindUsagesContext context) { var text = await document.GetTextAsync().ConfigureAwait(false); var lspClient = _roslynLspClientServiceFactory.ActiveLanguageServerClient; if (lspClient == null) { return; } var referenceParams = new LSP.ReferenceParams { Context = new LSP.ReferenceContext { IncludeDeclaration = false }, TextDocument = ProtocolConversions.DocumentToTextDocumentIdentifier(document), Position = ProtocolConversions.LinePositionToPosition(text.Lines.GetLinePosition(position)) }; var locations = await lspClient.RequestAsync(LSP.Methods.TextDocumentReferences.ToLSRequest(), referenceParams, context.CancellationToken).ConfigureAwait(false); if (locations == null) { return; } // TODO: Need to get real definition data from the server. var dummyDef = DefinitionItem.CreateNonNavigableItem(ImmutableArray <string> .Empty, ImmutableArray <TaggedText> .Empty); await context.OnDefinitionFoundAsync(dummyDef).ConfigureAwait(false); foreach (var location in locations) { var documentSpan = await _remoteLanguageServiceWorkspace.GetDocumentSpanFromLocation(location, context.CancellationToken).ConfigureAwait(false); if (documentSpan == null) { continue; } #pragma warning disable CS0612 // Type or member is obsolete. TODO. await context.OnReferenceFoundAsync(new SourceReferenceItem(dummyDef, documentSpan.Value, isWrittenTo : false)).ConfigureAwait(false); #pragma warning restore CS0612 // Type or member is obsolete } }
public override async Task <DocumentOnTypeRenameResponseItem?> HandleRequestAsync( DocumentOnTypeRenameParams 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.ThrowIfNull(result.Ranges); return(new DocumentOnTypeRenameResponseItem { Ranges = result.Ranges .Select(s => ProtocolConversions.TextSpanToRange(s, text)) .ToArray(), WordPattern = result.WordPattern }); }
internal static LSP.SymbolInformation CreateSymbolInformation(LSP.SymbolKind kind, string name, LSP.Location location, Glyph glyph, string?containerName = null) { var info = new LSP.VSSymbolInformation() { Kind = kind, Name = name, Location = location, Icon = ProtocolConversions.GetImageIdFromGlyph(glyph) }; if (containerName != null) { info.ContainerName = containerName; } return(info); }
public override async Task <WorkspaceEdit?> HandleRequestAsync(RenameParams request, RequestContext context, CancellationToken cancellationToken) { var document = context.Document; if (document != null) { 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 renameLocationSet = await renameInfo.FindRenameLocationsAsync(oldSolution.Workspace.Options, cancellationToken).ConfigureAwait(false); var renameReplacementInfo = await renameLocationSet.GetReplacementsAsync(request.NewName, oldSolution.Workspace.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.Workspace.Services.GetRequiredService <IDocumentTextDifferencingService>(); var documentEdits = await ProtocolConversions.ChangedDocumentsToTextDocumentEditsAsync(changedDocuments, renamedSolution.GetRequiredDocument, oldSolution.GetRequiredDocument, textDiffService, cancellationToken).ConfigureAwait(false); return(new WorkspaceEdit { DocumentChanges = documentEdits }); } return(null); }
public override async Task <VSInternalDocumentOnAutoInsertResponseItem?> HandleRequestAsync(VSInternalDocumentOnAutoInsertParams 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 VSInternalDocumentOnAutoInsertResponseItem { TextEditFormat = insertFormat, TextEdit = new TextEdit { NewText = insertText, Range = ProtocolConversions.TextSpanToRange(result.TextChange.Span, text) } }); }
private static CompletionItem CreateCompletionItem(XamlCompletionItem xamlCompletion, DocumentId documentId, SourceText text, Position position, TextDocumentIdentifier textDocument, Dictionary <XamlCompletionKind, ImmutableArray <VSInternalCommitCharacter> > commitCharactersCach) { var item = new VSInternalCompletionItem { Label = xamlCompletion.DisplayText, VsCommitCharacters = GetCommitCharacters(xamlCompletion, commitCharactersCach), Detail = xamlCompletion.Detail, InsertText = xamlCompletion.InsertText, Preselect = xamlCompletion.Preselect.GetValueOrDefault(), SortText = xamlCompletion.SortText, FilterText = xamlCompletion.FilterText, Kind = GetItemKind(xamlCompletion.Kind), Description = xamlCompletion.Description, Icon = xamlCompletion.Icon, InsertTextFormat = xamlCompletion.IsSnippet ? InsertTextFormat.Snippet : InsertTextFormat.Plaintext, Data = new CompletionResolveData { ProjectGuid = documentId.ProjectId.Id, DocumentGuid = documentId.Id, Position = position, DisplayText = xamlCompletion.DisplayText } }; if (xamlCompletion.Span.HasValue) { item.TextEdit = new TextEdit { NewText = xamlCompletion.InsertText, Range = ProtocolConversions.LinePositionToRange(text.Lines.GetLinePositionSpan(xamlCompletion.Span.Value)) }; } if (xamlCompletion.EventDescription.HasValue) { item.Command = new Command() { CommandIdentifier = StringConstants.CreateEventHandlerCommand, Arguments = new object[] { textDocument, xamlCompletion.EventDescription }, Title = CreateEventHandlerCommandTitle }; } else if (xamlCompletion.RetriggerCompletion) { // Retriger completion after commit item.Command = s_retriggerCompletionCommand; } return(item); }
private async Task AddRemoteClassificationsAsync(string classificationsType, string filePath, SourceText sourceText, TextSpan textSpan, ArrayBuilder <ClassifiedSpan> result, CancellationToken cancellationToken) { var lspClient = _roslynLspClientServiceFactory.ActiveLanguageServerClient; if (lspClient == null) { return; } // TODO - Move to roslyn client initialization once liveshare initialization is fixed. // https://devdiv.visualstudio.com/DevDiv/_workitems/edit/964288 await _roslynLspClientServiceFactory.EnsureInitialized(cancellationToken).ConfigureAwait(false); var classificationParams = new ClassificationParams { TextDocument = new TextDocumentIdentifier { Uri = lspClient.ProtocolConverter.ToProtocolUri(new Uri(filePath)) }, Range = ProtocolConversions.TextSpanToRange(textSpan, sourceText) }; var request = new LS.LspRequest <ClassificationParams, ClassificationSpan[]>(classificationsType); var classificationSpans = await lspClient.RequestAsync(request, classificationParams, cancellationToken).ConfigureAwait(false); if (classificationSpans == null) { return; } foreach (var classificationSpan in classificationSpans) { // The host may return more classifications than are supported by the guest. As an example, 15.7 added classifications for type members which wouldnt be understood by a 15.6 guest. // Check with the classificationTypeMap to see if this is a known classification. var classification = classificationSpan.Classification; if (_classificationTypeMap.GetClassificationType(classification) == null) { classification = ClassificationTypeNames.Identifier; } var span = ProtocolConversions.RangeToTextSpan(classificationSpan.Range, sourceText); if (span.End <= sourceText.Length) { result.Add(new ClassifiedSpan(classification, span)); } } }
static async Task <LSP.Location?> ComputeLocationAsync( Document document, int position, DocumentSpan documentSpan, IMetadataAsSourceFileService metadataAsSourceFileService, CancellationToken cancellationToken) { if (documentSpan != default) { // We do have a source span, so compute location normally. return(await ProtocolConversions.DocumentSpanToLocationAsync(documentSpan, cancellationToken).ConfigureAwait(false)); } // If we have no source span, our location may be in metadata. var symbol = await SymbolFinder.FindSymbolAtPositionAsync(document, position, cancellationToken).ConfigureAwait(false); if (symbol == null || symbol.Locations == null || symbol.Locations.IsEmpty || !symbol.Locations.First().IsInMetadata) { // We couldn't find the location in metadata and it's not in any of our known documents. return(null); } var declarationFile = await metadataAsSourceFileService.GetGeneratedFileAsync( document.Project, symbol, allowDecompilation : false, cancellationToken).ConfigureAwait(false); var linePosSpan = declarationFile.IdentifierLocation.GetLineSpan().Span; if (string.IsNullOrEmpty(declarationFile.FilePath)) { return(null); } try { return(new LSP.Location { Uri = ProtocolConversions.GetUriFromFilePath(declarationFile.FilePath), Range = ProtocolConversions.LinePositionToRange(linePosSpan), }); } catch (UriFormatException e) when(FatalError.ReportWithoutCrash(e)) { // We might reach this point if the file path is formatted incorrectly. return(null); } }
public override async Task <LSP.VSInternalDocumentOnAutoInsertResponseItem?> HandleRequestAsync( LSP.VSInternalDocumentOnAutoInsertParams request, RequestContext context, CancellationToken cancellationToken) { var document = context.Document; if (document == null) { return(null); } var service = document.GetRequiredLanguageService <IDocumentationCommentSnippetService>(); // 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); // 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 (request.Character == "\n" || request.Character == service.DocumentationCommentCharacter) { var documentationCommentResponse = await GetDocumentationCommentResponseAsync( request, document, service, documentOptions, cancellationToken).ConfigureAwait(false); if (documentationCommentResponse != null) { return(documentationCommentResponse); } } // Only support this for razor as LSP doesn't support overtype yet. // https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1165179/ // Once LSP supports overtype we can move all of brace completion to LSP. if (request.Character == "\n" && context.ClientName == document.Services.GetService <DocumentPropertiesService>()?.DiagnosticsLspClientName) { var braceCompletionAfterReturnResponse = await GetBraceCompletionAfterReturnResponseAsync( request, document, documentOptions, cancellationToken).ConfigureAwait(false); if (braceCompletionAfterReturnResponse != null) { return(braceCompletionAfterReturnResponse); } } return(null); }
public Task <ActiveProjectContexts?> HandleRequestAsync(GetTextDocumentWithContextParams request, RequestContext context, CancellationToken cancellationToken) { var documents = _solutionProvider.GetDocuments(request.TextDocument.Uri, context.ClientName); if (!documents.Any()) { return(SpecializedTasks.Null <ActiveProjectContexts>()); } var contexts = new List <ProjectContext>(); foreach (var document in documents) { var project = document.Project; var projectContext = new ProjectContext { Id = ProtocolConversions.ProjectIdToProjectContextId(project.Id), Label = project.Name }; if (project.Language == LanguageNames.CSharp) { projectContext.Kind = ProjectContextKind.CSharp; } else if (project.Language == LanguageNames.VisualBasic) { projectContext.Kind = ProjectContextKind.VisualBasic; } contexts.Add(projectContext); } // If the document is open, it doesn't matter which DocumentId we pass to GetDocumentIdInCurrentContext since // all the documents are linked at that point, so we can just pass the first arbitrarily. If the document is closed // GetDocumentIdInCurrentContext will just return the same ID back, which means we're going to pick the first // ID in GetDocumentIdsWithFilePath, but there's really nothing we can do since we don't have contexts for // close documents anyways. var openDocument = documents.First(); var currentContextDocumentId = openDocument.Project.Solution.Workspace.GetDocumentIdInCurrentContext(openDocument.Id); return(Task.FromResult <ActiveProjectContexts?>(new ActiveProjectContexts { ProjectContexts = contexts.ToArray(), DefaultIndex = documents.IndexOf(d => d.Id == currentContextDocumentId) })); }
public async Task <LSP.CompletionItem> HandleRequestAsync(LSP.CompletionItem completionItem, RequestContext context, CancellationToken cancellationToken) { Contract.ThrowIfNull(context.Solution); if (completionItem is not VSInternalCompletionItem vsCompletionItem) { return(completionItem); } CompletionResolveData?data; if (completionItem.Data is JToken token) { data = token.ToObject <CompletionResolveData>(); Assumes.Present(data); } else { return(completionItem); } var documentId = DocumentId.CreateFromSerialized(ProjectId.CreateFromSerialized(data.ProjectGuid), data.DocumentGuid); var document = context.Solution.GetDocument(documentId) ?? context.Solution.GetAdditionalDocument(documentId); if (document == null) { return(completionItem); } var 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 options = _globalOptions.GetSymbolDescriptionOptions(document.Project.Language); var description = await symbol.GetDescriptionAsync(document, options, cancellationToken).ConfigureAwait(false); vsCompletionItem.Description = new ClassifiedTextElement(description.Select(tp => new ClassifiedTextRun(tp.Tag.ToClassificationTypeName(), tp.Text))); return(vsCompletionItem); }
protected async Task <LSP.TextEdit[]> GetTextEditsAsync( RequestContext context, LSP.FormattingOptions options, CancellationToken cancellationToken, LSP.Range?range = null ) { var edits = new ArrayBuilder <LSP.TextEdit>(); var document = context.Document; if (document != null) { var formattingService = document.Project.LanguageServices.GetRequiredService <IEditorFormattingService>(); var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); TextSpan?textSpan = null; if (range != null) { textSpan = ProtocolConversions.RangeToTextSpan(range, text); } // We should use the options passed in by LSP instead of the document's options. var documentOptions = await ProtocolConversions .FormattingOptionsToDocumentOptionsAsync(options, document, cancellationToken) .ConfigureAwait(false); var textChanges = await GetFormattingChangesAsync( formattingService, document, textSpan, documentOptions, cancellationToken ) .ConfigureAwait(false); edits.AddRange( textChanges.Select( change => ProtocolConversions.TextChangeToTextEdit(change, text) ) ); } return(edits.ToArrayAndFree()); }
public async Task TestExternalAccessTypeScriptHandlerInvoked() { var workspaceXml = @$ "<Workspace> <Project Language=" "TypeScript" " CommonReferences=" "true" " AssemblyName=" "TypeScriptProj" "> <Document FilePath=" "C:\T.ts" "></Document> </Project> </Workspace>"; using var testLspServer = await CreateTsTestLspServerAsync(workspaceXml); var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); var request = new TSRequest(document.GetURI(), ProtocolConversions.ProjectIdToProjectContextId(document.Project.Id)); var response = await testLspServer.ExecuteRequestAsync <TSRequest, int>(TypeScriptHandler.MethodName, request, CancellationToken.None); Assert.Equal(TypeScriptHandler.Response, response); }
public static async Task <(ImmutableArray <CodeFixCollection>, ImmutableArray <CodeRefactoring>)> GetCodeFixesAndRefactoringsAsync( Document document, ICodeFixService codeFixService, ICodeRefactoringService codeRefactoringService, LSP.Range selection, CancellationToken cancellationToken) { var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); var textSpan = ProtocolConversions.RangeToTextSpan(selection, text); var codeFixCollectionsTask = codeFixService.GetFixesAsync(document, textSpan, includeSuppressionFixes: true, cancellationToken); var codeRefactoringsTask = codeRefactoringService.GetRefactoringsAsync(document, textSpan, cancellationToken); await Task.WhenAll(codeFixCollectionsTask, codeRefactoringsTask).ConfigureAwait(false); return(await codeFixCollectionsTask.ConfigureAwait(false), await codeRefactoringsTask.ConfigureAwait(false)); }
private static async Task <LSP.Location?> GetSourceDefinitionLocationAsync(XamlSourceDefinition sourceDefinition, RequestContext context, CancellationToken cancellationToken) { Contract.ThrowIfNull(sourceDefinition.FilePath); if (sourceDefinition.Span != null) { // If the Span is not null, use the span. var document = context.Solution?.GetDocuments(ProtocolConversions.GetUriFromFilePath(sourceDefinition.FilePath)).FirstOrDefault(); if (document != null) { return(await ProtocolConversions.TextSpanToLocationAsync( document, sourceDefinition.Span.Value, isStale : false, cancellationToken).ConfigureAwait(false)); } else { // Cannot find the file in solution. This is probably a file lives outside of the solution like generic.xaml // which lives in the Windows SDK folder. Try getting the SourceText from the file path. using var fileStream = new FileStream(sourceDefinition.FilePath, FileMode.Open, FileAccess.Read); var sourceText = SourceText.From(fileStream); return(new LSP.Location { Uri = new Uri(sourceDefinition.FilePath), Range = ProtocolConversions.TextSpanToRange(sourceDefinition.Span.Value, sourceText) }); } } else { // We should have the line and column, so use them to build the LSP Range. var position = new Position(sourceDefinition.Line, sourceDefinition.Column); return(new LSP.Location { Uri = new Uri(sourceDefinition.FilePath), Range = new LSP.Range() { Start = position, End = position } }); } }
public async Task AddRemoteClassificationsAsync(string classificationsServiceName, string filePath, SourceText sourceText, TextSpan textSpan, Action <ClassifiedSpan> tagAdder, CancellationToken cancellationToken) { var lspClient = _roslynLspClientServiceFactory.ActiveLanguageServerClient; if (lspClient == null) { return; } await EnsureInitializationAsync(cancellationToken).ConfigureAwait(false); var classificationParams = new ClassificationParams { TextDocument = new TextDocumentIdentifier { Uri = lspClient.ProtocolConverter.ToProtocolUri(new Uri(filePath)) }, Range = ProtocolConversions.TextSpanToRange(textSpan, sourceText) }; var request = new LS.LspRequest <ClassificationParams, ClassificationSpan[]>(classificationsServiceName); var classificationSpans = await lspClient.RequestAsync(request, classificationParams, cancellationToken).ConfigureAwait(false); if (classificationSpans == null) { return; } foreach (var classificationSpan in classificationSpans) { // The host may return more classifications than are supported by the guest. As an example, 15.7 added classifications for type members which wouldnt be understood by a 15.6 guest. // Check with the classificationTypeMap to see if this is a known classification. var classification = classificationSpan.Classification; if (_classificationTypeMap.GetClassificationType(classification) == null) { classification = ClassificationTypeNames.Identifier; } var span = ProtocolConversions.RangeToTextSpan(classificationSpan.Range, sourceText); if (span.End <= sourceText.Length) { tagAdder(new ClassifiedSpan(classification, span)); } } }
public override async Task <TextEdit[]> HandleRequestAsync( DocumentOnTypeFormattingParams request, RequestContext context, CancellationToken cancellationToken) { var edits = new ArrayBuilder <TextEdit>(); var document = context.Document; if (document != null) { 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()); }
public override async Task <LSP.VSReferenceItem[]?> 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 <TextEdit[]?> HandleRequestAsync( DocumentOnTypeFormattingParams 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); if (string.IsNullOrEmpty(request.Character) || SyntaxFacts.IsNewLine(request.Character[0])) { return(Array.Empty <TextEdit>()); } var formattingService = document.Project.LanguageServices.GetRequiredService <ISyntaxFormattingService>(); if (!await formattingService.ShouldFormatOnTypedCharacterAsync(document, request.Character[0], position, cancellationToken).ConfigureAwait(false)) { return(Array.Empty <TextEdit>()); } // We should use the options passed in by LSP instead of the document's options. var formattingOptions = await ProtocolConversions.GetFormattingOptionsAsync(request.Options, document, cancellationToken).ConfigureAwait(false); var indentationOptions = new IndentationOptions(formattingOptions, _globalOptions.GetAutoFormattingOptions(document.Project.Language)); var textChanges = await formattingService.GetFormattingChangesOnTypedCharacterAsync(document, position, indentationOptions, cancellationToken).ConfigureAwait(false); if (textChanges.IsEmpty) { return(Array.Empty <TextEdit>()); } var edits = new ArrayBuilder <TextEdit>(); var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); edits.AddRange(textChanges.Select(change => ProtocolConversions.TextChangeToTextEdit(change, text))); return(edits.ToArrayAndFree()); }
protected static LSP.TextDocumentIdentifier CreateTextDocumentIdentifier( Uri uri, ProjectId?projectContext = null ) { var documentIdentifier = new LSP.VSTextDocumentIdentifier { Uri = uri }; if (projectContext != null) { documentIdentifier.ProjectContext = new LSP.ProjectContext { Id = ProtocolConversions.ProjectIdToProjectContextId(projectContext) }; } return(documentIdentifier); }
public async Task AddItemAsync(Project project, INavigateToSearchResult result, CancellationToken cancellationToken) { var location = await ProtocolConversions.TextSpanToLocationAsync( result.NavigableItem.Document, result.NavigableItem.SourceSpan, result.NavigableItem.IsStale, _context, cancellationToken).ConfigureAwait(false); if (location == null) { return; } _progress.Report(new VSSymbolInformation { Name = result.Name, ContainerName = result.AdditionalInformation, Kind = ProtocolConversions.NavigateToKindToSymbolKind(result.Kind), Location = location, Icon = VSLspExtensionConversions.GetImageIdFromGlyph(result.NavigableItem.Glyph) }); }
private static async Task <ImmutableArray <SymbolInformation> > ConvertAsync( ImmutableArray <INavigateToSearchResult> results, CancellationToken cancellationToken) { var symbols = ImmutableArray.CreateBuilder <SymbolInformation>(); foreach (var result in results) { symbols.Add(new VSSymbolInformation() { Name = result.Name, ContainerName = result.AdditionalInformation, Kind = ProtocolConversions.NavigateToKindToSymbolKind(result.Kind), Location = await ProtocolConversions.TextSpanToLocationAsync(result.NavigableItem.Document, result.NavigableItem.SourceSpan, cancellationToken).ConfigureAwait(false), Icon = new VisualStudio.Text.Adornments.ImageElement(result.NavigableItem.Glyph.GetImageId()) }); } return(symbols.ToImmutableArray()); }
private static async Task <Hover> GetHoverAsync( QuickInfoItem info, SourceText text, string language, Document?document, ClassificationOptions?classificationOptions, ClientCapabilities?clientCapabilities, CancellationToken cancellationToken) { Contract.ThrowIfFalse(document is null == (classificationOptions == null)); var supportsVSExtensions = clientCapabilities.HasVisualStudioLspCapability(); if (supportsVSExtensions) { var context = document == null ? null : new IntellisenseQuickInfoBuilderContext( document, classificationOptions !.Value, threadingContext: null, operationExecutor: null, asynchronousOperationListener: null, streamingPresenter: null); return(new VSInternalHover { 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. // TODO - Switch to markup content once it supports classifications. // https://devdiv.visualstudio.com/DevDiv/_workitems/edit/918138 RawContent = await IntellisenseQuickInfoBuilder.BuildContentWithoutNavigationActionsAsync(info, context, cancellationToken).ConfigureAwait(false) }); } else { return(new Hover { Range = ProtocolConversions.TextSpanToRange(info.Span, text), Contents = GetContents(info, language, clientCapabilities), }); }
private static async Task <LSP.Location[]> GetSymbolDefinitionLocationsAsync(XamlSymbolDefinition symbolDefinition, RequestContext context, IMetadataAsSourceFileService metadataAsSourceFileService, CancellationToken cancellationToken) { Contract.ThrowIfNull(symbolDefinition.Symbol); using var _ = ArrayBuilder <LSP.Location> .GetInstance(out var locations); var symbol = symbolDefinition.Symbol; var items = NavigableItemFactory.GetItemsFromPreferredSourceLocations(context.Solution, symbol, displayTaggedParts: null, cancellationToken); if (items.Any()) { foreach (var item in items) { var location = await ProtocolConversions.TextSpanToLocationAsync( item.Document, item.SourceSpan, item.IsStale, cancellationToken).ConfigureAwait(false); locations.AddIfNotNull(location); } } else { var metadataLocation = symbol.Locations.Where(loc => loc.IsInMetadata).FirstOrDefault(); if (metadataLocation != null && metadataAsSourceFileService.IsNavigableMetadataSymbol(symbol)) { var project = context.Document?.GetCodeProject(); if (project != null) { var declarationFile = await metadataAsSourceFileService.GetGeneratedFileAsync(project, symbol, allowDecompilation : 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.ToArray()); }
protected async Task <LSP.TextEdit[]> GetTextEditsAsync( LSP.FormattingOptions formattingOptions, RequestContext context, CancellationToken cancellationToken, LSP.Range?range = null ) { using var _ = ArrayBuilder <LSP.TextEdit> .GetInstance(out var edits); var document = context.Document; var formattingService = document?.Project.LanguageServices.GetService <IXamlFormattingService>(); if (document != null && formattingService != null) { var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); TextSpan?textSpan = null; if (range != null) { textSpan = ProtocolConversions.RangeToTextSpan(range, text); } var options = new XamlFormattingOptions { InsertSpaces = formattingOptions.InsertSpaces, TabSize = formattingOptions.TabSize, OtherOptions = formattingOptions.OtherOptions }; var textChanges = await formattingService .GetFormattingChangesAsync(document, options, textSpan, cancellationToken) .ConfigureAwait(false); edits.AddRange( textChanges.Select( change => ProtocolConversions.TextChangeToTextEdit(change, text) ) ); } return(edits.ToArray()); }
public override async Task <LSP.CompletionList?> HandleRequestAsync(LSP.CompletionParams request, RequestContext context, CancellationToken cancellationToken) { var document = SolutionProvider.GetDocument(request.TextDocument, context.ClientName); if (document == null) { return(null); } var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false); // Filter out snippets as they are not supported in the LSP client // https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1139740 // 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.SnippetsBehavior, SnippetsRule.NeverInclude) .WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, false) .WithChangedOption(CompletionServiceOptions.IsExpandedCompletion, false) .WithChangedOption(CompletionServiceOptions.DisallowAddingImports, true); var completionService = document.Project.LanguageServices.GetRequiredService <CompletionService>(); var list = await completionService.GetCompletionsAsync(document, position, options : completionOptions, cancellationToken : cancellationToken).ConfigureAwait(false); if (list == null) { return(null); } var lspVSClientCapability = context.ClientCapabilities?.HasVisualStudioLspCapability() == true; return(new LSP.VSCompletionList { Items = list.Items.Select(item => CreateLSPCompletionItem(request, item, lspVSClientCapability)).ToArray(), SuggesstionMode = list.SuggestionModeItem != null, });
public override async Task <Hover?> HandleRequestAsync(TextDocumentPositionParams request, RequestContext context, CancellationToken cancellationToken) { var document = context.Document; Contract.ThrowIfNull(document); 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); return(await GetHoverAsync(info, text, document.Project.Language, document, context.ClientCapabilities, cancellationToken).ConfigureAwait(false)); }
public override Task <object?> HandleRequestAsync( LSP.DidChangeTextDocumentParams request, RequestContext context, CancellationToken cancellationToken ) { var text = context.GetTrackedDocumentSourceText(request.TextDocument.Uri); // Per the LSP spec, each text change builds upon the previous, so we don't need to translate // any text positions between changes, which makes this quite easy. var changes = request.ContentChanges.Select( change => ProtocolConversions.ContentChangeEventToTextChange(change, text) ); text = text.WithChanges(changes); context.UpdateTrackedDocument(request.TextDocument.Uri, text); return(SpecializedTasks.Default <object>()); }
public async Task <ImmutableArray <Diagnostic> > GetDiagnosticsAsync(Document document, CancellationToken cancellationToken) { var lspClient = _roslynLspClientServiceFactory.ActiveLanguageServerClient; if (lspClient == null) { return(ImmutableArray <Diagnostic> .Empty); } var textDocumentParams = new TextDocumentParams { TextDocument = new LSP.TextDocumentIdentifier { Uri = lspClient.ProtocolConverter.ToProtocolUri(new Uri(document.FilePath)) } }; var request = new LSP.LspRequest <TextDocumentParams, RoslynDiagnostic[]>(Methods.GetDocumentDiagnosticsName); var lspDiagnostics = await lspClient.RequestAsync(request, textDocumentParams, cancellationToken).ConfigureAwait(false); if (lspDiagnostics == null) { return(ImmutableArray <Diagnostic> .Empty); } var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); var diagnostics = ImmutableArray.CreateBuilder <Diagnostic>(); foreach (var diagnostic in lspDiagnostics) { var location = Location.Create(document.FilePath, ProtocolConversions.RangeToTextSpan(diagnostic.Range, text), ProtocolConversions.RangeToLinePositionSpan(diagnostic.Range)); var severity = ToDiagnosticSeverity(diagnostic.Severity); var diag = Diagnostic.Create(diagnostic.Code ?? "VSLS", string.Empty, diagnostic.Message, severity, severity, true, severity == DiagnosticSeverity.Error ? 0 : 1, location: location, customTags: diagnostic.Tags); diagnostics.Add(diag); } return(diagnostics.ToImmutableArray()); }