Skip to main content

format_keeping

Function format_keeping 

Source
pub fn format_keeping<K: TokenKind + Ord>(
    tree: &Node<K>,
    source: &str,
    style: &Style<K>,
    width: usize,
    keep: &[Span],
) -> Result<String, FormatError>
Expand description

Formats like format(), but leaves the whitespace around and inside the keep regions exactly as written.

Pass the spans of lexical errors here (an LSP server has them as diagnostics). Some tokens end where the source’s whitespace happens to be: an unterminated string usually ends at the line break, so removing that line break would pull the next token into the string. The formatter cannot tell such a token from a complete one, but every gap that touches a kept region keeps its original whitespace, no separator is inserted there, and a separator inside one is never removed, so the token stays as it was.

A gap touches a kept region when the region overlaps the token before the gap, the gap itself, or the token after it; an empty region (a diagnostic pointing between two tokens) keeps the gap it falls in. Regions may overlap and come in any order.

Idempotence holds as long as the regions mark the same tokens when the output is formatted again. Lexical errors do (they belong to a token); parse errors such as “expected ;” may point elsewhere once whitespace has changed, so keeping their regions can shift between runs.

§Errors

As for format().

§Examples

use fmt_lang::{format, format_keeping, NodeRule, Rules, Space, Trailing};
use lang_forge::Language;

let json = Language::from_lsf(
    "[language]\nname = \"j\"\n[lexer]\nstrings = ['\"']\n[rules]\n\
     list = \"'[' (STRING (',' STRING)*)? ']'\"\n",
)?;
let style = Rules::new()
    .node(NodeRule::new("list").delimiters("[", "]", Space::None).separator(",", Space::Single, Trailing::Preserve))
    .compile(|n| json.kind(n))?;

// `"open` is unterminated: the string ends at the line break.
let parse = json.parse("[\"open\n, \"b\"]");
let spans: Vec<_> = parse.diagnostics().iter().map(|d| d.primary().span()).collect();

let kept = format_keeping(parse.tree(), parse.source(), &style, 80, &spans)?;
assert_eq!(kept, "[\"open\n, \"b\"]\n");

// Without the error spans the break goes, and the comma joins the string.
let lost = format(parse.tree(), parse.source(), &style, 80)?;
assert_eq!(lost, "[\"open, \"b\"]\n");