mirror of
https://github.com/nsnail/spectre.console.git
synced 2025-04-14 16:02:50 +08:00
Introduce MarkupInterpolated and MarkupLineInterpolated extensions (#761)
* Introduce MarkupInterpolated and MarkupLineInterpolated extensions These new methods enable easily writing markup with a nice and intuitive syntax without having to worry about escaping the markup. ```csharp string input = args[0]; string output = Process(input); AnsiConsole.MarkupLineInterpolated($"[blue]{input}[/] -> [green]{output}[/]"); ``` The `Interpolated` suffix was inspired by the Entity Framework Core [FromSqlInterpolated][1] method. [1]: https://docs.microsoft.com/en-us/ef/core/querying/raw-sql#passing-parameters * Fixing whitespace to match file scoped namespaces * Adding FromInterpolated helper to Markup widget Allows automatic handling of interpolated strings to be used on the Markup widget. This would be helpful for people working with Tables and the Tree control who would not be using the MarkupInterpolated methods. * Documentation for markup interpolated methods. Co-authored-by: Cédric Luthi <cedric.luthi@gmail.com>
This commit is contained in:
parent
1f2629e2e1
commit
1d8154f9b0
@ -60,6 +60,10 @@ Spectre.Console will tell your terminal to use the color that is configured in t
|
||||
If you are using an 8 or 24-bit color for the foreground text, it is recommended that you also set an appropriate
|
||||
background color to match.
|
||||
|
||||
**Do** escape data when outputting any user input or any external data via Markup using the [`EscapeMarkup`](xref:M:Spectre.Console.Markup.Escape(System.String)) method on the data. Any user input containing `[` or `]` will likely cause a runtime error while rendering otherwise.
|
||||
|
||||
**Consider** replacing `Markup` and `MarkupLine` with [`MarkupInterpolated`](xref:M:Spectre.Console.AnsiConsole.MarkupInterpolated(System.FormattableString)) and [`MarkupLineInterpolated`](xref:M:Spectre.Console.AnsiConsole.MarkupLineInterpolated(System.FormattableString)). Both these methods will automatically escape all data in the interpolated string holes. When working with widgets such as the Table and Tree, consider using [`Markup.FromInterpolated`](xref:M:Spectre.Console.Markup.FromInterpolated(System.FormattableString,Spectre.Console.Style)) to generate an `IRenderable` from an interpolated string.
|
||||
|
||||
### Live-Rendering Best Practices
|
||||
|
||||
Spectre.Console has a variety of [live-rendering capabilities](live) widgets. These widgets can be used to display data
|
||||
|
@ -63,6 +63,15 @@ You can also use the `Markup.Escape` method.
|
||||
```csharp
|
||||
AnsiConsole.Markup("[red]{0}[/]", Markup.Escape("Hello [World]"));
|
||||
```
|
||||
|
||||
## Escaping Interpolated Strings
|
||||
|
||||
When working with interpolated string, you can use the `MarkupInterpolated` and `MarkupInterpolatedLine` methods to automatically escape the values in the interpolated string holes.
|
||||
|
||||
```csharp
|
||||
AnsiConsole.MarkupInterpolated("[red]{0}[/]", "Hello [World]");
|
||||
```
|
||||
|
||||
## Setting background color
|
||||
|
||||
You can set the background color in markup by prefixing the color with
|
||||
|
@ -24,6 +24,24 @@ public static partial class AnsiConsole
|
||||
Console.Markup(format, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the specified markup to the console.
|
||||
/// <para/>
|
||||
/// All interpolation holes which contain a string are automatically escaped so you must not call <see cref="StringExtensions.EscapeMarkup"/>.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// string input = args[0];
|
||||
/// string output = Process(input);
|
||||
/// AnsiConsole.MarkupInterpolated($"[blue]{input}[/] -> [green]{output}[/]");
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="value">The interpolated string value to write.</param>
|
||||
public static void MarkupInterpolated(FormattableString value)
|
||||
{
|
||||
Console.MarkupInterpolated(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the specified markup to the console.
|
||||
/// </summary>
|
||||
@ -35,6 +53,25 @@ public static partial class AnsiConsole
|
||||
Console.Markup(provider, format, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the specified markup to the console.
|
||||
/// <para/>
|
||||
/// All interpolation holes which contain a string are automatically escaped so you must not call <see cref="StringExtensions.EscapeMarkup"/>.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// string input = args[0];
|
||||
/// string output = Process(input);
|
||||
/// AnsiConsole.MarkupInterpolated(CultureInfo.InvariantCulture, $"[blue]{input}[/] -> [green]{output}[/]");
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="provider">An object that supplies culture-specific formatting information.</param>
|
||||
/// <param name="value">The interpolated string value to write.</param>
|
||||
public static void MarkupInterpolated(IFormatProvider provider, FormattableString value)
|
||||
{
|
||||
Console.MarkupInterpolated(provider, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the specified markup, followed by the current line terminator, to the console.
|
||||
/// </summary>
|
||||
@ -54,6 +91,24 @@ public static partial class AnsiConsole
|
||||
Console.MarkupLine(format, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the specified markup, followed by the current line terminator, to the console.
|
||||
/// <para/>
|
||||
/// All interpolation holes which contain a string are automatically escaped so you must not call <see cref="StringExtensions.EscapeMarkup"/>.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// string input = args[0];
|
||||
/// string output = Process(input);
|
||||
/// AnsiConsole.MarkupLineInterpolated($"[blue]{input}[/] -> [green]{output}[/]");
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="value">The interpolated string value to write.</param>
|
||||
public static void MarkupLineInterpolated(FormattableString value)
|
||||
{
|
||||
Console.MarkupLineInterpolated(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the specified markup, followed by the current line terminator, to the console.
|
||||
/// </summary>
|
||||
@ -64,4 +119,23 @@ public static partial class AnsiConsole
|
||||
{
|
||||
Console.MarkupLine(provider, format, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the specified markup, followed by the current line terminator, to the console.
|
||||
/// <para/>
|
||||
/// All interpolation holes which contain a string are automatically escaped so you must not call <see cref="StringExtensions.EscapeMarkup"/>.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// string input = args[0];
|
||||
/// string output = Process(input);
|
||||
/// AnsiConsole.MarkupLineInterpolated(CultureInfo.InvariantCulture, $"[blue]{input}[/] -> [green]{output}[/]");
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="provider">An object that supplies culture-specific formatting information.</param>
|
||||
/// <param name="value">The interpolated string value to write.</param>
|
||||
public static void MarkupLineInterpolated(IFormatProvider provider, FormattableString value)
|
||||
{
|
||||
Console.MarkupLineInterpolated(provider, value);
|
||||
}
|
||||
}
|
@ -16,6 +16,25 @@ public static partial class AnsiConsoleExtensions
|
||||
Markup(console, CultureInfo.CurrentCulture, format, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the specified markup to the console.
|
||||
/// <para/>
|
||||
/// All interpolation holes which contain a string are automatically escaped so you must not call <see cref="StringExtensions.EscapeMarkup"/>.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// string input = args[0];
|
||||
/// string output = Process(input);
|
||||
/// console.MarkupInterpolated($"[blue]{input}[/] -> [green]{output}[/]");
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="console">The console to write to.</param>
|
||||
/// <param name="value">The interpolated string value to write.</param>
|
||||
public static void MarkupInterpolated(this IAnsiConsole console, FormattableString value)
|
||||
{
|
||||
MarkupInterpolated(console, CultureInfo.CurrentCulture, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the specified markup to the console.
|
||||
/// </summary>
|
||||
@ -28,6 +47,26 @@ public static partial class AnsiConsoleExtensions
|
||||
Markup(console, string.Format(provider, format, args));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the specified markup to the console.
|
||||
/// <para/>
|
||||
/// All interpolation holes which contain a string are automatically escaped so you must not call <see cref="StringExtensions.EscapeMarkup"/>.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// string input = args[0];
|
||||
/// string output = Process(input);
|
||||
/// console.MarkupInterpolated(CultureInfo.InvariantCulture, $"[blue]{input}[/] -> [green]{output}[/]");
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="console">The console to write to.</param>
|
||||
/// <param name="provider">An object that supplies culture-specific formatting information.</param>
|
||||
/// <param name="value">The interpolated string value to write.</param>
|
||||
public static void MarkupInterpolated(this IAnsiConsole console, IFormatProvider provider, FormattableString value)
|
||||
{
|
||||
Markup(console, Console.Markup.EscapeInterpolated(provider, value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the specified markup to the console.
|
||||
/// </summary>
|
||||
@ -49,6 +88,25 @@ public static partial class AnsiConsoleExtensions
|
||||
MarkupLine(console, CultureInfo.CurrentCulture, format, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the specified markup, followed by the current line terminator, to the console.
|
||||
/// <para/>
|
||||
/// All interpolation holes which contain a string are automatically escaped so you must not call <see cref="StringExtensions.EscapeMarkup"/>.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// string input = args[0];
|
||||
/// string output = Process(input);
|
||||
/// console.MarkupLineInterpolated($"[blue]{input}[/] -> [green]{output}[/]");
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="console">The console to write to.</param>
|
||||
/// <param name="value">The interpolated string value to write.</param>
|
||||
public static void MarkupLineInterpolated(this IAnsiConsole console, FormattableString value)
|
||||
{
|
||||
MarkupLineInterpolated(console, CultureInfo.CurrentCulture, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the specified markup, followed by the current line terminator, to the console.
|
||||
/// </summary>
|
||||
@ -70,4 +128,24 @@ public static partial class AnsiConsoleExtensions
|
||||
{
|
||||
Markup(console, provider, format + Environment.NewLine, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the specified markup, followed by the current line terminator, to the console.
|
||||
/// <para/>
|
||||
/// All interpolation holes which contain a string are automatically escaped so you must not call <see cref="StringExtensions.EscapeMarkup"/>.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// string input = args[0];
|
||||
/// string output = Process(input);
|
||||
/// console.MarkupLineInterpolated(CultureInfo.InvariantCulture, $"[blue]{input}[/] -> [green]{output}[/]");
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="console">The console to write to.</param>
|
||||
/// <param name="provider">An object that supplies culture-specific formatting information.</param>
|
||||
/// <param name="value">The interpolated string value to write.</param>
|
||||
public static void MarkupLineInterpolated(this IAnsiConsole console, IFormatProvider provider, FormattableString value)
|
||||
{
|
||||
MarkupLine(console, Console.Markup.EscapeInterpolated(provider, value));
|
||||
}
|
||||
}
|
@ -54,6 +54,29 @@ public sealed class Markup : Renderable, IAlignable, IOverflowable
|
||||
return ((IRenderable)_paragraph).Render(context, maxWidth);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new instance of a Markup widget from an interpolated string.
|
||||
/// </summary>
|
||||
/// <param name="value">The interpolated string value to write.</param>
|
||||
/// <param name="style">The style of the text.</param>
|
||||
/// <returns>A new markup instance.</returns>
|
||||
public static Markup FromInterpolated(FormattableString value, Style? style = null)
|
||||
{
|
||||
return FromInterpolated(CultureInfo.CurrentCulture, value, style);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new instance of a Markup widget from an interpolated string.
|
||||
/// </summary>
|
||||
/// <param name="provider">The format provider to use.</param>
|
||||
/// <param name="value">The interpolated string value to write.</param>
|
||||
/// <param name="style">The style of the text.</param>
|
||||
/// <returns>A new markup instance.</returns>
|
||||
public static Markup FromInterpolated(IFormatProvider provider, FormattableString value, Style? style = null)
|
||||
{
|
||||
return new Markup(EscapeInterpolated(provider, value), style);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Escapes text so that it won’t be interpreted as markup.
|
||||
/// </summary>
|
||||
@ -83,4 +106,10 @@ public sealed class Markup : Renderable, IAlignable, IOverflowable
|
||||
|
||||
return text.RemoveMarkup();
|
||||
}
|
||||
|
||||
internal static string EscapeInterpolated(IFormatProvider provider, FormattableString value)
|
||||
{
|
||||
object?[] args = value.GetArguments().Select(arg => arg is string s ? s.EscapeMarkup() : arg).ToArray();
|
||||
return string.Format(provider, value.Format, args);
|
||||
}
|
||||
}
|
@ -72,6 +72,25 @@ public sealed class MarkupTests
|
||||
// Then
|
||||
result.ShouldBe(expected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Hello", "World", "\x1B[38;5;11mHello\x1B[0m \x1B[38;5;9mWorld\x1B[0m 2021-02-03")]
|
||||
[InlineData("Hello", "World [", "\x1B[38;5;11mHello\x1B[0m \x1B[38;5;9mWorld [\x1B[0m 2021-02-03")]
|
||||
[InlineData("Hello", "World ]", "\x1B[38;5;11mHello\x1B[0m \x1B[38;5;9mWorld ]\x1B[0m 2021-02-03")]
|
||||
[InlineData("[Hello]", "World", "\x1B[38;5;11m[Hello]\x1B[0m \x1B[38;5;9mWorld\x1B[0m 2021-02-03")]
|
||||
[InlineData("[[Hello]]", "[World]", "\x1B[38;5;11m[[Hello]]\x1B[0m \x1B[38;5;9m[World]\x1B[0m 2021-02-03")]
|
||||
public void Should_Escape_Markup_When_Using_MarkupInterpolated(string input1, string input2, string expected)
|
||||
{
|
||||
// Given
|
||||
var console = new TestConsole().EmitAnsiSequences();
|
||||
var date = new DateTime(2021, 2, 3);
|
||||
|
||||
// When
|
||||
console.MarkupInterpolated($"[yellow]{input1}[/] [red]{input2}[/] {date:yyyy-MM-dd}");
|
||||
|
||||
// Then
|
||||
console.Output.ShouldBe(expected);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@ -134,4 +153,25 @@ public sealed class MarkupTests
|
||||
console.Output.NormalizeLineEndings()
|
||||
.ShouldBe("{\n");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Can_Use_Interpolated_Markup_As_IRenderable()
|
||||
{
|
||||
// Given
|
||||
var console = new TestConsole();
|
||||
const string Num = "[value[";
|
||||
var table = new Table().AddColumns("First Column");
|
||||
table.AddRow(Markup.FromInterpolated($"Result: {Num}"));
|
||||
|
||||
// When
|
||||
console.Write(table);
|
||||
|
||||
// Then
|
||||
console.Output.NormalizeLineEndings().ShouldBe(@"┌─────────────────┐
|
||||
│ First Column │
|
||||
├─────────────────┤
|
||||
│ Result: [value[ │
|
||||
└─────────────────┘
|
||||
".NormalizeLineEndings());
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user