diff options
| author | Anhgelus Morhtuuzh <william@herges.fr> | 2026-04-27 20:49:13 +0200 |
|---|---|---|
| committer | Anhgelus Morhtuuzh <william@herges.fr> | 2026-04-27 20:49:13 +0200 |
| commit | e154408e8ddeaee83242002f4c7af68b29d3a677 (patch) | |
| tree | d0caadc01e77aa15edb204b1529fac168775329a /src/callout.zig | |
| parent | 3b0e9424a66058da82d11d432da886ec7b6ce7eb (diff) | |
feat(): support callout
Diffstat (limited to 'src/callout.zig')
| -rw-r--r-- | src/callout.zig | 87 |
1 files changed, 87 insertions, 0 deletions
diff --git a/src/callout.zig b/src/callout.zig new file mode 100644 index 0000000..2761944 --- /dev/null +++ b/src/callout.zig @@ -0,0 +1,87 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const eql = std.mem.eql; +const Token = @import("lexer/Token.zig"); +const Lexer = @import("lexer/Lexer.zig"); +const Element = @import("eval/Element.zig"); +const testing = @import("testing.zig"); +const paragraph = @import("paragraph.zig"); +const doTest = testing.do; +const doTestError = testing.doError; + +pub const Error = error{InvalidCallout} || paragraph.Error || Allocator.Error; + +pub fn parse(alloc: Allocator, l: *Lexer) Error!Element { + _ = l.next(); + var beg = l.next() orelse return Error.InvalidCallout; + var kind: ?[]const u8 = null; + var title: ?[]const u8 = null; + switch (beg.kind) { + .literal => { + var iter = std.mem.splitAny(u8, beg.content, " "); + kind = iter.first(); + if (iter.peek() != null) title = iter.buffer[iter.index.?..]; + beg = l.next() orelse return Error.InvalidCallout; + if (!beg.kind.isDelimiter()) return Error.InvalidCallout; + }, + else => if (!beg.kind.isDelimiter()) return Error.InvalidCallout, + } + var root = try Element.Root.init(alloc); + while (l.peek()) |it| { + if (it.kind == .callout) { + l.consume(); + break; + } + if (it.kind.isDelimiter()) { + const next = l.peek() orelse return Error.InvalidCallout; + if (next.kind == .callout) { + l.consume(); + break; + } + } + try root.append(try paragraph.parse(root.allocator(), l)); + _ = l.peek() orelse return Error.InvalidCallout; + } + var el = try Element.Callout.init(alloc, root.element()); + el.kind = kind; + el.title = title; + const end = l.next() orelse return el.element(); + if (!end.kind.isDelimiter()) return Error.InvalidCallout; + return el.element(); +} + +test "callout" { + const alloc = std.testing.allocator; + + try doTest(parse, alloc, + \\::: + \\hey + \\::: + , "<div class=\"callout\"><p>hey</p></div>"); + try doTest(parse, alloc, + \\:::info + \\hey + \\::: + , "<div data-callout=\"info\" class=\"callout\"><p>hey</p></div>"); + try doTest(parse, alloc, + \\:::info Title + \\hey + \\::: + , "<div data-callout=\"info\" class=\"callout\"><p>hey</p></div>"); + // cannot test content with \n + + try doTestError(parse, alloc, ":::", Error.InvalidCallout); + try doTestError(parse, alloc, + \\::: + \\hey + , Error.InvalidCallout); + try doTestError(parse, alloc, + \\::: + \\hey::: + , Error.IllegalPlacement); + try doTestError(parse, alloc, + \\::: + \\hey + \\::: nope + , Error.InvalidCallout); +} |
