1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
//! CST → Rd-tree projector: the primary roxygen2 conformance engine.
//!
//! [`project_to_rd`] walks arity's lossless CST and emits the **parser-owned**
//! Rd section subtrees in roxygen2's canonical S-expression shape --- the same
//! shape the R driver's `block-to-sections` op mints (`tests/oracle/
//! roxygen_oracle.R`). The projector-parity gate (`tests/roxygen_projector.rs`)
//! diffs this against a *pinned* `expected.rdtree` per corpus case, so it runs in
//! plain `cargo test` with no R, and **structural** divergences (a `\describe`
//! the CST never modeled as a block, a markdown list still flat prose) surface as
//! a mismatch. That is the signal that drives parser growth.
//!
//! ## What it projects, and what it deliberately does not
//!
//! It is a **faithful encoding translation**, never a roxygen2 roclet
//! reimplementation (RECAP's first invariant). It projects what the parser
//! models: the title/description derived from the intro paragraphs, and the
//! body of the prose section tags (`@details`, `@return` → `\value`,
//! `@seealso`, `@source`, `@format`, `@section`, …). It excludes everything
//! roxygen2 *generates* rather than parses --- `\name`/`\alias` (the object),
//! `\usage` (the formals), and the `\arguments` wrapper that groups `@param`
//! (the `block-to-sections` op drops the same set, so the two stay aligned).
//!
//! ## Current reach
//!
//! A section body is projected as a *sequence* of inline atoms: prose runs
//! coalesce into whitespace-normalized `(TEXT …)`, and inline Rd macros
//! (`\code`/`\link`/`\emph`/`\url`/…, including nesting, a dropped `[pkg]`
//! option, and verbatim `(VERB …)` bodies) surface as nested subtrees from the
//! CST's `ROXYGEN_RD_MACRO` nodes. A section the CST does not yet model
//! structurally --- a multi-line `\describe`/`\itemize`/`\tabular`, or markdown
//! that roxygen2 translates into nodes under a resolved `@md` mode (`*x*` →
//! `\emph{x}`) --- still projects as flat text and therefore **diverges**. Those
//! divergences are the backlog: each is closed by teaching the *parser* the
//! structure, then the projector grows a faithful arm for the new nodes. Never
//! patch the projector to make a case pass.
use Cow;
use NodeOrToken;
use crate;
use crateparse;
use crate;
use crateentities;
use crate;
use *;
use *;
use *;
use *;
use *;
use *;
use *;
use *;
use *;
use *;
use *;
/// Project `text` to the parser-owned Rd section subtrees, one canonical
/// S-expression per line, sorted --- byte-identical to the R driver's
/// `block-to-sections` output for the cases the projector models.
///
/// Sections are sorted (not in document order) because roxygen2's Rd emission
/// order is not the document order, and the projector does not replicate it; the
/// gate compares a *set* of section subtrees. Sections from every
/// `ROXYGEN_BLOCK` in `text` are merged into one sorted set.
///
/// Blocks that resolve to the **same topic** (`@name`/`@rdname` value) are one
/// Rd file in roxygen2, so their sections *merge* rather than sit side by side
/// (`RoxyTopic$add`): the merge combines each section type's value vector, and
/// the per-type `format` method then decides how those values render — `\title`
/// keeps only the first (`format_first`), while `\description`/`\details`/… join
/// with a paragraph break (`format_collapse`). See [`project_merged_topic`].
/// Blocks with no explicit topic name form their own singleton topic (arity does
/// not statically resolve an object's default topic, and distinct objects get
/// distinct files anyway), so the common single-block path is untouched.
/// End of roxygen2's comment-scan range. The regions `comments()` (tokenize.R)
/// builds tile `[byte 1, end of last top-level expression]` contiguously, so a
/// `#'` line starting past that end — after the last expression, or anywhere in
/// a file with no expression at all — is never tokenized. A block *inside* an
/// expression (a `#'` line in a function body) starts within the enclosing
/// expression's region, joins that region's block, and still renders. Bare
/// atoms (`NULL`, a lone literal) are tokens at top level, so both nodes and
/// non-structural tokens count; trivia, comments, semicolons, and the roxygen
/// blocks themselves do not.
/// The topic key a block documents: the trimmed value of its `@name` (or, absent
/// that, `@rdname`) tag. Blocks sharing a key render into one Rd file and so merge
/// (`project_to_rd`). Returns `None` when the block names no topic — arity does
/// not statically derive an object's default topic, so such a block never merges.
///
/// The value comes from [`RoxygenTag::value_text`], not the first `ROXYGEN_TEXT`
/// leaf: under `@md` a name containing `_` or `*` lexes as several leaves around
/// an unresolved markdown delimiter, and reading one leaf would truncate
/// `missing_arg` to `missing` — merging two distinct topics. This is the narrower
/// twin of [`crate::project::roxygen::topic_key`], which also derives an object's
/// default topic; keep the two in step.
/// One inline element of a section body: a run of prose text (coalesced and
/// whitespace-normalized at serialization) or an Rd macro node (projected as a
/// nested subtree). Modeling the body as a *sequence* — rather than one flat
/// string — is what lets inline `\code`/`\link`/… surface as structure.