Skip to main content

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}