mirror of
https://github.com/nsnail/spectre.console.git
synced 2025-06-19 21:38:16 +08:00
Add support for recording console output
This commit adds support for recording console output as well as exporting it to either text or HTML. A user can also provide their own encoder if they wish.
This commit is contained in:

committed by
Patrik Svensson

parent
b197f278ed
commit
cd0d182f12
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Spectre.Console.Rendering;
|
||||
|
||||
namespace Spectre.Console.Internal
|
||||
{
|
||||
@ -48,21 +49,14 @@ namespace Spectre.Console.Internal
|
||||
_ansiBuilder = new AnsiBuilder(Capabilities, linkHasher);
|
||||
}
|
||||
|
||||
public void Write(string text, Style style)
|
||||
public void Write(Segment segment)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
style ??= Style.Plain;
|
||||
|
||||
var parts = text.NormalizeLineEndings().Split(new[] { '\n' });
|
||||
var parts = segment.Text.NormalizeLineEndings().Split(new[] { '\n' });
|
||||
foreach (var (_, _, last, part) in parts.Enumerate())
|
||||
{
|
||||
if (!string.IsNullOrEmpty(part))
|
||||
{
|
||||
_out.Write(_ansiBuilder.GetAnsi(part, style));
|
||||
_out.Write(_ansiBuilder.GetAnsi(part, segment.Style));
|
||||
}
|
||||
|
||||
if (!last)
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Spectre.Console.Rendering;
|
||||
|
||||
namespace Spectre.Console.Internal
|
||||
{
|
||||
@ -61,14 +62,14 @@ namespace Spectre.Console.Internal
|
||||
Capabilities = capabilities;
|
||||
}
|
||||
|
||||
public void Write(string text, Style style)
|
||||
public void Write(Segment segment)
|
||||
{
|
||||
if (_lastStyle?.Equals(style) != true)
|
||||
if (_lastStyle?.Equals(segment.Style) != true)
|
||||
{
|
||||
SetStyle(style);
|
||||
SetStyle(segment.Style);
|
||||
}
|
||||
|
||||
_out.Write(text.NormalizeLineEndings(native: true));
|
||||
_out.Write(segment.Text.NormalizeLineEndings(native: true));
|
||||
}
|
||||
|
||||
private void SetStyle(Style style)
|
||||
|
114
src/Spectre.Console/Internal/HtmlEncoder.cs
Normal file
114
src/Spectre.Console/Internal/HtmlEncoder.cs
Normal file
@ -0,0 +1,114 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Spectre.Console.Rendering;
|
||||
|
||||
namespace Spectre.Console.Internal
|
||||
{
|
||||
internal sealed class HtmlEncoder : IAnsiConsoleEncoder
|
||||
{
|
||||
public string Encode(IEnumerable<Segment> segments)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
|
||||
builder.Append("<pre style=\"font-size:90%;font-family:consolas,'Courier New',monospace\">\n");
|
||||
|
||||
foreach (var (_, first, _, segment) in segments.Enumerate())
|
||||
{
|
||||
if (segment.Text == "\n" && !first)
|
||||
{
|
||||
builder.Append('\n');
|
||||
continue;
|
||||
}
|
||||
|
||||
var parts = segment.Text.Split(new[] { '\n' }, StringSplitOptions.None);
|
||||
foreach (var (_, _, last, line) in parts.Enumerate())
|
||||
{
|
||||
if (string.IsNullOrEmpty(line))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
builder.Append("<span");
|
||||
if (!segment.Style.Equals(Style.Plain))
|
||||
{
|
||||
builder.Append(" style=\"");
|
||||
builder.Append(BuildCss(segment.Style));
|
||||
builder.Append('"');
|
||||
}
|
||||
|
||||
builder.Append('>');
|
||||
builder.Append(line);
|
||||
builder.Append("</span>");
|
||||
|
||||
if (parts.Length > 1 && !last)
|
||||
{
|
||||
builder.Append('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
builder.Append("</pre>");
|
||||
|
||||
return builder.ToString().TrimEnd('\n');
|
||||
}
|
||||
|
||||
private static string BuildCss(Style style)
|
||||
{
|
||||
var css = new List<string>();
|
||||
|
||||
var foreground = style.Foreground;
|
||||
var background = style.Background;
|
||||
|
||||
if ((style.Decoration & Decoration.Invert) != 0)
|
||||
{
|
||||
var temp = foreground;
|
||||
foreground = background;
|
||||
background = temp;
|
||||
}
|
||||
|
||||
if ((style.Decoration & Decoration.Dim) != 0)
|
||||
{
|
||||
var blender = background;
|
||||
if (blender.Equals(Color.Default))
|
||||
{
|
||||
blender = Color.White;
|
||||
}
|
||||
|
||||
foreground = foreground.Blend(blender, 0.5f);
|
||||
}
|
||||
|
||||
if (!foreground.Equals(Color.Default))
|
||||
{
|
||||
css.Add($"color: #{foreground.ToHex()}");
|
||||
}
|
||||
|
||||
if (!background.Equals(Color.Default))
|
||||
{
|
||||
css.Add($"background-color: #{background.ToHex()}");
|
||||
}
|
||||
|
||||
if ((style.Decoration & Decoration.Bold) != 0)
|
||||
{
|
||||
css.Add("font-weight: bold");
|
||||
}
|
||||
|
||||
if ((style.Decoration & Decoration.Bold) != 0)
|
||||
{
|
||||
css.Add("font-style: italic");
|
||||
}
|
||||
|
||||
if ((style.Decoration & Decoration.Underline) != 0)
|
||||
{
|
||||
css.Add("text-decoration: underline");
|
||||
}
|
||||
|
||||
if ((style.Decoration & Decoration.Strikethrough) != 0)
|
||||
{
|
||||
css.Add("text-decoration: line-through");
|
||||
}
|
||||
|
||||
return string.Join(";", css);
|
||||
}
|
||||
}
|
||||
}
|
21
src/Spectre.Console/Internal/TextEncoder.cs
Normal file
21
src/Spectre.Console/Internal/TextEncoder.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Spectre.Console.Rendering;
|
||||
|
||||
namespace Spectre.Console.Internal
|
||||
{
|
||||
internal sealed class TextEncoder : IAnsiConsoleEncoder
|
||||
{
|
||||
public string Encode(IEnumerable<Segment> segments)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
|
||||
foreach (var segment in Segment.Merge(segments))
|
||||
{
|
||||
builder.Append(segment.Text);
|
||||
}
|
||||
|
||||
return builder.ToString().TrimEnd('\n');
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user