Exemplo n.º 1
0
        private static int DeterminePosition(IEditorBuffer editorBuffer, int caretPosition)
        {
            var         snapshot   = editorBuffer.CurrentSnapshot;
            IEditorLine line       = null;
            var         lineNumber = snapshot.GetLineNumberFromPosition(caretPosition);

            for (var i = lineNumber; i < snapshot.LineCount; i++)
            {
                line = snapshot.GetLineFromLineNumber(i);
                if (line.Length > 0)
                {
                    break;
                }
            }

            if (line == null || line.Length == 0)
            {
                return(-1);
            }

            var offset       = line.Length - line.GetText().TrimStart().Length + 1;
            var linePosition = line.Start + offset;

            return(linePosition < snapshot.Length ? linePosition : -1);
        }
Exemplo n.º 2
0
        public static int OuterIndentSizeFromLine(IEditorLine line, RFormatOptions options)
        {
            var lineText          = line.GetText();
            var leadingWhitespace = lineText.Substring(0, lineText.Length - lineText.TrimStart().Length);

            return(IndentBuilder.TextIndentInSpaces(leadingWhitespace, options.TabSize));
        }
Exemplo n.º 3
0
        /// <summary>
        /// Attempts to insert Roxygen documentation block based
        /// on the user function signature.
        /// </summary>
        public static bool TryInsertBlock(IEditorBuffer editorBuffer, AstRoot ast, int position)
        {
            // First determine if position is right before the function declaration
            var         snapshot   = editorBuffer.CurrentSnapshot;
            IEditorLine line       = null;
            var         lineNumber = snapshot.GetLineNumberFromPosition(position);

            for (int i = lineNumber; i < snapshot.LineCount; i++)
            {
                line = snapshot.GetLineFromLineNumber(i);
                if (line.Length > 0)
                {
                    break;
                }
            }

            if (line == null || line.Length == 0)
            {
                return(false);
            }

            Variable v;
            int      offset = line.Length - line.GetText().TrimStart().Length + 1;

            if (line.Start + offset >= snapshot.Length)
            {
                return(false);
            }

            var fd = ast.FindFunctionDefinition(line.Start + offset, out v);

            if (fd != null && v != null && !string.IsNullOrEmpty(v.Name))
            {
                int definitionStart = Math.Min(v.Start, fd.Start);
                var insertionSpan   = GetRoxygenBlockPosition(snapshot, definitionStart);
                if (insertionSpan != null)
                {
                    string lineBreak = snapshot.GetLineFromPosition(position).LineBreak;
                    if (string.IsNullOrEmpty(lineBreak))
                    {
                        lineBreak = "\n";
                    }
                    string block = GenerateRoxygenBlock(v.Name, fd, lineBreak);
                    if (block.Length > 0)
                    {
                        if (insertionSpan.Length == 0)
                        {
                            editorBuffer.Insert(insertionSpan.Start, block + lineBreak);
                        }
                        else
                        {
                            editorBuffer.Replace(insertionSpan, block);
                        }
                        return(true);
                    }
                }
            }
            return(false);
        }
Exemplo n.º 4
0
        public static int InnerIndentSizeFromLine(IEditorLine line, RFormatOptions options)
        {
            var lineText          = line.GetText();
            var leadingWhitespace = lineText.Substring(0, lineText.Length - lineText.TrimStart().Length);
            var indentbuilder     = new IndentBuilder(options.IndentType, options.IndentSize, options.TabSize);

            return(IndentBuilder.TextIndentInSpaces(leadingWhitespace + indentbuilder.SingleIndentString, options.TabSize));
        }
Exemplo n.º 5
0
        private static bool LineHasContinuation(IEditorLine line)
        {
            var tokenizer     = new RTokenizer();
            var tokens        = tokenizer.Tokenize(line.GetText());
            var lastTokenType = tokens.Count > 0 ? tokens[tokens.Count - 1].TokenType : RTokenType.Unknown;

            return(lastTokenType == RTokenType.Operator || lastTokenType == RTokenType.Comma);
        }
Exemplo n.º 6
0
        internal static bool CommentLine(IEditorLine line)
        {
            string lineText = line.GetText();

            if (!string.IsNullOrWhiteSpace(lineText))
            {
                int leadingWsLength = lineText.Length - lineText.TrimStart().Length;
                line.Snapshot.EditorBuffer.Insert(line.Start + leadingWsLength, "#");
                return(true);
            }

            return(false);
        }
Exemplo n.º 7
0
        internal static bool UncommentLine(IEditorLine line)
        {
            string lineText = line.GetText();

            if (!string.IsNullOrWhiteSpace(lineText))
            {
                int leadingWsLength = lineText.Length - lineText.TrimStart().Length;
                if (leadingWsLength < lineText.Length)
                {
                    if (lineText[leadingWsLength] == '#')
                    {
                        line.Snapshot.EditorBuffer.Delete(new TextRange(line.Start + leadingWsLength, 1));
                        return(true);
                    }
                }
            }
            return(false);
        }
Exemplo n.º 8
0
        public static string GetItemAtPosition(IEditorLine line, int position, Func <RTokenType, bool> tokenTypeCheck, out ITextRange span)
        {
            string lineText         = line.GetText();
            var    offset           = 0;
            var    positionInTokens = position - line.Start;
            var    tokenizer        = new RTokenizer();
            var    tokens           = tokenizer.Tokenize(lineText);
            var    tokenIndex       = tokens.GetItemContaining(positionInTokens);

            if (tokenIndex >= 0)
            {
                var token = tokens[tokenIndex];
                if (token.TokenType == RTokenType.Comment)
                {
                    // Tokenize inside comment since we do want F1 to work inside
                    // commented out code, code samples or Roxygen blocks.
                    positionInTokens -= token.Start;
                    var positionAfterHash = token.Start + 1;
                    tokens     = tokenizer.Tokenize(lineText.Substring(positionAfterHash, token.Length - 1));
                    tokenIndex = tokens.GetItemContaining(positionInTokens);
                    if (tokenIndex >= 0)
                    {
                        token  = tokens[tokenIndex];
                        offset = positionAfterHash;
                    }
                }
                if (tokenTypeCheck(token.TokenType))
                {
                    var start = token.Start + offset;
                    var end   = Math.Min(start + token.Length, line.End);
                    span = TextRange.FromBounds(line.Start + start, line.Start + end); // return view span
                    return(lineText.Substring(start, end - start));
                }
            }

            span = TextRange.EmptyRange;
            return(string.Empty);
        }
Exemplo n.º 9
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="settings">Editor settings</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(IEditorLine line, IREditorSettings settings, AstRoot ast = null,
                                         int originalIndentSizeInSpaces = -1, bool formatting = false)
        {
            var         editorBuffer = line.Snapshot.EditorBuffer;
            IEditorLine prevLine     = null;

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

            ast = ast ?? editorBuffer.GetEditorDocument <IREditorDocument>()?.EditorTree?.AstRoot;
            if (ast == null)
            {
                return(0);
            }

            // 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);
            var 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, settings) + settings.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(), settings.TabSize);
                                    if (fc.CloseBrace == null || fc.CloseBrace.End >= (formatting ? line.Start : line.End))
                                    {
                                        fcIndentSize += settings.IndentSize;
                                    }
                                    return(fcIndentSize);
                                }
                                return(GetBlockIndent(line, settings));
                            }
                            return(originalIndentSizeInSpaces);
                        }
                    }
                }
            }

            // Candidate position #1 is first non-whitespace character
            // in the the previous line
            var 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.
                    }
                }
            }

            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?.Length ?? int.MaxValue).FirstOrDefault();

            var 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)
            {
                if (scope.Parent is IAstNodeWithScope parentStarement && 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, settings) + settings.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, settings) + settings.IndentSize);
                    }
                    // Line is not part of the scope, provide regular indent
                    return(OuterIndentSizeFromNode(editorBuffer, scopeStatement, settings.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)
                {
                    var endOfScopeLine = editorBuffer.CurrentSnapshot.GetLineNumberFromPosition(scopeStatement.Scope.CloseCurlyBrace.Start);
                    if (endOfScopeLine <= line.LineNumber)
                    {
                        return(OuterIndentSizeFromNode(editorBuffer, scopeStatement, settings.FormatOptions));
                    }
                }

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

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

            // Try locate the scope itself, if any
            if (scope?.OpenCurlyBrace != null)
            {
                if (scope.CloseCurlyBrace != null)
                {
                    var endOfScopeLine = editorBuffer.CurrentSnapshot.GetLineNumberFromPosition(scope.CloseCurlyBrace.Start);
                    if (endOfScopeLine == line.LineNumber)
                    {
                        return(OuterIndentSizeFromNode(editorBuffer, scope, settings.FormatOptions));
                    }
                }
                return(InnerIndentSizeFromNode(editorBuffer, scope, settings.FormatOptions));
            }

            return(0);
        }