╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ markdown.zig ─────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭ find file ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╮ ╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╮ │ │ │http│ ssh │ │ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╯ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╯
730 var i: usize = 0; 731 while (i < s.len) : (i += 1) { 732 if (s[i] == '\\' and i + 1 < s.len and isPunct(s[i + 1])) i += 1; 733 try out.append(arena, s[i]); 734 } 735 return out.items; 736 } 737 738 // the index of the next run of exactly `n` backticks at or after `start` 739 fn findBacktickRun(s: []const u8, start: usize, n: usize) ?usize { 740 var i = start; 741 while (i < s.len) { 742 if (s[i] != '`') { 743 i += 1; 744 continue; 745 } 746 const len = runLength(s, i, '`'); 747 if (len == n) return i; 748 i += len; 749 } 750 return null; 751 } 752 753 fn codeContent(arena: std.mem.Allocator, raw: []const u8) ![]const u8 { 754 var content = raw; 755 if (std.mem.indexOfScalar(u8, content, '\n') != null) { 756 const copy = try arena.dupe(u8, content); 757 std.mem.replaceScalar(u8, copy, '\n', ' '); 758 content = copy; 759 } 760 // one space is stripped from each side of content that isn't all spaces 761 if (content.len >= 2 and content[0] == ' ' and content[content.len - 1] == ' ' and !isBlank(content)) { 762 content = content[1 .. content.len - 1]; 763 } 764 return content; 765 } 766 767 // parse the `(dest "title")` of an inline link starting at the `(` at `open`, 768 // ignoring the title. `next_close` caches the index of the next `)` (or 769 // src.len for none), so links scanned left to right never rescan for it. 770 fn inlineLink(arena: std.mem.Allocator, src: []const u8, open: usize, next_close: *usize) !?struct { dest: []const u8, end: usize } { 771 var j = open + 1; 772 while (j < src.len and isSpace(src[j])) j += 1; 773 const start = j; 774 var depth: usize = 0; 775 while (j < src.len) { 776 const c = src[j]; 777 if (c == ' ' or c < 0x20) break; 778 if (c == '\\' and j + 1 < src.len and isPunct(src[j + 1])) { 779 j += 2; 780 continue; 781 } 782 if (c == '(') { 783 // deeper nesting fails, which bounds the rescanning 784 if (depth == max_paren_depth) return null; 785 depth += 1; 786 } 787 if (c == ')') { 788 if (depth == 0) break; 789 depth -= 1; 790 } 791 j += 1; 792 } 793 if (depth != 0) return null; 794 const dest = src[start..j]; 795 const before_title = j; 796 while (j < src.len and isSpace(src[j])) j += 1; 797 // a title follows the destination after a space and runs to the `)` 798 if (j < src.len and src[j] != ')' and j > before_title) { 799 if (next_close.* < j) next_close.* = std.mem.indexOfScalarPos(u8, src, j, ')') orelse src.len; 800 j = next_close.*; 801 } 802 if (j >= src.len or src[j] != ')') return null; 803 return .{ .dest = try unescape(arena, dest), .end = j + 1 }; 804 } 805 806 // an autolink `<scheme:...>` or `<user@host>` at the start of `s` 807 fn autolink(s: []const u8) ?[]const u8 { 808 if (s.len < 3 or s[0] != '<') return null; 809 var close: usize = 1; 810 while (close < s.len and s[close] > ' ' and s[close] != '<' and s[close] != '>') close += 1; 811 if (close >= s.len or s[close] != '>') return null; 812 const body = s[1..close]; 813 814 const colon = std.mem.indexOfScalar(u8, body, ':') orelse return null; 815 const scheme = body[0..colon]; 816 if (scheme.len < 2 or scheme.len > 32 or !std.ascii.isAlphabetic(scheme[0])) return null; 817 for (scheme) |c| { 818 if (!std.ascii.isAlphanumeric(c) and c != '+' and c != '.' and c != '-') return null; 819 } 820 return body; 821 } 822 823 fn isAutolinkBoundary(c: u8) bool { 824 return switch (c) { 825 ' ', '\n', '*', '_', '~', '(' => true, 826 else => false, 827 }; 828 } 829 830 // the length of a bare `http://` or `https://` link at the start of `s` 831 fn bareAutolink(s: []const u8) ?usize { 832 const prefix_len: usize = if (std.ascii.startsWithIgnoreCase(s, "https://")) 8 else if (std.ascii.startsWithIgnoreCase(s, "http://")) 7 else return null; 833 var end = prefix_len; 834 while (end < s.len and !isSpace(s[end]) and s[end] != '<') end += 1; 835 var unmatched_closes = @as(isize, @intCast(std.mem.count(u8, s[0..end], ")"))) - @as(isize, @intCast(std.mem.count(u8, s[0..end], "("))); 836 // trailing punctuation is left out, as is an unbalanced closing paren 837 while (end > prefix_len) : (end -= 1) { 838 const c = s[end - 1]; 839 if (c == ')' and unmatched_closes > 0) { 840 unmatched_closes -= 1; 841 } else if (std.mem.indexOfScalar(u8, "?!.,:;*_~'\"", c) == null) break; 842 } 843 return if (end > prefix_len) end else null; 844 } 845