コード例 #1
0
        private static bool IsNodeEligibleForRename(INode node)
        {
            switch (node.Kind)
            {
            case SyntaxKind.Identifier:
            case SyntaxKind.StringLiteral:
            case SyntaxKind.ThisKeyword:
                return(true);

            case SyntaxKind.NumericLiteral:
                // TODO: Our version of IsLiteralNameOfPropertyDeclarationOrIndexAccess does not suppoert computed property or string literal type
                // TODO: Whereas the typescript version does.
                return(DScriptUtilities.IsLiteralNameOfPropertyDeclarationOrIndexAccess(node));

            default:
                return(false);
            }
        }
コード例 #2
0
        /// <summary>
        /// Attempts to add a file to a source file list. The conditions that must be met are specified in the
        /// array of <paramref name="configurations"/>
        /// </summary>
        public static bool TryAddSourceFileToSourceFile(ITypeChecker checker, ISourceFile sourceFile, string sourceFileName, Workspace workspace, PathTable pathTable, AddSourceFileConfiguration[] configurations)
        {
            INode sourcesNode = null;

            try
            {
                // Use single or default to ensure that we only match a single sources property.
                // If we find more than one, we don't know which source file list to augment.
                // SingleOrDefault throws an InvalidOperationException if it finds more than one element
                // and returns default<T> if there are 0.
                sourcesNode = NodeWalker.TraverseBreadthFirstAndSelf(sourceFile).SingleOrDefault(node =>
                {
                    // We expect that the property for the source file list to be in an object literal
                    // and hence be a property assignment inside that object literal.
                    // The statement will look something like:
                    // const result = TargetType.build( { sources: [f`foo.cpp`] } );
                    if (node.Kind == SyntaxKind.PropertyAssignment &&
                        node.Cast <IPropertyAssignment>().Name.Kind == SyntaxKind.Identifier &&
                        node.Parent?.Kind == SyntaxKind.ObjectLiteralExpression)
                    {
                        var propertyName = node.Cast <IPropertyAssignment>().Name.Text;

                        // Now check the configurations to see if the any match as there
                        // can be different names (such as "references", "sources", etc.) as
                        // well as different functions, etc.

                        AddSourceFileConfiguration singleConfiguration = null;

                        try
                        {
                            // We use single or default to ensure that only one matching configuration is found.
                            // SingleOrDefault throws an InvalidOperationException if it finds more than one element
                            // and returns default<T> if there are 0.
                            singleConfiguration = configurations.SingleOrDefault(configuration =>
                            {
                                // Check to see if this is the correct property name.
                                if (propertyName != configuration.PropertyName)
                                {
                                    return(false);
                                }

                                // Now we will try to find the matching call expression (function name)
                                // The reason we are going to walk parent nodes is that we allow
                                // a "merge" or "override" to be nested inside the function call
                                // as long as the argument type and the expected module the type exists
                                // in match the configuration parameter.
                                var nodeParent = node.Parent.Parent;
                                while (nodeParent != null)
                                {
                                    if (nodeParent.Kind != SyntaxKind.CallExpression)
                                    {
                                        return(false);
                                    }

                                    var callExpression    = nodeParent.Cast <ICallExpression>();
                                    string calledFunction = string.Empty;

                                    // Depending on the module the function is being called from it may be a straight
                                    // call (such as "build()") or it could be an accessor if it was imported
                                    // from another module (such as "StaticLibrary.build()").
                                    if (callExpression.Expression?.Kind == SyntaxKind.PropertyAccessExpression)
                                    {
                                        var propertyAccessExpression = callExpression.Expression.Cast <IPropertyAccessExpression>();
                                        calledFunction = propertyAccessExpression.Name?.Text;
                                    }
                                    else if (callExpression.Expression?.Kind == SyntaxKind.Identifier)
                                    {
                                        calledFunction = callExpression.Expression.Cast <Identifier>().Text;
                                    }
                                    else
                                    {
                                        return(false);
                                    }

                                    // If the called function matches, and has the minimum number of parameters to contain our argument type
                                    // then verify it matches the type name given in the configuration.
                                    if (calledFunction == configuration.FunctionName && callExpression.Arguments?.Length > configuration.ArgumentPosition)
                                    {
                                        var type = checker.GetContextualType(callExpression.Arguments[configuration.ArgumentPosition]);
                                        if (type != null && IsTypeCorrectForAddSourceFileConfiguration(type, workspace, pathTable, configuration))
                                        {
                                            return(true);
                                        }
                                    }
                                    else if (DScriptUtilities.IsMergeOrOverrideCallExpression(callExpression))
                                    {
                                        // In the case of a merge or override function, we make sure it is the proper type and keep moving
                                        // up the parent chain to find the function call.
                                        var type = checker.GetTypeAtLocation(callExpression.TypeArguments[0]);
                                        if (type != null && IsTypeCorrectForAddSourceFileConfiguration(type, workspace, pathTable, configuration))
                                        {
                                            nodeParent = nodeParent.Parent;
                                            continue;
                                        }
                                    }

                                    return(false);
                                }

                                return(false);
                            });
                        }
                        catch (InvalidOperationException)
                        {
                            return(false);
                        }

                        return(singleConfiguration != null);
                    }

                    return(false);
                });
            }
            catch (InvalidOperationException)
            {
            }

            if (sourcesNode != null)
            {
                var propertyAssignment = sourcesNode.Cast <IPropertyAssignment>();
                // Will support array literals for now.
                var initializer = propertyAssignment.Initializer.As <IArrayLiteralExpression>();
                if (initializer == null)
                {
                    // TODO: potentially we could have a glob call here, and what we can do this:
                    // [...(oldExpression), newFile]
                    return(false);
                }

                var alreadyPresent = initializer.Elements.Any(element =>
                {
                    return(element.Kind == SyntaxKind.TaggedTemplateExpression &&
                           element.Cast <ITaggedTemplateExpression>().Template?.Text.Equals(sourceFileName, StringComparison.OrdinalIgnoreCase) == true);
                });

                if (!alreadyPresent)
                {
                    initializer.Elements.Add(new TaggedTemplateExpression("f", sourceFileName));
                    return(true);
                }
            }
            return(false);
        }
コード例 #3
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));
        }