5 changed files with 537 additions and 1 deletions
@ -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; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@ -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…
Reference in new issue