Skip to documentation
SLOP

tiny.markdown.@"inline"

Reference tiny.markdown @"inline"

Defined in tiny.markdown.

API (8)

Actions

Public operations.

Types and contracts

Public types and contracts.

No direct callersNo direct callstiny.markdown@"inline"
Static calls · unresolved targets: unknown · external targets: unknown.

Source

Source: lib/markdown/src/inline.zig

zig
const std = @import("std");const termtex = @import("termtex");pub const Role = enum {    strong,    emphasis,    strong_emphasis,    strikethrough,    strong_strikethrough,    emphasis_strikethrough,    strong_emphasis_strikethrough,    code,    math,    link,    strong_link,    emphasis_link,    strong_emphasis_link,    strikethrough_link,    strong_strikethrough_link,    emphasis_strikethrough_link,    strong_emphasis_strikethrough_link,    image,};pub const Span = struct {    start: usize,    end: usize,    role: Role,    link: ?usize = null,    math: ?usize = null,    image: ?usize = null,};pub const Link = struct {    url: []u8,};pub const Math = struct {    source: []u8,};pub const Image = struct {    url: []u8,};pub const Line = struct {    text: []u8,    spans: []Span,    links: []Link = &.{},    math: []Math = &.{},    images: []Image = &.{},    pub fn deinit(self: *Line, allocator: std.mem.Allocator) void {        for (self.links) |link| allocator.free(link.url);        for (self.math) |math| allocator.free(math.source);        for (self.images) |image| allocator.free(image.url);        if (self.links.len != 0) allocator.free(self.links);        if (self.math.len != 0) allocator.free(self.math);        if (self.images.len != 0) allocator.free(self.images);        allocator.free(self.text);        allocator.free(self.spans);        self.* = undefined;    }};pub fn parse(allocator: std.mem.Allocator, text: []const u8) std.mem.Allocator.Error!Line {    return parseWithMath(.terminal, allocator, text);}pub fn parseSource(allocator: std.mem.Allocator, text: []const u8) std.mem.Allocator.Error!Line {    return parseWithMath(.source, allocator, text);}const MathPresentation = enum {    source,    terminal,};fn parseWithMath(comptime math_presentation: MathPresentation, allocator: std.mem.Allocator, text: []const u8) std.mem.Allocator.Error!Line {    var rendered: std.ArrayListUnmanaged(u8) = .empty;    errdefer rendered.deinit(allocator);    var spans: std.ArrayListUnmanaged(Span) = .empty;    errdefer spans.deinit(allocator);    var links: std.ArrayListUnmanaged(Link) = .empty;    errdefer {        for (links.items) |link| allocator.free(link.url);        links.deinit(allocator);    }    var math_sources: std.ArrayListUnmanaged(Math) = .empty;    errdefer {        for (math_sources.items) |item| allocator.free(item.source);        math_sources.deinit(allocator);    }    var image_sources: std.ArrayListUnmanaged(Image) = .empty;    errdefer {        for (image_sources.items) |item| allocator.free(item.url);        image_sources.deinit(allocator);    }    var strong_delimiter: ?[]const u8 = null;    var emphasis_delimiter: ?[]const u8 = null;    var strikethrough_delimiter: ?[]const u8 = null;    var in_code = false;    var segment_start: usize = 0;    var index: usize = 0;    while (index < text.len) {        if (!in_code) {            if (strong_delimiter) |delimiter| {                if (std.mem.startsWith(u8, text[index..], delimiter) and canCloseDelimiter(text, index, delimiter)) {                    try appendSegment(allocator, &rendered, &spans, text[segment_start..index], inlineRole(strong_delimiter != null, emphasis_delimiter != null, strikethrough_delimiter != null, in_code));                    strong_delimiter = null;                    index += delimiter.len;                    segment_start = index;                    continue;                }            } else if (strongDelimiterAt(text, index)) |delimiter| {                if (!(canOpenDelimiter(text, index, delimiter) and closingDelimiter(text, index + delimiter.len, delimiter))) {                    index += delimiter.len;                    continue;                }                try appendSegment(allocator, &rendered, &spans, text[segment_start..index], inlineRole(strong_delimiter != null, emphasis_delimiter != null, strikethrough_delimiter != null, in_code));                strong_delimiter = delimiter;                index += delimiter.len;                segment_start = index;                continue;            }        }        if (!in_code) {            if (emphasis_delimiter) |delimiter| {                if (std.mem.startsWith(u8, text[index..], delimiter) and canCloseDelimiter(text, index, delimiter)) {                    try appendSegment(allocator, &rendered, &spans, text[segment_start..index], inlineRole(strong_delimiter != null, emphasis_delimiter != null, strikethrough_delimiter != null, in_code));                    emphasis_delimiter = null;                    index += delimiter.len;                    segment_start = index;                    continue;                }            } else if (emphasisDelimiterAt(text, index)) |delimiter| {                if (!(canOpenDelimiter(text, index, delimiter) and closingDelimiter(text, index + delimiter.len, delimiter))) {                    index += delimiter.len;                    continue;                }                try appendSegment(allocator, &rendered, &spans, text[segment_start..index], inlineRole(strong_delimiter != null, emphasis_delimiter != null, strikethrough_delimiter != null, in_code));                emphasis_delimiter = delimiter;                index += delimiter.len;                segment_start = index;                continue;            }        }        if (!in_code) {            if (strikethrough_delimiter) |delimiter| {                if (std.mem.startsWith(u8, text[index..], delimiter) and canCloseDelimiter(text, index, delimiter)) {                    try appendSegment(allocator, &rendered, &spans, text[segment_start..index], inlineRole(strong_delimiter != null, emphasis_delimiter != null, strikethrough_delimiter != null, in_code));                    strikethrough_delimiter = null;                    index += delimiter.len;                    segment_start = index;                    continue;                }            } else if (strikethroughDelimiterAt(text, index)) |delimiter| {                if (!(canOpenDelimiter(text, index, delimiter) and closingDelimiter(text, index + delimiter.len, delimiter))) {                    index += delimiter.len;                    continue;                }                try appendSegment(allocator, &rendered, &spans, text[segment_start..index], inlineRole(strong_delimiter != null, emphasis_delimiter != null, strikethrough_delimiter != null, in_code));                strikethrough_delimiter = delimiter;                index += delimiter.len;                segment_start = index;                continue;            }        }        if (text[index] == '`' and (in_code or closingDelimiter(text, index + 1, "`"))) {            try appendSegment(allocator, &rendered, &spans, text[segment_start..index], inlineRole(strong_delimiter != null, emphasis_delimiter != null, strikethrough_delimiter != null, in_code));            in_code = !in_code;            index += 1;            segment_start = index;            continue;        }        if (!in_code and (text[index] == '$' or std.mem.startsWith(u8, text[index..], "\\("))) {            if (try parseMath(math_presentation, allocator, text, index)) |parsed_math| {                defer allocator.free(parsed_math.text);                try appendSegment(allocator, &rendered, &spans, text[segment_start..index], inlineRole(strong_delimiter != null, emphasis_delimiter != null, strikethrough_delimiter != null, in_code));                try appendMathSegment(allocator, &rendered, &spans, &math_sources, parsed_math.text, parsed_math.source);                index = parsed_math.end;                segment_start = index;                continue;            }        }        if (!in_code and text[index] == '!') {            if (parseImage(text, index)) |image| {                try appendSegment(allocator, &rendered, &spans, text[segment_start..index], inlineRole(strong_delimiter != null, emphasis_delimiter != null, strikethrough_delimiter != null, in_code));                try appendImageSegment(allocator, &rendered, &spans, &image_sources, image.text, image.url);                index = image.end;                segment_start = index;                continue;            }        }        if (!in_code and text[index] == '[') {            if (parseLink(text, index)) |link| {                try appendSegment(allocator, &rendered, &spans, text[segment_start..index], inlineRole(strong_delimiter != null, emphasis_delimiter != null, strikethrough_delimiter != null, in_code));                try appendLinkSegment(allocator, &rendered, &spans, &links, link.text, inlineLinkRole(strong_delimiter != null, emphasis_delimiter != null, strikethrough_delimiter != null), link.url);                index = link.end;                segment_start = index;                continue;            }        }        index += 1;    }    try appendSegment(allocator, &rendered, &spans, text[segment_start..], inlineRole(strong_delimiter != null, emphasis_delimiter != null, strikethrough_delimiter != null, in_code));    const owned_text = try rendered.toOwnedSlice(allocator);    errdefer allocator.free(owned_text);    const owned_spans = try spans.toOwnedSlice(allocator);    errdefer allocator.free(owned_spans);    const owned_links = try links.toOwnedSlice(allocator);    errdefer {        for (owned_links) |link| allocator.free(link.url);        allocator.free(owned_links);    }    const owned_math = try math_sources.toOwnedSlice(allocator);    errdefer {        for (owned_math) |item| allocator.free(item.source);        allocator.free(owned_math);    }    const owned_images = try image_sources.toOwnedSlice(allocator);    errdefer {        for (owned_images) |item| allocator.free(item.url);        allocator.free(owned_images);    }    return .{        .text = owned_text,        .spans = owned_spans,        .links = owned_links,        .math = owned_math,        .images = owned_images,    };}fn appendSegment(    allocator: std.mem.Allocator,    rendered: *std.ArrayListUnmanaged(u8),    spans: *std.ArrayListUnmanaged(Span),    segment: []const u8,    role: ?Role,) std.mem.Allocator.Error!void {    if (segment.len == 0) return;    const start = rendered.items.len;    try rendered.appendSlice(allocator, segment);    const end = rendered.items.len;    if (role) |active| try spans.append(allocator, .{ .start = start, .end = end, .role = active });}fn appendLinkSegment(    allocator: std.mem.Allocator,    rendered: *std.ArrayListUnmanaged(u8),    spans: *std.ArrayListUnmanaged(Span),    links: *std.ArrayListUnmanaged(Link),    segment: []const u8,    role: Role,    url: []const u8,) std.mem.Allocator.Error!void {    if (segment.len == 0) return;    var owned_url = try allocator.dupe(u8, url);    errdefer if (owned_url.len != 0) allocator.free(owned_url);    const link_index = links.items.len;    try links.append(allocator, .{ .url = owned_url });    owned_url = "";    const start = rendered.items.len;    try rendered.appendSlice(allocator, segment);    const end = rendered.items.len;    try spans.append(allocator, .{ .start = start, .end = end, .role = role, .link = link_index });}fn appendMathSegment(    allocator: std.mem.Allocator,    rendered: *std.ArrayListUnmanaged(u8),    spans: *std.ArrayListUnmanaged(Span),    math: *std.ArrayListUnmanaged(Math),    segment: []const u8,    source: []const u8,) std.mem.Allocator.Error!void {    if (segment.len == 0) return;    var owned_source = try allocator.dupe(u8, source);    errdefer allocator.free(owned_source);    const math_index = math.items.len;    try math.append(allocator, .{ .source = owned_source });    owned_source = "";    const start = rendered.items.len;    try rendered.appendSlice(allocator, segment);    const end = rendered.items.len;    try spans.append(allocator, .{ .start = start, .end = end, .role = .math, .math = math_index });}fn appendImageSegment(    allocator: std.mem.Allocator,    rendered: *std.ArrayListUnmanaged(u8),    spans: *std.ArrayListUnmanaged(Span),    images: *std.ArrayListUnmanaged(Image),    segment: []const u8,    url: []const u8,) std.mem.Allocator.Error!void {    const display = if (segment.len == 0) "image" else segment;    var owned_url = try allocator.dupe(u8, url);    errdefer if (owned_url.len != 0) allocator.free(owned_url);    const image_index = images.items.len;    try images.append(allocator, .{ .url = owned_url });    owned_url = "";    const start = rendered.items.len;    try rendered.appendSlice(allocator, display);    const end = rendered.items.len;    try spans.append(allocator, .{ .start = start, .end = end, .role = .image, .image = image_index });}fn inlineRole(in_strong: bool, in_emphasis: bool, in_strikethrough: bool, in_code: bool) ?Role {    if (in_code) return .code;    if (in_strong and in_emphasis and in_strikethrough) return .strong_emphasis_strikethrough;    if (in_strong and in_strikethrough) return .strong_strikethrough;    if (in_emphasis and in_strikethrough) return .emphasis_strikethrough;    if (in_strong and in_emphasis) return .strong_emphasis;    if (in_strikethrough) return .strikethrough;    if (in_strong) return .strong;    if (in_emphasis) return .emphasis;    return null;}fn inlineLinkRole(in_strong: bool, in_emphasis: bool, in_strikethrough: bool) Role {    if (in_strong and in_emphasis and in_strikethrough) return .strong_emphasis_strikethrough_link;    if (in_strong and in_strikethrough) return .strong_strikethrough_link;    if (in_emphasis and in_strikethrough) return .emphasis_strikethrough_link;    if (in_strong and in_emphasis) return .strong_emphasis_link;    if (in_strikethrough) return .strikethrough_link;    if (in_strong) return .strong_link;    if (in_emphasis) return .emphasis_link;    return .link;}fn closingDelimiter(text: []const u8, start: usize, delimiter: []const u8) bool {    if (start >= text.len) return false;    var index = start;    while (std.mem.indexOf(u8, text[index..], delimiter)) |relative| {        const actual = index + relative;        if (canCloseDelimiter(text, actual, delimiter)) return true;        index = actual + delimiter.len;        if (index >= text.len) return false;    }    return false;}fn strongDelimiterAt(text: []const u8, index: usize) ?[]const u8 {    if (std.mem.startsWith(u8, text[index..], "**")) return "**";    if (std.mem.startsWith(u8, text[index..], "__")) return "__";    return null;}fn emphasisDelimiterAt(text: []const u8, index: usize) ?[]const u8 {    if (text[index] == '*') return "*";    if (text[index] == '_') return "_";    return null;}fn strikethroughDelimiterAt(text: []const u8, index: usize) ?[]const u8 {    if (std.mem.startsWith(u8, text[index..], "~~")) return "~~";    return null;}fn canOpenDelimiter(text: []const u8, index: usize, delimiter: []const u8) bool {    if (delimiter[0] != '_') return true;    if (index > 0 and std.ascii.isAlphanumeric(text[index - 1])) return false;    const after = index + delimiter.len;    return after < text.len and !isWhitespace(text[after]);}fn canCloseDelimiter(text: []const u8, index: usize, delimiter: []const u8) bool {    if (delimiter[0] != '_') return true;    if (index == 0 or isWhitespace(text[index - 1])) return false;    const after = index + delimiter.len;    return after >= text.len or !std.ascii.isAlphanumeric(text[after]);}fn isWhitespace(byte: u8) bool {    return byte == ' ' or byte == '\t' or byte == '\r' or byte == '\n';}const ParsedLink = struct {    text: []const u8,    url: []const u8,    end: usize,};const ParsedImage = struct {    text: []const u8,    url: []const u8,    end: usize,};const ParsedMath = struct {    text: []u8,    source: []const u8,    end: usize,};const MathDelimiter = struct {    open: []const u8,    close: []const u8,};fn parseMath(comptime presentation: MathPresentation, allocator: std.mem.Allocator, text: []const u8, start: usize) std.mem.Allocator.Error!?ParsedMath {    const delimiter = mathDelimiterAt(text, start) orelse return null;    const source_start = start + delimiter.open.len;    var index = source_start;    while (index < text.len) {        if (std.mem.startsWith(u8, text[index..], delimiter.close)) {            if (index == source_start) return null;            if (std.mem.eql(u8, delimiter.close, "$") and index + 1 < text.len and text[index + 1] == '$') return null;            const source = text[source_start..index];            const rendered = switch (presentation) {                .source => try allocator.dupe(u8, source),                .terminal => termtex.renderInlineAlloc(allocator, source, .{}) catch |err| switch (err) {                    error.OutOfMemory => return error.OutOfMemory,                    else => return null,                },            };            return .{ .text = rendered, .source = source, .end = index + delimiter.close.len };        }        if (text[index] == '\\') {            index += 1;            if (index < text.len) index += 1;            continue;        }        if (text[index] == '\n' or text[index] == '\r') return null;        const len = std.unicode.utf8ByteSequenceLength(text[index]) catch 1;        index += @min(len, text.len - index);    }    return null;}fn mathDelimiterAt(text: []const u8, start: usize) ?MathDelimiter {    if (start >= text.len) return null;    if (text[start] == '$') {        if (start + 2 >= text.len) return null;        if (start + 1 < text.len and text[start + 1] == '$') return null;        return .{ .open = "$", .close = "$" };    }    if (std.mem.startsWith(u8, text[start..], "\\(")) return .{ .open = "\\(", .close = "\\)" };    return null;}fn parseLink(text: []const u8, start: usize) ?ParsedLink {    if (start + 3 >= text.len or text[start] != '[') return null;    const label_start = start + 1;    const label_end_rel = std.mem.indexOfScalar(u8, text[label_start..], ']') orelse return null;    const label_end = label_start + label_end_rel;    if (label_end == label_start) return null;    if (label_end + 1 >= text.len or text[label_end + 1] != '(') return null;    const url_start = label_end + 2;    const url_end_rel = std.mem.indexOfScalar(u8, text[url_start..], ')') orelse return null;    const url_end = url_start + url_end_rel;    if (url_end == url_start) return null;    return .{        .text = text[label_start..label_end],        .url = text[url_start..url_end],        .end = url_end + 1,    };}fn parseImage(text: []const u8, start: usize) ?ParsedImage {    if (start + 4 >= text.len or text[start] != '!' or text[start + 1] != '[') return null;    const label_start = start + 2;    const label_end_rel = std.mem.indexOfScalar(u8, text[label_start..], ']') orelse return null;    const label_end = label_start + label_end_rel;    if (label_end + 1 >= text.len or text[label_end + 1] != '(') return null;    const url_start = label_end + 2;    const url_end_rel = std.mem.indexOfScalar(u8, text[url_start..], ')') orelse return null;    const url_end = url_start + url_end_rel;    if (url_end == url_start) return null;    return .{        .text = text[label_start..label_end],        .url = text[url_start..url_end],        .end = url_end + 1,    };}test "parse returns display text and styled byte ranges" {    const allocator = std.testing.allocator;    var line = try parse(allocator, "plain **bold** *em* ***both*** `code` [docs](https://example.com/docs) **[strong](https://example.com/strong)** *[soft](https://example.com/soft)* **open");    defer line.deinit(allocator);    try std.testing.expectEqualStrings("plain bold em both code docs strong soft **open", line.text);    try std.testing.expectEqual(@as(usize, 7), line.spans.len);    try std.testing.expectEqual(Role.strong, line.spans[0].role);    try std.testing.expectEqualStrings("bold", line.text[line.spans[0].start..line.spans[0].end]);    try std.testing.expectEqual(Role.emphasis, line.spans[1].role);    try std.testing.expectEqualStrings("em", line.text[line.spans[1].start..line.spans[1].end]);    try std.testing.expectEqual(Role.strong_emphasis, line.spans[2].role);    try std.testing.expectEqualStrings("both", line.text[line.spans[2].start..line.spans[2].end]);    try std.testing.expectEqual(Role.code, line.spans[3].role);    try std.testing.expectEqualStrings("code", line.text[line.spans[3].start..line.spans[3].end]);    try std.testing.expectEqual(Role.link, line.spans[4].role);    try std.testing.expectEqualStrings("docs", line.text[line.spans[4].start..line.spans[4].end]);    try std.testing.expectEqualStrings("https://example.com/docs", line.links[line.spans[4].link.?].url);    try std.testing.expectEqual(Role.strong_link, line.spans[5].role);    try std.testing.expectEqualStrings("strong", line.text[line.spans[5].start..line.spans[5].end]);    try std.testing.expectEqualStrings("https://example.com/strong", line.links[line.spans[5].link.?].url);    try std.testing.expectEqual(Role.emphasis_link, line.spans[6].role);    try std.testing.expectEqualStrings("soft", line.text[line.spans[6].start..line.spans[6].end]);    try std.testing.expectEqualStrings("https://example.com/soft", line.links[line.spans[6].link.?].url);}test "parse renders math through termtex" {    const allocator = std.testing.allocator;    var line = try parse(allocator, "area $\\pi r^2$ and bad $\\frac{x$ stays");    defer line.deinit(allocator);    try std.testing.expectEqualStrings("area πr² and bad $\\frac{x$ stays", line.text);    try std.testing.expectEqual(@as(usize, 1), line.spans.len);    try std.testing.expectEqual(Role.math, line.spans[0].role);    try std.testing.expectEqual(@as(?usize, 0), line.spans[0].math);    try std.testing.expectEqualStrings("πr²", line.text[line.spans[0].start..line.spans[0].end]);    try std.testing.expectEqual(@as(usize, 1), line.math.len);    try std.testing.expectEqualStrings("\\pi r^2", line.math[0].source);}test "parse renders paren math through termtex" {    const allocator = std.testing.allocator;    var line = try parse(allocator, "area \\(\\pi r^2\\) and bad \\(\\frac{x stays");    defer line.deinit(allocator);    try std.testing.expectEqualStrings("area πr² and bad \\(\\frac{x stays", line.text);    try std.testing.expectEqual(@as(usize, 1), line.spans.len);    try std.testing.expectEqual(Role.math, line.spans[0].role);    try std.testing.expectEqual(@as(?usize, 0), line.spans[0].math);    try std.testing.expectEqualStrings("πr²", line.text[line.spans[0].start..line.spans[0].end]);    try std.testing.expectEqual(@as(usize, 1), line.math.len);    try std.testing.expectEqualStrings("\\pi r^2", line.math[0].source);}test "parseSource preserves math for semantic renderers" {    const allocator = std.testing.allocator;    var line = try parseSource(allocator, "area $\\pi r^2$");    defer line.deinit(allocator);    try std.testing.expectEqualStrings("area \\pi r^2", line.text);    try std.testing.expectEqual(@as(usize, 1), line.spans.len);    try std.testing.expectEqual(Role.math, line.spans[0].role);    try std.testing.expectEqualStrings("\\pi r^2", line.math[0].source);}test "parse records image links" {    const allocator = std.testing.allocator;    var line = try parse(allocator, "open ![plot](plot.svg) and ![](empty.png)");    defer line.deinit(allocator);    try std.testing.expectEqualStrings("open plot and image", line.text);    try std.testing.expectEqual(@as(usize, 2), line.spans.len);    try std.testing.expectEqual(Role.image, line.spans[0].role);    try std.testing.expectEqual(@as(?usize, 0), line.spans[0].image);    try std.testing.expectEqualStrings("plot", line.text[line.spans[0].start..line.spans[0].end]);    try std.testing.expectEqualStrings("plot.svg", line.images[0].url);    try std.testing.expectEqual(Role.image, line.spans[1].role);    try std.testing.expectEqual(@as(?usize, 1), line.spans[1].image);    try std.testing.expectEqualStrings("empty.png", line.images[1].url);}test "parse supports underscore emphasis without snake case" {    const allocator = std.testing.allocator;    var line = try parse(allocator, "plain _em_ __strong__ ___both___ snake_case");    defer line.deinit(allocator);    try std.testing.expectEqualStrings("plain em strong both snake_case", line.text);    try std.testing.expectEqual(@as(usize, 3), line.spans.len);    try std.testing.expectEqual(Role.emphasis, line.spans[0].role);    try std.testing.expectEqualStrings("em", line.text[line.spans[0].start..line.spans[0].end]);    try std.testing.expectEqual(Role.strong, line.spans[1].role);    try std.testing.expectEqualStrings("strong", line.text[line.spans[1].start..line.spans[1].end]);    try std.testing.expectEqual(Role.strong_emphasis, line.spans[2].role);    try std.testing.expectEqualStrings("both", line.text[line.spans[2].start..line.spans[2].end]);    try std.testing.expect(std.mem.indexOf(u8, line.text, "snake_case") != null);}test "parse supports strikethrough" {    const allocator = std.testing.allocator;    var line = try parse(allocator, "plain ~~old~~ **~~bold old~~** ~~[docs](https://example.com/docs)~~ ~~open");    defer line.deinit(allocator);    try std.testing.expectEqualStrings("plain old bold old docs ~~open", line.text);    try std.testing.expectEqual(@as(usize, 3), line.spans.len);    try std.testing.expectEqual(Role.strikethrough, line.spans[0].role);    try std.testing.expectEqualStrings("old", line.text[line.spans[0].start..line.spans[0].end]);    try std.testing.expectEqual(Role.strong_strikethrough, line.spans[1].role);    try std.testing.expectEqualStrings("bold old", line.text[line.spans[1].start..line.spans[1].end]);    try std.testing.expectEqual(Role.strikethrough_link, line.spans[2].role);    try std.testing.expectEqualStrings("docs", line.text[line.spans[2].start..line.spans[2].end]);    try std.testing.expectEqualStrings("https://example.com/docs", line.links[line.spans[2].link.?].url);}test "parse leaves links literal inside code spans" {    const allocator = std.testing.allocator;    var line = try parse(allocator, "`[docs](https://example.com/docs)`");    defer line.deinit(allocator);    try std.testing.expectEqualStrings("[docs](https://example.com/docs)", line.text);    try std.testing.expectEqual(@as(usize, 1), line.spans.len);    try std.testing.expectEqual(Role.code, line.spans[0].role);    try std.testing.expect(line.spans[0].link == null);    try std.testing.expectEqual(@as(usize, 0), line.links.len);}

Source: lib/markdown/src/root.zig:10

zig
pub const @"inline" = @import("inline.zig");

Audit

Definitions1
Public names1
Members0
Version26.7.0
Revisiondaab053ee433