-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathTextFormatter.cs
More file actions
135 lines (105 loc) · 2.81 KB
/
TextFormatter.cs
File metadata and controls
135 lines (105 loc) · 2.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
namespace Menees.Chords.Formatters;
#region Using Directives
using System.Collections.Generic;
using System.Text;
#endregion
/// <summary>
/// Formats an <see cref="IEntryContainer"/> as flat or indented text.
/// </summary>
public sealed class TextFormatter : ContainerFormatter
{
#region Private Data Members
private readonly string? indent;
// Start at -1 so we'll be at level 0 when we begin the root container.
private int level = -1;
private string? text;
private StringBuilder? builder;
#endregion
#region Constructors
/// <summary>
/// Creates a new instance.
/// </summary>
/// <param name="container">The container to format.</param>
/// <param name="indent">The text to use for each level of indentation. This can be null/empty,
/// a tab character, multiple spaces, or a custom pattern like "--->".</param>
public TextFormatter(IEntryContainer container, string? indent = null)
: base(container)
{
this.indent = indent;
}
#endregion
#region Public Methods
/// <summary>
/// Trims trailing whitespace from <paramref name="value"/>.
/// </summary>
/// <param name="value">The builder to trim.</param>
/// <returns>The input <paramref name="value"/>.</returns>
public static StringBuilder TrimEnd(StringBuilder value)
{
Conditions.RequireNonNull(value);
while (value.Length > 0 && char.IsWhiteSpace(value[^1]))
{
value.Length--;
}
return value;
}
/// <inheritdoc/>
public override string ToString()
{
if (this.text is null)
{
this.Format();
}
return this.text ?? string.Empty;
}
#endregion
#region Protected Methods
/// <inheritdoc/>
protected override void Format(Entry entry, IReadOnlyCollection<IEntryContainer> hierarchy)
{
Conditions.RequireNonNull(this.builder);
Indent();
string text = entry.ToString();
foreach (char ch in text)
{
this.builder.Append(ch);
// If a formatted entry spans multiple lines, then we need to indent subsequent lines too.
// Inspired by Hans: https://stackoverflow.com/a/2547800/1882616
if (ch == '\n')
{
Indent();
}
}
this.builder.AppendLine();
void Indent()
{
if (!string.IsNullOrEmpty(this.indent))
{
for (int i = 0; i < this.level; i++)
{
this.builder.Append(this.indent);
}
}
}
}
/// <inheritdoc/>
protected override void BeginContainer(IEntryContainer container, IReadOnlyCollection<IEntryContainer> hierarchy)
{
base.BeginContainer(container, hierarchy);
this.builder ??= new();
this.level++;
}
/// <inheritdoc/>
protected override void EndContainer(IEntryContainer container, IReadOnlyCollection<IEntryContainer> hierarchy)
{
Conditions.RequireNonNull(this.builder);
base.EndContainer(container, hierarchy);
this.level--;
if (hierarchy.Count == 0)
{
TrimEnd(this.builder);
this.text = this.builder.ToString();
}
}
#endregion
}