fmt_lang/rules.rs
1//! The rule model: formatting described as plain data, keyed by kind *names*.
2//!
3//! A [`Rules`] value is what a sketch's `[tooling.format]` section describes:
4//! per-node layout ([`NodeRule`]), per-token spacing ([`TokenRule`]), and a few
5//! file-wide settings. Kinds are named by text (`"object"`, `","`, `"ERROR"`),
6//! the way a sketch names them, and [`Rules::compile`] resolves the names
7//! against a language once, producing the [`Style`](crate::Style) the
8//! formatter runs on.
9//!
10//! Anything the rules do not mention keeps its original whitespace.
11
12use alloc::string::String;
13use alloc::vec::Vec;
14
15/// What goes between two neighbouring pieces of output.
16///
17/// The five values form a small lattice. When several rules speak about the
18/// same gap, the formatter takes the most generous answer: a gap gets a space if
19/// any rule asks for one, may break if any rule allows it, and always breaks if
20/// any rule demands it. So [`SoftLine`](Space::SoftLine) combined with
21/// [`Single`](Space::Single) is [`Line`](Space::Line), and anything combined
22/// with [`Hard`](Space::Hard) is `Hard`.
23///
24/// | Value | When the enclosing group fits | When it breaks |
25/// |---|---|---|
26/// | `None` | nothing | nothing |
27/// | `Single` | one space | one space |
28/// | `SoftLine` | nothing | a line break |
29/// | `Line` | one space | a line break |
30/// | `Hard` | a line break | a line break |
31///
32/// A gap that resolves to `Hard` keeps up to the configured number of blank
33/// lines the source had there ([`Rules::max_blank_lines`],
34/// [`NodeRule::blank_lines`]).
35///
36/// # Examples
37///
38/// ```
39/// use fmt_lang::{Rules, Space, TokenRule};
40///
41/// // No space before a comma, one after it.
42/// let rules = Rules::new().token(TokenRule::new(",").before(Space::None).after(Space::Single));
43/// # let _ = rules;
44/// ```
45#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
46#[non_exhaustive]
47pub enum Space {
48 /// Nothing: the neighbours touch.
49 None,
50 /// Exactly one space.
51 Single,
52 /// Nothing, or a line break when the enclosing group does not fit.
53 SoftLine,
54 /// One space, or a line break when the enclosing group does not fit.
55 Line,
56 /// Always a line break.
57 Hard,
58}
59
60/// How a node indents its contents.
61///
62/// # Examples
63///
64/// ```
65/// use fmt_lang::{Indent, NodeRule, Space};
66///
67/// // A block: `{`, an indented body, then `}` back at the block's level.
68/// let block = NodeRule::new("block").delimiters("{", "}", Space::Hard).indent(Indent::Block);
69/// # let _ = block;
70/// ```
71#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
72#[non_exhaustive]
73pub enum Indent {
74 /// No indentation of its own.
75 #[default]
76 None,
77 /// Indent the body between the node's delimiters ([`NodeRule::delimiters`]):
78 /// every line break after the opening delimiter and before the closing one.
79 /// The closing delimiter itself returns to the node's level. Without
80 /// delimiters present in the tree, nothing is indented.
81 Block,
82 /// Indent every line break inside the node (a hanging indent, as for a long
83 /// binary expression that wraps).
84 Hanging,
85}
86
87/// What to do with a separator after the last element of a delimited list.
88///
89/// Policies other than [`Preserve`](Trailing::Preserve) change the token
90/// stream (they add or remove one separator token), so they apply only to
91/// lists that are well formed: delimiters present at both ends, elements and
92/// separators strictly alternating, and no error node among the elements.
93/// Anything else is left as written.
94///
95/// # Examples
96///
97/// ```
98/// use fmt_lang::{NodeRule, Space, Trailing};
99///
100/// let array = NodeRule::new("array")
101/// .delimiters("[", "]", Space::SoftLine)
102/// .separator(",", Space::Line, Trailing::Never);
103/// # let _ = array;
104/// ```
105#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
106#[non_exhaustive]
107pub enum Trailing {
108 /// Keep a trailing separator if there is one; never add one.
109 #[default]
110 Preserve,
111 /// Ensure a non-empty list ends with a separator.
112 Always,
113 /// Remove a trailing separator.
114 Never,
115}
116
117/// Spacing rules for one token kind (or for every token), on either side.
118///
119/// A token rule lives either at the top level of [`Rules`] (a default that
120/// applies wherever the token appears) or inside a [`NodeRule`] (applies only
121/// to tokens that are direct children of that node, and wins over the
122/// top-level default). On each side the most specific rule wins: a node's rule
123/// for this exact kind, then the node's rule for any token, then the top-level
124/// rule for this kind, then the top-level rule for any token.
125///
126/// # Examples
127///
128/// ```
129/// use fmt_lang::{Space, TokenRule};
130///
131/// let plus = TokenRule::new("+").around(Space::Single);
132/// let open_paren = TokenRule::new("(").after(Space::None);
133/// // Every direct child token of a node (used inside a `NodeRule`).
134/// let tight = TokenRule::any().before(Space::None).after(Space::None);
135/// # let _ = (plus, open_paren, tight);
136/// ```
137#[derive(Clone, Debug, PartialEq, Eq, Hash)]
138pub struct TokenRule {
139 pub(crate) kind: Option<String>,
140 pub(crate) before: Option<Space>,
141 pub(crate) after: Option<Space>,
142 pub(crate) optional: bool,
143}
144
145impl TokenRule {
146 /// A rule for tokens of the kind named `kind`, with no spacing set yet.
147 ///
148 /// # Examples
149 ///
150 /// ```
151 /// use fmt_lang::{Space, TokenRule};
152 ///
153 /// let semi = TokenRule::new(";").before(Space::None);
154 /// # let _ = semi;
155 /// ```
156 #[must_use]
157 pub fn new(kind: impl Into<String>) -> Self {
158 Self {
159 kind: Some(kind.into()),
160 before: None,
161 after: None,
162 optional: false,
163 }
164 }
165
166 /// A rule for every token, whatever its kind. Inside a [`NodeRule`] it
167 /// covers every direct child token of that node.
168 ///
169 /// # Examples
170 ///
171 /// ```
172 /// use fmt_lang::{NodeRule, Space, TokenRule};
173 ///
174 /// // A prefix operator hugs its operand: `-x`, not `- x`.
175 /// let prefix = NodeRule::new("prefix").token(TokenRule::any().before(Space::None).after(Space::None));
176 /// # let _ = prefix;
177 /// ```
178 #[must_use]
179 pub fn any() -> Self {
180 Self {
181 kind: None,
182 before: None,
183 after: None,
184 optional: false,
185 }
186 }
187
188 /// Spacing in the gap before the token.
189 ///
190 /// # Examples
191 ///
192 /// ```
193 /// use fmt_lang::{Space, TokenRule};
194 ///
195 /// let close = TokenRule::new(")").before(Space::None);
196 /// # let _ = close;
197 /// ```
198 #[must_use]
199 pub fn before(mut self, space: Space) -> Self {
200 self.before = Some(space);
201 self
202 }
203
204 /// Spacing in the gap after the token.
205 ///
206 /// # Examples
207 ///
208 /// ```
209 /// use fmt_lang::{Space, TokenRule};
210 ///
211 /// let comma = TokenRule::new(",").after(Space::Single);
212 /// # let _ = comma;
213 /// ```
214 #[must_use]
215 pub fn after(mut self, space: Space) -> Self {
216 self.after = Some(space);
217 self
218 }
219
220 /// The same spacing on both sides.
221 ///
222 /// # Examples
223 ///
224 /// ```
225 /// use fmt_lang::{Space, TokenRule};
226 ///
227 /// let assign = TokenRule::new("=").around(Space::Single);
228 /// # let _ = assign;
229 /// ```
230 #[must_use]
231 pub fn around(self, space: Space) -> Self {
232 self.before(space).after(space)
233 }
234
235 /// Marks the rule optional: if the language has no kind by this name,
236 /// [`Rules::compile`] drops the rule instead of reporting
237 /// [`RuleError::UnknownKind`](crate::RuleError::UnknownKind). Meant for
238 /// presets shared across languages, such as [`Rules::conventional`].
239 ///
240 /// # Examples
241 ///
242 /// ```
243 /// use fmt_lang::{Rules, Space, TokenRule};
244 ///
245 /// let rules = Rules::new().token(TokenRule::new("=>").around(Space::Single).optional());
246 /// // The language below has no `=>`; the rule is dropped, not an error.
247 /// let style = rules.compile(|name| (name == "+").then_some(1u8))?;
248 /// # let _ = style;
249 /// # Ok::<(), fmt_lang::RuleError>(())
250 /// ```
251 #[must_use]
252 pub fn optional(mut self) -> Self {
253 self.optional = true;
254 self
255 }
256}
257
258/// A list delimiter pair and the spacing just inside it.
259#[derive(Clone, Debug, PartialEq, Eq, Hash)]
260pub(crate) struct DelimitersRule {
261 pub(crate) open: String,
262 pub(crate) close: String,
263 pub(crate) inner: Space,
264}
265
266/// A list separator and its trailing policy.
267#[derive(Clone, Debug, PartialEq, Eq, Hash)]
268pub(crate) struct SeparatorRule {
269 pub(crate) kind: String,
270 pub(crate) text: Option<String>,
271 pub(crate) before: Space,
272 pub(crate) after: Space,
273 pub(crate) trailing: Trailing,
274}
275
276/// Layout rules for one node kind.
277///
278/// A node rule can make the node a *group* (laid out on one line if it fits,
279/// broken at its line opportunities otherwise), *indent* its contents, ask for
280/// spacing *before* and *after* the node, name the *delimiters* and
281/// *separator* of a list, cap the *blank lines* kept inside it, and carry
282/// *token rules* for its direct child tokens.
283///
284/// # Examples
285///
286/// ```
287/// use fmt_lang::{Indent, NodeRule, Space, Trailing};
288///
289/// // A JSON object: `{ "a": 1, "b": 2 }` when it fits, one member per line
290/// // (indented) when it does not, and no trailing comma.
291/// let object = NodeRule::new("object")
292/// .group()
293/// .indent(Indent::Block)
294/// .delimiters("{", "}", Space::Line)
295/// .separator(",", Space::Line, Trailing::Never);
296/// # let _ = object;
297/// ```
298#[derive(Clone, Debug, PartialEq, Eq, Hash)]
299pub struct NodeRule {
300 pub(crate) kind: String,
301 pub(crate) group: bool,
302 pub(crate) indent: Indent,
303 pub(crate) before: Option<Space>,
304 pub(crate) after: Option<Space>,
305 pub(crate) delimiters: Option<DelimitersRule>,
306 pub(crate) empty: Space,
307 pub(crate) separator: Option<SeparatorRule>,
308 pub(crate) blank_lines: Option<u8>,
309 pub(crate) tokens: Vec<TokenRule>,
310 pub(crate) optional: bool,
311}
312
313impl NodeRule {
314 /// A rule for nodes of the kind named `kind`, with nothing set yet.
315 ///
316 /// # Examples
317 ///
318 /// ```
319 /// use fmt_lang::NodeRule;
320 ///
321 /// let stmt = NodeRule::new("stmt");
322 /// # let _ = stmt;
323 /// ```
324 #[must_use]
325 pub fn new(kind: impl Into<String>) -> Self {
326 Self {
327 kind: kind.into(),
328 group: false,
329 indent: Indent::None,
330 before: None,
331 after: None,
332 delimiters: None,
333 empty: Space::None,
334 separator: None,
335 blank_lines: None,
336 tokens: Vec::new(),
337 optional: false,
338 }
339 }
340
341 /// Makes the node a group: its [`Line`](Space::Line) and
342 /// [`SoftLine`](Space::SoftLine) gaps all stay flat if the whole node fits
343 /// in the remaining width, and all break otherwise.
344 ///
345 /// # Examples
346 ///
347 /// ```
348 /// use fmt_lang::NodeRule;
349 ///
350 /// let call_args = NodeRule::new("args").group();
351 /// # let _ = call_args;
352 /// ```
353 #[must_use]
354 pub fn group(mut self) -> Self {
355 self.group = true;
356 self
357 }
358
359 /// Sets how the node indents its contents (by [`Rules::indent`] columns).
360 ///
361 /// # Examples
362 ///
363 /// ```
364 /// use fmt_lang::{Indent, NodeRule};
365 ///
366 /// let binary = NodeRule::new("binary").indent(Indent::Hanging);
367 /// # let _ = binary;
368 /// ```
369 #[must_use]
370 pub fn indent(mut self, indent: Indent) -> Self {
371 self.indent = indent;
372 self
373 }
374
375 /// Spacing in the gap before the node (before its first token).
376 ///
377 /// # Examples
378 ///
379 /// ```
380 /// use fmt_lang::{NodeRule, Space};
381 ///
382 /// // Every statement starts a new line.
383 /// let stmt = NodeRule::new("stmt").before(Space::Hard);
384 /// # let _ = stmt;
385 /// ```
386 #[must_use]
387 pub fn before(mut self, space: Space) -> Self {
388 self.before = Some(space);
389 self
390 }
391
392 /// Spacing in the gap after the node (after its last token).
393 ///
394 /// # Examples
395 ///
396 /// ```
397 /// use fmt_lang::{NodeRule, Space};
398 ///
399 /// let stmt = NodeRule::new("stmt").after(Space::Hard);
400 /// # let _ = stmt;
401 /// ```
402 #[must_use]
403 pub fn after(mut self, space: Space) -> Self {
404 self.after = Some(space);
405 self
406 }
407
408 /// Names the node's delimiter tokens and the spacing just inside them (after
409 /// `open` and before `close`). An empty body (`open` immediately followed by
410 /// `close`) gets [`Space::None`] unless [`empty`](NodeRule::empty) says
411 /// otherwise.
412 ///
413 /// A delimiter is recognised only as the node's first (for `open`) or last
414 /// (for `close`) significant direct child, so the same kinds may appear
415 /// elsewhere inside the node without confusion.
416 ///
417 /// # Examples
418 ///
419 /// ```
420 /// use fmt_lang::{NodeRule, Space};
421 ///
422 /// // `[1, 2]` flat, or one element per line when broken.
423 /// let array = NodeRule::new("array").group().delimiters("[", "]", Space::SoftLine);
424 /// # let _ = array;
425 /// ```
426 #[must_use]
427 pub fn delimiters(
428 mut self,
429 open: impl Into<String>,
430 close: impl Into<String>,
431 inner: Space,
432 ) -> Self {
433 self.delimiters = Some(DelimitersRule {
434 open: open.into(),
435 close: close.into(),
436 inner,
437 });
438 self
439 }
440
441 /// Spacing between the delimiters when the body is empty. Has no effect
442 /// unless [`delimiters`](NodeRule::delimiters) is set (before or after this
443 /// call).
444 ///
445 /// # Examples
446 ///
447 /// ```
448 /// use fmt_lang::{NodeRule, Space};
449 ///
450 /// // `{ }` rather than `{}` for an empty block.
451 /// let block = NodeRule::new("block").delimiters("{", "}", Space::Hard).empty(Space::Single);
452 /// # let _ = block;
453 /// ```
454 #[must_use]
455 pub fn empty(mut self, space: Space) -> Self {
456 self.empty = space;
457 self
458 }
459
460 /// Names the list separator among the node's direct child tokens, the
461 /// spacing after it (before it is [`Space::None`]), and what to do with a
462 /// trailing one.
463 ///
464 /// # Examples
465 ///
466 /// ```
467 /// use fmt_lang::{NodeRule, Space, Trailing};
468 ///
469 /// let args = NodeRule::new("args").separator(",", Space::Line, Trailing::Preserve);
470 /// # let _ = args;
471 /// ```
472 #[must_use]
473 pub fn separator(mut self, kind: impl Into<String>, after: Space, trailing: Trailing) -> Self {
474 self.separator = Some(SeparatorRule {
475 kind: kind.into(),
476 text: None,
477 before: Space::None,
478 after,
479 trailing,
480 });
481 self
482 }
483
484 /// The text written when [`Trailing::Always`] adds a separator. Defaults to
485 /// the separator's kind name, which is its text in languages (such as
486 /// those forged by lang-forge) that name symbol tokens by their text. Has
487 /// no effect without a [`separator`](NodeRule::separator).
488 ///
489 /// # Examples
490 ///
491 /// ```
492 /// use fmt_lang::{NodeRule, Space, Trailing};
493 ///
494 /// let list = NodeRule::new("list")
495 /// .delimiters("(", ")", Space::SoftLine)
496 /// .separator("COMMA", Space::Line, Trailing::Always)
497 /// .separator_text(",");
498 /// # let _ = list;
499 /// ```
500 #[must_use]
501 pub fn separator_text(mut self, text: impl Into<String>) -> Self {
502 if let Some(sep) = &mut self.separator {
503 sep.text = Some(text.into());
504 }
505 self
506 }
507
508 /// Caps the blank lines kept between lines inside this node (overrides
509 /// [`Rules::max_blank_lines`] here).
510 ///
511 /// # Examples
512 ///
513 /// ```
514 /// use fmt_lang::NodeRule;
515 ///
516 /// // No blank lines inside an argument list.
517 /// let args = NodeRule::new("args").blank_lines(0);
518 /// # let _ = args;
519 /// ```
520 #[must_use]
521 pub fn blank_lines(mut self, max: u8) -> Self {
522 self.blank_lines = Some(max);
523 self
524 }
525
526 /// Adds a token rule for the node's direct child tokens. It wins over a
527 /// top-level [`Rules::token`] rule for the same side.
528 ///
529 /// # Examples
530 ///
531 /// ```
532 /// use fmt_lang::{NodeRule, Space, TokenRule};
533 ///
534 /// let member = NodeRule::new("member").token(TokenRule::new(":").before(Space::None).after(Space::Single));
535 /// # let _ = member;
536 /// ```
537 #[must_use]
538 pub fn token(mut self, rule: TokenRule) -> Self {
539 self.tokens.push(rule);
540 self
541 }
542
543 /// Marks the rule optional: dropped by [`Rules::compile`] if the language
544 /// lacks any kind it names, instead of an error.
545 ///
546 /// # Examples
547 ///
548 /// ```
549 /// use fmt_lang::{NodeRule, Rules, Space};
550 ///
551 /// let rules = Rules::new().node(NodeRule::new("block").before(Space::Hard).optional());
552 /// let style = rules.compile(|_| None::<u8>)?; // no `block` here: dropped
553 /// # let _ = style;
554 /// # Ok::<(), fmt_lang::RuleError>(())
555 /// ```
556 #[must_use]
557 pub fn optional(mut self) -> Self {
558 self.optional = true;
559 self
560 }
561}
562
563/// A language's formatting rules, as data.
564///
565/// Built with chained calls, then [`compile`](Rules::compile)d against the
566/// language's kinds. An empty `Rules` formats nothing: every gap keeps its
567/// original whitespace (trailing spaces at line ends and blank space at the
568/// start and end of the file are still trimmed, and a final newline is added).
569///
570/// # Examples
571///
572/// ```
573/// use fmt_lang::{Indent, NodeRule, Rules, Space, TokenRule, Trailing};
574///
575/// let rules = Rules::new()
576/// .indent(2)
577/// .verbatim("ERROR")
578/// .token(TokenRule::new(":").before(Space::None).after(Space::Single))
579/// .node(
580/// NodeRule::new("array")
581/// .group()
582/// .indent(Indent::Block)
583/// .delimiters("[", "]", Space::SoftLine)
584/// .separator(",", Space::Line, Trailing::Never),
585/// );
586/// # let _ = rules;
587/// ```
588#[derive(Clone, Debug, PartialEq, Eq, Hash)]
589pub struct Rules {
590 pub(crate) indent: u8,
591 pub(crate) max_indent: u16,
592 pub(crate) max_blank_lines: u8,
593 pub(crate) final_newline: bool,
594 pub(crate) verbatim: Vec<String>,
595 pub(crate) tokens: Vec<TokenRule>,
596 pub(crate) nodes: Vec<NodeRule>,
597}
598
599/// The widest indentation step [`Rules::indent`] accepts.
600pub const MAX_INDENT_STEP: u8 = 16;
601
602impl Default for Rules {
603 fn default() -> Self {
604 Self::new()
605 }
606}
607
608impl Rules {
609 /// No rules: indentation step 4, at most one blank line kept, indentation
610 /// capped at 120 columns, a final newline.
611 ///
612 /// # Examples
613 ///
614 /// ```
615 /// use fmt_lang::Rules;
616 ///
617 /// let rules = Rules::new();
618 /// assert_eq!(rules, Rules::default());
619 /// ```
620 #[must_use]
621 pub fn new() -> Self {
622 Self {
623 indent: 4,
624 max_indent: 120,
625 max_blank_lines: 1,
626 final_newline: true,
627 verbatim: Vec::new(),
628 tokens: Vec::new(),
629 nodes: Vec::new(),
630 }
631 }
632
633 /// Conventional token spacing shared by most C-family languages, every
634 /// rule [`optional`](TokenRule::optional) so the preset compiles against
635 /// any language:
636 ///
637 /// - nothing before `,` `;` `)` `]`, one space after `,` and `;`;
638 /// - nothing after `(` `[`;
639 /// - one space around `=` `==` `!=` `<=` `>=` `+=` `-=` `*=` `/=` `%=`
640 /// `&&` `||` `=>` `->`.
641 ///
642 /// Operators that are also prefix operators in common languages (`-`,
643 /// `+`, `*`, `&`, `!`) and the angle brackets (`<`, `>`, generics in many
644 /// languages) are left out on purpose: spacing them needs node context,
645 /// which a [`NodeRule`] supplies.
646 ///
647 /// # Examples
648 ///
649 /// ```
650 /// use fmt_lang::{NodeRule, Rules, Space};
651 ///
652 /// let rules = Rules::conventional().node(NodeRule::new("stmt").before(Space::Hard));
653 /// # let _ = rules;
654 /// ```
655 #[must_use]
656 pub fn conventional() -> Self {
657 let mut rules = Self::new();
658 for kind in [",", ";"] {
659 rules = rules.token(
660 TokenRule::new(kind)
661 .before(Space::None)
662 .after(Space::Single)
663 .optional(),
664 );
665 }
666 for kind in [")", "]"] {
667 rules = rules.token(TokenRule::new(kind).before(Space::None).optional());
668 }
669 for kind in ["(", "["] {
670 rules = rules.token(TokenRule::new(kind).after(Space::None).optional());
671 }
672 for kind in [
673 "=", "==", "!=", "<=", ">=", "+=", "-=", "*=", "/=", "%=", "&&", "||", "=>", "->",
674 ] {
675 rules = rules.token(TokenRule::new(kind).around(Space::Single).optional());
676 }
677 rules
678 }
679
680 /// Columns per indentation level (default 4, at most
681 /// [`MAX_INDENT_STEP`]).
682 ///
683 /// # Examples
684 ///
685 /// ```
686 /// use fmt_lang::Rules;
687 ///
688 /// let rules = Rules::new().indent(2);
689 /// # let _ = rules;
690 /// ```
691 #[must_use]
692 pub fn indent(mut self, columns: u8) -> Self {
693 self.indent = columns;
694 self
695 }
696
697 /// The deepest indentation, in columns, the formatter will produce
698 /// (default 120). Nesting past it stops adding indentation. This is the
699 /// output budget against hostile input: without it, a file nested a
700 /// million levels deep would format to terabytes of spaces.
701 ///
702 /// # Examples
703 ///
704 /// ```
705 /// use fmt_lang::Rules;
706 ///
707 /// let rules = Rules::new().max_indent(60);
708 /// # let _ = rules;
709 /// ```
710 #[must_use]
711 pub fn max_indent(mut self, columns: u16) -> Self {
712 self.max_indent = columns;
713 self
714 }
715
716 /// The most blank lines kept wherever a [`Space::Hard`] gap had blank
717 /// lines in the source (default 1). Gaps that keep their original
718 /// whitespace are not capped.
719 ///
720 /// # Examples
721 ///
722 /// ```
723 /// use fmt_lang::Rules;
724 ///
725 /// let rules = Rules::new().max_blank_lines(2);
726 /// # let _ = rules;
727 /// ```
728 #[must_use]
729 pub fn max_blank_lines(mut self, max: u8) -> Self {
730 self.max_blank_lines = max;
731 self
732 }
733
734 /// Whether non-empty output ends with a line break (default `true`).
735 ///
736 /// # Examples
737 ///
738 /// ```
739 /// use fmt_lang::Rules;
740 ///
741 /// let rules = Rules::new().final_newline(false);
742 /// # let _ = rules;
743 /// ```
744 #[must_use]
745 pub fn final_newline(mut self, yes: bool) -> Self {
746 self.final_newline = yes;
747 self
748 }
749
750 /// Names a node kind whose contents are always written exactly as in the
751 /// source, such as a parser's error node (`"ERROR"` in languages forged by
752 /// lang-forge). Only the whitespace around such a node is formatted.
753 ///
754 /// # Examples
755 ///
756 /// ```
757 /// use fmt_lang::Rules;
758 ///
759 /// let rules = Rules::new().verbatim("ERROR").verbatim("raw_block");
760 /// # let _ = rules;
761 /// ```
762 #[must_use]
763 pub fn verbatim(mut self, kind: impl Into<String>) -> Self {
764 self.verbatim.push(kind.into());
765 self
766 }
767
768 /// Adds a top-level token rule: a default for that token kind (or, with
769 /// [`TokenRule::any`], for every token) wherever it appears.
770 ///
771 /// # Examples
772 ///
773 /// ```
774 /// use fmt_lang::{Rules, Space, TokenRule};
775 ///
776 /// let rules = Rules::new().token(TokenRule::new("+").around(Space::Single));
777 /// # let _ = rules;
778 /// ```
779 #[must_use]
780 pub fn token(mut self, rule: TokenRule) -> Self {
781 self.tokens.push(rule);
782 self
783 }
784
785 /// Adds a node rule.
786 ///
787 /// # Examples
788 ///
789 /// ```
790 /// use fmt_lang::{NodeRule, Rules, Space};
791 ///
792 /// let rules = Rules::new().node(NodeRule::new("stmt").before(Space::Hard));
793 /// # let _ = rules;
794 /// ```
795 #[must_use]
796 pub fn node(mut self, rule: NodeRule) -> Self {
797 self.nodes.push(rule);
798 self
799 }
800}