// // AstFormattingVisitor.cs // // Author: // Mike Krüger // // Copyright (c) 2010 Novell, Inc (http://www.novell.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. using System; using System.Diagnostics; using System.Text; using System.Collections.Generic; using System.Linq; using ICSharpCode.NRefactory.Editor; using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.NRefactory.CSharp.Refactoring; namespace ICSharpCode.NRefactory.CSharp { public enum FormattingMode { OnTheFly, Intrusive } public class AstFormattingVisitor : DepthFirstAstVisitor { sealed class TextReplaceAction { internal readonly int Offset; internal readonly int RemovalLength; internal readonly string NewText; internal TextReplaceAction DependsOn; #if DEBUG internal readonly string StackTrace; #endif public TextReplaceAction (int offset, int removalLength, string newText) { this.Offset = offset; this.RemovalLength = removalLength; this.NewText = newText ?? string.Empty; #if DEBUG this.StackTrace = Environment.StackTrace; #endif } public override bool Equals(object obj) { TextReplaceAction other = obj as TextReplaceAction; if (other == null) { return false; } return this.Offset == other.Offset && this.RemovalLength == other.RemovalLength && this.NewText == other.NewText; } public override int GetHashCode() { return 0; } public override string ToString() { return string.Format("[TextReplaceAction: Offset={0}, RemovalLength={1}, NewText={2}]", Offset, RemovalLength, NewText); } } CSharpFormattingOptions policy; IDocument document; List changes = new List (); Indent curIndent; readonly TextEditorOptions options; public FormattingMode FormattingMode { get; set; } public bool HadErrors { get; set; } public AstFormattingVisitor(CSharpFormattingOptions policy, IDocument document, TextEditorOptions options = null) { if (policy == null) { throw new ArgumentNullException("policy"); } if (document == null) { throw new ArgumentNullException("document"); } this.policy = policy; this.document = document; this.options = options ?? TextEditorOptions.Default; curIndent = new Indent(this.options); } /// /// Applies the changes to the input document. /// public void ApplyChanges() { ApplyChanges(0, document.TextLength, document.Replace, (o, l, v) => document.GetText(o, l) == v); } public void ApplyChanges(int startOffset, int length) { ApplyChanges(startOffset, length, document.Replace, (o, l, v) => document.GetText(o, l) == v); } /// /// Applies the changes to the given Script instance. /// public void ApplyChanges(Script script) { ApplyChanges(0, document.TextLength, script.Replace); } public void ApplyChanges(int startOffset, int length, Script script) { ApplyChanges(startOffset, length, script.Replace); } public void ApplyChanges(int startOffset, int length, Action documentReplace, Func filter = null) { int endOffset = startOffset + length; TextReplaceAction previousChange = null; int delta = 0; var depChanges = new List (); foreach (var change in changes.OrderBy(c => c.Offset)) { if (previousChange != null) { if (change.Equals(previousChange)) { // ignore duplicate changes continue; } if (change.Offset < previousChange.Offset + previousChange.RemovalLength) { #if DEBUG Console.WriteLine ("change 1:" + change); Console.WriteLine (change.StackTrace); Console.WriteLine ("change 2:" + change); Console.WriteLine (previousChange.StackTrace); #endif throw new InvalidOperationException ("Detected overlapping changes " + change + "/" + previousChange); } } previousChange = change; bool skipChange = change.Offset < startOffset || change.Offset > endOffset; skipChange |= filter != null && filter(change.Offset + delta, change.RemovalLength, change.NewText); skipChange &= !depChanges.Contains(change); if (!skipChange) { documentReplace(change.Offset + delta, change.RemovalLength, change.NewText); delta += change.NewText.Length - change.RemovalLength; if (change.DependsOn != null) { depChanges.Add(change.DependsOn); } } } changes.Clear(); } public override void VisitCompilationUnit(CompilationUnit unit) { base.VisitCompilationUnit(unit); } public void EnsureBlankLinesAfter(AstNode node, int blankLines) { if (FormattingMode != FormattingMode.Intrusive) return; var loc = node.EndLocation; int line = loc.Line; do { line++; } while (line < document.LineCount && IsSpacing(document.GetLineByNumber(line))); var start = document.GetOffset(node.EndLocation); int foundBlankLines = line - loc.Line - 1; StringBuilder sb = new StringBuilder(); for (int i = 0; i < blankLines - foundBlankLines; i++) { sb.Append(this.options.EolMarker); } int ws = start; while (ws < document.TextLength && IsSpacing (document.GetCharAt (ws))) { ws++; } int removedChars = ws - start; if (foundBlankLines > blankLines) { removedChars += document.GetLineByNumber(loc.Line + foundBlankLines - blankLines).EndOffset - document.GetLineByNumber(loc.Line).EndOffset; } AddChange(start, removedChars, sb.ToString()); } public void EnsureBlankLinesBefore(AstNode node, int blankLines) { if (FormattingMode != FormattingMode.Intrusive) return; var loc = node.StartLocation; int line = loc.Line; do { line--; } while (line > 0 && IsSpacing(document.GetLineByNumber(line))); int end = document.GetOffset(loc.Line, 1); int start = document.GetOffset(line + 1, 1); StringBuilder sb = new StringBuilder (); for (int i = 0; i < blankLines; i++) { sb.Append(this.options.EolMarker); } AddChange(start, end - start, sb.ToString()); } public override void VisitUsingDeclaration(UsingDeclaration usingDeclaration) { if (!(usingDeclaration.PrevSibling is UsingDeclaration || usingDeclaration.PrevSibling is UsingAliasDeclaration)) { EnsureBlankLinesBefore(usingDeclaration, policy.BlankLinesBeforeUsings); } if (!(usingDeclaration.NextSibling is UsingDeclaration || usingDeclaration.NextSibling is UsingAliasDeclaration)) { EnsureBlankLinesAfter(usingDeclaration, policy.BlankLinesAfterUsings); } } public override void VisitUsingAliasDeclaration(UsingAliasDeclaration usingDeclaration) { if (!(usingDeclaration.PrevSibling is UsingDeclaration || usingDeclaration.PrevSibling is UsingAliasDeclaration)) { EnsureBlankLinesBefore(usingDeclaration, policy.BlankLinesBeforeUsings); } if (!(usingDeclaration.NextSibling is UsingDeclaration || usingDeclaration.NextSibling is UsingAliasDeclaration)) { EnsureBlankLinesAfter(usingDeclaration, policy.BlankLinesAfterUsings); } } public override void VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration) { var firstNsMember = namespaceDeclaration.Members.FirstOrDefault(); if (firstNsMember != null) { EnsureBlankLinesBefore(firstNsMember, policy.BlankLinesBeforeFirstDeclaration); } FixIndentationForceNewLine(namespaceDeclaration.StartLocation); EnforceBraceStyle(policy.NamespaceBraceStyle, namespaceDeclaration.LBraceToken, namespaceDeclaration.RBraceToken); if (policy.IndentNamespaceBody) { curIndent.Push(IndentType.Block); } base.VisitNamespaceDeclaration(namespaceDeclaration); if (policy.IndentNamespaceBody) { curIndent.Pop (); } FixIndentation(namespaceDeclaration.RBraceToken.StartLocation); } public override void VisitTypeDeclaration(TypeDeclaration typeDeclaration) { FormatAttributedNode(typeDeclaration); BraceStyle braceStyle; bool indentBody = false; switch (typeDeclaration.ClassType) { case ClassType.Class: braceStyle = policy.ClassBraceStyle; indentBody = policy.IndentClassBody; break; case ClassType.Struct: braceStyle = policy.StructBraceStyle; indentBody = policy.IndentStructBody; break; case ClassType.Interface: braceStyle = policy.InterfaceBraceStyle; indentBody = policy.IndentInterfaceBody; break; case ClassType.Enum: braceStyle = policy.EnumBraceStyle; indentBody = policy.IndentEnumBody; break; default: throw new InvalidOperationException("unsupported class type : " + typeDeclaration.ClassType); } EnforceBraceStyle(braceStyle, typeDeclaration.LBraceToken, typeDeclaration.RBraceToken); if (indentBody) { curIndent.Push(IndentType.Block); } base.VisitTypeDeclaration(typeDeclaration); if (indentBody) { curIndent.Pop (); } if (typeDeclaration.NextSibling is TypeDeclaration || typeDeclaration.NextSibling is DelegateDeclaration) { EnsureBlankLinesAfter(typeDeclaration, policy.BlankLinesBetweenTypes); } } bool IsSimpleAccessor(Accessor accessor) { if (accessor.IsNull || accessor.Body.IsNull || accessor.Body.FirstChild == null) { return true; } if (accessor.Body.Statements.Count() != 1) { return false; } return !(accessor.Body.Statements.FirstOrDefault() is BlockStatement); } bool IsSpacing(char ch) { return ch == ' ' || ch == '\t'; } bool IsSpacing(ISegment segment) { int endOffset = segment.EndOffset; for (int i = segment.Offset; i < endOffset; i++) { if (!IsSpacing(document.GetCharAt(i))) { return false; } } return true; } int SearchLastNonWsChar(int startOffset, int endOffset) { startOffset = System.Math.Max(0, startOffset); endOffset = System.Math.Max(startOffset, endOffset); if (startOffset >= endOffset) { return startOffset; } int result = -1; bool inComment = false; for (int i = startOffset; i < endOffset && i < document.TextLength; i++) { char ch = document.GetCharAt(i); if (IsSpacing(ch)) { continue; } if (ch == '/' && i + 1 < document.TextLength && document.GetCharAt(i + 1) == '/') { return result; } if (ch == '/' && i + 1 < document.TextLength && document.GetCharAt(i + 1) == '*') { inComment = true; i++; continue; } if (inComment && ch == '*' && i + 1 < document.TextLength && document.GetCharAt(i + 1) == '/') { inComment = false; i++; continue; } if (!inComment) { result = i; } } return result; } void ForceSpace(int startOffset, int endOffset, bool forceSpace) { int lastNonWs = SearchLastNonWsChar(startOffset, endOffset); AddChange(lastNonWs + 1, System.Math.Max(0, endOffset - lastNonWs - 1), forceSpace ? " " : ""); } // void ForceSpacesAfter (AstNode n, bool forceSpaces) // { // if (n == null) // return; // AstLocation location = n.EndLocation; // int offset = data.LocationToOffset (location.Line, location.Column); // int i = offset; // while (i < data.Length && IsSpacing (data.GetCharAt (i))) { // i++; // } // ForceSpace (offset - 1, i, forceSpaces); // } void ForceSpacesAfter(AstNode n, bool forceSpaces) { if (n == null) { return; } TextLocation location = n.EndLocation; int offset = document.GetOffset(location); if (location.Column > document.GetLineByNumber(location.Line).Length) { return; } int i = offset; while (i < document.TextLength && IsSpacing (document.GetCharAt (i))) { i++; } ForceSpace(offset - 1, i, forceSpaces); } // int ForceSpacesBefore (AstNode n, bool forceSpaces) // { // if (n == null || n.IsNull) // return 0; // AstLocation location = n.StartLocation; // // int offset = data.LocationToOffset (location.Line, location.Column); // int i = offset - 1; // // while (i >= 0 && IsSpacing (data.GetCharAt (i))) { // i--; // } // ForceSpace (i, offset, forceSpaces); // return i; // } int ForceSpacesBefore(AstNode n, bool forceSpaces) { if (n == null || n.IsNull) { return 0; } TextLocation location = n.StartLocation; // respect manual line breaks. if (location.Column <= 1 || GetIndentation(location.Line).Length == location.Column - 1) { return 0; } int offset = document.GetOffset(location); int i = offset - 1; while (i >= 0 && IsSpacing (document.GetCharAt (i))) { i--; } ForceSpace(i, offset, forceSpaces); return i; } int ForceSpacesBeforeRemoveNewLines(AstNode n, bool forceSpace = true) { if (n == null || n.IsNull) { return 0; } int offset = document.GetOffset(n.StartLocation); int i = offset - 1; while (i >= 0) { char ch = document.GetCharAt(i); if (!IsSpacing(ch) && ch != '\r' && ch != '\n') break; i--; } var length = System.Math.Max(0, (offset - 1) - i); AddChange(i + 1, length, forceSpace ? " " : ""); return i; } public override void VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) { FormatAttributedNode(propertyDeclaration); bool oneLine = false; switch (policy.PropertyFormatting) { case PropertyFormatting.AllowOneLine: bool isSimple = IsSimpleAccessor(propertyDeclaration.Getter) && IsSimpleAccessor(propertyDeclaration.Setter); if (!isSimple || propertyDeclaration.LBraceToken.StartLocation.Line != propertyDeclaration.RBraceToken.StartLocation.Line) { EnforceBraceStyle(policy.PropertyBraceStyle, propertyDeclaration.LBraceToken, propertyDeclaration.RBraceToken); } else { ForceSpacesBefore(propertyDeclaration.Getter, true); ForceSpacesBefore(propertyDeclaration.Setter, true); ForceSpacesBefore(propertyDeclaration.RBraceToken, true); oneLine = true; } break; case PropertyFormatting.ForceNewLine: EnforceBraceStyle(policy.PropertyBraceStyle, propertyDeclaration.LBraceToken, propertyDeclaration.RBraceToken); break; case PropertyFormatting.ForceOneLine: isSimple = IsSimpleAccessor(propertyDeclaration.Getter) && IsSimpleAccessor(propertyDeclaration.Setter); if (isSimple) { int offset = this.document.GetOffset(propertyDeclaration.LBraceToken.StartLocation); int start = SearchWhitespaceStart(offset); int end = SearchWhitespaceEnd(offset); AddChange(start, offset - start, " "); AddChange(offset + 1, end - offset - 2, " "); offset = this.document.GetOffset(propertyDeclaration.RBraceToken.StartLocation); start = SearchWhitespaceStart(offset); AddChange(start, offset - start, " "); oneLine = true; } else { EnforceBraceStyle(policy.PropertyBraceStyle, propertyDeclaration.LBraceToken, propertyDeclaration.RBraceToken); } break; } if (policy.IndentPropertyBody) { curIndent.Push(IndentType.Block); } ///System.Console.WriteLine ("one line: " + oneLine); if (!propertyDeclaration.Getter.IsNull) { if (!oneLine) { if (!IsLineIsEmptyUpToEol(propertyDeclaration.Getter.StartLocation)) { int offset = this.document.GetOffset(propertyDeclaration.Getter.StartLocation); int start = SearchWhitespaceStart(offset); string indentString = this.curIndent.IndentString; AddChange(start, offset - start, this.options.EolMarker + indentString); } else { FixIndentation(propertyDeclaration.Getter.StartLocation); } } else { int offset = this.document.GetOffset(propertyDeclaration.Getter.StartLocation); int start = SearchWhitespaceStart(offset); AddChange(start, offset - start, " "); ForceSpacesBefore(propertyDeclaration.Getter.Body.LBraceToken, true); ForceSpacesBefore(propertyDeclaration.Getter.Body.RBraceToken, true); } if (!propertyDeclaration.Getter.Body.IsNull) { if (!policy.AllowPropertyGetBlockInline || propertyDeclaration.Getter.Body.LBraceToken.StartLocation.Line != propertyDeclaration.Getter.Body.RBraceToken.StartLocation.Line) { EnforceBraceStyle(policy.PropertyGetBraceStyle, propertyDeclaration.Getter.Body.LBraceToken, propertyDeclaration.Getter.Body.RBraceToken); } else { nextStatementIndent = " "; } VisitBlockWithoutFixingBraces(propertyDeclaration.Getter.Body, policy.IndentBlocks); } } if (!propertyDeclaration.Setter.IsNull) { if (!oneLine) { if (!IsLineIsEmptyUpToEol(propertyDeclaration.Setter.StartLocation)) { int offset = this.document.GetOffset(propertyDeclaration.Setter.StartLocation); int start = SearchWhitespaceStart(offset); string indentString = this.curIndent.IndentString; AddChange(start, offset - start, this.options.EolMarker + indentString); } else { FixIndentation(propertyDeclaration.Setter.StartLocation); } } else { int offset = this.document.GetOffset(propertyDeclaration.Setter.StartLocation); int start = SearchWhitespaceStart(offset); AddChange(start, offset - start, " "); ForceSpacesBefore(propertyDeclaration.Setter.Body.LBraceToken, true); ForceSpacesBefore(propertyDeclaration.Setter.Body.RBraceToken, true); } if (!propertyDeclaration.Setter.Body.IsNull) { if (!policy.AllowPropertySetBlockInline || propertyDeclaration.Setter.Body.LBraceToken.StartLocation.Line != propertyDeclaration.Setter.Body.RBraceToken.StartLocation.Line) { EnforceBraceStyle(policy.PropertySetBraceStyle, propertyDeclaration.Setter.Body.LBraceToken, propertyDeclaration.Setter.Body.RBraceToken); } else { nextStatementIndent = " "; } VisitBlockWithoutFixingBraces(propertyDeclaration.Setter.Body, policy.IndentBlocks); } } if (policy.IndentPropertyBody) { curIndent.Pop (); } if (IsMember(propertyDeclaration.NextSibling)) { EnsureBlankLinesAfter(propertyDeclaration, policy.BlankLinesBetweenMembers); } } public override void VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration) { ForceSpacesBefore(indexerDeclaration.LBracketToken, policy.SpaceBeforeIndexerDeclarationBracket); ForceSpacesAfter(indexerDeclaration.LBracketToken, policy.SpaceWithinIndexerDeclarationBracket); ForceSpacesBefore(indexerDeclaration.RBracketToken, policy.SpaceWithinIndexerDeclarationBracket); FormatCommas(indexerDeclaration, policy.SpaceBeforeIndexerDeclarationParameterComma, policy.SpaceAfterIndexerDeclarationParameterComma); FormatAttributedNode(indexerDeclaration); EnforceBraceStyle(policy.PropertyBraceStyle, indexerDeclaration.LBraceToken, indexerDeclaration.RBraceToken); if (policy.IndentPropertyBody) { curIndent.Push(IndentType.Block); } if (!indexerDeclaration.Getter.IsNull) { FixIndentation(indexerDeclaration.Getter.StartLocation); if (!indexerDeclaration.Getter.Body.IsNull) { if (!policy.AllowPropertyGetBlockInline || indexerDeclaration.Getter.Body.LBraceToken.StartLocation.Line != indexerDeclaration.Getter.Body.RBraceToken.StartLocation.Line) { EnforceBraceStyle(policy.PropertyGetBraceStyle, indexerDeclaration.Getter.Body.LBraceToken, indexerDeclaration.Getter.Body.RBraceToken); } else { nextStatementIndent = " "; } VisitBlockWithoutFixingBraces(indexerDeclaration.Getter.Body, policy.IndentBlocks); } } if (!indexerDeclaration.Setter.IsNull) { FixIndentation(indexerDeclaration.Setter.StartLocation); if (!indexerDeclaration.Setter.Body.IsNull) { if (!policy.AllowPropertySetBlockInline || indexerDeclaration.Setter.Body.LBraceToken.StartLocation.Line != indexerDeclaration.Setter.Body.RBraceToken.StartLocation.Line) { EnforceBraceStyle(policy.PropertySetBraceStyle, indexerDeclaration.Setter.Body.LBraceToken, indexerDeclaration.Setter.Body.RBraceToken); } else { nextStatementIndent = " "; } VisitBlockWithoutFixingBraces(indexerDeclaration.Setter.Body, policy.IndentBlocks); } } if (policy.IndentPropertyBody) { curIndent.Pop (); } if (IsMember(indexerDeclaration.NextSibling)) { EnsureBlankLinesAfter(indexerDeclaration, policy.BlankLinesBetweenMembers); } } static bool IsSimpleEvent(AstNode node) { return node is EventDeclaration; } public override void VisitCustomEventDeclaration(CustomEventDeclaration eventDeclaration) { FormatAttributedNode(eventDeclaration); EnforceBraceStyle(policy.EventBraceStyle, eventDeclaration.LBraceToken, eventDeclaration.RBraceToken); if (policy.IndentEventBody) { curIndent.Push(IndentType.Block); } if (!eventDeclaration.AddAccessor.IsNull) { FixIndentation(eventDeclaration.AddAccessor.StartLocation); if (!eventDeclaration.AddAccessor.Body.IsNull) { if (!policy.AllowEventAddBlockInline || eventDeclaration.AddAccessor.Body.LBraceToken.StartLocation.Line != eventDeclaration.AddAccessor.Body.RBraceToken.StartLocation.Line) { EnforceBraceStyle(policy.EventAddBraceStyle, eventDeclaration.AddAccessor.Body.LBraceToken, eventDeclaration.AddAccessor.Body.RBraceToken); } else { nextStatementIndent = " "; } VisitBlockWithoutFixingBraces(eventDeclaration.AddAccessor.Body, policy.IndentBlocks); } } if (!eventDeclaration.RemoveAccessor.IsNull) { FixIndentation(eventDeclaration.RemoveAccessor.StartLocation); if (!eventDeclaration.RemoveAccessor.Body.IsNull) { if (!policy.AllowEventRemoveBlockInline || eventDeclaration.RemoveAccessor.Body.LBraceToken.StartLocation.Line != eventDeclaration.RemoveAccessor.Body.RBraceToken.StartLocation.Line) { EnforceBraceStyle(policy.EventRemoveBraceStyle, eventDeclaration.RemoveAccessor.Body.LBraceToken, eventDeclaration.RemoveAccessor.Body.RBraceToken); } else { nextStatementIndent = " "; } VisitBlockWithoutFixingBraces(eventDeclaration.RemoveAccessor.Body, policy.IndentBlocks); } } if (policy.IndentEventBody) { curIndent.Pop (); } if (eventDeclaration.NextSibling is EventDeclaration && IsSimpleEvent(eventDeclaration) && IsSimpleEvent(eventDeclaration.NextSibling)) { EnsureBlankLinesAfter(eventDeclaration, policy.BlankLinesBetweenEventFields); } else if (IsMember(eventDeclaration.NextSibling)) { EnsureBlankLinesAfter(eventDeclaration, policy.BlankLinesBetweenMembers); } } public override void VisitEventDeclaration(EventDeclaration eventDeclaration) { FormatAttributedNode(eventDeclaration); if (eventDeclaration.NextSibling is EventDeclaration && IsSimpleEvent(eventDeclaration) && IsSimpleEvent(eventDeclaration.NextSibling)) { EnsureBlankLinesAfter(eventDeclaration, policy.BlankLinesBetweenEventFields); } else if (IsMember(eventDeclaration.NextSibling)) { EnsureBlankLinesAfter(eventDeclaration, policy.BlankLinesBetweenMembers); } var lastLoc = eventDeclaration.StartLocation; curIndent.Push(IndentType.Block); foreach (var initializer in eventDeclaration.Variables) { if (lastLoc.Line != initializer.StartLocation.Line) { FixStatementIndentation(initializer.StartLocation); lastLoc = initializer.StartLocation; } initializer.AcceptVisitor(this); } curIndent.Pop (); } public override void VisitAccessor(Accessor accessor) { FixIndentationForceNewLine(accessor.StartLocation); base.VisitAccessor(accessor); } public override void VisitFieldDeclaration(FieldDeclaration fieldDeclaration) { FormatAttributedNode(fieldDeclaration); fieldDeclaration.ReturnType.AcceptVisitor(this); FormatCommas(fieldDeclaration, policy.SpaceBeforeFieldDeclarationComma, policy.SpaceAfterFieldDeclarationComma); if (fieldDeclaration.NextSibling is FieldDeclaration || fieldDeclaration.NextSibling is FixedFieldDeclaration) { EnsureBlankLinesAfter(fieldDeclaration, policy.BlankLinesBetweenFields); } else if (IsMember(fieldDeclaration.NextSibling)) { EnsureBlankLinesAfter(fieldDeclaration, policy.BlankLinesBetweenMembers); } var lastLoc = fieldDeclaration.StartLocation; curIndent.Push(IndentType.Block); foreach (var initializer in fieldDeclaration.Variables) { if (lastLoc.Line != initializer.StartLocation.Line) { FixStatementIndentation(initializer.StartLocation); lastLoc = initializer.StartLocation; } initializer.AcceptVisitor(this); } curIndent.Pop (); } public override void VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration) { FormatAttributedNode(fixedFieldDeclaration); FormatCommas(fixedFieldDeclaration, policy.SpaceBeforeFieldDeclarationComma, policy.SpaceAfterFieldDeclarationComma); if (fixedFieldDeclaration.NextSibling is FieldDeclaration || fixedFieldDeclaration.NextSibling is FixedFieldDeclaration) { EnsureBlankLinesAfter(fixedFieldDeclaration, policy.BlankLinesBetweenFields); } else if (IsMember(fixedFieldDeclaration.NextSibling)) { EnsureBlankLinesAfter(fixedFieldDeclaration, policy.BlankLinesBetweenMembers); } var lastLoc = fixedFieldDeclaration.StartLocation; curIndent.Push(IndentType.Block); foreach (var initializer in fixedFieldDeclaration.Variables) { if (lastLoc.Line != initializer.StartLocation.Line) { FixStatementIndentation(initializer.StartLocation); lastLoc = initializer.StartLocation; } initializer.AcceptVisitor(this); } curIndent.Pop (); } public override void VisitEnumMemberDeclaration(EnumMemberDeclaration enumMemberDeclaration) { FormatAttributedNode(enumMemberDeclaration); base.VisitEnumMemberDeclaration(enumMemberDeclaration); } public override void VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration) { FormatAttributedNode(delegateDeclaration); ForceSpacesBefore(delegateDeclaration.LParToken, policy.SpaceBeforeDelegateDeclarationParentheses); if (delegateDeclaration.Parameters.Any()) { ForceSpacesAfter(delegateDeclaration.LParToken, policy.SpaceWithinDelegateDeclarationParentheses); ForceSpacesBefore(delegateDeclaration.RParToken, policy.SpaceWithinDelegateDeclarationParentheses); } else { ForceSpacesAfter(delegateDeclaration.LParToken, policy.SpaceBetweenEmptyDelegateDeclarationParentheses); ForceSpacesBefore(delegateDeclaration.RParToken, policy.SpaceBetweenEmptyDelegateDeclarationParentheses); } FormatCommas(delegateDeclaration, policy.SpaceBeforeDelegateDeclarationParameterComma, policy.SpaceAfterDelegateDeclarationParameterComma); if (delegateDeclaration.NextSibling is TypeDeclaration || delegateDeclaration.NextSibling is DelegateDeclaration) { EnsureBlankLinesAfter(delegateDeclaration, policy.BlankLinesBetweenTypes); } else if (IsMember(delegateDeclaration.NextSibling)) { EnsureBlankLinesAfter(delegateDeclaration, policy.BlankLinesBetweenMembers); } base.VisitDelegateDeclaration(delegateDeclaration); } static bool IsMember(AstNode nextSibling) { return nextSibling != null && nextSibling.NodeType == NodeType.Member; } void FormatAttributedNode(AstNode node) { if (node == null) { return; } AstNode child = node.FirstChild; while (child != null && child is AttributeSection) { FixIndentationForceNewLine(child.StartLocation); child = child.NextSibling; } if (child != null) { FixIndentationForceNewLine(child.StartLocation); } } public override void VisitMethodDeclaration(MethodDeclaration methodDeclaration) { FormatAttributedNode(methodDeclaration); ForceSpacesBefore(methodDeclaration.LParToken, policy.SpaceBeforeMethodDeclarationParentheses); if (methodDeclaration.Parameters.Any()) { ForceSpacesAfter(methodDeclaration.LParToken, policy.SpaceWithinMethodDeclarationParentheses); ForceSpacesBefore(methodDeclaration.RParToken, policy.SpaceWithinMethodDeclarationParentheses); } else { ForceSpacesAfter(methodDeclaration.LParToken, policy.SpaceBetweenEmptyMethodDeclarationParentheses); ForceSpacesBefore(methodDeclaration.RParToken, policy.SpaceBetweenEmptyMethodDeclarationParentheses); } FormatCommas(methodDeclaration, policy.SpaceBeforeMethodDeclarationParameterComma, policy.SpaceAfterMethodDeclarationParameterComma); if (!methodDeclaration.Body.IsNull) { EnforceBraceStyle(policy.MethodBraceStyle, methodDeclaration.Body.LBraceToken, methodDeclaration.Body.RBraceToken); VisitBlockWithoutFixingBraces(methodDeclaration.Body, policy.IndentMethodBody); } if (IsMember(methodDeclaration.NextSibling)) { EnsureBlankLinesAfter(methodDeclaration, policy.BlankLinesBetweenMembers); } } public override void VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration) { FormatAttributedNode(operatorDeclaration); ForceSpacesBefore(operatorDeclaration.LParToken, policy.SpaceBeforeMethodDeclarationParentheses); if (operatorDeclaration.Parameters.Any()) { ForceSpacesAfter(operatorDeclaration.LParToken, policy.SpaceWithinMethodDeclarationParentheses); ForceSpacesBefore(operatorDeclaration.RParToken, policy.SpaceWithinMethodDeclarationParentheses); } else { ForceSpacesAfter(operatorDeclaration.LParToken, policy.SpaceBetweenEmptyMethodDeclarationParentheses); ForceSpacesBefore(operatorDeclaration.RParToken, policy.SpaceBetweenEmptyMethodDeclarationParentheses); } FormatCommas(operatorDeclaration, policy.SpaceBeforeMethodDeclarationParameterComma, policy.SpaceAfterMethodDeclarationParameterComma); if (!operatorDeclaration.Body.IsNull) { EnforceBraceStyle(policy.MethodBraceStyle, operatorDeclaration.Body.LBraceToken, operatorDeclaration.Body.RBraceToken); VisitBlockWithoutFixingBraces(operatorDeclaration.Body, policy.IndentMethodBody); } if (IsMember(operatorDeclaration.NextSibling)) { EnsureBlankLinesAfter(operatorDeclaration, policy.BlankLinesBetweenMembers); } } public override void VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration) { FormatAttributedNode(constructorDeclaration); ForceSpacesBefore(constructorDeclaration.LParToken, policy.SpaceBeforeConstructorDeclarationParentheses); if (constructorDeclaration.Parameters.Any()) { ForceSpacesAfter(constructorDeclaration.LParToken, policy.SpaceWithinConstructorDeclarationParentheses); ForceSpacesBefore(constructorDeclaration.RParToken, policy.SpaceWithinConstructorDeclarationParentheses); } else { ForceSpacesAfter(constructorDeclaration.LParToken, policy.SpaceBetweenEmptyConstructorDeclarationParentheses); ForceSpacesBefore(constructorDeclaration.RParToken, policy.SpaceBetweenEmptyConstructorDeclarationParentheses); } FormatCommas(constructorDeclaration, policy.SpaceBeforeConstructorDeclarationParameterComma, policy.SpaceAfterConstructorDeclarationParameterComma); if (!constructorDeclaration.Body.IsNull) { EnforceBraceStyle(policy.ConstructorBraceStyle, constructorDeclaration.Body.LBraceToken, constructorDeclaration.Body.RBraceToken); VisitBlockWithoutFixingBraces(constructorDeclaration.Body, policy.IndentMethodBody); } if (IsMember(constructorDeclaration.NextSibling)) { EnsureBlankLinesAfter(constructorDeclaration, policy.BlankLinesBetweenMembers); } } public override void VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration) { FormatAttributedNode(destructorDeclaration); CSharpTokenNode lParen = destructorDeclaration.LParToken; int offset = this.document.GetOffset(lParen.StartLocation); ForceSpaceBefore(offset, policy.SpaceBeforeConstructorDeclarationParentheses); if (!destructorDeclaration.Body.IsNull) { EnforceBraceStyle(policy.DestructorBraceStyle, destructorDeclaration.Body.LBraceToken, destructorDeclaration.Body.RBraceToken); VisitBlockWithoutFixingBraces(destructorDeclaration.Body, policy.IndentMethodBody); } if (IsMember(destructorDeclaration.NextSibling)) { EnsureBlankLinesAfter(destructorDeclaration, policy.BlankLinesBetweenMembers); } } #region Statements public override void VisitExpressionStatement(ExpressionStatement expressionStatement) { base.VisitExpressionStatement(expressionStatement); FixSemicolon(expressionStatement.SemicolonToken); } void VisitBlockWithoutFixingBraces(BlockStatement blockStatement, bool indent) { if (indent) { curIndent.Push(IndentType.Block); } foreach (var child in blockStatement.Children) { if (child.Role == Roles.LBrace || child.Role == Roles.RBrace) { continue; } if (child is Statement) { FixStatementIndentation(child.StartLocation); child.AcceptVisitor(this); } else if (child is Comment) { child.AcceptVisitor(this); } else { // pre processor directives at line start, if they are there. if (child.StartLocation.Column > 1) FixStatementIndentation(child.StartLocation); } } if (indent) { curIndent.Pop (); } } public override void VisitBlockStatement(BlockStatement blockStatement) { FixIndentation(blockStatement.StartLocation); VisitBlockWithoutFixingBraces(blockStatement, policy.IndentBlocks); FixIndentation(blockStatement.EndLocation, -1); } public override void VisitComment(Comment comment) { if (comment.StartsLine && !HadErrors && (!policy.KeepCommentsAtFirstColumn || comment.StartLocation.Column > 1)) { FixIndentation(comment.StartLocation); } } public override void VisitBreakStatement(BreakStatement breakStatement) { FixSemicolon(breakStatement.SemicolonToken); } public override void VisitCheckedStatement(CheckedStatement checkedStatement) { FixEmbeddedStatment(policy.StatementBraceStyle, policy.FixedBraceForcement, checkedStatement.Body); } public override void VisitContinueStatement(ContinueStatement continueStatement) { FixSemicolon(continueStatement.SemicolonToken); } public override void VisitEmptyStatement(EmptyStatement emptyStatement) { // Empty } public override void VisitFixedStatement(FixedStatement fixedStatement) { FixEmbeddedStatment(policy.StatementBraceStyle, policy.FixedBraceForcement, fixedStatement.EmbeddedStatement); } public override void VisitForeachStatement(ForeachStatement foreachStatement) { ForceSpacesBefore(foreachStatement.LParToken, policy.SpaceBeforeForeachParentheses); ForceSpacesAfter(foreachStatement.LParToken, policy.SpacesWithinForeachParentheses); ForceSpacesBefore(foreachStatement.RParToken, policy.SpacesWithinForeachParentheses); FixEmbeddedStatment(policy.StatementBraceStyle, policy.ForEachBraceForcement, foreachStatement.EmbeddedStatement); } void FixEmbeddedStatment(BraceStyle braceStyle, BraceForcement braceForcement, AstNode node) { FixEmbeddedStatment(braceStyle, braceForcement, null, false, node); } void FixEmbeddedStatment(BraceStyle braceStyle, BraceForcement braceForcement, CSharpTokenNode token, bool allowInLine, AstNode node, bool statementAlreadyIndented = false) { if (node == null) { return; } bool isBlock = node is BlockStatement; TextReplaceAction beginBraceAction = null; TextReplaceAction endBraceAction = null; switch (braceForcement) { case BraceForcement.DoNotChange: //nothing break; case BraceForcement.AddBraces: if (!isBlock) { AstNode n = node.Parent.GetCSharpNodeBefore(node); int start = document.GetOffset(n.EndLocation); string startBrace = ""; switch (braceStyle) { case BraceStyle.EndOfLineWithoutSpace: startBrace = "{"; break; case BraceStyle.BannerStyle: case BraceStyle.EndOfLine: startBrace = " {"; break; case BraceStyle.NextLine: startBrace = this.options.EolMarker + curIndent.IndentString + "{"; break; case BraceStyle.NextLineShifted2: case BraceStyle.NextLineShifted: curIndent.Push(IndentType.Block); startBrace = this.options.EolMarker + curIndent.IndentString + "{"; curIndent.Pop(); break; } beginBraceAction = AddChange(start, 0, startBrace); } break; case BraceForcement.RemoveBraces: if (isBlock) { BlockStatement block = node as BlockStatement; if (block.Statements.Count() == 1) { int offset1 = document.GetOffset(node.StartLocation); int start = SearchWhitespaceStart(offset1); int offset2 = document.GetOffset(node.EndLocation); int end = SearchWhitespaceStart(offset2 - 1); beginBraceAction = AddChange(start, offset1 - start + 1, null); endBraceAction = AddChange(end + 1, offset2 - end, null); node = block.FirstChild; isBlock = false; } } break; } if (isBlock) { BlockStatement block = node as BlockStatement; if (allowInLine && block.StartLocation.Line == block.EndLocation.Line && block.Statements.Count() <= 1) { if (block.Statements.Count() == 1) { nextStatementIndent = " "; } } else { if (!statementAlreadyIndented) { EnforceBraceStyle(braceStyle, block.LBraceToken, block.RBraceToken); } } if (braceStyle == BraceStyle.NextLineShifted2) { curIndent.Push(IndentType.Block); } } else { if (allowInLine && token.StartLocation.Line == node.EndLocation.Line) { nextStatementIndent = " "; } } if (policy.IndentBlocks && !(policy.AlignEmbeddedIfStatements && node is IfElseStatement && node.Parent is IfElseStatement || policy.AlignEmbeddedUsingStatements && node is UsingStatement && node.Parent is UsingStatement)) { curIndent.Push(IndentType.Block); } if (isBlock) { VisitBlockWithoutFixingBraces((BlockStatement)node, false); } else { if (!statementAlreadyIndented) { FixStatementIndentation(node.StartLocation); } node.AcceptVisitor(this); } if (policy.IndentBlocks && !(policy.AlignEmbeddedIfStatements && node is IfElseStatement && node.Parent is IfElseStatement || policy.AlignEmbeddedUsingStatements && node is UsingStatement && node.Parent is UsingStatement)) { curIndent.Pop(); } switch (braceForcement) { case BraceForcement.DoNotChange: break; case BraceForcement.AddBraces: if (!isBlock) { int offset = document.GetOffset(node.EndLocation); if (!char.IsWhiteSpace(document.GetCharAt(offset))) { offset++; } string startBrace = ""; switch (braceStyle) { case BraceStyle.DoNotChange: startBrace = null; break; case BraceStyle.EndOfLineWithoutSpace: startBrace = this.options.EolMarker + curIndent.IndentString + "}"; break; case BraceStyle.EndOfLine: startBrace = this.options.EolMarker + curIndent.IndentString + "}"; break; case BraceStyle.NextLine: startBrace = this.options.EolMarker + curIndent.IndentString + "}"; break; case BraceStyle.BannerStyle: case BraceStyle.NextLineShifted2: case BraceStyle.NextLineShifted: curIndent.Push(IndentType.Block); startBrace = this.options.EolMarker + curIndent.IndentString + "}"; curIndent.Pop (); break; } if (startBrace != null) { endBraceAction = AddChange(offset, 0, startBrace); } } break; } if (beginBraceAction != null && endBraceAction != null) { beginBraceAction.DependsOn = endBraceAction; endBraceAction.DependsOn = beginBraceAction; } } void EnforceBraceStyle(BraceStyle braceStyle, AstNode lbrace, AstNode rbrace) { if (lbrace.IsNull || rbrace.IsNull) { return; } // LineSegment lbraceLineSegment = data.Document.GetLine (lbrace.StartLocation.Line); int lbraceOffset = document.GetOffset(lbrace.StartLocation); // LineSegment rbraceLineSegment = data.Document.GetLine (rbrace.StartLocation.Line); int rbraceOffset = document.GetOffset(rbrace.StartLocation); int whitespaceStart = SearchWhitespaceStart(lbraceOffset); int whitespaceEnd = SearchWhitespaceLineStart(rbraceOffset); string startIndent = ""; string endIndent = ""; switch (braceStyle) { case BraceStyle.DoNotChange: startIndent = endIndent = null; break; case BraceStyle.EndOfLineWithoutSpace: startIndent = ""; endIndent = IsLineIsEmptyUpToEol(rbraceOffset) ? curIndent.IndentString : this.options.EolMarker + curIndent.IndentString; break; case BraceStyle.BannerStyle: var prevNode = lbrace.GetPrevNode(); if (prevNode is Comment) { // delete old bracket AddChange(whitespaceStart, lbraceOffset - whitespaceStart + 1, ""); while (prevNode is Comment) { prevNode = prevNode.GetPrevNode(); } whitespaceStart = document.GetOffset(prevNode.EndLocation); lbraceOffset = whitespaceStart; startIndent = " {"; } else { startIndent = " "; } curIndent.Push(IndentType.Block); endIndent = IsLineIsEmptyUpToEol(rbraceOffset) ? curIndent.IndentString : this.options.EolMarker + curIndent.IndentString; curIndent.Pop(); break; case BraceStyle.EndOfLine: prevNode = lbrace.GetPrevNode(); if (prevNode is Comment) { // delete old bracket AddChange(whitespaceStart, lbraceOffset - whitespaceStart + 1, ""); while (prevNode is Comment) { prevNode = prevNode.GetPrevNode(); } whitespaceStart = document.GetOffset(prevNode.EndLocation); lbraceOffset = whitespaceStart; startIndent = " {"; } else { startIndent = " "; } endIndent = IsLineIsEmptyUpToEol(rbraceOffset) ? curIndent.IndentString : this.options.EolMarker + curIndent.IndentString; break; case BraceStyle.NextLine: startIndent = this.options.EolMarker + curIndent.IndentString; endIndent = IsLineIsEmptyUpToEol(rbraceOffset) ? curIndent.IndentString : this.options.EolMarker + curIndent.IndentString; break; case BraceStyle.NextLineShifted2: case BraceStyle.NextLineShifted: curIndent.Push(IndentType.Block); startIndent = this.options.EolMarker + curIndent.IndentString; endIndent = IsLineIsEmptyUpToEol(rbraceOffset) ? curIndent.IndentString : this.options.EolMarker + curIndent.IndentString; curIndent.Pop (); break; } if (lbraceOffset > 0 && startIndent != null) { AddChange(whitespaceStart, lbraceOffset - whitespaceStart, startIndent); } if (rbraceOffset > 0 && endIndent != null) { AddChange(whitespaceEnd, rbraceOffset - whitespaceEnd, endIndent); } } TextReplaceAction AddChange(int offset, int removedChars, string insertedText) { var action = new TextReplaceAction (offset, removedChars, insertedText); changes.Add(action); return action; } public bool IsLineIsEmptyUpToEol(TextLocation startLocation) { return IsLineIsEmptyUpToEol(document.GetOffset(startLocation) - 1); } bool IsLineIsEmptyUpToEol(int startOffset) { for (int offset = startOffset - 1; offset >= 0; offset--) { char ch = document.GetCharAt(offset); if (ch != ' ' && ch != '\t') { return ch == '\n' || ch == '\r'; } } return true; } int SearchWhitespaceStart(int startOffset) { if (startOffset < 0) { throw new ArgumentOutOfRangeException ("startoffset", "value : " + startOffset); } for (int offset = startOffset - 1; offset >= 0; offset--) { char ch = document.GetCharAt(offset); if (!Char.IsWhiteSpace(ch)) { return offset + 1; } } return 0; } int SearchWhitespaceEnd(int startOffset) { if (startOffset > document.TextLength) { throw new ArgumentOutOfRangeException ("startoffset", "value : " + startOffset); } for (int offset = startOffset + 1; offset < document.TextLength; offset++) { char ch = document.GetCharAt(offset); if (!Char.IsWhiteSpace(ch)) { return offset + 1; } } return document.TextLength - 1; } int SearchWhitespaceLineStart(int startOffset) { if (startOffset < 0) { throw new ArgumentOutOfRangeException ("startoffset", "value : " + startOffset); } for (int offset = startOffset - 1; offset >= 0; offset--) { char ch = document.GetCharAt(offset); if (ch != ' ' && ch != '\t') { return offset + 1; } } return 0; } public override void VisitForStatement(ForStatement forStatement) { foreach (AstNode node in forStatement.Children) { if (node.Role == Roles.Semicolon) { if (node.NextSibling is CSharpTokenNode || node.NextSibling is EmptyStatement) { continue; } ForceSpacesBefore(node, policy.SpaceBeforeForSemicolon); ForceSpacesAfter(node, policy.SpaceAfterForSemicolon); } } ForceSpacesBefore(forStatement.LParToken, policy.SpaceBeforeForParentheses); ForceSpacesAfter(forStatement.LParToken, policy.SpacesWithinForParentheses); ForceSpacesBefore(forStatement.RParToken, policy.SpacesWithinForParentheses); FixEmbeddedStatment(policy.StatementBraceStyle, policy.ForBraceForcement, forStatement.EmbeddedStatement); } public override void VisitGotoStatement(GotoStatement gotoStatement) { VisitChildren(gotoStatement); FixSemicolon(gotoStatement.SemicolonToken); } public override void VisitIfElseStatement(IfElseStatement ifElseStatement) { ForceSpacesBefore(ifElseStatement.LParToken, policy.SpaceBeforeIfParentheses); ForceSpacesAfter(ifElseStatement.LParToken, policy.SpacesWithinIfParentheses); ForceSpacesBefore(ifElseStatement.RParToken, policy.SpacesWithinIfParentheses); if (!(ifElseStatement.Parent is IfElseStatement && ((IfElseStatement)ifElseStatement.Parent).FalseStatement == ifElseStatement)) { FixStatementIndentation(ifElseStatement.StartLocation); } if (!ifElseStatement.Condition.IsNull) { ifElseStatement.Condition.AcceptVisitor(this); } if (!ifElseStatement.TrueStatement.IsNull) { FixEmbeddedStatment(policy.StatementBraceStyle, policy.IfElseBraceForcement, ifElseStatement.IfToken, policy.AllowIfBlockInline, ifElseStatement.TrueStatement); } if (!ifElseStatement.FalseStatement.IsNull) { PlaceOnNewLine(policy.PlaceElseOnNewLine || !(ifElseStatement.TrueStatement is BlockStatement) && policy.IfElseBraceForcement != BraceForcement.AddBraces, ifElseStatement.ElseToken); var forcement = policy.IfElseBraceForcement; if (ifElseStatement.FalseStatement is IfElseStatement) { forcement = BraceForcement.DoNotChange; PlaceOnNewLine(policy.PlaceElseIfOnNewLine, ((IfElseStatement)ifElseStatement.FalseStatement).IfToken); } FixEmbeddedStatment(policy.StatementBraceStyle, forcement, ifElseStatement.ElseToken, policy.AllowIfBlockInline, ifElseStatement.FalseStatement, ifElseStatement.FalseStatement is IfElseStatement); } } public override void VisitLabelStatement(LabelStatement labelStatement) { // TODO VisitChildren(labelStatement); } public override void VisitLockStatement(LockStatement lockStatement) { ForceSpacesBefore(lockStatement.LParToken, policy.SpaceBeforeLockParentheses); ForceSpacesAfter(lockStatement.LParToken, policy.SpacesWithinLockParentheses); ForceSpacesBefore(lockStatement.RParToken, policy.SpacesWithinLockParentheses); FixEmbeddedStatment(policy.StatementBraceStyle, policy.FixedBraceForcement, lockStatement.EmbeddedStatement); } public override void VisitReturnStatement(ReturnStatement returnStatement) { VisitChildren(returnStatement); FixSemicolon(returnStatement.SemicolonToken); } public override void VisitSwitchStatement(SwitchStatement switchStatement) { ForceSpacesBefore(switchStatement.LParToken, policy.SpaceBeforeSwitchParentheses); ForceSpacesAfter(switchStatement.LParToken, policy.SpacesWithinSwitchParentheses); ForceSpacesBefore(switchStatement.RParToken, policy.SpacesWithinSwitchParentheses); EnforceBraceStyle(policy.StatementBraceStyle, switchStatement.LBraceToken, switchStatement.RBraceToken); VisitChildren(switchStatement); } public override void VisitSwitchSection(SwitchSection switchSection) { if (policy.IndentSwitchBody) { curIndent.Push(IndentType.Block); } foreach (CaseLabel label in switchSection.CaseLabels) { FixStatementIndentation(label.StartLocation); label.AcceptVisitor(this); } if (policy.IndentCaseBody) { curIndent.Push(IndentType.Block); } foreach (var stmt in switchSection.Statements) { if (stmt is BreakStatement && !policy.IndentBreakStatements && policy.IndentCaseBody) { curIndent.Pop(); FixStatementIndentation(stmt.StartLocation); stmt.AcceptVisitor(this); curIndent.Push(IndentType.Block); continue; } FixStatementIndentation(stmt.StartLocation); stmt.AcceptVisitor(this); } if (policy.IndentCaseBody) { curIndent.Pop (); } if (policy.IndentSwitchBody) { curIndent.Pop (); } } public override void VisitCaseLabel(CaseLabel caseLabel) { FixSemicolon(caseLabel.ColonToken); } public override void VisitThrowStatement(ThrowStatement throwStatement) { VisitChildren(throwStatement); FixSemicolon(throwStatement.SemicolonToken); } public override void VisitTryCatchStatement(TryCatchStatement tryCatchStatement) { if (!tryCatchStatement.TryBlock.IsNull) { FixEmbeddedStatment(policy.StatementBraceStyle, BraceForcement.DoNotChange, tryCatchStatement.TryBlock); } foreach (CatchClause clause in tryCatchStatement.CatchClauses) { PlaceOnNewLine(policy.PlaceCatchOnNewLine, clause.CatchToken); if (!clause.LParToken.IsNull) { ForceSpacesBefore(clause.LParToken, policy.SpaceBeforeCatchParentheses); ForceSpacesAfter(clause.LParToken, policy.SpacesWithinCatchParentheses); ForceSpacesBefore(clause.RParToken, policy.SpacesWithinCatchParentheses); } FixEmbeddedStatment(policy.StatementBraceStyle, BraceForcement.DoNotChange, clause.Body); } if (!tryCatchStatement.FinallyBlock.IsNull) { PlaceOnNewLine(policy.PlaceFinallyOnNewLine, tryCatchStatement.FinallyToken); FixEmbeddedStatment(policy.StatementBraceStyle, BraceForcement.DoNotChange, tryCatchStatement.FinallyBlock); } } public override void VisitCatchClause(CatchClause catchClause) { // Handled in TryCatchStatement } public override void VisitUncheckedStatement(UncheckedStatement uncheckedStatement) { FixEmbeddedStatment(policy.StatementBraceStyle, policy.FixedBraceForcement, uncheckedStatement.Body); } public override void VisitUnsafeStatement(UnsafeStatement unsafeStatement) { FixEmbeddedStatment(policy.StatementBraceStyle, BraceForcement.DoNotChange, unsafeStatement.Body); } public override void VisitUsingStatement(UsingStatement usingStatement) { ForceSpacesBefore(usingStatement.LParToken, policy.SpaceBeforeUsingParentheses); ForceSpacesAfter(usingStatement.LParToken, policy.SpacesWithinUsingParentheses); ForceSpacesBefore(usingStatement.RParToken, policy.SpacesWithinUsingParentheses); FixEmbeddedStatment(policy.StatementBraceStyle, policy.UsingBraceForcement, usingStatement.EmbeddedStatement); } public override void VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement) { if ((variableDeclarationStatement.Modifiers & Modifiers.Const) == Modifiers.Const) { ForceSpacesAround(variableDeclarationStatement.Type, true); } else { ForceSpacesAfter(variableDeclarationStatement.Type, true); } var lastLoc = variableDeclarationStatement.StartLocation; foreach (var initializer in variableDeclarationStatement.Variables) { if (lastLoc.Line != initializer.StartLocation.Line) { FixStatementIndentation(initializer.StartLocation); lastLoc = initializer.StartLocation; } initializer.AcceptVisitor(this); } FormatCommas(variableDeclarationStatement, policy.SpaceBeforeLocalVariableDeclarationComma, policy.SpaceAfterLocalVariableDeclarationComma); FixSemicolon(variableDeclarationStatement.SemicolonToken); } public override void VisitDoWhileStatement(DoWhileStatement doWhileStatement) { PlaceOnNewLine(policy.PlaceWhileOnNewLine, doWhileStatement.WhileToken); FixEmbeddedStatment(policy.StatementBraceStyle, policy.WhileBraceForcement, doWhileStatement.EmbeddedStatement); } public override void VisitWhileStatement(WhileStatement whileStatement) { ForceSpacesBefore(whileStatement.LParToken, policy.SpaceBeforeWhileParentheses); ForceSpacesAfter(whileStatement.LParToken, policy.SpacesWithinWhileParentheses); ForceSpacesBefore(whileStatement.RParToken, policy.SpacesWithinWhileParentheses); FixEmbeddedStatment(policy.StatementBraceStyle, policy.WhileBraceForcement, whileStatement.EmbeddedStatement); } public override void VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement) { FixSemicolon(yieldBreakStatement.SemicolonToken); } public override void VisitYieldReturnStatement(YieldReturnStatement yieldStatement) { yieldStatement.Expression.AcceptVisitor(this); FixSemicolon(yieldStatement.SemicolonToken); } public override void VisitVariableInitializer(VariableInitializer variableInitializer) { if (!variableInitializer.AssignToken.IsNull) { ForceSpacesAround(variableInitializer.AssignToken, policy.SpaceAroundAssignment); } if (!variableInitializer.Initializer.IsNull) { variableInitializer.Initializer.AcceptVisitor(this); } } #endregion #region Expressions public override void VisitComposedType(ComposedType composedType) { var spec = composedType.ArraySpecifiers.FirstOrDefault(); if (spec != null) { ForceSpacesBefore(spec.LBracketToken, policy.SpaceBeforeArrayDeclarationBrackets); } base.VisitComposedType(composedType); } public override void VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression) { if (!anonymousMethodExpression.Body.IsNull) { EnforceBraceStyle(policy.AnonymousMethodBraceStyle, anonymousMethodExpression.Body.LBraceToken, anonymousMethodExpression.Body.RBraceToken); } base.VisitAnonymousMethodExpression(anonymousMethodExpression); } public override void VisitAssignmentExpression(AssignmentExpression assignmentExpression) { ForceSpacesAround(assignmentExpression.OperatorToken, policy.SpaceAroundAssignment); base.VisitAssignmentExpression(assignmentExpression); } public override void VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression) { bool forceSpaces = false; switch (binaryOperatorExpression.Operator) { case BinaryOperatorType.Equality: case BinaryOperatorType.InEquality: forceSpaces = policy.SpaceAroundEqualityOperator; break; case BinaryOperatorType.GreaterThan: case BinaryOperatorType.GreaterThanOrEqual: case BinaryOperatorType.LessThan: case BinaryOperatorType.LessThanOrEqual: forceSpaces = policy.SpaceAroundRelationalOperator; break; case BinaryOperatorType.ConditionalAnd: case BinaryOperatorType.ConditionalOr: forceSpaces = policy.SpaceAroundLogicalOperator; break; case BinaryOperatorType.BitwiseAnd: case BinaryOperatorType.BitwiseOr: case BinaryOperatorType.ExclusiveOr: forceSpaces = policy.SpaceAroundBitwiseOperator; break; case BinaryOperatorType.Add: case BinaryOperatorType.Subtract: forceSpaces = policy.SpaceAroundAdditiveOperator; break; case BinaryOperatorType.Multiply: case BinaryOperatorType.Divide: case BinaryOperatorType.Modulus: forceSpaces = policy.SpaceAroundMultiplicativeOperator; break; case BinaryOperatorType.ShiftLeft: case BinaryOperatorType.ShiftRight: forceSpaces = policy.SpaceAroundShiftOperator; break; case BinaryOperatorType.NullCoalescing: forceSpaces = policy.SpaceAroundNullCoalescingOperator; break; } ForceSpacesAround(binaryOperatorExpression.OperatorToken, forceSpaces); base.VisitBinaryOperatorExpression(binaryOperatorExpression); // Handle line breaks in binary opeartor expression. if (binaryOperatorExpression.Left.EndLocation.Line != binaryOperatorExpression.Right.StartLocation.Line) { curIndent.Push(IndentType.Block); if (binaryOperatorExpression.OperatorToken.StartLocation.Line == binaryOperatorExpression.Right.StartLocation.Line) { FixStatementIndentation(binaryOperatorExpression.OperatorToken.StartLocation); } else { FixStatementIndentation(binaryOperatorExpression.Right.StartLocation); } curIndent.Pop (); } } public override void VisitConditionalExpression(ConditionalExpression conditionalExpression) { ForceSpacesBefore(conditionalExpression.QuestionMarkToken, policy.SpaceBeforeConditionalOperatorCondition); ForceSpacesAfter(conditionalExpression.QuestionMarkToken, policy.SpaceAfterConditionalOperatorCondition); ForceSpacesBefore(conditionalExpression.ColonToken, policy.SpaceBeforeConditionalOperatorSeparator); ForceSpacesAfter(conditionalExpression.ColonToken, policy.SpaceAfterConditionalOperatorSeparator); base.VisitConditionalExpression(conditionalExpression); } public override void VisitCastExpression(CastExpression castExpression) { if (castExpression.RParToken != null) { ForceSpacesAfter(castExpression.LParToken, policy.SpacesWithinCastParentheses); ForceSpacesBefore(castExpression.RParToken, policy.SpacesWithinCastParentheses); ForceSpacesAfter(castExpression.RParToken, policy.SpaceAfterTypecast); } base.VisitCastExpression(castExpression); } void ForceSpacesAround(AstNode node, bool forceSpaces) { if (node.IsNull) { return; } ForceSpacesBefore(node, forceSpaces); ForceSpacesAfter(node, forceSpaces); } void FormatCommas(AstNode parent, bool before, bool after) { if (parent.IsNull) { return; } foreach (CSharpTokenNode comma in parent.Children.Where (node => node.Role == Roles.Comma)) { ForceSpacesAfter(comma, after); ForceSpacesBefore(comma, before); } } bool DoWrap(Wrapping wrapping, AstNode wrapNode) { return wrapping == Wrapping.WrapAlways || options.WrapLineLength > 0 && wrapping == Wrapping.WrapIfTooLong && wrapNode.StartLocation.Column >= options.WrapLineLength; } public override void VisitInvocationExpression(InvocationExpression invocationExpression) { ForceSpacesBefore(invocationExpression.LParToken, policy.SpaceBeforeMethodCallParentheses); if (invocationExpression.Arguments.Any()) { ForceSpacesAfter(invocationExpression.LParToken, policy.SpaceWithinMethodCallParentheses); } else { ForceSpacesAfter(invocationExpression.LParToken, policy.SpaceBetweenEmptyMethodCallParentheses); ForceSpacesBefore(invocationExpression.RParToken, policy.SpaceBetweenEmptyMethodCallParentheses); } if (invocationExpression.Target is MemberReferenceExpression) { var mt = (MemberReferenceExpression)invocationExpression.Target; if (mt.Target is InvocationExpression) { if (DoWrap(policy.ChainedMethodCallWrapping, mt.DotToken)) { curIndent.Push(IndentType.Continuation); FixStatementIndentation(mt.DotToken.StartLocation); curIndent.Pop(); } else { ForceSpacesBeforeRemoveNewLines(mt.DotToken, false); } } } bool wrapMethodCall = DoWrap(policy.MethodCallArgumentWrapping, invocationExpression.RParToken); if (wrapMethodCall && invocationExpression.Arguments.Any()) { if (policy.NewLineAferMethodCallOpenParentheses) { curIndent.Push(IndentType.Continuation); foreach (var arg in invocationExpression.Arguments) { FixStatementIndentation(arg.StartLocation); } curIndent.Pop(); } else { int extraSpaces = invocationExpression.Arguments.First().StartLocation.Column - 1 - curIndent.IndentString.Length; curIndent.ExtraSpaces += extraSpaces; foreach (var arg in invocationExpression.Arguments.Skip (1)) { FixStatementIndentation(arg.StartLocation); } curIndent.ExtraSpaces -= extraSpaces; } if (policy.MethodClosingParenthesesOnNewLine) FixStatementIndentation(invocationExpression.RParToken.StartLocation); } else { foreach (var arg in invocationExpression.Arguments) { ForceSpacesBeforeRemoveNewLines(arg, policy.SpaceAfterMethodCallParameterComma && arg.PrevSibling.Role == Roles.Comma); arg.AcceptVisitor(this); } ForceSpacesBeforeRemoveNewLines(invocationExpression.RParToken, policy.SpaceWithinMethodCallParentheses); } foreach (CSharpTokenNode comma in invocationExpression.Children.Where (node => node.Role == Roles.Comma)) { ForceSpacesBefore(comma, policy.SpaceBeforeMethodCallParameterComma); } if (!invocationExpression.Target.IsNull) invocationExpression.Target.AcceptVisitor(this); } public override void VisitIndexerExpression(IndexerExpression indexerExpression) { ForceSpacesBefore(indexerExpression.LBracketToken, policy.SpacesBeforeBrackets); ForceSpacesAfter(indexerExpression.LBracketToken, policy.SpacesWithinBrackets); bool wrapMethodCall = DoWrap(policy.MethodCallArgumentWrapping, indexerExpression.RBracketToken); if (wrapMethodCall && indexerExpression.Arguments.Any()) { if (policy.NewLineAferMethodCallOpenParentheses) { curIndent.Push(IndentType.Continuation); foreach (var arg in indexerExpression.Arguments) { FixStatementIndentation(arg.StartLocation); } curIndent.Pop(); } else { int extraSpaces = indexerExpression.Arguments.First().StartLocation.Column - 1 - curIndent.IndentString.Length; curIndent.ExtraSpaces += extraSpaces; foreach (var arg in indexerExpression.Arguments.Skip (1)) { FixStatementIndentation(arg.StartLocation); } curIndent.ExtraSpaces -= extraSpaces; } if (policy.MethodClosingParenthesesOnNewLine) FixStatementIndentation(indexerExpression.RBracketToken.StartLocation); } else { foreach (var arg in indexerExpression.Arguments) { ForceSpacesBeforeRemoveNewLines(arg, policy.SpaceAfterBracketComma && arg.PrevSibling.Role == Roles.Comma); arg.AcceptVisitor(this); } ForceSpacesBeforeRemoveNewLines(indexerExpression.RBracketToken, policy.SpacesWithinBrackets); } foreach (CSharpTokenNode comma in indexerExpression.Children.Where (node => node.Role == Roles.Comma)) { ForceSpacesBefore(comma, policy.SpaceBeforeBracketComma); } if (!indexerExpression.Target.IsNull) indexerExpression.Target.AcceptVisitor(this); } public override void VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression) { ForceSpacesAfter(parenthesizedExpression.LParToken, policy.SpacesWithinParentheses); ForceSpacesBefore(parenthesizedExpression.RParToken, policy.SpacesWithinParentheses); base.VisitParenthesizedExpression(parenthesizedExpression); } public override void VisitSizeOfExpression(SizeOfExpression sizeOfExpression) { ForceSpacesBefore(sizeOfExpression.LParToken, policy.SpaceBeforeSizeOfParentheses); ForceSpacesAfter(sizeOfExpression.LParToken, policy.SpacesWithinSizeOfParentheses); ForceSpacesBefore(sizeOfExpression.RParToken, policy.SpacesWithinSizeOfParentheses); base.VisitSizeOfExpression(sizeOfExpression); } public override void VisitTypeOfExpression(TypeOfExpression typeOfExpression) { ForceSpacesBefore(typeOfExpression.LParToken, policy.SpaceBeforeTypeOfParentheses); ForceSpacesAfter(typeOfExpression.LParToken, policy.SpacesWithinTypeOfParentheses); ForceSpacesBefore(typeOfExpression.RParToken, policy.SpacesWithinTypeOfParentheses); base.VisitTypeOfExpression(typeOfExpression); } public override void VisitCheckedExpression(CheckedExpression checkedExpression) { ForceSpacesAfter(checkedExpression.LParToken, policy.SpacesWithinCheckedExpressionParantheses); ForceSpacesBefore(checkedExpression.RParToken, policy.SpacesWithinCheckedExpressionParantheses); base.VisitCheckedExpression(checkedExpression); } public override void VisitUncheckedExpression(UncheckedExpression uncheckedExpression) { ForceSpacesAfter(uncheckedExpression.LParToken, policy.SpacesWithinCheckedExpressionParantheses); ForceSpacesBefore(uncheckedExpression.RParToken, policy.SpacesWithinCheckedExpressionParantheses); base.VisitUncheckedExpression(uncheckedExpression); } public override void VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression) { ForceSpacesBefore(objectCreateExpression.LParToken, policy.SpaceBeforeNewParentheses); if (objectCreateExpression.Arguments.Any()) { if (!objectCreateExpression.LParToken.IsNull) ForceSpacesAfter(objectCreateExpression.LParToken, policy.SpacesWithinNewParentheses); } else { if (!objectCreateExpression.LParToken.IsNull) ForceSpacesAfter(objectCreateExpression.LParToken, policy.SpacesBetweenEmptyNewParentheses); } bool wrapMethodCall = DoWrap(policy.MethodCallArgumentWrapping, objectCreateExpression.RParToken); if (wrapMethodCall && objectCreateExpression.Arguments.Any()) { if (policy.NewLineAferMethodCallOpenParentheses) { curIndent.Push(IndentType.Continuation); foreach (var arg in objectCreateExpression.Arguments) { FixStatementIndentation(arg.StartLocation); } curIndent.Pop(); } else { int extraSpaces = objectCreateExpression.Arguments.First().StartLocation.Column - 1 - curIndent.IndentString.Length; curIndent.ExtraSpaces += extraSpaces; foreach (var arg in objectCreateExpression.Arguments.Skip (1)) { FixStatementIndentation(arg.StartLocation); } curIndent.ExtraSpaces -= extraSpaces; } if (policy.MethodClosingParenthesesOnNewLine) FixStatementIndentation(objectCreateExpression.RParToken.StartLocation); } else { foreach (var arg in objectCreateExpression.Arguments) { ForceSpacesBeforeRemoveNewLines(arg, policy.SpaceAfterNewParameterComma && arg.PrevSibling.Role == Roles.Comma); arg.AcceptVisitor(this); } ForceSpacesBeforeRemoveNewLines(objectCreateExpression.RParToken, policy.SpacesWithinNewParentheses); } foreach (CSharpTokenNode comma in objectCreateExpression.Children.Where (node => node.Role == Roles.Comma)) { ForceSpacesBefore(comma, policy.SpaceBeforeNewParameterComma); } if (!objectCreateExpression.Type.IsNull) objectCreateExpression.Type.AcceptVisitor(this); } public override void VisitArrayCreateExpression(ArrayCreateExpression arrayObjectCreateExpression) { FormatCommas(arrayObjectCreateExpression, policy.SpaceBeforeMethodCallParameterComma, policy.SpaceAfterMethodCallParameterComma); base.VisitArrayCreateExpression(arrayObjectCreateExpression); } public override void VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression) { if (DoWrap(policy.ArrayInitializerWrapping, arrayInitializerExpression.RBraceToken)) { EnforceBraceStyle(policy.ArrayInitializerBraceStyle, arrayInitializerExpression.LBraceToken, arrayInitializerExpression.RBraceToken); curIndent.Push(IndentType.Block); foreach (var init in arrayInitializerExpression.Elements) { FixStatementIndentation(init.StartLocation); init.AcceptVisitor(this); } curIndent.Pop(); } else if (policy.ArrayInitializerWrapping == Wrapping.DoNotWrap) { ForceSpacesBeforeRemoveNewLines(arrayInitializerExpression.LBraceToken); ForceSpacesBeforeRemoveNewLines(arrayInitializerExpression.RBraceToken); foreach (var init in arrayInitializerExpression.Elements) { ForceSpacesBeforeRemoveNewLines(init); init.AcceptVisitor(this); } } else { base.VisitArrayInitializerExpression(arrayInitializerExpression); } } public override void VisitLambdaExpression(LambdaExpression lambdaExpression) { ForceSpacesAfter(lambdaExpression.ArrowToken, true); ForceSpacesBefore(lambdaExpression.ArrowToken, true); base.VisitLambdaExpression(lambdaExpression); } public override void VisitNamedArgumentExpression(NamedArgumentExpression namedArgumentExpression) { ForceSpacesAfter(namedArgumentExpression.ColonToken, policy.SpaceInNamedArgumentAfterDoubleColon); base.VisitNamedArgumentExpression(namedArgumentExpression); } public override void VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression) { ForceSpacesAfter(memberReferenceExpression.DotToken, false); base.VisitMemberReferenceExpression(memberReferenceExpression); } #endregion void ForceSpaceBefore(int offset, bool forceSpace) { bool insertedSpace = false; do { char ch = document.GetCharAt(offset); //Console.WriteLine (ch); if (!IsSpacing(ch) && (insertedSpace || !forceSpace)) { break; } if (ch == ' ' && forceSpace) { if (insertedSpace) { AddChange(offset, 1, null); } else { insertedSpace = true; } } else if (forceSpace) { if (!insertedSpace) { AddChange(offset, IsSpacing(ch) ? 1 : 0, " "); insertedSpace = true; } else if (IsSpacing(ch)) { AddChange(offset, 1, null); } } offset--; } while (offset >= 0); } /* int GetLastNonWsChar (LineSegment line, int lastColumn) { int result = -1; bool inComment = false; for (int i = 0; i < lastColumn; i++) { char ch = data.GetCharAt (line.Offset + i); if (Char.IsWhiteSpace (ch)) continue; if (ch == '/' && i + 1 < line.EditableLength && data.GetCharAt (line.Offset + i + 1) == '/') return result; if (ch == '/' && i + 1 < line.EditableLength && data.GetCharAt (line.Offset + i + 1) == '*') { inComment = true; i++; continue; } if (inComment && ch == '*' && i + 1 < line.EditableLength && data.GetCharAt (line.Offset + i + 1) == '/') { inComment = false; i++; continue; } if (!inComment) result = i; } return result; } */ public void FixSemicolon(CSharpTokenNode semicolon) { if (semicolon.IsNull) { return; } int endOffset = document.GetOffset(semicolon.StartLocation); int offset = endOffset; while (offset - 1 > 0 && char.IsWhiteSpace (document.GetCharAt (offset - 1))) { offset--; } if (offset < endOffset) { AddChange(offset, endOffset - offset, null); } } void PlaceOnNewLine(bool newLine, AstNode keywordNode) { if (keywordNode == null) { return; } int offset = document.GetOffset(keywordNode.StartLocation); int whitespaceStart = SearchWhitespaceStart(offset); string indentString = newLine ? this.options.EolMarker + this.curIndent.IndentString : " "; AddChange(whitespaceStart, offset - whitespaceStart, indentString); } string nextStatementIndent = null; void FixStatementIndentation(TextLocation location) { int offset = document.GetOffset(location); if (offset <= 0) { Console.WriteLine("possible wrong offset"); Console.WriteLine(Environment.StackTrace); return; } bool isEmpty = IsLineIsEmptyUpToEol(offset); int lineStart = SearchWhitespaceLineStart(offset); string indentString = nextStatementIndent == null ? (isEmpty ? "" : this.options.EolMarker) + this.curIndent.IndentString : nextStatementIndent; nextStatementIndent = null; AddChange(lineStart, offset - lineStart, indentString); } void FixIndentation(TextLocation location) { FixIndentation(location, 0); } void FixIndentation(TextLocation location, int relOffset) { if (location.Line < 1 || location.Line > document.LineCount) { Console.WriteLine("Invalid location " + location); Console.WriteLine(Environment.StackTrace); return; } string lineIndent = GetIndentation(location.Line); string indentString = this.curIndent.IndentString; if (indentString != lineIndent && location.Column - 1 + relOffset == lineIndent.Length) { AddChange(document.GetOffset(location.Line, 1), lineIndent.Length, indentString); } } void FixIndentationForceNewLine(TextLocation location) { string lineIndent = GetIndentation(location.Line); string indentString = this.curIndent.IndentString; if (location.Column - 1 == lineIndent.Length) { AddChange(document.GetOffset(location.Line, 1), lineIndent.Length, indentString); } else { int offset = document.GetOffset(location); int start = SearchWhitespaceLineStart(offset); if (start > 0) { char ch = document.GetCharAt(start - 1); if (ch == '\n') { start--; if (start > 1 && document.GetCharAt(start - 1) == '\r') { start--; } } else if (ch == '\r') { start--; } AddChange(start, offset - start, this.options.EolMarker + indentString); } } } string GetIndentation(int lineNumber) { IDocumentLine line = document.GetLineByNumber(lineNumber); StringBuilder b = new StringBuilder (); int endOffset = line.EndOffset; for (int i = line.Offset; i < endOffset; i++) { char c = document.GetCharAt(i); if (!IsSpacing(c)) { break; } b.Append(c); } return b.ToString(); } } }