diff --git a/src/AddIns/BackendBindings/VBNetBinding/Project/Src/FormattingStrategy/VBNetFormattingStrategy.cs b/src/AddIns/BackendBindings/VBNetBinding/Project/Src/FormattingStrategy/VBNetFormattingStrategy.cs index 30ff1c4ddf..662730e902 100644 --- a/src/AddIns/BackendBindings/VBNetBinding/Project/Src/FormattingStrategy/VBNetFormattingStrategy.cs +++ b/src/AddIns/BackendBindings/VBNetBinding/Project/Src/FormattingStrategy/VBNetFormattingStrategy.cs @@ -9,6 +9,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; using System.Text; using System.Text.RegularExpressions; @@ -230,11 +231,12 @@ namespace VBNetBinding.FormattingStrategy VBStatement statement = statement_; // allow passing statement byref if (Regex.IsMatch(texttoreplace.Trim(), statement.StartRegex, RegexOptions.IgnoreCase)) { string indentation = GetIndentation(textArea, lineNr - 1); - if (IsEndStatementNeeded(textArea, ref statement, lineNr)) { + if (IsEndStatementNeeded(textArea, ref statement, lineNr)) textArea.Document.Replace(curLine.Offset, curLine.Length, terminator + indentation + statement.EndStatement); - } - for (int i = 0; i < statement.IndentPlus; i++) { - indentation += Tab.GetIndentationString(textArea.Document); + if (!IsInsideInterface(textArea, lineNr) || statement == interfaceStatement) { + for (int i = 0; i < statement.IndentPlus; i++) { + indentation += Tab.GetIndentationString(textArea.Document); + } } textArea.Document.Replace(curLine.Offset, curLine.Length, indentation + curLineText.Trim()); @@ -242,17 +244,6 @@ namespace VBNetBinding.FormattingStrategy return; } } - - // fix for SD2-1284 - string prevLineText = textArea.Document.GetText(lineAbove); - string prevLineText2 = (lineNr > 1) ? textArea.Document.GetText(textArea.Document.GetLineSegment(lineNr - 2)) : ""; - if (StripComment(prevLineText.ToLowerInvariant()).Trim(' ', '\t', '\r', '\n').StartsWith("case")) { - string indentation = GetIndentation(textArea, lineNr - 1) + Tab.GetIndentationString(textArea.Document); - textArea.Document.Replace(curLine.Offset, curLine.Length, indentation + curLineText.Trim()); - SmartIndentInternal(textArea, lineNr - 1, lineNr); - textArea.Caret.Column = GetIndentation(textArea, lineNr).Length; - return; - } } if (IsInString(lineAboveText)) @@ -283,8 +274,8 @@ namespace VBNetBinding.FormattingStrategy textArea.Document.Replace(curLine.Offset, curLine.Length, newLineText); } if (IsElseConstruct(lineAboveText)) - SmartIndentLine(textArea, lineNr - 1); - textArea.Caret.Column = indent.Length; + SmartIndentInternal(textArea, lineNr - 1, lineNr); + textArea.Caret.Column = GetIndentation(textArea, lineNr).Length; } } else if(ch == '>') @@ -337,13 +328,53 @@ namespace VBNetBinding.FormattingStrategy return (count > 0); } + bool IsInsideInterface(TextArea area, int lineNr) + { + ILexer lexer = ParserFactory.CreateLexer(SupportedLanguage.VBNet, new StringReader(area.Document.TextContent)); + + Stack tokens = new Stack(); + + Token currentToken = null; + Token prevToken = null; + + while ((currentToken = lexer.NextToken()).Kind != Tokens.EOF) { + if (prevToken == null) + prevToken = currentToken; + + if (currentToken.EndLocation.Line <= lineNr && + IsDeclaration(currentToken.Kind) && + IsBlockStart(lexer, currentToken, prevToken)) { + + tokens.Push(currentToken); + } + + if (currentToken.EndLocation.Line <= lineNr && + IsDeclaration(currentToken.Kind) && + IsBlockEnd(currentToken, prevToken)) { + + if (tokens.Count > 0) + tokens.Pop(); + } + + if (currentToken.EndLocation.Line > lineNr) + break; + } + + if (tokens.Count > 0) + return tokens.Pop().Kind == Tokens.Interface; + + return false; + } + bool IsElseConstruct(string line) { - string t = StripComment(line).ToLowerInvariant(); - if (t.StartsWith("case ")) return true; - if (t == "else" || t.StartsWith("elseif ")) return true; - if (t == "catch" || t.StartsWith("catch ")) return true; - if (t == "finally") return true; + string t = StripComment(line); + if (t.StartsWith("case ", StringComparison.OrdinalIgnoreCase)) return true; + if (string.Compare(t, "else", true) == 0 || + t.StartsWith("elseif ", StringComparison.OrdinalIgnoreCase)) return true; + if (string.Compare(t, "catch", true) == 0 || + t.StartsWith("catch ", StringComparison.OrdinalIgnoreCase)) return true; + if (string.Compare(t, "finally", true) == 0) return true; return false; } @@ -374,7 +405,10 @@ namespace VBNetBinding.FormattingStrategy bool IsDeclaration(int type) { - return (type == Tokens.Class) || (type == Tokens.Module); + return (type == Tokens.Class) || + (type == Tokens.Module) || + (type == Tokens.Structure) || + (type == Tokens.Interface); } bool IsEndStatementNeeded(TextArea textArea, ref VBStatement statement, int lineNr) @@ -410,9 +444,12 @@ namespace VBNetBinding.FormattingStrategy prevToken = currentToken; } - if (missingEnds.Count > 0) { + while (tokens.Count > 0) + missingEnds.Add(tokens.Pop()); + + if (missingEnds.Count > 0) return GetClosestMissing(missingEnds, statement, textArea, lineNr) != null; - } else + else return false; } @@ -471,8 +508,6 @@ namespace VBNetBinding.FormattingStrategy bool IsMatchingStatement(Token token, VBStatement statement) { - // funktioniert noch nicht! - if (token.Kind == Tokens.For && statement.EndStatement == "Next") return true; @@ -519,7 +554,9 @@ namespace VBNetBinding.FormattingStrategy { ILexer lexer = ParserFactory.CreateLexer(SupportedLanguage.VBNet, new StringReader(textArea.Document.TextContent)); - int indentation = 0; + Stack indentation = new Stack(); + + indentation.Push(string.Empty); int oldLine = 0; @@ -548,38 +585,38 @@ namespace VBNetBinding.FormattingStrategy isDelegate = isDeclare = isMustOverride = false; if (IsSpecialCase(currentToken, prevToken)) { - ApplyToRange(textArea, ref indentation, oldLine, currentToken.Location.Line - 1, begin, end); - indentation--; - ApplyToRange(textArea, ref indentation, currentToken.Location.Line - 1, currentToken.Location.Line, begin, end); - indentation++; + ApplyToRange(textArea, indentation, oldLine, currentToken.Location.Line, begin, end); + Unindent(indentation); + ApplyToRange(textArea, indentation, currentToken.Location.Line - 1, currentToken.Location.Line, begin, end); + Indent(textArea, indentation); oldLine = currentToken.Location.Line; } if (IsBlockEnd(currentToken, prevToken)) { - ApplyToRange(textArea, ref indentation, oldLine, currentToken.Location.Line - 1, begin, end); + ApplyToRange(textArea, indentation, oldLine, currentToken.Location.Line - 1, begin, end); if (currentToken.Kind == Tokens.Interface) inInterface = false; if (!inInterface && !isMustOverride && !isDeclare && !isDelegate) { - indentation--; + Unindent(indentation); if (currentToken.Kind == Tokens.Select) - indentation--; + Unindent(indentation); } oldLine = currentToken.Location.Line - 1; } if (IsBlockStart(lexer, currentToken, prevToken)) { - ApplyToRange(textArea, ref indentation, oldLine, currentToken.Location.Line, begin, end); + ApplyToRange(textArea, indentation, oldLine, currentToken.Location.Line, begin, end); if (!inInterface && !isMustOverride && !isDeclare && !isDelegate) { - indentation++; + Indent(textArea, indentation); if (currentToken.Kind == Tokens.Select) - indentation++; + Indent(textArea, indentation); } if (currentToken.Kind == Tokens.Interface) @@ -592,9 +629,29 @@ namespace VBNetBinding.FormattingStrategy } // do last indent step - ApplyToRange(textArea, ref indentation, oldLine, prevToken.Location.Line, begin, end); + int newLine = prevToken.Location.Line; + + if (oldLine > newLine) + newLine = oldLine + 1; - return indentation; + ApplyToRange(textArea, indentation, oldLine, newLine, begin, end); + + return indentation.Peek().Length; + } + + void Unindent(Stack indentation) + { + indentation.Pop(); + } + + void Indent(TextArea textArea, Stack indentation) + { + bool useSpaces = textArea.TextEditorProperties.ConvertTabsToSpaces; + int indentationSize = textArea.TextEditorProperties.IndentationSize; + + string addIndent = (useSpaces) ? new string(' ', indentationSize) : "\t"; + + indentation.Push(indentation.Peek() + addIndent); } bool IsBlockStart(ILexer lexer, Token current, Token prev) @@ -609,12 +666,8 @@ namespace VBNetBinding.FormattingStrategy Token currentToken = null; while ((currentToken = lexer.Peek()).Kind != Tokens.EOL) { - if (currentToken.Kind == Tokens.Then) { - if (lexer.Peek().Kind == Tokens.EOL) - return true; - else - return false; - } + if (currentToken.Kind == Tokens.Then) + return lexer.Peek().Kind == Tokens.EOL; } } @@ -695,10 +748,7 @@ namespace VBNetBinding.FormattingStrategy case Tokens.Else: return true; case Tokens.Case: - if (prev.Kind == Tokens.Select) - return false; - else - return true; + return prev.Kind != Tokens.Select; case Tokens.ElseIf: return true; case Tokens.Catch: @@ -710,74 +760,38 @@ namespace VBNetBinding.FormattingStrategy return false; } - bool multiLine = false; - bool otherMultiLine = false; - - void ApplyToRange(TextArea textArea, ref int indentation, int begin, int end, int selBegin, int selEnd) + void ApplyToRange(TextArea textArea, Stack indentation, int begin, int end, int selBegin, int selEnd) { - bool useSpaces = textArea.TextEditorProperties.ConvertTabsToSpaces; - int indentationSize = textArea.TextEditorProperties.IndentationSize; - int tabSize = textArea.TextEditorProperties.TabIndent; - int spaces = indentationSize * (indentation < 0 ? 0 : indentation); - int tabs = 0; - - if (begin >= end) { - LineSegment curLine = textArea.Document.GetLineSegment(begin); - string lineText = textArea.Document.GetText(curLine).Trim(' ', '\t', '\r', '\n'); - - string newLine = new string('\t', tabs) + new string(' ', spaces) + lineText; - - if (begin >= selBegin && begin <= selEnd) - SmartReplaceLine(textArea.Document, curLine, newLine); - } + bool multiLine = false; for (int i = begin; i < end; i++) { LineSegment curLine = textArea.Document.GetLineSegment(i); string lineText = textArea.Document.GetText(curLine).Trim(' ', '\t', '\r', '\n'); - string noComments = StripComment(lineText).TrimEnd(' ', '\t', '\r', '\n'); - - if (otherMultiLine && noComments == "}") { - indentation--; - otherMultiLine = false; + if (i < selBegin || i > selEnd) { + indentation.Pop(); + indentation.Push(GetIndentation(textArea, i)); } - spaces = indentationSize * (indentation < 0 ? 0 : indentation); - tabs = 0; - - if (!useSpaces) { - tabs = (int)(spaces / tabSize); - spaces %= tabSize; + // change indentation before (indent this line) + if (multiLine && noComments.EndsWith("}")) { + Unindent(indentation); + multiLine = false; } - - string newLine = new string('\t', tabs) + new string(' ', spaces) + lineText; + SmartReplaceLine(textArea.Document, curLine, indentation.Peek() + lineText); - if (noComments.EndsWith("_") && !IsStatement(noComments) && !otherMultiLine) { - otherMultiLine = true; - indentation++; - } - - if (noComments.EndsWith("_") && IsStatement(noComments)) { + // change indentation afterwards (indent next line) + if (!multiLine && noComments.EndsWith("_")) { + Indent(textArea, indentation); multiLine = true; - indentation++; } - - if (!noComments.EndsWith("_") && multiLine) { - indentation--; + + if (multiLine && !noComments.EndsWith("_")) { multiLine = false; + Unindent(indentation); } - - if (!noComments.EndsWith("_") && otherMultiLine) { - indentation--; - otherMultiLine = false; - } - - if (i >= selBegin && i <= selEnd) - SmartReplaceLine(textArea.Document, curLine, newLine); - - LoggingService.Debug("'" + newLine + "'"); } } diff --git a/src/AddIns/BackendBindings/VBNetBinding/Test/FormattingStrategy/IndentationTests.cs b/src/AddIns/BackendBindings/VBNetBinding/Test/FormattingStrategy/IndentationTests.cs new file mode 100644 index 0000000000..c48e47eafa --- /dev/null +++ b/src/AddIns/BackendBindings/VBNetBinding/Test/FormattingStrategy/IndentationTests.cs @@ -0,0 +1,87 @@ +// +// +// +// +// $Revision$ +// + +using ICSharpCode.TextEditor; +using System; +using ICSharpCode.Core; +using NUnit.Framework; +using NUnit.Framework.SyntaxHelpers; +using VBNetBinding.FormattingStrategy; + +namespace VBNetBinding.Tests.FormattingStrategy +{ + [TestFixture] + public class IndentationTests + { + [Test] + public void SimpleInterfaceTest() + { + string code = @"Interface t +Sub Test() +Sub Test2() +End Interface"; + + string expectedCode = @"Interface t + Sub Test() + Sub Test2() +End Interface"; + + RunFormatTest(code, expectedCode); + } + + [Test] + public void ArrayInitializerTest() + { + string expected = @"Public Class Test + Private Sub Tester() + test(asdf, _ + asdf, _ + asdf, _ + asdf) + + Dim test As Integer() = { _ + 2,2,3,34,4,5 _ + } + End Sub +End Class"; + + string code = @"Public Class Test + Private Sub Tester() + test(asdf, _ + asdf, _ + asdf, _ + asdf) + + Dim test As Integer() = { _ + 2,2,3,34,4,5 _ + } + End Sub +End Class"; + + RunFormatTest(code, expected); + } + + void RunFormatTest(string code, string expectedCode) + { + using (TextEditorControl editor = new TextEditorControl()) { + editor.Document.TextContent = code; + VBFormattingStrategy formattingStrategy = new VBFormattingStrategy(); + formattingStrategy.IndentLines(editor.ActiveTextAreaControl.TextArea, 0, editor.Document.TotalNumberOfLines); + + Assert.AreEqual(expectedCode, editor.Document.TextContent); + } + } + + [TestFixtureSetUp] + public void Init() + { + if (!PropertyService.Initialized) { + PropertyService.InitializeService(String.Empty, String.Empty, "VBNetBindingTests"); + } + } + } +} diff --git a/src/AddIns/BackendBindings/VBNetBinding/Test/FormattingStrategy/InterfaceTests.cs b/src/AddIns/BackendBindings/VBNetBinding/Test/FormattingStrategy/InterfaceTests.cs index e3d3327718..3d07eb8acd 100644 --- a/src/AddIns/BackendBindings/VBNetBinding/Test/FormattingStrategy/InterfaceTests.cs +++ b/src/AddIns/BackendBindings/VBNetBinding/Test/FormattingStrategy/InterfaceTests.cs @@ -42,7 +42,7 @@ namespace VBNetBinding.Tests string expectedCode = "Public Interface Foo\r\n" + "\tPublic Sub Bar\r\n" + - "\t\t\r\n" + + "\t\r\n" + "End Interface"; using (TextEditorControl editor = new TextEditorControl()) { diff --git a/src/AddIns/BackendBindings/VBNetBinding/Test/VBNetBinding.Tests.csproj b/src/AddIns/BackendBindings/VBNetBinding/Test/VBNetBinding.Tests.csproj index 4c853ff983..5cdcc32a58 100644 --- a/src/AddIns/BackendBindings/VBNetBinding/Test/VBNetBinding.Tests.csproj +++ b/src/AddIns/BackendBindings/VBNetBinding/Test/VBNetBinding.Tests.csproj @@ -64,6 +64,7 @@ +