/// <summary> /// Returns the semantic tokens data for a given document with an optional range. /// </summary> internal static async Task <int[]> ComputeSemanticTokensDataAsync( Document document, Dictionary <string, int> tokenTypesToIndex, LSP.Range?range, CancellationToken cancellationToken) { var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); // By default we calculate the tokens for the full document span, although the user // can pass in a range if they wish. var textSpan = range == null ? root.FullSpan : ProtocolConversions.RangeToTextSpan(range, text); var classifiedSpans = await Classifier.GetClassifiedSpansAsync(document, textSpan, cancellationToken).ConfigureAwait(false); Contract.ThrowIfNull(classifiedSpans, "classifiedSpans is null"); // Multi-line tokens are not supported by VS (tracked by https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1265495). // Roslyn's classifier however can return multi-line classified spans, so we must break these up into single-line spans. var updatedClassifiedSpans = ConvertMultiLineToSingleLineSpans(text, classifiedSpans.ToArray()); // TO-DO: We should implement support for streaming if LSP adds support for it: // https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1276300 return(ComputeTokens(text.Lines, updatedClassifiedSpans, tokenTypesToIndex)); }
protected async Task <LSP.TextEdit[]> GetTextEditsAsync(LSP.TextDocumentIdentifier documentIdentifier, 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()); }
protected static async Task <LSP.TextEdit[]?> GetTextEditsAsync( RequestContext context, LSP.FormattingOptions options, IGlobalOptionService globalOptions, CancellationToken cancellationToken, LSP.Range?range = null) { var document = context.Document; if (document == null) { return(null); } var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var rangeSpan = (range != null) ? ProtocolConversions.RangeToTextSpan(range, text) : new TextSpan(0, root.FullSpan.Length); var formattingSpan = CommonFormattingHelpers.GetFormattingSpan(root, rangeSpan); // We should use the options passed in by LSP instead of the document's options. var formattingOptions = await ProtocolConversions.GetFormattingOptionsAsync(options, document, globalOptions, cancellationToken).ConfigureAwait(false); var services = document.Project.Solution.Workspace.Services; var textChanges = Formatter.GetFormattedTextChanges(root, SpecializedCollections.SingletonEnumerable(formattingSpan), services, formattingOptions, rules: null, cancellationToken); var edits = new ArrayBuilder <LSP.TextEdit>(); edits.AddRange(textChanges.Select(change => ProtocolConversions.TextChangeToTextEdit(change, text))); return(edits.ToArrayAndFree()); }
public async Task <DocumentSpan?> GetDocumentSpanFromLocation(LSP.Location location, CancellationToken cancellationToken) { var document = GetOrAddDocument(location.Uri.LocalPath); if (document == null) { return(null); } var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); // The protocol converter would have synced the file to disk but we the document snapshot that was in the workspace before the sync would have empty text. // So we need to read from disk in order to map from line\column to a textspan. if (string.IsNullOrEmpty(text.ToString())) { text = SourceText.From(File.ReadAllText(document.FilePath)); // Some features like the FindRefs window try to get the text at the span without opening the document (for eg to classify the span). // So fork the document to get one with the text. Note that this new document will not be in the CurrentSolution and we don't intend to // apply it back. By fetching the file, the workspace will get updated anyway. The assumption here is that this document that we are // handing out is only used for simple inspection and it's version is never compared with the Workspace.CurrentSolution. document = document.WithText(text); } var textSpan = ProtocolConversions.RangeToTextSpan(location.Range, text); return(new DocumentSpan(document, textSpan)); }
internal static async Task <IEnumerable <CodeAction> > GetCodeActionsAsync(Document?document, ICodeFixService codeFixService, ICodeRefactoringService codeRefactoringService, LSP.Range selection, CancellationToken cancellationToken) { if (document == null) { return(ImmutableArray <CodeAction> .Empty); } var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); var textSpan = ProtocolConversions.RangeToTextSpan(selection, text); var codeFixCollections = await codeFixService.GetFixesAsync(document, textSpan, true, cancellationToken).ConfigureAwait(false); var codeRefactorings = await codeRefactoringService.GetRefactoringsAsync(document, textSpan, cancellationToken).ConfigureAwait(false); var codeActions = codeFixCollections.SelectMany(c => c.Fixes.Select(f => f.Action)).Concat( codeRefactorings.SelectMany(r => r.CodeActions.Select(ca => ca.action))); // Flatten out the nested codeactions. var nestedCodeActions = codeActions.Where(c => c is CodeAction.CodeActionWithNestedActions nc && nc.IsInlinable).SelectMany(nc => nc.NestedCodeActions); codeActions = codeActions.Where(c => !(c is CodeAction.CodeActionWithNestedActions)).Concat(nestedCodeActions); return(codeActions); }
public async Task <IEnumerable <CodeAction> > GetCodeActionsAsync(Solution solution, Uri documentUri, LSP.Range selection, bool keepThreadContext, CancellationToken cancellationToken) { var document = solution.GetDocumentFromURI(documentUri); if (document == null) { return(ImmutableArray <CodeAction> .Empty); } var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(keepThreadContext); var textSpan = ProtocolConversions.RangeToTextSpan(selection, text); var codeFixCollections = await _codeFixService.GetFixesAsync(document, textSpan, true, cancellationToken).ConfigureAwait(keepThreadContext); var codeRefactorings = await _codeRefactoringService.GetRefactoringsAsync(document, textSpan, cancellationToken).ConfigureAwait(keepThreadContext); var codeActions = codeFixCollections.SelectMany(c => c.Fixes.Select(f => f.Action)).Concat( codeRefactorings.SelectMany(r => r.Actions)); // Flatten out the nested codeactions. var nestedCodeActions = codeActions.Where(c => c is CodeAction.CodeActionWithNestedActions nc && nc.IsInlinable).SelectMany(nc => nc.NestedCodeActions); codeActions = codeActions.Where(c => !(c is CodeAction.CodeActionWithNestedActions)).Concat(nestedCodeActions); return(codeActions); }
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()); }
private static async Task <ImmutableArray <UnifiedSuggestedActionSet>?> GetActionSetsAsync( Document document, ICodeFixService codeFixService, ICodeRefactoringService codeRefactoringService, IThreadingContext threadingContext, LSP.Range selection, CancellationToken cancellationToken) { var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); var textSpan = ProtocolConversions.RangeToTextSpan(selection, text); // The logic to filter code actions requires the UI thread await threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); var codeFixes = UnifiedSuggestedActionsSource.GetFilterAndOrderCodeFixes_MustBeCalledFromUIThread( document.Project.Solution.Workspace, codeFixService, document, textSpan, includeSuppressionFixes: true, isBlocking: false, addOperationScope: _ => null, cancellationToken); var codeRefactorings = UnifiedSuggestedActionsSource.GetFilterAndOrderCodeRefactorings_MustBeCalledFromUIThread( document.Project.Solution.Workspace, codeRefactoringService, document, textSpan, isBlocking: false, addOperationScope: _ => null, filterOutsideSelection: false, cancellationToken); var actionSets = UnifiedSuggestedActionsSource.FilterAndOrderActionSets(codeFixes, codeRefactorings, textSpan); return(actionSets); }
private async Task <Document> ApplyEditsAsync(Document document, LSP.TextEdit[] textEdits, CancellationToken cancellationToken) { var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); var textChanges = textEdits.Select(te => new TextChange(ProtocolConversions.RangeToTextSpan(te.Range, text), te.NewText)); var newDocument = document.WithText(text.WithChanges(textChanges)); return(newDocument); }
public async Task <LSP.Range?> HandleRequestAsync(LSP.VSInternalValidateBreakableRangeParams request, RequestContext context, CancellationToken cancellationToken) { var document = context.Document; Contract.ThrowIfNull(document); var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); var span = ProtocolConversions.RangeToTextSpan(request.Range, text); var breakpointService = document.Project.LanguageServices.GetRequiredService <IBreakpointResolutionService>(); if (span.Length > 0) { // If we have a non-empty span then it means that the debugger is asking us to adjust an // existing span. In Everett we didn't do this so we had some good and some bad // behavior. For example if you had a breakpoint on: "int i = 1;" and you changed it to "int // i = 1, j = 2;", then the breakpoint wouldn't adjust. That was bad. However, if you had the // breakpoint on an open or close curly brace then it would always "stick" to that brace // which was good. // // So we want to keep the best parts of both systems. We want to appropriately "stick" // to tokens and we also want to adjust spans intelligently. // // However, it turns out the latter is hard to do when there are parse errors in the // code. Things like missing name nodes cause a lot of havoc and make it difficult to // track a closing curly brace. // // So the way we do this is that we default to not intelligently adjusting the spans // while there are parse errors. But when there are no parse errors then the span is // adjusted. if (document.SupportsSyntaxTree) { var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); Contract.ThrowIfNull(tree); if (tree.GetDiagnostics(cancellationToken).Any(d => d.Severity == DiagnosticSeverity.Error)) { // Keep the span as is. return(request.Range); } } } var result = await breakpointService.ResolveBreakpointAsync(document, span, cancellationToken).ConfigureAwait(false); if (result == null) { return(null); } // zero-width range means line breakpoint: var breakpointSpan = result.IsLineBreakpoint ? new TextSpan(span.Start, length: 0) : result.TextSpan; return(ProtocolConversions.TextSpanToRange(breakpointSpan, text)); }
private async TPL.Task <ImmutableArray <DefinitionItem> > GetDefinitionItemsAsync(Document document, int position, CancellationToken cancellationToken) { var lspClient = _roslynLspClientServiceFactory.ActiveLanguageServerClient; if (lspClient == null) { return(ImmutableArray <DefinitionItem> .Empty); } var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); var textDocumentPositionParams = ProtocolConversions.PositionToTextDocumentPositionParams(position, text, document); var response = await lspClient.RequestAsync(LSP.Methods.TextDocumentDefinition.ToLSRequest(), textDocumentPositionParams, cancellationToken).ConfigureAwait(false); var locations = ((JToken)response)?.ToObject <LSP.Location[]>(); if (locations == null) { return(ImmutableArray <DefinitionItem> .Empty); } var definitionItems = ImmutableArray.CreateBuilder <DefinitionItem>(); foreach (var location in locations) { DocumentSpan?documentSpan; if (lspClient.ProtocolConverter.IsExternalDocument(location.Uri)) { var externalDocument = _remoteWorkspace.GetOrAddExternalDocument(location.Uri.LocalPath, document.Project.Language); var externalText = await externalDocument.GetTextAsync(cancellationToken).ConfigureAwait(false); var textSpan = ProtocolConversions.RangeToTextSpan(location.Range, externalText); documentSpan = new DocumentSpan(externalDocument, textSpan); } else { documentSpan = await _remoteWorkspace.GetDocumentSpanFromLocation(location, cancellationToken).ConfigureAwait(false); if (documentSpan == null) { continue; } } definitionItems.Add(DefinitionItem.Create(ImmutableArray <string> .Empty, ImmutableArray <TaggedText> .Empty, documentSpan.Value)); } return(definitionItems.ToImmutable()); }
private static NavigationBarItem CreateNavigationBarItem(SymbolInformation symbolInformation, SourceText text, ImmutableArray <NavigationBarItem> children) { try { var name = symbolInformation.Name; var glyph = ProtocolConversions.SymbolKindToGlyph(symbolInformation.Kind); var textSpan = ProtocolConversions.RangeToTextSpan(symbolInformation.Location.Range, text); return(new RemoteNavigationBarItem(name, glyph, ImmutableArray.Create(textSpan), children)); } catch (ArgumentOutOfRangeException ex) when(FatalError.ReportWithoutCrash(ex)) { return(null); } }
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)); } } }
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)); }
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 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()); }
internal static async Task <int[]> ComputeSemanticTokensDataAsync( Document document, Dictionary <string, int> tokenTypesToIndex, LSP.Range?range, CancellationToken cancellationToken) { var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); // By default we calculate the tokens for the full document span, although the user // can pass in a range if they wish. var textSpan = range == null ? root.FullSpan : ProtocolConversions.RangeToTextSpan(range, text); var classifiedSpans = await Classifier.GetClassifiedSpansAsync(document, textSpan, cancellationToken).ConfigureAwait(false); Contract.ThrowIfNull(classifiedSpans, "classifiedSpans is null"); // TO-DO: We should implement support for streaming once this LSP bug is fixed: // https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1132601 return(ComputeTokens(text.Lines, classifiedSpans.ToArray(), tokenTypesToIndex)); }
public async Task <object[]> HandleAsync(ClassificationParams request, RequestContext <Solution> requestContext, CancellationToken cancellationToken) { var actualDocumentURI = requestContext.ProtocolConverter.FromProtocolUri(request.TextDocument.Uri); var document = requestContext.Context.GetDocumentFromURI(actualDocumentURI); var classificationService = document?.Project.LanguageServices.GetService <IClassificationService>(); if (document == null || classificationService == null) { return(Array.Empty <ClassificationSpan>()); } var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); var textSpan = ProtocolConversions.RangeToTextSpan(request.Range, text); var spans = new List <ClassifiedSpan>(); await classificationService.AddSemanticClassificationsAsync(document, textSpan, spans, cancellationToken).ConfigureAwait(false); return(spans.Select(c => new ClassificationSpan { Classification = c.ClassificationType, Range = ProtocolConversions.TextSpanToRange(c.TextSpan, text) }).ToArray()); }
private static async Task <ImmutableArray <UnifiedSuggestedActionSet>?> GetActionSetsAsync( 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 codeFixes = await UnifiedSuggestedActionsSource.GetFilterAndOrderCodeFixesAsync( document.Project.Solution.Workspace, codeFixService, document, textSpan, includeSuppressionFixes : true, isBlocking : false, addOperationScope : _ => null, cancellationToken).ConfigureAwait(false); var codeRefactorings = await UnifiedSuggestedActionsSource.GetFilterAndOrderCodeRefactoringsAsync( document.Project.Solution.Workspace, codeRefactoringService, document, textSpan, isBlocking : false, addOperationScope : _ => null, filterOutsideSelection : false, cancellationToken).ConfigureAwait(false); var actionSets = UnifiedSuggestedActionsSource.FilterAndOrderActionSets(codeFixes, codeRefactorings, textSpan); return(actionSets); }
public override async Task <LSP.Range?> HandleRequestAsync(LSP.VSInternalValidateBreakableRangeParams request, RequestContext context, CancellationToken cancellationToken) { var document = context.Document; Contract.ThrowIfNull(document); var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); var span = ProtocolConversions.RangeToTextSpan(request.Range, text); var breakpointService = document.Project.LanguageServices.GetRequiredService <IBreakpointResolutionService>(); var result = await breakpointService.ResolveBreakpointAsync(document, span, cancellationToken).ConfigureAwait(false); if (result == null) { return(null); } // zero-width range means line breakpoint: var breakpointSpan = result.IsLineBreakpoint ? new TextSpan(span.Start, length: 0) : result.TextSpan; return(ProtocolConversions.TextSpanToRange(breakpointSpan, text)); }
protected async Task <LSP.TextEdit[]> GetTextEdits(Solution solution, Uri documentUri, bool keepThreadContext, CancellationToken cancellationToken, LSP.Range range = null) { var edits = new ArrayBuilder <LSP.TextEdit>(); var document = solution.GetDocumentFromURI(documentUri); if (document != null) { var formattingService = document.Project.LanguageServices.GetService <IEditorFormattingService>(); var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(keepThreadContext); TextSpan?textSpan = null; if (range != null) { textSpan = ProtocolConversions.RangeToTextSpan(range, text); } var textChanges = await formattingService.GetFormattingChangesAsync(document, textSpan, cancellationToken).ConfigureAwait(keepThreadContext); edits.AddRange(textChanges.Select(change => ProtocolConversions.TextChangeToTextEdit(change, text))); } return(edits.ToArrayAndFree()); }
protected async Task <LSP.TextEdit[]> GetTextEditsAsync(LSP.TextDocumentIdentifier documentIdentifier, RequestContext context, CancellationToken cancellationToken, LSP.Range?range = null) { var edits = new ArrayBuilder <LSP.TextEdit>(); var document = SolutionProvider.GetDocument(documentIdentifier, context.ClientName); 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); } var textChanges = await GetFormattingChangesAsync(formattingService, document, textSpan, cancellationToken).ConfigureAwait(false); edits.AddRange(textChanges.Select(change => ProtocolConversions.TextChangeToTextEdit(change, text))); } return(edits.ToArrayAndFree()); }
/// <summary> /// Returns the semantic tokens data for a given document with an optional range. /// </summary> internal static async Task <(int[], bool isFinalized)> ComputeSemanticTokensDataAsync( Document document, Dictionary <string, int> tokenTypesToIndex, LSP.Range?range, bool includeSyntacticClassifications, CancellationToken cancellationToken) { var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); // By default we calculate the tokens for the full document span, although the user // can pass in a range if they wish. var textSpan = range is null ? root.FullSpan : ProtocolConversions.RangeToTextSpan(range, text); // If the full compilation is not yet available, we'll try getting a partial one. It may contain inaccurate // results but will speed up how quickly we can respond to the client's request. var frozenDocument = document.WithFrozenPartialSemantics(cancellationToken); var semanticModel = await frozenDocument.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); var isFinalized = document.Project.TryGetCompilation(out var compilation) && compilation == semanticModel.Compilation; document = frozenDocument; var options = ClassificationOptions.From(document.Project); var classifiedSpans = await GetClassifiedSpansForDocumentAsync( document, textSpan, options, includeSyntacticClassifications, cancellationToken).ConfigureAwait(false); // Multi-line tokens are not supported by VS (tracked by https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1265495). // Roslyn's classifier however can return multi-line classified spans, so we must break these up into single-line spans. var updatedClassifiedSpans = ConvertMultiLineToSingleLineSpans(text, classifiedSpans); // TO-DO: We should implement support for streaming if LSP adds support for it: // https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1276300 return(ComputeTokens(text.Lines, updatedClassifiedSpans, tokenTypesToIndex), isFinalized); }
public async Task <ImmutableArray <DocumentHighlights> > GetDocumentHighlightsAsync(Document document, int position, IImmutableSet <Document> documentsToSearch, CancellationToken cancellationToken) { var lspClient = _roslynLspClientServiceFactory.ActiveLanguageServerClient; if (lspClient == null) { return(ImmutableArray <DocumentHighlights> .Empty); } var text = await document.GetTextAsync().ConfigureAwait(false); var textDocumentPositionParams = ProtocolConversions.PositionToTextDocumentPositionParams(position, text, document); var highlights = await lspClient.RequestAsync(Methods.TextDocumentDocumentHighlight.ToLSRequest(), textDocumentPositionParams, cancellationToken).ConfigureAwait(false); if (highlights == null) { return(ImmutableArray <DocumentHighlights> .Empty); } var highlightSpans = highlights.Select(highlight => new HighlightSpan(ProtocolConversions.RangeToTextSpan(highlight.Range, text), ToHighlightSpanKind(highlight.Kind))); return(ImmutableArray.Create(new DocumentHighlights(document, highlightSpans.ToImmutableArray()))); }