Formatting Options
JuliaFormatter is intended to be configurable and comes with a number of options to control the output of the formatter. Suggestions for more options are welcome; please open an issue!
All of these formatting options can be specified as:
- keyword arguments to
JuliaFormatter.format,JuliaFormatter.format_textandJuliaFormatter.format_file - entries in a
.JuliaFormatter.tomlfile, or - command-line arguments to the
jlfmtcommand.
.JuliaFormatter.toml and jlfmt further accept additional options that control which files are to be formatted. These options are not passed to format and friends, and are thus categorised as file options instead: please see the File Options page for more deatils.
Below is a table of all the currently available formatting options, along with what each style sets each option to. Values that differ from DefaultStyle are shown in bold.
Note that, although styles each define a different set of options, they are not just collections of options; they also have unique formatting rules that are not captured by the options.
A note about options
Not all options are created equal. This section is intended to help you understand the implications of enabling certain options.
Syntax transformations
Some of the options below are more dangerous than others, because they perform syntax transformations (i.e., Meta.parse(format_text(s)) may not be equal to Meta.parse(s)). These options can therefore change the meaning of the code being formatted depending on the context, and should be used with caution.
To help you identify dangerous options, there is an emoji legend in the 'Kind' column:
- 📐: purely about whitespace
- ♻️: syntax normalisation — this does more than just whitespace as it can insert e.g. parentheses, but it does not change the AST in any way
- ⚠️: syntax transformation — this does change the AST, but the transformations are conservatively scoped and thus should be safe
- 🔥: dangerous syntax transformation — this can change the AST, and it is not actually possible to be conservative because that would defeat the entire purpose of the option.
Currently, only pipe_to_function_call is in the last category. Note that YASStyle and BlueStyle both enable pipe_to_function_call by default, so if you use either of those styles, you should be aware that this can change the meaning of your code!
As a guard against unwanted syntax transformations, by default, JuliaFormatter does not perform any syntax transformations inside macros or Exprs.
This can be overly cautious in some cases: many macros, such as @test and @inline, are completely agnostic to their contents. If you want to enable selected syntax transformations inside macros, you can set transform_syntax_in_macros to true. However, there is no option to enable syntax transformations inside Exprs as that changes the literal meaning of the Expr.
Options that can spoil idempotence
Some of the options below additionally have the potential to cause non-idempotent formatting if enabled (or, in the case of v2_stable_multiline_strings, if disabled). They are marked with a 🪃 emoji.
If you want to ensure idempotence, it is recommended to disable these options. If you absolutely must enable them, you may need to run the formatter multiple times to reach a fixed point: you can use the max_iterations option to control the number of passes JuliaFormatter will do.
| Option | Kind | Default | YAS | Blue | SciML | Minimal |
|---|---|---|---|---|---|---|
align_assignment | 📐 | false | false | false | false | false |
align_conditional | 📐 | false | false | false | false | false |
align_matrix | 📐 | false | false | false | false | false |
align_pair_arrow | 📐 | false | false | false | false | false |
align_struct_field | 📐 | false | false | false | false | false |
always_for_in | ♻️ | false | true | true | true | nothing |
always_use_return | ⚠️ | false | true | true | false | false |
annotate_untyped_fields_with_any | ⚠️ | true | true | false | true | false |
conditional_to_if | 🪃 ♻️ | false | false | true | false | false |
disallow_single_arg_nesting | 📐 | false | false | false | true | false |
for_in_replacement | ♻️ | "in" | "in" | "in" | "in" | "in" |
force_long_function_def | ⚠️ | false | false | false | false | false |
format_docstrings | ⚠️ | false | false | false | false | false |
import_to_using | ⚠️ | false | true | true | false | false |
indent | 📐 | 4 | 4 | 4 | 4 | 4 |
indent_submodule | 📐 | false | false | true | false | false |
join_lines_based_on_source | 📐 | false | true | false | true | true |
long_to_short_function_def | ⚠️ | false | false | false | false | false |
margin | 📐 | 92 | 92 | 92 | 92 | 10_000 |
max_iterations | 1 | 1 | 1 | 1 | 1 | |
normalize_line_endings | 📐 | "auto" | "auto" | "auto" | "unix" | "auto" |
pipe_to_function_call | 🪃 🔥 | false | true | true | false | false |
remove_extra_newlines | 📐 | false | true | true | true | false |
sciml_margin_overrun | 📐 | unused | unused | unused | 20 | unused |
separate_kwargs_with_semicolon | ⚠️ | false | true | true | false | false |
short_circuit_to_if | ⚠️ | false | false | false | false | false |
short_to_long_function_def | ⚠️ | false | true | true | true | false |
surround_whereop_typeparameters | ♻️ | true | true | true | true | false |
trailing_comma | ♻️ | true | true | true | false | nothing |
trailing_zero | ♻️ | true | true | true | true | false |
transform_syntax_in_macros | ⚠️ | false | false | false | false | false |
v2_stable_multiline_strings | 🪃 📐 | false | false | false | false | false |
variable_call_indent | 📐 | [] | [] | [] | [] | [] |
whitespace_in_kwargs | 📐 | true | false | false | true | false |
whitespace_ops_in_indices | 📐 | false | true | true | true | false |
whitespace_typedefs | 📐 | false | false | false | true | false |
yas_style_nesting | 📐 | false | false | false | false | false |
align_...
Default: false
The align_assignment, align_conditional, align_pair_arrow, and align_struct_field options allow you to vertically align operators on consecutive lines. See the Custom Alignment page for more details and examples.
align_matrix
Default: false
If enabled, this option preserves pre-existing whitespace surrounding matrix elements in the original source file. If you want to align matrix elements yourself, you should set this to true.
using JuliaFormatter: format_text
# Elements left-aligned in original source
s = """
a = [
100 300 400
1 eee 40000
2 α b
]"""
format_text(s, align_matrix=true) |> printa = [
100 300 400
1 eee 40000
2 α b
]# Elements right-aligned in original source
s = """
a = [
100 300 400
1 ee 40000
2 a b
]"""
format_text(s, align_matrix=true) |> printa = [
100 300 400
1 ee 40000
2 a b
]always_for_in
Default: false
If true, iterators e.g. in for loops will always be formatted with in instead of =. The default replacement is in, but this can be changed with the for_in_replacement option. For example, to replace all = and in with ∈ in iterators, set for_in_replacement to "∈".
If false, JuliaFormatter still performs a very specific normalisation: for range iterators, e.g. for i = 1:10, JuliaFormatter uses =. For all other iterators, in is used.
If nothing, the choice between in and = is left to the user, and no normalisation is performed.
!!! Setting an option to nothing in .JuliaFormatter.toml TOML does not have a null value. To specify a nothing value in a .JuliaFormatter.toml file, set the option to the string "nothing", e.g. always_for_in = "nothing".
always_use_return
Default: false
If true, return will be prepended to the last expression in function definitions, macro definitions, and do blocks.
For example:
s = """
function foo()
expr1
expr2
end"""
using JuliaFormatter: format_text
format_text(s; always_use_return=true) |> printlnfunction foo()
expr1
return expr2
endA number of cases are skipped over:
- If the final expression is (already)
return. - If the final expression is a macro.
- If the final expression is a multiline block expression (e.g.
if...end,begin...end, etc.). This includes calls with do blocks. - If the final expression contains a
returnstatement somewhere in it (e.g.cond ? (return 1) : (return 2)). - If the final expression has a docstring attached to it.
Note that return is prepended to calls to functions such as throw(...) and error(). If you don't want this behaviour, add a return nothing at the end of the function (or just disable this option!). Click the "Show explanation" toggle below for a more detailed explanation of why I choose not to do this. (You may also be interested in the corresponding discussion on Runic.jl, which has the same behaviour.)
Show explanation for always_use_return
There are several reasons why I don't want to carve out an exception for throw(...).
The first is pragmatic. Previous versions of JuliaFormatter implemented always_use_return at the FST level, that is to say, it would construct an FST and then insert return in (usually...) the right place. This, however, causes idempotence issues because the formatting of the return value can depend on whether it is prepended with return or not. In other words, formatting choices are already baked into the FST before the return is inserted, and on the second formatting pass, the formatting choices may be different, which can lead to non-idempotence!
To fix these bugs I moved the return insertion one stage prior, so that the FST is constructed with the return keyword from the start. However, this means that we don't have easy access to the identifier throw, not without a lot of incredibly finicky code to extract the CST node and then count the offsets carefully to find the identifier in the source text.
While I could have implemented this, I'm unwilling to because of the second reason, which is more philosophical. I don't even really agree that throw() should be exempt from return. Firstly, throw() does have a type: it's Union{}, which is a bottom type. Sure, that type cannot be inhabited, i.e., there's no actual value that belongs to that type.
However, from a theoretical point of view there's absolutely nothing wrong with "return"ing something that has type Union{}. It is vacuous, much like saying "select an element from an empty set", but that doesn't mean it's wrong; it's just meaningless.
Furthermore, Julia's semantics are that the last statement in a block is the value it evaluates to. (This is not unique to Julia: see e.g. Haskell / OCaml / Rust.) If throw(...) could not be treated as a value, then following that logic, throw() should not even be allowed to be the last statement in a block. But clearly it can be! Correspondingly, there's no reason why returning it is wrong.
The last reason is that it is impossible at the syntax level to determine what expressions are guaranteed to throw an exception (or more generally, what expressions are guaranteed to not return a value). For example, if we special-case throw, then one could argue that we should also special-case qualified calls like Base.throw, and other functions like error and exit, and then user-defined functions like
mythrow(x) = throw("error: \$x")or constructs like
function f()
do_other_stuff()
f() # This never returns, so maybe we shouldn't add return?!
endObviously it doesn't make any sense to special-case any of that, so the only principled approach is not to special-case anything. I don't believe that JuliaFormatter should have an exception list that claims to do the right thing but cannot.
Indeed, even if we could perform semantic analysis on the programme, it's not possible to determine such cases, because answering the question 'does this expression return a value' amounts to solving the halting problem.
annotate_untyped_fields_with_any
Default: true
Annotates fields in a type definitions with ::Any if no type annotation is provided:
s = """
struct A
arg1
end
"""
using JuliaFormatter: format_text
format_text(s; annotate_untyped_fields_with_any=true) |> printlnstruct A
arg1::Any
endconditional_to_if
Default: false
If a ternary expression exceeds the margin, convert it into the equivalent if block:
s = "cond ? trueval : falseval"
using JuliaFormatter: format_text
format_text(s; margin=20, conditional_to_if=true) |> printlnif cond
trueval
else
falseval
endEnabling conditional_to_if can cause non-idempotent formatting, because the if block may trigger different formatting choices compared to the ternary.
For more details, click on the "Show explanation" toggle below.
Show explanation for conditional_to_if
Here is an example of non-idempotence. As you can see it is very easy to trigger!
s = "foo(cond ? trueval : falseval)"
length(s)30Let's format this with a margin of 28. (Obviously, this is smaller than typical real-world margins; but this is chosen for the purposes of simplicity. With a margin of 92, you can still hit such cases easily with longer expressions, variable names, or indentation—basically, with real-world code!)
On the first pass, two things happen:
The function call's arguments are nested because the length of the arguments (including the ternary) exceeds the line margin.
This at first leads to something like
foo( cond ? trueval : falseval, )(this string itself is never materialised, but there is an intermediate data structure that essentially represents this). Now, because the ternary itself is still over the margin, the ternary is converted to
if...end.
Collectively, these steps give us the formatting of the first pass:
out1 = format_text(s; margin=28, conditional_to_if=true)
println(out1)foo(
if cond
trueval
else
falseval
end,
)On the second time we format, the function call foo(...) sees that it has only one argument which is a block, and thus collapses the nesting (the logic being that the argument can handle the line-breaking itself):
out2 = format_text(out1; margin=28, conditional_to_if=true)
println(out2)foo(if cond
trueval
else
falseval
end)Fundamentally, the issue is that formatting choices for the function call foo(...) depend on whether or not the ternary is expanded or not, but by the time the ternary is expanded, the function call has already been formatted.
disallow_single_arg_nesting
Prevents the nesting of a single argument arg in parenthesis, brackets, and curly braces.
# Without `disallow_single_arg_nesting`:
function_call(
"String argument"
)
[array_item(
10
)]
{key => value(
"String value"
)}
# With `disallow_single_arg_nesting` enabled:
function_call("String argument")
[array_item(10)]
{key => value("String value")}for_in_replacement
Can be used when always_for_in is true to replace the default in with ∈ (\\in), or = instead. The replacement options are ("in", "=", "∈").
for a = 1:10
end
# formatted with always_for_in = true, for_in_replacement = "∈"
for a ∈ 1:10
endforce_long_function_def
Default: false
If true, tweaks the behavior of short_to_long_function_def to force the transformation no matter how short the function definition is.
format_docstrings
Default: false
Format code docstrings with the same options used for the code source.
Markdown is formatted with CommonMark alongside Julia code.
import_to_using
Default: false
If true, import expressions are rewritten to equivalent using expressions:
s = """
import A
import B, C, D"""
using JuliaFormatter: format_text
format_text(s; import_to_using=true) |> printlnusing A: A
using B: B
using C: C
using D: DThere are some exceptions to this, which makes this transformation quite conservative:
This transformation is disabled inside macros or
Exprs.import X as Ycannot be rewritten in a semantically equivalent way, so is skipped.import A.bcannot be properly resolved (it may either beusing A: borusing A.b: b), so is skipped.import ..Xcannot be rewritten becauseXmay not necessarily be a module. See e.g. this issue.
indent
Default: 4
The number of spaces used for one level of indentation.
There is at present no option to use tabs for indentation: please open an issue if you want this feature.
indent_submodule
Default: false
When set to true, submodule(s) appearing in the same file will be indented.
module A
a = 1
module B
b = 2
module C
c = 3
end
end
d = 4
endwill be formatted to:
module A
a = 1
module B
b = 2
module C
c = 3
end
end
d = 4
endjoin_lines_based_on_source
Default: false
When true lines are joined as they appear in the original source file.
function foo(arg1,
arg2, arg3
)
body
endWhen false and the maximum margin is > than the length of "function foo(arg1, arg2, arg3)" this is formatted to
function foo(arg1, arg2, arg3)
body
endWhen true, arg1 and arg2, arg3 will remain on separate lines even if they can fit on the same line since it's within maximum margin. The indentation is dependent on the style.
function foo(arg1,
arg2, arg3,
)
endThere are exceptions to this:
if a body1 elseif b body2 else body3 endwill be formatted to the following, even if this option is set to true:
if a
body1
elseif b
body2
else
body3
endlong_to_short_function_def
Default: false
Transforms a long function definition
function f(arg2, arg2)
body
endto a short function definition if the short function definition does not exceed the maximum margin.
f(arg1, arg2) = bodySee also: short_to_long_function_def.
margin
Default: 92
The maximum length of a line. Code exceeding this margin will, in general, be formatted across multiple lines.
max_iterations
Default: 1
JuliaFormatter is intended to be idempotent, meaning that formatted code should not change if formatted again. However, with certain options enabled, this may not be the case. (Please see the top of this page for more information!)
This option exists in order to allow you to perform multiple rounds of formatting, if you wish to. Note that this will cause formatting to be slower. JuliaFormatter will format the code up to max_iterations times, stopping if the code is unchanged after a formatting pass.
normalize_line_endings
Default: "auto"
One of "unix" (normalize all \r\n to \n), "windows" (normalize all \n to \r\n), "auto" (automatically choose based on which line ending is more common in the file).
pipe_to_function_call
Default: false
If true, x |> f is rewritten to f(x), and x .|> f to f.(x).
Note that this transformation may change the semantics of the code in some cases:
If
Base.:(|>)is overloaded to have a different meaning for a givenfandx.If the call to
x |> fis intercepted by a macro that transforms it to something other thanf(x). For example, Pipe.jl.
To avoid (2), JuliaFormatter refuses to apply this transformation within the body of a macro. However, there is no way to detect (1). As such, JuliaFormatter will emit a warning if this transformation is applied during formatting, to alert the user to the potential of unwanted changes.
It is recommended to set this option to true only if you are confident that there are no such cases. Note: pipe_to_function_call is set to true by default for Blue and YAS styles, so in such cases you have to opt out manually!
Enabling pipe_to_function_call can cause non-idempotent formatting, because the function call may trigger different formatting choices compared to the pipe. See this issue for an example.
In principle, this can be fixed, since we can determine ahead of time when the pipe will be transformed into the function call. However, my current judgment is that this is not really worth it since it would make the codebase more complex and brittle. On top of that, it's probably not a good idea to enable pipe_to_function_call anyway.
remove_extra_newlines
Default: false
If true, superfluous newlines will be removed. For example:
module M
a = 1
function foo()
return nothing
end
b = 2
endis rewritten as
module M
a = 1
function foo()
return nothing
end
b = 2
endModules are the only type of code block where it is permissible to keep a single newline prior to the initial or after the final piece of code.
sciml_margin_overrun
Default: 20
Additional columns SciMLStyle may use when a slightly over-margin line is more readable than an aggressive line break. Set this to 0 to make SciML soft-margin checks strict.
This option has no effect for other styles.
separate_kwargs_with_semicolon
Default: false
When set to true, keyword arguments in a function call will be separated with a semicolon.
s = "f(a, b = 1)"
using JuliaFormatter: format_text
format_text(s; separate_kwargs_with_semicolon=true) |> printlnf(a; b = 1)This transformation is disabled in several cases:
Inside macros or
Exprs.In the signature of a function definition. For example, these are different:
function f(a, b = 1) # b is an optional positional argument body end function f(a; b = 1) # b is a keyword argument body endWhen the function call does not have a semicolon, but has positional arguments after keyword arguments. For example,
f(p, q=r, s)is not transformed. See this issue for details.
short_circuit_to_if
If true, converts logical operators to the equivalent if-expression, if it is clear that the value is not used.
In particular, a && f() is converted to if a; f(); end, and a || f() is converted to if !a; f(); end.
s = """
function foo(a, b)
a || return "bar"
b && return "ooo"
return 5
end"""
using JuliaFormatter: format_text
format_text(s; short_circuit_to_if=true) |> printlnfunction foo(a, b)
if !(a)
return "bar"
end
if b
return "ooo"
end
return 5
endIn general these are only converted if they are a top-level statement inside a block. If the value of the logical operator is in any way used, it will not be converted. These include:
- Conditions in
if,elseif, andwhilestatements. - The value being assigned to a variable or otherwise being part of some expression.
- The value being returned (including implicitly, by being the last statement in a block).
short_to_long_function_def
Default: false
Transforms a short function definition
f(arg1, arg2) = bodyto a long function definition if the short function definition exceeds the maximum margin, or if force_long_function_def is set to true.
function f(arg2, arg2)
body
endSee also: long_to_short_function_def.
This transformation is disabled inside macros or Exprs.
surround_whereop_typeparameters
Default: true
If true, surrounds type parameters with curly braces if the braces are not already present.
function func(...) where TPARAM
endbecomes
function func(...) where {TPARAM}
endtrailing_comma
Default: true
One of true, false, or nothing.
Trailing commas are added after the final argument when nesting occurs and the closing punctuation appears on the next line.
For example when the following is nested (assuming DefaultStyle):
funccall(arg1, arg2, arg3)it turns into:
funccall(
arg1,
arg2,
arg3, # trailing comma added after `arg3` (final argument) !!!
)- When set to
true, the trailing comma is always added during nesting. - When set to
false, the trailing comma is always removed during nesting. - When set to
nothing, the trailing comma appears as it does in the original source.
!!! Setting an option to nothing in .JuliaFormatter.toml TOML does not have a null value. To specify a nothing value in a .JuliaFormatter.toml file, set the option to the string "nothing", e.g. always_for_in = "nothing".
trailing_zero
Default: true
Add a trailing zero, if needed.
transform_syntax_in_macros
Default: false
If true, the following syntax transformations will be permitted inside macros (if the corresponding option has been enabled):
always_use_returnannotate_untyped_fields_with_anyimport_to_usinglong_to_short_function_defshort_circuit_to_ifshort_to_long_function_def
The following syntax transformations will not be permitted inside macros, even if transform_syntax_in_macros is true:
For example, always_use_return does not fire inside a macro by default:
s = """
@macro function f()
1
end"""
using JuliaFormatter: format_text
format_text(s; always_use_return=true) |> println@macro function f()
1
endHowever, if transform_syntax_in_macros is set to true, then it does:
format_text(s; always_use_return=true, transform_syntax_in_macros=true) |> println@macro function f()
return 1
endv2_stable_multiline_strings
Default: false
If true, changes the nesting behaviour for multiline strings to guarantee idempotence on formatting.
In general, setting this option to true will cause multiline strings to be indented more to the right; equivalently, setting it to false will cause multiline strings to be shifted onto new lines more easily. However, note that this option is not intended to primarily be used as a way to control indentation of multiline strings. Its primary purpose is to guarantee idempotence; the indentation differences are a side effect which are documented here for users' awareness.
Here is an example of the different output that can be produced by the two settings. This is with the default value of false:
s = """
throw(ArgumentError(\"""
ooohhhhhh a very long thing
\"""))
"""
using JuliaFormatter: format_text
format_text(s; margin=46, v2_stable_multiline_strings=false) |> printlnthrow(
ArgumentError(
"""
ooohhhhhh a very long thing
""",
),
)But when v2_stable_multiline_strings is set to true, the output is:
format_text(s; margin=46, v2_stable_multiline_strings=true) |> printlnthrow(ArgumentError("""
ooohhhhhh a very long thing
"""))If that description is enough for you, feel free to not read on.
However, a rather long explanation (it is a complicated issue!) is included for the curious reader. Click on the "Show explanation" toggle below to read it.
Show explanation for v2_stable_multiline_strings
We begin by introducing an example of non-idempotence:
s = """
foooo((\"""
12345\""", g),
a, b)
"""
s |> printlnfoooo(("""
12345""", g),
a, b)Formatting once:
using JuliaFormatter: format_text
format_text(s; margin=21) |> printlnfoooo((
"""
12345""",
g,
), a, b)And formatting twice:
format_text(format_text(s; margin=21); margin=21) |> printlnfoooo(
(
"""
12345""",
g,
),
a,
b,
)What on earth is going on here?! When JuliaFormatter decides whether or not to insert line breaks between arguments, it does so based on the sum of the 'length's of the arguments. That is, for example, f(a, b, c) is ten characters long. If margin < 10, then JuliaFormatter will opt to move a, b, and c onto their own lines.
'Length's of most arguments are calculated very straightforwardly by calling length(). (Note that this should really be textwidth(): this is a known issue with JuliaFormatter, which will be fixed in a future version.)
The 'length' of a multiline string, with v2_stable_multiline_strings = false, is the greatest extent to which any line extends past the column before the opening quote of the string. For example, in the string below, the opening """ begins at column 6, so the column before it is 5. The "length" of the string is the greatest extent to which any line extends past column 5. From the numbers below we can see that this is 7.
"""4
000001234
01234567
"""Cool. Now returning to our input text
s |> printlnfoooo(("""
12345""", g),
a, b)the second line ends before the end of the opening """, so the 'length' of the multiline string is 3 (which corresponds to the triple quotes in the first line). If you sum the lengths up you should find that the total length of the arguments is 21 — exactly equal to the margin we used. So when JuliaFormatter looks at the outer call to foooo(...), it decides that it doesn't need to nest its arguments, in other words we can do foooo((...), a, b) instead of foooo(\n (...),\n a,\n b,\n):
format_text(s; margin=21) |> printlnfoooo((
"""
12345""",
g,
), a, b)As expected, a and b are also kept on the same line.
To be honest, this logic doesn't really make a lot of sense to me. The total 'length' being 21 here is not really a meaningful metric because the arguments to foooo(...) would never go on the same line anyway! The treatment of multiline strings will probably be reworked a future version.
Okay, so far so good. The problem begins with the fact that for the inner tuple, it sees that it has a multiline string as an argument, and because of this it will force line breaks inside the tuple. (In general, any block-like argument, e.g. if...else...end or begin...end will force nesting.) That's why the first output causes the multiline string and g to be moved onto separate lines.
So far so good, but if we now look at that output, the opening triple quotes have been moved down into a new line. The line below it, 12345""", now extends beyond the opening triple quote! And so the 'length' of the multiline string is now 4, which causes the entire expression to be over the margin, and the next formatting pass will cause the outer foooo(...) call to also be nested.
format_text(format_text(s; margin=21); margin=21) |> printlnfoooo(
(
"""
12345""",
g,
),
a,
b,
)v2_stable_multiline_strings = true fixes this by changing the way the 'length' of a multiline string is calculated. With this option enabled, the 'length' is always simply the length of the first line. In the example above, this is 3, i.e., just the triple quotes. Because of this, the outer call is never nested, and the output looks like:
kw = (margin=21, v2_stable_multiline_strings=true)
format_text(s; kw...) |> printlnfoooo((
"""
12345""",
g,
), a, b)Importantly, though, this option means that regardless of how the multiline string is indented, the 'length' will always be constant. So, the decision of whether or not to nest the arguments will be consistent across multiple formatting passes.
format_text(s; kw...) == format_text(format_text(s; kw...); kw...)trueFrom the description above, it follows that the 'length' of the multiline string with v2_stable_multiline_strings = true is always less than or equal to the 'length' with v2_stable_multiline_strings = false (since the former only inspects the first line, whereas the latter takes a maximum over all lines).
This means that v2_stable_multiline_strings = true will reduce the amount of line breaks, at the cost of subsequent lines of the multiline string potentially extending past the margin. Returning to the example at the start of this section:
s = """
throw(ArgumentError(\"""
ooohhhhhh a very long thing
\"""))
"""
# multiline string's 'length' is 27, hence over margin
format_text(s; margin=46, v2_stable_multiline_strings=false) |> printlnthrow(
ArgumentError(
"""
ooohhhhhh a very long thing
""",
),
)# multiline string's 'length' is 3, hence under margin
format_text(s; margin=46, v2_stable_multiline_strings=true) |> printlnthrow(ArgumentError("""
ooohhhhhh a very long thing
"""))I will happily admit that this is not principled either! In general, the decision of whether or not to nest arguments when a multiline string is present is more complicated than just looking at the 'length' of the arguments.
If we take a step back, the broader problem is that the outer call to foooo(...) is making formatting decisions without any knowledge of how its children are going to be nested, which could itself change the decisions that foooo(...) makes. This means that instead of exploring the full space of possible formatting outputs, where nesting decisions in parent and child nodes are coupled, JuliaFormatter is decomposing this into two 'local' decisions: one for the parent and one for the child. In many cases these can be fully decoupled, but in this case it can't; furthermore, even though decoupling does not actually ruin idempotence in many other cases, it can still lead to suboptimal formatting (which makes sense – after all we're trying fewer possibilities). Fixing this properly therefore requires a complete rethink of the formatting algorithm. If you're interested in that, see e.g. this issue.
variable_call_indent
Default: []
The SciMLStyle supports the additional option variable_call_indent. It permits continuation lines in calls to not align with the opening parenthesis.
For example, if variable_call_indent = ["Dict"], the following is allowed:
Dict{Int, Int}(
1 => 2,
3 => 4)(Note that in the configuration, "Dict" must be passed as a string: this is because JuliaFormatter matches it against the name of the function being called.)
If variable_call_indent is empty, the above will be formatted to
Dict{Int, Int}(1 => 2,
3 => 4)whitespace_in_kwargs
Default: true
If true, = in keyword arguments will be surrounded by whitespace.
f(; a=4)to
f(; a = 4)Note that if this option is false, the arguments on either side of the equals may sometimes be parenthesised to avoid parsing ambiguities. For example, f(s! = x) will be transformed to f((s!)=x), and f(t = >=(1)) will be transformed to f(t=(>=(1))).
whitespace_ops_in_indices
Default: false
If true, whitespace is added for binary operations in indices. Make this true if you prefer arr[a + b] to arr[a+b]. Additionally, if there's a colon : involved, parenthesis will be added to the LHS and RHS.
Example: arr[(i1 + i2):(i3 + i4)] instead of arr[i1+i2:i3+i4].
whitespace_typedefs
Default: false
If true, whitespace is added for type definitions. Make this true if you prefer Union{A <: B, C} to Union{A<:B,C}.
yas_style_nesting
Default: false
The option yas_style_nesting is set to false by default. Setting it to true makes the SciMLStyle use the YASStyle nesting rules. For other styles, this option has no effect.
# With `yas_style_nesting = false`
function my_large_function(argument1, argument2,
argument3, argument4,
argument5, x, y, z)
foo(x) + goo(y)
end
# With `yas_style_nesting = true`
function my_large_function(argument1, argument2,
argument3, argument4,
argument5, x, y, z)
foo(x) + goo(y)
end