예제 #1
0
        public async Task TestDocumentDiagnosticsForRemovedDocument()
        {
            var markup = @"class A {";

            using var testLspServer = CreateTestWorkspaceWithDiagnostics(
                      markup,
                      BackgroundAnalysisScope.OpenFilesAndProjects
                      );
            var workspace = testLspServer.TestWorkspace;

            // Calling GetTextBuffer will effectively open the file.
            workspace.Documents.Single().GetTextBuffer();

            var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single();

            // Get the diagnostics for the solution containing the doc.
            var solution = document.Project.Solution;

            await OpenDocumentAsync(testLspServer, document);

            await WaitForDiagnosticsAsync(workspace);

            var results = await testLspServer.ExecuteRequestAsync <
                DocumentDiagnosticsParams,
                DiagnosticReport[]
                >(
                MSLSPMethods.DocumentPullDiagnosticName,
                CreateDocumentDiagnosticParams(document),
                new LSP.ClientCapabilities(),
                clientName : null,
                CancellationToken.None
                );

            Assert.Equal("CS1513", results.Single().Diagnostics.Single().Code);

            // Now remove the doc.
            workspace.OnDocumentRemoved(workspace.Documents.Single().Id);
            await CloseDocumentAsync(testLspServer, document);

            // And get diagnostic again, using the same doc-id as before.
            await WaitForDiagnosticsAsync(workspace);

            results = await testLspServer.ExecuteRequestAsync <
                DocumentDiagnosticsParams,
                DiagnosticReport[]
                >(
                MSLSPMethods.DocumentPullDiagnosticName,
                new DocumentDiagnosticsParams
            {
                PreviousResultId = results.Single().ResultId,
                TextDocument     = ProtocolConversions.DocumentToTextDocumentIdentifier(document)
            },
                new LSP.ClientCapabilities(),
                clientName : null,
                CancellationToken.None
                );

            Assert.Null(results.Single().Diagnostics);
            Assert.Null(results.Single().ResultId);
        }
예제 #2
0
        public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
        {
            // This provider is exported for all workspaces - so limit it to just our workspace.
            var(document, span, cancellationToken) = context;
            if (document.Project.Solution.Workspace.Kind != WorkspaceKind.AnyCodeRoslynWorkspace)
            {
                return;
            }

            var lspClient = _roslynLspClientServiceFactory.ActiveLanguageServerClient;

            if (lspClient == null)
            {
                return;
            }

            var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);

            var diagnostics = await _diagnosticAnalyzerService.GetDiagnosticsForSpanAsync(document, span, cancellationToken : cancellationToken).ConfigureAwait(false);

            var diagnostic = diagnostics?.FirstOrDefault();

            if (diagnostic != null)
            {
                span = diagnostic.TextSpan;
            }

            var codeActionParams = new LSP.CodeActionParams
            {
                TextDocument = ProtocolConversions.DocumentToTextDocumentIdentifier(document),
                Range        = ProtocolConversions.TextSpanToRange(span, text)
            };

            var commands = await lspClient.RequestAsync(LSP.Methods.TextDocumentCodeAction.ToLSRequest(), codeActionParams, cancellationToken).ConfigureAwait(false);

            if (commands == null)
            {
                return;
            }

            foreach (var command in commands)
            {
                if (LanguageServicesUtils.TryParseJson(command, out LSP.Command lspCommand))
                {
                    // The command can either wrap a Command or a CodeAction.
                    // If a Command, leave it unchanged; we want to dispatch it to the host to execute.
                    // If a CodeAction, unwrap the CodeAction so the guest can run it locally.
                    var commandArguments = lspCommand.Arguments.Single();

                    if (LanguageServicesUtils.TryParseJson(commandArguments, out LSP.CodeAction lspCodeAction))
                    {
                        context.RegisterRefactoring(new RoslynRemoteCodeAction(document, lspCodeAction.Command, lspCodeAction.Edit, lspCodeAction.Title, lspClient));
                    }
                    else
                    {
                        context.RegisterRefactoring(new RoslynRemoteCodeAction(document, lspCommand, lspCommand?.Title, lspClient));
                    }
                }
            }
        }
        public async Task <TReport[]?> HandleRequestAsync(TDiagnosticsParams diagnosticsParams, RequestContext context, CancellationToken cancellationToken)
        {
            Contract.ThrowIfNull(context.Solution);

            using var progress = BufferedProgress.Create(GetProgress(diagnosticsParams));

            // Get the set of results the request said were previously reported.
            var previousResults = GetPreviousResults(diagnosticsParams);

            var documentToPreviousResultId = new Dictionary <Document, string?>();

            if (previousResults != null)
            {
                // Go through the previousResults and check if we need to remove diagnostic information for any documents
                foreach (var previousResult in previousResults)
                {
                    if (previousResult.TextDocument != null)
                    {
                        var document = context.Solution.GetDocument(previousResult.TextDocument);
                        if (document == null)
                        {
                            // We can no longer get this document, return null for both diagnostics and resultId
                            progress.Report(CreateReport(previousResult.TextDocument, diagnostics: null, resultId: null));
                        }
                        else
                        {
                            // Cache the document to previousResultId mapping so we can easily retrieve the resultId later.
                            documentToPreviousResultId[document] = previousResult.PreviousResultId;
                        }
                    }
                }
            }

            // Go through the documents that we need to process and call XamlPullDiagnosticService to get the diagnostic report
            foreach (var document in GetDocuments(context))
            {
                var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);

                var documentId = ProtocolConversions.DocumentToTextDocumentIdentifier(document);

                // If we can get a previousId of the document, use it,
                // otherwise use null as the previousId to pass into the XamlPullDiagnosticService
                var previousResultId = documentToPreviousResultId.TryGetValue(document, out var id) ? id : null;

                // Call XamlPullDiagnosticService to get the diagnostic report for this document.
                // We will compute what to report inside XamlPullDiagnosticService, for example, whether we should keep using the previousId or use a new resultId,
                // and the handler here just return the result get from XamlPullDiagnosticService.
                var diagnosticReport = await _xamlDiagnosticService.GetDiagnosticReportAsync(document, previousResultId, cancellationToken).ConfigureAwait(false);

                progress.Report(CreateReport(
                                    documentId,
                                    ConvertToVSDiagnostics(diagnosticReport.Diagnostics, document, text),
                                    diagnosticReport.ResultId));
            }

            return(progress.GetValues());
        }
예제 #4
0
        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, 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
            }
        }
예제 #5
0
        public override async Task ProvideCompletionsAsync(CompletionContext context)
        {
            // This provider is exported for all workspaces - so limit it to just our workspace & the debugger's intellisense workspace
            if (context.Document.Project.Solution.Workspace.Kind != WorkspaceKind.AnyCodeRoslynWorkspace &&
                context.Document.Project.Solution.Workspace.Kind != StringConstants.DebuggerIntellisenseWorkspaceKind)
            {
                return;
            }

            var lspClient = _roslynLspClientServiceFactory.ActiveLanguageServerClient;

            if (lspClient == null)
            {
                return;
            }

            var text = await context.Document.GetTextAsync(context.CancellationToken).ConfigureAwait(false);

            var completionParams = new LSP.CompletionParams
            {
                TextDocument = ProtocolConversions.DocumentToTextDocumentIdentifier(context.Document),
                Position     = ProtocolConversions.LinePositionToPosition(text.Lines.GetLinePosition(context.Position)),
                Context      = new LSP.CompletionContext {
                    TriggerCharacter = context.Trigger.Character.ToString(), TriggerKind = GetTriggerKind(context.Trigger)
                }
            };

            var completionObject = await lspClient.RequestAsync(LSP.Methods.TextDocumentCompletion.ToLSRequest(), completionParams, context.CancellationToken).ConfigureAwait(false);

            if (completionObject == null)
            {
                return;
            }

            var completionList = ((JToken)completionObject).ToObject <RoslynCompletionItem[]>();

            foreach (var item in completionList)
            {
                ImmutableArray <string> tags;
                if (item.Tags != null)
                {
                    tags = item.Tags.AsImmutable();
                }
                else
                {
                    var glyph = ProtocolConversions.CompletionItemKindToGlyph(item.Kind);
                    tags = GlyphTags.GetTags(glyph);
                }

                var properties = ImmutableDictionary.CreateBuilder <string, string>();
                if (!string.IsNullOrEmpty(item.Detail))
                {
                    // The display text is encoded as TaggedText | value
                    properties.Add("Description", $"Text|{item.Detail}");
                }

                properties.Add("InsertionText", item.InsertText);
                properties.Add("ResolveData", JToken.FromObject(item).ToString());
                var completionItem = CompletionItem.Create(item.Label, item.FilterText, item.SortText, properties: properties.ToImmutable(), tags: tags);
                context.AddItem(completionItem);
            }
        }