3 unstable releases

0.2.0 Apr 23, 2024
0.1.1 Sep 25, 2023
0.1.0 Sep 16, 2023

#130 in Value formatting

Download history 57/week @ 2024-01-29 29/week @ 2024-02-12 28/week @ 2024-02-19 109/week @ 2024-02-26 57/week @ 2024-03-04 93/week @ 2024-03-11 23/week @ 2024-03-18 9/week @ 2024-03-25 65/week @ 2024-04-01 92/week @ 2024-04-08 4/week @ 2024-04-15 196/week @ 2024-04-22

358 downloads per month
Used in 2 crates

MIT license

36KB
325 lines

tiny_pretty

Crates.io docs.rs

Tiny implementation of Wadler-style pretty printing algorithm, with some advanced options like controlling line break and indentation kind.

For tutorial and API documentation, please refer to docs.rs.

Credits

License

MIT License

Copyright (c) 2023-present Pig Fang


lib.rs:

Tiny implementation of Wadler-style pretty printing algorithm.

Basic Usage

Supposed we're going to print a code snippet of function calls, and we already have data structure defined as:

struct FunctionCall {
    name: String,
    args: Vec<FunctionCall>,
}

We may have a function call that is very very long, so we need to pretty print it for better readability. Our function call may behave like:

let fn_call = FunctionCall {
    name: "foo".into(),
    args: vec![
        FunctionCall { name: "really_long_arg".into(), args: vec![] },
        FunctionCall { name: "omg_so_many_parameters".into(), args: vec![] },
        FunctionCall { name: "we_should_refactor_this".into(), args: vec![] },
        FunctionCall { name: "is_there_seriously_another_one".into(), args: vec![] },
    ],
};

(This example is copied from Prettier with modifications.)

Now we're going to implement about building [Doc] from the data structure above. We expect arguments should be placed on a single line as possible. If they're too long to fit, we insert line break with indentation:

  • When being on a single line, there're no spaces after left paren and before right paren, and there must be a space after each argument comma.
  • When being splitted into different lines, there must be indentation when printing arguments, and there must be a line break between arguments.

So, we can build [Doc] like this:

use itertools::Itertools;
use tiny_pretty::Doc;

fn build_doc(fn_call: &FunctionCall) -> Doc {
    Doc::text(&fn_call.name)
        .append(Doc::text("("))
        .append(
            Doc::line_or_nil()
                .append(Doc::list(Itertools::intersperse(
                    fn_call.args.iter().map(build_doc),
                    Doc::text(",").append(Doc::line_or_space())
                ).collect()))
                .nest(2)
                .append(Doc::line_or_nil())
                .group()
        )
        .append(Doc::text(")"))
}

Once we have a [Doc], we can pretty print it:

#
use tiny_pretty::{print, PrintOptions};

assert_eq!(r#"
foo(
  really_long_arg(),
  omg_so_many_parameters(),
  we_should_refactor_this(),
  is_there_seriously_another_one()
)"#.trim(), &print(&build_doc(&fn_call), &PrintOptions::default()));

Besides, if we have a function call which is short enough to fit on single line:

use tiny_pretty::{print, PrintOptions};

let fn_call = FunctionCall {
    name: "foo".into(),
    args: vec![
        FunctionCall { name: "a".into(), args: vec![] },
        FunctionCall { name: "b".into(), args: vec![] },
        FunctionCall { name: "c".into(), args: vec![] },
        FunctionCall { name: "d".into(), args: vec![] },
    ],
};

#
assert_eq!(
    "foo(a(), b(), c(), d())",
    &print(&build_doc(&fn_call), &PrintOptions::default()),
);

You can specify advanced printing options, such as controlling line break and indentation kind. See PrintOptions for details.

Text Width Measurement

By default, text width is measured as "visual width". This strategy makes it satisfy the width limitation visually.

But sometimes for some Unicode characters, you may want the column to be close to width limitation as possible, though it will exceed visually. To achieve that, please enable the unicode-width feature gate.

Dependencies

~20KB