mdom 0.2.0 → 0.3.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.
@@ -0,0 +1,336 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../node"
4
+
5
+ # Markdown DOM 的 AST1 节点定义。
6
+ #
7
+ # 块级解析产出的树只描述**块结构**:段落、标题、列表等的正文一律以
8
+ # +attributes[:text]+ 保存**尚未做行内解析**的原文,等待后续的 InlineParser
9
+ # 展开为行内节点。嵌入节点(RubyEmbed)保存嵌入源码原文与位置,不求值。
10
+ #
11
+ # 每个具体节点类只固定自己的 +type+ 与语义属性,其余能力(挂载、遍历、
12
+ # 导航、深度维护……)全部继承自 +BaseNode+。
13
+ module MDOM
14
+ module Node
15
+ # 文档根节点。
16
+ class Document < BaseNode
17
+ def initialize(attributes: {}, children: [])
18
+ super(:document, attributes: attributes, children: children)
19
+ end
20
+
21
+ # 文档级元数据(+Hash+,惰性创建)。钩子常往里放东西(CSM §5.3),
22
+ # +MDOM.compile+ 也会把它作为 +:after_serialize+ 的第二个参数交出去。
23
+ def meta
24
+ attributes[:meta] ||= {}
25
+ end
26
+ end
27
+
28
+ # 节块:连续四个 `\n` 开启的新节,映射为 `<div>`。
29
+ class Div < BaseNode
30
+ def initialize(attributes: {}, children: [])
31
+ super(:div, attributes: attributes, children: children)
32
+ end
33
+ end
34
+
35
+ # 段落。+text+ 是尚未做行内解析的原文。
36
+ class Paragraph < BaseNode
37
+ def initialize(text = "", attributes: {}, children: [])
38
+ super(:paragraph, attributes: { text: text }.merge(attributes), children: children)
39
+ end
40
+
41
+ def text
42
+ attributes[:text]
43
+ end
44
+
45
+ def text=(value)
46
+ attributes[:text] = value
47
+ end
48
+ end
49
+
50
+ # ATX 标题。+level+ 为 1–6。
51
+ class Heading < BaseNode
52
+ def initialize(level, text = "", closed: false, attributes: {}, children: [])
53
+ attrs = { level: level, text: text, closed: closed }.merge(attributes)
54
+ super(:heading, attributes: attrs, children: children)
55
+ end
56
+
57
+ def level
58
+ attributes[:level]
59
+ end
60
+
61
+ def text
62
+ attributes[:text]
63
+ end
64
+
65
+ # 是否以**等长**的 `#` 收尾(CSM §1.2 的 hgroup 用法)。
66
+ def closed?
67
+ attributes[:closed]
68
+ end
69
+ end
70
+
71
+ # 内容策略为 FLOW 的容器(CSM 元素表:`blockquote` / `li` / `dd`)。
72
+ #
73
+ # 它们的正文**不直接挂在容器上**,而是先成为一个段落——FLOW 容器的直接子节点
74
+ # 是块级节点。+text+ 因此是派生读取:取第一个段落子节点的原文。
75
+ module FlowBody
76
+ # 正文原文;还没有段落时返回 +nil+。
77
+ def text
78
+ paragraph = children.find { |child| child.type == :paragraph }
79
+ paragraph&.text
80
+ end
81
+ end
82
+
83
+ # 块引用。正文(FLOW 内容)在 +children+ 里,通常是段落。
84
+ class Blockquote < BaseNode
85
+ include FlowBody
86
+
87
+ def initialize(attributes: {}, children: [])
88
+ super(:blockquote, attributes: attributes, children: children)
89
+ end
90
+
91
+ # `>-` 的出处:派生自 #Cite 子节点(CSM §1.4),没有时 +nil+。
92
+ def cite
93
+ source = children.find { |child| child.type == :cite }
94
+ source&.text
95
+ end
96
+ end
97
+
98
+ # 引用来源(`<cite>`,由 `>-` 收尾产生)。内容策略为 PHRASING(CSM §1.4),
99
+ # 所以正文直接是行内节点。
100
+ class Cite < BaseNode
101
+ def initialize(text = "", attributes: {}, children: [])
102
+ super(:cite, attributes: { text: text }.merge(attributes), children: children)
103
+ end
104
+
105
+ def text
106
+ attributes[:text]
107
+ end
108
+
109
+ def text=(value)
110
+ attributes[:text] = value
111
+ end
112
+ end
113
+
114
+ # 围栏代码块。+code+ 是围栏内的原文(不做任何转义)。
115
+ class CodeBlock < BaseNode
116
+ def initialize(code = "", lang: nil, params: nil, fence: 3, attributes: {}, children: [])
117
+ attrs = { code: code, lang: lang, params: params, fence: fence }.merge(attributes)
118
+ super(:code_block, attributes: attrs, children: children)
119
+ end
120
+
121
+ def code
122
+ attributes[:code]
123
+ end
124
+
125
+ def lang
126
+ attributes[:lang]
127
+ end
128
+
129
+ def params
130
+ attributes[:params]
131
+ end
132
+
133
+ # 围栏的反引号个数(CSM §1.5:3–6 个)。
134
+ def fence
135
+ attributes[:fence]
136
+ end
137
+ end
138
+
139
+ # 列表容器。+ordered+ 为 +true+ 时是有序列表;+task+ 为 +true+ 时是任务列表。
140
+ class List < BaseNode
141
+ def initialize(ordered: false, task: false, marker: "-", attributes: {}, children: [])
142
+ attrs = { ordered: ordered, task: task, marker: marker }.merge(attributes)
143
+ super(:list, attributes: attrs, children: children)
144
+ end
145
+
146
+ def ordered?
147
+ attributes[:ordered]
148
+ end
149
+
150
+ def task?
151
+ attributes[:task]
152
+ end
153
+ end
154
+
155
+ # 列表项。正文(FLOW 内容)在 +children+ 里,通常是段落。
156
+ # +checked+ 仅在任务列表中非 +nil+(+true+ / +false+)。
157
+ class ListItem < BaseNode
158
+ include FlowBody
159
+
160
+ def initialize(marker: "-", task: false, checked: nil, attributes: {}, children: [])
161
+ attrs = { marker: marker, task: task, checked: checked }.merge(attributes)
162
+ super(:list_item, attributes: attrs, children: children)
163
+ end
164
+
165
+ def marker
166
+ attributes[:marker]
167
+ end
168
+
169
+ def task?
170
+ attributes[:task]
171
+ end
172
+
173
+ def checked
174
+ attributes[:checked]
175
+ end
176
+ end
177
+
178
+ # 定义列表容器。
179
+ class DefinitionList < BaseNode
180
+ def initialize(attributes: {}, children: [])
181
+ super(:definition_list, attributes: attributes, children: children)
182
+ end
183
+ end
184
+
185
+ # 定义术语(`<dt>`)。
186
+ class Term < BaseNode
187
+ def initialize(text = "", attributes: {}, children: [])
188
+ super(:term, attributes: { text: text }.merge(attributes), children: children)
189
+ end
190
+
191
+ def text
192
+ attributes[:text]
193
+ end
194
+ end
195
+
196
+ # 定义内容(`<dd>`)。正文(FLOW 内容)在 +children+ 里,通常是段落。
197
+ class Definition < BaseNode
198
+ include FlowBody
199
+
200
+ def initialize(attributes: {}, children: [])
201
+ super(:definition, attributes: attributes, children: children)
202
+ end
203
+ end
204
+
205
+ # 表格(FLOW,**纯结构**):行是 +TableRow+,行内是 +TableHeaderCell+ /
206
+ # +TableCell+。单元格内容策略为 PHRASING(CSM §1.12)。
207
+ class Table < BaseNode
208
+ def initialize(attributes: {}, children: [])
209
+ super(:table, attributes: attributes, children: children)
210
+ end
211
+ end
212
+
213
+ # 表格行(`<tr>`,纯结构)。
214
+ class TableRow < BaseNode
215
+ def initialize(attributes: {}, children: [])
216
+ super(:table_row, attributes: attributes, children: children)
217
+ end
218
+ end
219
+
220
+ # `th` / `td` 共用:正文(PHRASING 原文)与列对齐。
221
+ # +align+ 为 +nil+(默认对齐)/ +:left+ / +:center+ / +:right+(CSM §1.12)。
222
+ module Cell
223
+ def text
224
+ attributes[:text]
225
+ end
226
+
227
+ def text=(value)
228
+ attributes[:text] = value
229
+ end
230
+
231
+ def align
232
+ attributes[:align]
233
+ end
234
+
235
+ def align=(value)
236
+ attributes[:align] = value
237
+ end
238
+ end
239
+
240
+ # 表头单元格(`th`)。由分隔行把它前面那一行提升而来。
241
+ class TableHeaderCell < BaseNode
242
+ include Cell
243
+
244
+ def initialize(text = "", align: nil, attributes: {}, children: [])
245
+ attrs = { text: text, align: align }.merge(attributes)
246
+ super(:table_header_cell, attributes: attrs, children: children)
247
+ end
248
+ end
249
+
250
+ # 数据单元格(`td`)。
251
+ class TableCell < BaseNode
252
+ include Cell
253
+
254
+ def initialize(text = "", align: nil, attributes: {}, children: [])
255
+ attrs = { text: text, align: align }.merge(attributes)
256
+ super(:table_cell, attributes: attrs, children: children)
257
+ end
258
+ end
259
+
260
+ # 分隔线(`<hr>`)。
261
+ class Hrule < BaseNode
262
+ def initialize(attributes: {}, children: [])
263
+ super(:hrule, attributes: attributes, children: children)
264
+ end
265
+ end
266
+
267
+ # 块级公式(`$$…$$`,FLOW)。+tex+ 是 TeX 原文(由 KaTeX 渲染,CSM §1.11)。
268
+ class MathBlock < BaseNode
269
+ def initialize(tex = "", attributes: {}, children: [])
270
+ super(:math_block, attributes: { tex: tex }.merge(attributes), children: children)
271
+ end
272
+
273
+ def tex
274
+ attributes[:tex]
275
+ end
276
+ end
277
+
278
+ # Ruby 嵌入块(三类通道统一为此节点)。
279
+ #
280
+ # * +source+ —— 已剥离块首行 CSM 缩进后的源码原文;
281
+ # * +form+ —— +:begin+(引导块关键字)/ +:free+(方法调用带块)/ +:tilde+(`~` 行);
282
+ # * +lines+ —— 在**文档**中的行号区间(+Range+,1 起);
283
+ # * +indent+ —— 块首行的 CSM 缩进(空格数);
284
+ # * +render+ —— 是否以 `.render` 收尾(`~` 行恒为 +false+)。
285
+ #
286
+ # 节点只承载「已剥离 N 层缩进」的字符串;块在 CSM 树中的层级由节点在树中的
287
+ # 位置决定,不由字符串承载。
288
+ class RubyEmbed < BaseNode
289
+ def initialize(source:, form:, lines:, indent: 0, render: false, attributes: {}, children: [])
290
+ attrs = {
291
+ source: source,
292
+ form: form,
293
+ lines: lines,
294
+ indent: indent,
295
+ render: render
296
+ }.merge(attributes)
297
+ super(:ruby_embed, attributes: attrs, children: children)
298
+ end
299
+
300
+ def source
301
+ attributes[:source]
302
+ end
303
+
304
+ def form
305
+ attributes[:form]
306
+ end
307
+
308
+ def lines
309
+ attributes[:lines]
310
+ end
311
+
312
+ def indent
313
+ attributes[:indent]
314
+ end
315
+
316
+ def render?
317
+ attributes[:render]
318
+ end
319
+ end
320
+
321
+ # `__END__` 边界。其后的原文挂在 +data+ 上,直通给 Ruby 的 +DATA+。
322
+ class EndMarker < BaseNode
323
+ def initialize(line, data = nil, attributes: {}, children: [])
324
+ super(:end_marker, attributes: { line: line, data: data }.merge(attributes), children: children)
325
+ end
326
+
327
+ def line
328
+ attributes[:line]
329
+ end
330
+
331
+ def data
332
+ attributes[:data]
333
+ end
334
+ end
335
+ end
336
+ end