Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions HISTORY_v2.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# v2.11.5

Fixed more buggy cases of YASStyle indentation (some of which were regressions in v2.11.1). (#1230, #1231)

# v2.11.4

Fixed a bug where comments from outside docstrings would be incorrectly duplicated inside the docstring if the vertical length of the docstring changed during formatting. (#1223, #1229)
Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "JuliaFormatter"
uuid = "98e50ef6-434e-11e9-1051-2b60c6c9e899"
version = "2.11.4"
version = "2.11.5"
authors = ["Dominique Luna <dluna132@gmail.com> and contributors"]

[workspace]
Expand Down
107 changes: 107 additions & 0 deletions src/fst.jl
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,113 @@ function is_import_expr(x::FST)
return x.typ in (Import, Using, Export, Public)
end

"""
is_indent_nest(fst::FST)

Whether nesting the binary call `fst` at its operator indents the RHS by one level from the
start of the line:

```julia
aaaaaaaaaaaa =>
rhs
```

as opposed to bringing it back to the column the LHS starts at, which is what every other
operator does:

```julia
aaaaaaaaaaaa in
rhs
```

True for assignments, `=>`, `->` and standalone short-circuits.
"""
function is_indent_nest(fst::FST)
md = fst.metadata
return (!isnothing(md) && (md::Metadata).is_short_form_function) ||
is_assignment(fst) ||
op_kind(fst) in KSet"=> ->" ||
(!isnothing(md) && (md::Metadata).is_standalone_shortcircuit)
end

"""
is_column_aligned(fst::FST)

Whether YASStyle lays out the continuation lines of `fst` relative to the column at which
`fst` itself starts, rather than relative to the start of the line it appears on.

Iterables are column-aligned, because their arguments line up with the bracket that opens
them, as is `where`, whose type parameters line up with the `{`:

```julia
f(aaaaaaaaaaaaaaaaaaaaaa,
bbbbbbbbbbbbbbbbbbbbbb)

Foo{A} where {Aaaaaaaaaaaaaaa<:Xxxxxxxxxxxx,
Bbbbbbbbbbbbbbb<:Yyyyyyyyyyyy}
```

Block-like constructs are not, because their bodies are indented by one level from the
start of the line, regardless of where `function f()` starts:

```julia
function f()
body
end
```

For nested constructs we might need to inspect their children. For example in

```julia
@mymacro [aaaaaaaaaaaaaaaaaaaa,
bbbbbbbbbbbbbbbbbbbb]
```

the second line is indented according to the `[` iff the last argument of the
macro is column-aligned.

A binary call is column-aligned unless its operator is one of those that indents the RHS
from the start of the line instead (see [`is_indent_nest`](@ref)).

`Chain`, `Comparison` and `Conditional` are column-aligned in YASStyle too, but they are
deliberately not listed here because the only caller (`n_binaryopcall!`) accounts for their
full width before it will consider undoing a nesting, so it never has to move one of them
that has already been split over several lines.
"""
function is_column_aligned(fst::FST)
return if is_leaf(fst)
false
elseif fst.typ === Binary
nodes = fst.nodes::Vector{FST}
if any(n -> n.typ === NEWLINE, nodes)
# It has been split across its own operator, so `n_binaryopcall!` decided
# where the RHS goes.
!is_indent_nest(fst)
else
# It is still on one line, so any continuation lines belong to the RHS
# operand. In `@mac aaaa = [bbbbb, ccccc]` it is the vector literal, not the
# assignment, that the second line is aligned against.
is_column_aligned(nodes[end])
end
elseif fst.typ === MacroBlock
# The trailing argument is the one that continuation lines follow on from.
nodes = fst.nodes::Vector{FST}
!isempty(nodes) && is_column_aligned(nodes[end])
elseif fst.typ === Unary
# Either `!x` or `x...`; in both cases it's the operand that matters, not the
# operator.
nodes = fst.nodes::Vector{FST}
idx = findfirst(n -> n.typ !== OPERATOR, nodes)
idx !== nothing && is_column_aligned(nodes[idx])
elseif fst.typ === Where # `where {...}`
true
elseif is_iterable(fst)
true
else
false
end
end

"""
Returns whether `fst` can be an iterable argument. For example in
the case of a function call, which is of type `Call`:
Expand Down
52 changes: 28 additions & 24 deletions src/styles/default/nest.jl
Original file line number Diff line number Diff line change
Expand Up @@ -917,14 +917,7 @@ function n_binaryopcall!(
fst[i1] = Newline(; length = fst[i1].len)
nested = true

indent_nest =
(!isnothing(fst.metadata) && (fst.metadata::Metadata).is_short_form_function) ||
is_assignment(fst) ||
op_kind(fst) in KSet"=> ->" ||
(
!isnothing(fst.metadata) &&
(fst.metadata::Metadata).is_standalone_shortcircuit
)
indent_nest = is_indent_nest(fst)

if indent_nest
s.line_offset = fst.indent + s.opts.indent
Expand All @@ -943,7 +936,12 @@ function n_binaryopcall!(
end
end

# rhs
# RHS.

# Remember the column the RHS starts at in this (nested) layout. Any alignment
# that nesting the RHS sets up is relative to this column, so if the nesting is
# undone further below we need to know how far the RHS has moved sideways.
rhs_line_offset_when_op_nested = s.line_offset
fst[end].extra_margin = fst.extra_margin
nest!(style, fst[end], s, lineage)

Expand Down Expand Up @@ -993,22 +991,28 @@ function n_binaryopcall!(
fst[i2] = Whitespace(0)
line_offset = s.line_offset
walk(unnest!(style; dedent = true), rhs, s)
# Iterables in YASStyle need to be aligned with the
# open bracket
if style isa YASStyle
if is_unnamed_iterable(rhs)
extra_indent = if !isempty(rhs.nodes) && is_opener(rhs[1])
line_offset - rhs.indent + 1
else
line_offset - rhs.indent
end
add_indent!(rhs, s, extra_indent)
elseif is_named_iterable(rhs)
extra_indent =
line_offset - rhs.indent + length(rhs[1]) + length(rhs[2])
add_indent!(rhs, s, extra_indent)
end
# The RHS may span multiple lines, in which case we need to decide
# how to reindent its continuation lines.
#
# If the RHS aligns its continuation lines against the column it
# starts at (see `is_column_aligned`), then we need to reuse the
# `rhs_line_offset_when_op_nested` that we calculated earlier.
#
# When we undo the nesting, the rhs gets moved to column `line_offset`,
# so every indent inside it has to be offset by the difference with
# `rhs_line_offset_when_op_nested`.
#
# `s.opts.indent` is added back on because `unnest!` above dedented the
# whole RHS by one level.
if style isa YASStyle && is_column_aligned(rhs)
add_indent!(
rhs,
s,
line_offset - rhs_line_offset_when_op_nested + s.opts.indent,
)
end
# For default style, nothing is 'column aligned' so we don't need to do
# anything.
end
end
end
Expand Down
80 changes: 80 additions & 0 deletions test/yas_style.jl
Original file line number Diff line number Diff line change
Expand Up @@ -801,6 +801,86 @@ using JuliaFormatter: format_text
test_format(s2, s2, YASStyle(); margin=margin)
end
end

@testset "realignment of RHS after un-nesting a binary op" begin
s_ = """
for x in @mac [aaaaa, bbbbb, ccccc]
foo
end"""
s = """
for x in @mac [aaaaa, bbbbb,
ccccc]
foo
end"""
test_format(s_, s, YASStyle(); margin=30)

s_ = """
for x in @mac f(aaaaa, bbbbb, ccccc)
foo
end"""
s = """
for x in @mac f(aaaaa, bbbbb,
ccccc)
foo
end"""
test_format(s_, s, YASStyle(); margin=31)

s_ = """
ccc => @mac function ()
return aaa
end"""
s = """
ccc => @mac function ()
return aaa
end"""
test_format(s_, s, YASStyle(); margin=24)

s_ = """
for x in yyy::Foo(aaaaa, bbbbb, ccccc)
foo
end"""
s = """
for x in yyy::Foo(aaaaa, bbbbb,
ccccc)

foo
end"""
test_format(s_, s, YASStyle(); margin=33)

s_ = """
for x in @mac aaaa = [bbbbbb, cccccc, dddddd]
foo
end"""
s = """
for x in @mac aaaa = [bbbbbb, cccccc,
dddddd]
foo
end"""
test_format(s_, s, YASStyle(); margin=39)

s_ = """
for x in !(aaaa == 1 && (bbbb(cc) || dddd))
foo
end"""
s = """
for x in !(aaaa == 1 &&
(bbbb(cc) || dddd))
foo
end"""
test_format(s_, s, YASStyle(); margin=24)

s_ = """
for x in Foo{A} where {Aa<:Xx, Bb<:Yy, Cc<:Zz}
foo
end"""
s = """
for x in Foo{A} where {Aa<:Xx,Bb<:Yy,
Cc<:Zz}

foo
end"""
test_format(s_, s, YASStyle(); margin=38)
end
end

end