red_quilt 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +109 -0
- data/.rubocop_todo.yml +7 -0
- data/CHANGELOG.md +57 -0
- data/README.md +284 -0
- data/Rakefile +8 -0
- data/ast-spec.md +1227 -0
- data/docs/architecture.md +81 -0
- data/docs/arena-usage.md +363 -0
- data/docs/commonmark-conformance.md +241 -0
- data/exe/redquilt +7 -0
- data/lib/red_quilt/arena.rb +366 -0
- data/lib/red_quilt/block_parser.rb +724 -0
- data/lib/red_quilt/blockquote.rb +151 -0
- data/lib/red_quilt/cli.rb +182 -0
- data/lib/red_quilt/diagnostic.rb +47 -0
- data/lib/red_quilt/document.rb +126 -0
- data/lib/red_quilt/extended_autolink_pass.rb +185 -0
- data/lib/red_quilt/footnote_definition.rb +147 -0
- data/lib/red_quilt/footnote_pass.rb +39 -0
- data/lib/red_quilt/footnote_registry.rb +68 -0
- data/lib/red_quilt/indentation.rb +73 -0
- data/lib/red_quilt/inline/builder.rb +674 -0
- data/lib/red_quilt/inline/flanking.rb +120 -0
- data/lib/red_quilt/inline/html_entities.rb +2180 -0
- data/lib/red_quilt/inline/lexer.rb +280 -0
- data/lib/red_quilt/inline/link_scanner.rb +315 -0
- data/lib/red_quilt/inline/token_kind.rb +39 -0
- data/lib/red_quilt/inline/tokens.rb +73 -0
- data/lib/red_quilt/inline.rb +34 -0
- data/lib/red_quilt/inline_pass.rb +53 -0
- data/lib/red_quilt/line.rb +14 -0
- data/lib/red_quilt/lint_pass.rb +71 -0
- data/lib/red_quilt/list.rb +317 -0
- data/lib/red_quilt/node_ref.rb +114 -0
- data/lib/red_quilt/node_type.rb +66 -0
- data/lib/red_quilt/plain_text.rb +46 -0
- data/lib/red_quilt/reference_definition.rb +309 -0
- data/lib/red_quilt/renderer/html.rb +279 -0
- data/lib/red_quilt/renderer/mdast.rb +152 -0
- data/lib/red_quilt/source_map.rb +29 -0
- data/lib/red_quilt/source_span.rb +26 -0
- data/lib/red_quilt/theme.rb +28 -0
- data/lib/red_quilt/themes/default.css +87 -0
- data/lib/red_quilt/version.rb +5 -0
- data/lib/red_quilt.rb +86 -0
- data/mise.toml +2 -0
- data/sig/red_quilt.rbs +45 -0
- metadata +91 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RedQuilt
|
|
4
|
+
module Inline
|
|
5
|
+
# CommonMark spec 6.2 flanking delimiter run helpers.
|
|
6
|
+
#
|
|
7
|
+
# Determines whether a delimiter run can open and/or close an emphasis.
|
|
8
|
+
# All input positions are byte offsets into the document source.
|
|
9
|
+
module Flanking
|
|
10
|
+
UNICODE_WHITESPACE_RE = /\A[\s - ]\z/
|
|
11
|
+
# CommonMark 0.31.2 expanded the definition of "punctuation" for
|
|
12
|
+
# flanking purposes to also include Unicode S (symbol) category, so
|
|
13
|
+
# currency / math / other symbols form delimiter-run boundaries.
|
|
14
|
+
UNICODE_PUNCT_RE = /\A[\p{P}\p{S}]\z/
|
|
15
|
+
|
|
16
|
+
# Fast-path lookup table for ASCII whitespace. Flanking inputs are
|
|
17
|
+
# mostly single-byte ASCII; the table lets us skip regex matches
|
|
18
|
+
# entirely on the hot path. (ASCII punctuation uses the shared
|
|
19
|
+
# Inline::ASCII_PUNCT table.)
|
|
20
|
+
ASCII_WHITESPACE = Array.new(128, false)
|
|
21
|
+
[0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x20].each { |b| ASCII_WHITESPACE[b] = true }
|
|
22
|
+
ASCII_WHITESPACE.freeze
|
|
23
|
+
|
|
24
|
+
module_function
|
|
25
|
+
|
|
26
|
+
# Returns the character immediately before the byte position, or nil
|
|
27
|
+
# if at the start of source / outside the inline range.
|
|
28
|
+
def char_before(source, byte_pos, range_start)
|
|
29
|
+
return nil if byte_pos <= range_start
|
|
30
|
+
|
|
31
|
+
prev = byte_pos - 1
|
|
32
|
+
b = source.getbyte(prev)
|
|
33
|
+
# ASCII fast path: shared 1-byte string (avoids byteslice + alloc).
|
|
34
|
+
return BYTE_CHR[b] if b < 0x80
|
|
35
|
+
|
|
36
|
+
# Walk back at most 4 bytes to find the UTF-8 code point start.
|
|
37
|
+
i = prev
|
|
38
|
+
while i >= range_start && i > byte_pos - 4
|
|
39
|
+
b = source.getbyte(i)
|
|
40
|
+
if b < 0x80 || b >= 0xC0
|
|
41
|
+
return source.byteslice(i, byte_pos - i)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
i -= 1
|
|
45
|
+
end
|
|
46
|
+
nil
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def char_at(source, byte_pos, range_end)
|
|
50
|
+
return nil if byte_pos >= range_end
|
|
51
|
+
|
|
52
|
+
b = source.getbyte(byte_pos)
|
|
53
|
+
return BYTE_CHR[b] if b < 0x80
|
|
54
|
+
|
|
55
|
+
len = if b < 0xC0
|
|
56
|
+
1
|
|
57
|
+
elsif b < 0xE0
|
|
58
|
+
2
|
|
59
|
+
elsif b < 0xF0
|
|
60
|
+
3
|
|
61
|
+
else
|
|
62
|
+
4
|
|
63
|
+
end
|
|
64
|
+
source.byteslice(byte_pos, [len, range_end - byte_pos].min)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def whitespace?(char)
|
|
68
|
+
return true if char.nil?
|
|
69
|
+
if char.bytesize == 1
|
|
70
|
+
return ASCII_WHITESPACE[char.getbyte(0)]
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
UNICODE_WHITESPACE_RE.match?(char)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def punctuation?(char)
|
|
77
|
+
return false if char.nil?
|
|
78
|
+
if char.bytesize == 1
|
|
79
|
+
return ASCII_PUNCT[char.getbyte(0)]
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
UNICODE_PUNCT_RE.match?(char)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# CommonMark spec: left-flanking delimiter run.
|
|
86
|
+
def left_flanking?(prev_char, next_char)
|
|
87
|
+
return false if whitespace?(next_char)
|
|
88
|
+
return true unless punctuation?(next_char)
|
|
89
|
+
|
|
90
|
+
whitespace?(prev_char) || punctuation?(prev_char)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# CommonMark spec: right-flanking delimiter run.
|
|
94
|
+
def right_flanking?(prev_char, next_char)
|
|
95
|
+
return false if whitespace?(prev_char)
|
|
96
|
+
return true unless punctuation?(prev_char)
|
|
97
|
+
|
|
98
|
+
whitespace?(next_char) || punctuation?(next_char)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Returns [can_open, can_close] for a delimiter run.
|
|
102
|
+
#
|
|
103
|
+
# char must be "*", "_", or "~". For "_", word-character adjacency
|
|
104
|
+
# rules apply on top of the flanking rules; "*" and "~" use plain
|
|
105
|
+
# flanking only.
|
|
106
|
+
def can_open_close(char, prev_char, next_char)
|
|
107
|
+
left = left_flanking?(prev_char, next_char)
|
|
108
|
+
right = right_flanking?(prev_char, next_char)
|
|
109
|
+
if char == "_"
|
|
110
|
+
can_open = left && (!right || punctuation?(prev_char))
|
|
111
|
+
can_close = right && (!left || punctuation?(next_char))
|
|
112
|
+
else
|
|
113
|
+
can_open = left
|
|
114
|
+
can_close = right
|
|
115
|
+
end
|
|
116
|
+
[can_open, can_close]
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|