예제 #1
0
        private static int CalculateIndentation(string baseline, ITextSnapshotLine line, IEditorOptions options, IClassifier classifier, ITextView textView) {
            int indentation = GetIndentation(baseline, options.GetTabSize());
            int tabSize = options.GetIndentSize();
            var tokens = classifier.GetClassificationSpans(line.Extent);
            if (tokens.Count > 0 && !IsUnterminatedStringToken(tokens[tokens.Count - 1])) {
                int tokenIndex = tokens.Count - 1;

                while (tokenIndex >= 0 &&
                    (tokens[tokenIndex].ClassificationType.IsOfType(PredefinedClassificationTypeNames.Comment) ||
                    tokens[tokenIndex].ClassificationType.IsOfType(PredefinedClassificationTypeNames.WhiteSpace))) {
                    tokenIndex--;
                }

                if (tokenIndex < 0) {
                    return indentation;
                }

                if (ReverseExpressionParser.IsExplicitLineJoin(tokens[tokenIndex])) {
                    // explicit line continuation, we indent 1 level for the continued line unless
                    // we're already indented because of multiple line continuation characters.

                    indentation = GetIndentation(line.GetText(), options.GetTabSize());
                    var joinedLine = tokens[tokenIndex].Span.Start.GetContainingLine();
                    if (joinedLine.LineNumber > 0) {
                        var prevLineSpans = classifier.GetClassificationSpans(tokens[tokenIndex].Span.Snapshot.GetLineFromLineNumber(joinedLine.LineNumber - 1).Extent);
                        if (prevLineSpans.Count == 0 || !ReverseExpressionParser.IsExplicitLineJoin(prevLineSpans[prevLineSpans.Count - 1])) {
                            indentation += tabSize;
                        }
                    } else {
                        indentation += tabSize;
                    }

                    return indentation;
                }

                string sline = tokens[tokenIndex].Span.GetText();
                var lastChar = sline.Length == 0 ? '\0' : sline[sline.Length - 1];

                // use the expression parser to figure out if we're in a grouping...
                var spans = textView.BufferGraph.MapDownToFirstMatch(
                    tokens[tokenIndex].Span,
                    SpanTrackingMode.EdgePositive,
                    PythonContentTypePrediciate
                );
                if (spans.Count == 0) {
                    return indentation;
                }
                
                var revParser = new ReverseExpressionParser(
                        spans[0].Snapshot,
                        spans[0].Snapshot.TextBuffer,
                        spans[0].Snapshot.CreateTrackingSpan(
                            spans[0].Span,
                            SpanTrackingMode.EdgePositive
                        )
                    );

                var tokenStack = new System.Collections.Generic.Stack<ClassificationSpan>();
                tokenStack.Push(null);  // end with an implicit newline
                bool endAtNextNull = false;

                foreach (var token in revParser) {
                    tokenStack.Push(token);
                    if (token == null && endAtNextNull) {
                        break;
                    } else if (token != null &&
                       token.ClassificationType == revParser.Classifier.Provider.Keyword &&
                       PythonKeywords.IsOnlyStatementKeyword(token.Span.GetText())) {
                        endAtNextNull = true;
                    }
                }

                var indentStack = new System.Collections.Generic.Stack<LineInfo>();
                var current = LineInfo.Empty;

                while (tokenStack.Count > 0) {
                    var token = tokenStack.Pop();
                    if (token == null) {
                        current.NeedsUpdate = true;
                    } else if (token.IsOpenGrouping()) {
                        indentStack.Push(current);
                        var start = token.Span.Start;
                        var line2 = start.GetContainingLine();
                        var next = tokenStack.Count > 0 ? tokenStack.Peek() : null;
                        if (next != null && next.Span.End <= line2.End) {
                            current = new LineInfo {
                                Indentation = start.Position - line2.Start.Position + 1
                            };
                        } else {
                            current = new LineInfo {
                                Indentation = GetIndentation(line2.GetText(), tabSize) + tabSize
                            };
                        }
                    } else if (token.IsCloseGrouping()) {
                        if (indentStack.Count > 0) {
                            current = indentStack.Pop();
                        } else {
                            current.NeedsUpdate = true;
                        }
                    } else if (ReverseExpressionParser.IsExplicitLineJoin(token)) {
                        while (token != null && tokenStack.Count > 0) {
                            token = tokenStack.Pop();
                        }
                    } else if (current.NeedsUpdate == true) {
                        var line2 = token.Span.Start.GetContainingLine();
                        current = new LineInfo {
                            Indentation = GetIndentation(line2.GetText(), tabSize)
                        };
                    }

                    if (token != null && ShouldDedentAfterKeyword(token)) {     // dedent after some statements
                        current.ShouldDedentAfter = true;
                    }

                    if (token != null && token.Span.GetText() == ":" &&         // indent after a colon
                        indentStack.Count == 0) {                               // except in a grouping
                        current.ShouldIndentAfter = true;
                        // If the colon isn't at the end of the line, cancel it out.
                        // If the following is a ShouldDedentAfterKeyword, only one dedent will occur.
                        current.ShouldDedentAfter = (tokenStack.Count != 0 && tokenStack.Peek() != null);
                    }
                }

                indentation = current.Indentation +
                    (current.ShouldIndentAfter ? tabSize : 0) -
                    (current.ShouldDedentAfter ? tabSize : 0);
            }

            // Map indentation back to the view's text buffer.
            int offset = 0;
            var viewLineStart = textView.BufferGraph.MapUpToSnapshot(line.Start, PointTrackingMode.Positive, PositionAffinity.Successor, textView.TextSnapshot);
            if (viewLineStart.HasValue) {
                offset = viewLineStart.Value.Position - viewLineStart.Value.GetContainingLine().Start.Position;
            }

            return offset + indentation;
        }
예제 #2
0
        void TextBuffer_Changed(object sender, TextContentChangedEventArgs e)
        {
            string filename = e.After.TextBuffer.GetFilePath();

            if (_textView != null &&
                _textView.Caret.Position.BufferPosition.Position >= 0 &&
                _textView.Caret.Position.BufferPosition.Position <= e.After.Length &&
                _ignoreNextChange != _textView.Caret.Position.BufferPosition.Position)
            {
                if (e.Changes.Count == 0 ||
                    e.Changes[0].Delta < 0 ||
                    string.IsNullOrWhiteSpace(e.Changes[0].NewText))
                {
                    return;
                }

                var line           = e.After.GetLineFromPosition(_textView.Caret.Position.BufferPosition.Position);
                var currLineTokens = _classifier.GetClassificationSpans(line);

                var lineStr = line.GetText();

                TokenKind alignWith        = TokenKind.EndOfFile;
                bool      indentAfterAlign = false;
                bool      useContains      = false;

                if (currLineTokens.Count > 0 && currLineTokens[0].ClassificationType.IsOfType(PredefinedClassificationTypeNames.Keyword))
                {
                    if (currLineTokens[0].Span.GetText().Equals("end", StringComparison.OrdinalIgnoreCase))
                    {
                        if (currLineTokens.Count > 1)
                        {
                            var tok = Tokens.GetToken(currLineTokens[1].Span.GetText());
                            if (tok != null && AutoIndent.BlockKeywords.ContainsKey(tok.Kind))
                            {
                                useContains = AutoIndent.BlockKeywordsContainsCheck.Contains(tok.Kind);
                                alignWith   = tok.Kind;
                            }
                        }
                    }
                    else
                    {
                        var tok = Tokens.GetToken(currLineTokens[0].Span.GetText());
                        if (tok != null && AutoIndent.SubBlockKeywords.ContainsKey(tok.Kind))
                        {
                            alignWith        = AutoIndent.SubBlockKeywords[tok.Kind].Item1;
                            indentAfterAlign = AutoIndent.SubBlockKeywords[tok.Kind].Item2;
                        }
                    }
                }

                if (alignWith != TokenKind.EndOfFile)
                {
                    var keyword = Tokens.TokenKinds[alignWith];
                    ITextSnapshotLine prevLine;
                    int    prevLineNo  = line.LineNumber - 1;
                    bool   found       = false;
                    string prevLineStr = null;
                    // find the line that corresponds
                    int inNestedBlockCount = 0;

                    IList <ClassificationSpan> lineTokens = null;

                    while (prevLineNo >= 0)
                    {
                        prevLine    = e.After.GetLineFromLineNumber(prevLineNo);
                        prevLineStr = prevLine.GetText();
                        lineTokens  = _classifier.GetClassificationSpans(prevLine);

                        if (lineTokens.Count > 0 && lineTokens[0].ClassificationType.IsOfType(PredefinedClassificationTypeNames.Keyword))
                        {
                            if (lineTokens[0].Span.GetText().Equals("end", StringComparison.OrdinalIgnoreCase) &&
                                lineTokens.Count > 1 &&
                                lineTokens[1].Span.GetText().Equals(keyword, StringComparison.OrdinalIgnoreCase))
                            {
                                inNestedBlockCount++;
                            }
                            else if ((!useContains && lineTokens[0].Span.GetText().Equals(keyword, StringComparison.OrdinalIgnoreCase)) ||
                                     (useContains && lineTokens.Any(x => x.Span.GetText().Equals(keyword, StringComparison.OrdinalIgnoreCase))))
                            {
                                if (inNestedBlockCount > 0)
                                {
                                    inNestedBlockCount--;
                                }
                                else
                                {
                                    found = true;
                                    break;
                                }
                            }
                            else if (!useContains &&
                                     keyword.Equals("function", StringComparison.OrdinalIgnoreCase) &&
                                     lineTokens.Count > 1 &&
                                     lineTokens[1].Span.GetText().Equals(keyword, StringComparison.OrdinalIgnoreCase))
                            {
                                found = true;
                                break;
                            }
                        }
                        prevLineNo--;
                    }

                    // get the line's indentation
                    if (found)
                    {
                        int indentSize         = _options.GetIndentSize();
                        int desiredIndentation = AutoIndent.GetIndentation(prevLineStr, indentSize);
                        if (indentAfterAlign)
                        {
                            desiredIndentation += indentSize;
                        }
                        int currIndentation = AutoIndent.GetIndentation(lineStr, indentSize);
                        if (desiredIndentation != currIndentation)
                        {
                            string replacement = null;
                            if (desiredIndentation < currIndentation)
                            {
                                replacement = lineStr.Substring(currIndentation - desiredIndentation);
                            }
                            else
                            {
                                StringBuilder sb = new StringBuilder();
                                for (int i = 0; i < (desiredIndentation - currIndentation); i++)
                                {
                                    sb.Append(' ');
                                }
                                sb.Append(lineStr);
                                replacement = sb.ToString();
                            }
                            _ignoreNextChange = _textView.Caret.Position.BufferPosition.Position;
                            ITextEdit edit = _textView.TextBuffer.CreateEdit();
                            edit.Replace(new Span(line.Start, line.Length), replacement);
                            edit.Apply();
                            return;
                        }
                    }
                }
            }
            _ignoreNextChange = -1;
        }
예제 #3
0
        private static int CalculateIndentation(string baseline, ITextSnapshotLine line, IEditorOptions options, IClassifier classifier, ITextView textView)
        {
            int indentation = GetIndentation(baseline, options.GetTabSize());
            int tabSize     = options.GetIndentSize();
            var tokens      = classifier.GetClassificationSpans(line.Extent);

            if (tokens.Count > 0 && !IsUnterminatedStringToken(tokens[tokens.Count - 1]))
            {
                int tokenIndex = tokens.Count - 1;

                while (tokenIndex >= 0 &&
                       (tokens[tokenIndex].ClassificationType.IsOfType(PredefinedClassificationTypeNames.Comment) ||
                        tokens[tokenIndex].ClassificationType.IsOfType(PredefinedClassificationTypeNames.WhiteSpace)))
                {
                    tokenIndex--;
                }

                if (tokenIndex < 0)
                {
                    return(indentation);
                }

                if (Genero4glReverseParser.IsExplicitLineJoin(tokens[tokenIndex]))
                {
                    // explicit line continuation, we indent 1 level for the continued line unless
                    // we're already indented because of multiple line continuation characters.

                    indentation = GetIndentation(line.GetText(), options.GetTabSize());
                    var joinedLine = tokens[tokenIndex].Span.Start.GetContainingLine();
                    if (joinedLine.LineNumber > 0)
                    {
                        var prevLineSpans = classifier.GetClassificationSpans(tokens[tokenIndex].Span.Snapshot.GetLineFromLineNumber(joinedLine.LineNumber - 1).Extent);
                        if (prevLineSpans.Count == 0 || !Genero4glReverseParser.IsExplicitLineJoin(prevLineSpans[prevLineSpans.Count - 1]))
                        {
                            indentation += tabSize;
                        }
                    }
                    else
                    {
                        indentation += tabSize;
                    }

                    return(indentation);
                }

                string sline    = tokens[tokenIndex].Span.GetText();
                var    lastChar = sline.Length == 0 ? '\0' : sline[sline.Length - 1];

                // use the expression parser to figure out if we're in a grouping...
                var spans = textView.BufferGraph.MapDownToFirstMatch(
                    tokens[tokenIndex].Span,
                    SpanTrackingMode.EdgePositive,
                    PythonContentTypePrediciate
                    );
                if (spans.Count == 0)
                {
                    return(indentation);
                }

                var revParser = new Genero4glReverseParser(
                    spans[0].Snapshot,
                    spans[0].Snapshot.TextBuffer,
                    spans[0].Snapshot.CreateTrackingSpan(
                        spans[0].Span,
                        SpanTrackingMode.EdgePositive
                        )
                    );

                var tokenStack = new List <ClassificationSpan>();
                tokenStack.Insert(0, null);
                bool endAtNextNull = false;

                foreach (var token in revParser)
                {
                    tokenStack.Insert(0, token);
                    if (token == null && endAtNextNull)
                    {
                        break;
                    }
                    else if (token != null &&
                             token.ClassificationType == Genero4glClassifierProvider.Keyword)
                    {
                        var tok = Tokens.GetToken(token.Span.GetText());
                        if (tok != null && Genero4glAst.ValidStatementKeywords.Contains(tok.Kind))
                        {
                            switch (tok.Kind)
                            {
                            // Handle any tokens that are valid statement keywords in the autocomplete context but not in the "statement start" context
                            case TokenKind.EndKeyword:
                                continue;

                            default:
                                endAtNextNull = true;
                                break;
                            }
                        }
                    }
                }

                var indentStack = new System.Collections.Generic.Stack <LineInfo>();
                var current     = LineInfo.Empty;
                List <CancelIndent> cancelIndent           = null;
                int                cancelIndentStartingAt  = -1;
                TokenKind          firstStatement          = TokenKind.EndOfFile;
                TokenKind          latestIndentChangeToken = TokenKind.EndOfFile;
                ClassificationSpan firstToken = null;

                for (int i = 0; i < tokenStack.Count; i++)
                {
                    var token = tokenStack[i];
                    if (token != null && firstToken == null)
                    {
                        firstToken = token;
                    }

                    if (token == null)
                    {
                        current.NeedsUpdate = true;
                    }
                    else if (token.IsOpenGrouping())
                    {
                        indentStack.Push(current);
                        var start = token.Span.Start;
                        var line2 = start.GetContainingLine();
                        current = new LineInfo
                        {
                            Indentation = start.Position - line2.Start.Position + 1
                        };
                    }
                    else if (token.IsCloseGrouping())
                    {
                        if (indentStack.Count > 0)
                        {
                            current = indentStack.Pop();
                        }
                        else
                        {
                            current.NeedsUpdate = true;
                        }
                    }
                    else if (Genero4glReverseParser.IsExplicitLineJoin(token))
                    {
                        while (token != null && i + 1 < tokenStack.Count)
                        {
                            i++;
                            token = tokenStack[i];
                        }
                    }
                    else if (current.NeedsUpdate == true)
                    {
                        var tok = Tokens.GetToken(token.Span.GetText());
                        if (tok == null || !Genero4glAst.ValidStatementKeywords.Contains(tok.Kind))
                        {
                            current.NeedsUpdate = false;
                        }
                        else
                        {
                            switch (tok.Kind)
                            {
                            // Handle any tokens that are valid statement keywords in the autocomplete context but not in the "statement start" context
                            case TokenKind.EndKeyword:
                                if (firstStatement != TokenKind.EndOfFile)
                                {
                                    current.NeedsUpdate = false;
                                }
                                else
                                {
                                    latestIndentChangeToken = tok.Kind;
                                }
                                break;

                            default:
                            {
                                if (firstStatement == TokenKind.EndOfFile)
                                {
                                    firstStatement = tok.Kind;
                                }
                                var line2 = token.Span.Start.GetContainingLine();
                                current = new LineInfo
                                {
                                    Indentation = GetIndentation(line2.GetText(), tabSize)
                                };
                                break;
                            }
                            }
                        }
                    }

                    if (token != null && current.ShouldIndentAfter && cancelIndent != null)
                    {
                        // Check to see if we have following tokens that would cancel the current indent.

                        var  tok           = Tokens.GetToken(token.Span.GetText());
                        var  tokenCategory = token.ClassificationType;
                        bool allPast       = true;
                        bool cancel        = false;
                        foreach (var ci in cancelIndent)
                        {
                            if (ci.TokensAhead < (i - cancelIndentStartingAt))
                            {
                                continue;
                            }
                            else
                            {
                                allPast = false;
                                if (ci.TokensAhead == (i - cancelIndentStartingAt))
                                {
                                    if (ci.UseCategory && ci.CancelCategory != null)
                                    {
                                        cancel = tokenCategory == ci.CancelCategory;
                                    }
                                    else if (tok != null)
                                    {
                                        cancel = tok.Kind == ci.CancelToken;
                                    }
                                    if (cancel)
                                    {
                                        break;
                                    }
                                }
                            }
                        }
                        if (cancel)
                        {
                            current.ShouldIndentAfter = false;
                        }
                        if (cancel || allPast)
                        {
                            cancelIndent            = null;
                            cancelIndentStartingAt  = -1;
                            latestIndentChangeToken = TokenKind.EndOfFile;
                        }
                    }

                    if (token != null && ShouldDedentAfterKeyword(token))
                    {     // dedent after some statements
                        current.ShouldDedentAfter = true;
                    }

                    TokenKind tempChangeToken;
                    if (token != null &&
                        indentStack.Count == 0 &&
                        firstToken == token &&
                        ShouldIndentAfterKeyword(token, out tempChangeToken, out cancelIndent))
                    {                               // except in a grouping
                        if (latestIndentChangeToken != TokenKind.EndKeyword)
                        {
                            current.ShouldIndentAfter = true;
                        }
                        latestIndentChangeToken = tempChangeToken;
                        if (cancelIndent != null)
                        {
                            cancelIndentStartingAt = i;
                        }
                    }
                }

                if (tokenStack.Count > 2 &&
                    tokenStack[tokenStack.Count - 2] != null)
                {
                    if (latestIndentChangeToken != TokenKind.EndOfFile &&
                        _customIndentingRules.ContainsKey(latestIndentChangeToken))
                    {
                        var potentialIndent = _customIndentingRules[latestIndentChangeToken](tokenStack, tabSize);
                        if (potentialIndent != 0)
                        {
                            return(potentialIndent);
                        }
                    }

                    // see if we have specific alignment rules
                    if (firstStatement != TokenKind.EndOfFile &&
                        _customIndentingRules.ContainsKey(firstStatement))
                    {
                        var potentialIndent = _customIndentingRules[firstStatement](tokenStack, tabSize);
                        if (potentialIndent != 0)
                        {
                            return(potentialIndent);
                        }
                    }
                }

                indentation = current.Indentation +
                              (current.ShouldIndentAfter ? tabSize : 0) -
                              (current.ShouldDedentAfter ? tabSize : 0);
            }

            return(indentation);
        }
예제 #4
0
        private static int CalculateIndentation(string baseline, ITextSnapshotLine line, IEditorOptions options, IClassifier classifier, ITextView textView)
        {
            int indentation = GetIndentation(baseline, options.GetTabSize());
            int tabSize     = options.GetIndentSize();
            var tokens      = classifier.GetClassificationSpans(line.Extent);

            if (tokens.Count > 0 && !IsUnterminatedStringToken(tokens[tokens.Count - 1]))
            {
                int tokenIndex = tokens.Count - 1;

                while (tokenIndex >= 0 &&
                       (tokens[tokenIndex].ClassificationType.IsOfType(PredefinedClassificationTypeNames.Comment) ||
                        tokens[tokenIndex].ClassificationType.IsOfType(PredefinedClassificationTypeNames.WhiteSpace)))
                {
                    tokenIndex--;
                }

                if (tokenIndex < 0)
                {
                    return(indentation);
                }

                if (ReverseExpressionParser.IsExplicitLineJoin(tokens[tokenIndex]))
                {
                    // explicit line continuation, we indent 1 level for the continued line unless
                    // we're already indented because of multiple line continuation characters.

                    indentation = GetIndentation(line.GetText(), options.GetTabSize());
                    var joinedLine = tokens[tokenIndex].Span.Start.GetContainingLine();
                    if (joinedLine.LineNumber > 0)
                    {
                        var prevLineSpans = classifier.GetClassificationSpans(tokens[tokenIndex].Span.Snapshot.GetLineFromLineNumber(joinedLine.LineNumber - 1).Extent);
                        if (prevLineSpans.Count == 0 || !ReverseExpressionParser.IsExplicitLineJoin(prevLineSpans[prevLineSpans.Count - 1]))
                        {
                            indentation += tabSize;
                        }
                    }
                    else
                    {
                        indentation += tabSize;
                    }

                    return(indentation);
                }

                string sline    = tokens[tokenIndex].Span.GetText();
                var    lastChar = sline.Length == 0 ? '\0' : sline[sline.Length - 1];

                // use the expression parser to figure out if we're in a grouping...
                var spans = textView.BufferGraph.MapDownToFirstMatch(
                    tokens[tokenIndex].Span,
                    SpanTrackingMode.EdgePositive,
                    PythonContentTypePrediciate
                    );
                if (spans.Count == 0)
                {
                    return(indentation);
                }

                var revParser = new ReverseExpressionParser(
                    spans[0].Snapshot,
                    spans[0].Snapshot.TextBuffer,
                    spans[0].Snapshot.CreateTrackingSpan(
                        spans[0].Span,
                        SpanTrackingMode.EdgePositive
                        )
                    );

                var tokenStack = new System.Collections.Generic.Stack <ClassificationSpan>();
                tokenStack.Push(null);  // end with an implicit newline
                bool endAtNextNull = false;

                foreach (var token in revParser)
                {
                    tokenStack.Push(token);
                    if (token == null && endAtNextNull)
                    {
                        break;
                    }
                    else if (token != null &&
                             token.ClassificationType == revParser.Classifier.Provider.Keyword &&
                             PythonKeywords.IsOnlyStatementKeyword(token.Span.GetText()))
                    {
                        endAtNextNull = true;
                    }
                }

                var indentStack = new System.Collections.Generic.Stack <LineInfo>();
                var current     = LineInfo.Empty;

                while (tokenStack.Count > 0)
                {
                    var token = tokenStack.Pop();
                    if (token == null)
                    {
                        current.NeedsUpdate = true;
                    }
                    else if (token.IsOpenGrouping())
                    {
                        indentStack.Push(current);
                        var start = token.Span.Start;
                        var line2 = start.GetContainingLine();
                        var next  = tokenStack.Count > 0 ? tokenStack.Peek() : null;
                        if (next != null && next.Span.End <= line2.End)
                        {
                            current = new LineInfo {
                                Indentation = start.Position - line2.Start.Position + 1
                            };
                        }
                        else
                        {
                            current = new LineInfo {
                                Indentation = GetIndentation(line2.GetText(), tabSize) + tabSize
                            };
                        }
                    }
                    else if (token.IsCloseGrouping())
                    {
                        if (indentStack.Count > 0)
                        {
                            current = indentStack.Pop();
                        }
                        else
                        {
                            current.NeedsUpdate = true;
                        }
                    }
                    else if (ReverseExpressionParser.IsExplicitLineJoin(token))
                    {
                        while (token != null && tokenStack.Count > 0)
                        {
                            token = tokenStack.Pop();
                        }
                    }
                    else if (current.NeedsUpdate == true)
                    {
                        var line2 = token.Span.Start.GetContainingLine();
                        current = new LineInfo {
                            Indentation = GetIndentation(line2.GetText(), tabSize)
                        };
                    }

                    if (token != null && ShouldDedentAfterKeyword(token))       // dedent after some statements
                    {
                        current.ShouldDedentAfter = true;
                    }

                    if (token != null && token.Span.GetText() == ":" &&         // indent after a colon
                        indentStack.Count == 0)                                 // except in a grouping
                    {
                        current.ShouldIndentAfter = true;
                        // If the colon isn't at the end of the line, cancel it out.
                        // If the following is a ShouldDedentAfterKeyword, only one dedent will occur.
                        current.ShouldDedentAfter = (tokenStack.Count != 0 && tokenStack.Peek() != null);
                    }
                }

                indentation = current.Indentation +
                              (current.ShouldIndentAfter ? tabSize : 0) -
                              (current.ShouldDedentAfter ? tabSize : 0);
            }

            return(indentation);
        }
예제 #5
0
파일: AutoIndent.cs 프로젝트: yepeiwen/PTVS
        private static int CalculateIndentation(
            string baseline,
            ITextSnapshotLine line,
            IEditorOptions options,
            PythonTextBufferInfo buffer
            )
        {
            var snapshot = line.Snapshot;

            if (snapshot.TextBuffer != buffer.Buffer)
            {
                throw new ArgumentException("buffer mismatch");
            }

            int indentation = GetIndentation(baseline, options.GetTabSize());
            int tabSize     = options.GetIndentSize();
            var tokens      = buffer.GetTokens(line).ToList();

            while (tokens.Count > 0 && IsWhitespace(tokens[tokens.Count - 1].Category))
            {
                tokens.RemoveAt(tokens.Count - 1);
            }

            if (tokens.Count == 0 || IsUnterminatedStringToken(tokens[tokens.Count - 1], snapshot))
            {
                return(indentation);
            }

            if (HasExplicitLineJoin(tokens, snapshot))
            {
                // explicit line continuation, we indent 1 level for the continued line unless
                // we're already indented because of multiple line continuation characters.
                indentation = GetIndentation(line.GetText(), options.GetTabSize());
                var joinedLine = line.LineNumber - 1;
                if (joinedLine >= 0)
                {
                    var prevLineTokens = buffer.GetTokens(snapshot.GetLineFromLineNumber(joinedLine)).ToList();
                    if (prevLineTokens.Count == 0 || !HasExplicitLineJoin(prevLineTokens, snapshot))
                    {
                        indentation += tabSize;
                    }
                }
                else
                {
                    indentation += tabSize;
                }

                return(indentation);
            }

            var tokenStack = new Stack <TrackingTokenInfo?>();

            tokenStack.Push(null);  // end with an implicit newline
            int endAtLine = -1, currentLine = tokens.Last().LineNumber;

            foreach (var t in buffer.GetTokensInReverseFromPoint(tokens.Last().ToSnapshotSpan(snapshot).Start))
            {
                if (t.LineNumber == currentLine)
                {
                    tokenStack.Push(t);
                }
                else
                {
                    tokenStack.Push(null);
                }

                if (t.LineNumber == endAtLine)
                {
                    break;
                }
                else if (t.Category == TokenCategory.Keyword && PythonKeywords.IsOnlyStatementKeyword(t.GetText(snapshot), buffer.LanguageVersion))
                {
                    endAtLine = t.LineNumber - 1;
                }

                if (t.LineNumber != currentLine)
                {
                    currentLine = t.LineNumber;
                    if (t.Category != TokenCategory.WhiteSpace && t.Category != TokenCategory.Comment && t.Category != TokenCategory.LineComment)
                    {
                        tokenStack.Push(t);
                    }
                }
            }

            var indentStack = new Stack <LineInfo>();
            var current     = LineInfo.Empty;

            while (tokenStack.Count > 0)
            {
                var t = tokenStack.Pop();
                if (t == null)
                {
                    current.NeedsUpdate = true;
                    continue;
                }

                var tline = new Lazy <string>(() => snapshot.GetLineFromLineNumber(t.Value.LineNumber).GetText());

                if (IsOpenGrouping(t.Value, snapshot))
                {
                    indentStack.Push(current);
                    var next = tokenStack.Count > 0 ? tokenStack.Peek() : null;
                    if (next != null && next.Value.LineNumber == t.Value.LineNumber)
                    {
                        // Put indent at same depth as grouping
                        current = new LineInfo {
                            Indentation = t.Value.ToSourceSpan().End.Column - 1
                        };
                    }
                    else
                    {
                        // Put indent at one indent deeper than this line
                        current = new LineInfo {
                            Indentation = GetIndentation(tline.Value, tabSize) + tabSize
                        };
                    }
                }
                else if (IsCloseGrouping(t.Value, snapshot))
                {
                    if (indentStack.Count > 0)
                    {
                        current = indentStack.Pop();
                    }
                    else
                    {
                        current.NeedsUpdate = true;
                    }
                }
                else if (IsExplicitLineJoin(t.Value, snapshot))
                {
                    while (t != null && tokenStack.Count > 0)
                    {
                        t = tokenStack.Pop();
                    }
                    if (!t.HasValue)
                    {
                        continue;
                    }
                }
                else if (current.NeedsUpdate == true)
                {
                    current = new LineInfo {
                        Indentation = GetIndentation(tline.Value, tabSize)
                    };
                }

                if (ShouldDedentAfterKeyword(t.Value, snapshot))      // dedent after some statements
                {
                    current.ShouldDedentAfter = true;
                }

                if (IsColon(t.Value, snapshot) &&       // indent after a colon
                    indentStack.Count == 0)             // except in a grouping
                {
                    current.ShouldIndentAfter = true;
                    // If the colon isn't at the end of the line, cancel it out.
                    // If the following is a ShouldDedentAfterKeyword, only one dedent will occur.
                    current.ShouldDedentAfter = (tokenStack.Count != 0 && tokenStack.Peek() != null);
                }
            }

            indentation = current.Indentation +
                          (current.ShouldIndentAfter ? tabSize : 0) -
                          (current.ShouldDedentAfter ? tabSize : 0);

            return(indentation);
        }