using Blog.Models; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Rendering; namespace Blog.Components.Pages.Git; /// /// One line of code, as the spans git.css can tell apart. /// /// /// A render tree rather than markup because the cell this lands in is white-space: pre: any /// newline or indentation a .razor file left between two tokens would be rendered as part of the /// file. A render tree emits exactly the nodes it is handed, and AddContent escapes them. /// public sealed class GitCode : ComponentBase { [Parameter] public IReadOnlyList Tokens { get; set; } = []; /// /// A diff's + or -, repeated out of the gutter into the line so the diff still /// reads as one when it is copied or read without colour. git.css marks it unselectable, so /// copying gives back the code rather than the patch. /// [Parameter] public char? Sign { get; set; } protected override void BuildRenderTree(RenderTreeBuilder builder) { // Sequence numbers exist for diffing one render against the next, which these pages never // do - they are rendered once, statically, and thrown away. var sequence = 0; if (Sign is { } sign) { builder.OpenElement(sequence++, "span"); builder.AddAttribute(sequence++, "class", "sign"); builder.AddContent(sequence++, sign); builder.CloseElement(); } foreach (var token in Tokens) { if (token.Kind is SyntaxKind.Text) { builder.AddContent(sequence++, token.Text); continue; } builder.OpenElement(sequence++, "span"); builder.AddAttribute(sequence++, "class", Class(token.Kind)); builder.AddContent(sequence++, token.Text); builder.CloseElement(); } } private static string Class(SyntaxKind kind) => kind switch { SyntaxKind.Comment => "tok-comment", SyntaxKind.String => "tok-string", SyntaxKind.Number => "tok-number", SyntaxKind.Keyword => "tok-keyword", SyntaxKind.Meta => "tok-meta", _ => "" }; }