示例#1
0
        private static bool GetFunction(AstRoot astRoot, ref int position, out FunctionCall functionCall, out Variable functionVariable)
        {
            functionVariable = null;
            functionCall = astRoot.GetNodeOfTypeFromPosition<FunctionCall>(position);

            if (functionCall == null && position > 0)
            {
                // Retry in case caret is at the very end of function signature
                // that does not have final close brace yet.
                functionCall = astRoot.GetNodeOfTypeFromPosition<FunctionCall>(position - 1, includeEnd: true);
                if(functionCall != null)
                {
                    // But if signature does have closing brace and caret
                    // is beyond it, we are really otuside of the signature.
                    if(functionCall.CloseBrace != null && position >= functionCall.CloseBrace.End) {
                        return false;
                    }

                    if (position > functionCall.SignatureEnd) {
                        position = functionCall.SignatureEnd;
                    }
                }
            }

            if (functionCall != null && functionCall.Children.Count > 0)
            {
                functionVariable = functionCall.Children[0] as Variable;
                return functionVariable != null;
            }

            return false;
        }
示例#2
0
        /// <summary>
        /// Locates function or variable definition given the item name and
        /// the position of the name in the text buffer.
        /// </summary>
        /// <returns>AST node that defines the specified item</returns>
        public static IAstNode FindItemDefinition(this AstRoot ast, int position, string itemName)
        {
            var scope = ast.GetNodeOfTypeFromPosition <IScope>(position);
            var func  = scope.FindFunctionDefinitionByName(itemName, position);

            return(func ?? scope.FindVariableDefinitionByName(itemName, position));
        }
示例#3
0
        /// <summary>
        /// Enumerates function definitions applicable to the current scope.
        /// Returns definitions that appear in the file up to the specified
        /// position.
        /// </summary>
        public static IEnumerable <RFunction> GetFunctionsFromPosition(this AstRoot ast, int position)
        {
            var scope     = ast.GetNodeOfTypeFromPosition <IScope>(position);
            var variables = scope.GetApplicableVariables(position);

            return(variables.Where(x => x.Value is RFunction).Select(x => x.Value as RFunction));
        }
示例#4
0
        public static string IsInLibraryStatement(this AstRoot ast, int position)
        {
            var fc = ast.GetNodeOfTypeFromPosition <FunctionCall>(position);

            if (fc?.RightOperand != null)
            {
                var funcName = ast.TextProvider.GetText(fc.RightOperand);
                if (funcName == "library" && fc.Arguments.Count == 1)
                {
                    return(ast.TextProvider.GetText(fc.Arguments[0]));
                }
            }
            return(string.Empty);
        }
示例#5
0
文件: AstSearch.cs 项目: xoriath/RTVS
        public static string IsInLibraryStatement(this AstRoot ast, int position)
        {
            var fc = ast.GetNodeOfTypeFromPosition <FunctionCall>(position);

            if (fc != null && fc.RightOperand != null)
            {
                string funcName = ast.TextProvider.GetText(fc.RightOperand);
                if (funcName.Equals("library", StringComparison.Ordinal))
                {
                    if (fc.Arguments.Count == 1)
                    {
                        return(ast.TextProvider.GetText(fc.Arguments[0]));
                    }
                }
            }
            return(string.Empty);
        }
示例#6
0
        /// <summary>
        /// Locates function or variable definition given the item name and
        /// the position of the name in the text buffer.
        /// </summary>
        /// <returns>AST node that defines the specified item</returns>
        public static IAstNode FindItemDefinition(this AstRoot ast, int position, string itemName)
        {
            var scope = ast.GetNodeOfTypeFromPosition <IScope>(position);
            var func  = scope.FindFunctionDefinitionByName(itemName, position);

            if (func != null)
            {
                return(func);
            }
            else
            {
                var v = scope.FindVariableDefinitionByName(itemName, position);
                if (v != null)
                {
                    return(v);
                }
            }
            return(null);
        }
示例#7
0
        public static bool FormatRangeExact(ITextView textView, ITextBuffer textBuffer, ITextRange formatRange,
                                            AstRoot ast, RFormatOptions options, 
                                            int scopeStatementPosition, bool respectUserIndent = true) {
            ITextSnapshot snapshot = textBuffer.CurrentSnapshot;
            Span spanToFormat = new Span(formatRange.Start, formatRange.Length);
            string spanText = snapshot.GetText(spanToFormat.Start, spanToFormat.Length);
            string trimmedSpanText = spanText.Trim();

            if (trimmedSpanText == "}") {
                // Locate opening { and its statement
                var scopeNode = ast.GetNodeOfTypeFromPosition<IAstNodeWithScope>(spanToFormat.Start);
                if (scopeNode != null) {
                    scopeStatementPosition = scopeNode.Start;
                }
            }

            RFormatter formatter = new RFormatter(options);
            string formattedText = formatter.Format(trimmedSpanText);

            formattedText = formattedText.Trim(); // there may be inserted line breaks after {
            formattedText = IndentLines(textBuffer, spanToFormat.Start, ast, formattedText, options, scopeStatementPosition, respectUserIndent);

            if (!spanText.Equals(formattedText, StringComparison.Ordinal)) {
                var selectionTracker = new RSelectionTracker(textView, textBuffer);
                RTokenizer tokenizer = new RTokenizer();
                IReadOnlyTextRangeCollection<RToken> oldTokens = tokenizer.Tokenize(spanText);
                IReadOnlyTextRangeCollection<RToken> newTokens = tokenizer.Tokenize(formattedText);
                IncrementalTextChangeApplication.ApplyChangeByTokens(
                    textBuffer,
                    new TextStream(spanText), new TextStream(formattedText),
                    oldTokens, newTokens,
                    formatRange,
                    Resources.AutoFormat, selectionTracker);
                return true;
            }

            return false;
        }
 public static IFunctionDefinition FindFunctionDefinition(ITextBuffer textBuffer, AstRoot ast, int position, out Variable v) {
     v = null;
     var exp = ast.GetNodeOfTypeFromPosition<IExpressionStatement>(position);
     return exp?.GetVariableOrFunctionDefinition(out v);
 }
示例#9
0
 public static IAstNode GetIndentDefiningNode(AstRoot ast, int position) {
     IScope scope = ast.GetNodeOfTypeFromPosition<IScope>(position);
     // Scope indentation is defined by its parent statement.
     IAstNodeWithScope parentStatement = scope.Parent as IAstNodeWithScope;
     if (parentStatement != null && parentStatement.Scope == scope) {
         return parentStatement;
     }
     return scope;
 }
示例#10
0
 private static IKeywordScopeStatement GetFormatScope(ITextView textView, ITextBuffer textBuffer, AstRoot ast) {
     SnapshotPoint? caret = REditorDocument.MapCaretPositionFromView(textView);
     if (caret.HasValue) {
         try {
             int lineNumber = textBuffer.CurrentSnapshot.GetLineNumberFromPosition(caret.Value.Position);
             ITextSnapshotLine line = textBuffer.CurrentSnapshot.GetLineFromLineNumber(lineNumber);
             string lineText = line.GetText();
             if (lineText.TrimEnd().EndsWith("}", StringComparison.Ordinal)) {
                 IKeywordScopeStatement scopeStatement = ast.GetNodeOfTypeFromPosition<IKeywordScopeStatement>(caret.Value);
                 return scopeStatement;
             }
         } catch (Exception) { }
     }
     return null;
 }
示例#11
0
        public static int GetSmartIndent(ITextSnapshotLine line, AstRoot ast = null) {
            ITextBuffer textBuffer = line.Snapshot.TextBuffer;

            if (ast == null) {
                IREditorDocument document = REditorDocument.TryFromTextBuffer(textBuffer);
                if (document == null) {
                    return 0;
                }
                ast = document.EditorTree.AstRoot;
            }

            if (line.LineNumber > 0) {
                ITextSnapshotLine prevLine = line.Snapshot.GetLineFromLineNumber(line.LineNumber - 1);

                string prevLineText = prevLine.GetText();
                int nonWsPosition = prevLine.Start + (prevLineText.Length - prevLineText.TrimStart().Length) + 1;

                IAstNodeWithScope scopeStatement = ast.GetNodeOfTypeFromPosition<IAstNodeWithScope>(nonWsPosition);
                if (scopeStatement != null) {
                    if (scopeStatement.Scope == null) {
                        // No scope of any kind, use block indent
                        return GetBlockIndent(line) + REditorSettings.IndentSize;
                    }

                    if (scopeStatement.Scope is SimpleScope) {
                        // There is statement with a simple scope above. We need to check 
                        // if the line that is being formatted is actually part of this scope.
                        if (line.Start < scopeStatement.Scope.End) {
                            // Indent line one level deeper that the statement
                            return GetBlockIndent(line) + REditorSettings.IndentSize;
                        }
                        
                        // Line is not part of the scope, hence regular indent
                        return OuterIndentSizeFromNode(textBuffer, scopeStatement, REditorSettings.FormatOptions);
                    }

                    // Check if line is the last line in scope and if so, 
                    // it should be indented at the outer indent
                    if (scopeStatement.Scope.CloseCurlyBrace != null) {
                        int endOfScopeLine = textBuffer.CurrentSnapshot.GetLineNumberFromPosition(scopeStatement.Scope.CloseCurlyBrace.Start);
                        if (endOfScopeLine == line.LineNumber) {
                            return OuterIndentSizeFromNode(textBuffer, scopeStatement, REditorSettings.FormatOptions);
                        }
                    }

                    return InnerIndentSizeFromNode(textBuffer, scopeStatement, REditorSettings.FormatOptions);
                }
            }

            IAstNodeWithScope node = ast.GetNodeOfTypeFromPosition<IAstNodeWithScope>(line.Start);
            if (node != null && node.Scope != null && node.Scope.OpenCurlyBrace != null) {
                return InnerIndentSizeFromNode(textBuffer, node, REditorSettings.FormatOptions);
            }

            // See if we are in function arguments and indent at the function level
            var fc = ast.GetNodeOfTypeFromPosition<FunctionCall>(line.Start);
            if(fc != null && fc.Arguments != null && fc.OpenBrace != null && line.Start >= fc.OpenBrace.End) {
                return InnerIndentSizeFromNode(textBuffer, fc, REditorSettings.FormatOptions);
            }

            // If nothing is found, default to block indent
            return GetBlockIndent(line);
        }
示例#12
0
        /// <summary>
        /// Determines level of indentation in the line from AST and surrounding context.
        /// Called when user hits ENTER and editor needs to know level of indentation in
        /// the new line as well as when code is being auto-formatted and range formatter
        /// needs to know how to indent freshly formatted lines of code.
        /// </summary>
        /// <param name="line">Line to find the indent for</param>
        /// <param name="ast">Optional AST</param>
        /// <param name="formatting">
        /// Indicates if current call is from formatter or 
        /// from the core editor for indentation when user typed Enter.
        /// </param>
        /// <returns>Level of indent in spaces</returns>
        public static int GetSmartIndent(ITextSnapshotLine line, AstRoot ast = null, 
                                         int originalIndentSizeInSpaces = -1, bool formatting = false) {
            ITextBuffer textBuffer = line.Snapshot.TextBuffer;
            ITextSnapshotLine prevLine = null;

            if (line.LineNumber == 0) {
                // Nothing to indent at the first line
                return 0;
            }

            if (ast == null) {
                IREditorDocument document = REditorDocument.TryFromTextBuffer(textBuffer);
                if (document == null) {
                    return 0;
                }
                ast = document.EditorTree.AstRoot;
            }

            // The challenge here is to find scope to base the indent on.
            // The scope may or may not have braces and may or may not be closed. 
            // Current line is normally empty so we use previous line data assuming 
            // it is not empty. If previous line is empty, we do not look up 
            // to the nearest non-empty. This is the same as C# behavior.
            // So we need to locate nearest node that implements IAstNodeWithScope
            // or the scope (implemeting IScope) itself is scope is just '{ }'.

            // First try based on the previous line. We will try start of the line
            // like in 'if(...)' { in order to locate 'if' and then, if nothing is found,
            // try end of the line as in 'x <- function(...) {'
            prevLine = line.Snapshot.GetLineFromLineNumber(line.LineNumber - 1);
            string prevLineText = prevLine.GetText();
            if (prevLineText.Trim().Equals("else", StringComparison.Ordinal)) {
                // Quick short circuit for new 'else' since it is not in the ASt yet.
                return GetBlockIndent(line) + REditorSettings.IndentSize;
            }

            // First, let's see if we are in a function argument list and then indent based on 
            // the opening brace position. This needs to be done before looking for scopes
            // since function definition is a scope-defining statement.
            // Examples: 'call(a,\n<Enter>' or 'x <- function(a,<Enter>'
            if (prevLine.Length > 0) {
                var fc1 = ast.GetNodeOfTypeFromPosition<IFunction>(prevLine.End - 1);
                var fc2 = ast.GetNodeOfTypeFromPosition<IFunction>(line.Start);
                // Pick narrowest function. This happens when function definition appears
                // inside the argument list such as list(a = function(...)).
                var fc = fc2 ?? fc1;
                if (fc != null && fc.Arguments != null && fc.OpenBrace != null) {
                    if (fc.CloseBrace == null || fc.CloseBrace.End > prevLine.End) {
                        // We only want to indent here if position is in arguments and not in the function scope.
                        if (line.Start >= fc.OpenBrace.End && !(fc.CloseBrace != null && line.Start >= fc.CloseBrace.End)) {
                            if (originalIndentSizeInSpaces < 0) {
                                // Indent one level deeper from the function definition line.
                                var fcLine = line.Snapshot.GetLineFromPosition(fc.Start);
                                if (fcLine.LineNumber == prevLine.LineNumber) {
                                    int fcIndentSize = IndentBuilder.TextIndentInSpaces(fcLine.GetText(), REditorSettings.TabSize);
                                    if (fc.CloseBrace == null || fc.CloseBrace.End >= (formatting ? line.Start : line.End)) {
                                        fcIndentSize += REditorSettings.IndentSize;
                                    }
                                    return fcIndentSize;
                                } else {
                                    return GetBlockIndent(line);
                                }
                            } else {
                                return originalIndentSizeInSpaces;
                            }
                        }
                    }
                }
            }

            // Candidate position #1 is first non-whitespace character
            // in the the previous line
            int startOfNoWsOnPreviousLine = prevLine.Start + (prevLineText.Length - prevLineText.TrimStart().Length) + 1;

            // Try current new line so in case of 'if () { } else { | }' we find
            // the 'else' which defines the scope and not the parent 'if'.
            var scopeStatement1 = ast.GetNodeOfTypeFromPosition<IAstNodeWithScope>(line.Start);
            if (scopeStatement1 == null) {
                // If not found, try previous line that may define the indent
                scopeStatement1 = ast.GetNodeOfTypeFromPosition<IAstNodeWithScope>(startOfNoWsOnPreviousLine);
                if (scopeStatement1 == null) {
                    // Line start position works for typical scope-defining statements like if() or while()
                    // but it won't find function definition in 'x <- function(a) {'
                    // Try end of the line instead
                    var lastNonWsOnPreviousLine = Math.Max(0, prevLineText.TrimEnd().Length - 1);
                    scopeStatement1 = ast.GetNodeOfTypeFromPosition<IAstNodeWithScope>(lastNonWsOnPreviousLine);
                    // Verify that line we are asked to provide the smart indent for is actually inside 
                    // this scope since we could technically find end of x <- function(a) {}
                    // when we went up one line.
                    if (scopeStatement1?.Scope?.CloseCurlyBrace != null && !scopeStatement1.Contains(line.Start)) {
                        scopeStatement1 = null; // line is outside of this scope.
                    }
                }
            }

            IAstNodeWithScope scopeStatement;
            var scopeStatement2 = ast.GetNodeOfTypeFromPosition<IAstNodeWithScope>(startOfNoWsOnPreviousLine);

            // Locate standalone scope which is not a statement, if any
            var scope = ast.GetNodeOfTypeFromPosition<IScope>(prevLine.End);

            // Pick the narrowest item
            // so in case of 
            //  x <- function() {
            //      if(...)<Enter>
            //  }
            // we will use the 'if' and not the function definition
            var scopeCandidates = new List<IAstNode>() { scopeStatement1, scopeStatement2, scope };
            var smallestScope = scopeCandidates.OrderBy(x => x != null ? x.Length : Int32.MaxValue).FirstOrDefault();

            scopeStatement = smallestScope as IAstNodeWithScope;
            scope = smallestScope as IScope;

            // If IScope is a scope defined by the parent statement, use
            // the parent statement so in 
            // x <- function(...) {
            //      |
            // }
            // the indent in scope is defined by the function and not by the opening {
            if (scope != null) {
                var parentStarement = scope.Parent as IAstNodeWithScope;
                if (parentStarement != null && parentStarement.Scope == scope) {
                    scopeStatement = parentStarement;
                    scope = null;
                }
            }

            if (scopeStatement != null) {
                if (scopeStatement.Scope == null) {
                    // There is nothing after statement that allows simple scope
                    // such as in 'if(...)EOF'
                    return GetBlockIndent(line) + REditorSettings.IndentSize;
                }

                if (scopeStatement.Scope is SimpleScope) {
                    // There is statement with a simple scope above such as 'if' without { }. 
                    // We need to check if the line that is being formatted is part of this scope.
                    if (line.Start < scopeStatement.Scope.End) {
                        // Indent line one level deeper that the statement
                        return GetBlockIndent(line) + REditorSettings.IndentSize;
                    }
                    // Line is not part of the scope, provide regular indent
                    return OuterIndentSizeFromNode(textBuffer, scopeStatement, REditorSettings.FormatOptions);
                }

                // Check if line is the last line in a real scope (i.e. scope with { }) and only consists
                // of the closing }, it should be indented at the outer indent so closing scope aligns with
                // the beginning of the statement.
                if (scopeStatement.Scope.CloseCurlyBrace != null) {
                    int endOfScopeLine = textBuffer.CurrentSnapshot.GetLineNumberFromPosition(scopeStatement.Scope.CloseCurlyBrace.Start);
                    if (endOfScopeLine <= line.LineNumber) {
                        return OuterIndentSizeFromNode(textBuffer, scopeStatement, REditorSettings.FormatOptions);
                    }
                }

                if (scopeStatement.Scope.OpenCurlyBrace != null && REditorSettings.FormatOptions.BracesOnNewLine) {
                    int startOfScopeLine = textBuffer.CurrentSnapshot.GetLineNumberFromPosition(scopeStatement.Scope.OpenCurlyBrace.Start);
                    if (startOfScopeLine == line.LineNumber) {
                        return OuterIndentSizeFromNode(textBuffer, scopeStatement, REditorSettings.FormatOptions);
                    }
                }

                // We are inside a scope so provide inner indent
                return InnerIndentSizeFromNode(textBuffer, scopeStatement, REditorSettings.FormatOptions);
            }

            // Try locate the scope itself, if any
            if (scope != null && scope.OpenCurlyBrace != null) {
                if (scope.CloseCurlyBrace != null) {
                    int endOfScopeLine = textBuffer.CurrentSnapshot.GetLineNumberFromPosition(scope.CloseCurlyBrace.Start);
                    if (endOfScopeLine == line.LineNumber) {
                        return OuterIndentSizeFromNode(textBuffer, scope, REditorSettings.FormatOptions);
                    }
                }
                return InnerIndentSizeFromNode(textBuffer, scope, REditorSettings.FormatOptions);
            }

            return 0;
        }
示例#13
0
        /// <summary>
        /// Determines if a given position is in area where user
        /// specified indentation must be respected. For example,
        /// in multi-line list of function arguments, 
        /// multi-line expressions and so on.
        /// </summary>
        private static bool RespectUserIndent(ITextBuffer textBuffer, AstRoot ast, int position) {
            // Look up nearest expression
            IAstNode node = ast.GetNodeOfTypeFromPosition<Expression>(position);
            if (IsMultilineNode(textBuffer, node)) {
                return true;
            }

            node = ast.GetNodeOfTypeFromPosition<FunctionDefinition>(position);
            if (IsMultilineNode(textBuffer, node)) {
                return true;
            }

            node = ast.GetNodeOfTypeFromPosition<FunctionCall>(position);
            if (IsMultilineNode(textBuffer, node)) {
                return true;
            }

            return false;
        }