コード例 #1
0
        // 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);
        }
コード例 #2
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.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
            }
        }
コード例 #3
0
ファイル: OnTypeRenameHandler.cs プロジェクト: belav/roslyn
        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
            });
        }
コード例 #4
0
        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);
        }
コード例 #5
0
ファイル: RenameHandler.cs プロジェクト: mbpframework/roslyn
        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);
        }
コード例 #6
0
        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)
                }
            });
        }
コード例 #7
0
        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);
        }
コード例 #8
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));
                }
            }
        }
コード例 #9
0
            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);
                }
            }
コード例 #10
0
        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);
        }
コード例 #11
0
        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)
            }));
        }
コード例 #12
0
        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);
        }
コード例 #13
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());
        }
コード例 #14
0
    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);
    }
コード例 #15
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));
        }
コード例 #16
0
        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
                    }
                });
            }
        }
コード例 #17
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));
                }
            }
        }
コード例 #18
0
        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());
        }
コード例 #19
0
        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());
        }
コード例 #20
0
        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());
        }
コード例 #21
0
        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);
        }
コード例 #22
0
            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)
                });
            }
コード例 #23
0
ファイル: LanguageServer.cs プロジェクト: waiyiptam/roslyn
        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());
        }
コード例 #24
0
        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),
                });
            }
コード例 #25
0
        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());
        }
コード例 #26
0
        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());
        }
コード例 #27
0
        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,
            });
コード例 #28
0
ファイル: HoverHandler.cs プロジェクト: mpeyrotc/roslyn
        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));
        }
コード例 #29
0
ファイル: DidChangeHandler.cs プロジェクト: belav/roslyn
        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>());
        }
コード例 #30
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());
        }