arity_parser/syntax.rs
1use rowan::Language;
2
3pub mod ptr;
4
5pub use ptr::NodePtr;
6
7#[allow(non_camel_case_types)]
8#[derive(
9 Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
10)]
11#[repr(u16)]
12pub enum SyntaxKind {
13 ROOT,
14 BINARY_EXPR,
15 ASSIGNMENT_EXPR,
16 PAREN_EXPR,
17 IF_EXPR,
18 FOR_EXPR,
19 WHILE_EXPR,
20 REPEAT_EXPR,
21 FUNCTION_EXPR,
22 BLOCK_EXPR,
23 UNARY_EXPR,
24 IDENT,
25 INT,
26 FLOAT,
27 STRING,
28 COMMENT,
29 TILDE,
30 USER_OP,
31 LBRACK,
32 RBRACK,
33 LBRACK2,
34 RBRACK2,
35 PLUS,
36 MINUS,
37 STAR,
38 SLASH,
39 CARET,
40 PIPE,
41 COLON,
42 COLON2,
43 COLON3,
44 DOLLAR,
45 AT,
46 SEMICOLON,
47 COMMA,
48 OR,
49 OR2,
50 AND,
51 AND2,
52 EQUAL2,
53 NOT_EQUAL,
54 BANG,
55 LESS_THAN,
56 LESS_THAN_OR_EQUAL,
57 GREATER_THAN,
58 GREATER_THAN_OR_EQUAL,
59 LPAREN,
60 RPAREN,
61 IF_KW,
62 ELSE_KW,
63 FOR_KW,
64 WHILE_KW,
65 REPEAT_KW,
66 FUNCTION_KW,
67 IN_KW,
68 LBRACE,
69 RBRACE,
70 WHITESPACE,
71 NEWLINE,
72 ASSIGN_LEFT,
73 SUPER_ASSIGN,
74 ASSIGN_RIGHT,
75 SUPER_ASSIGN_RIGHT,
76 ASSIGN_EQ,
77 CALL_EXPR,
78 SUBSET_EXPR,
79 SUBSET2_EXPR,
80 ARG_LIST,
81 ARG,
82 ERROR,
83 COMPLEX,
84 QUESTION,
85 WALRUS,
86 // Roxygen tokens (leaves). A roxygen line (`#'` …) is sub-tokenized so its
87 // structure lives in the CST; the texts of these tokens tile the line's
88 // bytes exactly (losslessness).
89 ROXYGEN_MARKER,
90 ROXYGEN_AT,
91 ROXYGEN_TAG_NAME,
92 ROXYGEN_TAG_ARG,
93 ROXYGEN_TEXT,
94 // Roxygen protected-span leaves: inline markup carved out of a `ROXYGEN_TEXT`
95 // run so the formatter can treat each as an atomic unit during prose reflow
96 // (and a future linter can resolve the code references inside them). Each
97 // holds its whole span, delimiters included, so the run still tiles exactly.
98 ROXYGEN_CODE,
99 ROXYGEN_RD_MACRO,
100 ROXYGEN_MD_LINK,
101 // Roxygen nodes.
102 ROXYGEN_BLOCK,
103 /// Reserved: the legacy physical-line node. No longer emitted since the
104 /// CST re-model (a roxygen block now owns logical content --- sections and
105 /// paragraphs --- with `#'` markers and newlines threaded in as trivia, the
106 /// way rowan/rust-analyzer trees attach whitespace). Kept in the enum so the
107 /// `as u16` discriminants of the later variants stay stable.
108 ROXYGEN_LINE,
109 ROXYGEN_TAG,
110 // Inline Rd-macro structure. A `ROXYGEN_RD_MACRO` is materialized as a *node*
111 // (not a leaf) whose children carve the macro into its parts so the CST models
112 // what `tools::parse_Rd` parses: `\code{\link{x}}` becomes nested macro nodes,
113 // not flat text. These leaves tile the macro span exactly (losslessness).
114 ROXYGEN_RD_MACRO_NAME, // the `\name` head (backslash included)
115 ROXYGEN_RD_MACRO_OPT, // a `[...]` option group (e.g. `\link[pkg]{x}`)
116 ROXYGEN_RD_MACRO_DELIM, // a `{` or `}` content delimiter
117 ROXYGEN_RD_MACRO_VERB, // verbatim content of a VERB macro (`\url`, `\verb`, …)
118 // Roxygen logical-content nodes (the CST re-model). A block's children are
119 // `ROXYGEN_SECTION`s (the intro prose, then one per `@tag`); a section's prose
120 // is grouped into `ROXYGEN_PARAGRAPH`s between blank-line separators. Markers,
121 // the marker→content whitespace, and inter-line newlines live as trivia leaves
122 // threaded into the enclosing node.
123 ROXYGEN_SECTION,
124 ROXYGEN_PARAGRAPH,
125 // Markdown inline leaves, emitted **only** under a resolved `@md` block mode
126 // (Rd-first, markdown-second). Like the other protected-span leaves they hold
127 // their whole span (delimiters included) so the run tiles exactly, but their
128 // *kind* is what records the resolved mode: in non-markdown mode `*x*` and
129 // `` `x` `` stay literal `ROXYGEN_TEXT`/`ROXYGEN_CODE`, so the CST (and the
130 // projected Rd) differ by mode. The projector maps emphasis/strong to
131 // `\emph`/`\strong`, and a code span to `\code` or `\verb` per roxygen2's
132 // R-parseability rule. Appended here (not beside `ROXYGEN_CODE`) to keep the
133 // earlier variants' `as u16` discriminants stable.
134 ROXYGEN_MD_EMPH,
135 ROXYGEN_MD_STRONG,
136 ROXYGEN_MD_CODE,
137 /// A markdown list item's leading marker (`-`/`*`/`+` or `1.`/`1)`), emitted
138 /// **only** under a resolved `@md` block mode and only at a line's content
139 /// start. Like the other markdown leaves it holds its literal source (the
140 /// bullet/number punctuation, *without* the trailing space, so a marker that
141 /// does not form a list — see the CommonMark interrupt rule — chunks for
142 /// reflow exactly as the plain text it stands in for). The projector maps the
143 /// enclosing `ROXYGEN_MD_LIST` to `\itemize`/`\enumerate` and each item to a
144 /// name-only `\item`.
145 ROXYGEN_MD_LIST_MARKER,
146 // Markdown block-list nodes (resolved `@md` mode only). A `ROXYGEN_MD_LIST`
147 // groups consecutive `ROXYGEN_MD_LIST_ITEM`s; each item owns its marker leaf
148 // and inline content. `#'` markers, the marker→content whitespace, and
149 // inter-line newlines are threaded in as trivia (losslessness), the way the
150 // block Rd macros thread them.
151 ROXYGEN_MD_LIST,
152 ROXYGEN_MD_LIST_ITEM,
153 /// A markdown image ``, emitted **only** under a resolved
154 /// `@md` block mode. Like the other markdown leaves it holds its whole span
155 /// (delimiters included) so the run tiles exactly. The projector maps it to
156 /// `\figure{url}{title}`, wrapping the figure in `\if{html}{…}`/`\if{pdf}{…}`
157 /// per roxygen2's extension-keyed `get_image_format` rule. Appended last to
158 /// keep the earlier variants' `as u16` discriminants stable.
159 ROXYGEN_MD_IMAGE,
160 /// A markdown fenced code block (resolved `@md` mode only): a
161 /// `ROXYGEN_MD_FENCE` opener leaf, the verbatim code lines, and a
162 /// `ROXYGEN_MD_FENCE` closer leaf, with the `#'` markers, marker→content
163 /// whitespace, and inter-line newlines threaded in as trivia (losslessness),
164 /// the way the block Rd macros and markdown lists thread them. The projector
165 /// maps it to roxygen2's `\if{html}{\out{<div…>}} \preformatted{…}
166 /// \if{html}{\out{</div>}}` triple. Appended last to keep the earlier
167 /// variants' `as u16` discriminants stable.
168 ROXYGEN_MD_CODE_BLOCK,
169 /// A fenced-code-block delimiter line (3+ backticks plus an optional info
170 /// string), a leaf inside a `ROXYGEN_MD_CODE_BLOCK`.
171 ROXYGEN_MD_FENCE,
172 /// A raw inline-HTML tag (`<img …>`, `</span>`), emitted **only** under a
173 /// resolved `@md` block mode. Like the other markdown leaves it holds its
174 /// whole span so the run tiles exactly. The projector maps it to roxygen2's
175 /// `\if{html}{\out{<tag>}}` (`mdxml_html_inline`). Appended last to keep the
176 /// earlier variants' `as u16` discriminants stable.
177 ROXYGEN_MD_HTML,
178 /// A markdown **HTML block** (resolved `@md` mode only): a run of `#'` lines
179 /// opened by a CommonMark HTML-block start (condition 6 — a block-level tag at
180 /// line start) and continuing to the next blank line, with the `#'` markers,
181 /// marker→content whitespace, and inter-line newlines threaded in as trivia
182 /// (losslessness), the way the fenced code block threads them. Its line content
183 /// is verbatim `ROXYGEN_TEXT`; the projector reconstructs the block text and
184 /// maps it to roxygen2's `\if{html}{\out{…}}` (`mdxml_html_block`). Appended
185 /// last to keep the earlier variants' `as u16` discriminants stable.
186 ROXYGEN_MD_HTML_BLOCK,
187 /// A raw markdown emphasis-delimiter run leaf (`*`, `**`, `_`, …), emitted
188 /// only under a resolved `@md` block mode. The lexer carves the maximal same-
189 /// char run neutrally; the inline pass (`parser::roxygen::inline`) resolves
190 /// matched runs into `ROXYGEN_MD_EMPH`/`ROXYGEN_MD_STRONG` **nodes** (now node
191 /// kinds, not leaves), whose opener/closer delimiters are themselves
192 /// `ROXYGEN_MD_DELIM` leaves, and leaves an unmatched run as a literal
193 /// `ROXYGEN_MD_DELIM` leaf (projected as plain text). Appended last to keep the
194 /// earlier variants' `as u16` discriminants stable.
195 ROXYGEN_MD_DELIM,
196 /// A GFM **table** (resolved `@md` mode only): a header row, a delimiter row
197 /// (`|---|:--:|`) whose cell count matches the header, and zero or more body
198 /// rows, with the `#'` markers, marker→content whitespace, and inter-line
199 /// newlines threaded in as trivia (losslessness), the way the fenced code block
200 /// and HTML block thread them. Its line content is verbatim `ROXYGEN_TEXT` (and,
201 /// for the delimiter row, whatever inline leaves the lexer carved); the
202 /// projector reconstructs the rows and cells from the node text and maps them to
203 /// roxygen2's `\tabular{<align>}{… \tab … \cr}`. Appended last to keep the
204 /// earlier variants' `as u16` discriminants stable.
205 ROXYGEN_MD_TABLE,
206 /// A markdown ATX **heading** line (resolved `@md` mode only): a single line
207 /// whose content is `#{1,6}` followed by a space/tab or the line end, holding
208 /// the `#'` marker (trivia) and the verbatim `ROXYGEN_TEXT` heading leaf. The
209 /// projector reads the level and title from the node text and hoists it into an
210 /// Rd `\section` (level 1) / `\subsection` (level >= 2) out of the enclosing
211 /// `@description`/`@details`. Appended last to keep the earlier variants'
212 /// `as u16` discriminants stable.
213 ROXYGEN_MD_HEADING,
214 /// A markdown **block quote** (resolved `@md` mode only): a run of consecutive
215 /// `#'` lines each opening with `>` (after up to three spaces), with the `#'`
216 /// markers, marker→content whitespace, and inter-line newlines threaded in as
217 /// trivia (losslessness), the way the HTML block threads them. Its line content
218 /// is verbatim `ROXYGEN_TEXT`. roxygen2 does not support block quotes: it warns
219 /// and renders the node's *flattened plain text* (`escape_comment(xml_text)`),
220 /// which the projector reproduces (strip each `>` marker, flatten the inner
221 /// markdown, concatenate with no separator). Appended last to keep the earlier
222 /// variants' `as u16` discriminants stable.
223 ROXYGEN_MD_BLOCK_QUOTE,
224 /// A markdown **thematic break** (resolved `@md` mode only): a single line whose
225 /// whole content is a CommonMark thematic break (`***`/`---`/`___`, three or more
226 /// of one character with optional spaces between, up to three leading spaces),
227 /// holding the `#'` marker (trivia) and the verbatim break leaf. roxygen2 has no
228 /// thematic-break support: it warns and renders the empty `escape_comment(xml_text)`
229 /// (a thematic break has no text), so the break contributes nothing and the
230 /// surrounding paragraphs coalesce. The projector drops it in `section_body_parts`.
231 /// Appended last to keep the earlier variants' `as u16` discriminants stable.
232 ROXYGEN_MD_THEMATIC_BREAK,
233 /// A markdown **indented code block** resolved under `@md` mode: a run of
234 /// prose lines each indented four or more columns past roxygen2's stripped
235 /// `#'` prefix (a CommonMark indented code block), with interior blank lines,
236 /// holding the `#'` markers (trivia) and the verbatim code leaves. roxygen2
237 /// renders it exactly like a fenced code block — the three-atom
238 /// `\if{html}{\out{<div…>}}` / `\preformatted{…}` / `\if{html}{\out{</div>}}`
239 /// sequence — but with a bare `sourceCode` class and each line's indentation
240 /// stripped (see `serialize_md_indented_code`). Appended last to keep the
241 /// earlier variants' `as u16` discriminants stable.
242 ROXYGEN_MD_INDENTED_CODE,
243}
244
245impl SyntaxKind {
246 /// Number of distinct kinds, sized to the last variant. Used to allocate
247 /// dispatch tables indexed by `kind as usize` (see the linter's single-walk
248 /// rule dispatch). Stays correct as long as `ROXYGEN_MD_INDENTED_CODE` remains
249 /// the last variant.
250 pub const COUNT: usize = SyntaxKind::ROXYGEN_MD_INDENTED_CODE as usize + 1;
251
252 /// A roxygen line's bytes are carried by these leaf tokens, which stand in
253 /// for the single `COMMENT` token a non-roxygen comment line uses.
254 pub fn is_roxygen_token(self) -> bool {
255 matches!(
256 self,
257 SyntaxKind::ROXYGEN_MARKER
258 | SyntaxKind::ROXYGEN_AT
259 | SyntaxKind::ROXYGEN_TAG_NAME
260 | SyntaxKind::ROXYGEN_TAG_ARG
261 | SyntaxKind::ROXYGEN_TEXT
262 | SyntaxKind::ROXYGEN_CODE
263 | SyntaxKind::ROXYGEN_RD_MACRO
264 | SyntaxKind::ROXYGEN_MD_LINK
265 | SyntaxKind::ROXYGEN_MD_CODE
266 | SyntaxKind::ROXYGEN_MD_LIST_MARKER
267 | SyntaxKind::ROXYGEN_MD_IMAGE
268 | SyntaxKind::ROXYGEN_MD_FENCE
269 | SyntaxKind::ROXYGEN_MD_HTML
270 | SyntaxKind::ROXYGEN_MD_DELIM
271 )
272 }
273
274 /// The prose / inline-markup roxygen content elements: plain text plus the
275 /// protected spans (inline code, Rd macro, markdown link/code/list-marker/…)
276 /// and the resolved emphasis/strong **nodes**. The `SyntaxKind`-side
277 /// counterpart of [`crate::parser::lexer::RoxygenRole::Content`]; the single
278 /// list the formatter derives "is this a prose element" from. Includes the
279 /// `ROXYGEN_MD_EMPH`/`ROXYGEN_MD_STRONG` nodes (the inline pass's output) so the
280 /// formatter treats a resolved span as one atomic prose chunk.
281 pub fn is_roxygen_prose_content(self) -> bool {
282 matches!(
283 self,
284 SyntaxKind::ROXYGEN_TEXT
285 | SyntaxKind::ROXYGEN_CODE
286 | SyntaxKind::ROXYGEN_RD_MACRO
287 | SyntaxKind::ROXYGEN_MD_LINK
288 | SyntaxKind::ROXYGEN_MD_EMPH
289 | SyntaxKind::ROXYGEN_MD_STRONG
290 | SyntaxKind::ROXYGEN_MD_CODE
291 | SyntaxKind::ROXYGEN_MD_LIST_MARKER
292 | SyntaxKind::ROXYGEN_MD_IMAGE
293 | SyntaxKind::ROXYGEN_MD_FENCE
294 | SyntaxKind::ROXYGEN_MD_HTML
295 | SyntaxKind::ROXYGEN_MD_DELIM
296 )
297 }
298}
299
300impl From<SyntaxKind> for rowan::SyntaxKind {
301 fn from(kind: SyntaxKind) -> Self {
302 Self(kind as u16)
303 }
304}
305
306#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
307pub enum RLanguage {}
308
309impl Language for RLanguage {
310 type Kind = SyntaxKind;
311
312 fn kind_from_raw(raw: rowan::SyntaxKind) -> Self::Kind {
313 match raw.0 {
314 0 => SyntaxKind::ROOT,
315 1 => SyntaxKind::BINARY_EXPR,
316 2 => SyntaxKind::ASSIGNMENT_EXPR,
317 3 => SyntaxKind::PAREN_EXPR,
318 4 => SyntaxKind::IF_EXPR,
319 5 => SyntaxKind::FOR_EXPR,
320 6 => SyntaxKind::WHILE_EXPR,
321 7 => SyntaxKind::REPEAT_EXPR,
322 8 => SyntaxKind::FUNCTION_EXPR,
323 9 => SyntaxKind::BLOCK_EXPR,
324 10 => SyntaxKind::UNARY_EXPR,
325 11 => SyntaxKind::IDENT,
326 12 => SyntaxKind::INT,
327 13 => SyntaxKind::FLOAT,
328 14 => SyntaxKind::STRING,
329 15 => SyntaxKind::COMMENT,
330 16 => SyntaxKind::TILDE,
331 17 => SyntaxKind::USER_OP,
332 18 => SyntaxKind::LBRACK,
333 19 => SyntaxKind::RBRACK,
334 20 => SyntaxKind::LBRACK2,
335 21 => SyntaxKind::RBRACK2,
336 22 => SyntaxKind::PLUS,
337 23 => SyntaxKind::MINUS,
338 24 => SyntaxKind::STAR,
339 25 => SyntaxKind::SLASH,
340 26 => SyntaxKind::CARET,
341 27 => SyntaxKind::PIPE,
342 28 => SyntaxKind::COLON,
343 29 => SyntaxKind::COLON2,
344 30 => SyntaxKind::COLON3,
345 31 => SyntaxKind::DOLLAR,
346 32 => SyntaxKind::AT,
347 33 => SyntaxKind::SEMICOLON,
348 34 => SyntaxKind::COMMA,
349 35 => SyntaxKind::OR,
350 36 => SyntaxKind::OR2,
351 37 => SyntaxKind::AND,
352 38 => SyntaxKind::AND2,
353 39 => SyntaxKind::EQUAL2,
354 40 => SyntaxKind::NOT_EQUAL,
355 41 => SyntaxKind::BANG,
356 42 => SyntaxKind::LESS_THAN,
357 43 => SyntaxKind::LESS_THAN_OR_EQUAL,
358 44 => SyntaxKind::GREATER_THAN,
359 45 => SyntaxKind::GREATER_THAN_OR_EQUAL,
360 46 => SyntaxKind::LPAREN,
361 47 => SyntaxKind::RPAREN,
362 48 => SyntaxKind::IF_KW,
363 49 => SyntaxKind::ELSE_KW,
364 50 => SyntaxKind::FOR_KW,
365 51 => SyntaxKind::WHILE_KW,
366 52 => SyntaxKind::REPEAT_KW,
367 53 => SyntaxKind::FUNCTION_KW,
368 54 => SyntaxKind::IN_KW,
369 55 => SyntaxKind::LBRACE,
370 56 => SyntaxKind::RBRACE,
371 57 => SyntaxKind::WHITESPACE,
372 58 => SyntaxKind::NEWLINE,
373 59 => SyntaxKind::ASSIGN_LEFT,
374 60 => SyntaxKind::SUPER_ASSIGN,
375 61 => SyntaxKind::ASSIGN_RIGHT,
376 62 => SyntaxKind::SUPER_ASSIGN_RIGHT,
377 63 => SyntaxKind::ASSIGN_EQ,
378 64 => SyntaxKind::CALL_EXPR,
379 65 => SyntaxKind::SUBSET_EXPR,
380 66 => SyntaxKind::SUBSET2_EXPR,
381 67 => SyntaxKind::ARG_LIST,
382 68 => SyntaxKind::ARG,
383 69 => SyntaxKind::ERROR,
384 70 => SyntaxKind::COMPLEX,
385 71 => SyntaxKind::QUESTION,
386 72 => SyntaxKind::WALRUS,
387 73 => SyntaxKind::ROXYGEN_MARKER,
388 74 => SyntaxKind::ROXYGEN_AT,
389 75 => SyntaxKind::ROXYGEN_TAG_NAME,
390 76 => SyntaxKind::ROXYGEN_TAG_ARG,
391 77 => SyntaxKind::ROXYGEN_TEXT,
392 78 => SyntaxKind::ROXYGEN_CODE,
393 79 => SyntaxKind::ROXYGEN_RD_MACRO,
394 80 => SyntaxKind::ROXYGEN_MD_LINK,
395 81 => SyntaxKind::ROXYGEN_BLOCK,
396 82 => SyntaxKind::ROXYGEN_LINE,
397 83 => SyntaxKind::ROXYGEN_TAG,
398 84 => SyntaxKind::ROXYGEN_RD_MACRO_NAME,
399 85 => SyntaxKind::ROXYGEN_RD_MACRO_OPT,
400 86 => SyntaxKind::ROXYGEN_RD_MACRO_DELIM,
401 87 => SyntaxKind::ROXYGEN_RD_MACRO_VERB,
402 88 => SyntaxKind::ROXYGEN_SECTION,
403 89 => SyntaxKind::ROXYGEN_PARAGRAPH,
404 90 => SyntaxKind::ROXYGEN_MD_EMPH,
405 91 => SyntaxKind::ROXYGEN_MD_STRONG,
406 92 => SyntaxKind::ROXYGEN_MD_CODE,
407 93 => SyntaxKind::ROXYGEN_MD_LIST_MARKER,
408 94 => SyntaxKind::ROXYGEN_MD_LIST,
409 95 => SyntaxKind::ROXYGEN_MD_LIST_ITEM,
410 96 => SyntaxKind::ROXYGEN_MD_IMAGE,
411 97 => SyntaxKind::ROXYGEN_MD_CODE_BLOCK,
412 98 => SyntaxKind::ROXYGEN_MD_FENCE,
413 99 => SyntaxKind::ROXYGEN_MD_HTML,
414 100 => SyntaxKind::ROXYGEN_MD_HTML_BLOCK,
415 101 => SyntaxKind::ROXYGEN_MD_DELIM,
416 102 => SyntaxKind::ROXYGEN_MD_TABLE,
417 103 => SyntaxKind::ROXYGEN_MD_HEADING,
418 104 => SyntaxKind::ROXYGEN_MD_BLOCK_QUOTE,
419 105 => SyntaxKind::ROXYGEN_MD_THEMATIC_BREAK,
420 106 => SyntaxKind::ROXYGEN_MD_INDENTED_CODE,
421 _ => SyntaxKind::ERROR,
422 }
423 }
424
425 fn kind_to_raw(kind: Self::Kind) -> rowan::SyntaxKind {
426 kind.into()
427 }
428}
429
430pub type SyntaxNode = rowan::SyntaxNode<RLanguage>;
431pub type SyntaxToken = rowan::SyntaxToken<RLanguage>;
432pub type SyntaxElement = rowan::SyntaxElement<RLanguage>;