Default style

JuliaFormatter has a default style. This page is intended to give a rough overview of how the output of a formatted file looks like when using the default style. Additional examples can be found in JuliaFormatter's codebase itself.

All examples assume indentation of 4 spaces.

Nesting decisions

Where possible JuliaFormatter's default style attempts to keep code on a single line.

With no arguments

Functions, macros, and structs with no arguments are placed on a single line. This also applies to abstract and primitive types:

function  foo
end
abstract type
AbstractFoo
end

is formatted to

function foo end
abstract type AbstractFoo end

With arguments

Functions calls foo(args...), tuples (args...), arrays [args...], braces {args...}, and type parameter definitions Foo{args...} are placed on a single line. This applies to any code which has opening and closing punctuation: (...), {...}, [...].

Whitespace is inserted after commas, but not for type definitions:

f(

a,b

,c )

Foo{
a,b
,c }

is formatted to

f(a, b, c)
Foo{a,b,c}

Blocks

Blocks and their bodies are spread across multiple lines and properly indented.

Example 1:

begin
  a
    b; c
       end

struct Foo{A, B}
 a::A
  b::B
end

is formatted to

begin
    a
    b
    c
end

struct Foo{A,B}
    a::A
    b::B
end

Binary operators (without nesting)

Binary calls are placed on a single line where possible. Their operands are separated by whitespace, except for : operators (i.e. ranges), and for operators which are inside indexing brackets.

Example 1:

a+b
a : a : c
list[a + b]
a + b
a:b:c
list[a+b]

Ternary expressions are placed on a single line and separated by whitespace:

cond1 ?
expr1 :     expr2

is formatted to

cond1 ? expr1 : expr2

Comments are aligned with surrounding code blocks.

# comment
if a
# comment
else
# comment
end
# comment

is formatted to

# comment
if a
    # comment
else
    # comment
end
# comment

Binary operators (with nesting)

If a binary operation exceeds the margin, they are nested back-to-front:

arg1 + arg2

becomes (with a small margin)

arg1 + 
arg2

For the ternary operator, as the margin decreases, the : is first moved to the next line, and then the ?:

cond ? e1 : e2

becomes (with a small margin)

cond ? e1 :
e2

and with an even smaller margin

cond ? 
e1 : e2

and with a yet smaller margin

cond ? 
e1 :
e2

If nesting is required for an assignment (i.e., a binary operation with = as the operator), the RHS is placed on the following line and indented.

foo() = body

is formatted to

foo() =
    body

All arguments of a function call (applies to any opening/closing punctuation type) are nested if the expression exceeds the margin. The arguments are indented one level.

function longfunctionname_that_is_long(lots, of, args, even, more, args)
    body
end

becomes

function longfunctionname_that_is_long(
    lots, 
    of, 
    args,
    even, 
    more, 
    args,
)
    body
end

With where operations (A where B), A is nested prior to B.

function f(arg1::A, key1 = val1; key2 = val2) where {A,B,C}
    body
end

becomes

function f(
    arg1::A,
    key1 = val1;
    key2 = val2,
) where {A,B,C}
    body
end

and if the margin is shrunk even more,

function f(
    arg1::A,
    key1 = val1;
    key2 = val2,
) where {
    A,
    B,
    C,
}
    body
end

If a comment is detected inside of an expression, that expression is automatically nested:

var = foo(
    a, b, # comment
    c,
)

becomes

var = foo(
    a,
    b, # comment
    c,
)

Unnesting

In certain cases it is desirable to unnest parts of a FST to avoid excessive whitespace. For example, the following

var =
    funccall(
        arg1,
        arg2,
        arg3,
    )

will be un-nested to

var = funccall(arg1, arg2, arg3)

or

var = funccall(
    arg1,
    arg2,
    arg3,
)

Syntax transformations

for in vs. for =

By default if the RHS is a range, e.g. 1:10, then for ... in ... is converted to for ... = .... Otherwise for ... = ... is converted to for ... in .... See this issue for the rationale and further explanation.

This behaviour can be controlled using the always_for_in option. Setting always_for_in=true will always convert = to in even if the RHS is a range. always_for_in=nothing will leave the choice of in vs = up to the user.

Trailing Commas

If the node is iterable, for example a function call or list and is nested, a trailing comma is added to the last argument. The trailing comma is removed if unnested:

func(a, b, c)

becomes

func(
    a,
    b,
    c,
)

See this issue for more details.

Trailing Semicolons

If a matrix node is nested, the semicolons are removed.

A = [1 0; 0 1]

->

A = [
    1 0
    0 1
]

See this issue for more details.

Leading and trailing 0s for float literals

If a float literal is missing a trailing or leading 0, it is added:

a = 1.
b = .1

becomes

a = 1.0
b = 0.1

For Float32 literals, if there is no decimal point, .0 is added:

a = 1f0

->

a = 1.0f0

See this issue for more details.

Surround where arguments with curly brackets

If the arguments of a where call are not surrounded by curly brackets, they are added:

foo(x::T) where T = ...

becomes

foo(x::T) where {T} = ...

This can be controlled with the surround_whereop_typeparameters option.

See this issue for more details.

Annotate unannotated type fields with Any

In structs, if a field is unannotated, it is annotated with Any:

struct Foo
    field
end

becomes

struct Foo
    field::Any
end

This can be controlled with the annotate_untyped_fields_with_any option.

Move @ in macro calls to the final identifier

@Module.macro

becomes

Module.@macro