.NET Decompiler with support for PDB generation, ReadyToRun, Metadata (&more) - cross-platform!
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

1659 lines
64 KiB

//
// AstFormattingVisitor.cs
//
// Author:
// Mike Krüger <mkrueger@novell.com>
//
// 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.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 class AstFormattingVisitor : DepthFirstAstVisitor<object, object>
{
CSharpFormattingOptions policy;
IDocument document;
IActionFactory factory;
List<TextReplaceAction> changes = new List<TextReplaceAction> ();
Indent curIndent = new Indent ();
public int IndentLevel {
get {
return curIndent.Level;
}
set {
curIndent.Level = value;
}
}
public int CurrentSpaceIndents {
get;
set;
}
public List<TextReplaceAction> Changes {
get { return this.changes; }
}
public bool CorrectBlankLines {
get;
set;
}
public bool HadErrors {
get;
set;
}
public string EolMarker { get; set; }
public AstFormattingVisitor (CSharpFormattingOptions policy, IDocument document, IActionFactory factory,
bool tabsToSpaces = false, int indentationSize = 4)
{
if (factory == null)
throw new ArgumentNullException ("factory");
this.policy = policy;
this.document = document;
this.curIndent.TabsToSpaces = tabsToSpaces;
this.curIndent.TabSize = indentationSize;
this.factory = factory;
this.EolMarker = Environment.NewLine;
CorrectBlankLines = true;
}
public override object VisitCompilationUnit (CompilationUnit unit, object data)
{
base.VisitCompilationUnit (unit, data);
return null;
}
public void EnsureBlankLinesAfter (AstNode node, int blankLines)
{
if (!CorrectBlankLines)
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.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 (!CorrectBlankLines)
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.EolMarker);
AddChange (start, end - start, sb.ToString ());
}
public override object VisitUsingDeclaration (UsingDeclaration usingDeclaration, object data)
{
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);
return null;
}
public override object VisitUsingAliasDeclaration (UsingAliasDeclaration usingDeclaration, object data)
{
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);
return null;
}
public override object VisitNamespaceDeclaration (NamespaceDeclaration namespaceDeclaration, object data)
{
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)
IndentLevel++;
object result = base.VisitNamespaceDeclaration (namespaceDeclaration, data);
if (policy.IndentNamespaceBody)
IndentLevel--;
FixIndentation (namespaceDeclaration.RBraceToken.StartLocation);
return result;
}
public override object VisitTypeDeclaration (TypeDeclaration typeDeclaration, object data)
{
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)
IndentLevel++;
object result = base.VisitTypeDeclaration (typeDeclaration, data);
if (indentBody)
IndentLevel--;
if (typeDeclaration.NextSibling is TypeDeclaration || typeDeclaration.NextSibling is DelegateDeclaration)
EnsureBlankLinesAfter (typeDeclaration, policy.BlankLinesBetweenTypes);
return result;
}
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;
}
public override object VisitPropertyDeclaration (PropertyDeclaration propertyDeclaration, object data)
{
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)
IndentLevel++;
///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.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 = " ";
}
VisitBlockWithoutFixIndentation (propertyDeclaration.Getter.Body, policy.IndentBlocks, data);
}
}
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.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 = " ";
}
VisitBlockWithoutFixIndentation (propertyDeclaration.Setter.Body, policy.IndentBlocks, data);
}
}
if (policy.IndentPropertyBody)
IndentLevel--;
if (IsMember (propertyDeclaration.NextSibling))
EnsureBlankLinesAfter (propertyDeclaration, policy.BlankLinesBetweenMembers);
return null;
}
public override object VisitIndexerDeclaration (IndexerDeclaration indexerDeclaration, object data)
{
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)
IndentLevel++;
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 = " ";
}
VisitBlockWithoutFixIndentation (indexerDeclaration.Getter.Body, policy.IndentBlocks, data);
}
}
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 = " ";
}
VisitBlockWithoutFixIndentation (indexerDeclaration.Setter.Body, policy.IndentBlocks, data);
}
}
if (policy.IndentPropertyBody)
IndentLevel--;
if (IsMember (indexerDeclaration.NextSibling))
EnsureBlankLinesAfter (indexerDeclaration, policy.BlankLinesBetweenMembers);
return null;
}
static bool IsSimpleEvent (AstNode node)
{
return node is EventDeclaration;
}
public override object VisitCustomEventDeclaration (CustomEventDeclaration eventDeclaration, object data)
{
FormatAttributedNode (eventDeclaration);
EnforceBraceStyle (policy.EventBraceStyle, eventDeclaration.LBraceToken, eventDeclaration.RBraceToken);
if (policy.IndentEventBody)
IndentLevel++;
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 = " ";
}
VisitBlockWithoutFixIndentation (eventDeclaration.AddAccessor.Body, policy.IndentBlocks, data);
}
}
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 = " ";
}
VisitBlockWithoutFixIndentation (eventDeclaration.RemoveAccessor.Body, policy.IndentBlocks, data);
}
}
if (policy.IndentEventBody)
IndentLevel--;
if (eventDeclaration.NextSibling is EventDeclaration && IsSimpleEvent (eventDeclaration) && IsSimpleEvent (eventDeclaration.NextSibling)) {
EnsureBlankLinesAfter (eventDeclaration, policy.BlankLinesBetweenEventFields);
} else if (IsMember (eventDeclaration.NextSibling)) {
EnsureBlankLinesAfter (eventDeclaration, policy.BlankLinesBetweenMembers);
}
return null;
}
public override object VisitEventDeclaration (EventDeclaration eventDeclaration, object data)
{
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);
}
return null;
}
public override object VisitAccessor (Accessor accessor, object data)
{
FixIndentationForceNewLine (accessor.StartLocation);
object result = base.VisitAccessor (accessor, data);
return result;
}
public override object VisitFieldDeclaration (FieldDeclaration fieldDeclaration, object data)
{
FormatAttributedNode (fieldDeclaration);
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);
}
return base.VisitFieldDeclaration (fieldDeclaration, data);
}
public override object VisitFixedFieldDeclaration (FixedFieldDeclaration fixedFieldDeclaration, object data)
{
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);
}
return base.VisitFixedFieldDeclaration (fixedFieldDeclaration, data);
}
public override object VisitEnumMemberDeclaration (EnumMemberDeclaration enumMemberDeclaration, object data)
{
FormatAttributedNode (enumMemberDeclaration);
return base.VisitEnumMemberDeclaration (enumMemberDeclaration, data);
}
public override object VisitDelegateDeclaration (DelegateDeclaration delegateDeclaration, object data)
{
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);
}
return base.VisitDelegateDeclaration (delegateDeclaration, data);
}
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 object VisitMethodDeclaration (MethodDeclaration methodDeclaration, object data)
{
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);
if (policy.IndentMethodBody)
IndentLevel++;
base.VisitBlockStatement (methodDeclaration.Body, data);
if (policy.IndentMethodBody)
IndentLevel--;
}
if (IsMember (methodDeclaration.NextSibling))
EnsureBlankLinesAfter (methodDeclaration, policy.BlankLinesBetweenMembers);
return null;
}
public override object VisitOperatorDeclaration (OperatorDeclaration operatorDeclaration, object data)
{
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);
if (policy.IndentMethodBody)
IndentLevel++;
base.VisitBlockStatement (operatorDeclaration.Body, data);
if (policy.IndentMethodBody)
IndentLevel--;
}
if (IsMember (operatorDeclaration.NextSibling))
EnsureBlankLinesAfter (operatorDeclaration, policy.BlankLinesBetweenMembers);
return null;
}
public override object VisitConstructorDeclaration (ConstructorDeclaration constructorDeclaration, object data)
{
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);
object result = null;
if (!constructorDeclaration.Body.IsNull) {
EnforceBraceStyle (policy.ConstructorBraceStyle, constructorDeclaration.Body.LBraceToken, constructorDeclaration.Body.RBraceToken);
if (policy.IndentMethodBody)
IndentLevel++;
result = base.VisitBlockStatement (constructorDeclaration.Body, data);
if (policy.IndentMethodBody)
IndentLevel--;
}
if (IsMember (constructorDeclaration.NextSibling))
EnsureBlankLinesAfter (constructorDeclaration, policy.BlankLinesBetweenMembers);
return result;
}
public override object VisitDestructorDeclaration (DestructorDeclaration destructorDeclaration, object data)
{
FormatAttributedNode (destructorDeclaration);
CSharpTokenNode lParen = destructorDeclaration.LParToken;
int offset = this.document.GetOffset (lParen.StartLocation);
ForceSpaceBefore (offset, policy.SpaceBeforeConstructorDeclarationParentheses);
object result = null;
if (!destructorDeclaration.Body.IsNull) {
EnforceBraceStyle (policy.DestructorBraceStyle, destructorDeclaration.Body.LBraceToken, destructorDeclaration.Body.RBraceToken);
if (policy.IndentMethodBody)
IndentLevel++;
result = base.VisitBlockStatement (destructorDeclaration.Body, data);
if (policy.IndentMethodBody)
IndentLevel--;
}
if (IsMember (destructorDeclaration.NextSibling))
EnsureBlankLinesAfter (destructorDeclaration, policy.BlankLinesBetweenMembers);
return result;
}
#region Statements
public override object VisitExpressionStatement (ExpressionStatement expressionStatement, object data)
{
FixStatementIndentation (expressionStatement.StartLocation);
FixSemicolon (expressionStatement.SemicolonToken);
return base.VisitExpressionStatement (expressionStatement, data);
}
object VisitBlockWithoutFixIndentation (BlockStatement blockStatement, bool indent, object data)
{
if (indent)
IndentLevel++;
object result = base.VisitBlockStatement (blockStatement, data);
if (indent)
IndentLevel--;
return result;
}
public override object VisitBlockStatement (BlockStatement blockStatement, object data)
{
FixIndentation (blockStatement.StartLocation);
object result = VisitBlockWithoutFixIndentation (blockStatement, policy.IndentBlocks, data);
FixIndentation (blockStatement.EndLocation, -1);
return result;
}
public override object VisitComment (Comment comment, object data)
{
if (comment.StartsLine && !HadErrors && comment.StartLocation.Column > 1)
FixIndentation (comment.StartLocation);
return null;
}
public override object VisitBreakStatement (BreakStatement breakStatement, object data)
{
FixStatementIndentation (breakStatement.StartLocation);
return null;
}
public override object VisitCheckedStatement (CheckedStatement checkedStatement, object data)
{
FixStatementIndentation (checkedStatement.StartLocation);
return FixEmbeddedStatment (policy.StatementBraceStyle, policy.FixedBraceForcement, checkedStatement.Body);
}
public override object VisitContinueStatement (ContinueStatement continueStatement, object data)
{
FixStatementIndentation (continueStatement.StartLocation);
return null;
}
public override object VisitEmptyStatement (EmptyStatement emptyStatement, object data)
{
FixStatementIndentation (emptyStatement.StartLocation);
return null;
}
public override object VisitFixedStatement (FixedStatement fixedStatement, object data)
{
FixStatementIndentation (fixedStatement.StartLocation);
return FixEmbeddedStatment (policy.StatementBraceStyle, policy.FixedBraceForcement, fixedStatement.EmbeddedStatement);
}
public override object VisitForeachStatement (ForeachStatement foreachStatement, object data)
{
FixStatementIndentation (foreachStatement.StartLocation);
ForceSpacesBefore (foreachStatement.LParToken, policy.SpaceBeforeForeachParentheses);
ForceSpacesAfter (foreachStatement.LParToken, policy.SpacesWithinForeachParentheses);
ForceSpacesBefore (foreachStatement.RParToken, policy.SpacesWithinForeachParentheses);
return FixEmbeddedStatment (policy.StatementBraceStyle, policy.ForEachBraceForcement, foreachStatement.EmbeddedStatement);
}
object FixEmbeddedStatment (BraceStyle braceStyle, BraceForcement braceForcement, AstNode node)
{
return FixEmbeddedStatment (braceStyle, braceForcement, null, false, node);
}
object FixEmbeddedStatment (BraceStyle braceStyle, BraceForcement braceForcement, CSharpTokenNode token, bool allowInLine, AstNode node)
{
if (node == null)
return null;
int originalLevel = curIndent.Level;
bool isBlock = node is BlockStatement;
switch (braceForcement) {
case BraceForcement.DoNotChange:
//nothing
break;
case BraceForcement.AddBraces:
if (!isBlock) {
AstNode n = node.Parent.GetCSharpNodeBefore (node);
int start = document.GetOffset (n.EndLocation);
var next = n.GetNextNode ();
int offset = document.GetOffset (next.StartLocation);
string startBrace = "";
switch (braceStyle) {
case BraceStyle.EndOfLineWithoutSpace:
startBrace = "{";
break;
case BraceStyle.EndOfLine:
startBrace = " {";
break;
case BraceStyle.NextLine:
startBrace = this.EolMarker + curIndent.IndentString + "{";
break;
case BraceStyle.NextLineShifted2:
case BraceStyle.NextLineShifted:
startBrace = this.EolMarker + curIndent.IndentString + curIndent.SingleIndent + "{";
break;
}
if (IsLineIsEmptyUpToEol (document.GetOffset (node.StartLocation)))
startBrace += this.EolMarker + GetIndentation (node.StartLocation.Line);
AddChange (start, offset - start, 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);
AddChange (start, offset1 - start + 1, null);
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 {
EnforceBraceStyle (braceStyle, block.LBraceToken, block.RBraceToken);
}
if (braceStyle == BraceStyle.NextLineShifted2)
curIndent.Level++;
} 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.Level++;
object result = isBlock ? base.VisitBlockStatement ((BlockStatement)node, null) : node.AcceptVisitor (this, null);
curIndent.Level = originalLevel;
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.EolMarker + curIndent.IndentString + "}";
break;
case BraceStyle.EndOfLine:
startBrace = this.EolMarker + curIndent.IndentString + "}";
break;
case BraceStyle.NextLine:
startBrace = this.EolMarker + curIndent.IndentString + "}";
break;
case BraceStyle.NextLineShifted2:
case BraceStyle.NextLineShifted:
startBrace = this.EolMarker + curIndent.IndentString + curIndent.SingleIndent + "}";
break;
}
if (startBrace != null)
AddChange (offset, 0, startBrace);
}
break;
}
return result;
}
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.EolMarker + curIndent.IndentString;
break;
case BraceStyle.EndOfLine:
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 = " ";
}
endIndent = IsLineIsEmptyUpToEol (rbraceOffset) ? curIndent.IndentString : this.EolMarker + curIndent.IndentString;
break;
case BraceStyle.NextLine:
startIndent = this.EolMarker + curIndent.IndentString;
endIndent = IsLineIsEmptyUpToEol (rbraceOffset) ? curIndent.IndentString : this.EolMarker + curIndent.IndentString;
break;
case BraceStyle.NextLineShifted2:
case BraceStyle.NextLineShifted:
startIndent = this.EolMarker + curIndent.IndentString + curIndent.SingleIndent;
endIndent = IsLineIsEmptyUpToEol (rbraceOffset) ? curIndent.IndentString + curIndent.SingleIndent : this.EolMarker + curIndent.IndentString + curIndent.SingleIndent;
break;
}
if (lbraceOffset > 0 && startIndent != null)
AddChange (whitespaceStart, lbraceOffset - whitespaceStart, startIndent);
if (rbraceOffset > 0 && endIndent != null)
AddChange (whitespaceEnd, rbraceOffset - whitespaceEnd, endIndent);
}
void AddChange (int offset, int removedChars, string insertedText)
{
if (changes.Any (c => c.Offset == offset && c.RemovedChars == removedChars
&& c.InsertedText == insertedText))
return;
string currentText = document.GetText (offset, removedChars);
if (currentText == insertedText)
return;
if (currentText.Any (c => !(char.IsWhiteSpace (c) || c == '\r' || c == '\t' || c == '{' || c == '}')))
throw new InvalidOperationException ("Tried to remove non ws chars: '" + currentText + "'");
foreach (var change in changes) {
if (change.Offset == offset) {
if (removedChars > 0 && insertedText == change.InsertedText) {
change.RemovedChars = removedChars;
// change.InsertedText = insertedText;
return;
}
if (!string.IsNullOrEmpty (change.InsertedText)) {
change.InsertedText += insertedText;
} else {
change.InsertedText = insertedText;
}
change.RemovedChars = System.Math.Max (removedChars, change.RemovedChars);
return;
}
}
//Console.WriteLine ("offset={0}, removedChars={1}, insertedText={2}", offset, removedChars, insertedText == null ? "<null>" : insertedText.Replace ("\n", "\\n").Replace ("\r", "\\r").Replace ("\t", "\\t").Replace (" ", "."));
//Console.WriteLine (Environment.StackTrace);
changes.Add (factory.CreateTextReplaceAction (offset, removedChars, insertedText));
}
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 object VisitForStatement (ForStatement forStatement, object data)
{
FixStatementIndentation (forStatement.StartLocation);
foreach (AstNode node in forStatement.Children) {
if (node.Role == ForStatement.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);
return FixEmbeddedStatment (policy.StatementBraceStyle, policy.ForBraceForcement, forStatement.EmbeddedStatement);
}
public override object VisitGotoStatement (GotoStatement gotoStatement, object data)
{
FixStatementIndentation (gotoStatement.StartLocation);
return VisitChildren (gotoStatement, data);
}
public override object VisitIfElseStatement (IfElseStatement ifElseStatement, object data)
{
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, data);
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);
}
return null;
}
public override object VisitLabelStatement (LabelStatement labelStatement, object data)
{
// TODO
return VisitChildren (labelStatement, data);
}
public override object VisitLockStatement (LockStatement lockStatement, object data)
{
FixStatementIndentation (lockStatement.StartLocation);
ForceSpacesBefore (lockStatement.LParToken, policy.SpaceBeforeLockParentheses);
ForceSpacesAfter (lockStatement.LParToken, policy.SpacesWithinLockParentheses);
ForceSpacesBefore (lockStatement.RParToken, policy.SpacesWithinLockParentheses);
return FixEmbeddedStatment (policy.StatementBraceStyle, policy.FixedBraceForcement, lockStatement.EmbeddedStatement);
}
public override object VisitReturnStatement (ReturnStatement returnStatement, object data)
{
FixStatementIndentation (returnStatement.StartLocation);
return VisitChildren (returnStatement, data);
}
public override object VisitSwitchStatement (SwitchStatement switchStatement, object data)
{
FixStatementIndentation (switchStatement.StartLocation);
ForceSpacesBefore (switchStatement.LParToken, policy.SpaceBeforeSwitchParentheses);
ForceSpacesAfter (switchStatement.LParToken, policy.SpacesWithinSwitchParentheses);
ForceSpacesBefore (switchStatement.RParToken, policy.SpacesWithinSwitchParentheses);
EnforceBraceStyle (policy.StatementBraceStyle, switchStatement.LBraceToken, switchStatement.RBraceToken);
object result = VisitChildren (switchStatement, data);
return result;
}
public override object VisitSwitchSection (SwitchSection switchSection, object data)
{
if (policy.IndentSwitchBody)
curIndent.Level++;
foreach (CaseLabel label in switchSection.CaseLabels) {
FixStatementIndentation (label.StartLocation);
}
if (policy.IndentCaseBody)
curIndent.Level++;
foreach (var stmt in switchSection.Statements) {
if (stmt is BreakStatement && !policy.IndentBreakStatements && policy.IndentCaseBody) {
curIndent.Level--;
stmt.AcceptVisitor (this, null);
curIndent.Level++;
continue;
}
stmt.AcceptVisitor (this, null);
}
if (policy.IndentCaseBody)
curIndent.Level--;
if (policy.IndentSwitchBody)
curIndent.Level--;
return null;
}
public override object VisitCaseLabel (CaseLabel caseLabel, object data)
{
// handled in switchsection
return null;
}
public override object VisitThrowStatement (ThrowStatement throwStatement, object data)
{
FixStatementIndentation (throwStatement.StartLocation);
return VisitChildren (throwStatement, data);
}
public override object VisitTryCatchStatement (TryCatchStatement tryCatchStatement, object data)
{
FixStatementIndentation (tryCatchStatement.StartLocation);
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);
}
return VisitChildren (tryCatchStatement, data);
}
public override object VisitCatchClause (CatchClause catchClause, object data)
{
// Handled in TryCatchStatement
return null;
}
public override object VisitUncheckedStatement (UncheckedStatement uncheckedStatement, object data)
{
FixStatementIndentation (uncheckedStatement.StartLocation);
return FixEmbeddedStatment (policy.StatementBraceStyle, policy.FixedBraceForcement, uncheckedStatement.Body);
}
public override object VisitUnsafeStatement (UnsafeStatement unsafeStatement, object data)
{
FixStatementIndentation (unsafeStatement.StartLocation);
return FixEmbeddedStatment (policy.StatementBraceStyle, BraceForcement.DoNotChange, unsafeStatement.Body);
}
public override object VisitUsingStatement (UsingStatement usingStatement, object data)
{
FixStatementIndentation (usingStatement.StartLocation);
ForceSpacesBefore (usingStatement.LParToken, policy.SpaceBeforeUsingParentheses);
ForceSpacesAfter (usingStatement.LParToken, policy.SpacesWithinUsingParentheses);
ForceSpacesBefore (usingStatement.RParToken, policy.SpacesWithinUsingParentheses);
return FixEmbeddedStatment (policy.StatementBraceStyle, policy.UsingBraceForcement, usingStatement.EmbeddedStatement);
}
public override object VisitVariableDeclarationStatement (VariableDeclarationStatement variableDeclarationStatement, object data)
{
if (!variableDeclarationStatement.SemicolonToken.IsNull)
FixStatementIndentation (variableDeclarationStatement.StartLocation);
if ((variableDeclarationStatement.Modifiers & Modifiers.Const) == Modifiers.Const) {
ForceSpacesAround (variableDeclarationStatement.Type, true);
} else {
ForceSpacesAfter (variableDeclarationStatement.Type, true);
}
foreach (var initializer in variableDeclarationStatement.Variables) {
initializer.AcceptVisitor (this, data);
}
FormatCommas (variableDeclarationStatement, policy.SpaceBeforeLocalVariableDeclarationComma, policy.SpaceAfterLocalVariableDeclarationComma);
FixSemicolon (variableDeclarationStatement.SemicolonToken);
return null;
}
public override object VisitDoWhileStatement (DoWhileStatement doWhileStatement, object data)
{
FixStatementIndentation (doWhileStatement.StartLocation);
PlaceOnNewLine (policy.PlaceWhileOnNewLine, doWhileStatement.WhileToken);
return FixEmbeddedStatment (policy.StatementBraceStyle, policy.WhileBraceForcement, doWhileStatement.EmbeddedStatement);
}
public override object VisitWhileStatement (WhileStatement whileStatement, object data)
{
FixStatementIndentation (whileStatement.StartLocation);
ForceSpacesBefore (whileStatement.LParToken, policy.SpaceBeforeWhileParentheses);
ForceSpacesAfter (whileStatement.LParToken, policy.SpacesWithinWhileParentheses);
ForceSpacesBefore (whileStatement.RParToken, policy.SpacesWithinWhileParentheses);
return FixEmbeddedStatment (policy.StatementBraceStyle, policy.WhileBraceForcement, whileStatement.EmbeddedStatement);
}
public override object VisitYieldBreakStatement (YieldBreakStatement yieldBreakStatement, object data)
{
FixStatementIndentation (yieldBreakStatement.StartLocation);
return null;
}
public override object VisitYieldReturnStatement (YieldReturnStatement yieldStatement, object data)
{
FixStatementIndentation (yieldStatement.StartLocation);
return null;
}
public override object VisitVariableInitializer (VariableInitializer variableInitializer, object data)
{
if (!variableInitializer.AssignToken.IsNull)
ForceSpacesAround (variableInitializer.AssignToken, policy.SpaceAroundAssignment);
if (!variableInitializer.Initializer.IsNull)
variableInitializer.Initializer.AcceptVisitor (this, data);
return data;
}
#endregion
#region Expressions
public override object VisitComposedType (ComposedType composedType, object data)
{
var spec = composedType.ArraySpecifiers.FirstOrDefault ();
if (spec != null)
ForceSpacesBefore (spec.LBracketToken, policy.SpaceBeforeArrayDeclarationBrackets);
return base.VisitComposedType (composedType, data);
}
public override object VisitAssignmentExpression (AssignmentExpression assignmentExpression, object data)
{
ForceSpacesAround (assignmentExpression.OperatorToken, policy.SpaceAroundAssignment);
return base.VisitAssignmentExpression (assignmentExpression, data);
}
public override object VisitBinaryOperatorExpression (BinaryOperatorExpression binaryOperatorExpression, object data)
{
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);
return base.VisitBinaryOperatorExpression (binaryOperatorExpression, data);
}
public override object VisitConditionalExpression (ConditionalExpression conditionalExpression, object data)
{
ForceSpacesBefore (conditionalExpression.QuestionMarkToken, policy.SpaceBeforeConditionalOperatorCondition);
ForceSpacesAfter (conditionalExpression.QuestionMarkToken, policy.SpaceAfterConditionalOperatorCondition);
ForceSpacesBefore (conditionalExpression.ColonToken, policy.SpaceBeforeConditionalOperatorSeparator);
ForceSpacesAfter (conditionalExpression.ColonToken, policy.SpaceAfterConditionalOperatorSeparator);
return base.VisitConditionalExpression (conditionalExpression, data);
}
public override object VisitCastExpression (CastExpression castExpression, object data)
{
if (castExpression.RParToken != null) {
ForceSpacesAfter (castExpression.LParToken, policy.SpacesWithinCastParentheses);
ForceSpacesBefore (castExpression.RParToken, policy.SpacesWithinCastParentheses);
ForceSpacesAfter (castExpression.RParToken, policy.SpaceAfterTypecast);
}
return base.VisitCastExpression (castExpression, data);
}
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 == FieldDeclaration.Roles.Comma)) {
ForceSpacesAfter (comma, after);
ForceSpacesBefore (comma, before);
}
}
public override object VisitInvocationExpression (InvocationExpression invocationExpression, object data)
{
ForceSpacesBefore (invocationExpression.LParToken, policy.SpaceBeforeMethodCallParentheses);
if (invocationExpression.Arguments.Any ()) {
ForceSpacesAfter (invocationExpression.LParToken, policy.SpaceWithinMethodCallParentheses);
ForceSpacesBefore (invocationExpression.RParToken, policy.SpaceWithinMethodCallParentheses);
} else {
ForceSpacesAfter (invocationExpression.LParToken, policy.SpaceBetweenEmptyMethodCallParentheses);
ForceSpacesBefore (invocationExpression.RParToken, policy.SpaceBetweenEmptyMethodCallParentheses);
}
FormatCommas (invocationExpression, policy.SpaceBeforeMethodCallParameterComma, policy.SpaceAfterMethodCallParameterComma);
return base.VisitInvocationExpression (invocationExpression, data);
}
public override object VisitIndexerExpression (IndexerExpression indexerExpression, object data)
{
ForceSpacesBefore (indexerExpression.LBracketToken, policy.SpacesBeforeBrackets);
ForceSpacesAfter (indexerExpression.LBracketToken, policy.SpacesWithinBrackets);
ForceSpacesBefore (indexerExpression.RBracketToken, policy.SpacesWithinBrackets);
FormatCommas (indexerExpression, policy.SpaceBeforeBracketComma, policy.SpaceAfterBracketComma);
return base.VisitIndexerExpression (indexerExpression, data);
}
public override object VisitParenthesizedExpression (ParenthesizedExpression parenthesizedExpression, object data)
{
ForceSpacesAfter (parenthesizedExpression.LParToken, policy.SpacesWithinParentheses);
ForceSpacesBefore (parenthesizedExpression.RParToken, policy.SpacesWithinParentheses);
return base.VisitParenthesizedExpression (parenthesizedExpression, data);
}
public override object VisitSizeOfExpression (SizeOfExpression sizeOfExpression, object data)
{
ForceSpacesBefore (sizeOfExpression.LParToken, policy.SpaceBeforeSizeOfParentheses);
ForceSpacesAfter (sizeOfExpression.LParToken, policy.SpacesWithinSizeOfParentheses);
ForceSpacesBefore (sizeOfExpression.RParToken, policy.SpacesWithinSizeOfParentheses);
return base.VisitSizeOfExpression (sizeOfExpression, data);
}
public override object VisitTypeOfExpression (TypeOfExpression typeOfExpression, object data)
{
ForceSpacesBefore (typeOfExpression.LParToken, policy.SpaceBeforeTypeOfParentheses);
ForceSpacesAfter (typeOfExpression.LParToken, policy.SpacesWithinTypeOfParentheses);
ForceSpacesBefore (typeOfExpression.RParToken, policy.SpacesWithinTypeOfParentheses);
return base.VisitTypeOfExpression (typeOfExpression, data);
}
public override object VisitCheckedExpression (CheckedExpression checkedExpression, object data)
{
ForceSpacesAfter (checkedExpression.LParToken, policy.SpacesWithinCheckedExpressionParantheses);
ForceSpacesBefore (checkedExpression.RParToken, policy.SpacesWithinCheckedExpressionParantheses);
return base.VisitCheckedExpression (checkedExpression, data);
}
public override object VisitUncheckedExpression (UncheckedExpression uncheckedExpression, object data)
{
ForceSpacesAfter (uncheckedExpression.LParToken, policy.SpacesWithinCheckedExpressionParantheses);
ForceSpacesBefore (uncheckedExpression.RParToken, policy.SpacesWithinCheckedExpressionParantheses);
return base.VisitUncheckedExpression (uncheckedExpression, data);
}
public override object VisitObjectCreateExpression (ObjectCreateExpression objectCreateExpression, object data)
{
ForceSpacesBefore (objectCreateExpression.LParToken, policy.SpaceBeforeNewParentheses);
if (objectCreateExpression.Arguments.Any ()) {
if (!objectCreateExpression.LParToken.IsNull)
ForceSpacesAfter (objectCreateExpression.LParToken, policy.SpacesWithinNewParentheses);
if (!objectCreateExpression.RParToken.IsNull)
ForceSpacesBefore (objectCreateExpression.RParToken, policy.SpacesWithinNewParentheses);
} else {
if (!objectCreateExpression.LParToken.IsNull)
ForceSpacesAfter (objectCreateExpression.LParToken, policy.SpacesBetweenEmptyNewParentheses);
if (!objectCreateExpression.RParToken.IsNull)
ForceSpacesBefore (objectCreateExpression.RParToken, policy.SpacesBetweenEmptyNewParentheses);
}
FormatCommas (objectCreateExpression, policy.SpaceBeforeNewParameterComma, policy.SpaceAfterNewParameterComma);
return base.VisitObjectCreateExpression (objectCreateExpression, data);
}
public override object VisitArrayCreateExpression (ArrayCreateExpression arrayObjectCreateExpression, object data)
{
FormatCommas (arrayObjectCreateExpression, policy.SpaceBeforeMethodCallParameterComma, policy.SpaceAfterMethodCallParameterComma);
return base.VisitArrayCreateExpression (arrayObjectCreateExpression, data);
}
public override object VisitLambdaExpression (LambdaExpression lambdaExpression, object data)
{
ForceSpacesAfter (lambdaExpression.ArrowToken, true);
ForceSpacesBefore (lambdaExpression.ArrowToken, true);
return base.VisitLambdaExpression (lambdaExpression, data);
}
#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.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.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.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();
}
}
}