diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpInsightItem.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpInsightItem.cs
index 0aeaa92216..3c6328355f 100644
--- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpInsightItem.cs
+++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpInsightItem.cs
@@ -63,7 +63,7 @@ namespace CSharpBinding.Completion
get { return null; }
}
- sealed class ParameterHighlightingOutputFormatter : TextWriterOutputFormatter
+ sealed class ParameterHighlightingOutputFormatter : TextWriterTokenWriter
{
StringBuilder b;
int highlightedParameterIndex;
diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/SegmentTrackingOutputFormatter.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/SegmentTrackingOutputFormatter.cs
index ba90bffa24..d0cbc4d054 100644
--- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/SegmentTrackingOutputFormatter.cs
+++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/SegmentTrackingOutputFormatter.cs
@@ -15,7 +15,7 @@ namespace CSharpBinding.Completion
///
/// Output formatter that creates a dictionary from AST nodes to segments in the output text.
///
- public class SegmentTrackingOutputFormatter : TextWriterOutputFormatter
+ public class SegmentTrackingOutputFormatter : TextWriterTokenWriter
{
Dictionary segments = new Dictionary();
Stack startOffsets = new Stack();
diff --git a/src/AddIns/Debugger/Debugger.AddIn/NRefactory/ExpressionEvaluationVisitor.cs b/src/AddIns/Debugger/Debugger.AddIn/NRefactory/ExpressionEvaluationVisitor.cs
index 37083cacf1..3706a68490 100644
--- a/src/AddIns/Debugger/Debugger.AddIn/NRefactory/ExpressionEvaluationVisitor.cs
+++ b/src/AddIns/Debugger/Debugger.AddIn/NRefactory/ExpressionEvaluationVisitor.cs
@@ -424,11 +424,11 @@ namespace Debugger.AddIn
sb.Append("}");
return sb.ToString();
} else if (val.Type.IsKnownType(KnownTypeCode.Char)) {
- return "'" + CSharpOutputVisitor.ConvertChar((char)val.PrimitiveValue) + "'";
+ return "'" + TextWriterTokenWriter.ConvertChar((char)val.PrimitiveValue) + "'";
} else if (val.Type.IsKnownType(KnownTypeCode.String)) {
- return "\"" + CSharpOutputVisitor.ConvertString((string)val.PrimitiveValue) + "\"";
+ return "\"" + TextWriterTokenWriter.ConvertString((string)val.PrimitiveValue) + "\"";
} else if (val.Type.IsPrimitiveType()) {
- return CSharpOutputVisitor.PrintPrimitiveValue(val.PrimitiveValue);
+ return TextWriterTokenWriter.PrintPrimitiveValue(val.PrimitiveValue);
} else {
return val.InvokeToString(evalThread);
}
diff --git a/src/Libraries/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/src/Libraries/ICSharpCode.Decompiler/Ast/AstBuilder.cs
index b915fc5040..4a41825ca9 100644
--- a/src/Libraries/ICSharpCode.Decompiler/Ast/AstBuilder.cs
+++ b/src/Libraries/ICSharpCode.Decompiler/Ast/AstBuilder.cs
@@ -150,6 +150,13 @@ namespace ICSharpCode.Decompiler.Ast
get { return syntaxTree; }
}
+ ///
+ /// Gets the context used by this AstBuilder.
+ ///
+ public DecompilerContext Context {
+ get { return context; }
+ }
+
///
/// Generates C# code from the abstract source tree.
///
@@ -160,7 +167,7 @@ namespace ICSharpCode.Decompiler.Ast
RunTransformations();
syntaxTree.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = true });
- var outputFormatter = new TextOutputFormatter(output) { FoldBraces = context.Settings.FoldBraces };
+ var outputFormatter = new TextTokenWriter(output) { FoldBraces = context.Settings.FoldBraces };
var formattingPolicy = context.Settings.CSharpFormattingOptions;
syntaxTree.AcceptVisitor(new CSharpOutputVisitor(outputFormatter, formattingPolicy));
}
diff --git a/src/Libraries/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs b/src/Libraries/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs
index 613ac1c582..08da545bce 100644
--- a/src/Libraries/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs
+++ b/src/Libraries/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs
@@ -27,7 +27,7 @@ using Mono.Cecil;
namespace ICSharpCode.Decompiler.Ast
{
- public class TextOutputFormatter : IOutputFormatter
+ public class TextTokenWriter : ITokenWriter
{
readonly ITextOutput output;
readonly Stack nodeStack = new Stack();
@@ -40,37 +40,37 @@ namespace ICSharpCode.Decompiler.Ast
public bool FoldBraces = false;
- public TextOutputFormatter(ITextOutput output)
+ public TextTokenWriter(ITextOutput output)
{
if (output == null)
throw new ArgumentNullException("output");
this.output = output;
}
- public void WriteIdentifier(string identifier)
+ public void WriteIdentifier(Identifier identifier)
{
var definition = GetCurrentDefinition();
if (definition != null) {
- output.WriteDefinition(identifier, definition, false);
+ output.WriteDefinition(identifier.Name, definition, false);
return;
}
object memberRef = GetCurrentMemberReference();
if (memberRef != null) {
- output.WriteReference(identifier, memberRef);
+ output.WriteReference(identifier.Name, memberRef);
return;
}
definition = GetCurrentLocalDefinition();
if (definition != null) {
- output.WriteDefinition(identifier, definition);
+ output.WriteDefinition(identifier.Name, definition);
return;
}
memberRef = GetCurrentLocalReference();
if (memberRef != null) {
- output.WriteReference(identifier, memberRef, true);
+ output.WriteReference(identifier.Name, memberRef, true);
return;
}
@@ -79,7 +79,7 @@ namespace ICSharpCode.Decompiler.Ast
firstUsingDeclaration = false;
}
- output.Write(identifier);
+ output.Write(identifier.Name);
}
MemberReference GetCurrentMemberReference()
@@ -160,12 +160,12 @@ namespace ICSharpCode.Decompiler.Ast
return null;
}
- public void WriteKeyword(string keyword)
+ public void WriteKeyword(Role role, string keyword)
{
output.Write(keyword);
}
- public void WriteToken(string token)
+ public void WriteToken(Role role, string token)
{
// Attach member reference to token only if there's no identifier in the current node.
MemberReference memberRef = GetCurrentMemberReference();
@@ -267,6 +267,11 @@ namespace ICSharpCode.Decompiler.Ast
output.WriteLine();
}
+ public void WritePrimitiveValue(object value)
+ {
+
+ }
+
Stack startLocations = new Stack();
Stack symbolsStack = new Stack();
diff --git a/src/Libraries/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs b/src/Libraries/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs
index 63567c71ff..4c2080045e 100644
--- a/src/Libraries/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs
+++ b/src/Libraries/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs
@@ -222,7 +222,7 @@ namespace ICSharpCode.Decompiler.Disassembler
} else {
// The ECMA specification says that ' inside SQString should be ecaped using an octal escape sequence,
// but we follow Microsoft's ILDasm and use \'.
- return "'" + NRefactory.CSharp.CSharpOutputVisitor.ConvertString(identifier).Replace("'", "\\'") + "'";
+ return "'" + NRefactory.CSharp.TextWriterTokenWriter.ConvertString(identifier).Replace("'", "\\'") + "'";
}
}
@@ -353,7 +353,7 @@ namespace ICSharpCode.Decompiler.Disassembler
string s = operand as string;
if (s != null) {
- writer.Write("\"" + NRefactory.CSharp.CSharpOutputVisitor.ConvertString(s) + "\"");
+ writer.Write("\"" + NRefactory.CSharp.TextWriterTokenWriter.ConvertString(s) + "\"");
} else if (operand is char) {
writer.Write(((int)(char)operand).ToString());
} else if (operand is float) {
diff --git a/src/Libraries/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs b/src/Libraries/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs
index ec20cfa72a..1a7d1a4f71 100644
--- a/src/Libraries/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs
+++ b/src/Libraries/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs
@@ -126,10 +126,10 @@ namespace ICSharpCode.Decompiler.Disassembler
output.Write("pinvokeimpl");
if (method.HasPInvokeInfo && method.PInvokeInfo != null) {
PInvokeInfo info = method.PInvokeInfo;
- output.Write("(\"" + NRefactory.CSharp.CSharpOutputVisitor.ConvertString(info.Module.Name) + "\"");
+ output.Write("(\"" + NRefactory.CSharp.TextWriterTokenWriter.ConvertString(info.Module.Name) + "\"");
if (!string.IsNullOrEmpty(info.EntryPoint) && info.EntryPoint != method.Name)
- output.Write(" as \"" + NRefactory.CSharp.CSharpOutputVisitor.ConvertString(info.EntryPoint) + "\"");
+ output.Write(" as \"" + NRefactory.CSharp.TextWriterTokenWriter.ConvertString(info.EntryPoint) + "\"");
if (info.IsNoMangle)
output.Write(" nomangle");
@@ -346,7 +346,7 @@ namespace ICSharpCode.Decompiler.Disassembler
output.Write(" = ");
if (na.Argument.Value is string) {
// secdecls use special syntax for strings
- output.Write("string('{0}')", NRefactory.CSharp.CSharpOutputVisitor.ConvertString((string)na.Argument.Value).Replace("'", "\'"));
+ output.Write("string('{0}')", NRefactory.CSharp.TextWriterTokenWriter.ConvertString((string)na.Argument.Value).Replace("'", "\'"));
} else {
WriteConstant(na.Argument.Value);
}
@@ -574,10 +574,10 @@ namespace ICSharpCode.Decompiler.Disassembler
if (cmi == null)
goto default;
output.Write("custom(\"{0}\", \"{1}\"",
- NRefactory.CSharp.CSharpOutputVisitor.ConvertString(cmi.ManagedType.FullName),
- NRefactory.CSharp.CSharpOutputVisitor.ConvertString(cmi.Cookie));
+ NRefactory.CSharp.TextWriterTokenWriter.ConvertString(cmi.ManagedType.FullName),
+ NRefactory.CSharp.TextWriterTokenWriter.ConvertString(cmi.Cookie));
if (cmi.Guid != Guid.Empty || !string.IsNullOrEmpty(cmi.UnmanagedType)) {
- output.Write(", \"{0}\", \"{1}\"", cmi.Guid.ToString(), NRefactory.CSharp.CSharpOutputVisitor.ConvertString(cmi.UnmanagedType));
+ output.Write(", \"{0}\", \"{1}\"", cmi.Guid.ToString(), NRefactory.CSharp.TextWriterTokenWriter.ConvertString(cmi.UnmanagedType));
}
output.Write(')');
break;
diff --git a/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj b/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj
index aa3954d20b..fbc4919866 100644
--- a/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj
+++ b/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj
@@ -208,6 +208,7 @@
+
diff --git a/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/CSharpAmbience.cs b/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/CSharpAmbience.cs
index 8d7522529b..6c150c72a9 100644
--- a/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/CSharpAmbience.cs
+++ b/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/CSharpAmbience.cs
@@ -37,98 +37,98 @@ namespace ICSharpCode.NRefactory.CSharp
throw new ArgumentNullException("entity");
StringWriter writer = new StringWriter();
- ConvertEntity(entity, new TextWriterOutputFormatter(writer), FormattingOptionsFactory.CreateMono ());
+ ConvertEntity(entity, new TextWriterTokenWriter(writer), FormattingOptionsFactory.CreateMono ());
return writer.ToString();
}
- public void ConvertEntity(IEntity entity, IOutputFormatter formatter, CSharpFormattingOptions formattingPolicy)
+ public void ConvertEntity(IEntity entity, ITokenWriter writer, CSharpFormattingOptions formattingPolicy)
{
if (entity == null)
throw new ArgumentNullException("entity");
- if (formatter == null)
- throw new ArgumentNullException("formatter");
+ if (writer == null)
+ throw new ArgumentNullException("writer");
if (formattingPolicy == null)
throw new ArgumentNullException("options");
TypeSystemAstBuilder astBuilder = CreateAstBuilder();
EntityDeclaration node = astBuilder.ConvertEntity(entity);
- PrintModifiers(node.Modifiers, formatter);
+ PrintModifiers(node.Modifiers, writer);
if ((ConversionFlags & ConversionFlags.ShowDefinitionKeyword) == ConversionFlags.ShowDefinitionKeyword) {
if (node is TypeDeclaration) {
switch (((TypeDeclaration)node).ClassType) {
case ClassType.Class:
- formatter.WriteKeyword("class");
+ writer.WriteKeyword(Roles.ClassKeyword, "class");
break;
case ClassType.Struct:
- formatter.WriteKeyword("struct");
+ writer.WriteKeyword(Roles.StructKeyword, "struct");
break;
case ClassType.Interface:
- formatter.WriteKeyword("interface");
+ writer.WriteKeyword(Roles.InterfaceKeyword, "interface");
break;
case ClassType.Enum:
- formatter.WriteKeyword("enum");
+ writer.WriteKeyword(Roles.EnumKeyword, "enum");
break;
default:
throw new Exception("Invalid value for ClassType");
}
- formatter.Space();
+ writer.Space();
} else if (node is DelegateDeclaration) {
- formatter.WriteKeyword("delegate");
- formatter.Space();
+ writer.WriteKeyword(Roles.DelegateKeyword, "delegate");
+ writer.Space();
} else if (node is EventDeclaration) {
- formatter.WriteKeyword("event");
- formatter.Space();
+ writer.WriteKeyword(EventDeclaration.EventKeywordRole, "event");
+ writer.Space();
}
}
if ((ConversionFlags & ConversionFlags.ShowReturnType) == ConversionFlags.ShowReturnType) {
var rt = node.GetChildByRole(Roles.Type);
if (!rt.IsNull) {
- rt.AcceptVisitor(new CSharpOutputVisitor(formatter, formattingPolicy));
- formatter.Space();
+ rt.AcceptVisitor(new CSharpOutputVisitor(writer, formattingPolicy));
+ writer.Space();
}
}
if (entity is ITypeDefinition)
- WriteTypeDeclarationName((ITypeDefinition)entity, formatter, formattingPolicy);
+ WriteTypeDeclarationName((ITypeDefinition)entity, writer, formattingPolicy);
else
- WriteMemberDeclarationName((IMember)entity, formatter, formattingPolicy);
+ WriteMemberDeclarationName((IMember)entity, writer, formattingPolicy);
if ((ConversionFlags & ConversionFlags.ShowParameterList) == ConversionFlags.ShowParameterList && HasParameters(entity)) {
- formatter.WriteToken(entity.SymbolKind == SymbolKind.Indexer ? "[" : "(");
+ writer.WriteToken(entity.SymbolKind == SymbolKind.Indexer ? Roles.LBracket : Roles.LPar, entity.SymbolKind == SymbolKind.Indexer ? "[" : "(");
bool first = true;
foreach (var param in node.GetChildrenByRole(Roles.Parameter)) {
if (first) {
first = false;
} else {
- formatter.WriteToken(",");
- formatter.Space();
+ writer.WriteToken(Roles.Comma, ",");
+ writer.Space();
}
- param.AcceptVisitor(new CSharpOutputVisitor(formatter, formattingPolicy));
+ param.AcceptVisitor(new CSharpOutputVisitor(writer, formattingPolicy));
}
- formatter.WriteToken(entity.SymbolKind == SymbolKind.Indexer ? "]" : ")");
+ writer.WriteToken(entity.SymbolKind == SymbolKind.Indexer ? Roles.RBracket : Roles.RPar, entity.SymbolKind == SymbolKind.Indexer ? "]" : ")");
}
if ((ConversionFlags & ConversionFlags.ShowBody) == ConversionFlags.ShowBody && !(node is TypeDeclaration)) {
IProperty property = entity as IProperty;
if (property != null) {
- formatter.Space();
- formatter.WriteToken("{");
- formatter.Space();
+ writer.Space();
+ writer.WriteToken(Roles.LBrace, "{");
+ writer.Space();
if (property.CanGet) {
- formatter.WriteKeyword("get");
- formatter.WriteToken(";");
- formatter.Space();
+ writer.WriteKeyword(PropertyDeclaration.GetKeywordRole, "get");
+ writer.WriteToken(Roles.Semicolon, ";");
+ writer.Space();
}
if (property.CanSet) {
- formatter.WriteKeyword("set");
- formatter.WriteToken(";");
- formatter.Space();
+ writer.WriteKeyword(PropertyDeclaration.SetKeywordRole, "set");
+ writer.WriteToken(Roles.Semicolon, ";");
+ writer.Space();
}
- formatter.WriteToken("}");
+ writer.WriteToken(Roles.RBrace, "}");
} else {
- formatter.WriteToken(";");
+ writer.WriteToken(Roles.Semicolon, ";");
}
}
}
@@ -160,87 +160,97 @@ namespace ICSharpCode.NRefactory.CSharp
return astBuilder;
}
- void WriteTypeDeclarationName(ITypeDefinition typeDef, IOutputFormatter formatter, CSharpFormattingOptions formattingPolicy)
+ void WriteTypeDeclarationName(ITypeDefinition typeDef, ITokenWriter writer, CSharpFormattingOptions formattingPolicy)
{
TypeSystemAstBuilder astBuilder = CreateAstBuilder();
+ TypeDeclaration node = (TypeDeclaration)astBuilder.ConvertEntity(typeDef);
if (typeDef.DeclaringTypeDefinition != null) {
- WriteTypeDeclarationName(typeDef.DeclaringTypeDefinition, formatter, formattingPolicy);
- formatter.WriteToken(".");
+ WriteTypeDeclarationName(typeDef.DeclaringTypeDefinition, writer, formattingPolicy);
+ writer.WriteToken(Roles.Dot, ".");
} else if ((ConversionFlags & ConversionFlags.UseFullyQualifiedTypeNames) == ConversionFlags.UseFullyQualifiedTypeNames) {
- formatter.WriteIdentifier(typeDef.Namespace);
- formatter.WriteToken(".");
+ WriteQualifiedName(typeDef.Namespace, writer, formattingPolicy);
+ writer.WriteToken(Roles.Dot, ".");
}
- formatter.WriteIdentifier(typeDef.Name);
+ writer.WriteIdentifier(node.NameToken);
if ((ConversionFlags & ConversionFlags.ShowTypeParameterList) == ConversionFlags.ShowTypeParameterList) {
- var outputVisitor = new CSharpOutputVisitor(formatter, formattingPolicy);
- outputVisitor.WriteTypeParameters(astBuilder.ConvertEntity(typeDef).GetChildrenByRole(Roles.TypeParameter));
+ var outputVisitor = new CSharpOutputVisitor(writer, formattingPolicy);
+ outputVisitor.WriteTypeParameters(node.GetChildrenByRole(Roles.TypeParameter));
}
}
- void WriteMemberDeclarationName(IMember member, IOutputFormatter formatter, CSharpFormattingOptions formattingPolicy)
+ void WriteMemberDeclarationName(IMember member, ITokenWriter writer, CSharpFormattingOptions formattingPolicy)
{
TypeSystemAstBuilder astBuilder = CreateAstBuilder();
+ EntityDeclaration node = astBuilder.ConvertEntity(member);
+ TypeDeclaration typeDecl = (TypeDeclaration)astBuilder.ConvertEntity(member.DeclaringTypeDefinition);
if ((ConversionFlags & ConversionFlags.ShowDeclaringType) == ConversionFlags.ShowDeclaringType) {
- ConvertType(member.DeclaringType, formatter, formattingPolicy);
- formatter.WriteToken(".");
+ ConvertType(member.DeclaringType, writer, formattingPolicy);
+ writer.WriteToken(Roles.Dot, ".");
}
switch (member.SymbolKind) {
case SymbolKind.Indexer:
- formatter.WriteKeyword("this");
+ writer.WriteKeyword(Roles.Identifier, "this");
break;
case SymbolKind.Constructor:
- formatter.WriteIdentifier(member.DeclaringType.Name);
+ WriteQualifiedName(member.DeclaringType.Name, writer, formattingPolicy);
break;
case SymbolKind.Destructor:
- formatter.WriteToken("~");
- formatter.WriteIdentifier(member.DeclaringType.Name);
+ writer.WriteToken(DestructorDeclaration.TildeRole, "~");
+ WriteQualifiedName(member.DeclaringType.Name, writer, formattingPolicy);
break;
case SymbolKind.Operator:
switch (member.Name) {
case "op_Implicit":
- formatter.WriteKeyword("implicit");
- formatter.Space();
- formatter.WriteKeyword("operator");
- formatter.Space();
- ConvertType(member.ReturnType, formatter, formattingPolicy);
+ writer.WriteKeyword(OperatorDeclaration.ImplicitRole, "implicit");
+ writer.Space();
+ writer.WriteKeyword(OperatorDeclaration.OperatorKeywordRole, "operator");
+ writer.Space();
+ ConvertType(member.ReturnType, writer, formattingPolicy);
break;
case "op_Explicit":
- formatter.WriteKeyword("explicit");
- formatter.Space();
- formatter.WriteKeyword("operator");
- formatter.Space();
- ConvertType(member.ReturnType, formatter, formattingPolicy);
+ writer.WriteKeyword(OperatorDeclaration.ExplicitRole, "explicit");
+ writer.Space();
+ writer.WriteKeyword(OperatorDeclaration.OperatorKeywordRole, "operator");
+ writer.Space();
+ ConvertType(member.ReturnType, writer, formattingPolicy);
break;
default:
- formatter.WriteKeyword("operator");
- formatter.Space();
+ writer.WriteKeyword(OperatorDeclaration.OperatorKeywordRole, "operator");
+ writer.Space();
var operatorType = OperatorDeclaration.GetOperatorType(member.Name);
if (operatorType.HasValue)
- formatter.WriteToken(OperatorDeclaration.GetToken(operatorType.Value));
+ writer.WriteToken(OperatorDeclaration.GetRole(operatorType.Value), OperatorDeclaration.GetToken(operatorType.Value));
else
- formatter.WriteIdentifier(member.Name);
+ writer.WriteIdentifier(node.NameToken);
break;
}
break;
default:
- formatter.WriteIdentifier(member.Name);
+ writer.WriteIdentifier(node.NameToken);
break;
}
if ((ConversionFlags & ConversionFlags.ShowTypeParameterList) == ConversionFlags.ShowTypeParameterList && member.SymbolKind == SymbolKind.Method) {
- var outputVisitor = new CSharpOutputVisitor(formatter, formattingPolicy);
- outputVisitor.WriteTypeParameters(astBuilder.ConvertEntity(member).GetChildrenByRole(Roles.TypeParameter));
+ var outputVisitor = new CSharpOutputVisitor(writer, formattingPolicy);
+ outputVisitor.WriteTypeParameters(node.GetChildrenByRole(Roles.TypeParameter));
}
}
- void PrintModifiers(Modifiers modifiers, IOutputFormatter formatter)
+ void PrintModifiers(Modifiers modifiers, ITokenWriter writer)
{
foreach (var m in CSharpModifierToken.AllModifiers) {
if ((modifiers & m) == m) {
- formatter.WriteKeyword(CSharpModifierToken.GetModifierName(m));
- formatter.Space();
+ writer.WriteKeyword(EntityDeclaration.ModifierRole, CSharpModifierToken.GetModifierName(m));
+ writer.Space();
}
}
}
+
+ void WriteQualifiedName(string name, ITokenWriter writer, CSharpFormattingOptions formattingPolicy)
+ {
+ var node = AstType.Create(name);
+ var outputVisitor = new CSharpOutputVisitor(writer, formattingPolicy);
+ node.AcceptVisitor(outputVisitor);
+ }
#endregion
public string ConvertVariable(IVariable v)
@@ -260,16 +270,16 @@ namespace ICSharpCode.NRefactory.CSharp
return astType.ToString();
}
- public void ConvertType(IType type, IOutputFormatter formatter, CSharpFormattingOptions formattingPolicy)
+ public void ConvertType(IType type, ITokenWriter writer, CSharpFormattingOptions formattingPolicy)
{
TypeSystemAstBuilder astBuilder = CreateAstBuilder();
AstType astType = astBuilder.ConvertType(type);
- astType.AcceptVisitor(new CSharpOutputVisitor(formatter, formattingPolicy));
+ astType.AcceptVisitor(new CSharpOutputVisitor(writer, formattingPolicy));
}
public string ConvertConstantValue(object constantValue)
{
- return CSharpOutputVisitor.PrintPrimitiveValue(constantValue);
+ return TextWriterTokenWriter.PrintPrimitiveValue(constantValue);
}
public string WrapComment(string comment)
diff --git a/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/CSharpOutputVisitor.cs b/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/CSharpOutputVisitor.cs
index a669d624dd..ee72a53c80 100644
--- a/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/CSharpOutputVisitor.cs
+++ b/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/CSharpOutputVisitor.cs
@@ -25,6 +25,7 @@ using System.Text;
using System.Threading.Tasks;
using ICSharpCode.NRefactory.PatternMatching;
using ICSharpCode.NRefactory.TypeSystem;
+using ICSharpCode.NRefactory.CSharp;
namespace ICSharpCode.NRefactory.CSharp
{
@@ -33,27 +34,9 @@ namespace ICSharpCode.NRefactory.CSharp
///
public class CSharpOutputVisitor : IAstVisitor
{
- readonly IOutputFormatter formatter;
+ readonly ITokenWriter writer;
readonly CSharpFormattingOptions policy;
readonly Stack containerStack = new Stack ();
- readonly Stack positionStack = new Stack ();
-
- ///
- /// Used to insert the minimal amount of spaces so that the lexer recognizes the tokens that were written.
- ///
- LastWritten lastWritten;
-
- enum LastWritten
- {
- Whitespace,
- Other,
- KeywordOrIdentifier,
- Plus,
- Minus,
- Ampersand,
- QuestionMark,
- Division
- }
public CSharpOutputVisitor (TextWriter textWriter, CSharpFormattingOptions formattingPolicy)
{
@@ -63,19 +46,19 @@ namespace ICSharpCode.NRefactory.CSharp
if (formattingPolicy == null) {
throw new ArgumentNullException ("formattingPolicy");
}
- this.formatter = new TextWriterOutputFormatter (textWriter);
+ this.writer = new InsertRequiredSpacesDecorator(new TextWriterTokenWriter(textWriter));
this.policy = formattingPolicy;
}
- public CSharpOutputVisitor (IOutputFormatter formatter, CSharpFormattingOptions formattingPolicy)
+ public CSharpOutputVisitor (ITokenWriter writer, CSharpFormattingOptions formattingPolicy)
{
- if (formatter == null) {
- throw new ArgumentNullException ("formatter");
+ if (writer == null) {
+ throw new ArgumentNullException ("writer");
}
if (formattingPolicy == null) {
throw new ArgumentNullException ("formattingPolicy");
}
- this.formatter = formatter;
+ this.writer = new InsertRequiredSpacesDecorator(writer);
this.policy = formattingPolicy;
}
@@ -85,84 +68,15 @@ namespace ICSharpCode.NRefactory.CSharp
// Ensure that nodes are visited in the proper nested order.
// Jumps to different subtrees are allowed only for the child of a placeholder node.
Debug.Assert(containerStack.Count == 0 || node.Parent == containerStack.Peek() || containerStack.Peek().NodeType == NodeType.Pattern);
- if (positionStack.Count > 0) {
- WriteSpecialsUpToNode(node);
- }
containerStack.Push(node);
- positionStack.Push(node.FirstChild);
- formatter.StartNode(node);
+ writer.StartNode(node);
}
void EndNode(AstNode node)
{
Debug.Assert(node == containerStack.Peek());
- AstNode pos = positionStack.Pop();
- Debug.Assert(pos == null || pos.Parent == node);
- WriteSpecials(pos, null);
containerStack.Pop();
- formatter.EndNode(node);
- }
- #endregion
-
- #region WriteSpecials
- ///
- /// Writes all specials from start to end (exclusive). Does not touch the positionStack.
- ///
- void WriteSpecials(AstNode start, AstNode end)
- {
- for (AstNode pos = start; pos != end; pos = pos.NextSibling) {
- if (pos.Role == Roles.Comment || pos.Role == Roles.NewLine || pos.Role == Roles.PreProcessorDirective) {
- pos.AcceptVisitor(this);
- }
- }
- }
-
- ///
- /// Writes all specials between the current position (in the positionStack) and the next
- /// node with the specified role. Advances the current position.
- ///
- void WriteSpecialsUpToRole(Role role)
- {
- WriteSpecialsUpToRole(role, null);
- }
-
- void WriteSpecialsUpToRole(Role role, AstNode nextNode)
- {
- if (positionStack.Count == 0) {
- return;
- }
- // Look for the role between the current position and the nextNode.
- for (AstNode pos = positionStack.Peek(); pos != null && pos != nextNode; pos = pos.NextSibling) {
- if (pos.Role == role) {
- WriteSpecials(positionStack.Pop(), pos);
- // Push the next sibling because the node matching the role is not a special,
- // and should be considered to be already handled.
- positionStack.Push(pos.NextSibling);
- // This is necessary for OptionalComma() to work correctly.
- break;
- }
- }
- }
-
- ///
- /// Writes all specials between the current position (in the positionStack) and the specified node.
- /// Advances the current position.
- ///
- void WriteSpecialsUpToNode(AstNode node)
- {
- if (positionStack.Count == 0) {
- return;
- }
- for (AstNode pos = positionStack.Peek(); pos != null; pos = pos.NextSibling) {
- if (pos == node) {
- WriteSpecials(positionStack.Pop(), pos);
- // Push the next sibling because the node itself is not a special,
- // and should be considered to be already handled.
- positionStack.Push(pos.NextSibling);
- // This is necessary for OptionalComma() to work correctly.
- break;
- }
- }
+ writer.EndNode(node);
}
#endregion
@@ -174,11 +88,9 @@ namespace ICSharpCode.NRefactory.CSharp
/// When set prevents printing a space after comma.
void Comma(AstNode nextNode, bool noSpaceAfterComma = false)
{
- WriteSpecialsUpToRole(Roles.Comma, nextNode);
Space(policy.SpaceBeforeBracketComma);
// TODO: Comma policy has changed.
- formatter.WriteToken(",");
- lastWritten = LastWritten.Other;
+ writer.WriteToken(Roles.Comma, ",");
Space(!noSpaceAfterComma && policy.SpaceAfterBracketComma);
// TODO: Comma policy has changed.
}
@@ -186,10 +98,9 @@ namespace ICSharpCode.NRefactory.CSharp
///
/// Writes an optional comma, e.g. at the end of an enum declaration or in an array initializer
///
- void OptionalComma()
+ void OptionalComma(AstNode pos)
{
// Look if there's a comma after the current node, and insert it if it exists.
- AstNode pos = positionStack.Peek();
while (pos != null && pos.NodeType == NodeType.Whitespace) {
pos = pos.NextSibling;
}
@@ -201,12 +112,11 @@ namespace ICSharpCode.NRefactory.CSharp
///
/// Writes an optional semicolon, e.g. at the end of a type or namespace declaration.
///
- void OptionalSemicolon()
+ void OptionalSemicolon(AstNode pos)
{
// Look if there's a semicolon after the current node, and insert it if it exists.
- AstNode pos = positionStack.Peek();
while (pos != null && pos.NodeType == NodeType.Whitespace) {
- pos = pos.NextSibling;
+ pos = pos.PrevSibling;
}
if (pos != null && pos.Role == Roles.Semicolon) {
Semicolon();
@@ -284,6 +194,8 @@ namespace ICSharpCode.NRefactory.CSharp
#endregion
#region Write tokens
+ bool isAtStartOfLine = true;
+
///
/// Writes a keyword, and all specials up to
///
@@ -294,40 +206,20 @@ namespace ICSharpCode.NRefactory.CSharp
void WriteKeyword(string token, Role tokenRole = null)
{
- if (tokenRole != null) {
- WriteSpecialsUpToRole(tokenRole);
- }
- if (lastWritten == LastWritten.KeywordOrIdentifier) {
- formatter.Space();
- }
- formatter.WriteKeyword(token);
- lastWritten = LastWritten.KeywordOrIdentifier;
+ writer.WriteKeyword(tokenRole, token);
+ isAtStartOfLine = false;
}
-/* void WriteKeyword (string keyword, Role tokenRole)
+ void WriteIdentifier(Identifier identifier)
{
- WriteSpecialsUpToRole (tokenRole);
- if (lastWritten == LastWritten.KeywordOrIdentifier)
- formatter.Space ();
- formatter.WriteKeyword (keyword);
- lastWritten = LastWritten.KeywordOrIdentifier;
- }*/
+ writer.WriteIdentifier(identifier);
+ isAtStartOfLine = false;
+ }
- void WriteIdentifier(string identifier, Role identifierRole = null)
+ void WriteIdentifier(string identifier)
{
- WriteSpecialsUpToRole(identifierRole ?? Roles.Identifier);
- if (IsKeyword(identifier, containerStack.Peek())) {
- if (lastWritten == LastWritten.KeywordOrIdentifier) {
- Space();
- }
- // this space is not strictly required, so we call Space()
- formatter.WriteToken("@");
- } else if (lastWritten == LastWritten.KeywordOrIdentifier) {
- formatter.Space();
- // this space is strictly required, so we directly call the formatter
- }
- formatter.WriteIdentifier(identifier);
- lastWritten = LastWritten.KeywordOrIdentifier;
+ AstType.Create(identifier).AcceptVisitor(this);
+ isAtStartOfLine = false;
}
void WriteToken(TokenRole tokenRole)
@@ -337,34 +229,8 @@ namespace ICSharpCode.NRefactory.CSharp
void WriteToken(string token, Role tokenRole)
{
- WriteSpecialsUpToRole(tokenRole);
- // Avoid that two +, - or ? tokens are combined into a ++, -- or ?? token.
- // Note that we don't need to handle tokens like = because there's no valid
- // C# program that contains the single token twice in a row.
- // (for +, - and &, this can happen with unary operators;
- // for ?, this can happen in "a is int? ? b : c" or "a as int? ?? 0";
- // and for /, this can happen with "1/ *ptr" or "1/ //comment".)
- if (lastWritten == LastWritten.Plus && token [0] == '+'
- || lastWritten == LastWritten.Minus && token [0] == '-'
- || lastWritten == LastWritten.Ampersand && token [0] == '&'
- || lastWritten == LastWritten.QuestionMark && token [0] == '?'
- || lastWritten == LastWritten.Division && token [0] == '*') {
- formatter.Space();
- }
- formatter.WriteToken(token);
- if (token == "+") {
- lastWritten = LastWritten.Plus;
- } else if (token == "-") {
- lastWritten = LastWritten.Minus;
- } else if (token == "&") {
- lastWritten = LastWritten.Ampersand;
- } else if (token == "?") {
- lastWritten = LastWritten.QuestionMark;
- } else if (token == "/") {
- lastWritten = LastWritten.Division;
- } else {
- lastWritten = LastWritten.Other;
- }
+ writer.WriteToken(tokenRole, token);
+ isAtStartOfLine = false;
}
void LPar()
@@ -396,29 +262,78 @@ namespace ICSharpCode.NRefactory.CSharp
void Space(bool addSpace = true)
{
if (addSpace) {
- formatter.Space();
- lastWritten = LastWritten.Whitespace;
+ writer.Space();
}
}
void NewLine()
{
- formatter.NewLine();
- lastWritten = LastWritten.Whitespace;
+ writer.NewLine();
+ isAtStartOfLine = true;
}
void OpenBrace(BraceStyle style)
{
- WriteSpecialsUpToRole(Roles.LBrace);
- formatter.OpenBrace(style);
- lastWritten = LastWritten.Other;
+ switch (style) {
+ case BraceStyle.DoNotChange:
+ case BraceStyle.EndOfLine:
+ case BraceStyle.BannerStyle:
+ if (!isAtStartOfLine)
+ writer.Space();
+ writer.WriteToken(Roles.LBrace, "{");
+ break;
+ case BraceStyle.EndOfLineWithoutSpace:
+ writer.WriteToken(Roles.LBrace, "{");
+ break;
+ case BraceStyle.NextLine:
+ if (!isAtStartOfLine)
+ NewLine();
+ writer.WriteToken(Roles.LBrace, "{");
+ break;
+ case BraceStyle.NextLineShifted:
+ NewLine();
+ writer.Indent();
+ writer.WriteToken(Roles.LBrace, "{");
+ NewLine();
+ return;
+ case BraceStyle.NextLineShifted2:
+ NewLine();
+ writer.Indent();
+ writer.WriteToken(Roles.LBrace, "{");
+ break;
+ default:
+ throw new ArgumentOutOfRangeException ();
+ }
+ writer.Indent();
+ NewLine();
}
void CloseBrace(BraceStyle style)
{
- WriteSpecialsUpToRole(Roles.RBrace);
- formatter.CloseBrace(style);
- lastWritten = LastWritten.Other;
+ switch (style) {
+ case BraceStyle.DoNotChange:
+ case BraceStyle.EndOfLine:
+ case BraceStyle.EndOfLineWithoutSpace:
+ case BraceStyle.NextLine:
+ writer.Unindent();
+ writer.WriteToken(Roles.RBrace, "}");
+ isAtStartOfLine = false;
+ break;
+ case BraceStyle.BannerStyle:
+ case BraceStyle.NextLineShifted:
+ writer.WriteToken(Roles.RBrace, "}");
+ isAtStartOfLine = false;
+ writer.Unindent();
+ break;
+ case BraceStyle.NextLineShifted2:
+ writer.Unindent();
+ writer.WriteToken(Roles.RBrace, "}");
+ isAtStartOfLine = false;
+ writer.Unindent();
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
}
#endregion
@@ -502,17 +417,10 @@ namespace ICSharpCode.NRefactory.CSharp
foreach (Identifier ident in identifiers) {
if (first) {
first = false;
- if (lastWritten == LastWritten.KeywordOrIdentifier) {
- formatter.Space();
- }
} else {
- WriteSpecialsUpToRole(Roles.Dot, ident);
- formatter.WriteToken(".");
- lastWritten = LastWritten.Other;
+ writer.WriteToken(Roles.Dot, ".");
}
- WriteSpecialsUpToNode(ident);
- formatter.WriteIdentifier(ident.Name);
- lastWritten = LastWritten.KeywordOrIdentifier;
+ writer.WriteIdentifier(ident);
}
}
@@ -527,9 +435,9 @@ namespace ICSharpCode.NRefactory.CSharp
VisitBlockStatement(block);
} else {
NewLine();
- formatter.Indent();
+ writer.Indent();
embeddedStatement.AcceptVisitor(this);
- formatter.Unindent();
+ writer.Unindent();
}
}
@@ -625,8 +533,8 @@ namespace ICSharpCode.NRefactory.CSharp
// The output visitor will output nested braces only if they are necessary,
// or if the braces tokens exist in the AST.
bool bracesAreOptional = arrayInitializerExpression.Elements.Count == 1
- && IsObjectOrCollectionInitializer(arrayInitializerExpression.Parent)
- && !CanBeConfusedWithObjectInitializer(arrayInitializerExpression.Elements.Single());
+ && IsObjectOrCollectionInitializer(arrayInitializerExpression.Parent)
+ && !CanBeConfusedWithObjectInitializer(arrayInitializerExpression.Elements.Single());
if (bracesAreOptional && arrayInitializerExpression.LBraceToken.IsNull) {
arrayInitializerExpression.Elements.Single().AcceptVisitor(this);
} else {
@@ -667,6 +575,7 @@ namespace ICSharpCode.NRefactory.CSharp
}
OpenBrace(style);
bool isFirst = true;
+ AstNode last = null;
foreach (AstNode node in elements) {
if (isFirst) {
isFirst = false;
@@ -674,9 +583,11 @@ namespace ICSharpCode.NRefactory.CSharp
Comma(node, noSpaceAfterComma: true);
NewLine();
}
+ last = node;
node.AcceptVisitor(this);
}
- OptionalComma();
+ if (last != null)
+ OptionalComma(last.NextSibling);
NewLine();
CloseBrace(style);
}
@@ -843,7 +754,7 @@ namespace ICSharpCode.NRefactory.CSharp
public void VisitIdentifierExpression(IdentifierExpression identifierExpression)
{
StartNode(identifierExpression);
- WriteIdentifier(identifierExpression.Identifier);
+ WriteIdentifier(identifierExpression.IdentifierToken);
WriteTypeArguments(identifierExpression.TypeArguments);
EndNode(identifierExpression);
}
@@ -909,7 +820,7 @@ namespace ICSharpCode.NRefactory.CSharp
StartNode(memberReferenceExpression);
memberReferenceExpression.Target.AcceptVisitor(this);
WriteToken(Roles.Dot);
- WriteIdentifier(memberReferenceExpression.MemberName);
+ WriteIdentifier(memberReferenceExpression.MemberNameToken);
WriteTypeArguments(memberReferenceExpression.TypeArguments);
EndNode(memberReferenceExpression);
}
@@ -984,7 +895,7 @@ namespace ICSharpCode.NRefactory.CSharp
StartNode(pointerReferenceExpression);
pointerReferenceExpression.Target.AcceptVisitor(this);
WriteToken(PointerReferenceExpression.ArrowRole);
- WriteIdentifier(pointerReferenceExpression.MemberName);
+ WriteIdentifier(pointerReferenceExpression.MemberNameToken);
WriteTypeArguments(pointerReferenceExpression.TypeArguments);
EndNode(pointerReferenceExpression);
}
@@ -994,183 +905,12 @@ namespace ICSharpCode.NRefactory.CSharp
{
StartNode(primitiveExpression);
if (!string.IsNullOrEmpty(primitiveExpression.LiteralValue)) {
- formatter.WriteToken(primitiveExpression.LiteralValue);
+ writer.WriteToken(primitiveExpression.Role, primitiveExpression.LiteralValue);
} else {
- WritePrimitiveValue(primitiveExpression.Value);
+ TextWriterTokenWriter.PrintPrimitiveValue(primitiveExpression.Value);
}
EndNode(primitiveExpression);
}
-
- public static string PrintPrimitiveValue(object val)
- {
- StringWriter writer = new StringWriter();
- CSharpOutputVisitor visitor = new CSharpOutputVisitor(writer, new CSharpFormattingOptions());
- visitor.WritePrimitiveValue(val);
- return writer.ToString();
- }
-
- void WritePrimitiveValue(object val)
- {
- if (val == null) {
- // usually NullReferenceExpression should be used for this, but we'll handle it anyways
- WriteKeyword("null");
- return;
- }
-
- if (val is bool) {
- if ((bool)val) {
- WriteKeyword("true");
- } else {
- WriteKeyword("false");
- }
- return;
- }
-
- if (val is string) {
- formatter.WriteToken("\"" + ConvertString(val.ToString()) + "\"");
- lastWritten = LastWritten.Other;
- } else if (val is char) {
- formatter.WriteToken("'" + ConvertCharLiteral((char)val) + "'");
- lastWritten = LastWritten.Other;
- } else if (val is decimal) {
- formatter.WriteToken(((decimal)val).ToString(NumberFormatInfo.InvariantInfo) + "m");
- lastWritten = LastWritten.Other;
- } else if (val is float) {
- float f = (float)val;
- if (float.IsInfinity(f) || float.IsNaN(f)) {
- // Strictly speaking, these aren't PrimitiveExpressions;
- // but we still support writing these to make life easier for code generators.
- WriteKeyword("float");
- WriteToken(Roles.Dot);
- if (float.IsPositiveInfinity(f)) {
- WriteIdentifier("PositiveInfinity");
- } else if (float.IsNegativeInfinity(f)) {
- WriteIdentifier("NegativeInfinity");
- } else {
- WriteIdentifier("NaN");
- }
- return;
- }
- if (f == 0 && 1 / f == float.NegativeInfinity) {
- // negative zero is a special case
- // (again, not a primitive expression, but it's better to handle
- // the special case here than to do it in all code generators)
- formatter.WriteToken("-");
- }
- formatter.WriteToken(f.ToString("R", NumberFormatInfo.InvariantInfo) + "f");
- lastWritten = LastWritten.Other;
- } else if (val is double) {
- double f = (double)val;
- if (double.IsInfinity(f) || double.IsNaN(f)) {
- // Strictly speaking, these aren't PrimitiveExpressions;
- // but we still support writing these to make life easier for code generators.
- WriteKeyword("double");
- WriteToken(Roles.Dot);
- if (double.IsPositiveInfinity(f)) {
- WriteIdentifier("PositiveInfinity");
- } else if (double.IsNegativeInfinity(f)) {
- WriteIdentifier("NegativeInfinity");
- } else {
- WriteIdentifier("NaN");
- }
- return;
- }
- if (f == 0 && 1 / f == double.NegativeInfinity) {
- // negative zero is a special case
- // (again, not a primitive expression, but it's better to handle
- // the special case here than to do it in all code generators)
- formatter.WriteToken("-");
- }
- string number = f.ToString("R", NumberFormatInfo.InvariantInfo);
- if (number.IndexOf('.') < 0 && number.IndexOf('E') < 0) {
- number += ".0";
- }
- formatter.WriteToken(number);
- // needs space if identifier follows number; this avoids mistaking the following identifier as type suffix
- lastWritten = LastWritten.KeywordOrIdentifier;
- } else if (val is IFormattable) {
- StringBuilder b = new StringBuilder ();
- // if (primitiveExpression.LiteralFormat == LiteralFormat.HexadecimalNumber) {
- // b.Append("0x");
- // b.Append(((IFormattable)val).ToString("x", NumberFormatInfo.InvariantInfo));
- // } else {
- b.Append(((IFormattable)val).ToString(null, NumberFormatInfo.InvariantInfo));
- // }
- if (val is uint || val is ulong) {
- b.Append("u");
- }
- if (val is long || val is ulong) {
- b.Append("L");
- }
- formatter.WriteToken(b.ToString());
- // needs space if identifier follows number; this avoids mistaking the following identifier as type suffix
- lastWritten = LastWritten.KeywordOrIdentifier;
- } else {
- formatter.WriteToken(val.ToString());
- lastWritten = LastWritten.Other;
- }
- }
-
- static string ConvertCharLiteral(char ch)
- {
- if (ch == '\'') {
- return "\\'";
- }
- return ConvertChar(ch);
- }
-
- ///
- /// Gets the escape sequence for the specified character.
- ///
- /// This method does not convert ' or ".
- public static string ConvertChar(char ch)
- {
- switch (ch) {
- case '\\':
- return "\\\\";
- case '\0':
- return "\\0";
- case '\a':
- return "\\a";
- case '\b':
- return "\\b";
- case '\f':
- return "\\f";
- case '\n':
- return "\\n";
- case '\r':
- return "\\r";
- case '\t':
- return "\\t";
- case '\v':
- return "\\v";
- default:
- if (char.IsControl(ch) || char.IsSurrogate(ch) ||
- // print all uncommon white spaces as numbers
- (char.IsWhiteSpace(ch) && ch != ' ')) {
- return "\\u" + ((int)ch).ToString("x4");
- } else {
- return ch.ToString();
- }
- }
- }
-
- ///
- /// Converts special characters to escape sequences within the given string.
- ///
- public static string ConvertString(string str)
- {
- StringBuilder sb = new StringBuilder ();
- foreach (char ch in str) {
- if (ch == '"') {
- sb.Append("\\\"");
- } else {
- sb.Append(ConvertChar(ch));
- }
- }
- return sb.ToString();
- }
-
#endregion
public void VisitSizeOfExpression(SizeOfExpression sizeOfExpression)
@@ -1261,7 +1001,7 @@ namespace ICSharpCode.NRefactory.CSharp
StartNode(queryExpression);
bool indent = !(queryExpression.Parent is QueryContinuationClause);
if (indent) {
- formatter.Indent();
+ writer.Indent();
NewLine();
}
bool first = true;
@@ -1276,7 +1016,7 @@ namespace ICSharpCode.NRefactory.CSharp
clause.AcceptVisitor(this);
}
if (indent) {
- formatter.Unindent();
+ writer.Unindent();
}
EndNode(queryExpression);
}
@@ -1334,7 +1074,7 @@ namespace ICSharpCode.NRefactory.CSharp
WriteKeyword(QueryJoinClause.JoinKeywordRole);
queryJoinClause.Type.AcceptVisitor(this);
Space();
- WriteIdentifier(queryJoinClause.JoinIdentifier, QueryJoinClause.JoinIdentifierRole);
+ WriteIdentifier(queryJoinClause.JoinIdentifierToken);
Space();
WriteKeyword(QueryJoinClause.InKeywordRole);
Space();
@@ -1350,7 +1090,7 @@ namespace ICSharpCode.NRefactory.CSharp
if (queryJoinClause.IsGroupJoin) {
Space();
WriteKeyword(QueryJoinClause.IntoKeywordRole);
- WriteIdentifier(queryJoinClause.IntoIdentifier, QueryJoinClause.IntoIdentifierRole);
+ WriteIdentifier(queryJoinClause.IntoIdentifierToken);
}
EndNode(queryJoinClause);
}
@@ -1465,7 +1205,7 @@ namespace ICSharpCode.NRefactory.CSharp
member.AcceptVisitor(this);
}
CloseBrace(policy.NamespaceBraceStyle);
- OptionalSemicolon();
+ OptionalSemicolon(namespaceDeclaration.LastChild);
NewLine();
EndNode(namespaceDeclaration);
}
@@ -1508,6 +1248,7 @@ namespace ICSharpCode.NRefactory.CSharp
OpenBrace(braceStyle);
if (typeDeclaration.ClassType == ClassType.Enum) {
bool first = true;
+ AstNode last = null;
foreach (var member in typeDeclaration.Members) {
if (first) {
first = false;
@@ -1515,9 +1256,11 @@ namespace ICSharpCode.NRefactory.CSharp
Comma(member, noSpaceAfterComma: true);
NewLine();
}
+ last = member;
member.AcceptVisitor(this);
}
- OptionalComma();
+ if (last != null)
+ OptionalComma(last.NextSibling);
NewLine();
} else {
foreach (var member in typeDeclaration.Members) {
@@ -1525,7 +1268,7 @@ namespace ICSharpCode.NRefactory.CSharp
}
}
CloseBrace(braceStyle);
- OptionalSemicolon();
+ OptionalSemicolon(typeDeclaration.LastChild);
NewLine();
EndNode(typeDeclaration);
}
@@ -1534,7 +1277,7 @@ namespace ICSharpCode.NRefactory.CSharp
{
StartNode(usingAliasDeclaration);
WriteKeyword(UsingAliasDeclaration.UsingKeywordRole);
- WriteIdentifier(usingAliasDeclaration.Alias, UsingAliasDeclaration.AliasRole);
+ WriteIdentifier(usingAliasDeclaration.GetChildByRole(UsingAliasDeclaration.AliasRole));
Space(policy.SpaceAroundEqualityOperator);
WriteToken(Roles.Assign);
Space(policy.SpaceAroundEqualityOperator);
@@ -1745,7 +1488,7 @@ namespace ICSharpCode.NRefactory.CSharp
{
StartNode(gotoStatement);
WriteKeyword(GotoStatement.GotoKeywordRole);
- WriteIdentifier(gotoStatement.Label);
+ WriteIdentifier(gotoStatement.GetChildByRole(Roles.Identifier));
Semicolon();
EndNode(gotoStatement);
}
@@ -1771,7 +1514,7 @@ namespace ICSharpCode.NRefactory.CSharp
public void VisitLabelStatement(LabelStatement labelStatement)
{
StartNode(labelStatement);
- WriteIdentifier(labelStatement.Label);
+ WriteIdentifier(labelStatement.GetChildByRole(Roles.Identifier));
WriteToken(Roles.Colon);
bool foundLabelledStatement = false;
for (AstNode tmp = labelStatement.NextSibling; tmp != null; tmp = tmp.NextSibling) {
@@ -1825,7 +1568,7 @@ namespace ICSharpCode.NRefactory.CSharp
RPar();
OpenBrace(policy.StatementBraceStyle);
if (!policy.IndentSwitchBody) {
- formatter.Unindent();
+ writer.Unindent();
}
foreach (var section in switchStatement.SwitchSections) {
@@ -1833,7 +1576,7 @@ namespace ICSharpCode.NRefactory.CSharp
}
if (!policy.IndentSwitchBody) {
- formatter.Indent();
+ writer.Indent();
}
CloseBrace(policy.StatementBraceStyle);
NewLine();
@@ -1853,7 +1596,7 @@ namespace ICSharpCode.NRefactory.CSharp
}
bool isBlock = switchSection.Statements.Count == 1 && switchSection.Statements.Single() is BlockStatement;
if (policy.IndentCaseBody && !isBlock) {
- formatter.Indent();
+ writer.Indent();
}
if (!isBlock)
@@ -1864,7 +1607,7 @@ namespace ICSharpCode.NRefactory.CSharp
}
if (policy.IndentCaseBody && !isBlock) {
- formatter.Unindent();
+ writer.Unindent();
}
EndNode(switchSection);
@@ -2040,7 +1783,7 @@ namespace ICSharpCode.NRefactory.CSharp
var nameToken = constructorDeclaration.NameToken;
if (!nameToken.IsNull)
StartNode(nameToken);
- WriteIdentifier(type != null ? type.Name : constructorDeclaration.Name);
+ WriteIdentifier(type != null ? type.NameToken : constructorDeclaration.NameToken);
if (!nameToken.IsNull)
EndNode(nameToken);
Space(policy.SpaceBeforeConstructorDeclarationParentheses);
@@ -2078,7 +1821,7 @@ namespace ICSharpCode.NRefactory.CSharp
var nameToken = destructorDeclaration.NameToken;
if (!nameToken.IsNull)
StartNode(nameToken);
- WriteIdentifier(type != null ? type.Name : destructorDeclaration.Name);
+ WriteIdentifier(type != null ? type.NameToken : destructorDeclaration.NameToken);
if (!nameToken.IsNull)
EndNode(nameToken);
Space(policy.SpaceBeforeConstructorDeclarationParentheses);
@@ -2235,7 +1978,7 @@ namespace ICSharpCode.NRefactory.CSharp
WriteKeyword(OperatorDeclaration.OperatorKeywordRole);
Space();
if (operatorDeclaration.OperatorType == OperatorType.Explicit
- || operatorDeclaration.OperatorType == OperatorType.Implicit) {
+ || operatorDeclaration.OperatorType == OperatorType.Implicit) {
operatorDeclaration.ReturnType.AcceptVisitor(this);
} else {
WriteToken(OperatorDeclaration.GetToken(operatorDeclaration.OperatorType), OperatorDeclaration.GetRole(operatorDeclaration.OperatorType));
@@ -2328,7 +2071,7 @@ namespace ICSharpCode.NRefactory.CSharp
public void VisitSimpleType(SimpleType simpleType)
{
StartNode(simpleType);
- WriteIdentifier(simpleType.Identifier);
+ WriteIdentifier(simpleType.IdentifierToken);
WriteTypeArguments(simpleType.TypeArguments);
EndNode(simpleType);
}
@@ -2342,7 +2085,7 @@ namespace ICSharpCode.NRefactory.CSharp
} else {
WriteToken(Roles.Dot);
}
- WriteIdentifier(memberType.MemberName);
+ WriteIdentifier(memberType.MemberNameToken);
WriteTypeArguments(memberType.TypeArguments);
EndNode(memberType);
}
@@ -2368,9 +2111,7 @@ namespace ICSharpCode.NRefactory.CSharp
StartNode(arraySpecifier);
WriteToken(Roles.LBracket);
foreach (var comma in arraySpecifier.GetChildrenByRole(Roles.Comma)) {
- WriteSpecialsUpToNode(comma);
- formatter.WriteToken(",");
- lastWritten = LastWritten.Other;
+ writer.WriteToken(Roles.Comma, ",");
}
WriteToken(Roles.RBracket);
EndNode(arraySpecifier);
@@ -2390,15 +2131,9 @@ namespace ICSharpCode.NRefactory.CSharp
public void VisitComment(Comment comment)
{
- if (lastWritten == LastWritten.Division) {
- // When there's a comment starting after a division operator
- // "1.0 / /*comment*/a", then we need to insert a space in front of the comment.
- formatter.Space();
- }
- formatter.StartNode(comment);
- formatter.WriteComment(comment.CommentType, comment.Content);
- formatter.EndNode(comment);
- lastWritten = LastWritten.Whitespace;
+ writer.StartNode(comment);
+ writer.WriteComment(comment.CommentType, comment.Content);
+ writer.EndNode(comment);
}
public void VisitNewLine(NewLineNode newLineNode)
@@ -2420,10 +2155,9 @@ namespace ICSharpCode.NRefactory.CSharp
public void VisitPreProcessorDirective(PreProcessorDirective preProcessorDirective)
{
- formatter.StartNode(preProcessorDirective);
- formatter.WritePreProcessorDirective(preProcessorDirective.Type, preProcessorDirective.Argument);
- formatter.EndNode(preProcessorDirective);
- lastWritten = LastWritten.Whitespace;
+ writer.StartNode(preProcessorDirective);
+ writer.WritePreProcessorDirective(preProcessorDirective.Type, preProcessorDirective.Argument);
+ writer.EndNode(preProcessorDirective);
}
public void VisitTypeParameterDeclaration(TypeParameterDeclaration typeParameterDeclaration)
@@ -2451,7 +2185,7 @@ namespace ICSharpCode.NRefactory.CSharp
StartNode(constraint);
Space();
WriteKeyword(Roles.WhereKeyword);
- WriteIdentifier(constraint.TypeParameter.Identifier);
+ WriteIdentifier(constraint.TypeParameter.IdentifierToken);
Space();
WriteToken(Roles.Colon);
Space();
@@ -2474,7 +2208,7 @@ namespace ICSharpCode.NRefactory.CSharp
public void VisitIdentifier(Identifier identifier)
{
StartNode(identifier);
- WriteIdentifier(identifier.Name);
+ WriteIdentifier(identifier);
EndNode(identifier);
}
@@ -2518,7 +2252,7 @@ namespace ICSharpCode.NRefactory.CSharp
Space();
LPar();
NewLine();
- formatter.Indent();
+ writer.Indent();
foreach (INode alternative in choice) {
VisitNodeInPattern(alternative);
if (alternative != choice.Last()) {
@@ -2526,7 +2260,7 @@ namespace ICSharpCode.NRefactory.CSharp
}
NewLine();
}
- formatter.Unindent();
+ writer.Unindent();
RPar();
}
@@ -2580,7 +2314,7 @@ namespace ICSharpCode.NRefactory.CSharp
} else if (childNode is Repeat) {
VisitRepeat((Repeat)childNode);
} else {
- WritePrimitiveValue(childNode);
+ TextWriterTokenWriter.PrintPrimitiveValue(childNode);
}
}
#endregion
@@ -2618,7 +2352,7 @@ namespace ICSharpCode.NRefactory.CSharp
}
break;
default:
- WriteIdentifier(documentationReference.MemberName);
+ WriteIdentifier(documentationReference.GetChildByRole(Roles.Identifier));
break;
}
WriteTypeArguments(documentationReference.TypeArguments);
@@ -2634,4 +2368,230 @@ namespace ICSharpCode.NRefactory.CSharp
}
#endregion
}
+
+ public interface ITokenWriter
+ {
+ void StartNode(AstNode node);
+ void EndNode(AstNode node);
+
+ ///
+ /// Writes an identifier.
+ ///
+ void WriteIdentifier(Identifier identifier);
+
+ ///
+ /// Writes a keyword to the output.
+ ///
+ void WriteKeyword(Role role, string keyword);
+
+ ///
+ /// Writes a token to the output.
+ ///
+ void WriteToken(Role role, string token);
+
+ ///
+ /// Writes a primitive/literal value
+ ///
+ void WritePrimitiveValue(object value);
+
+ void Space();
+
+ void Indent();
+ void Unindent();
+
+ void NewLine();
+
+ void WriteComment(CommentType commentType, string content);
+ void WritePreProcessorDirective(PreProcessorDirectiveType type, string argument);
+ }
+
+ public abstract class DecoratingTokenWriter : ITokenWriter
+ {
+ ITokenWriter decoratedWriter;
+
+ protected DecoratingTokenWriter(ITokenWriter decoratedWriter)
+ {
+ if (decoratedWriter == null)
+ throw new ArgumentNullException("decoratedWriter");
+ this.decoratedWriter = decoratedWriter;
+ }
+
+ public virtual void StartNode(AstNode node)
+ {
+ decoratedWriter.StartNode(node);
+ }
+
+ public virtual void EndNode(AstNode node)
+ {
+ decoratedWriter.EndNode(node);
+ }
+
+ public virtual void WriteIdentifier(Identifier identifier)
+ {
+ decoratedWriter.WriteIdentifier(identifier);
+ }
+
+ public virtual void WriteKeyword(Role role, string keyword)
+ {
+ decoratedWriter.WriteKeyword(role, keyword);
+ }
+
+ public virtual void WriteToken(Role role, string token)
+ {
+ decoratedWriter.WriteToken(role, token);
+ }
+
+ public virtual void WritePrimitiveValue(object value)
+ {
+ decoratedWriter.WritePrimitiveValue(value);
+ }
+
+ public virtual void Space()
+ {
+ decoratedWriter.Space();
+ }
+
+ public virtual void Indent()
+ {
+ decoratedWriter.Indent();
+ }
+
+ public virtual void Unindent()
+ {
+ decoratedWriter.Unindent();
+ }
+
+ public virtual void NewLine()
+ {
+ decoratedWriter.NewLine();
+ }
+
+ public virtual void WriteComment(CommentType commentType, string content)
+ {
+ decoratedWriter.WriteComment(commentType, content);
+ }
+
+ public virtual void WritePreProcessorDirective(PreProcessorDirectiveType type, string argument)
+ {
+ decoratedWriter.WritePreProcessorDirective(type, argument);
+ }
+ }
+
+ class InsertSpecialsDecorator : DecoratingTokenWriter
+ {
+ readonly Stack positionStack = new Stack();
+
+ public InsertSpecialsDecorator(ITokenWriter writer)
+ : base(writer)
+ {
+ }
+
+ public override void StartNode(AstNode node)
+ {
+ if (positionStack.Count > 0) {
+ WriteSpecialsUpToNode(node);
+ }
+ positionStack.Push(node.FirstChild);
+ base.StartNode(node);
+ }
+
+ public override void EndNode(AstNode node)
+ {
+ base.EndNode(node);
+ AstNode pos = positionStack.Pop();
+ Debug.Assert(pos == null || pos.Parent == node);
+ WriteSpecials(pos, null);
+ }
+
+ public override void WriteKeyword(Role role, string keyword)
+ {
+ if (role != null) {
+ WriteSpecialsUpToRole(role);
+ }
+ base.WriteKeyword(role, keyword);
+ }
+
+ public override void WriteIdentifier(Identifier identifier)
+ {
+ WriteSpecialsUpToRole(identifier.Role ?? Roles.Identifier);
+ base.WriteIdentifier(identifier);
+ }
+
+ public override void WriteToken(Role role, string token)
+ {
+ WriteSpecialsUpToRole(role);
+ base.WriteToken(role, token);
+ }
+
+ #region WriteSpecials
+ ///
+ /// Writes all specials from start to end (exclusive). Does not touch the positionStack.
+ ///
+ void WriteSpecials(AstNode start, AstNode end)
+ {
+ for (AstNode pos = start; pos != end; pos = pos.NextSibling) {
+ if (pos.Role == Roles.Comment) {
+ var node = (Comment)pos;
+ WriteComment(node.CommentType, node.Content);
+ }
+ if (pos.Role == Roles.NewLine) {
+ NewLine();
+ }
+ if (pos.Role == Roles.PreProcessorDirective) {
+ var node = (PreProcessorDirective)pos;
+ WritePreProcessorDirective(node.Type, node.Argument);
+ }
+ }
+ }
+
+ ///
+ /// Writes all specials between the current position (in the positionStack) and the next
+ /// node with the specified role. Advances the current position.
+ ///
+ void WriteSpecialsUpToRole(Role role)
+ {
+ WriteSpecialsUpToRole(role, null);
+ }
+
+ void WriteSpecialsUpToRole(Role role, AstNode nextNode)
+ {
+ if (positionStack.Count == 0) {
+ return;
+ }
+ // Look for the role between the current position and the nextNode.
+ for (AstNode pos = positionStack.Peek(); pos != null && pos != nextNode; pos = pos.NextSibling) {
+ if (pos.Role == role) {
+ WriteSpecials(positionStack.Pop(), pos);
+ // Push the next sibling because the node matching the role is not a special,
+ // and should be considered to be already handled.
+ positionStack.Push(pos.NextSibling);
+ // This is necessary for OptionalComma() to work correctly.
+ break;
+ }
+ }
+ }
+
+ ///
+ /// Writes all specials between the current position (in the positionStack) and the specified node.
+ /// Advances the current position.
+ ///
+ void WriteSpecialsUpToNode(AstNode node)
+ {
+ if (positionStack.Count == 0) {
+ return;
+ }
+ for (AstNode pos = positionStack.Peek(); pos != null; pos = pos.NextSibling) {
+ if (pos == node) {
+ WriteSpecials(positionStack.Pop(), pos);
+ // Push the next sibling because the node itself is not a special,
+ // and should be considered to be already handled.
+ positionStack.Push(pos.NextSibling);
+ // This is necessary for OptionalComma() to work correctly.
+ break;
+ }
+ }
+ }
+ #endregion
+ }
+
}
diff --git a/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/InsertRequiredSpacesDecorator.cs b/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/InsertRequiredSpacesDecorator.cs
new file mode 100644
index 0000000000..af5f90366f
--- /dev/null
+++ b/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/InsertRequiredSpacesDecorator.cs
@@ -0,0 +1,183 @@
+// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team
+//
+// 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.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using ICSharpCode.NRefactory.PatternMatching;
+using ICSharpCode.NRefactory.TypeSystem;
+using ICSharpCode.NRefactory.CSharp;
+
+namespace ICSharpCode.NRefactory.CSharp
+{
+ class InsertRequiredSpacesDecorator : DecoratingTokenWriter
+ {
+ ///
+ /// Used to insert the minimal amount of spaces so that the lexer recognizes the tokens that were written.
+ ///
+ LastWritten lastWritten;
+
+ enum LastWritten
+ {
+ Whitespace,
+ Other,
+ KeywordOrIdentifier,
+ Plus,
+ Minus,
+ Ampersand,
+ QuestionMark,
+ Division
+ }
+
+ public InsertRequiredSpacesDecorator(ITokenWriter writer)
+ : base(writer)
+ {
+ }
+
+ public override void WriteIdentifier(Identifier identifier)
+ {
+ if (identifier.IsVerbatim) {
+ if (lastWritten == LastWritten.KeywordOrIdentifier) {
+ // this space is not strictly required, so we call Space()
+ Space();
+ }
+ } else if (lastWritten == LastWritten.KeywordOrIdentifier) {
+ // this space is strictly required, so we directly call the formatter
+ base.Space();
+ }
+ base.WriteIdentifier(identifier);
+ lastWritten = LastWritten.KeywordOrIdentifier;
+ }
+
+ public override void WriteKeyword(Role role, string keyword)
+ {
+ if (lastWritten == LastWritten.KeywordOrIdentifier) {
+ Space();
+ }
+ base.WriteKeyword(role, keyword);
+ lastWritten = LastWritten.KeywordOrIdentifier;
+ }
+
+ public override void WriteToken(Role role, string token)
+ {
+ // Avoid that two +, - or ? tokens are combined into a ++, -- or ?? token.
+ // Note that we don't need to handle tokens like = because there's no valid
+ // C# program that contains the single token twice in a row.
+ // (for +, - and &, this can happen with unary operators;
+ // for ?, this can happen in "a is int? ? b : c" or "a as int? ?? 0";
+ // and for /, this can happen with "1/ *ptr" or "1/ //comment".)
+ if (lastWritten == LastWritten.Plus && token[0] == '+' ||
+ lastWritten == LastWritten.Minus && token[0] == '-' ||
+ lastWritten == LastWritten.Ampersand && token[0] == '&' ||
+ lastWritten == LastWritten.QuestionMark && token[0] == '?' ||
+ lastWritten == LastWritten.Division && token[0] == '*') {
+ base.Space();
+ }
+ base.WriteToken(role, token);
+ if (token == "+") {
+ lastWritten = LastWritten.Plus;
+ } else if (token == "-") {
+ lastWritten = LastWritten.Minus;
+ } else if (token == "&") {
+ lastWritten = LastWritten.Ampersand;
+ } else if (token == "?") {
+ lastWritten = LastWritten.QuestionMark;
+ } else if (token == "/") {
+ lastWritten = LastWritten.Division;
+ } else {
+ lastWritten = LastWritten.Other;
+ }
+ }
+
+ public override void Space()
+ {
+ base.Space();
+ lastWritten = LastWritten.Whitespace;
+ }
+
+ public void Indent()
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Unindent()
+ {
+ throw new NotImplementedException();
+ }
+
+ public override void NewLine()
+ {
+ base.NewLine();
+ lastWritten = LastWritten.Whitespace;
+ }
+
+ public override void WriteComment(CommentType commentType, string content)
+ {
+ if (lastWritten == LastWritten.Division) {
+ // When there's a comment starting after a division operator
+ // "1.0 / /*comment*/a", then we need to insert a space in front of the comment.
+ base.Space();
+ }
+ base.WriteComment(commentType, content);
+ lastWritten = LastWritten.Whitespace;
+ }
+
+ public override void WritePreProcessorDirective(PreProcessorDirectiveType type, string argument)
+ {
+ base.WritePreProcessorDirective(type, argument);
+ lastWritten = LastWritten.Whitespace;
+ }
+
+ public override void WritePrimitiveValue(object value)
+ {
+ base.WritePrimitiveValue(value);
+ if (value == null || value is bool)
+ return;
+ if (value is string) {
+ lastWritten = LastWritten.Other;
+ } else if (value is char) {
+ lastWritten = LastWritten.Other;
+ } else if (value is decimal) {
+ lastWritten = LastWritten.Other;
+ } else if (value is float) {
+ float f = (float)value;
+ if (float.IsInfinity(f) || float.IsNaN(f)) return;
+ lastWritten = LastWritten.Other;
+ } else if (value is double) {
+ double f = (double)value;
+ if (double.IsInfinity(f) || double.IsNaN(f)) return;
+ // needs space if identifier follows number;
+ // this avoids mistaking the following identifier as type suffix
+ lastWritten = LastWritten.KeywordOrIdentifier;
+ } else if (value is IFormattable) {
+ // needs space if identifier follows number;
+ // this avoids mistaking the following identifier as type suffix
+ lastWritten = LastWritten.KeywordOrIdentifier;
+ } else {
+ lastWritten = LastWritten.Other;
+ }
+ }
+ }
+}
+
+
diff --git a/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/TextWriterOutputFormatter.cs b/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/TextWriterOutputFormatter.cs
index 4d1a0d364e..8d0e664998 100644
--- a/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/TextWriterOutputFormatter.cs
+++ b/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/OutputVisitor/TextWriterOutputFormatter.cs
@@ -17,14 +17,16 @@
// DEALINGS IN THE SOFTWARE.
using System;
+using System.Globalization;
using System.IO;
+using System.Text;
namespace ICSharpCode.NRefactory.CSharp
{
///
/// Writes C# code into a TextWriter.
///
- public class TextWriterOutputFormatter : IOutputFormatter
+ public class TextWriterTokenWriter : ITokenWriter
{
readonly TextWriter textWriter;
int indentation;
@@ -42,7 +44,7 @@ namespace ICSharpCode.NRefactory.CSharp
public string IndentationString { get; set; }
- public TextWriterOutputFormatter(TextWriter textWriter)
+ public TextWriterTokenWriter(TextWriter textWriter)
{
if (textWriter == null)
throw new ArgumentNullException("textWriter");
@@ -50,21 +52,23 @@ namespace ICSharpCode.NRefactory.CSharp
this.IndentationString = "\t";
}
- public void WriteIdentifier(string ident)
+ public void WriteIdentifier(Identifier identifier)
{
WriteIndentation();
- textWriter.Write(ident);
+ if (identifier.IsVerbatim)
+ textWriter.Write('@');
+ textWriter.Write(identifier.Name);
isAtStartOfLine = false;
}
- public void WriteKeyword(string keyword)
+ public void WriteKeyword(Role role, string keyword)
{
WriteIndentation();
textWriter.Write(keyword);
isAtStartOfLine = false;
}
- public void WriteToken(string token)
+ public void WriteToken(Role role, string token)
{
WriteIndentation();
textWriter.Write(token);
@@ -77,79 +81,6 @@ namespace ICSharpCode.NRefactory.CSharp
textWriter.Write(' ');
}
- public void OpenBrace(BraceStyle style)
- {
- switch (style) {
- case BraceStyle.DoNotChange:
- case BraceStyle.EndOfLine:
- case BraceStyle.BannerStyle:
- WriteIndentation();
- if (!isAtStartOfLine)
- textWriter.Write(' ');
- textWriter.Write('{');
- break;
- case BraceStyle.EndOfLineWithoutSpace:
- WriteIndentation();
- textWriter.Write('{');
- break;
- case BraceStyle.NextLine:
- if (!isAtStartOfLine)
- NewLine();
- WriteIndentation();
- textWriter.Write('{');
- break;
-
- case BraceStyle.NextLineShifted:
- NewLine ();
- Indent();
- WriteIndentation();
- textWriter.Write('{');
- NewLine();
- return;
- case BraceStyle.NextLineShifted2:
- NewLine ();
- Indent();
- WriteIndentation();
- textWriter.Write('{');
- break;
- default:
- throw new ArgumentOutOfRangeException ();
- }
- Indent();
- NewLine();
- }
-
- public void CloseBrace(BraceStyle style)
- {
- switch (style) {
- case BraceStyle.DoNotChange:
- case BraceStyle.EndOfLine:
- case BraceStyle.EndOfLineWithoutSpace:
- case BraceStyle.NextLine:
- Unindent();
- WriteIndentation();
- textWriter.Write('}');
- isAtStartOfLine = false;
- break;
- case BraceStyle.BannerStyle:
- case BraceStyle.NextLineShifted:
- WriteIndentation();
- textWriter.Write('}');
- isAtStartOfLine = false;
- Unindent();
- break;
- case BraceStyle.NextLineShifted2:
- Unindent();
- WriteIndentation();
- textWriter.Write('}');
- isAtStartOfLine = false;
- Unindent();
- break;
- default:
- throw new ArgumentOutOfRangeException ();
- }
- }
-
protected void WriteIndentation()
{
if (needsIndent) {
@@ -220,6 +151,167 @@ namespace ICSharpCode.NRefactory.CSharp
NewLine();
}
+ public static string PrintPrimitiveValue(object value)
+ {
+ TextWriter writer = new StringWriter();
+ TextWriterTokenWriter tokenWriter = new TextWriterTokenWriter(writer);
+ tokenWriter.WritePrimitiveValue(value);
+ return writer.ToString();
+ }
+
+ public void WritePrimitiveValue(object value)
+ {
+ if (value == null) {
+ // usually NullReferenceExpression should be used for this, but we'll handle it anyways
+ textWriter.Write("null");
+ return;
+ }
+
+ if (value is bool) {
+ if ((bool)value) {
+ textWriter.Write("true");
+ } else {
+ textWriter.Write("false");
+ }
+ return;
+ }
+
+ if (value is string) {
+ textWriter.Write("\"" + ConvertString(value.ToString()) + "\"");
+ } else if (value is char) {
+ textWriter.Write("'" + ConvertCharLiteral((char)value) + "'");
+ } else if (value is decimal) {
+ textWriter.Write(((decimal)value).ToString(NumberFormatInfo.InvariantInfo) + "m");
+ } else if (value is float) {
+ float f = (float)value;
+ if (float.IsInfinity(f) || float.IsNaN(f)) {
+ // Strictly speaking, these aren't PrimitiveExpressions;
+ // but we still support writing these to make life easier for code generators.
+ textWriter.Write("float");
+ WriteToken(Roles.Dot, ".");
+ if (float.IsPositiveInfinity(f)) {
+ textWriter.Write("PositiveInfinity");
+ } else if (float.IsNegativeInfinity(f)) {
+ textWriter.Write("NegativeInfinity");
+ } else {
+ textWriter.Write("NaN");
+ }
+ return;
+ }
+ if (f == 0 && 1 / f == float.NegativeInfinity) {
+ // negative zero is a special case
+ // (again, not a primitive expression, but it's better to handle
+ // the special case here than to do it in all code generators)
+ textWriter.Write("-");
+ }
+ textWriter.Write(f.ToString("R", NumberFormatInfo.InvariantInfo) + "f");
+ } else if (value is double) {
+ double f = (double)value;
+ if (double.IsInfinity(f) || double.IsNaN(f)) {
+ // Strictly speaking, these aren't PrimitiveExpressions;
+ // but we still support writing these to make life easier for code generators.
+ textWriter.Write("double");
+ textWriter.Write(".");
+ if (double.IsPositiveInfinity(f)) {
+ textWriter.Write("PositiveInfinity");
+ } else if (double.IsNegativeInfinity(f)) {
+ textWriter.Write("NegativeInfinity");
+ } else {
+ textWriter.Write("NaN");
+ }
+ return;
+ }
+ if (f == 0 && 1 / f == double.NegativeInfinity) {
+ // negative zero is a special case
+ // (again, not a primitive expression, but it's better to handle
+ // the special case here than to do it in all code generators)
+ textWriter.Write("-");
+ }
+ string number = f.ToString("R", NumberFormatInfo.InvariantInfo);
+ if (number.IndexOf('.') < 0 && number.IndexOf('E') < 0) {
+ number += ".0";
+ }
+ textWriter.Write(number);
+ } else if (value is IFormattable) {
+ StringBuilder b = new StringBuilder ();
+// if (primitiveExpression.LiteralFormat == LiteralFormat.HexadecimalNumber) {
+// b.Append("0x");
+// b.Append(((IFormattable)val).ToString("x", NumberFormatInfo.InvariantInfo));
+// } else {
+ b.Append(((IFormattable)value).ToString(null, NumberFormatInfo.InvariantInfo));
+// }
+ if (value is uint || value is ulong) {
+ b.Append("u");
+ }
+ if (value is long || value is ulong) {
+ b.Append("L");
+ }
+ textWriter.Write(b.ToString());
+ } else {
+ textWriter.Write(value.ToString());
+ }
+ }
+
+ static string ConvertCharLiteral(char ch)
+ {
+ if (ch == '\'') {
+ return "\\'";
+ }
+ return ConvertChar(ch);
+ }
+
+ ///
+ /// Gets the escape sequence for the specified character.
+ ///
+ /// This method does not convert ' or ".
+ public static string ConvertChar(char ch)
+ {
+ switch (ch) {
+ case '\\':
+ return "\\\\";
+ case '\0':
+ return "\\0";
+ case '\a':
+ return "\\a";
+ case '\b':
+ return "\\b";
+ case '\f':
+ return "\\f";
+ case '\n':
+ return "\\n";
+ case '\r':
+ return "\\r";
+ case '\t':
+ return "\\t";
+ case '\v':
+ return "\\v";
+ default:
+ if (char.IsControl(ch) || char.IsSurrogate(ch) ||
+ // print all uncommon white spaces as numbers
+ (char.IsWhiteSpace(ch) && ch != ' ')) {
+ return "\\u" + ((int)ch).ToString("x4");
+ } else {
+ return ch.ToString();
+ }
+ }
+ }
+
+ ///
+ /// Converts special characters to escape sequences within the given string.
+ ///
+ public static string ConvertString(string str)
+ {
+ StringBuilder sb = new StringBuilder ();
+ foreach (char ch in str) {
+ if (ch == '"') {
+ sb.Append("\\\"");
+ } else {
+ sb.Append(ConvertChar(ch));
+ }
+ }
+ return sb.ToString();
+ }
+
public virtual void StartNode(AstNode node)
{
// Write out the indentation, so that overrides of this method
diff --git a/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/Script.cs b/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/Script.cs
index b133b22287..3a4c90085d 100644
--- a/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/Script.cs
+++ b/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/Script.cs
@@ -263,13 +263,13 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
return 0;
}
- sealed class SegmentTrackingOutputFormatter : TextWriterOutputFormatter
+ sealed class SegmentTrackingTokenWriter : TextWriterTokenWriter
{
internal List> NewSegments = new List>();
Stack startOffsets = new Stack();
readonly StringWriter stringWriter;
- public SegmentTrackingOutputFormatter (StringWriter stringWriter)
+ public SegmentTrackingTokenWriter(StringWriter stringWriter)
: base(stringWriter)
{
this.stringWriter = stringWriter;
@@ -293,7 +293,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
protected NodeOutput OutputNode(int indentLevel, AstNode node, bool startWithNewLine = false)
{
var stringWriter = new StringWriter ();
- var formatter = new SegmentTrackingOutputFormatter (stringWriter);
+ var formatter = new SegmentTrackingTokenWriter(stringWriter);
formatter.Indentation = indentLevel;
formatter.IndentationString = Options.TabsToSpaces ? new string (' ', Options.IndentSize) : "\t";
stringWriter.NewLine = Options.EolMarker;
diff --git a/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/TypeSystem/CSharpUnresolvedFile.cs b/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/TypeSystem/CSharpUnresolvedFile.cs
index 88f61f1c93..cbff80cc0a 100644
--- a/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/TypeSystem/CSharpUnresolvedFile.cs
+++ b/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/TypeSystem/CSharpUnresolvedFile.cs
@@ -31,7 +31,7 @@ namespace ICSharpCode.NRefactory.CSharp.TypeSystem
/// Represents a file that was parsed and converted for the type system.
///
[Serializable, FastSerializerVersion(TypeSystemConvertVisitor.version)]
- public sealed class CSharpUnresolvedFile : AbstractFreezable, IUnresolvedFile, IUnresolvedDocumentationProvider
+ public class CSharpUnresolvedFile : AbstractFreezable, IUnresolvedFile, IUnresolvedDocumentationProvider
{
// The 'FastSerializerVersion' attribute on CSharpUnresolvedFile must be incremented when fixing
// bugs in the TypeSystemConvertVisitor
diff --git a/src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/CSharpOutputVisitorTests.cs b/src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/CSharpOutputVisitorTests.cs
index e858c26339..a1522e91f4 100644
--- a/src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/CSharpOutputVisitorTests.cs
+++ b/src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/CSharpOutputVisitorTests.cs
@@ -31,7 +31,7 @@ namespace ICSharpCode.NRefactory.CSharp
policy = FormattingOptionsFactory.CreateMono();
StringWriter w = new StringWriter();
w.NewLine = "\n";
- node.AcceptVisitor(new CSharpOutputVisitor(new TextWriterOutputFormatter(w) { IndentationString = "$" }, policy));
+ node.AcceptVisitor(new CSharpOutputVisitor(new TextWriterTokenWriter(w) { IndentationString = "$" }, policy));
Assert.AreEqual(expected.Replace("\r", ""), w.ToString());
}
diff --git a/src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/InsertParenthesesVisitorTests.cs b/src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/InsertParenthesesVisitorTests.cs
index 5b0d3fc36d..5483d82b2f 100644
--- a/src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/InsertParenthesesVisitorTests.cs
+++ b/src/Libraries/NRefactory/ICSharpCode.NRefactory.Tests/CSharp/InsertParenthesesVisitorTests.cs
@@ -39,7 +39,7 @@ namespace ICSharpCode.NRefactory.CSharp
expr.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = true });
StringWriter w = new StringWriter();
w.NewLine = " ";
- expr.AcceptVisitor(new CSharpOutputVisitor(new TextWriterOutputFormatter(w) { IndentationString = "" }, policy));
+ expr.AcceptVisitor(new CSharpOutputVisitor(new TextWriterTokenWriter(w) { IndentationString = "" }, policy));
return w.ToString();
}
@@ -49,7 +49,7 @@ namespace ICSharpCode.NRefactory.CSharp
expr.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = false });
StringWriter w = new StringWriter();
w.NewLine = " ";
- expr.AcceptVisitor(new CSharpOutputVisitor(new TextWriterOutputFormatter(w) { IndentationString = "" }, policy));
+ expr.AcceptVisitor(new CSharpOutputVisitor(new TextWriterTokenWriter(w) { IndentationString = "" }, policy));
return w.ToString();
}