tiny.zen.code
Defined in tiny.zen.
API (4)
Actions
Public operations.
Types and contracts
Public types and contracts.
Source
Source: lib/zen/src/code.zig
zig
const std = @import("std");const html = @import("html.zig");const syntax = @import("syntax.zig");const Allocator = std.mem.Allocator;pub const Options = struct { highlighting: bool = true, line_numbers: bool = true, titles: bool = true, language_labels: bool = true, word_wrap: bool = true,};pub const Info = struct { language: []const u8 = "", title: []const u8 = "",};pub fn parseInfo(info: []const u8) Info { const trimmed = std.mem.trim(u8, info, " \t"); if (trimmed.len == 0) return .{}; const language = firstWord(trimmed); const rest = std.mem.trim(u8, trimmed[language.len..], " \t"); if (rest.len == 0) return .{ .language = language }; if (parseTitle(rest)) |title| return .{ .language = language, .title = title }; if (onlyRenderOptions(rest)) return .{ .language = language }; return .{ .language = language, .title = rest };}pub fn render( allocator: Allocator, out: *std.ArrayList(u8), info_text: []const u8, source: []const u8, options: Options,) !void { const info = parseInfo(info_text); const anchors = optionValue(info_text, "anchors") orelse ""; const exact_source = hasOption(info_text, "source", "exact"); const wide = exact_source or hasOption(info_text, "role", "wide"); try out.appendSlice(allocator, "<div class=\"zen-code-block"); if (options.word_wrap) { try out.appendSlice(allocator, " zen-code-wrap"); } else { try out.appendSlice(allocator, " zen-code-nowrap"); } if (!options.line_numbers) try out.appendSlice(allocator, " zen-code-no-lines"); if (exact_source) { try out.appendSlice(allocator, " zen-source-listing zen-role-wide"); } else if (wide) { try out.appendSlice(allocator, " zen-code-longform zen-role-wide"); } else { try out.appendSlice(allocator, " zen-code-example zen-role-reading"); } try out.append(allocator, '"'); if (info.language.len != 0) { try out.appendSlice(allocator, " data-language=\""); try html.appendAttributeEscaped(out, allocator, info.language); try out.append(allocator, '"'); } if (exact_source) { try out.appendSlice(allocator, " data-source=\"exact\""); } try out.append(allocator, '>'); if ((options.titles and info.title.len != 0) or (options.language_labels and info.language.len != 0)) { try out.appendSlice(allocator, "<div class=\"zen-code-header\">"); if (options.titles and info.title.len != 0) { try out.appendSlice(allocator, "<span class=\"zen-code-title\">"); try html.appendEscaped(out, allocator, info.title); try out.appendSlice(allocator, "</span>"); } if (options.language_labels and info.language.len != 0) { try out.appendSlice(allocator, "<span class=\"zen-code-language\">"); try html.appendEscaped(out, allocator, info.language); try out.appendSlice(allocator, "</span>"); } try out.appendSlice(allocator, "</div>"); } try out.appendSlice(allocator, "<pre class=\"zen-code\"><code"); if (info.language.len != 0) { try out.appendSlice(allocator, " class=\"language-"); try html.appendAttributeEscaped(out, allocator, info.language); try out.append(allocator, '"'); } try out.append(allocator, '>'); try appendLines(allocator, out, info.language, source, anchors, options); try out.appendSlice(allocator, "</code></pre></div>\n");}fn appendLines( allocator: Allocator, out: *std.ArrayList(u8), language: []const u8, source: []const u8, anchors: []const u8, options: Options,) !void { var highlighter = syntax.Highlighter.init(language, options.highlighting); var anchor_cursor = AnchorCursor{ .encoded = anchors }; if (source.len == 0) { try appendLine(allocator, out, &highlighter, &anchor_cursor, "", 1, options); try anchor_cursor.finish(); return; } var start: usize = 0; var number: usize = 1; while (start < source.len) : (number += 1) { const end = std.mem.indexOfScalarPos(u8, source, start, '\n') orelse source.len; try appendLine( allocator, out, &highlighter, &anchor_cursor, source[start..end], number, options, ); if (end == source.len) break; start = end + 1; } try anchor_cursor.finish();}fn appendLine( allocator: Allocator, out: *std.ArrayList(u8), highlighter: *syntax.Highlighter, anchors: *AnchorCursor, line: []const u8, number: usize, options: Options,) !void { try out.appendSlice(allocator, "<span class=\"zen-code-line\">"); try anchors.appendForLine(allocator, out, number); if (options.line_numbers) { try out.appendSlice(allocator, "<span class=\"zen-code-line-number\" aria-hidden=\"true\">"); try appendUsize(out, allocator, number); try out.appendSlice(allocator, "</span>"); } try out.appendSlice(allocator, "<span class=\"zen-code-line-source\">"); try highlighter.appendLine(out, allocator, line); try out.appendSlice(allocator, "</span></span>");}const AnchorCursor = struct { encoded: []const u8, cursor: usize = 0, fn appendForLine( self: *AnchorCursor, allocator: Allocator, out: *std.ArrayList(u8), line: usize, ) !void { while (self.cursor < self.encoded.len) { const end = std.mem.indexOfScalarPos( u8, self.encoded, self.cursor, ',', ) orelse self.encoded.len; const entry = self.encoded[self.cursor..end]; const split = std.mem.indexOfScalar(u8, entry, ':') orelse return error.InvalidData; const target = std.fmt.parseInt(usize, entry[0..split], 10) catch return error.InvalidData; if (target == 0 or target < line) return error.InvalidData; if (target > line) return; const anchor = entry[split + 1 ..]; if (anchor.len == 0) return error.InvalidData; try out.appendSlice(allocator, "<span id=\""); try html.appendAttributeEscaped(out, allocator, anchor); try out.appendSlice(allocator, "\"></span>"); self.cursor = end + @intFromBool(end < self.encoded.len); } } fn finish(self: AnchorCursor) !void { if (self.cursor != self.encoded.len) return error.InvalidData; }};fn onlyRenderOptions(value: []const u8) bool { var cursor: usize = 0; var count: usize = 0; while (nextToken(value, &cursor)) |token| { if (!std.mem.startsWith(u8, token, "anchors=") and !std.mem.eql(u8, token, "source=exact") and !std.mem.eql(u8, token, "role=reading") and !std.mem.eql(u8, token, "role=wide")) return false; count += 1; } return count != 0;}fn hasOption(info: []const u8, key: []const u8, expected: []const u8) bool { const value = optionValue(info, key) orelse return false; return std.mem.eql(u8, value, expected);}fn optionValue(info: []const u8, key: []const u8) ?[]const u8 { const trimmed = std.mem.trim(u8, info, " \t"); var cursor: usize = firstWord(trimmed).len; while (nextToken(trimmed, &cursor)) |token| { const split = std.mem.indexOfScalar(u8, token, '=') orelse continue; if (std.mem.eql(u8, token[0..split], key)) return token[split + 1 ..]; } return null;}fn nextToken(value: []const u8, cursor: *usize) ?[]const u8 { while (cursor.* < value.len and (value[cursor.*] == ' ' or value[cursor.*] == '\t')) : (cursor.* += 1) {} if (cursor.* == value.len) return null; const start = cursor.*; cursor.* = skipToken(value, cursor.*); return value[start..cursor.*];}fn parseTitle(rest: []const u8) ?[]const u8 { var cursor: usize = 0; while (cursor < rest.len) { while (cursor < rest.len and (rest[cursor] == ' ' or rest[cursor] == '\t')) : (cursor += 1) {} if (cursor >= rest.len) return null; if (std.mem.startsWith(u8, rest[cursor..], "title=")) { const value_start = cursor + "title=".len; if (value_start >= rest.len) return ""; const quote = rest[value_start]; if (quote == '"' or quote == '\'') { const text_start = value_start + 1; const text_end = std.mem.indexOfScalarPos(u8, rest, text_start, quote) orelse rest.len; return rest[text_start..text_end]; } var end = value_start; while (end < rest.len and rest[end] != ' ' and rest[end] != '\t') : (end += 1) {} return rest[value_start..end]; } cursor = skipToken(rest, cursor); } return null;}fn skipToken(value: []const u8, start: usize) usize { var index = start; while (index < value.len and value[index] != ' ' and value[index] != '\t') { if (value[index] == '"' or value[index] == '\'') { const quote = value[index]; index += 1; while (index < value.len and value[index] != quote) : (index += 1) {} } if (index < value.len) index += 1; } return index;}fn firstWord(value: []const u8) []const u8 { var end: usize = 0; while (end < value.len and value[end] != ' ' and value[end] != '\t') : (end += 1) {} return value[0..end];}fn appendUsize(out: *std.ArrayList(u8), allocator: Allocator, value: usize) Allocator.Error!void { var buffer: [20]u8 = undefined; const text = std.fmt.bufPrint(&buffer, "{d}", .{value}) catch unreachable; try out.appendSlice(allocator, text);}test "code parses fence language and titles" { try std.testing.expectEqualDeep(Info{ .language = "zig", .title = "Comptime DSL" }, parseInfo("zig title=\"Comptime DSL\"")); try std.testing.expectEqualDeep(Info{ .language = "c", .title = "C API smoke" }, parseInfo("c C API smoke")); try std.testing.expectEqualDeep( Info{ .language = "zig" }, parseInfo("zig source=exact anchors=1:member/field/Doc/empty"), );}test "code renders highlighted titled numbered lines" { var out: std.ArrayList(u8) = .empty; defer out.deinit(std.testing.allocator); try render(std.testing.allocator, &out, "zig title=\"Example\"", "const x: u8 = 1;\n", .{}); try std.testing.expect(std.mem.indexOf( u8, out.items, "<div class=\"zen-code-block zen-code-wrap " ++ "zen-code-example zen-role-reading\" data-language=\"zig\">", ) != null); try std.testing.expect(std.mem.indexOf(u8, out.items, "<span class=\"zen-code-title\">Example</span>") != null); try std.testing.expect(std.mem.indexOf(u8, out.items, "<span class=\"zen-code-line-number\" aria-hidden=\"true\">1</span>") != null); try std.testing.expect(std.mem.indexOf(u8, out.items, "<span class=\"zen-code-keyword\">const</span>") != null); try std.testing.expect(std.mem.indexOf(u8, out.items, "<span class=\"zen-code-type\">u8</span>") != null); try std.testing.expect(std.mem.indexOf(u8, out.items, "<span class=\"zen-code-number\">1</span>") != null);}test "code places typed anchors on exact source lines" { var out: std.ArrayList(u8) = .empty; defer out.deinit(std.testing.allocator); try render( std.testing.allocator, &out, "zig source=exact anchors=1:member/field/Doc/empty," ++ "2:member/field/Doc/text", "empty,\ntext: []const u8,\n", .{}, ); try std.testing.expect(std.mem.indexOf( u8, out.items, "data-source=\"exact\"", ) != null); try std.testing.expect(std.mem.indexOf( u8, out.items, "zen-source-listing zen-role-wide", ) != null); try std.testing.expect(std.mem.indexOf( u8, out.items, "id=\"member/field/Doc/empty\"", ) != null); try std.testing.expect(std.mem.indexOf( u8, out.items, "id=\"member/field/Doc/text\"", ) != null); try std.testing.expect(std.mem.indexOf(u8, out.items, "anchors=") == null);}test "code gives authored wide blocks a distinct semantic role" { var out: std.ArrayList(u8) = .empty; defer out.deinit(std.testing.allocator); try render(std.testing.allocator, &out, "text role=wide", "dense\n", .{}); try std.testing.expect(std.mem.indexOf( u8, out.items, "zen-code-longform zen-role-wide", ) != null); try std.testing.expect(std.mem.indexOf(u8, out.items, "zen-code-example") == null);}test "code rejects invalid and out of range source anchors" { var out: std.ArrayList(u8) = .empty; defer out.deinit(std.testing.allocator); try std.testing.expectError( error.InvalidData, render(std.testing.allocator, &out, "zig anchors=0:zero", "value\n", .{}), ); out.clearRetainingCapacity(); try std.testing.expectError( error.InvalidData, render(std.testing.allocator, &out, "zig anchors=2:late", "value\n", .{}), );}Source: lib/zen/src/root.zig:15
zig
/// Code-fence metadata, syntax highlighting, and HTML rendering.pub const code = @import("code.zig");Audit
| Definitions | 1 |
|---|---|
| Public names | 1 |
| Members | 0 |
| Version | 26.7.0 |
| Revision | daab053ee433 |