Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add JSON text renderer #1086

Merged
merged 2 commits into from
Dec 31, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ await Bootstrapper.Factory
{
"../../src/Spectre.Console/**/{!bin,!obj,!packages,!*.Tests,}/**/*.cs",
"../../src/Spectre.Console.Cli/**/{!bin,!obj,!packages,!*.Tests,}/**/*.cs",
"../../src/Spectre.Console.ImageSharp/**/{!bin,!obj,!packages,!*.Tests,}/**/*.cs"
"../../src/Spectre.Console.ImageSharp/**/{!bin,!obj,!packages,!*.Tests,}/**/*.cs",
"../../src/Spectre.Console.Json/**/{!bin,!obj,!packages,!*.Tests,}/**/*.cs"
})
.AddSetting(Constants.ExampleSourceFiles, new List<string>
{
Expand Down
3 changes: 3 additions & 0 deletions docs/input/assets/casts/json-plain.cast
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{"version": 2, "width": 62, "height": 20, "title": "json (plain)", "env": {"TERM": "Spectre.Console"}}
[0, "o", "\u001B[37m\u250C\u2500Some JSON in a panel\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\u001B[0m\r\n\u001B[37m\u2502\u001B[0m \u001B[37m{\u001B[0m \u001B[37m\u2502\u001B[0m\r\n\u001B[37m\u2502\u001B[0m \u001B[34m\u0022hello\u0022\u001B[0m\u001B[37m:\u001B[0m \u001B[32m32\u001B[0m\u001B[37m,\u001B[0m \u001B[37m\u2502\u001B[0m\r\n\u001B[37m\u2502\u001B[0m \u001B[34m\u0022world\u0022\u001B[0m\u001B[37m:\u001B[0m \u001B[37m{\u001B[0m \u001B[37m\u2502\u001B[0m\r\n\u001B[37m\u2502\u001B[0m \u001B[34m\u0022foo\u0022\u001B[0m\u001B[37m:\u001B[0m \u001B[32m21\u001B[0m\u001B[37m,\u001B[0m \u001B[37m\u2502\u001B[0m\r\n\u001B[37m\u2502\u001B[0m \u001B[34m\u0022bar\u0022\u001B[0m\u001B[37m:\u001B[0m \u001B[32m255\u001B[0m\u001B[37m,\u001B[0m \u001B[37m\u2502\u001B[0m\r\n\u001B[37m\u2502\u001B[0m \u001B[34m\u0022baz\u0022\u001B[0m\u001B[37m:\u001B[0m \u001B[37m[\u001B[0m \u001B[37m\u2502\u001B[0m\r\n\u001B[37m\u2502\u001B[0m \u001B[32m0.32\u001B[0m\u001B[37m,\u001B[0m \u001B[37m\u2502\u001B[0m\r\n\u001B[37m\u2502\u001B[0m \u001B[32m0.33e-32\u001B[0m\u001B[37m,\u001B[0m \u001B[37m\u2502\u001B[0m\r\n\u001B[37m\u2502\u001B[0m \u001B[32m0.42e32\u001B[0m\u001B[37m,\u001B[0m \u001B[37m\u2502\u001B[0m\r\n\u001B[37m\u2502\u001B[0m \u001B[32m0.55e\u002B32\u001B[0m\u001B[37m,\u001B[0m \u001B[37m\u2502\u001B[0m\r\n\u001B[37m\u2502\u001B[0m \u001B[37m{\u001B[0m \u001B[37m\u2502\u001B[0m\r\n\u001B[37m\u2502\u001B[0m \u001B[34m\u0022hello\u0022\u001B[0m\u001B[37m:\u001B[0m \u001B[31m\u0022world\u0022\u001B[0m\u001B[37m,\u001B[0m \u001B[37m\u2502\u001B[0m\r\n\u001B[37m\u2502\u001B[0m \u001B[34m\u0022lol\u0022\u001B[0m\u001B[37m:\u001B[0m \u001B[37mnull\u001B[0m \u001B[37m\u2502\u001B[0m\r\n\u001B[37m\u2502\u001B[0m \u001B[37m}\u001B[0m \u001B[37m\u2502\u001B[0m\r\n\u001B[37m\u2502\u001B[0m \u001B[37m]\u001B[0m \u001B[37m\u2502\u001B[0m\r\n\u001B[37m\u2502\u001B[0m \u001B[37m}\u001B[0m \u001B[37m\u2502\u001B[0m\r\n\u001B[37m\u2502\u001B[0m \u001B[37m}\u001B[0m \u001B[37m\u2502\u001B[0m\r\n\u001B[37m\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\u001B[0m\r\n"]

3 changes: 3 additions & 0 deletions docs/input/assets/casts/json-rich.cast
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{"version": 2, "width": 62, "height": 20, "title": "json (rich)", "env": {"TERM": "Spectre.Console"}}
[0, "o", "\u001B[38;5;11m\u256D\u2500Some JSON in a panel\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E\u001B[0m\r\n\u001B[38;5;11m\u2502\u001B[0m \u001B[38;5;8m{\u001B[0m \u001B[38;5;11m\u2502\u001B[0m\r\n\u001B[38;5;11m\u2502\u001B[0m \u001B[38;5;12m\u0022hello\u0022\u001B[0m\u001B[38;5;11m:\u001B[0m \u001B[38;5;2m32\u001B[0m\u001B[38;5;8m,\u001B[0m \u001B[38;5;11m\u2502\u001B[0m\r\n\u001B[38;5;11m\u2502\u001B[0m \u001B[38;5;12m\u0022world\u0022\u001B[0m\u001B[38;5;11m:\u001B[0m \u001B[38;5;8m{\u001B[0m \u001B[38;5;11m\u2502\u001B[0m\r\n\u001B[38;5;11m\u2502\u001B[0m \u001B[38;5;12m\u0022foo\u0022\u001B[0m\u001B[38;5;11m:\u001B[0m \u001B[38;5;2m21\u001B[0m\u001B[38;5;8m,\u001B[0m \u001B[38;5;11m\u2502\u001B[0m\r\n\u001B[38;5;11m\u2502\u001B[0m \u001B[38;5;12m\u0022bar\u0022\u001B[0m\u001B[38;5;11m:\u001B[0m \u001B[38;5;2m255\u001B[0m\u001B[38;5;8m,\u001B[0m \u001B[38;5;11m\u2502\u001B[0m\r\n\u001B[38;5;11m\u2502\u001B[0m \u001B[38;5;12m\u0022baz\u0022\u001B[0m\u001B[38;5;11m:\u001B[0m \u001B[38;5;8m[\u001B[0m \u001B[38;5;11m\u2502\u001B[0m\r\n\u001B[38;5;11m\u2502\u001B[0m \u001B[38;5;2m0.32\u001B[0m\u001B[38;5;8m,\u001B[0m \u001B[38;5;11m\u2502\u001B[0m\r\n\u001B[38;5;11m\u2502\u001B[0m \u001B[38;5;2m0.33e-32\u001B[0m\u001B[38;5;8m,\u001B[0m \u001B[38;5;11m\u2502\u001B[0m\r\n\u001B[38;5;11m\u2502\u001B[0m \u001B[38;5;2m0.42e32\u001B[0m\u001B[38;5;8m,\u001B[0m \u001B[38;5;11m\u2502\u001B[0m\r\n\u001B[38;5;11m\u2502\u001B[0m \u001B[38;5;2m0.55e\u002B32\u001B[0m\u001B[38;5;8m,\u001B[0m \u001B[38;5;11m\u2502\u001B[0m\r\n\u001B[38;5;11m\u2502\u001B[0m \u001B[38;5;8m{\u001B[0m \u001B[38;5;11m\u2502\u001B[0m\r\n\u001B[38;5;11m\u2502\u001B[0m \u001B[38;5;12m\u0022hello\u0022\u001B[0m\u001B[38;5;11m:\u001B[0m \u001B[38;5;9m\u0022world\u0022\u001B[0m\u001B[38;5;8m,\u001B[0m \u001B[38;5;11m\u2502\u001B[0m\r\n\u001B[38;5;11m\u2502\u001B[0m \u001B[38;5;12m\u0022lol\u0022\u001B[0m\u001B[38;5;11m:\u001B[0m \u001B[38;5;8mnull\u001B[0m \u001B[38;5;11m\u2502\u001B[0m\r\n\u001B[38;5;11m\u2502\u001B[0m \u001B[38;5;8m}\u001B[0m \u001B[38;5;11m\u2502\u001B[0m\r\n\u001B[38;5;11m\u2502\u001B[0m \u001B[38;5;8m]\u001B[0m \u001B[38;5;11m\u2502\u001B[0m\r\n\u001B[38;5;11m\u2502\u001B[0m \u001B[38;5;8m}\u001B[0m \u001B[38;5;11m\u2502\u001B[0m\r\n\u001B[38;5;11m\u2502\u001B[0m \u001B[38;5;8m}\u001B[0m \u001B[38;5;11m\u2502\u001B[0m\r\n\u001B[38;5;11m\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F\u001B[0m\r\n"]

69 changes: 69 additions & 0 deletions docs/input/widgets/json.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
Title: JSON
Order: 70
Description: "Use *ImageSharp* to parse images and render them as Ascii art to the console."
Reference: T:Spectre.Console.Json.JsonText
---

To add JSON superpowers to
your console application to render JSON text, you will need to install
the [Spectre.Console.Json](https://www.nuget.org/packages/Spectre.Console.Json) NuGet package.

```text
> dotnet add package Spectre.Console.Json
```

## Loading images

Once you've added the `Spectre.Console.Json` NuGet package,
you can start rendering JSON to the console.

```csharp
using Spectre.Console.Json;

var json = new JsonText(
"""
{
"hello": 32,
"world": {
"foo": 21,
"bar": 255,
"baz": [
0.32, 0.33e-32,
0.42e32, 0.55e+32,
{
"hello": "world",
"lol": null
}
]
}
}
""");

AnsiConsole.Write(
new Panel(json)
.Header("Some JSON in a panel")
.Collapse()
.RoundedBorder()
.BorderColor(Color.Yellow));
```

### Result

<?# AsciiCast cast="json" /?>

## Styling

All the different JSON parts can be customized to have unique styles.

```csharp
AnsiConsole.Write(
new JsonText(json)
.BracesColor(Color.Red)
.BracketColor(Color.Green)
.ColonColor(Color.Blue)
.CommaColor(Color.Red)
.StringColor(Color.Green)
.NumberColor(Color.Blue)
.BooleanColor(Color.Red)
.NullColor(Color.Green));
```
2 changes: 1 addition & 1 deletion dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
]
},
"dotnet-example": {
"version": "1.6.0",
"version": "2.0.0",
"commands": [
"dotnet-example"
]
Expand Down
16 changes: 16 additions & 0 deletions examples/Console/Json/Json.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ExampleTitle>Json</ExampleTitle>
<ExampleDescription>Demonstrates how to print syntax highlighted JSON.</ExampleDescription>
<ExampleGroup>Widgets</ExampleGroup>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\Shared\Shared.csproj" />
<ProjectReference Include="..\..\..\src\Spectre.Console.Json\Spectre.Console.Json.csproj" />
</ItemGroup>

</Project>
36 changes: 36 additions & 0 deletions examples/Console/Json/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using Spectre.Console;
using Spectre.Console.Json;

namespace Json;

public static class Program
{
public static void Main()
{
var json = new JsonText(
"""
{
"hello": 32,
"world": {
"foo": 21,
"bar": 255,
"baz": [
0.32, 0.33e-32,
0.42e32, 0.55e+32,
{
"hello": "world",
"lol": null
}
]
}
}
""");

AnsiConsole.Write(
new Panel(json)
.Header("Some JSON in a panel")
.Collapse()
.RoundedBorder()
.BorderColor(Color.Yellow));
}
}
14 changes: 14 additions & 0 deletions examples/Examples.sln
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spectre.Console.Cli", "..\s
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Layout", "Console\Layout\Layout.csproj", "{A9FDE73A-8452-4CA3-B366-3F900597E132}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Json", "Console\Json\Json.csproj", "{ABE3E734-0756-4D5A-B28A-E6E526D9927D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -521,6 +523,18 @@ Global
{A9FDE73A-8452-4CA3-B366-3F900597E132}.Release|x64.Build.0 = Release|Any CPU
{A9FDE73A-8452-4CA3-B366-3F900597E132}.Release|x86.ActiveCfg = Release|Any CPU
{A9FDE73A-8452-4CA3-B366-3F900597E132}.Release|x86.Build.0 = Release|Any CPU
{ABE3E734-0756-4D5A-B28A-E6E526D9927D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ABE3E734-0756-4D5A-B28A-E6E526D9927D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ABE3E734-0756-4D5A-B28A-E6E526D9927D}.Debug|x64.ActiveCfg = Debug|Any CPU
{ABE3E734-0756-4D5A-B28A-E6E526D9927D}.Debug|x64.Build.0 = Debug|Any CPU
{ABE3E734-0756-4D5A-B28A-E6E526D9927D}.Debug|x86.ActiveCfg = Debug|Any CPU
{ABE3E734-0756-4D5A-B28A-E6E526D9927D}.Debug|x86.Build.0 = Debug|Any CPU
{ABE3E734-0756-4D5A-B28A-E6E526D9927D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ABE3E734-0756-4D5A-B28A-E6E526D9927D}.Release|Any CPU.Build.0 = Release|Any CPU
{ABE3E734-0756-4D5A-B28A-E6E526D9927D}.Release|x64.ActiveCfg = Release|Any CPU
{ABE3E734-0756-4D5A-B28A-E6E526D9927D}.Release|x64.Build.0 = Release|Any CPU
{ABE3E734-0756-4D5A-B28A-E6E526D9927D}.Release|x86.ActiveCfg = Release|Any CPU
{ABE3E734-0756-4D5A-B28A-E6E526D9927D}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ public class FigletSample : BaseSample

public override void Run(IAnsiConsole console)
{
console.Write(new FigletText("Left aligned").LeftAligned().Color(Color.Red));
console.Write(new FigletText("Left aligned").LeftJustified().Color(Color.Red));
console.Write(new FigletText("Centered").Centered().Color(Color.Green));
console.Write(new FigletText("Right aligned").RightAligned().Color(Color.Blue));
console.Write(new FigletText("Right aligned").RightJustified().Color(Color.Blue));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public override void Run(IAnsiConsole console)
console.DisplayThenType(c => password = AskPassword(c), "hunter2↲");
console.DisplayThenType(c => color = AskColor(c), "↲");

AnsiConsole.Write(new Rule("[yellow]Results[/]").RuleStyle("grey").LeftAligned());
AnsiConsole.Write(new Rule("[yellow]Results[/]").RuleStyle("grey").LeftJustified());
AnsiConsole.Write(new Table().AddColumns("[grey]Question[/]", "[grey]Answer[/]")
.RoundedBorder()
.BorderColor(Color.Grey)
Expand All @@ -33,7 +33,7 @@ public override void Run(IAnsiConsole console)
private static string AskName(IAnsiConsole console)
{
console.WriteLine();
console.Write(new Rule("[yellow]Strings[/]").RuleStyle("grey").LeftAligned());
console.Write(new Rule("[yellow]Strings[/]").RuleStyle("grey").LeftJustified());
var name = console.Ask<string>("What's your [green]name[/]?");
return name;
}
Expand All @@ -42,7 +42,7 @@ private static string AskName(IAnsiConsole console)
private static string AskSport(IAnsiConsole console)
{
console.WriteLine();
console.Write(new Rule("[yellow]Choices[/]").RuleStyle("grey").LeftAligned());
console.Write(new Rule("[yellow]Choices[/]").RuleStyle("grey").LeftJustified());

return console.Prompt(
new TextPrompt<string>("What's your [green]favorite sport[/]?")
Expand All @@ -56,7 +56,7 @@ private static string AskSport(IAnsiConsole console)
private static int AskAge(IAnsiConsole console)
{
console.WriteLine();
console.Write(new Rule("[yellow]Integers[/]").RuleStyle("grey").LeftAligned());
console.Write(new Rule("[yellow]Integers[/]").RuleStyle("grey").LeftJustified());

return console.Prompt(
new TextPrompt<int>("How [green]old[/] are you?")
Expand All @@ -76,7 +76,7 @@ private static int AskAge(IAnsiConsole console)
private static string AskPassword(IAnsiConsole console)
{
console.WriteLine();
console.Write(new Rule("[yellow]Secrets[/]").RuleStyle("grey").LeftAligned());
console.Write(new Rule("[yellow]Secrets[/]").RuleStyle("grey").LeftJustified());

return console.Prompt(
new TextPrompt<string>("Enter [green]password[/]?")
Expand All @@ -87,7 +87,7 @@ private static string AskPassword(IAnsiConsole console)
private static string AskColor(IAnsiConsole console)
{
console.WriteLine();
console.Write(new Rule("[yellow]Optional[/]").RuleStyle("grey").LeftAligned());
console.Write(new Rule("[yellow]Optional[/]").RuleStyle("grey").LeftJustified());

return console.Prompt(
new TextPrompt<string>("[grey][[Optional]][/] What is your [green]favorite color[/]?")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using Spectre.Console;
using Spectre.Console.Json;

namespace Generator.Commands.Samples
{
public class JsonSample : BaseSample
{
public override (int Cols, int Rows) ConsoleSize => (60, 20);

public override void Run(IAnsiConsole console)
{
var json = new JsonText(
"""
{
"hello": 32,
"world": {
"foo": 21,
"bar": 255,
"baz": [
0.32, 0.33e-32,
0.42e32, 0.55e+32,
{
"hello": "world",
"lol": null
}
]
}
}
""");

AnsiConsole.Write(
new Panel(json)
.Header("Some JSON in a panel")
.Collapse()
.RoundedBorder()
.BorderColor(Color.Yellow));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ public override void Run(IAnsiConsole console)
{
console.Write(new Rule());
console.WriteLine();
console.Write(new Rule("[blue]Left aligned[/]").LeftAligned().RuleStyle("red"));
console.Write(new Rule("[blue]Left aligned[/]").LeftJustified().RuleStyle("red"));
console.WriteLine();
console.Write(new Rule("[green]Centered[/]").Centered().RuleStyle("green"));
console.WriteLine();
console.Write(new Rule("[red]Right aligned[/]").RightAligned().RuleStyle("blue"));
console.Write(new Rule("[red]Right aligned[/]").RightJustified().RuleStyle("blue"));
console.WriteLine();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ public override void Run(IAnsiConsole console)
console.Write(
new Panel(
new Padder(new Rows(
new TextPath("/This/Is/A/Long/Path/That/Will/Be/Truncated.txt").LeftAligned(),
new TextPath("/This/Is/A/Long/Path/That/Will/Be/Truncated.txt").LeftJustified(),
new TextPath("/This/Is/A/Long/Path/That/Will/Be/Truncated.txt").Centered(),
new TextPath("/This/Is/A/Long/Path/That/Will/Be/Truncated.txt").RightAligned()), new Padding(0,1)))
new TextPath("/This/Is/A/Long/Path/That/Will/Be/Truncated.txt").RightJustified()), new Padding(0,1)))
.BorderStyle(new Style(foreground: Color.Grey))
.Header("Alignment"));
}
Expand Down
5 changes: 3 additions & 2 deletions resources/scripts/Generator/Generator.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
Expand Down Expand Up @@ -51,8 +51,9 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
<ProjectReference Include="..\..\..\src\Spectre.Console.Cli\Spectre.Console.Cli.csproj" />
<ProjectReference Include="..\..\..\src\Spectre.Console.ImageSharp\Spectre.Console.ImageSharp.csproj" />
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
<ProjectReference Include="..\..\..\src\Spectre.Console.Json\Spectre.Console.Json.csproj" />
</ItemGroup>
</Project>
14 changes: 14 additions & 0 deletions src/Spectre.Console.Json/IJsonParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace Spectre.Console.Json;

/// <summary>
/// Represents a JSON parser.
/// </summary>
public interface IJsonParser
{
/// <summary>
/// Parses the provided JSON into an abstract syntax tree.
/// </summary>
/// <param name="json">The JSON to parse.</param>
/// <returns>An <see cref="JsonSyntax"/> instance.</returns>
JsonSyntax Parse(string json);
}
Loading