Browse Source

[Formatter] Worked on a indent engine.

newNRvisualizers
Mike Krüger 14 years ago
parent
commit
b609c8d6f7
  1. 324
      ICSharpCode.NRefactory.CSharp/Formatter/CSharpIndentEngine.cs
  2. 39
      ICSharpCode.NRefactory.CSharp/Formatter/Indent.cs
  3. 1
      ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj
  4. 1
      ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj
  5. 173
      ICSharpCode.NRefactory.Tests/IndentationTests/IndentationTests.cs

324
ICSharpCode.NRefactory.CSharp/Formatter/CSharpIndentEngine.cs

@ -0,0 +1,324 @@ @@ -0,0 +1,324 @@
using System;
using ICSharpCode.NRefactory.Editor;
using System.Text;
namespace ICSharpCode.NRefactory.CSharp
{
public class CSharpIndentEngine
{
readonly IDocument document;
readonly CSharpFormattingOptions options;
readonly TextEditorOptions textEditorOptions;
readonly StringBuilder wordBuf = new StringBuilder();
Indent thisLineindent;
Indent indent;
public string ThisLineIndent {
get {
return thisLineindent.IndentString;
}
}
public string NewLineIndent {
get {
return indent.IndentString;
}
}
public CSharpIndentEngine(IDocument document, TextEditorOptions textEditorOptions, CSharpFormattingOptions formattingOptions)
{
this.document = document;
this.options = formattingOptions;
this.textEditorOptions = textEditorOptions;
this.indent = new Indent(textEditorOptions);
this.thisLineindent = new Indent(textEditorOptions);
}
int offset;
void Reset()
{
offset = 0;
thisLineindent.Reset();
indent.Reset();
pc = '\0';
IsLineStart = true;
addContinuation = false;
inside = Inside.Empty;
nextBody = currentBody = Body.None;
}
public void UpdateToOffset (int toOffset)
{
if (toOffset < offset)
Reset();
for (int i = offset; i < toOffset; i++)
Push(document.GetCharAt(i));
}
Inside inside = Inside.Empty;
bool IsLineStart = true;
bool IsInStringOrChar {
get {
return inside.HasFlag (Inside.StringOrChar);
}
}
bool IsInComment {
get {
return inside.HasFlag (Inside.Comment);
}
}
[Flags]
public enum Inside {
Empty = 0,
PreProcessor = (1 << 0),
MultiLineComment = (1 << 1),
LineComment = (1 << 2),
DocComment = (1 << 11),
Comment = (MultiLineComment | LineComment | DocComment),
VerbatimString = (1 << 3),
StringLiteral = (1 << 4),
CharLiteral = (1 << 5),
String = (VerbatimString | StringLiteral),
StringOrChar = (String | CharLiteral),
Attribute = (1 << 6),
ParenList = (1 << 7),
FoldedStatement = (1 << 8),
Block = (1 << 9),
Case = (1 << 10),
FoldedOrBlock = (FoldedStatement | Block),
FoldedBlockOrCase = (FoldedStatement | Block | Case)
}
char pc;
int parens = 0;
void Push(char ch)
{
if (inside.HasFlag (Inside.VerbatimString) && pc == '"' && ch != '"') {
inside &= ~Inside.String;
}
Console.WriteLine(ch);
switch (ch) {
case '#':
if (IsLineStart)
inside = Inside.PreProcessor;
break;
case '/':
if (IsInStringOrChar)
break;
if (pc == '/') {
if (inside.HasFlag (Inside.Comment)) {
inside |= Inside.DocComment;
} else {
inside |= Inside.Comment;
}
}
break;
case '*':
if (IsInStringOrChar || IsInComment)
break;
if (pc == '/')
inside |= Inside.MultiLineComment;
break;
case '\n':
case '\r':
inside &= ~(Inside.Comment | Inside.String | Inside.CharLiteral | Inside.PreProcessor);
CheckKeyword(wordBuf.ToString());
wordBuf.Length = 0;
if (addContinuation) {
indent.Push (IndentType.Continuation);
}
thisLineindent = indent.Clone ();
addContinuation = false;
IsLineStart = true;
break;
case '"':
if (IsInComment)
break;
if (inside.HasFlag (Inside.String)) {
if (pc != '\\')
inside &= ~Inside.String;
break;
}
if (pc =='@') {
inside |= Inside.VerbatimString;
} else {
inside |= Inside.String;
}
break;
case '<':
case '[':
case '(':
if (IsInComment || IsInStringOrChar)
break;
parens++;
indent.Push (IndentType.Block);
break;
case '>':
case ']':
case ')':
if (IsInComment || IsInStringOrChar)
break;
parens--;
indent.Pop ();
break;
case '{':
if (IsInComment || IsInStringOrChar)
break;
currentBody = nextBody;
if (indent.Count > 0 && indent.Peek() == IndentType.Continuation)
indent.Pop();
addContinuation = false;
AddIndentation (currentBody);
break;
case '}':
if (IsInComment || IsInStringOrChar)
break;
indent.Pop ();
if (indent.Count > 0 && indent.Peek() == IndentType.Continuation)
indent.Pop();
break;
case ';':
if (indent.Count > 0 && indent.Peek() == IndentType.Continuation)
indent.Pop();
break;
case '\'':
if (IsInComment || inside.HasFlag (Inside.String))
break;
if (inside.HasFlag (Inside.CharLiteral)) {
if (pc != '\\')
inside &= ~Inside.CharLiteral;
} else {
inside &= Inside.CharLiteral;
}
break;
}
if (!IsInComment && !IsInStringOrChar) {
if ((wordBuf.Length == 0 ? char.IsLetter(ch) : char.IsLetterOrDigit(ch)) || ch == '_') {
wordBuf.Append(ch);
} else {
CheckKeyword(wordBuf.ToString());
wordBuf.Length = 0;
}
}
if (addContinuation) {
indent.Push (IndentType.Continuation);
addContinuation = false;
}
IsLineStart &= ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r';
pc = ch;
}
void AddIndentation(BraceStyle braceStyle)
{
switch (braceStyle) {
case BraceStyle.DoNotChange:
case BraceStyle.EndOfLine:
case BraceStyle.EndOfLineWithoutSpace:
case BraceStyle.NextLine:
case BraceStyle.NextLineShifted:
case BraceStyle.BannerStyle:
indent.Push (IndentType.Block);
break;
case BraceStyle.NextLineShifted2:
indent.Push (IndentType.DoubleBlock);
break;
}
}
void AddIndentation(Body body)
{
switch (body) {
case Body.None:
indent.Push (IndentType.Block);
break;
case Body.Namespace:
AddIndentation (options.NamespaceBraceStyle);
break;
case Body.Class:
AddIndentation (options.ClassBraceStyle);
break;
case Body.Struct:
AddIndentation (options.StructBraceStyle);
break;
case Body.Interface:
AddIndentation (options.InterfaceBraceStyle);
break;
case Body.Enum:
AddIndentation (options.EnumBraceStyle);
break;
case Body.Switch:
if (options.IndentSwitchBody)
indent.Push (IndentType.Empty);
break;
default:
throw new ArgumentOutOfRangeException();
}
}
enum Body
{
None,
Namespace,
Class, Struct, Interface, Enum,
Switch
}
Body currentBody;
Body nextBody;
bool addContinuation;
void CheckKeyword (string keyword)
{
switch (currentBody) {
case Body.None:
if (keyword == "namespace") {
nextBody = Body.Namespace;
return;
}
goto case Body.Namespace;
case Body.Namespace:
if (keyword == "class") {
nextBody = Body.Class;
return;
}
if (keyword == "enum") {
nextBody = Body.Enum;
return;
}
if (keyword == "struct") {
nextBody = Body.Struct;
return;
}
if (keyword == "interface") {
nextBody = Body.Interface;
return;
}
break;
case Body.Class:
case Body.Enum:
case Body.Struct:
case Body.Interface:
if (keyword == "switch")
nextBody = Body.Switch;
if (keyword == "do" || keyword == "if" || keyword == "for" || keyword == "foreach" || keyword == "while") {
addContinuation = true;
}
break;
}
}
}
}

39
ICSharpCode.NRefactory.CSharp/Formatter/Indent.cs

@ -30,8 +30,10 @@ namespace ICSharpCode.NRefactory.CSharp @@ -30,8 +30,10 @@ namespace ICSharpCode.NRefactory.CSharp
{
public enum IndentType {
Block,
DoubleBlock,
Continuation,
Label
Label,
Empty
}
public class Indent
@ -44,8 +46,28 @@ namespace ICSharpCode.NRefactory.CSharp @@ -44,8 +46,28 @@ namespace ICSharpCode.NRefactory.CSharp
public Indent(TextEditorOptions options)
{
this.options = options;
Reset();
}
Indent(TextEditorOptions options, Stack<IndentType> indentStack, int curIndent) : this(options)
{
this.indentStack = indentStack;
this.curIndent = curIndent;
}
public Indent Clone()
{
var result = new Indent(options, new Stack<IndentType> (indentStack), curIndent);
result.indentString = indentString;
return result;
}
public void Reset()
{
curIndent = 0;
indentString = "";
indentStack.Clear();
}
public void Push(IndentType type)
{
@ -60,15 +82,30 @@ namespace ICSharpCode.NRefactory.CSharp @@ -60,15 +82,30 @@ namespace ICSharpCode.NRefactory.CSharp
Update();
}
public int Count {
get {
return indentStack.Count;
}
}
public IndentType Peek ()
{
return indentStack.Peek();
}
int GetIndent(IndentType indentType)
{
switch (indentType) {
case IndentType.Block:
return options.IndentSize;
case IndentType.DoubleBlock:
return options.IndentSize * 2;
case IndentType.Continuation:
return options.ContinuationIndent;
case IndentType.Label:
return options.LabelIndent;
case IndentType.Empty:
return 0;
default:
throw new ArgumentOutOfRangeException();
}

1
ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj

@ -505,6 +505,7 @@ @@ -505,6 +505,7 @@
<Compile Include="Refactoring\CodeActions\ConvertToInitializer\AccessPath.cs" />
<Compile Include="Refactoring\LocalReferenceFinder.cs" />
<Compile Include="Refactoring\CodeActions\SortUsingsAction.cs" />
<Compile Include="Formatter\CSharpIndentEngine.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ICSharpCode.NRefactory\ICSharpCode.NRefactory.csproj">

1
ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj

@ -362,6 +362,7 @@ @@ -362,6 +362,7 @@
<Compile Include="CSharp\CodeIssues\RedundantWhereWithPredicateIssueTests.cs" />
<Compile Include="CSharp\CodeActions\SortUsingsTests.cs" />
<Compile Include="CSharp\CodeActions\CreateEventInvocatorTests.cs" />
<Compile Include="IndentationTests\IndentationTests.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Mono.Cecil\Mono.Cecil.csproj">

173
ICSharpCode.NRefactory.Tests/IndentationTests/IndentationTests.cs

@ -0,0 +1,173 @@ @@ -0,0 +1,173 @@
using System;
using System;
using System.IO;
using NUnit.Framework;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.Editor;
using System.Text;
namespace ICSharpCode.NRefactory.CSharp.Indentation
{
[TestFixture]
public class IndentationTests
{
static CSharpIndentEngine CreateEngine (string text)
{
var policy = FormattingOptionsFactory.CreateMono ();
var sb = new StringBuilder();
int offset = 0;
for (int i = 0; i < text.Length; i++) {
var ch = text [i];
if (ch == '$') {
offset = i;
continue;
}
sb.Append (ch);
}
var document = new ReadOnlyDocument(sb.ToString ());
var options = new TextEditorOptions();
var result = new CSharpIndentEngine(document, options, policy);
result.UpdateToOffset(offset);
return result;
}
[Test]
public void TestNamespaceIndent ()
{
var indent = CreateEngine("namespace Foo {$");
Assert.AreEqual("", indent.ThisLineIndent);
Assert.AreEqual("\t", indent.NewLineIndent);
}
[Ignore ("TODO")]
[Test]
public void TestPreProcessorDirectives ()
{
var indent = CreateEngine(@"
namespace Foo {
class Foo {
#if NOTTHERE
{
#endif
$");
Assert.AreEqual("\t\t", indent.ThisLineIndent);
Assert.AreEqual("\t\t", indent.NewLineIndent);
}
[Test]
public void TestIf ()
{
var indent = CreateEngine(@"
class Foo {
void Test ()
{
if (true)$");
Assert.AreEqual("\t\t", indent.ThisLineIndent);
Assert.AreEqual("\t\t\t", indent.NewLineIndent);
}
[Test]
public void TestFor ()
{
var indent = CreateEngine(@"
class Foo {
void Test ()
{
for (;;)$");
Assert.AreEqual("\t\t", indent.ThisLineIndent);
Assert.AreEqual("\t\t\t", indent.NewLineIndent);
}
[Test]
public void TestForEach ()
{
var indent = CreateEngine(@"
class Foo {
void Test ()
{
foreach (;;)$");
Assert.AreEqual("\t\t", indent.ThisLineIndent);
Assert.AreEqual("\t\t\t", indent.NewLineIndent);
}
[Test]
public void TestDo ()
{
var indent = CreateEngine(@"
class Foo {
void Test ()
{
do
$");
Assert.AreEqual("\t\t\t", indent.ThisLineIndent);
}
[Test]
public void TestNestedDo ()
{
var indent = CreateEngine(@"
class Foo {
void Test ()
{
do do
$");
Assert.AreEqual("\t\t\t\t", indent.ThisLineIndent);
}
[Test]
public void TestNestedDoContinuationSetBack ()
{
var indent = CreateEngine(@"
class Foo {
void Test ()
{
do do do
foo();$");
Assert.AreEqual("\t\t\t\t\t", indent.ThisLineIndent);
Assert.AreEqual("\t\t\t\t", indent.NewLineIndent);
}
[Test]
public void TestWhile ()
{
var indent = CreateEngine(@"
class Foo {
void Test ()
{
while(true)$");
Assert.AreEqual("\t\t", indent.ThisLineIndent);
Assert.AreEqual("\t\t\t", indent.NewLineIndent);
}
[Ignore ("TODO")]
[Test]
public void TestParameters ()
{
var indent = CreateEngine(@"
class Foo {
void Test ()
{
Foo(true,$");
Assert.AreEqual("\t\t", indent.ThisLineIndent);
Assert.AreEqual("\t\t ", indent.NewLineIndent);
}
[Test]
public void TestParametersCase2 ()
{
var indent = CreateEngine(@"
class Foo {
void Test ()
{
Foo($");
Assert.AreEqual("\t\t", indent.ThisLineIndent);
Assert.AreEqual("\t\t\t", indent.NewLineIndent);
}
}
}
Loading…
Cancel
Save