Esempio n. 1
0
        /////////////////////////////////////////////////////////////////////////////////////////////////////
        // PUBLIC PROCEDURES
        /////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Returns the ideal amount of indent, always in columns, for the line containing the snapshot offset.
        /// </summary>
        /// <param name="snapshotOffset">The <see cref="TextSnapshotOffset"/> whose line should be examined.</param>
        /// <param name="defaultAmount">The default indent amount, which is the amount used in <c>Block</c> mode.</param>
        /// <returns>The ideal amount of indent, always in columns, for the line containing the snapshot offset.</returns>
        /// <remarks>
        /// This method is called when the <see cref="IndentMode"/> is <c>Smart</c>.
        /// The containing <see cref="ITextDocument"/> is accessible via the snapshot range's <see cref="ITextSnapshot"/>.
        /// </remarks>
        public override int GetIndentAmount(TextSnapshotOffset snapshotOffset, int defaultAmount)
        {
            // If the snapshot offset is deleted, return the default amount
            if (snapshotOffset.IsDeleted)
            {
                return(defaultAmount);
            }

            // Get the ICodeDocument from the snapshot
            ICodeDocument document = snapshotOffset.Snapshot.Document as ICodeDocument;

            if (document == null)
            {
                return(defaultAmount);
            }

            // Get the tab size
            int tabSize = document.TabSize;

            // Get a reader
            ITextSnapshotReader reader = snapshotOffset.Snapshot.GetReader(snapshotOffset.Offset);

            if (reader == null)
            {
                return(defaultAmount);
            }

            // Get the indentation base line index
            int indentationBaseLineIndex = Math.Max(0, snapshotOffset.Line.Index - 1);

            // Ensure we are at the start of the current token
            if (!reader.IsAtTokenStart)
            {
                reader.GoToCurrentTokenStart();
            }

            // If finding indentation for an open curly brace, move back a token
            bool isForOpenCurlyBrace = (reader.Token.Id == SimpleTokenId.OpenCurlyBrace);

            if (isForOpenCurlyBrace)
            {
                reader.GoToPreviousToken();
            }

            // Loop backwards
            bool keywordFoundAfterStatement = false;
            bool statementFound             = false;

            while (true)
            {
                switch (reader.Token.Id)
                {
                case SimpleTokenId.OpenCurlyBrace: {
                    // Indent from this open curly brace
                    return(reader.SnapshotLine.IndentAmount + tabSize);
                }

                case SimpleTokenId.CloseCurlyBrace:
                    // Return the indent level of the matching {
                    reader.GoToPreviousMatchingTokenById(SimpleTokenId.CloseCurlyBrace, SimpleTokenId.OpenCurlyBrace);
                    return(reader.SnapshotLine.IndentAmount);

                case SimpleTokenId.CloseParenthesis:
                case SimpleTokenId.SemiColon:
                    if (!statementFound)
                    {
                        // Flag that a statement was found
                        statementFound = true;

                        if (!keywordFoundAfterStatement)
                        {
                            // Use this line as indentation base
                            indentationBaseLineIndex = reader.SnapshotLine.Index;
                        }
                    }
                    break;

                default:
                    if ((!keywordFoundAfterStatement) && (!statementFound) && (reader.Offset < snapshotOffset.Offset) &&
                        (reader.Token.Id >= SimpleTokenId.Function) && (reader.Token.Id <= SimpleTokenId.Var))
                    {
                        // Flag that a keyword was found
                        keywordFoundAfterStatement = true;

                        // Use this line as indentation base
                        indentationBaseLineIndex = reader.SnapshotLine.Index;
                    }
                    break;
                }

                // Go to the previous token
                if (!reader.GoToPreviousToken())
                {
                    break;
                }
            }

            // Indent a level if on the statement after the keyword
            return(reader.Snapshot.Lines[indentationBaseLineIndex].IndentAmount + (keywordFoundAfterStatement && isForOpenCurlyBrace ? tabSize : 0));
        }
        /// <summary>
        /// Examines the snapshot text to determine more detail about the context.
        /// </summary>
        /// <param name="context">The <see cref="SimpleContext"/> to update.</param>
        /// <param name="includeArgumentInfo">Whether to populate the argument-related context properties, for use with parameter info.</param>
        private static void UpdateFromSnapshotText(SimpleContext context, bool includeArgumentInfo)
        {
            // Get the snapshot offset
            TextSnapshotOffset snapshotOffset = context.SnapshotOffset;

            // Create a default initialization range
            context.InitializationSnapshotRange = new TextSnapshotRange(snapshotOffset);

            // Get a snapshot reader
            ITextSnapshotReader reader = snapshotOffset.Snapshot.GetReader(snapshotOffset.Offset);

            if (reader == null)
            {
                return;
            }

            IToken token = reader.ReadToken();

            if (token != null)
            {
                // If the current token is a comment, flag no context
                switch (token.Id)
                {
                case SimpleTokenId.MultiLineCommentEndDelimiter:
                case SimpleTokenId.MultiLineCommentLineTerminator:
                case SimpleTokenId.MultiLineCommentStartDelimiter:
                case SimpleTokenId.MultiLineCommentText:
                case SimpleTokenId.SingleLineCommentEndDelimiter:
                case SimpleTokenId.SingleLineCommentStartDelimiter:
                case SimpleTokenId.SingleLineCommentText:
                    context.Type = SimpleContextType.None;
                    return;
                }
            }

            // If we are in a function declaration block...
            if (context.Type == SimpleContextType.FunctionDeclarationBlock)
            {
                // Get the AST
                ICodeDocument document = snapshotOffset.Snapshot.Document as ICodeDocument;
                if (document == null)
                {
                    return;
                }
                ILLParseData parseData = document.ParseData as ILLParseData;
                if (parseData == null)
                {
                    return;
                }
                CompilationUnit compilationUnit = parseData.Ast as CompilationUnit;
                if ((compilationUnit == null) || (!compilationUnit.HasMembers))
                {
                    return;
                }

                // If argument data should be scanned...
                if (includeArgumentInfo)
                {
                    // Scan backward to look for argument data
                    reader.Offset = snapshotOffset.Offset;
                    reader.GoToCurrentTokenStart();
                    var startOffset = reader.Offset;
                    token = reader.ReadTokenReverse();
                    while (token != null)
                    {
                        // Quit if we look behind too far (for performance reasons) - 500 is arbitrary number and could be tweaked
                        if ((!context.ArgumentIndex.HasValue) && (startOffset - reader.Offset > 500))
                        {
                            return;
                        }

                        switch (token.Id)
                        {
                        case SimpleTokenId.CloseParenthesis:
                            // Skip over ( ... ) pair
                            if (!reader.GoToPreviousMatchingTokenById(SimpleTokenId.CloseParenthesis, SimpleTokenId.OpenParenthesis))
                            {
                                return;
                            }
                            break;

                        case SimpleTokenId.Comma:
                            // Update the context data
                            if (context.ArgumentIndex.HasValue)
                            {
                                context.ArgumentIndex++;
                            }
                            else
                            {
                                context.ArgumentIndex          = 1;
                                context.ArgumentSnapshotOffset = new TextSnapshotOffset(reader.Snapshot, token.EndOffset);
                            }
                            break;

                        case SimpleTokenId.OpenParenthesis:
                            // Update the context data
                            context.ArgumentListSnapshotOffset = new TextSnapshotOffset(reader.Snapshot, token.EndOffset);
                            if (!context.ArgumentIndex.HasValue)
                            {
                                context.ArgumentIndex          = 0;
                                context.ArgumentSnapshotOffset = context.ArgumentListSnapshotOffset;
                            }

                            // Go to the previous token
                            reader.GoToPreviousToken();
                            token = reader.Token;
                            if ((token != null) && (token.Id == SimpleTokenId.Identifier))
                            {
                                // Loop through the AST nodes to ensure that the identifier text matches a declared function name
                                string functionName = reader.TokenText;
                                foreach (FunctionDeclaration functionAstNode in compilationUnit.Members)
                                {
                                    // If the name matches the function's name...
                                    if (functionAstNode.Name == functionName)
                                    {
                                        // Update the target function
                                        context.TargetFunction = functionAstNode;
                                        break;
                                    }
                                }
                            }
                            return;

                        default:
                            // Quit if any tokens are found that aren't allowed in invocations
                            if (!IsTokenAllowedInInvocation(token.Id))
                            {
                                return;
                            }
                            else
                            {
                                break;
                            }
                        }

                        // Move back a token
                        token = reader.ReadTokenReverse();
                    }
                }
                else
                {
                    // If the current token is an identifier...
                    if ((token != null) && (token.Id == SimpleTokenId.Identifier))
                    {
                        // Store the identifier snapshot range in case this is a function reference token
                        TextSnapshotRange identifierSnapshotRange = new TextSnapshotRange(reader.Snapshot, token.TextRange);

                        // Next, check to ensure the next non-whitespace token is a '('
                        token = reader.ReadToken();
                        while (!reader.IsAtSnapshotEnd)
                        {
                            if (token != null)
                            {
                                switch (token.Id)
                                {
                                case SimpleTokenId.Whitespace:
                                    // Continue
                                    break;

                                case SimpleTokenId.OpenParenthesis: {
                                    // Loop through the AST nodes to ensure that the identifier text matches a declared function name
                                    foreach (FunctionDeclaration functionAstNode in compilationUnit.Members)
                                    {
                                        // If the name matches the function's name...
                                        if (functionAstNode.Name == identifierSnapshotRange.Text)
                                        {
                                            // Update the context type
                                            context.Type = SimpleContextType.FunctionReference;
                                            context.InitializationSnapshotRange = identifierSnapshotRange;
                                            context.TargetFunction = functionAstNode;
                                            break;
                                        }
                                    }
                                    return;
                                }

                                default:
                                    // Quit
                                    return;
                                }
                            }

                            // Advance
                            token = reader.ReadToken();
                        }
                    }
                }
            }
        }