Skip to content

A formatter for the leptos view! macro

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT
Notifications You must be signed in to change notification settings

bram209/leptosfmt

Repository files navigation

leptosfmt

crates.io build security discord

A formatter for the leptos view! macro

All notable changes are documented in: CHANGELOG.md

Install

cargo install leptosfmt

or for trying out unreleased features:

cargo install --git https://github.com/bram209/leptosfmt.git

Usage

Usage: leptosfmt [OPTIONS] [INPUT_PATTERNS]...

Arguments:
  [INPUT_PATTERNS]...  A space separated list of file, directory or glob

Options:
  -m, --max-width <MAX_WIDTH>
          Maximum width of each line
  -t, --tab-spaces <TAB_SPACES>
          Number of spaces per tab
  -x, --excludes <EXCLUDE_PATTERNS>
          A space separated list of file, directory or glob
  -c, --config-file <CONFIG_FILE>
          Configuration file
  -s, --stdin
          Format stdin and write to stdout
  -r, --rustfmt
          Format with rustfmt after formatting with leptosfmt (requires stdin)
      --override-macro-names <OVERRIDE_MACRO_NAMES>...
          Override formatted macro names
  -e, --experimental-tailwind
          Format attributes with tailwind
      --tailwind-attr-names <TAILWIND_ATTR_NAMES>...
          Override attributes to be formatted with tailwind [default: class]
  -q, --quiet

      --check
          Check if the file is correctly formatted. Exit with code 1 if not
  -h, --help
          Print help
  -V, --version
          Print version

Using with Rust Analyzer

You have to do two things:

  • configure edition in rustfmt.toml
  • configure RA by setting the rust-analyzer.rustfmt.overrideCommand setting

Configure rustfmt edition

You must configure rustfmt to use the correct edition, place a rustfmt.toml file in the root of your project:

edition = "2021"
# (optional) other config...

Configure RA

Option 1: Using `rust-analyzer.toml` (Recommended)
A new way to configure `rust-analyzer` to use `leptosfmt` is to use directory based `rust-analyzer` configuration.

To do this, create a file named rust-analyzer.toml in the root of your project with the following content:

[rustfmt] 
overrideCommand = ["leptosfmt", "--stdin", "--rustfmt"]
# (optional) other config...

This method of setting up rust-analyzer is editor agnostic to any editor that uses rust-analyzer for formatting rust code.

Note: This feature of rust-analyzer is currently unstable and no guarantees are made that this will continue to work across versions. You have to use a recent version of rust-analyzer (2024-06-10 or newer).

Option 2: Editor specific config

VSCode:

For VSCode users, I recommend to use workpsace settings (CMD + shift + p -> Open workspace settings), so that you can only configure leptosfmt for workspaces that are using leptos.

Open your workspace settings and add the following configuration:

{
  "rust-analyzer.rustfmt.overrideCommand": ["leptosfmt", "--stdin", "--rustfmt"]
}

Neovim:

For Neovim users, I recommend using neoconf.nvim for managing project-local LSP configuration, so that you can only configure leptosfmt for workspaces that are using leptos.

Alternatively, you may directly configure nvim-lspconfig by appending the following to your .setup{} table:

lspconfig["rust_analyzer"].setup {
  settings = {
    ["rust-analyzer"] = {
      rustfmt = {
        overrideCommand = { "leptosfmt", "--stdin", "--rustfmt" },
      },
    },
  },
}

Emacs:

For Emacs users, see the relevant configuration option for LSP Mode.

Configuration

You can configure all settings through a leptosfmt.toml file.

max_width = 100 # Maximum width of each line
tab_spaces = 4 # Number of spaces per tab
indentation_style = "Auto" # "Tabs", "Spaces" or "Auto"
newline_style = "Auto" # "Unix", "Windows" or "Auto"
attr_value_brace_style = "WhenRequired" # "Always", "AlwaysUnlessLit", "WhenRequired" or "Preserve"
macro_names = [ "leptos::view", "view" ] # Macro names which will be formatted
closing_tag_style = "Preserve" # "Preserve", "SelfClosing" or "NonSelfClosing"

# Attribute values can be formatted by custom formatters
# Every attribute name may only select one formatter (this might change later on)
[attr_values]
class = "Tailwind" # "Tailwind" is the only attribute value formatter available for now

To see what each setting does, the see configuration docs

Examples

Single file

Format a specific file by name

leptosfmt ./examples/counter/src/lib.rs

Current directory

Format all .rs files within the current directory

leptosfmt .

Directory

Format all .rs files within the examples directory

leptosfmt ./examples

Glob

Format all .rs files ending with _test.rs within the examples directory

leptosfmt ./examples/**/*_test.rs

A note on non-doc comments

Currently this formatter does not support non-doc comments in code blocks. It uses a fork of prettyplease for formatting rust code, and prettyplease does not support this. I would like to not diverge this fork too much (so I can easily keep in sync with upstream), therefore I didn't add non-doc comment support in my prettyplease fork for now. This means that you can use non-doc comments throughout your view macro, as long as they don't reside within code blocks.

A bit more context: prettyplease uses syn to parse rust syntax. According to https://doc.rust-lang.org/reference/comments.html#non-doc-comments non-doc comments are interpreted as a form of whitespace by the parser; syn basically ignores/skips these comments and does not include them in the syntax tree.

Pretty-printer algorithm

The pretty-printer is based on Philip Karlton’s Mesa pretty-printer, as described in the appendix to Derek C. Oppen, “Pretty Printing” (1979), Stanford Computer Science Department STAN-CS-79-770, http://i.stanford.edu/pub/cstr/reports/cs/tr/79/770/CS-TR-79-770.pdf. This algorithm's implementation is taken from prettyplease which is adapted from rustc_ast_pretty.

The algorithm takes from an input stream of length n and an output device with margin width m, the algorithm requires time O(n) and space O(m). The algorithm is described in terms of two parallel processes; the first scans the input stream to determine the space required to print logical blocks of tokens; the second uses this information to decide where to break lines of text; the two processes communicate by means of a buffer of size o(m). The algorithm does not wait for the entire stream to be input, but begins printing as soon as it has received a linefull of input.