Skip to content

Commit

Permalink
FIx for Figlet Font
Browse files Browse the repository at this point in the history
Add Basic tests
  • Loading branch information
ChrisPulman committed Dec 20, 2023
1 parent 681d99d commit 63502bc
Show file tree
Hide file tree
Showing 54 changed files with 2,416 additions and 12 deletions.
17 changes: 17 additions & 0 deletions src/Spectre.Console.Rx.Json/IJsonParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) Chris Pulman. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace Spectre.Console.Rx.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);
}
78 changes: 78 additions & 0 deletions src/Spectre.Console.Rx.Json/JsonBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright (c) Chris Pulman. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace Spectre.Console.Rx.Json;

internal sealed class JsonBuilder : JsonSyntaxVisitor<JsonBuilderContext>
{
public static JsonBuilder Shared { get; } = new JsonBuilder();

public override void VisitObject(JsonObject syntax, JsonBuilderContext context)
{
context.Paragraph.Append("{", context.Styling.BracesStyle);
context.Paragraph.Append("\n");
context.Indentation++;

foreach (var (_, _, last, property) in syntax.Members.Enumerate())
{
context.InsertIndentation();
property.Accept(this, context);

if (!last)
{
context.Paragraph.Append(",", context.Styling.CommaStyle);
}

context.Paragraph.Append("\n");
}

context.Indentation--;
context.InsertIndentation();
context.Paragraph.Append("}", context.Styling.BracesStyle);
}

public override void VisitArray(JsonArray syntax, JsonBuilderContext context)
{
context.Paragraph.Append("[", context.Styling.BracketsStyle);
context.Paragraph.Append("\n");
context.Indentation++;

foreach (var (_, _, last, item) in syntax.Items.Enumerate())
{
context.InsertIndentation();
item.Accept(this, context);

if (!last)
{
context.Paragraph.Append(",", context.Styling.CommaStyle);
}

context.Paragraph.Append("\n");
}

context.Indentation--;
context.InsertIndentation();
context.Paragraph.Append("]", context.Styling.BracketsStyle);
}

public override void VisitMember(JsonMember syntax, JsonBuilderContext context)
{
context.Paragraph.Append(syntax.Name, context.Styling.MemberStyle);
context.Paragraph.Append(":", context.Styling.ColonStyle);
context.Paragraph.Append(" ");

syntax.Value.Accept(this, context);
}

public override void VisitNumber(JsonNumber syntax, JsonBuilderContext context) =>
context.Paragraph.Append(syntax.Lexeme, context.Styling.NumberStyle);

public override void VisitString(JsonString syntax, JsonBuilderContext context) =>
context.Paragraph.Append(syntax.Lexeme, context.Styling.StringStyle);

public override void VisitBoolean(JsonBoolean syntax, JsonBuilderContext context) =>
context.Paragraph.Append(syntax.Lexeme, context.Styling.BooleanStyle);

public override void VisitNull(JsonNull syntax, JsonBuilderContext context) =>
context.Paragraph.Append(syntax.Lexeme, context.Styling.NullStyle);
}
15 changes: 15 additions & 0 deletions src/Spectre.Console.Rx.Json/JsonBuilderContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) Chris Pulman. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace Spectre.Console.Rx.Json;

internal sealed class JsonBuilderContext(JsonTextStyles styling)
{
public Paragraph Paragraph { get; } = new Paragraph();

public int Indentation { get; set; }

public JsonTextStyles Styling { get; } = styling;

public void InsertIndentation() => Paragraph.Append(new string(' ', Indentation * 3));
}
141 changes: 141 additions & 0 deletions src/Spectre.Console.Rx.Json/JsonParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// Copyright (c) Chris Pulman. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace Spectre.Console.Rx.Json;

internal sealed class JsonParser : IJsonParser
{
public static JsonParser Shared { get; } = new JsonParser();

public JsonSyntax Parse(string json)
{
try
{
var tokens = JsonTokenizer.Tokenize(json);
var reader = new JsonTokenReader(tokens);
return ParseElement(reader);
}
catch
{
throw new InvalidOperationException("Invalid JSON");
}
}

private static JsonSyntax ParseElement(JsonTokenReader reader) => ParseValue(reader);

private static List<JsonSyntax> ParseElements(JsonTokenReader reader)
{
var members = new List<JsonSyntax>();

while (!reader.Eof)
{
members.Add(ParseElement(reader));

if (reader.Peek()?.Type != JsonTokenType.Comma)
{
break;
}

reader.Consume(JsonTokenType.Comma);
}

return members;
}

private static JsonSyntax ParseValue(JsonTokenReader reader)
{
var current = reader.Peek() ?? throw new InvalidOperationException("Could not parse value (EOF)");
if (current.Type == JsonTokenType.LeftBrace)
{
return ParseObject(reader);
}

if (current.Type == JsonTokenType.LeftBracket)
{
return ParseArray(reader);
}

if (current.Type == JsonTokenType.Number)
{
reader.Consume(JsonTokenType.Number);
return new JsonNumber(current.Lexeme);
}

if (current.Type == JsonTokenType.String)
{
reader.Consume(JsonTokenType.String);
return new JsonString(current.Lexeme);
}

if (current.Type == JsonTokenType.Boolean)
{
reader.Consume(JsonTokenType.Boolean);
return new JsonBoolean(current.Lexeme);
}

if (current.Type == JsonTokenType.Null)
{
reader.Consume(JsonTokenType.Null);
return new JsonNull(current.Lexeme);
}

throw new InvalidOperationException($"Unknown value token: {current.Type}");
}

private static JsonSyntax ParseObject(JsonTokenReader reader)
{
reader.Consume(JsonTokenType.LeftBrace);

var result = new JsonObject();

if (reader.Peek()?.Type != JsonTokenType.RightBrace)
{
result.Members.AddRange(ParseMembers(reader));
}

reader.Consume(JsonTokenType.RightBrace);
return result;
}

private static JsonSyntax ParseArray(JsonTokenReader reader)
{
reader.Consume(JsonTokenType.LeftBracket);

var result = new JsonArray();

if (reader.Peek()?.Type != JsonTokenType.RightBracket)
{
result.Items.AddRange(ParseElements(reader));
}

reader.Consume(JsonTokenType.RightBracket);
return result;
}

private static List<JsonMember> ParseMembers(JsonTokenReader reader)
{
var members = new List<JsonMember>();

while (!reader.Eof)
{
members.Add(ParseMember(reader));

if (reader.Peek()?.Type != JsonTokenType.Comma)
{
break;
}

reader.Consume(JsonTokenType.Comma);
}

return members;
}

private static JsonMember ParseMember(JsonTokenReader reader)
{
var name = reader.Consume(JsonTokenType.String);
reader.Consume(JsonTokenType.Colon);
var value = ParseElement(reader);
return new JsonMember(name.Lexeme, value);
}
}
99 changes: 99 additions & 0 deletions src/Spectre.Console.Rx.Json/JsonText.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Copyright (c) Chris Pulman. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace Spectre.Console.Rx.Json;

/// <summary>
/// A renderable piece of JSON text.
/// </summary>
/// <remarks>
/// Initializes a new instance of the <see cref="JsonText"/> class.
/// </remarks>
/// <param name="json">The JSON to render.</param>
public sealed class JsonText(string json) : JustInTimeRenderable
{
private readonly string _json = json ?? throw new ArgumentNullException(nameof(json));
private JsonSyntax? _syntax;
private IJsonParser? _parser;

/// <summary>
/// Gets or sets the style used for braces.
/// </summary>
public Style? BracesStyle { get; set; }

/// <summary>
/// Gets or sets the style used for brackets.
/// </summary>
public Style? BracketsStyle { get; set; }

/// <summary>
/// Gets or sets the style used for member names.
/// </summary>
public Style? MemberStyle { get; set; }

/// <summary>
/// Gets or sets the style used for colons.
/// </summary>
public Style? ColonStyle { get; set; }

/// <summary>
/// Gets or sets the style used for commas.
/// </summary>
public Style? CommaStyle { get; set; }

/// <summary>
/// Gets or sets the style used for string literals.
/// </summary>
public Style? StringStyle { get; set; }

/// <summary>
/// Gets or sets the style used for number literals.
/// </summary>
public Style? NumberStyle { get; set; }

/// <summary>
/// Gets or sets the style used for boolean literals.
/// </summary>
public Style? BooleanStyle { get; set; }

/// <summary>
/// Gets or sets the style used for <c>null</c> literals.
/// </summary>
public Style? NullStyle { get; set; }

/// <summary>
/// Gets or sets the JSON parser.
/// </summary>
public IJsonParser? Parser
{
get => _parser;

set
{
_syntax = null;
_parser = value;
}
}

/// <inheritdoc/>
protected override IRenderable Build()
{
_syntax ??= (Parser ?? JsonParser.Shared).Parse(_json);

var context = new JsonBuilderContext(new JsonTextStyles
{
BracesStyle = BracesStyle ?? Color.Grey,
BracketsStyle = BracketsStyle ?? Color.Grey,
MemberStyle = MemberStyle ?? Color.Blue,
ColonStyle = ColonStyle ?? Color.Yellow,
CommaStyle = CommaStyle ?? Color.Grey,
StringStyle = StringStyle ?? Color.Red,
NumberStyle = NumberStyle ?? Color.Green,
BooleanStyle = BooleanStyle ?? Color.Green,
NullStyle = NullStyle ?? Color.Grey,
});

_syntax.Accept(JsonBuilder.Shared, context);
return context.Paragraph;
}
}
Loading

0 comments on commit 63502bc

Please sign in to comment.