BadScript 2
Loading...
Searching...
No Matches
TextWrapper.cs
Go to the documentation of this file.
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Text;
5
7
8namespace CommandLine.Text
9{
13 public class TextWrapper
14 {
15 private string[] lines;
16
17 public TextWrapper(string input)
18 {
19 //start by splitting at newlines and then reinserting the newline as a separate word
20 //Note that on the input side, we can't assume the line-break style at run time so we have to
21 //be able to handle both. We can't use Environment.NewLine because that changes at
22 //_runtime_ and may not match the line-break style that was compiled in
23 lines = input
24 .Replace("\r", "")
25 .Split(new[] { '\n' },
26 StringSplitOptions.None
27 );
28 }
29
46 public TextWrapper WordWrap(int columnWidth)
47 {
48 //ensure we always use at least 1 column even if the client has told us there's no space available
49 columnWidth = Math.Max(1, columnWidth);
50
51 lines = lines
52 .SelectMany(line => WordWrapLine(line, columnWidth))
53 .ToArray();
54
55 return this;
56 }
57
63 public TextWrapper Indent(int numberOfSpaces)
64 {
65 lines = lines
66 .Select(line => numberOfSpaces.Spaces() + line)
67 .ToArray();
68
69 return this;
70 }
71
76 public string ToText()
77 {
78 //return the whole thing as a single string
79 return string.Join(Environment.NewLine, lines);
80 }
81
93 public static string WrapAndIndentText(string input, int indentLevel, int columnWidth)
94 {
95 return new TextWrapper(input)
96 .WordWrap(columnWidth)
97 .Indent(indentLevel)
98 .ToText();
99 }
100
101
102 private string[] WordWrapLine(string line, int columnWidth)
103 {
104 //create a list of individual lines generated from the supplied line
105
106 //When handling sub-indentation we must always reserve at least one column for text!
107 string unindentedLine = line.TrimStart();
108 int currentIndentLevel = Math.Min(line.Length - unindentedLine.Length, columnWidth - 1);
109 columnWidth -= currentIndentLevel;
110
111 return unindentedLine.Split(' ')
112 .Aggregate(new List<StringBuilder>(),
113 (lineList, word) =>
114 AddWordToLastLineOrCreateNewLineIfNecessary(lineList, word, columnWidth)
115 )
116 .Select(builder => currentIndentLevel.Spaces() +
117 builder.ToString()
118 .TrimEnd()
119 )
120 .ToArray();
121 }
122
134 private static List<StringBuilder> AddWordToLastLineOrCreateNewLineIfNecessary(
135 List<StringBuilder> lines,
136 string word,
137 int columnWidth)
138 {
139 //The current indentation level is based on the previous line but we need to be careful
140 string previousLine = lines.LastOrDefault()
141 ?.ToString() ??
142 string.Empty;
143
144 bool wouldWrap = !lines.Any() || (word.Length > 0 && previousLine.Length + word.Length > columnWidth);
145
146 if (!wouldWrap)
147 {
148 //The usual case is we just append the 'word' and a space to the current line
149 //Note that trailing spaces will get removed later when we turn the line list
150 //into a single string
151 lines.Last()
152 .Append(word + ' ');
153 }
154 else
155 {
156 //The 'while' here is to take account of the possibility of someone providing a word
157 //which just can't fit in the current column. In that case we just split it at the
158 //column end.
159 //That's a rare case though - most of the time we'll succeed in a single pass without
160 //having to split
161 //Note that we always do at least one pass even if the 'word' is empty in order to
162 //honour sub-indentation and extra spaces within strings
163 do
164 {
165 int availableCharacters = Math.Min(columnWidth, word.Length);
166 string segmentToAdd = LeftString(word, availableCharacters) + ' ';
167 lines.Add(new StringBuilder(segmentToAdd));
168 word = RightString(word, availableCharacters);
169 }
170 while (word.Length > 0);
171 }
172
173 return lines;
174 }
175
176
180 private static string RightString(string str, int n)
181 {
182 return n >= str.Length || str.Length == 0
183 ? string.Empty
184 : str.Substring(n);
185 }
186
190 private static string LeftString(string str, int n)
191 {
192 return n >= str.Length || str.Length == 0
193 ? str
194 : str.Substring(0, n);
195 }
196 }
197}
A utility class to word-wrap and indent blocks of text.
static string RightString(string str, int n)
Return the right part of a string in a way that compensates for Substring's deficiencies.
static string LeftString(string str, int n)
Return the left part of a string in a way that compensates for Substring's deficiencies.
static List< StringBuilder > AddWordToLastLineOrCreateNewLineIfNecessary(List< StringBuilder > lines, string word, int columnWidth)
When presented with a word, either append to the last line in the list or start a new line.
string ToText()
Returns the current state of the TextWrapper as a string.
TextWrapper Indent(int numberOfSpaces)
Indent all lines in the TextWrapper by the desired number of spaces.
static string WrapAndIndentText(string input, int indentLevel, int columnWidth)
Convenience method to wraps and indent a string in a single operation.
string[] WordWrapLine(string line, int columnWidth)
TextWrapper WordWrap(int columnWidth)
Splits a string into a words and performs wrapping while also preserving line-breaks and sub-indentat...