コード例 #1
0
        /// <nodoc/>
        public Result <SignatureHelp, ResponseError> SignatureHelp(TextDocumentPositionParams positionParams, CancellationToken token)
        {
            // TODO: support cancellation
            if (!TryFindNode(positionParams, out var nodeAtPosition))
            {
                return(Result <SignatureHelp, ResponseError> .Success(new SignatureHelp()));
            }

            ICallExpression callExpression = GetContainingCallExpression(nodeAtPosition);

            if (callExpression == null)
            {
                Logger.LanguageServerNonCriticalInternalIssue(LoggingContext, FormattableStringEx.I($"Couldn't find call expression containing {nodeAtPosition.GetFormattedText()}"));
                return(Result <SignatureHelp, ResponseError> .Success(new SignatureHelp()));
            }

            var callSymbol = TypeChecker.GetSymbolAtLocation(callExpression.Expression);

            // If the user has typed a call expresion to a symbol (function) that doesn't exist (i.e. "foo.bar()")
            // Then just issue a debug writeline and a success instead of crashing.
            // There is going to be a red-line squiggle under it anyway.
            if (callSymbol == null)
            {
                Logger.LanguageServerNonCriticalInternalIssue(LoggingContext, FormattableStringEx.I($"Couldn't find symbol for call expression containing {nodeAtPosition.GetFormattedText()}"));
                return(Result <SignatureHelp, ResponseError> .Success(new SignatureHelp()));
            }

            var signature = TypeChecker.GetSignaturesOfType(TypeChecker.GetTypeAtLocation(callExpression.Expression), SignatureKind.Call).FirstOrDefault();

            if (signature == null)
            {
                Logger.LanguageServerNonCriticalInternalIssue(LoggingContext, FormattableStringEx.I($"Couldn't find call signature for call expression containing {nodeAtPosition.GetFormattedText()}"));
                return(Result <SignatureHelp, ResponseError> .Success(new SignatureHelp()));
            }

            var functionDeclaration = DScriptFunctionSignature.FromSignature(callSymbol.Name, signature);

            var parameterInformations = functionDeclaration.FormattedParameterNames.Select(formattedParameterName => new ParameterInformation()
            {
                Label = formattedParameterName,
            });

            int activeParameterIndex = DScriptFunctionSignature.GetActiveParameterIndex(callExpression, nodeAtPosition);

            var signatureHelp = new SignatureHelp()
            {
                Signatures = new SignatureInformation[]
                {
                    new SignatureInformation()
                    {
                        Label         = functionDeclaration.FormattedFullFunctionSignature,
                        Parameters    = parameterInformations.ToArray(),
                        Documentation = DocumentationUtilities.GetDocumentationForSymbolAsString(callSymbol),
                    },
                },
                ActiveParameter = activeParameterIndex,
                ActiveSignature = 0,
            };

            return(Result <SignatureHelp, ResponseError> .Success(signatureHelp));
        }
コード例 #2
0
        public async Task HandleRequestAsync_CSharpProjection_InvokesCSharpLanguageServer()
        {
            // Arrange
            var called = false;

            var expectedContents = new SumType <SumType <string, MarkedString>, SumType <string, MarkedString>[], MarkupContent>(
                new MarkedString()
            {
                Language = "markdown",
                Value    = "Hover Details"
            }
                );

            var lspResponse = new Hover()
            {
                Range = new Range()
                {
                    Start = new Position(10, 0),
                    End   = new Position(10, 1)
                },
                Contents = expectedContents
            };

            var expectedItem = new Hover()
            {
                Range = new Range()
                {
                    Start = new Position(0, 0),
                    End   = new Position(0, 1)
                },
                Contents = expectedContents
            };

            var hoverRequest = new TextDocumentPositionParams()
            {
                TextDocument = new TextDocumentIdentifier()
                {
                    Uri = Uri
                },
                Position = new Position(0, 1)
            };

            var documentManager = new TestDocumentManager();

            documentManager.AddDocument(Uri, Mock.Of <LSPDocumentSnapshot>());

            var requestInvoker = new Mock <LSPRequestInvoker>();

            requestInvoker
            .Setup(r => r.RequestServerAsync <TextDocumentPositionParams, Hover>(It.IsAny <string>(), It.IsAny <LanguageServerKind>(), It.IsAny <TextDocumentPositionParams>(), It.IsAny <CancellationToken>()))
            .Callback <string, LanguageServerKind, TextDocumentPositionParams, CancellationToken>((method, serverKind, hoverParams, ct) =>
            {
                Assert.Equal(Methods.TextDocumentHoverName, method);
                Assert.Equal(LanguageServerKind.CSharp, serverKind);
                called = true;
            })
            .Returns(Task.FromResult(lspResponse));

            var projectionResult = new ProjectionResult()
            {
                LanguageKind = RazorLanguageKind.CSharp,
            };
            var projectionProvider = new Mock <LSPProjectionProvider>();

            projectionProvider.Setup(p => p.GetProjectionAsync(It.IsAny <LSPDocumentSnapshot>(), It.IsAny <Position>(), It.IsAny <CancellationToken>())).Returns(Task.FromResult(projectionResult));

            var remappingResult = new RazorMapToDocumentRangeResponse()
            {
                Range = new Range()
                {
                    Start = new Position(0, 0),
                    End   = new Position(0, 1)
                }
            };
            var documentMappingProvider = new Mock <LSPDocumentMappingProvider>();

            documentMappingProvider.Setup(d => d.MapToDocumentRangeAsync(RazorLanguageKind.CSharp, It.IsAny <Uri>(), It.IsAny <Range>(), It.IsAny <CancellationToken>())).
            Returns(Task.FromResult(remappingResult));

            var hoverHandler = new HoverHandler(JoinableTaskContext, requestInvoker.Object, documentManager, projectionProvider.Object, documentMappingProvider.Object);

            // Act
            var result = await hoverHandler.HandleRequestAsync(hoverRequest, new ClientCapabilities(), CancellationToken.None).ConfigureAwait(false);

            // Assert
            Assert.True(called);
            Assert.Equal(expectedItem.Contents, result.Contents);
            Assert.Equal(expectedItem.Range, result.Range);
        }
コード例 #3
0
 public Task <DocumentHighlight[]> GetTextDocumentDocumentHighlightsAsync(TextDocumentPositionParams textDocumentPositionParams, CancellationToken cancellationToken)
 => _protocol.ExecuteRequestAsync <TextDocumentPositionParams, DocumentHighlight[]>(Methods.TextDocumentDocumentHighlightName,
                                                                                    textDocumentPositionParams, _clientCapabilities, _clientName, cancellationToken);
コード例 #4
0
 public Task <LSP.Location[]> GetTextDocumentImplementationsAsync(TextDocumentPositionParams textDocumentPositionParams, CancellationToken cancellationToken)
 => _protocol.ExecuteRequestAsync <TextDocumentPositionParams, LSP.Location[]>(Methods.TextDocumentImplementationName,
                                                                               textDocumentPositionParams, _clientCapabilities, _clientName, cancellationToken);
コード例 #5
0
ファイル: InProcLanguageServer.cs プロジェクト: tmcmil/roslyn
 public Task <Hover?> GetTextDocumentDocumentHoverAsync(TextDocumentPositionParams textDocumentPositionParams, CancellationToken cancellationToken)
 => _requestHandlerProvider.ExecuteRequestAsync <TextDocumentPositionParams, Hover?>(_queue, Methods.TextDocumentHoverName,
                                                                                     textDocumentPositionParams, _clientCapabilities, _clientName, cancellationToken);
コード例 #6
0
ファイル: InProcLanguageServer.cs プロジェクト: tmcmil/roslyn
 public Task <SignatureHelp> GetTextDocumentSignatureHelpAsync(TextDocumentPositionParams textDocumentPositionParams, CancellationToken cancellationToken)
 => _requestHandlerProvider.ExecuteRequestAsync <TextDocumentPositionParams, SignatureHelp>(_queue, Methods.TextDocumentSignatureHelpName,
                                                                                            textDocumentPositionParams, _clientCapabilities, _clientName, cancellationToken);
コード例 #7
0
ファイル: LanguageServerTarget.cs プロジェクト: oahceh/roslyn
        public Task <LSP.SignatureHelp?> GetTextDocumentSignatureHelpAsync(TextDocumentPositionParams textDocumentPositionParams, CancellationToken cancellationToken)
        {
            Contract.ThrowIfNull(_clientCapabilities, $"{nameof(InitializeAsync)} has not been called.");

            return(RequestDispatcher.ExecuteRequestAsync <TextDocumentPositionParams, LSP.SignatureHelp?>(Queue, Methods.TextDocumentSignatureHelpName, textDocumentPositionParams, _clientCapabilities, ClientName, cancellationToken));
        }
コード例 #8
0
        public Task <SumType <Location[]?, VSInternalReferenceItem[]?> > GoToImplementationAsync(TextDocumentPositionParams positionParams, CancellationToken cancellationToken)
        {
            if (positionParams is null)
            {
                throw new ArgumentNullException(nameof(positionParams));
            }

            return(ExecuteRequestAsync <TextDocumentPositionParams, SumType <Location[]?, VSInternalReferenceItem[]?> >(Methods.TextDocumentImplementationName, positionParams, ClientCapabilities, cancellationToken));
        }
コード例 #9
0
 /// <nodoc />
 public Result <ArrayOrObject <Location, Location>, ResponseError> GetDefinitionAtPosition(TextDocumentPositionParams documentPosition, CancellationToken token)
 {
     return(m_gotoDefinitionProvider.GetDefinitionAtPosition(documentPosition, token));
 }
コード例 #10
0
        public Task <SignatureHelp> GetTextDocumentSignatureHelpAsync(TextDocumentPositionParams textDocumentPositionParams, CancellationToken cancellationToken)
        {
            Contract.ThrowIfNull(_clientCapabilities, $"{nameof(InitializeAsync)} has not been called.");

            return(_requestHandlerProvider.ExecuteRequestAsync <TextDocumentPositionParams, SignatureHelp>(_queue, Methods.TextDocumentSignatureHelpName, textDocumentPositionParams, _clientCapabilities, _clientName, cancellationToken));
        }
コード例 #11
0
 /// <nodoc />
 public Result <ArrayOrObject <CompletionItem, CompletionList>, ResponseError> Completion(TextDocumentPositionParams position, CancellationToken token)
 {
     return(m_autoCompleteProvider.Completion(position, token));
 }
コード例 #12
0
 /// <nodoc />
 public Result <SignatureHelp, ResponseError> SignatureHelp(TextDocumentPositionParams textDocumentPosition, CancellationToken token)
 {
     return(m_signatureHelpProvider.SignatureHelp(textDocumentPosition, token));
 }
コード例 #13
0
 /// <nodoc />
 public Result <Hover, ResponseError> Hover(TextDocumentPositionParams position, CancellationToken token)
 {
     return(m_hoverProvider.Hover(position, token));
 }
コード例 #14
0
 /// <summary>
 /// Returns an array with all usages of the identifier at the given position (if any) as DocumentHighlights.
 /// Returns null if the given file is listed as to be ignored,
 /// or if some parameters are unspecified (null),
 /// or if the specified position is not a valid position within the currently processed file content,
 /// or if no identifier exists at the specified position at this time.
 /// </summary>
 public DocumentHighlight[] DocumentHighlights(TextDocumentPositionParams param) =>
 ValidFileUri(param?.TextDocument?.Uri) && !IgnoreFile(param.TextDocument.Uri) ? this.Projects.DocumentHighlights(param) : null;
コード例 #15
0
        /// <summary>
        /// Implements the "textDocument/hover" portion of the language server protocol.
        /// </summary>
        public Result <Hover, ResponseError> Hover(TextDocumentPositionParams positionParams, CancellationToken token)
        {
            // TODO: support cancellation
            Contract.Requires(positionParams.Position != null);

            if (!TryFindNode(positionParams, out var node))
            {
                return(Result <Hover, ResponseError> .Success(null));
            }

            // Label names are things such as "break", "continue", "jump" for which
            // we do not need to provide any hover.
            if (DScriptUtilities.IsLabelName(node))
            {
                return(Result <Hover, ResponseError> .Success(null));
            }

            // Attempt to get the type from the node
            var typeAtLocation = TypeChecker.GetTypeAtLocation(node);

            if ((typeAtLocation.Flags & TypeFlags.Any) != TypeFlags.None && node.Kind == SyntaxKind.Identifier)
            {
                // Now if we are likely to return any (unknown), and we are on an identifier
                // its parent is a type-reference then let's display that instead.
                // An example of this is "someTemplateVariable.merge<>"
                if (node.Parent?.Kind == SyntaxKind.TypeReference)
                {
                    node = node.Parent;
                }
                else if (node.Parent?.Kind == SyntaxKind.QualifiedName)
                {
                    // The same type of thing can happen with values referenced
                    // from an imported value (i.e. qualified name) such as
                    // import "A" from "SomeModule";
                    // let z = A.B.C.value;
                    // "value" is the identifier and "A.B.C" are the qualified names.
                    // As you walk up the chain, you will eventually find "A" which is
                    // a type-reference.

                    var nodeWalk = node.Parent;
                    while (nodeWalk != null && nodeWalk.Kind == SyntaxKind.QualifiedName)
                    {
                        nodeWalk = nodeWalk.Parent;
                    }

                    if (nodeWalk.Kind == SyntaxKind.TypeReference)
                    {
                        node = nodeWalk;
                    }
                }
            }

            // TODO: Not sure why GetSymbolAtLocation isn't enough
            var symbol = TypeChecker.GetSymbolAtLocation(node) ?? node.Symbol ?? node.ResolvedSymbol;

            // Find the first declaration. We will use it to special case a few things below.
            var firstDeclaration = symbol?.GetFirstDeclarationOrDefault();

            // Grab the symbol name from the first declaration.
            string symbolAsString = firstDeclaration?.Name?.Text;

            // Handle a few special cases before we default to showing the type.
            // Typically these special cases are where hover is performed on a declaration of
            // things such as an enum, import statement (and eventually others)
            if (firstDeclaration != null && symbolAsString != null)
            {
                // Special case import * from "" and import {} from "" and import {x as y} from ""
                // So we can show the user a hover that says "import {} from 'module' so they can easily
                // see where it is coming from.
                if (firstDeclaration.Kind == SyntaxKind.ImportSpecifier || firstDeclaration.Kind == SyntaxKind.NamespaceImport)
                {
                    IImportClause importClause;
                    if (firstDeclaration.Kind == SyntaxKind.ImportSpecifier)
                    {
                        var importSpecifier = firstDeclaration.Cast <IImportSpecifier>();
                        var namedImports    = importSpecifier.Parent.Cast <INamedImports>();
                        importClause = namedImports.Parent.Cast <IImportClause>();
                    }
                    else
                    {
                        var namespaceImport = firstDeclaration.Cast <INamespaceImport>();
                        importClause = namespaceImport.Parent.Cast <IImportClause>();
                    }

                    var importDeclaration = importClause.Parent.Cast <IImportDeclaration>();

                    return(CreateScriptHoverWithCodeSnippetAndDocumentation(
                               symbol,
                               node,
                               string.Format(BuildXL.Ide.LanguageServer.Strings.HoverImportFormat, symbolAsString, importDeclaration.ModuleSpecifier.ToDisplayString())));
                }

                // For interface declarations, just show a hover of "interface X"
                if (firstDeclaration.Kind == SyntaxKind.InterfaceDeclaration)
                {
                    return(CreateScriptHoverWithCodeSnippetAndDocumentation(symbol, node, firstDeclaration.GetFormattedText()));
                    // We have decided to show the formatted text of the declaration as it allows
                    // the user to browse the entire interface.
                    // If we decide we want a more conscise hover, we can uncomment the following
                    // code.
                    //var interfaceDeclaration = firstDeclaration.Cast<IInterfaceDeclaration>();
                    //return CreateScriptHoverWithCodeSnippetAndDocumentation(symbol, node, string.Format(Strings.HoverInterfaceFormat, interfaceDeclaration.Name.Text));
                }

                // For enum declarations, show a hover of "enum { x, y, z}"
                if (firstDeclaration.Kind == SyntaxKind.EnumDeclaration)
                {
                    return(CreateScriptHoverWithCodeSnippetAndDocumentation(symbol, node, firstDeclaration.GetFormattedText()));
                    // We have decided to show the formatted text of the declaration as it allows
                    // the user to browse the entire enumeration.
                    // If we decide we want a more conscise hover, we can uncomment the following
                    // code.

                    /*
                     * var enumDeclaration = firstDeclaration.Cast<IEnumDeclaration>();
                     *
                     * var memberStrings = new List<string>();
                     * foreach (var member in enumDeclaration.Members)
                     * {
                     *  memberStrings.Add(member.Cast<IEnumMember>().Name.Text);
                     * }
                     *
                     * var memberString = memberStrings.Aggregate((current, next) => string.Format(Strings.HoverEnumMemberFormat, current, next));
                     *
                     * return CreateScriptHoverWithCodeSnippetAndDocumentation(symbol, node, string.Format(Strings.HoverEnumFormat, enumDeclaration.Name.Text, memberString));
                     */
                }


                // Handle things that are function-like (such as interface methods, functions, etc).
                var functionLike = firstDeclaration.As <IFunctionLikeDeclaration>();
                if (functionLike != null)
                {
                    return(CreateScriptHoverWithCodeSnippetAndDocumentation(symbol, node, functionLike.ToDisplayString()));
                }
            }

            // So if we can't find a suitable declaration for the node to display for hover,
            // Then we will display the type associated the symbol, which is super useful
            // for things like variable declarations, etc. (You'll notice that variable declarations are not
            // handled above, for exactly this purpose).
            if (node != null && symbol != null)
            {
                return(CreateHoverBasedOnTypeInformation(symbol, node, symbolAsString));
            }

            return(Result <Hover, ResponseError> .Success(null));
        }
コード例 #16
0
 /// <summary>
 /// Returns a list of suggested completion items for the given location.
 /// <para/>
 /// Returns null if the given file is listed as to be ignored, or if the given parameter or its uri or position is null.
 /// </summary>
 public CompletionList Completions(TextDocumentPositionParams param) =>
 ValidFileUri(param?.TextDocument?.Uri) && !IgnoreFile(param.TextDocument.Uri)
     ? this.Projects.Completions(param)
     : null;
コード例 #17
0
        /// <nodoc />
        public Result <ArrayOrObject <Location, Location>, ResponseError> GetDefinitionAtPosition(TextDocumentPositionParams @params, CancellationToken token)
        {
            // TODO: support cancellation
            if (!TryFindNode(@params, out var node))
            {
                return(SilentError());
            }

            // TODO: consider special hint/message if the current node is a template expression with interpolated expressions.
            if (node.Kind == SyntaxKind.NoSubstitutionTemplateLiteral && node.Parent.Kind == SyntaxKind.TaggedTemplateExpression &&
                node.Parent.Cast <ITaggedTemplateExpression>().IsWellKnownTemplateExpression(out var name))
            {
                return(DefinitionForTemplateExpression(node, name));
            }

            var result = GetDefinitionFromVariableDeclarationList(node);

            if (result != null)
            {
                return(result);
            }

            if (DScriptUtilities.IsJumpStatementTarget(node))
            {
                var labelName = node.Cast <IIdentifier>().Text;
                var label     = DScriptUtilities.GetTargetLabel(node.Parent, labelName);

                // TODO: saqadri - Port
                // return label ? [createDefinitionInfo(label, ScriptElementKind.label, labelName, /*containerName*/ undefined)] : undefined;
                return(Success(new[]
                {
                    GetLocationFromNode(label),
                }));
            }

            // TODO: saqadri - port
            // Triple slash reference comments
            // const comment = forEach(sourceFile.referencedFiles, r => (r.pos <= position && position < r.end) ? r : undefined);
            // if (comment)
            // {
            //    const referenceFile = tryResolveScriptReference(program, sourceFile, comment);
            //    if (referenceFile)
            //    {
            //        return [{
            //            fileName: referenceFile.fileName,
            //            textSpan: createTextSpanFromBounds(0, 0),
            //            kind: ScriptElementKind.scriptElement,
            //            name: comment.fileName,
            //            containerName: undefined,
            //            containerKind: undefined
            //        }];
            //    }
            //    return undefined;
            // }
            var symbol = TypeChecker.GetSymbolAtLocation(node) ?? node.Symbol ?? node.ResolvedSymbol;

            // Could not find a symbol e.g. node is string or number keyword,
            // or the symbol was an internal symbol and does not have a declaration e.g. undefined symbol
            if (symbol == null)
            {
                // return null;
                return(SilentError());
            }

            // If this is an alias, and the request came at the declaration location
            // get the aliased symbol instead. This allows for goto def on an import e.g.
            //   import {A, B} from "mod";
            // to jump to the implementation directly.
            if ((symbol.Flags & SymbolFlags.Alias) != SymbolFlags.None)
            {
                var declaration = symbol.Declarations.FirstOrDefault();

                // Go to the original declaration for cases:
                //
                //   (1) when the aliased symbol was declared in the location(parent).
                //   (2) when the aliased symbol is originating from a named import.
                if (node.Kind == SyntaxKind.Identifier &&
                    declaration != null &&
                    (node.Parent.ResolveUnionType() == declaration.ResolveUnionType() ||
                     (declaration.Kind == SyntaxKind.ImportSpecifier && declaration?.Parent.Kind == SyntaxKind.NamedImports)))
                {
                    symbol = TypeChecker.GetAliasedSymbol(symbol);
                }
            }

            // Because name in short-hand property assignment has two different meanings: property name and property value,
            // using go-to-definition at such position should go to the variable declaration of the property value rather than
            // go to the declaration of the property name (in this case stay at the same position). However, if go-to-definition
            // is performed at the location of property access, we would like to go to definition of the property in the short-hand
            // assignment. This case and others are handled by the following code.
            if (node.Parent.Kind == SyntaxKind.ShorthandPropertyAssignment)
            {
                var shorthandSymbol = TypeChecker.GetShorthandAssignmentValueSymbol(symbol.ValueDeclaration);
                if (shorthandSymbol == null)
                {
                    return(Success(new Location[] { }));
                }

                var shorthandDeclaratons = shorthandSymbol.GetDeclarations();

                // var shorthandSymbolKind = GetSymbolKind(shorthandSymbol, node);
                // var shorthandSymbolName = TypeChecker.SymbolToString(shorthandSymbol);
                // var shorthandContainerName = TypeChecker.SymbolToString(shorthandSymbol.Parent, node);
                return(Success(shorthandDeclaratons.Select(
                                   declaration => GetLocationFromNode(declaration)).ToArray()));
            }

            if (DScriptUtilities.IsNameOfPropertyAssignment(node))
            {
                return(GetDefinitionForPropertyAssignment(node));
            }

            return(GetDefinitionFromSymbol(symbol, node));
        }
コード例 #18
0
        public async Task HandleRequestAsync_CSharpProjection_InvokesCSharpLanguageServer_FailsRemappingResultRangeWithHostVersionChanged()
        {
            // Arrange
            var called = false;

            var expectedContents = new SumType <SumType <string, MarkedString>, SumType <string, MarkedString>[], MarkupContent>(
                new MarkedString()
            {
                Language = "markdown",
                Value    = "Hover Details"
            }
                );

            var lspResponse = new Hover()
            {
                Range = new Range()
                {
                    Start = new Position(10, 0),
                    End   = new Position(10, 1)
                },
                Contents = expectedContents
            };

            var hoverRequest = new TextDocumentPositionParams()
            {
                TextDocument = new TextDocumentIdentifier()
                {
                    Uri = Uri
                },
                Position = new Position(0, 1)
            };

            var requestInvoker = new Mock <LSPRequestInvoker>(MockBehavior.Strict);

            requestInvoker
            .Setup(r => r.ReinvokeRequestOnServerAsync <TextDocumentPositionParams, Hover>(
                       It.IsAny <ITextBuffer>(),
                       It.IsAny <string>(),
                       It.IsAny <string>(),
                       It.IsAny <TextDocumentPositionParams>(),
                       It.IsAny <CancellationToken>()))
            .Callback <ITextBuffer, string, string, TextDocumentPositionParams, CancellationToken>((textBuffer, method, clientName, hoverParams, ct) =>
            {
                Assert.Equal(Methods.TextDocumentHoverName, method);
                Assert.Equal(RazorLSPConstants.RazorCSharpLanguageServerName, clientName);
                called = true;
            })
            .Returns(Task.FromResult(new ReinvocationResponse <Hover>("LanguageClientName", lspResponse)));

            var projectionResult = new ProjectionResult()
            {
                LanguageKind = RazorLanguageKind.CSharp,
            };
            var projectionProvider = new Mock <LSPProjectionProvider>(MockBehavior.Strict);

            projectionProvider.Setup(p => p.GetProjectionAsync(It.IsAny <LSPDocumentSnapshot>(), It.IsAny <Position>(), It.IsAny <CancellationToken>())).Returns(Task.FromResult(projectionResult));

            var remappingResult = new RazorMapToDocumentRangesResponse()
            {
                Ranges = new[] { new Range() },
                HostDocumentVersion = 1
            };
            var documentMappingProvider = new Mock <LSPDocumentMappingProvider>(MockBehavior.Strict);

            documentMappingProvider.Setup(d => d.MapToDocumentRangesAsync(RazorLanguageKind.CSharp, It.IsAny <Uri>(), It.IsAny <Range[]>(), It.IsAny <CancellationToken>())).
            Returns(Task.FromResult(remappingResult));

            var hoverHandler = new HoverHandler(requestInvoker.Object, DocumentManager, projectionProvider.Object, documentMappingProvider.Object, LoggerProvider);

            // Act
            var result = await hoverHandler.HandleRequestAsync(hoverRequest, new ClientCapabilities(), CancellationToken.None).ConfigureAwait(false);

            // Assert
            Assert.True(called);
            Assert.Null(result);
        }
コード例 #19
0
        public async Task HandleRequestAsync_CSharpProjection_InvokesCSharpLanguageServer()
        {
            // Arrange
            var invokedLSPRequest   = false;
            var invokedRemapRequest = false;
            var expectedLocation    = GetLocation(5, 5, 5, 5, Uri);

            var virtualCSharpUri = new Uri("C:/path/to/file.razor.g.cs");
            var csharpLocation   = GetLocation(100, 100, 100, 100, virtualCSharpUri);
            var requestInvoker   = new Mock <LSPRequestInvoker>(MockBehavior.Strict);

            requestInvoker
            .Setup(r => r.ReinvokeRequestOnServerAsync <TextDocumentPositionParams, Location[]>(
                       It.IsAny <ITextBuffer>(),
                       It.IsAny <string>(),
                       It.IsAny <string>(),
                       It.IsAny <TextDocumentPositionParams>(),
                       It.IsAny <CancellationToken>()))
            .Callback <ITextBuffer, string, string, TextDocumentPositionParams, CancellationToken>((textBuffer, method, clientName, implementationParams, ct) =>
            {
                Assert.Equal(Methods.TextDocumentImplementationName, method);
                Assert.Equal(RazorLSPConstants.RazorCSharpLanguageServerName, clientName);
                invokedLSPRequest = true;
            })
            .Returns(Task.FromResult(new ReinvocationResponse <Location[]>("LanguageClientName", new[] { csharpLocation })));

            var projectionResult = new ProjectionResult()
            {
                LanguageKind = RazorLanguageKind.CSharp,
            };
            var projectionProvider = new Mock <LSPProjectionProvider>(MockBehavior.Strict);

            projectionProvider.Setup(p => p.GetProjectionAsync(It.IsAny <LSPDocumentSnapshot>(), It.IsAny <Position>(), It.IsAny <CancellationToken>())).Returns(Task.FromResult(projectionResult));

            var documentMappingProvider = new Mock <LSPDocumentMappingProvider>(MockBehavior.Strict);

            documentMappingProvider
            .Setup(d => d.RemapLocationsAsync(It.IsAny <Location[]>(), It.IsAny <CancellationToken>()))
            .Callback <Location[], CancellationToken>((locations, token) =>
            {
                Assert.Equal(csharpLocation, locations[0]);
                invokedRemapRequest = true;
            })
            .Returns(Task.FromResult(Array.Empty <Location>()));

            var implementationHandler = new GoToImplementationHandler(requestInvoker.Object, DocumentManager, projectionProvider.Object, documentMappingProvider.Object, LoggerProvider);
            var implementationRequest = new TextDocumentPositionParams()
            {
                TextDocument = new TextDocumentIdentifier()
                {
                    Uri = Uri
                },
                Position = new Position(10, 5)
            };

            // Act
            var result = await implementationHandler.HandleRequestAsync(implementationRequest, new ClientCapabilities(), CancellationToken.None).ConfigureAwait(false);

            // Assert
            Assert.True(invokedLSPRequest);
            Assert.True(invokedRemapRequest);

            // Actual remapping behavior is tested elsewhere.
        }
コード例 #20
0
ファイル: InProcLanguageServer.cs プロジェクト: tmcmil/roslyn
 public Task <LSP.Location[]> GetTextDocumentImplementationsAsync(TextDocumentPositionParams textDocumentPositionParams, CancellationToken cancellationToken)
 => _requestHandlerProvider.ExecuteRequestAsync <TextDocumentPositionParams, LSP.Location[]>(_queue, Methods.TextDocumentImplementationName,
                                                                                             textDocumentPositionParams, _clientCapabilities, _clientName, cancellationToken);
コード例 #21
0
        public override async Task <SignatureHelp> SignatureHelp(TextDocumentPositionParams @params, CancellationToken token)
        {
            var uri = @params.textDocument.uri;

            TraceMessage($"Signatures in {uri} at {@params.position}");

            var analysis = GetEntry(@params.textDocument) is ProjectEntry entry
                ? await entry.GetAnalysisAsync(waitingTimeout : 50, cancellationToken : token)
                : null;

            if (analysis == null)
            {
                TraceMessage($"No analysis found for {uri}");
                return(new SignatureHelp());
            }

            ProjectFiles.GetEntry(@params.textDocument, @params._version, out _, out var tree);

            IEnumerable <IOverloadResult> overloads;
            int activeSignature = -1, activeParameter = -1;

            if (!string.IsNullOrEmpty(@params._expr))
            {
                TraceMessage($"Getting signatures for {@params._expr}");
                overloads = analysis.GetSignatures(@params._expr, @params.position);
            }
            else
            {
                var finder = new ExpressionFinder(tree, new GetExpressionOptions {
                    Calls = true
                });
                var index = tree.LocationToIndex(@params.position);
                if (finder.GetExpression(@params.position) is CallExpression callExpr)
                {
                    TraceMessage($"Getting signatures for {callExpr.ToCodeString(tree, CodeFormattingOptions.Traditional)}");
                    overloads       = analysis.GetSignatures(callExpr.Target, @params.position);
                    activeParameter = -1;
                    if (callExpr.GetArgumentAtIndex(tree, index, out activeParameter) && activeParameter < 0)
                    {
                        // Returned 'true' and activeParameter == -1 means that we are after
                        // the trailing comma, so assume partially typed expression such as 'pow(x, y, |)
                        activeParameter = callExpr.Args.Count;
                    }
                }
                else
                {
                    TraceMessage($"No signatures found in {uri} at {@params.position}");
                    return(new SignatureHelp());
                }
            }

            var sigs = overloads.Select(ToSignatureInformation).ToArray();

            if (activeParameter >= 0 && activeSignature < 0)
            {
                // TODO: Better selection of active signature
                activeSignature = sigs
                                  .Select((s, i) => Tuple.Create(s, i))
                                  .OrderBy(t => t.Item1.parameters.Length)
                                  .FirstOrDefault(t => t.Item1.parameters.Length > activeParameter)
                                  ?.Item2 ?? -1;
            }

            activeSignature = activeSignature >= 0
                ? activeSignature
                : (sigs.Length > 0 ? 0 : -1);

            return(new SignatureHelp {
                signatures = sigs,
                activeSignature = activeSignature,
                activeParameter = activeParameter
            });
        }
コード例 #22
0
        public async Task HandleRequestAsync_HtmlProjection_InvokesHtmlLanguageServer()
        {
            // Arrange
            var invokedLSPRequest   = false;
            var invokedRemapRequest = false;
            var expectedLocation    = GetLocation(5, 5, 5, 5, Uri);
            var documentManager     = new TestDocumentManager();

            documentManager.AddDocument(Uri, Mock.Of <LSPDocumentSnapshot>());

            var virtualHtmlUri = new Uri("C:/path/to/file.razor__virtual.html");
            var htmlLocation   = GetLocation(100, 100, 100, 100, virtualHtmlUri);
            var requestInvoker = new Mock <LSPRequestInvoker>(MockBehavior.Strict);

            requestInvoker
            .Setup(r => r.ReinvokeRequestOnServerAsync <TextDocumentPositionParams, Location[]>(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <TextDocumentPositionParams>(), It.IsAny <CancellationToken>()))
            .Callback <string, string, TextDocumentPositionParams, CancellationToken>((method, serverContentType, definitionParams, ct) =>
            {
                Assert.Equal(Methods.TextDocumentDefinitionName, method);
                Assert.Equal(RazorLSPConstants.HtmlLSPContentTypeName, serverContentType);
                invokedLSPRequest = true;
            })
            .Returns(Task.FromResult(new[] { htmlLocation }));

            var projectionResult = new ProjectionResult()
            {
                LanguageKind = RazorLanguageKind.Html,
            };
            var projectionProvider = new Mock <LSPProjectionProvider>(MockBehavior.Strict);

            projectionProvider.Setup(p => p.GetProjectionAsync(It.IsAny <LSPDocumentSnapshot>(), It.IsAny <Position>(), It.IsAny <CancellationToken>())).Returns(Task.FromResult(projectionResult));

            var documentMappingProvider = new Mock <LSPDocumentMappingProvider>();

            documentMappingProvider
            .Setup(d => d.RemapLocationsAsync(It.IsAny <Location[]>(), It.IsAny <CancellationToken>()))
            .Callback <Location[], CancellationToken>((locations, token) =>
            {
                Assert.Equal(htmlLocation, locations[0]);
                invokedRemapRequest = true;
            })
            .Returns(Task.FromResult(Array.Empty <Location>()));

            var definitionHandler = new GoToDefinitionHandler(requestInvoker.Object, documentManager, projectionProvider.Object, documentMappingProvider.Object);
            var definitionRequest = new TextDocumentPositionParams()
            {
                TextDocument = new TextDocumentIdentifier()
                {
                    Uri = Uri
                },
                Position = new Position(10, 5)
            };

            // Act
            var result = await definitionHandler.HandleRequestAsync(definitionRequest, new ClientCapabilities(), CancellationToken.None).ConfigureAwait(false);

            // Assert
            Assert.True(invokedLSPRequest);
            Assert.True(invokedRemapRequest);

            // Actual remapping behavior is tested elsewhere.
        }
コード例 #23
0
 /// <summary>
 /// Returns the source file and position where the item at the given position is declared at,
 /// if such a declaration exists, and returns the given position and file otherwise.
 /// Returns null if the given file is listed as to be ignored or if the information cannot be determined at this point.
 /// </summary>
 public Location DefinitionLocation(TextDocumentPositionParams param) =>
 ValidFileUri(param?.TextDocument?.Uri) && !IgnoreFile(param.TextDocument.Uri) ? this.Projects.DefinitionLocation(param) : null;
コード例 #24
0
 public Task <Hover?> GetTextDocumentDocumentHoverAsync(TextDocumentPositionParams textDocumentPositionParams, CancellationToken cancellationToken)
 => _protocol.ExecuteRequestAsync <TextDocumentPositionParams, Hover?>(Methods.TextDocumentHoverName,
                                                                       textDocumentPositionParams, _clientCapabilities, _clientName, cancellationToken);
コード例 #25
0
 /// <summary>
 /// Returns the signature help information for a call expression if there is such an expression at the specified position.
 /// Returns null if the given file is listed as to be ignored,
 /// or if some parameters are unspecified (null),
 /// or if the specified position is not a valid position within the currently processed file content
 /// or if no call expression exists at the specified position at this time
 /// or if no signature help information can be provided for the call expression at the specified position.
 /// </summary>
 public SignatureHelp SignatureHelp(TextDocumentPositionParams param, MarkupKind format = MarkupKind.PlainText) =>
 ValidFileUri(param?.TextDocument?.Uri) && !IgnoreFile(param.TextDocument.Uri) ? this.Projects.SignatureHelp(param, format) : null;
コード例 #26
0
 public Task <SignatureHelp> GetTextDocumentSignatureHelpAsync(TextDocumentPositionParams textDocumentPositionParams, CancellationToken cancellationToken)
 => _protocol.ExecuteRequestAsync <TextDocumentPositionParams, SignatureHelp>(Methods.TextDocumentSignatureHelpName,
                                                                              textDocumentPositionParams, _clientCapabilities, _clientName, cancellationToken);
コード例 #27
0
 /// <summary>
 /// Returns information about the item at the specified position as Hover information.
 /// Returns null if the given file is listed as to be ignored,
 /// or if some parameters are unspecified (null),
 /// or if the specified position is not a valid position within the currently processed file content,
 /// or if no token exists at the specified position at this time.
 /// </summary>
 public Hover HoverInformation(TextDocumentPositionParams param, MarkupKind format = MarkupKind.PlainText) =>
 ValidFileUri(param?.TextDocument?.Uri) && !IgnoreFile(param.TextDocument.Uri) ? this.Projects.HoverInformation(param, format) : null;
コード例 #28
0
        public override async Task <Hover> Hover(TextDocumentPositionParams @params, CancellationToken cancellationToken)
        {
            var uri = @params.textDocument.uri;

            ProjectFiles.GetEntry(@params.textDocument, @params._version, out var entry, out var tree);

            TraceMessage($"Hover in {uri} at {@params.position}");

            var analysis = entry != null ? await entry.GetAnalysisAsync(50, cancellationToken) : null;

            if (analysis == null)
            {
                TraceMessage($"No analysis found for {uri}");
                return(EmptyHover);
            }

            tree = GetParseTree(entry, uri, cancellationToken, out var version) ?? tree;

            Expression expr;
            SourceSpan?exprSpan;

            var finder = new ExpressionFinder(tree, GetExpressionOptions.Hover);

            expr     = finder.GetExpression(@params.position) as Expression;
            exprSpan = expr?.GetSpan(tree);

            if (expr == null)
            {
                TraceMessage($"No hover info found in {uri} at {@params.position}");
                return(EmptyHover);
            }

            TraceMessage($"Getting hover for {expr.ToCodeString(tree, CodeFormattingOptions.Traditional)}");

            var hover = await GetSelfHoverAsync(expr, analysis, tree, @params.position, cancellationToken);

            if (hover != null && hover != EmptyHover)
            {
                return(hover);
            }

            // First try values from expression. This works for the import statement most of the time.
            var values = analysis.GetValues(expr, @params.position, null).ToList();

            if (values.Count == 0)
            {
                values = GetImportHover(entry, analysis, tree, @params.position, out hover).ToList();
                if (hover != null)
                {
                    return(hover);
                }
            }

            if (values.Count > 0)
            {
                string originalExpr;
                if (expr is ConstantExpression || expr is ErrorExpression)
                {
                    originalExpr = null;
                }
                else
                {
                    originalExpr = @params._expr?.Trim();
                    if (string.IsNullOrEmpty(originalExpr))
                    {
                        originalExpr = expr.ToCodeString(tree, CodeFormattingOptions.Traditional);
                    }
                }

                var names = values.Select(GetFullTypeName).Where(n => !string.IsNullOrEmpty(n)).Distinct().ToArray();
                var res   = new Hover {
                    contents = GetMarkupContent(
                        _displayTextBuilder.GetDocumentation(values, originalExpr),
                        _clientCaps.textDocument?.hover?.contentFormat),
                    range      = exprSpan,
                    _version   = version?.Version,
                    _typeNames = names
                };
                return(res);
            }

            return(EmptyHover);
        }
コード例 #29
0
        public override async Task <Hover> Hover(TextDocumentPositionParams @params, CancellationToken cancellationToken)
        {
            var uri = @params.textDocument.uri;

            ProjectFiles.GetEntry(@params.textDocument, @params._version, out var entry, out var tree);

            TraceMessage($"Hover in {uri} at {@params.position}");

            var analysis = entry != null ? await entry.GetAnalysisAsync(50, cancellationToken) : null;

            if (analysis == null)
            {
                TraceMessage($"No analysis found for {uri}");
                return(EmptyHover);
            }

            tree = GetParseTree(entry, uri, cancellationToken, out var version) ?? tree;

            Expression expr;
            SourceSpan?exprSpan;

            var finder = new ExpressionFinder(tree, GetExpressionOptions.Hover);

            expr     = finder.GetExpression(@params.position) as Expression;
            exprSpan = expr?.GetSpan(tree);

            if (expr == null)
            {
                TraceMessage($"No hover info found in {uri} at {@params.position}");
                return(EmptyHover);
            }

            TraceMessage($"Getting hover for {expr.ToCodeString(tree, CodeFormattingOptions.Traditional)}");

            // First try values from expression. This works for the import statement most of the time.
            var values = analysis.GetValues(expr, @params.position, null).ToList();

            if (values.Count == 0)
            {
                // See if this is hover over import statement
                var index = tree.LocationToIndex(@params.position);
                var w     = new ImportedModuleNameWalker(entry, index, tree);
                tree.Walk(w);

                if (w.ImportedType != null)
                {
                    values = analysis.GetValues(w.ImportedType.Name, @params.position).ToList();
                }
                else
                {
                    var sb   = new StringBuilder();
                    var span = SourceSpan.Invalid;
                    foreach (var n in w.ImportedModules)
                    {
                        if (Analyzer.Modules.TryGetImportedModule(n.Name, out var modRef) && modRef.AnalysisModule != null)
                        {
                            if (sb.Length > 0)
                            {
                                sb.AppendLine();
                                sb.AppendLine();
                            }
                            sb.Append(_displayTextBuilder.GetModuleDocumentation(modRef));
                            span = span.IsValid ? span.Union(n.SourceSpan) : n.SourceSpan;
                        }
                    }
                    if (sb.Length > 0)
                    {
                        return(new Hover {
                            contents = sb.ToString(),
                            range = span
                        });
                    }
                }
            }

            if (values.Count > 0)
            {
                string originalExpr;
                if (expr is ConstantExpression || expr is ErrorExpression)
                {
                    originalExpr = null;
                }
                else
                {
                    originalExpr = @params._expr?.Trim();
                    if (string.IsNullOrEmpty(originalExpr))
                    {
                        originalExpr = expr.ToCodeString(tree, CodeFormattingOptions.Traditional);
                    }
                }

                var names = values.Select(GetFullTypeName).Where(n => !string.IsNullOrEmpty(n)).Distinct().ToArray();
                var res   = new Hover {
                    contents = GetMarkupContent(
                        _displayTextBuilder.GetDocumentation(values, originalExpr),
                        _clientCaps.textDocument?.hover?.contentFormat),
                    range      = exprSpan,
                    _version   = version?.Version,
                    _typeNames = names
                };
                return(res);
            }

            return(EmptyHover);
        }