diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/FormattingStrategy/Indentation.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/FormattingStrategy/Indentation.cs index 5c9d4b8823..1d4566bdf8 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/FormattingStrategy/Indentation.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/FormattingStrategy/Indentation.cs @@ -20,14 +20,63 @@ namespace CSharpBinding.FormattingStrategy public sealed class IndentationReformatter { + /// + /// An indentation block. Tracks the state of the indentation. + /// public struct Block { + /// + /// The indentation outside of the block. + /// public string OuterIndent; + + /// + /// The indentation inside the block. + /// public string InnerIndent; + + /// + /// The last word that was seen inside this block. + /// Because parenthesis open a sub-block and thus don't change their parent's LastWord, + /// this property can be used to identify the type of block statement (if, while, switch) + /// at the position of the '{'. + /// public string LastWord; + + /// + /// The type of bracket that opened this block (, [ or { + /// public char Bracket; + + /// + /// Gets whether there's currently a line continuation going on inside this block. + /// public bool Continuation; - public bool OneLineBlock; + + /// + /// Gets whether there's currently a 'one-line-block' going on. 'one-line-blocks' occur + /// with if statements that don't use '{}'. They are not represented by a Block instance on + /// the stack, but are instead handled similar to line continuations. + /// This property is an integer because there might be multiple nested one-line-blocks. + /// As soon as there is a finished statement, OneLineBlock is reset to 0. + /// + public int OneLineBlock; + + /// + /// The previous value of one-line-block before it was reset. + /// Used to restore the indentation of 'else' to the correct level. + /// + public int PreviousOneLineBlock; + + public void ResetOneLineBlock() + { + PreviousOneLineBlock = OneLineBlock; + OneLineBlock = 0; + } + + /// + /// Gets the line number where this block started. + /// public int StartLine; public void Indent(IndentationSettings set) @@ -40,9 +89,15 @@ namespace CSharpBinding.FormattingStrategy OuterIndent = InnerIndent; InnerIndent += str; Continuation = false; - OneLineBlock = false; + ResetOneLineBlock(); LastWord = ""; } + + public override string ToString() + { + return string.Format("[Block StartLine={0}, LastWord='{1}', Continuation={2}, OneLineBlock={3}, PreviousOneLineBlock={4}]", + this.StartLine, this.LastWord, this.Continuation, this.OneLineBlock, this.PreviousOneLineBlock); + } } StringBuilder wordBuilder; @@ -78,7 +133,8 @@ namespace CSharpBinding.FormattingStrategy block.Bracket = '{'; block.Continuation = false; block.LastWord = ""; - block.OneLineBlock = false; + block.OneLineBlock = 0; + block.PreviousOneLineBlock = 0; block.StartLine = 0; inString = false; @@ -104,8 +160,7 @@ namespace CSharpBinding.FormattingStrategy if (blockComment || (inString && verbatim)) return; indent.Append(block.InnerIndent); - if (block.OneLineBlock) - indent.Append(set.IndentString); + indent.Append(Repeat(set.IndentString, block.OneLineBlock)); if (block.Continuation) indent.Append(set.IndentString); if (doc.Text != indent.ToString()) @@ -212,7 +267,7 @@ namespace CSharpBinding.FormattingStrategy #region Push/Pop the blocks switch (c) { case '{': - block.OneLineBlock = false; + block.ResetOneLineBlock(); blocks.Push(block); block.StartLine = doc.LineNumber; if (block.LastWord == "switch") { @@ -242,7 +297,7 @@ namespace CSharpBinding.FormattingStrategy if (blocks.Count == 0) break; block = blocks.Pop(); block.Continuation = false; - block.OneLineBlock = false; + block.ResetOneLineBlock(); break; case '(': case '[': @@ -252,7 +307,7 @@ namespace CSharpBinding.FormattingStrategy else block.StartLine = doc.LineNumber; block.Indent(set, - (oldBlock.OneLineBlock ? set.IndentString : "") + + Repeat(set.IndentString, oldBlock.OneLineBlock) + (oldBlock.Continuation ? set.IndentString : "") + (i == line.Length - 1 ? set.IndentString : new String(' ', i + 1))); block.Bracket = c; @@ -273,12 +328,12 @@ namespace CSharpBinding.FormattingStrategy case ';': case ',': block.Continuation = false; - block.OneLineBlock = false; + block.ResetOneLineBlock(); break; case ':': if (block.LastWord == "case" || line.StartsWith("case ") || line.StartsWith(block.LastWord + ":")) { block.Continuation = false; - block.OneLineBlock = false; + block.ResetOneLineBlock(); } break; } @@ -302,7 +357,7 @@ namespace CSharpBinding.FormattingStrategy if (line[0] == '}') { indent.Append(oldBlock.OuterIndent); - oldBlock.OneLineBlock = false; + oldBlock.ResetOneLineBlock(); oldBlock.Continuation = false; } else { indent.Append(oldBlock.InnerIndent); @@ -321,18 +376,19 @@ namespace CSharpBinding.FormattingStrategy indent.Remove(indent.Length - set.IndentString.Length, set.IndentString.Length); } else if (lastRealChar == ')') { if (IsSingleStatementKeyword(block.LastWord)) { - block.OneLineBlock = true; + block.OneLineBlock++; } } else if (lastRealChar == 'e' && block.LastWord == "else") { - block.OneLineBlock = true; + block.OneLineBlock = Math.Max(1, block.PreviousOneLineBlock); block.Continuation = false; + oldBlock.OneLineBlock = block.OneLineBlock - 1; } if (doc.ReadOnly) { // We can't change the current line, but we should accept the existing // indentation if possible (=if the current statement is not a multiline // statement). - if (!oldBlock.Continuation && !oldBlock.OneLineBlock && + if (!oldBlock.Continuation && oldBlock.OneLineBlock == 0 && oldBlock.StartLine == block.StartLine && block.StartLine < doc.LineNumber && lastRealChar != ':') { @@ -357,8 +413,7 @@ namespace CSharpBinding.FormattingStrategy if (line[0] != '{') { if (line[0] != ')' && oldBlock.Continuation && oldBlock.Bracket == '{') indent.Append(set.IndentString); - if (oldBlock.OneLineBlock) - indent.Append(set.IndentString); + indent.Append(Repeat(set.IndentString, oldBlock.OneLineBlock)); } // this is only for blockcomment lines starting with *, @@ -374,7 +429,20 @@ namespace CSharpBinding.FormattingStrategy } } - bool IsSingleStatementKeyword(string keyword) { + static string Repeat(string text, int count) + { + if (count == 0) + return string.Empty; + if (count == 1) + return text; + StringBuilder b = new StringBuilder(text.Length * count); + for (int i = 0; i < count; i++) + b.Append(text); + return b.ToString(); + } + + bool IsSingleStatementKeyword(string keyword) + { switch (keyword) { case "if": case "for":