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
//! AST node types for YAML.
//!
//! # CST invariants
//!
//! yaml-edit is a lossless editor: every mutation produces text that
//! re-parses into the same tree shape (see [`crate::debug::roundtrip_ok`]
//! and [`crate::debug::validate_tree`]). To keep that property, all
//! mutation helpers in this module must uphold the following invariants
//! on the concrete syntax tree.
//!
//! ## Where whitespace lives
//!
//! For a block-style mapping entry `key:\n - a`, the NEWLINE after
//! `key:` and the INDENT before the SEQUENCE both live inside the parent
//! VALUE, *not* inside the child SEQUENCE. The SEQUENCE_ENTRYs
//! themselves carry only the DASH, WHITESPACE, and content:
//!
//! ```text
//! MAPPING_ENTRY
//! KEY ...
//! COLON ":"
//! VALUE
//! NEWLINE "\n"
//! INDENT " "
//! SEQUENCE
//! SEQUENCE_ENTRY
//! DASH "-"
//! WHITESPACE " "
//! SCALAR ...
//! ```
//!
//! Consequence: when constructing a fresh block SEQUENCE or MAPPING to
//! splice under a key, do **not** prepend its own leading INDENT for the
//! first entry - the parent VALUE already carries one. Duplicating it
//! renders as doubled indentation and violates the no-stacked-INDENT
//! check in [`crate::debug::validate_tree`].
//!
//! For flow-style values (`{a: 1}`, `[1, 2]`) all separators live
//! *inside* the flow SEQUENCE / MAPPING: the parent VALUE has no direct
//! NEWLINE child. `value_is_block` in `mapping.rs` distinguishes the two
//! by looking for a NEWLINE token as a direct child of VALUE.
//!
//! ## Entry termination
//!
//! Block-style MAPPING_ENTRY and SEQUENCE_ENTRY nodes are terminated by
//! a NEWLINE. That terminator normally lives as the entry's own last
//! token, but the parser sometimes lifts it out when a trailing comment
//! separates it from the next sibling:
//!
//! ```text
//! # SEQUENCE_ENTRY ends with STRING; the NEWLINE lives at the SEQUENCE
//! # level after the following COMMENT.
//! SEQUENCE
//! SEQUENCE_ENTRY
//! DASH "-"
//! SCALAR ("a")
//! WHITESPACE " "
//! COMMENT "# note"
//! NEWLINE "\n"
//! SEQUENCE_ENTRY ...
//! ```
//!
//! Two carve-outs:
//! - The **last** entry of a block collection may lack a terminator
//! (unterminated sources like `a: 1\nb: 2` are legitimate YAML and
//! must roundtrip).
//! - An entry ending in an **implicit-null** scalar (see below) has a
//! zero-width `SCALAR { NULL "" }` as its last leaf, not a NEWLINE.
//! That's a well-formed terminated entry; the NEWLINE lives elsewhere
//! in the tree (inside the KEY subtree for `? key\n`, inside the VALUE
//! between COLON and SCALAR for `key:\n`).
//!
//! When inserting a new sibling entry after an existing one, mutation
//! helpers must first ensure the predecessor is terminated
//! (`ensure_trailing_newline` in `mapping.rs` / `sequence.rs`).
//!
//! ## Implicit-null values
//!
//! Every `MAPPING_ENTRY` and `SEQUENCE_ENTRY` holds a value node, even
//! when the source omits it. The parser emits a zero-width
//! `SCALAR { NULL "" }` at every "missing value" position:
//!
//! ```text
//! Source form CST shape
//! ------------------------------- ----------------------------------------
//! key:\n (block mapping) VALUE { NEWLINE, SCALAR { NULL "" } }
//! key: (block mapping, EOF) VALUE { SCALAR { NULL "" } }
//! {key} (flow mapping) MAPPING_ENTRY { KEY { SCALAR "key" },
//! VALUE { SCALAR { NULL "" } } }
//! {key:} (flow mapping) MAPPING_ENTRY { KEY { SCALAR "key" },
//! COLON,
//! VALUE { SCALAR { NULL "" } } }
//! {,} (flow mapping) MAPPING_ENTRY { KEY { SCALAR { NULL "" } },
//! VALUE { SCALAR { NULL "" } },
//! COMMA }
//! {: v} (flow mapping) KEY { SCALAR { NULL "" } }, COLON, VALUE ...
//! ? key\n (explicit key) MAPPING_ENTRY { QUESTION, KEY ..., NEWLINE,
//! VALUE { SCALAR { NULL "" } } }
//! - \n (block sequence) SEQUENCE_ENTRY { DASH, WHITESPACE,
//! SCALAR { NULL "" }, NEWLINE }
//! [a, , c] (flow sequence) SEQUENCE_ENTRY { SCALAR { NULL "" }, COMMA, ... }
//! ```
//!
//! The invariant: **every KEY, every VALUE, every SEQUENCE_ENTRY holds
//! exactly one scalar or collection node.** Mappings wrap the value in a
//! `VALUE` node; sequences hold the scalar directly as a
//! `SEQUENCE_ENTRY` child (matching how each shape wraps its non-null
//! values -- sequences never had a wrapper, mappings always did).
//!
//! The zero-width `NULL ""` token renders as nothing, so round-trips are
//! lossless. This is distinct from a programmatically-written null,
//! which uses `SCALAR { NULL "null" }` (textual, three characters) so
//! the value is visible in the output.
//!
//! Consequences for mutation helpers:
//!
//! - Iterating `SEQUENCE_ENTRY` children with `matches!(kind, SCALAR |
//! MAPPING | SEQUENCE | ALIAS | TAGGED_NODE)` finds the value in
//! every entry, including implicit-null ones. `len` / `get` /
//! `set` / `remove` therefore share the same set of indexes.
//! - `Scalar::is_null()` returns `true` for the zero-width form.
//! - `set(i, real_value)` on an implicit-null entry may need to insert
//! a separating WHITESPACE that the original didn't have (the value
//! was zero-width so no separator was needed). See `Sequence::set`
//! for the `after_dash` pattern used in block sequences.
//!
//! ## No stacked INDENTs, no double trailing NEWLINEs
//!
//! Two adjacent INDENT tokens as direct children of the same node
//! concatenate at render time and produce visibly-wrong indentation
//! (this was the shape of issue #38). Two adjacent NEWLINE tokens at the
//! tail of a MAPPING_ENTRY or SEQUENCE_ENTRY render as a stray blank
//! line (issue #18).
//!
//! Both are checked by [`crate::debug::validate_tree`]; mutation helpers
//! must not produce either. Note the check for double NEWLINE is scoped
//! to *entries*, not their parent MAPPING / SEQUENCE containers: bare
//! NEWLINEs between entries (blank-line separators) are valid formatting
//! and appear at the container level.
//!
//! ## Flow separators live on the previous entry
//!
//! Inside a flow mapping or sequence, the COMMA (and any following
//! WHITESPACE / NEWLINE / INDENT) between two entries is stored as
//! trailing siblings of the *previous* entry's KEY / VALUE / SCALAR,
//! not inside the entry it precedes. Inserters that append a new entry
//! must add a `, ` suffix to the entry they follow, not a `,` prefix on
//! themselves.
//!
//! ## Prefer targeted splices over whole-node rebuilds
//!
//! Reconstructing an entire MAPPING or SEQUENCE from a builder discards
//! anchors, tags, comments, quoting styles, and any tokens the fix
//! didn't know about. Mutation helpers should locate the specific
//! `NodeOrToken` range they need to change and use `splice_children`,
//! preserving the surrounding tokens.
use crateSyntaxKind;
/// YAML language type for rowan.
pub type SyntaxNode = SyntaxNode;
pub type SyntaxToken = SyntaxToken;
/// Build a standalone `SyntaxToken` of `kind` with `text`, ready to
/// splice into a parent's child list via `splice_children`.
pub
/// The first direct child node of `parent` with the given `kind`, if any.
pub
/// The `KEY` child of a `MAPPING_ENTRY`.
pub
/// The `VALUE` child of a `MAPPING_ENTRY` or `SEQUENCE_ENTRY`.
pub
/// A macro to create AST node wrappers.
pub use ast_node;
// Node modules
// Re-exports
pub use Alias;
pub use Comment;
pub use Directive;
pub use Document;
pub use ;
pub use ;
pub use ;
pub use Sequence;
pub use TaggedNode;