コード例 #1
0
        /// <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));
        }
コード例 #2
0
        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());
        }
コード例 #3
0
        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());
        }
コード例 #4
0
        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));
        }
コード例 #5
0
        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);
        }
コード例 #6
0
        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);
        }
コード例 #7
0
        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());
        }
コード例 #8
0
        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);
        }
コード例 #9
0
        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);
        }
コード例 #10
0
        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));
        }
コード例 #11
0
        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());
        }
コード例 #12
0
 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);
     }
 }
コード例 #13
0
        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));
                }
            }
        }
コード例 #14
0
        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));
        }
コード例 #15
0
        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));
                }
            }
        }
コード例 #16
0
        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());
        }
コード例 #17
0
        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));
        }
コード例 #18
0
        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());
        }
コード例 #19
0
        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);
        }
コード例 #20
0
        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));
        }
コード例 #21
0
        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());
        }
コード例 #22
0
        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());
        }
コード例 #23
0
        /// <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);
        }
コード例 #24
0
        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())));
        }