╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ markdown.zig ─────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
790 if (j < src.len and src[j] != ')' and j > before_title) { 791 if (next_close.* < j) next_close.* = std.mem.indexOfScalarPos(u8, src, j, ')') orelse src.len; 792 j = next_close.*; 793 } 794 if (j >= src.len or src[j] != ')') return null; 795 return .{ .dest = try unescape(arena, dest), .end = j + 1 }; 796 } 797 798 // an autolink `<scheme:...>` or `<user@host>` at the start of `s` 799 fn autolink(s: []const u8) ?[]const u8 { 800 if (s.len < 3 or s[0] != '<') return null; 801 var close: usize = 1; 802 while (close < s.len and s[close] > ' ' and s[close] != '<' and s[close] != '>') close += 1; 803 if (close >= s.len or s[close] != '>') return null; 804 const body = s[1..close]; 805 806 const colon = std.mem.indexOfScalar(u8, body, ':') orelse return null; 807 const scheme = body[0..colon]; 808 if (scheme.len < 2 or scheme.len > 32 or !std.ascii.isAlphabetic(scheme[0])) return null; 809 for (scheme) |c| { 810 if (!std.ascii.isAlphanumeric(c) and c != '+' and c != '.' and c != '-') return null; 811 } 812 return body; 813 } 814 815 fn isAutolinkBoundary(c: u8) bool { 816 return switch (c) { 817 ' ', '\n', '*', '_', '~', '(' => true, 818 else => false, 819 }; 820 } 821 822 // the length of a bare `http://` or `https://` link at the start of `s` 823 fn bareAutolink(s: []const u8) ?usize { 824 const prefix_len: usize = if (std.ascii.startsWithIgnoreCase(s, "https://")) 8 else if (std.ascii.startsWithIgnoreCase(s, "http://")) 7 else return null; 825 var end = prefix_len; 826 while (end < s.len and !isSpace(s[end]) and s[end] != '<') end += 1; 827 var unmatched_closes = @as(isize, @intCast(std.mem.count(u8, s[0..end], ")"))) - @as(isize, @intCast(std.mem.count(u8, s[0..end], "("))); 828 // trailing punctuation is left out, as is an unbalanced closing paren 829 while (end > prefix_len) : (end -= 1) { 830 const c = s[end - 1]; 831 if (c == ')' and unmatched_closes > 0) { 832 unmatched_closes -= 1; 833 } else if (std.mem.indexOfScalar(u8, "?!.,:;*_~'\"", c) == null) break; 834 } 835 return if (end > prefix_len) end else null; 836 } 837