red_quilt 0.6.1 → 0.7.0
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 +4 -4
- data/CHANGELOG.md +19 -0
- data/LICENSE.txt +21 -0
- data/README.md +63 -105
- data/docs/api.md +132 -0
- data/docs/{architecture.md → architecture.ja.md} +3 -3
- data/docs/{arena-usage.md → arena-usage.ja.md} +38 -30
- data/docs/{commonmark-conformance.md → commonmark-conformance.ja.md} +26 -10
- data/lib/red_quilt/cli.rb +1 -0
- data/lib/red_quilt/document.rb +4 -2
- data/lib/red_quilt/renderer/html.rb +8 -2
- data/lib/red_quilt/slug.rb +38 -0
- data/lib/red_quilt/tilt.rb +42 -0
- data/lib/red_quilt/version.rb +1 -1
- data/lib/red_quilt.rb +3 -2
- data/sig/red_quilt.rbs +199 -8
- metadata +15 -10
- data/.rspec +0 -3
- data/ast-spec.md +0 -1227
- data/mise.toml +0 -2
|
@@ -25,7 +25,7 @@ spec通りに動く挙動は仕様書(<https://spec.commonmark.org/0.31.2/>、<h
|
|
|
25
25
|
### 1.2 実装上の前提
|
|
26
26
|
|
|
27
27
|
- 入力はUTF-8文字列。`force_encoding(Encoding::UTF_8)` 等の前処理は呼び出し側の責任。
|
|
28
|
-
- spec§2.3 / §2.4が要求する正規化(NUL→U+FFFD、`\r\n` / `\r` → `\n
|
|
28
|
+
- spec§2.3 / §2.4が要求する正規化(NUL→U+FFFD、`\r\n` / `\r` → `\n`)と、blank line定義のspace/tab限定はいずれも実装している。
|
|
29
29
|
これはspec通りなので本書では個別項目化しない。
|
|
30
30
|
|
|
31
31
|
### 1.3 各項目のフォーマット
|
|
@@ -51,6 +51,7 @@ specの文言が複数解釈を許す箇所、あるいは "must" が曖昧な
|
|
|
51
51
|
**RedQuiltの挙動**: U+007Fを含めて拒否。
|
|
52
52
|
|
|
53
53
|
**実装**: `lib/red_quilt/inline/lexer.rb` — `URI_AUTOLINK_RE`
|
|
54
|
+
|
|
54
55
|
**テスト**: `spec/whitespace_strictness_spec.rb` — "URI autolink (CommonMark 6.5)"
|
|
55
56
|
|
|
56
57
|
### 2.2 Raw HTML tagのseparatorをspace/tab/CR/LFに限定
|
|
@@ -75,8 +76,8 @@ FF (U+000C) / VT (U+000B) は含めない。インラインのraw HTML、HTML bl
|
|
|
75
76
|
**RedQuiltの挙動**: link tail内ではspace / tabのみをseparatorとし、line endingは別途1回までカウント。
|
|
76
77
|
FF / VTが現れた場合はlinkとしては成立しない(段落の通常テキストとして扱う)。
|
|
77
78
|
|
|
78
|
-
**実装**: `lib/red_quilt/inline/
|
|
79
|
-
`skip_link_whitespace`、`
|
|
79
|
+
**実装**: `lib/red_quilt/inline/link_scanner.rb` — `link_tail_whitespace_byte?`、
|
|
80
|
+
`skip_link_whitespace`、`inline_link`、`parse_link_title`
|
|
80
81
|
|
|
81
82
|
**テスト**: `spec/whitespace_strictness_spec.rb` — "inline link tail whitespace (CommonMark 6.3)"
|
|
82
83
|
|
|
@@ -106,7 +107,8 @@ parentheses".
|
|
|
106
107
|
**実装**:
|
|
107
108
|
- 定数: `lib/red_quilt/reference_definition.rb` — `LABEL_MAX_LENGTH = 999`、`label_too_long?` ヘルパ
|
|
108
109
|
- 定義側: `match_label`(単一行・複数行のどちらでも判定)
|
|
109
|
-
- 使用側: `lib/red_quilt/inline/builder.rb` — `try_reference_link
|
|
110
|
+
- 使用側: `lib/red_quilt/inline/builder.rb` — `try_reference_link`、
|
|
111
|
+
`lib/red_quilt/inline/link_scanner.rb` — `reference_label`
|
|
110
112
|
|
|
111
113
|
**テスト**: `spec/link_validation_spec.rb` — "link label length limit (999 characters)"
|
|
112
114
|
|
|
@@ -151,14 +153,15 @@ specには規定が無いが、RedQuilt側で安全性・利便性のために
|
|
|
151
153
|
|
|
152
154
|
### 3.1 unsafe URL schemeのサニタイズ
|
|
153
155
|
|
|
154
|
-
**RedQuiltの挙動**: link / imageのdestinationのschemeが以下の安全リストに含まれない場合、`href` を空文字列にして出力する。
|
|
156
|
+
**RedQuiltの挙動**: link / imageのdestinationのschemeが以下の安全リストに含まれない場合、`href` / `src` を空文字列にして出力する。
|
|
155
157
|
同時に `:unsafe_url` のdiagnosticをwarningとして発行する。
|
|
158
|
+
CommonMark autolink (`<scheme:...>`) はspec適合のため安全リストではなくdenylistで扱い、script実行につながるschemeのみ空hrefにする。
|
|
156
159
|
|
|
157
160
|
**安全スキーム**: `http`、`https`、`mailto`、`ftp`、`tel`、`ssh`
|
|
158
161
|
|
|
159
|
-
|
|
162
|
+
**autolinkでブロックされるscheme**: `javascript`、`vbscript`、`data`
|
|
160
163
|
|
|
161
|
-
**実装**: `lib/red_quilt/inline/builder.rb` — `SAFE_SCHEMES
|
|
164
|
+
**実装**: `lib/red_quilt/inline/builder.rb` — `SAFE_SCHEMES`、`UNSAFE_AUTOLINK_SCHEMES`、`sanitize_destination`、`block_unsafe_autolink`
|
|
162
165
|
|
|
163
166
|
**テスト**: `spec/red_quilt_spec.rb` — "sanitizes unsafe URL schemes"
|
|
164
167
|
|
|
@@ -174,9 +177,13 @@ specには規定が無いが、RedQuilt側で安全性・利便性のために
|
|
|
174
177
|
|---|---|---|
|
|
175
178
|
| `:missing_reference` | warning | full reference link `[text][ref]` の定義が無い |
|
|
176
179
|
| `:duplicate_reference` | warning | 同じlabelのreference definitionが複数あった(最初の定義を採用) |
|
|
177
|
-
| `:
|
|
180
|
+
| `:duplicate_footnote` | warning | 同じlabelのfootnote definitionが複数あった(最初の定義を採用、`footnotes: true`時のみ) |
|
|
181
|
+
| `:unsafe_url` | warning | unsafeなURLを空の`href` / `src`に置換した |
|
|
182
|
+
| `:empty_link` | warning | link destinationが空(`lint: true`時のみ) |
|
|
183
|
+
| `:missing_alt` | info | imageのalt textが空(`lint: true`時のみ) |
|
|
184
|
+
| `:heading_level_skip` | info | 見出しレベルが1段を超えて飛んだ(`lint: true`時のみ) |
|
|
178
185
|
|
|
179
|
-
**実装**: `lib/red_quilt/diagnostic.rb`(値オブジェクト)、`lib/red_quilt/block_parser.rb`(duplicate)、`lib/red_quilt/inline/builder.rb`(missing / unsafe)
|
|
186
|
+
**実装**: `lib/red_quilt/diagnostic.rb`(値オブジェクト)、`lib/red_quilt/block_parser.rb`(duplicate reference)、`lib/red_quilt/footnote_definition.rb`(duplicate footnote)、`lib/red_quilt/inline/builder.rb`(missing / unsafe)、`lib/red_quilt/lint_pass.rb`(lint rules)
|
|
180
187
|
|
|
181
188
|
### 3.3 `allow_html` / `disallow_raw_html` フラグ
|
|
182
189
|
|
|
@@ -205,7 +212,7 @@ GFMが定めるdisallowedタグ群: `title`, `textarea`, `style`, `xmp`, `iframe
|
|
|
205
212
|
|
|
206
213
|
常時有効。`~~text~~` の2連tildeのみ対応(GFMの挙動と一致)。単一tilde`~text~` は通常テキストとして扱う。
|
|
207
214
|
|
|
208
|
-
**実装**: `lib/red_quilt/inline/
|
|
215
|
+
**実装**: `lib/red_quilt/inline/lexer.rb`(`SPECIAL_BYTES` と `scan_delim_run` の `~` 取扱)、`lib/red_quilt/inline/builder.rb`(`process_emphasis` で `STRIKETHROUGH` を生成)
|
|
209
216
|
|
|
210
217
|
### 4.3 GFM Disallowed Raw HTML
|
|
211
218
|
|
|
@@ -220,6 +227,13 @@ GFMが定めるdisallowedタグ群: `title`, `textarea`, `style`, `xmp`, `iframe
|
|
|
220
227
|
|
|
221
228
|
**実装**: `lib/red_quilt/extended_autolink_pass.rb`
|
|
222
229
|
|
|
230
|
+
### 4.5 GFM Footnotes
|
|
231
|
+
|
|
232
|
+
オプトイン。`footnotes: true` を指定すると `[^label]: ...` の定義を本文フローから除外し、`[^label]` 参照をsupリンクに変換する。
|
|
233
|
+
参照された定義だけを初回参照順に並べ、root末尾の`FOOTNOTES_SECTION`として出力する。未参照の定義は出力しない。
|
|
234
|
+
|
|
235
|
+
**実装**: `lib/red_quilt/footnote_definition.rb`、`lib/red_quilt/footnote_registry.rb`、`lib/red_quilt/footnote_pass.rb`
|
|
236
|
+
|
|
223
237
|
## 5. 未対応 / 既知の制限
|
|
224
238
|
|
|
225
239
|
- GFM Task List Items (`- [ ]` / `- [x]`) は未対応。通常のlist itemとしてパースされる。
|
|
@@ -237,5 +251,7 @@ GFMが定めるdisallowedタグ群: `title`, `textarea`, `style`, `xmp`, `iframe
|
|
|
237
251
|
| NCR桁上限と無効codepoint | `spec/numeric_character_reference_spec.rb` |
|
|
238
252
|
| GFM tableのcolumn count一致 | `spec/red_quilt_spec.rb` — "table separator validation" |
|
|
239
253
|
| GFM extended autolinkのdomain検証 | `spec/extended_autolink_spec.rb` |
|
|
254
|
+
| GFM footnotes | `spec/footnotes_spec.rb` |
|
|
240
255
|
| URL schemeサニタイズ | `spec/red_quilt_spec.rb` — "sanitizes unsafe URL schemes" |
|
|
256
|
+
| Diagnostics / lint diagnostics | `spec/diagnostic_spec.rb` |
|
|
241
257
|
| Disallowed Raw HTML | `spec/red_quilt_spec.rb` —disallow_raw_html系 |
|
data/lib/red_quilt/cli.rb
CHANGED
data/lib/red_quilt/document.rb
CHANGED
|
@@ -45,8 +45,10 @@ module RedQuilt
|
|
|
45
45
|
# theme: a bundled stylesheet to inline (`:none` embeds nothing, keeping
|
|
46
46
|
# the bare template; `:default` embeds RedQuilt's default theme). `css`
|
|
47
47
|
# (an external stylesheet link) is independent and may be combined.
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
# heading_ids: when true, every heading gets a slugified `id` (Unicode
|
|
49
|
+
# preserving, deduplicated within the document) for anchor links.
|
|
50
|
+
def to_html(standalone: false, title: nil, lang: "en", css: nil, theme: :none, heading_ids: false)
|
|
51
|
+
body = Renderer::HTML.new(self, heading_ids: heading_ids).render
|
|
50
52
|
return body unless standalone
|
|
51
53
|
|
|
52
54
|
wrap_standalone_html(body, title: title.to_s, lang: lang.to_s, css: css, theme: Theme.css(theme))
|
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
module RedQuilt
|
|
4
4
|
module Renderer
|
|
5
5
|
class HTML
|
|
6
|
-
def initialize(document)
|
|
6
|
+
def initialize(document, heading_ids: false)
|
|
7
7
|
@document = document
|
|
8
8
|
@arena = document.arena
|
|
9
9
|
@out = +""
|
|
10
|
+
@slugger = Slug::Counter.new if heading_ids
|
|
10
11
|
end
|
|
11
12
|
|
|
12
13
|
def render
|
|
@@ -45,7 +46,12 @@ module RedQuilt
|
|
|
45
46
|
@out << "</p>\n"
|
|
46
47
|
when NodeType::HEADING
|
|
47
48
|
level = @arena.int1(node_id)
|
|
48
|
-
@
|
|
49
|
+
if @slugger
|
|
50
|
+
id = @slugger.generate(PlainText.from(@arena, node_id))
|
|
51
|
+
@out << %(<h#{level} id="#{escape_html(id)}">)
|
|
52
|
+
else
|
|
53
|
+
@out << "<h#{level}>"
|
|
54
|
+
end
|
|
49
55
|
render_children(node_id)
|
|
50
56
|
@out << "</h#{level}>\n"
|
|
51
57
|
when NodeType::THEMATIC_BREAK
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RedQuilt
|
|
4
|
+
# Heading-anchor slugs, following GitHub's github-slugger approach:
|
|
5
|
+
# downcase, strip punctuation, spaces to hyphens -- but keep Unicode
|
|
6
|
+
# letters/marks/numbers verbatim, so Japanese (and other non-ASCII)
|
|
7
|
+
# headings survive instead of collapsing to empty ids.
|
|
8
|
+
module Slug
|
|
9
|
+
# Drop anything that is not a letter, mark, number, underscore, space,
|
|
10
|
+
# or hyphen. Browsers percent-encode non-ASCII fragment ids on the wire
|
|
11
|
+
# but resolve and display them fine, matching GitHub.
|
|
12
|
+
STRIP_RE = /[^\p{L}\p{M}\p{N}_ -]+/u
|
|
13
|
+
SPACE_RE = / +/
|
|
14
|
+
|
|
15
|
+
module_function
|
|
16
|
+
|
|
17
|
+
def slugify(text)
|
|
18
|
+
base = text.downcase.gsub(STRIP_RE, "").strip.gsub(SPACE_RE, "-")
|
|
19
|
+
base.empty? ? "section" : base
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Deduplicates slugs within a single document: the first occurrence of a
|
|
23
|
+
# base keeps it, later collisions get `-1`, `-2`, ... suffixes (matching
|
|
24
|
+
# GitHub's anchor numbering).
|
|
25
|
+
class Counter
|
|
26
|
+
def initialize
|
|
27
|
+
@seen = Hash.new(0)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def generate(text)
|
|
31
|
+
base = Slug.slugify(text)
|
|
32
|
+
count = @seen[base]
|
|
33
|
+
@seen[base] += 1
|
|
34
|
+
count.zero? ? base : "#{base}-#{count}"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "tilt"
|
|
4
|
+
require "red_quilt"
|
|
5
|
+
|
|
6
|
+
module RedQuilt
|
|
7
|
+
# Tilt template adapter. Require this file explicitly to register RedQuilt
|
|
8
|
+
# with Tilt -- `tilt` is an optional dependency, so a missing gem surfaces
|
|
9
|
+
# as a LoadError here rather than silently disabling the integration:
|
|
10
|
+
#
|
|
11
|
+
# require "red_quilt/tilt"
|
|
12
|
+
# Tilt.new("page.md").render
|
|
13
|
+
class TiltTemplate < Tilt::Template
|
|
14
|
+
self.default_mime_type = "text/html"
|
|
15
|
+
|
|
16
|
+
NATIVE_OPTIONS = %i[allow_html disallow_raw_html extended_autolinks footnotes lint heading_ids].freeze
|
|
17
|
+
|
|
18
|
+
def prepare; end
|
|
19
|
+
|
|
20
|
+
def evaluate(_scope, _locals)
|
|
21
|
+
@output ||= RedQuilt.render_html(data, **render_options)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# RedQuilt escapes raw HTML and never emits embedded scripting, so the
|
|
25
|
+
# output is a finished, script-free fragment.
|
|
26
|
+
def allows_script?
|
|
27
|
+
false
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def render_options
|
|
33
|
+
opts = options.slice(*NATIVE_OPTIONS)
|
|
34
|
+
# Tilt's cross-engine markdown convention is :escape_html; RedQuilt's
|
|
35
|
+
# native switch is its inverse (allow_html), so map it when present.
|
|
36
|
+
opts[:allow_html] = !options[:escape_html] if options.key?(:escape_html)
|
|
37
|
+
opts
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
Tilt.register(TiltTemplate, "md", "markdown", "mkd", "mkdn", "mdown")
|
|
42
|
+
end
|
data/lib/red_quilt/version.rb
CHANGED
data/lib/red_quilt.rb
CHANGED
|
@@ -10,6 +10,7 @@ require_relative "red_quilt/arena"
|
|
|
10
10
|
require_relative "red_quilt/node_ref"
|
|
11
11
|
require_relative "red_quilt/diagnostic"
|
|
12
12
|
require_relative "red_quilt/plain_text"
|
|
13
|
+
require_relative "red_quilt/slug"
|
|
13
14
|
require_relative "red_quilt/theme"
|
|
14
15
|
require_relative "red_quilt/document"
|
|
15
16
|
require_relative "red_quilt/inline"
|
|
@@ -56,13 +57,13 @@ module RedQuilt
|
|
|
56
57
|
document
|
|
57
58
|
end
|
|
58
59
|
|
|
59
|
-
def render_html(source, allow_html: false, disallow_raw_html: false, extended_autolinks: false, footnotes: false, lint: false)
|
|
60
|
+
def render_html(source, allow_html: false, disallow_raw_html: false, extended_autolinks: false, footnotes: false, lint: false, heading_ids: false)
|
|
60
61
|
parse(source,
|
|
61
62
|
allow_html: allow_html,
|
|
62
63
|
disallow_raw_html: disallow_raw_html,
|
|
63
64
|
extended_autolinks: extended_autolinks,
|
|
64
65
|
footnotes: footnotes,
|
|
65
|
-
lint: lint).to_html
|
|
66
|
+
lint: lint).to_html(heading_ids: heading_ids)
|
|
66
67
|
end
|
|
67
68
|
|
|
68
69
|
private
|
data/sig/red_quilt.rbs
CHANGED
|
@@ -1,19 +1,50 @@
|
|
|
1
1
|
module RedQuilt
|
|
2
2
|
VERSION: String
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
class Error < StandardError
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def self.parse: (String source,
|
|
8
|
+
?allow_html: bool,
|
|
9
|
+
?disallow_raw_html: bool,
|
|
10
|
+
?extended_autolinks: bool,
|
|
11
|
+
?footnotes: bool,
|
|
12
|
+
?lint: bool) -> Document
|
|
13
|
+
|
|
14
|
+
def self.render_html: (String source,
|
|
15
|
+
?allow_html: bool,
|
|
16
|
+
?disallow_raw_html: bool,
|
|
17
|
+
?extended_autolinks: bool,
|
|
18
|
+
?footnotes: bool,
|
|
19
|
+
?lint: bool,
|
|
20
|
+
?heading_ids: bool) -> String
|
|
6
21
|
|
|
7
22
|
class Document
|
|
8
23
|
attr_reader source: String
|
|
9
24
|
attr_reader arena: Arena
|
|
10
25
|
attr_reader root_id: Integer
|
|
26
|
+
attr_reader references: Hash[String, Hash[Symbol, untyped]]
|
|
27
|
+
attr_reader footnotes: FootnoteRegistry?
|
|
11
28
|
|
|
29
|
+
def initialize: (String source, Arena arena, Integer root_id,
|
|
30
|
+
?allow_html: bool,
|
|
31
|
+
?disallow_raw_html: bool,
|
|
32
|
+
?references: Hash[String, Hash[Symbol, untyped]],
|
|
33
|
+
?footnotes: FootnoteRegistry?) -> void
|
|
34
|
+
|
|
35
|
+
def allow_html?: () -> bool
|
|
36
|
+
def disallow_raw_html?: () -> bool
|
|
12
37
|
def root: () -> NodeRef
|
|
13
|
-
def
|
|
38
|
+
def walk: () { (NodeRef) -> void } -> void
|
|
39
|
+
| () -> Enumerator[NodeRef, void]
|
|
40
|
+
def to_html: (?standalone: bool, ?title: String?, ?lang: String,
|
|
41
|
+
?css: String?, ?theme: Symbol?, ?heading_ids: bool) -> String
|
|
14
42
|
def to_ast: () -> Hash[Symbol, untyped]
|
|
15
|
-
def
|
|
43
|
+
def to_json: (*untyped) -> String
|
|
44
|
+
def to_mdast: () -> Hash[String, untyped]
|
|
45
|
+
def first_heading_text: () -> String?
|
|
16
46
|
def source_map: () -> SourceMap
|
|
47
|
+
def diagnostics: () -> Array[Diagnostic]
|
|
17
48
|
end
|
|
18
49
|
|
|
19
50
|
class NodeRef
|
|
@@ -22,18 +53,83 @@ module RedQuilt
|
|
|
22
53
|
attr_reader document: Document
|
|
23
54
|
attr_reader node_id: Integer
|
|
24
55
|
|
|
56
|
+
def initialize: (Document document, Integer node_id) -> void
|
|
57
|
+
def each: () { (NodeRef) -> void } -> void
|
|
58
|
+
| () -> Enumerator[NodeRef, void]
|
|
25
59
|
def type: () -> Symbol
|
|
26
60
|
def children: () -> Array[NodeRef]
|
|
27
61
|
def walk: () { (NodeRef) -> void } -> void
|
|
28
|
-
|
|
29
|
-
def find_all: (Symbol type) -> Array[NodeRef]
|
|
30
|
-
def to_h: () -> Hash[Symbol, untyped]
|
|
62
|
+
| () -> Enumerator[NodeRef, void]
|
|
31
63
|
def text: () -> String?
|
|
32
64
|
def source_span: () -> SourceSpan?
|
|
65
|
+
def find_all: (Symbol type) -> Array[NodeRef]
|
|
33
66
|
def source_location: () -> { start_line: Integer, start_column: Integer, end_line: Integer, end_column: Integer }?
|
|
67
|
+
def to_h: () -> Hash[Symbol, untyped]
|
|
34
68
|
end
|
|
35
69
|
|
|
70
|
+
# Low-level parallel-array storage for the AST. See docs/arena-usage.ja.md.
|
|
36
71
|
class Arena
|
|
72
|
+
NO_NODE: Integer
|
|
73
|
+
|
|
74
|
+
class IntegrityError < StandardError
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
attr_reader source: String
|
|
78
|
+
|
|
79
|
+
def initialize: (String source) -> void
|
|
80
|
+
|
|
81
|
+
# Structure mutators. The child/insert/detach helpers return the
|
|
82
|
+
# affected node id; update_* / reparent return values are incidental.
|
|
83
|
+
def add_node: (Integer type,
|
|
84
|
+
?source_start: Integer, ?source_len: Integer,
|
|
85
|
+
?int1: Integer, ?int2: Integer, ?int3: Integer,
|
|
86
|
+
?str1: String?, ?str2: String?) -> Integer
|
|
87
|
+
def append_child: (Integer parent_id, Integer child_id) -> Integer
|
|
88
|
+
def insert_before: (Integer parent_id, Integer ref_id, Integer new_id) -> Integer
|
|
89
|
+
def detach: (Integer child_id) -> Integer
|
|
90
|
+
def reparent: (Integer new_parent_id, Integer first_id, Integer last_id) -> void
|
|
91
|
+
def update_span: (Integer id, Integer start_byte, Integer end_byte) -> void
|
|
92
|
+
def update_str1: (Integer id, String? value) -> void
|
|
93
|
+
def update_int3: (Integer id, Integer value) -> void
|
|
94
|
+
|
|
95
|
+
# Raw id accessors (may return NO_NODE)
|
|
96
|
+
def raw_parent_id: (Integer id) -> Integer
|
|
97
|
+
def raw_first_child_id: (Integer id) -> Integer
|
|
98
|
+
def raw_last_child_id: (Integer id) -> Integer
|
|
99
|
+
def raw_next_sibling_id: (Integer id) -> Integer
|
|
100
|
+
def raw_prev_sibling_id: (Integer id) -> Integer
|
|
101
|
+
|
|
102
|
+
# Column accessors
|
|
103
|
+
def type: (Integer id) -> Integer
|
|
104
|
+
def type_name: (Integer id) -> Symbol
|
|
105
|
+
def source_start: (Integer id) -> Integer
|
|
106
|
+
def source_len: (Integer id) -> Integer
|
|
107
|
+
def source_end: (Integer id) -> Integer
|
|
108
|
+
def int1: (Integer id) -> Integer
|
|
109
|
+
def int2: (Integer id) -> Integer
|
|
110
|
+
def int3: (Integer id) -> Integer
|
|
111
|
+
def str1: (Integer id) -> String?
|
|
112
|
+
def str2: (Integer id) -> String?
|
|
113
|
+
|
|
114
|
+
# Semantic accessors
|
|
115
|
+
def source_span: (Integer id) -> SourceSpan?
|
|
116
|
+
def text: (Integer id) -> String?
|
|
117
|
+
|
|
118
|
+
# Traversal
|
|
119
|
+
def each_child: (Integer id) { (Integer child_id) -> void } -> self
|
|
120
|
+
def child_ids: (Integer id) -> Enumerator[Integer, void]
|
|
121
|
+
|
|
122
|
+
def check_integrity!: (Integer root_id) -> self
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
class SourceSpan
|
|
126
|
+
attr_reader start_byte: Integer
|
|
127
|
+
attr_reader end_byte: Integer
|
|
128
|
+
|
|
129
|
+
def initialize: (Integer start_byte, Integer end_byte) -> void
|
|
130
|
+
def length: () -> Integer
|
|
131
|
+
def ==: (untyped other) -> bool
|
|
132
|
+
def to_h: () -> { start_byte: Integer, end_byte: Integer }
|
|
37
133
|
end
|
|
38
134
|
|
|
39
135
|
class SourceMap
|
|
@@ -41,5 +137,100 @@ module RedQuilt
|
|
|
41
137
|
def line_column: (Integer byte_offset) -> { line: Integer, column: Integer }
|
|
42
138
|
end
|
|
43
139
|
|
|
44
|
-
|
|
140
|
+
class Diagnostic
|
|
141
|
+
SEVERITIES: Array[Symbol]
|
|
142
|
+
|
|
143
|
+
attr_reader severity: Symbol
|
|
144
|
+
attr_reader rule: Symbol
|
|
145
|
+
attr_reader message: String
|
|
146
|
+
attr_reader source_span: SourceSpan?
|
|
147
|
+
|
|
148
|
+
def initialize: (severity: Symbol, rule: Symbol, message: String,
|
|
149
|
+
?source_span: SourceSpan?) -> void
|
|
150
|
+
def to_h: () -> Hash[Symbol, untyped]
|
|
151
|
+
def ==: (untyped other) -> bool
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Integer NodeType constants (block: 1, 10-22; inline: 100-112).
|
|
155
|
+
module NodeType
|
|
156
|
+
DOCUMENT: Integer
|
|
157
|
+
PARAGRAPH: Integer
|
|
158
|
+
HEADING: Integer
|
|
159
|
+
THEMATIC_BREAK: Integer
|
|
160
|
+
BLOCKQUOTE: Integer
|
|
161
|
+
LIST: Integer
|
|
162
|
+
LIST_ITEM: Integer
|
|
163
|
+
CODE_BLOCK: Integer
|
|
164
|
+
HTML_BLOCK: Integer
|
|
165
|
+
TABLE: Integer
|
|
166
|
+
TABLE_ROW: Integer
|
|
167
|
+
TABLE_CELL: Integer
|
|
168
|
+
FOOTNOTE_DEFINITION: Integer
|
|
169
|
+
FOOTNOTES_SECTION: Integer
|
|
170
|
+
TEXT: Integer
|
|
171
|
+
SOFTBREAK: Integer
|
|
172
|
+
HARDBREAK: Integer
|
|
173
|
+
EMPHASIS: Integer
|
|
174
|
+
STRONG: Integer
|
|
175
|
+
CODE_SPAN: Integer
|
|
176
|
+
LINK: Integer
|
|
177
|
+
IMAGE: Integer
|
|
178
|
+
HTML_INLINE: Integer
|
|
179
|
+
STRIKETHROUGH: Integer
|
|
180
|
+
FOOTNOTE_REFERENCE: Integer
|
|
181
|
+
|
|
182
|
+
TYPE_NAMES: Hash[Integer, Symbol]
|
|
183
|
+
|
|
184
|
+
def self.name_for: (Integer type) -> Symbol
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# Unicode-preserving heading-anchor slugs (github-slugger style).
|
|
188
|
+
module Slug
|
|
189
|
+
STRIP_RE: Regexp
|
|
190
|
+
SPACE_RE: Regexp
|
|
191
|
+
|
|
192
|
+
def self.slugify: (String text) -> String
|
|
193
|
+
|
|
194
|
+
# Per-document collision counter; later duplicates get `-N` suffixes.
|
|
195
|
+
class Counter
|
|
196
|
+
def initialize: () -> void
|
|
197
|
+
def generate: (String text) -> String
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# Bundled stylesheets for standalone HTML output (`:none` => no CSS).
|
|
202
|
+
# `name` is coerced with `to_sym`, so String names ("default") work too.
|
|
203
|
+
module Theme
|
|
204
|
+
NAMES: Array[Symbol]
|
|
205
|
+
|
|
206
|
+
def self.css: (Symbol | String | nil name) -> String?
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# Shared footnote state (created per parse when `footnotes: true`).
|
|
210
|
+
class FootnoteRegistry
|
|
211
|
+
def initialize: () -> void
|
|
212
|
+
def define: (String label, Integer node_id) -> bool
|
|
213
|
+
def defined?: (String label) -> bool
|
|
214
|
+
def definition_node: (String label) -> Integer?
|
|
215
|
+
def reference: (String label) -> [ Integer, Integer ]?
|
|
216
|
+
def number: (String label) -> Integer?
|
|
217
|
+
def occurrences: (String label) -> Integer
|
|
218
|
+
def referenced_labels: () -> Array[String]
|
|
219
|
+
def any_referenced?: () -> bool
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
module CLI
|
|
223
|
+
# Duck types for the injected IO objects (tests pass StringIO, not IO).
|
|
224
|
+
interface _Reader
|
|
225
|
+
def read: () -> String?
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
interface _Writer
|
|
229
|
+
def write: (*untyped) -> Integer
|
|
230
|
+
def puts: (*untyped) -> void
|
|
231
|
+
def <<: (untyped) -> self
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def self.run: (Array[String] argv, ?stdin: _Reader, ?stdout: _Writer, ?stderr: _Writer) -> Integer
|
|
235
|
+
end
|
|
45
236
|
end
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: red_quilt
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- takahashim
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 2026-05-29 00:00:00.000000000 Z
|
|
11
11
|
dependencies: []
|
|
12
12
|
description: A modern Markdown document processor in pure Ruby, with an arena-style
|
|
13
13
|
AST and full CommonMark spec test suite compliance.
|
|
@@ -18,16 +18,16 @@ executables:
|
|
|
18
18
|
extensions: []
|
|
19
19
|
extra_rdoc_files: []
|
|
20
20
|
files:
|
|
21
|
-
- ".rspec"
|
|
22
21
|
- ".rubocop.yml"
|
|
23
22
|
- ".rubocop_todo.yml"
|
|
24
23
|
- CHANGELOG.md
|
|
24
|
+
- LICENSE.txt
|
|
25
25
|
- README.md
|
|
26
26
|
- Rakefile
|
|
27
|
-
-
|
|
28
|
-
- docs/architecture.md
|
|
29
|
-
- docs/arena-usage.md
|
|
30
|
-
- docs/commonmark-conformance.md
|
|
27
|
+
- docs/api.md
|
|
28
|
+
- docs/architecture.ja.md
|
|
29
|
+
- docs/arena-usage.ja.md
|
|
30
|
+
- docs/commonmark-conformance.ja.md
|
|
31
31
|
- exe/redquilt
|
|
32
32
|
- lib/red_quilt.rb
|
|
33
33
|
- lib/red_quilt/arena.rb
|
|
@@ -59,18 +59,23 @@ files:
|
|
|
59
59
|
- lib/red_quilt/reference_definition.rb
|
|
60
60
|
- lib/red_quilt/renderer/html.rb
|
|
61
61
|
- lib/red_quilt/renderer/mdast.rb
|
|
62
|
+
- lib/red_quilt/slug.rb
|
|
62
63
|
- lib/red_quilt/source_map.rb
|
|
63
64
|
- lib/red_quilt/source_span.rb
|
|
64
65
|
- lib/red_quilt/theme.rb
|
|
65
66
|
- lib/red_quilt/themes/default.css
|
|
67
|
+
- lib/red_quilt/tilt.rb
|
|
66
68
|
- lib/red_quilt/version.rb
|
|
67
|
-
- mise.toml
|
|
68
69
|
- sig/red_quilt.rbs
|
|
69
70
|
homepage: https://github.com/takahashim/red_quilt
|
|
70
|
-
licenses:
|
|
71
|
+
licenses:
|
|
72
|
+
- MIT
|
|
71
73
|
metadata:
|
|
72
74
|
homepage_uri: https://github.com/takahashim/red_quilt
|
|
73
75
|
source_code_uri: https://github.com/takahashim/red_quilt
|
|
76
|
+
changelog_uri: https://github.com/takahashim/red_quilt/blob/main/CHANGELOG.md
|
|
77
|
+
documentation_uri: https://github.com/takahashim/red_quilt#readme
|
|
78
|
+
rubygems_mfa_required: 'true'
|
|
74
79
|
rdoc_options: []
|
|
75
80
|
require_paths:
|
|
76
81
|
- lib
|
|
@@ -85,7 +90,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
85
90
|
- !ruby/object:Gem::Version
|
|
86
91
|
version: '0'
|
|
87
92
|
requirements: []
|
|
88
|
-
rubygems_version:
|
|
93
|
+
rubygems_version: 3.6.2
|
|
89
94
|
specification_version: 4
|
|
90
95
|
summary: CommonMark-based Markdown processor written in pure Ruby
|
|
91
96
|
test_files: []
|
data/.rspec
DELETED