JuliaFormatter.jl Documentation

repository·main·Indexed 20 days ago

https://github.com/juliaeditorsupport/juliaformatter.jl

A width-sensitive formatter for Julia code inspired by gofmt and black. It provides a Julia API for formatting strings and files, as well as a standalone command-line tool called `jlfmt` (available in version 2.2.0+). Supports multiple pre-defined styles including DefaultStyle, YASStyle, BlueStyle, SciMLStyle, and MinimalStyle, and allows for project-specific configuration via .JuliaFormatter.toml files.

Tokens
18.8K
Snippets
80
Records
121
Agent score
70%

What's inside JuliaFormatter.jl

  1. Overview of JuliaFormatter.jl features

    main

    JuliaFormatter.jl is a width-sensitive formatter for Julia code built with JuliaSyntax.jl. Key features include:

    • Sane Defaults: Works out of the box with minimal configuration.
    • Style Guides: Built-in support for YAS, Blue, and SciML style guides.
    • Configuration: Supports project-level settings via a .JuliaFormatter.toml file.
    • Flexible Interfaces: Can be used as a Julia package in the REPL or as a standalone command-line tool (jlfmt).
  2. Understand `jlfmt` exit codes and output

    main

    jlfmt follows standard CLI conventions:

    • Exit code 0: Success.
    • Exit code 1: Formatting errors occurred, or the --check flag detected files that were not correctly formatted.
    • Stdout: Formatted output is sent to stdout by default (unless --inplace is used).
    • Stderr: Error messages are sent to stderr.
  3. Nesting and Unnesting logic in the default style

    main

    JuliaFormatter manages code density through nesting and unnesting rules:

    Nesting

    • Binary Operators: If an operation exceeds the margin, operands are moved to new lines. For ternary operators (? :), the : is moved first, then the ? as the margin shrinks.
    • Assignments: For foo() = body, if nesting is required, the RHS is placed on a new line and indented.
    • Function Arguments: If a call exceeds the margin, arguments are indented one level. If a comment is detected inside an expression, the expression is automatically nested.
    • Where Clauses: In A where B, A is nested prior to B.

    Unnesting

    To avoid excessive whitespace, JuliaFormatter will 'unnest' certain expressions that were previously broken into multiple lines if they can fit on a single line (e.g., simple function calls).

  4. Understand idempotence and the 🪃 symbol

    main

    An idempotent formatter is one that, when run multiple times on the same input, produces the same output every time. Some options can break this property (making the formatting non-idempotent).

    Options marked with the 🪃 emoji have the potential to cause non-idempotent formatting. If you enable these, you may need to run the formatter multiple times to reach a stable state (a "fixed point"). You can control the number of passes using the max_iterations option.

  5. Understand the characteristics of YASStyle

    main

    Compared to DefaultStyle, YASStyle follows these specific rules:

    1. Argument Alignment: Arguments are aligned immediately after the opening character (e.g., [, {, ().
      function_call(arg1,
                    arg2)
    2. Closer Placement: The closing character (e.g., )) sticks to the final argument.
    3. Nesting/Line Breaks: Line breaks for arguments only occur when the argument exceeds the maximum margin limit.
      function_call(arg1, arg2,
                    arg3)
    4. Assignment = Nesting: Unlike DefaultStyle, assignment operations (=) are not automatically nested. my_function(arg1, arg2) = arg1 * arg2 will remain on one line unless it exceeds the margin. Recommendation: Set short_to_long_function_def = true to transform long single-line definitions into standard function ... end blocks.

    Note on variable_call_indent: YASStyle supports the variable_call_indent option (defaulting to []). This controls whether certain types (like Dict) allow specific indentation patterns. If a type is not in variable_call_indent, the formatter will enforce a specific style (e.g., moving arguments to new lines) to maintain consistency.

  6. Understand the difference between Formatting Options and File Options

    main

    JuliaFormatter.jl distinguishes between two types of configuration settings:

    1. Formatting Options: These control the specific stylistic rules applied to the Julia code itself (e.g., indentation, spacing, line breaks).
    2. File Options: These control the behavior of the formatter when interacting with the filesystem (e.g., how it traverses directories or handles multiple threads). These options can only be used in .JuliaFormatter.toml configuration files and cannot be applied to individual strings or via certain direct API calls that bypass filesystem logic.
  7. Understand the `.JuliaFormatter.toml` search path and precedence

    main

    JuliaFormatter uses a hierarchical configuration model. It searches for .JuliaFormatter.toml starting from the directory of the file being formatted and moving upwards through parent directories.

    Precedence Rules:

    • Deepest wins: If multiple .JuliaFormatter.toml files exist in the hierarchy, the configuration file closest to the file being formatted (the deepest one) takes precedence.
    • Directory-wide application: Calling format("dir") will apply the configuration found in dir/.JuliaFormatter.toml to all files within dir and its subdirectories, unless a sub-directory contains its own .JuliaFormatter.toml file.
  8. Use SciMLStyle in JuliaFormatter.jl

    main

    The SciMLStyle() represents a collection of styles maintained for compatibility in JuliaFormatter v2.

    Warning: The official SciML Style Guide currently recommends using Runic.jl for formatting instead. SciMLStyle() in JuliaFormatter is a legacy implementation that predates the current recommendation and may be renamed in future major releases to avoid confusion with the current SciML standard.

    format("file.jl", SciMLStyle())
  9. Understand the JuliaFormatter default style

    main

    JuliaFormatter follows a default style designed to keep code concise by preferring single-line representations where possible, while using proper indentation and nesting when code exceeds the margin.

    Key behaviors include:

    • Single-line preference: Functions, macros, and structs with no arguments, as well as simple function calls, tuples, and arrays, are kept on one line.
    • Nesting: When expressions exceed the margin, they are nested (e.g., arguments in a function call are indented, and binary operators are broken into multiple lines).
    • Indentation: Uses 4 spaces by default.
    • Automatic transformations: Includes syntax normalization like adding leading/trailing zeros to floats and ensuring where arguments are wrapped in curly brackets.
  10. How JuliaFormatter works

    main

    JuliaFormatter processes source code through a four-stage pipeline to transform raw text into formatted code:

    1. Parsing (String -> CST): The source code is parsed into a Concrete Syntax Tree (CST). Unlike an Abstract Syntax Tree (AST), the CST preserves all non-semantic information like whitespace, comments, and specific parenthesization.
    2. Generating an FST (CST -> FST): The CST is transformed into a Formatted Syntax Tree (FST). The FST is a richer representation that includes substrings from the original source and special PLACEHOLDER nodes. These placeholders mark potential locations where newlines can be inserted.
    3. Nesting the FST: The formatter decides which PLACEHOLDER nodes should be converted into actual NEWLINE nodes. This decision is driven by formatting rules and constraints like the margin.
    4. Printing the FST: The final nested tree is traversed and printed out as a formatted string.
  11. Understand the difference between CST and AST

    main

    When working with JuliaFormatter, it is important to distinguish between a Concrete Syntax Tree (CST) and an Abstract Syntax Tree (AST):

    • CST (used by JuliaFormatter): Retains all information from the source code, including whitespace, extra parentheses, and comments. This allows the formatter to reconstruct code while preserving user-intended readability and comments.
    • AST (e.g., Meta.parse): Discards non-semantic information. Using an AST for formatting would result in the loss of comments and original parenthesization.

    JuliaFormatter v2.5 uses JuliaSyntax.jl v1 as its CST parser.

    using JuliaSyntax: parseall, GreenNode
    
    text = "x = f(y + z) # comment"
    cst = parseall(GreenNode, text)
  12. Understand syntax transformation risks

    main

    Some formatting options perform syntax transformations, meaning the Abstract Syntax Tree (AST) of the code might change. Use these with caution as they can potentially change the meaning of your code.

    JuliaFormatter categorizes options using an emoji legend to indicate risk levels:

    • 📐 Whitespace: Purely about whitespace (safest).
    • ♻️ Syntax Normalization: Inserts elements like parentheses but does not change the AST.
    • ⚠️ Syntax Transformation: Changes the AST, but transformations are conservatively scoped and generally safe.
    • 🔥 Dangerous Syntax Transformation: Changes the AST in ways that are not conservatively scoped and can change code meaning. pipe_to_function_call is currently the only option in this category.

    Safety Guard: By default, JuliaFormatter does not perform syntax transformations inside macros or Exprs. To enable transformations inside macros, set transform_syntax_in_macros=true. There is no option to enable transformations inside Exprs as it would change the literal meaning of the expression.