Browse Source

- fixed bugs in VB .NET Indentation

- added Unit Tests

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/branches/3.0@4040 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Siegfried Pammer 17 years ago
parent
commit
2a0842d436
  1. 218
      src/AddIns/BackendBindings/VBNetBinding/Project/Src/FormattingStrategy/VBNetFormattingStrategy.cs
  2. 87
      src/AddIns/BackendBindings/VBNetBinding/Test/FormattingStrategy/IndentationTests.cs
  3. 2
      src/AddIns/BackendBindings/VBNetBinding/Test/FormattingStrategy/InterfaceTests.cs
  4. 1
      src/AddIns/BackendBindings/VBNetBinding/Test/VBNetBinding.Tests.csproj

218
src/AddIns/BackendBindings/VBNetBinding/Project/Src/FormattingStrategy/VBNetFormattingStrategy.cs

@ -9,6 +9,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
@ -230,11 +231,12 @@ namespace VBNetBinding.FormattingStrategy
VBStatement statement = statement_; // allow passing statement byref VBStatement statement = statement_; // allow passing statement byref
if (Regex.IsMatch(texttoreplace.Trim(), statement.StartRegex, RegexOptions.IgnoreCase)) { if (Regex.IsMatch(texttoreplace.Trim(), statement.StartRegex, RegexOptions.IgnoreCase)) {
string indentation = GetIndentation(textArea, lineNr - 1); 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); textArea.Document.Replace(curLine.Offset, curLine.Length, terminator + indentation + statement.EndStatement);
} if (!IsInsideInterface(textArea, lineNr) || statement == interfaceStatement) {
for (int i = 0; i < statement.IndentPlus; i++) { for (int i = 0; i < statement.IndentPlus; i++) {
indentation += Tab.GetIndentationString(textArea.Document); indentation += Tab.GetIndentationString(textArea.Document);
}
} }
textArea.Document.Replace(curLine.Offset, curLine.Length, indentation + curLineText.Trim()); textArea.Document.Replace(curLine.Offset, curLine.Length, indentation + curLineText.Trim());
@ -242,17 +244,6 @@ namespace VBNetBinding.FormattingStrategy
return; 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)) if (IsInString(lineAboveText))
@ -283,8 +274,8 @@ namespace VBNetBinding.FormattingStrategy
textArea.Document.Replace(curLine.Offset, curLine.Length, newLineText); textArea.Document.Replace(curLine.Offset, curLine.Length, newLineText);
} }
if (IsElseConstruct(lineAboveText)) if (IsElseConstruct(lineAboveText))
SmartIndentLine(textArea, lineNr - 1); SmartIndentInternal(textArea, lineNr - 1, lineNr);
textArea.Caret.Column = indent.Length; textArea.Caret.Column = GetIndentation(textArea, lineNr).Length;
} }
} }
else if(ch == '>') else if(ch == '>')
@ -337,13 +328,53 @@ namespace VBNetBinding.FormattingStrategy
return (count > 0); return (count > 0);
} }
bool IsInsideInterface(TextArea area, int lineNr)
{
ILexer lexer = ParserFactory.CreateLexer(SupportedLanguage.VBNet, new StringReader(area.Document.TextContent));
Stack<Token> tokens = new Stack<Token>();
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) bool IsElseConstruct(string line)
{ {
string t = StripComment(line).ToLowerInvariant(); string t = StripComment(line);
if (t.StartsWith("case ")) return true; if (t.StartsWith("case ", StringComparison.OrdinalIgnoreCase)) return true;
if (t == "else" || t.StartsWith("elseif ")) return true; if (string.Compare(t, "else", true) == 0 ||
if (t == "catch" || t.StartsWith("catch ")) return true; t.StartsWith("elseif ", StringComparison.OrdinalIgnoreCase)) return true;
if (t == "finally") 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; return false;
} }
@ -374,7 +405,10 @@ namespace VBNetBinding.FormattingStrategy
bool IsDeclaration(int type) 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) bool IsEndStatementNeeded(TextArea textArea, ref VBStatement statement, int lineNr)
@ -410,9 +444,12 @@ namespace VBNetBinding.FormattingStrategy
prevToken = currentToken; 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; return GetClosestMissing(missingEnds, statement, textArea, lineNr) != null;
} else else
return false; return false;
} }
@ -471,8 +508,6 @@ namespace VBNetBinding.FormattingStrategy
bool IsMatchingStatement(Token token, VBStatement statement) bool IsMatchingStatement(Token token, VBStatement statement)
{ {
// funktioniert noch nicht!
if (token.Kind == Tokens.For && statement.EndStatement == "Next") if (token.Kind == Tokens.For && statement.EndStatement == "Next")
return true; return true;
@ -519,7 +554,9 @@ namespace VBNetBinding.FormattingStrategy
{ {
ILexer lexer = ParserFactory.CreateLexer(SupportedLanguage.VBNet, new StringReader(textArea.Document.TextContent)); ILexer lexer = ParserFactory.CreateLexer(SupportedLanguage.VBNet, new StringReader(textArea.Document.TextContent));
int indentation = 0; Stack<string> indentation = new Stack<string>();
indentation.Push(string.Empty);
int oldLine = 0; int oldLine = 0;
@ -548,38 +585,38 @@ namespace VBNetBinding.FormattingStrategy
isDelegate = isDeclare = isMustOverride = false; isDelegate = isDeclare = isMustOverride = false;
if (IsSpecialCase(currentToken, prevToken)) { if (IsSpecialCase(currentToken, prevToken)) {
ApplyToRange(textArea, ref indentation, oldLine, currentToken.Location.Line - 1, begin, end); ApplyToRange(textArea, indentation, oldLine, currentToken.Location.Line, begin, end);
indentation--; Unindent(indentation);
ApplyToRange(textArea, ref indentation, currentToken.Location.Line - 1, currentToken.Location.Line, begin, end); ApplyToRange(textArea, indentation, currentToken.Location.Line - 1, currentToken.Location.Line, begin, end);
indentation++; Indent(textArea, indentation);
oldLine = currentToken.Location.Line; oldLine = currentToken.Location.Line;
} }
if (IsBlockEnd(currentToken, prevToken)) { 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) if (currentToken.Kind == Tokens.Interface)
inInterface = false; inInterface = false;
if (!inInterface && !isMustOverride && !isDeclare && !isDelegate) { if (!inInterface && !isMustOverride && !isDeclare && !isDelegate) {
indentation--; Unindent(indentation);
if (currentToken.Kind == Tokens.Select) if (currentToken.Kind == Tokens.Select)
indentation--; Unindent(indentation);
} }
oldLine = currentToken.Location.Line - 1; oldLine = currentToken.Location.Line - 1;
} }
if (IsBlockStart(lexer, currentToken, prevToken)) { 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) { if (!inInterface && !isMustOverride && !isDeclare && !isDelegate) {
indentation++; Indent(textArea, indentation);
if (currentToken.Kind == Tokens.Select) if (currentToken.Kind == Tokens.Select)
indentation++; Indent(textArea, indentation);
} }
if (currentToken.Kind == Tokens.Interface) if (currentToken.Kind == Tokens.Interface)
@ -592,9 +629,29 @@ namespace VBNetBinding.FormattingStrategy
} }
// do last indent step // 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<string> indentation)
{
indentation.Pop();
}
void Indent(TextArea textArea, Stack<string> 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) bool IsBlockStart(ILexer lexer, Token current, Token prev)
@ -609,12 +666,8 @@ namespace VBNetBinding.FormattingStrategy
Token currentToken = null; Token currentToken = null;
while ((currentToken = lexer.Peek()).Kind != Tokens.EOL) { while ((currentToken = lexer.Peek()).Kind != Tokens.EOL) {
if (currentToken.Kind == Tokens.Then) { if (currentToken.Kind == Tokens.Then)
if (lexer.Peek().Kind == Tokens.EOL) return lexer.Peek().Kind == Tokens.EOL;
return true;
else
return false;
}
} }
} }
@ -695,10 +748,7 @@ namespace VBNetBinding.FormattingStrategy
case Tokens.Else: case Tokens.Else:
return true; return true;
case Tokens.Case: case Tokens.Case:
if (prev.Kind == Tokens.Select) return prev.Kind != Tokens.Select;
return false;
else
return true;
case Tokens.ElseIf: case Tokens.ElseIf:
return true; return true;
case Tokens.Catch: case Tokens.Catch:
@ -710,74 +760,38 @@ namespace VBNetBinding.FormattingStrategy
return false; return false;
} }
bool multiLine = false; void ApplyToRange(TextArea textArea, Stack<string> indentation, int begin, int end, int selBegin, int selEnd)
bool otherMultiLine = false;
void ApplyToRange(TextArea textArea, ref int indentation, int begin, int end, int selBegin, int selEnd)
{ {
bool useSpaces = textArea.TextEditorProperties.ConvertTabsToSpaces; bool multiLine = false;
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);
}
for (int i = begin; i < end; i++) { for (int i = begin; i < end; i++) {
LineSegment curLine = textArea.Document.GetLineSegment(i); LineSegment curLine = textArea.Document.GetLineSegment(i);
string lineText = textArea.Document.GetText(curLine).Trim(' ', '\t', '\r', '\n'); string lineText = textArea.Document.GetText(curLine).Trim(' ', '\t', '\r', '\n');
string noComments = StripComment(lineText).TrimEnd(' ', '\t', '\r', '\n'); string noComments = StripComment(lineText).TrimEnd(' ', '\t', '\r', '\n');
if (i < selBegin || i > selEnd) {
if (otherMultiLine && noComments == "}") { indentation.Pop();
indentation--; indentation.Push(GetIndentation(textArea, i));
otherMultiLine = false;
} }
spaces = indentationSize * (indentation < 0 ? 0 : indentation); // change indentation before (indent this line)
tabs = 0; if (multiLine && noComments.EndsWith("}")) {
Unindent(indentation);
if (!useSpaces) { multiLine = false;
tabs = (int)(spaces / tabSize);
spaces %= tabSize;
} }
string newLine = new string('\t', tabs) + new string(' ', spaces) + lineText; SmartReplaceLine(textArea.Document, curLine, indentation.Peek() + lineText);
if (noComments.EndsWith("_") && !IsStatement(noComments) && !otherMultiLine) { // change indentation afterwards (indent next line)
otherMultiLine = true; if (!multiLine && noComments.EndsWith("_")) {
indentation++; Indent(textArea, indentation);
}
if (noComments.EndsWith("_") && IsStatement(noComments)) {
multiLine = true; multiLine = true;
indentation++;
} }
if (!noComments.EndsWith("_") && multiLine) { if (multiLine && !noComments.EndsWith("_")) {
indentation--;
multiLine = false; multiLine = false;
Unindent(indentation);
} }
if (!noComments.EndsWith("_") && otherMultiLine) {
indentation--;
otherMultiLine = false;
}
if (i >= selBegin && i <= selEnd)
SmartReplaceLine(textArea.Document, curLine, newLine);
LoggingService.Debug("'" + newLine + "'");
} }
} }

87
src/AddIns/BackendBindings/VBNetBinding/Test/FormattingStrategy/IndentationTests.cs

@ -0,0 +1,87 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Siegfried Pammer" email="sie_pam@gmx.at"/>
// <version>$Revision$</version>
// </file>
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");
}
}
}
}

2
src/AddIns/BackendBindings/VBNetBinding/Test/FormattingStrategy/InterfaceTests.cs

@ -42,7 +42,7 @@ namespace VBNetBinding.Tests
string expectedCode = "Public Interface Foo\r\n" + string expectedCode = "Public Interface Foo\r\n" +
"\tPublic Sub Bar\r\n" + "\tPublic Sub Bar\r\n" +
"\t\t\r\n" + "\t\r\n" +
"End Interface"; "End Interface";
using (TextEditorControl editor = new TextEditorControl()) { using (TextEditorControl editor = new TextEditorControl()) {

1
src/AddIns/BackendBindings/VBNetBinding/Test/VBNetBinding.Tests.csproj

@ -64,6 +64,7 @@
<Folder Include="FormattingStrategy" /> <Folder Include="FormattingStrategy" />
<Compile Include="FormattingStrategy\EndOperatorTests.cs" /> <Compile Include="FormattingStrategy\EndOperatorTests.cs" />
<Compile Include="FormattingStrategy\EndSubTests.cs" /> <Compile Include="FormattingStrategy\EndSubTests.cs" />
<Compile Include="FormattingStrategy\IndentationTests.cs" />
<Compile Include="FormattingStrategy\InterfaceTests.cs" /> <Compile Include="FormattingStrategy\InterfaceTests.cs" />
<None Include="app.config" /> <None Include="app.config" />
</ItemGroup> </ItemGroup>

Loading…
Cancel
Save