mirror of
https://github.com/nsnail/spectre.console.git
synced 2025-12-29 20:05:48 +08:00
Use file scoped namespace declarations
This commit is contained in:
committed by
Phil Scott
parent
1dbaf50935
commit
ec1188b837
@@ -4,296 +4,295 @@ using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Spectre.Console.Rendering;
|
||||
|
||||
namespace Spectre.Console
|
||||
namespace Spectre.Console;
|
||||
|
||||
/// <summary>
|
||||
/// A paragraph of text where different parts
|
||||
/// of the paragraph can have individual styling.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("{_text,nq}")]
|
||||
public sealed class Paragraph : Renderable, IAlignable, IOverflowable
|
||||
{
|
||||
private readonly List<SegmentLine> _lines;
|
||||
|
||||
/// <summary>
|
||||
/// A paragraph of text where different parts
|
||||
/// of the paragraph can have individual styling.
|
||||
/// Gets or sets the alignment of the whole paragraph.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("{_text,nq}")]
|
||||
public sealed class Paragraph : Renderable, IAlignable, IOverflowable
|
||||
public Justify? Alignment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the text overflow strategy.
|
||||
/// </summary>
|
||||
public Overflow? Overflow { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the character count of the paragraph.
|
||||
/// </summary>
|
||||
public int Length => _lines.Sum(line => line.Length) + Math.Max(0, Lines - 1);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of lines in the paragraph.
|
||||
/// </summary>
|
||||
public int Lines => _lines.Count;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Paragraph"/> class.
|
||||
/// </summary>
|
||||
public Paragraph()
|
||||
{
|
||||
private readonly List<SegmentLine> _lines;
|
||||
_lines = new List<SegmentLine>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the alignment of the whole paragraph.
|
||||
/// </summary>
|
||||
public Justify? Alignment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the text overflow strategy.
|
||||
/// </summary>
|
||||
public Overflow? Overflow { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the character count of the paragraph.
|
||||
/// </summary>
|
||||
public int Length => _lines.Sum(line => line.Length) + Math.Max(0, Lines - 1);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of lines in the paragraph.
|
||||
/// </summary>
|
||||
public int Lines => _lines.Count;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Paragraph"/> class.
|
||||
/// </summary>
|
||||
public Paragraph()
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Paragraph"/> class.
|
||||
/// </summary>
|
||||
/// <param name="text">The text.</param>
|
||||
/// <param name="style">The style of the text or <see cref="Style.Plain"/> if <see langword="null"/>.</param>
|
||||
public Paragraph(string text, Style? style = null)
|
||||
: this()
|
||||
{
|
||||
if (text is null)
|
||||
{
|
||||
_lines = new List<SegmentLine>();
|
||||
throw new ArgumentNullException(nameof(text));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Paragraph"/> class.
|
||||
/// </summary>
|
||||
/// <param name="text">The text.</param>
|
||||
/// <param name="style">The style of the text or <see cref="Style.Plain"/> if <see langword="null"/>.</param>
|
||||
public Paragraph(string text, Style? style = null)
|
||||
: this()
|
||||
{
|
||||
if (text is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(text));
|
||||
}
|
||||
Append(text, style);
|
||||
}
|
||||
|
||||
Append(text, style);
|
||||
/// <summary>
|
||||
/// Appends some text to this paragraph.
|
||||
/// </summary>
|
||||
/// <param name="text">The text to append.</param>
|
||||
/// <param name="style">The style of the appended text or <see cref="Style.Plain"/> if <see langword="null"/>.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public Paragraph Append(string text, Style? style = null)
|
||||
{
|
||||
if (text is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(text));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Appends some text to this paragraph.
|
||||
/// </summary>
|
||||
/// <param name="text">The text to append.</param>
|
||||
/// <param name="style">The style of the appended text or <see cref="Style.Plain"/> if <see langword="null"/>.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public Paragraph Append(string text, Style? style = null)
|
||||
foreach (var (_, first, last, part) in text.SplitLines().Enumerate())
|
||||
{
|
||||
if (text is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(text));
|
||||
}
|
||||
var current = part;
|
||||
|
||||
foreach (var (_, first, last, part) in text.SplitLines().Enumerate())
|
||||
if (first)
|
||||
{
|
||||
var current = part;
|
||||
|
||||
if (first)
|
||||
var line = _lines.LastOrDefault();
|
||||
if (line == null)
|
||||
{
|
||||
var line = _lines.LastOrDefault();
|
||||
if (line == null)
|
||||
{
|
||||
_lines.Add(new SegmentLine());
|
||||
line = _lines.Last();
|
||||
}
|
||||
_lines.Add(new SegmentLine());
|
||||
line = _lines.Last();
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(current))
|
||||
{
|
||||
line.Add(Segment.Empty);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var span in current.SplitWords())
|
||||
{
|
||||
line.Add(new Segment(span, style ?? Style.Plain));
|
||||
}
|
||||
}
|
||||
if (string.IsNullOrEmpty(current))
|
||||
{
|
||||
line.Add(Segment.Empty);
|
||||
}
|
||||
else
|
||||
{
|
||||
var line = new SegmentLine();
|
||||
|
||||
if (string.IsNullOrEmpty(current))
|
||||
foreach (var span in current.SplitWords())
|
||||
{
|
||||
line.Add(Segment.Empty);
|
||||
line.Add(new Segment(span, style ?? Style.Plain));
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var span in current.SplitWords())
|
||||
{
|
||||
line.Add(new Segment(span, style ?? Style.Plain));
|
||||
}
|
||||
}
|
||||
|
||||
_lines.Add(line);
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override Measurement Measure(RenderContext context, int maxWidth)
|
||||
{
|
||||
if (_lines.Count == 0)
|
||||
else
|
||||
{
|
||||
return new Measurement(0, 0);
|
||||
}
|
||||
var line = new SegmentLine();
|
||||
|
||||
var min = _lines.Max(line => line.Max(segment => segment.CellCount()));
|
||||
var max = _lines.Max(x => x.CellCount());
|
||||
|
||||
return new Measurement(min, Math.Min(max, maxWidth));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override IEnumerable<Segment> Render(RenderContext context, int maxWidth)
|
||||
{
|
||||
if (context is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
if (_lines.Count == 0)
|
||||
{
|
||||
return Array.Empty<Segment>();
|
||||
}
|
||||
|
||||
var lines = context.SingleLine
|
||||
? new List<SegmentLine>(_lines)
|
||||
: SplitLines(maxWidth);
|
||||
|
||||
// Justify lines
|
||||
var justification = context.Justification ?? Alignment ?? Justify.Left;
|
||||
if (justification != Justify.Left)
|
||||
{
|
||||
foreach (var line in lines)
|
||||
if (string.IsNullOrEmpty(current))
|
||||
{
|
||||
Aligner.Align(context, line, justification, maxWidth);
|
||||
}
|
||||
}
|
||||
|
||||
if (context.SingleLine)
|
||||
{
|
||||
// Return the first line
|
||||
return lines[0].Where(segment => !segment.IsLineBreak);
|
||||
}
|
||||
|
||||
return new SegmentLineEnumerator(lines);
|
||||
}
|
||||
|
||||
private List<SegmentLine> Clone()
|
||||
{
|
||||
var result = new List<SegmentLine>();
|
||||
|
||||
foreach (var line in _lines)
|
||||
{
|
||||
var newLine = new SegmentLine();
|
||||
foreach (var segment in line)
|
||||
{
|
||||
newLine.Add(segment);
|
||||
}
|
||||
|
||||
result.Add(newLine);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<SegmentLine> SplitLines(int maxWidth)
|
||||
{
|
||||
if (maxWidth <= 0)
|
||||
{
|
||||
// Nothing fits, so return an empty line.
|
||||
return new List<SegmentLine>();
|
||||
}
|
||||
|
||||
if (_lines.Max(x => x.CellCount()) <= maxWidth)
|
||||
{
|
||||
return Clone();
|
||||
}
|
||||
|
||||
var lines = new List<SegmentLine>();
|
||||
var line = new SegmentLine();
|
||||
|
||||
var newLine = true;
|
||||
|
||||
using var iterator = new SegmentLineIterator(_lines);
|
||||
var queue = new Queue<Segment>();
|
||||
while (true)
|
||||
{
|
||||
var current = (Segment?)null;
|
||||
if (queue.Count == 0)
|
||||
{
|
||||
if (!iterator.MoveNext())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
current = iterator.Current;
|
||||
line.Add(Segment.Empty);
|
||||
}
|
||||
else
|
||||
{
|
||||
current = queue.Dequeue();
|
||||
}
|
||||
|
||||
if (current == null)
|
||||
{
|
||||
throw new InvalidOperationException("Iterator returned empty segment.");
|
||||
}
|
||||
|
||||
newLine = false;
|
||||
|
||||
if (current.IsLineBreak)
|
||||
{
|
||||
lines.Add(line);
|
||||
line = new SegmentLine();
|
||||
newLine = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
var length = current.CellCount();
|
||||
if (length > maxWidth)
|
||||
{
|
||||
// The current segment is longer than the width of the console,
|
||||
// so we will need to crop it up, into new segments.
|
||||
var segments = Segment.SplitOverflow(current, Overflow, maxWidth);
|
||||
if (segments.Count > 0)
|
||||
foreach (var span in current.SplitWords())
|
||||
{
|
||||
if (line.CellCount() + segments[0].CellCount() > maxWidth)
|
||||
{
|
||||
lines.Add(line);
|
||||
line = new SegmentLine();
|
||||
newLine = true;
|
||||
|
||||
segments.ForEach(s => queue.Enqueue(s));
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add the segment and push the rest of them to the queue.
|
||||
line.Add(segments[0]);
|
||||
segments.Skip(1).ForEach(s => queue.Enqueue(s));
|
||||
continue;
|
||||
}
|
||||
line.Add(new Segment(span, style ?? Style.Plain));
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
_lines.Add(line);
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override Measurement Measure(RenderContext context, int maxWidth)
|
||||
{
|
||||
if (_lines.Count == 0)
|
||||
{
|
||||
return new Measurement(0, 0);
|
||||
}
|
||||
|
||||
var min = _lines.Max(line => line.Max(segment => segment.CellCount()));
|
||||
var max = _lines.Max(x => x.CellCount());
|
||||
|
||||
return new Measurement(min, Math.Min(max, maxWidth));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override IEnumerable<Segment> Render(RenderContext context, int maxWidth)
|
||||
{
|
||||
if (context is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
if (_lines.Count == 0)
|
||||
{
|
||||
return Array.Empty<Segment>();
|
||||
}
|
||||
|
||||
var lines = context.SingleLine
|
||||
? new List<SegmentLine>(_lines)
|
||||
: SplitLines(maxWidth);
|
||||
|
||||
// Justify lines
|
||||
var justification = context.Justification ?? Alignment ?? Justify.Left;
|
||||
if (justification != Justify.Left)
|
||||
{
|
||||
foreach (var line in lines)
|
||||
{
|
||||
Aligner.Align(context, line, justification, maxWidth);
|
||||
}
|
||||
}
|
||||
|
||||
if (context.SingleLine)
|
||||
{
|
||||
// Return the first line
|
||||
return lines[0].Where(segment => !segment.IsLineBreak);
|
||||
}
|
||||
|
||||
return new SegmentLineEnumerator(lines);
|
||||
}
|
||||
|
||||
private List<SegmentLine> Clone()
|
||||
{
|
||||
var result = new List<SegmentLine>();
|
||||
|
||||
foreach (var line in _lines)
|
||||
{
|
||||
var newLine = new SegmentLine();
|
||||
foreach (var segment in line)
|
||||
{
|
||||
newLine.Add(segment);
|
||||
}
|
||||
|
||||
result.Add(newLine);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<SegmentLine> SplitLines(int maxWidth)
|
||||
{
|
||||
if (maxWidth <= 0)
|
||||
{
|
||||
// Nothing fits, so return an empty line.
|
||||
return new List<SegmentLine>();
|
||||
}
|
||||
|
||||
if (_lines.Max(x => x.CellCount()) <= maxWidth)
|
||||
{
|
||||
return Clone();
|
||||
}
|
||||
|
||||
var lines = new List<SegmentLine>();
|
||||
var line = new SegmentLine();
|
||||
|
||||
var newLine = true;
|
||||
|
||||
using var iterator = new SegmentLineIterator(_lines);
|
||||
var queue = new Queue<Segment>();
|
||||
while (true)
|
||||
{
|
||||
var current = (Segment?)null;
|
||||
if (queue.Count == 0)
|
||||
{
|
||||
if (!iterator.MoveNext())
|
||||
{
|
||||
if (line.CellCount() + length > maxWidth)
|
||||
break;
|
||||
}
|
||||
|
||||
current = iterator.Current;
|
||||
}
|
||||
else
|
||||
{
|
||||
current = queue.Dequeue();
|
||||
}
|
||||
|
||||
if (current == null)
|
||||
{
|
||||
throw new InvalidOperationException("Iterator returned empty segment.");
|
||||
}
|
||||
|
||||
newLine = false;
|
||||
|
||||
if (current.IsLineBreak)
|
||||
{
|
||||
lines.Add(line);
|
||||
line = new SegmentLine();
|
||||
newLine = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
var length = current.CellCount();
|
||||
if (length > maxWidth)
|
||||
{
|
||||
// The current segment is longer than the width of the console,
|
||||
// so we will need to crop it up, into new segments.
|
||||
var segments = Segment.SplitOverflow(current, Overflow, maxWidth);
|
||||
if (segments.Count > 0)
|
||||
{
|
||||
if (line.CellCount() + segments[0].CellCount() > maxWidth)
|
||||
{
|
||||
line.Add(Segment.Empty);
|
||||
lines.Add(line);
|
||||
line = new SegmentLine();
|
||||
newLine = true;
|
||||
|
||||
segments.ForEach(s => queue.Enqueue(s));
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add the segment and push the rest of them to the queue.
|
||||
line.Add(segments[0]);
|
||||
segments.Skip(1).ForEach(s => queue.Enqueue(s));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (newLine && current.IsWhiteSpace)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
newLine = false;
|
||||
|
||||
line.Add(current);
|
||||
}
|
||||
|
||||
// Flush remaining.
|
||||
if (line.Count > 0)
|
||||
else
|
||||
{
|
||||
lines.Add(line);
|
||||
if (line.CellCount() + length > maxWidth)
|
||||
{
|
||||
line.Add(Segment.Empty);
|
||||
lines.Add(line);
|
||||
line = new SegmentLine();
|
||||
newLine = true;
|
||||
}
|
||||
}
|
||||
|
||||
return lines;
|
||||
if (newLine && current.IsWhiteSpace)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
newLine = false;
|
||||
|
||||
line.Add(current);
|
||||
}
|
||||
|
||||
// Flush remaining.
|
||||
if (line.Count > 0)
|
||||
{
|
||||
lines.Add(line);
|
||||
}
|
||||
|
||||
return lines;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user