From 5078796b1777ce78bff1347579771846fd52a532 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Tue, 7 Dec 2021 19:22:20 +0100 Subject: [PATCH] Add support for string format alignment. --- .../TestCases/Pretty/StringInterpolation.cs | 2 + ICSharpCode.Decompiler/CSharp/CallBuilder.cs | 64 ++++++++++++++++--- .../OutputVisitor/CSharpOutputVisitor.cs | 5 ++ .../InterpolatedStringExpression.cs | 12 +++- 4 files changed, 72 insertions(+), 11 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/StringInterpolation.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/StringInterpolation.cs index 41f57d523..fc667d275 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/StringInterpolation.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/StringInterpolation.cs @@ -20,6 +20,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty Console.WriteLine($"\ta{args[0][0] == 'a'}"); Console.WriteLine($"\ta{$"a{args.Length}" == args[0]}"); Console.WriteLine($"\ta{args.Length}}}"); + Console.WriteLine($"{args.Length,5:x}"); + Console.WriteLine($"{args.Length,5}"); } public static void ArrayExpansionSpecialCases(object[] args) diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index 00f7fc91f..05c1568cc 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -357,7 +357,7 @@ namespace ICSharpCode.Decompiler.CSharp if (tokens.Count > 0) { - foreach (var (kind, index, text) in tokens) + foreach (var (kind, index, alignment, text) in tokens) { TranslatedExpression argument; switch (kind) @@ -373,7 +373,17 @@ namespace ICSharpCode.Decompiler.CSharp case TokenKind.ArgumentWithFormat: argument = arguments[index + 1]; UnpackSingleElementArray(ref argument); - content.Add(new Interpolation(argument, text)); + content.Add(new Interpolation(argument, suffix: text)); + break; + case TokenKind.ArgumentWithAlignment: + argument = arguments[index + 1]; + UnpackSingleElementArray(ref argument); + content.Add(new Interpolation(argument, alignment)); + break; + case TokenKind.ArgumentWithAlignmentAndFormat: + argument = arguments[index + 1]; + UnpackSingleElementArray(ref argument); + content.Add(new Interpolation(argument, alignment, text)); break; } } @@ -566,7 +576,7 @@ namespace ICSharpCode.Decompiler.CSharp ); } - private bool TryGetStringInterpolationTokens(ArgumentList argumentList, out string format, out List<(TokenKind, int, string)> tokens) + private bool TryGetStringInterpolationTokens(ArgumentList argumentList, out string format, out List<(TokenKind Kind, int Index, int Alignment, string Format)> tokens) { tokens = null; format = null; @@ -577,33 +587,56 @@ namespace ICSharpCode.Decompiler.CSharp return false; if (!arguments.Skip(1).All(a => !a.Expression.DescendantsAndSelf.OfType().Any(p => p.Value is string))) return false; - tokens = new List<(TokenKind, int, string)>(); + tokens = new List<(TokenKind Kind, int Index, int Alignment, string Format)>(); int i = 0; format = (string)crr.ConstantValue; foreach (var (kind, data) in TokenizeFormatString(format)) { int index; + string[] arg; switch (kind) { case TokenKind.Error: return false; case TokenKind.String: - tokens.Add((kind, -1, data)); + tokens.Add((kind, -1, 0, data)); break; case TokenKind.Argument: if (!int.TryParse(data, out index) || index != i) return false; i++; - tokens.Add((kind, index, null)); + tokens.Add((kind, index, 0, null)); break; case TokenKind.ArgumentWithFormat: - string[] arg = data.Split(new[] { ':' }, 2); + arg = data.Split(new[] { ':' }, 2); if (arg.Length != 2 || arg[1].Length == 0) return false; if (!int.TryParse(arg[0], out index) || index != i) return false; i++; - tokens.Add((kind, index, arg[1])); + tokens.Add((kind, index, 0, arg[1])); + break; + case TokenKind.ArgumentWithAlignment: + arg = data.Split(new[] { ',' }, 2); + if (arg.Length != 2 || arg[1].Length == 0) + return false; + if (!int.TryParse(arg[0], out index) || index != i) + return false; + if (!int.TryParse(arg[1], out int alignment)) + return false; + i++; + tokens.Add((kind, index, alignment, null)); + break; + case TokenKind.ArgumentWithAlignmentAndFormat: + arg = data.Split(new[] { ',', ':' }, 3); + if (arg.Length != 3 || arg[1].Length == 0 || arg[2].Length == 0) + return false; + if (!int.TryParse(arg[0], out index) || index != i) + return false; + if (!int.TryParse(arg[1], out alignment)) + return false; + i++; + tokens.Add((kind, index, alignment, arg[2])); break; default: return false; @@ -617,7 +650,9 @@ namespace ICSharpCode.Decompiler.CSharp Error, String, Argument, - ArgumentWithFormat + ArgumentWithFormat, + ArgumentWithAlignment, + ArgumentWithAlignmentAndFormat, } private IEnumerable<(TokenKind, string)> TokenizeFormatString(string value) @@ -685,8 +720,19 @@ namespace ICSharpCode.Decompiler.CSharp { kind = TokenKind.ArgumentWithFormat; } + else if (kind == TokenKind.ArgumentWithAlignment) + { + kind = TokenKind.ArgumentWithAlignmentAndFormat; + } sb.Append(':'); break; + case ',': + if (kind == TokenKind.Argument) + { + kind = TokenKind.ArgumentWithAlignment; + } + sb.Append(','); + break; default: sb.Append((char)next); break; diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs index 8f1bcc552..789253ad4 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs @@ -1168,6 +1168,11 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor writer.WriteToken(Interpolation.LBrace, "{"); interpolation.Expression.AcceptVisitor(this); + if (interpolation.Alignment != 0) + { + writer.WriteToken(Roles.Comma, ","); + writer.WritePrimitiveValue(interpolation.Alignment); + } if (interpolation.Suffix != null) { writer.WriteToken(Roles.Colon, ":"); diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/InterpolatedStringExpression.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/InterpolatedStringExpression.cs index 9f7c6ab47..562755a0a 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/InterpolatedStringExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/InterpolatedStringExpression.cs @@ -20,6 +20,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } + public InterpolatedStringExpression(IList content) + { + Content.AddRange(content); + } + public override void AcceptVisitor(IAstVisitor visitor) { visitor.VisitInterpolatedStringExpression(this); @@ -83,7 +88,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } /// - /// { Expression } + /// { Expression , Alignment : Suffix } /// public class Interpolation : InterpolatedStringContent { @@ -99,6 +104,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax set { SetChildByRole(Roles.Expression, value); } } + public int Alignment { get; } + public string Suffix { get; } public CSharpTokenNode RBraceToken { @@ -110,9 +117,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } - public Interpolation(Expression expression, string suffix = null) + public Interpolation(Expression expression, int alignment = 0, string suffix = null) { Expression = expression; + Alignment = alignment; Suffix = suffix; }