Browse Source

Consistent spacing in tuple/call argument lists, fixes #4.

pull/19/head
Fredrik Ekre 1 year ago
parent
commit
a0c5648469
No known key found for this signature in database
GPG Key ID: DE82E6D5E364C0A2
  1. 7
      src/Runic.jl
  2. 59
      src/chisels.jl
  3. 362
      src/runestone.jl
  4. 73
      test/runtests.jl

7
src/Runic.jl

@ -24,6 +24,9 @@ include("JuliaSyntax.jl")
const TagType = UInt32 const TagType = UInt32
struct NullNode end
const nullnode = NullNode()
# This is essentially just a re-packed `JuliaSyntax.GreenNode`. # This is essentially just a re-packed `JuliaSyntax.GreenNode`.
struct Node struct Node
# The next three fields directly match JuliaSyntax.GreenNode. We can not store a # The next three fields directly match JuliaSyntax.GreenNode. We can not store a
@ -186,9 +189,6 @@ function replace_bytes!(ctx::Context, bytes::Union{String, AbstractVector{UInt8}
return replace_bytes!(ctx.fmt_io, bytes, Int(sz)) return replace_bytes!(ctx.fmt_io, bytes, Int(sz))
end end
struct NullNode end
const nullnode = NullNode()
function format_node_with_kids!(ctx::Context, node::Node) function format_node_with_kids!(ctx::Context, node::Node)
# If the node doesn't have kids there is nothing to do here # If the node doesn't have kids there is nothing to do here
if is_leaf(node) if is_leaf(node)
@ -301,6 +301,7 @@ function format_node!(ctx::Context, node::Node)::Union{Node, Nothing, NullNode}
@return_something no_spaces_around_colon_etc(ctx, node) @return_something no_spaces_around_colon_etc(ctx, node)
@return_something for_loop_use_in(ctx, node) @return_something for_loop_use_in(ctx, node)
@return_something four_space_indent(ctx, node) @return_something four_space_indent(ctx, node)
@return_something spaces_in_listlike(ctx, node)
ctx.call_depth -= 1 ctx.call_depth -= 1
# If none of the transformations above changed the node (and thus returned back up one # If none of the transformations above changed the node (and thus returned back up one

59
src/chisels.jl

@ -6,7 +6,7 @@
# JuliaSyntax.jl overloads == for this but seems easier to just define a new function # JuliaSyntax.jl overloads == for this but seems easier to just define a new function
function nodes_equal(n1::Node, n2::Node) function nodes_equal(n1::Node, n2::Node)
return head(n1) == head(n2) && span(n1) == span(n2) && n1.tags == n2.tags && return head(n1) == head(n2) && span(n1) == span(n2) && # n1.tags == n2.tags &&
all(((x, y),) -> nodes_equal(x, y), zip(n1.kids, n2.kids)) all(((x, y),) -> nodes_equal(x, y), zip(n1.kids, n2.kids))
end end
@ -77,9 +77,13 @@ const TAG_DEDENT = TagType(1) << 1
const TAG_PRE_DEDENT = TagType(1) << 2 const TAG_PRE_DEDENT = TagType(1) << 2
# This (NewlineWs) node is a line continuation # This (NewlineWs) node is a line continuation
const TAG_LINE_CONT = UInt32(1) << 31 const TAG_LINE_CONT = UInt32(1) << 31
# Parameters that should have a trailing comma after last item
const TAG_TRAILING_COMMA = TagType(1) << 4
function add_tag(node::Node, tag::TagType) function add_tag(node::Node, tag::TagType)
@assert is_leaf(node) if kind(node) !== K"parameters"
@assert is_leaf(node)
end
return Node(head(node), span(node), node.kids, node.tags | tag) return Node(head(node), span(node), node.kids, node.tags | tag)
end end
@ -191,18 +195,44 @@ function first_leaf(node::Node)
end end
end end
function second_leaf(node::Node)
if is_leaf(node)
return nothing
else
kids = verified_kids(node)
if length(kids) == 0
return nothing
elseif !is_leaf(kids[1])
return second_leaf(kids[1])
elseif length(kids) > 1
@assert is_leaf(kids[1])
return first_leaf(kids[2])
else
@assert false
end
end
end
# Return number of non-whitespace kids, basically the length the equivalent # Return number of non-whitespace kids, basically the length the equivalent
# (expr::Expr).args # (expr::Expr).args
function meta_nargs(node::Node) function meta_nargs(node::Node)
return is_leaf(node) ? 0 : count(!JuliaSyntax.is_whitespace, verified_kids(node)) return is_leaf(node) ? 0 : count(!JuliaSyntax.is_whitespace, verified_kids(node))
end end
function replace_first_leaf(node::Node, kid′::Node) # Replace the first leaf
# TODO: Append the replacement bytes inside this utility function?
function replace_first_leaf(node::Node, kid′::Union{Node, NullNode})
if is_leaf(node) if is_leaf(node)
return kid′ return kid′
else else
kids′ = copy(verified_kids(node)) kids′ = copy(verified_kids(node))
kids′[1] = replace_first_leaf(kids′[1], kid′) kid′′ = replace_first_leaf(kids′[1], kid′)
if kid′′ === nullnode
popfirst!(kids′)
else
kids′[1] = kid′′
end
# kids′[1] = replace_first_leaf(kids′[1], kid′)
@assert length(kids′) > 0 @assert length(kids′) > 0
return make_node(node, kids′) return make_node(node, kids′)
end end
@ -216,6 +246,27 @@ function last_leaf(node::Node)
end end
end end
function has_newline_after_non_whitespace(node::Node)
if is_leaf(node)
@assert kind(node) !== K"NewlineWs"
return false
else
kids = verified_kids(node)
idx = findlast(!JuliaSyntax.is_whitespace, kids)
if idx === nothing
@assert false
# Everything is whitespace...
return any(x -> kind(x) === K"NewlineWs", kids)
end
return any(x -> kind(x) === K"NewlineWs", kids[idx + 1:end]) ||
has_newline_after_non_whitespace(kids[idx])
# if idx === nothing
# # All is whitespace, check if any of the kids is a newline
# return any(x -> kind(x) === K"NewlineWs", kids)
# end
end
end
function is_assignment(node::Node) function is_assignment(node::Node)
return JuliaSyntax.is_prec_assignment(node) return JuliaSyntax.is_prec_assignment(node)
# return !is_leaf(node) && JuliaSyntax.is_prec_assignment(node) # return !is_leaf(node) && JuliaSyntax.is_prec_assignment(node)

362
src/runestone.jl

@ -288,6 +288,362 @@ function spaces_around_x(ctx::Context, node::Node, is_x::F, n_leaves_per_x::Int
end end
end end
# Insert space after comma and semicolon in list-like expressions. Aim for the form
# `<nospace><item><comma><space><item><comma><space>...<item><nospace>`.
# TODO: Why did this function become sooo complicated?
function spaces_in_listlike(ctx::Context, node::Node)
if !(
kind(node) === K"tuple" ||
(kind(node) === K"call" && flags(node) == 0) || # Flag check rules out op-calls
(kind(node) === K"dotcall" && flags(node) == 0) ||
kind(node) === K"parameters"
)
return nothing
end
if kind(node) === K"parameters"
# TODO: Can probably show up elsewhere but...
@assert ctx.lineage_kinds[end] in KSet"tuple call dotcall"
end
@assert !is_leaf(node)
kids = verified_kids(node)
kids′ = kids
peek(i) = i < length(kids) ? kind(kids[i + 1]) : nothing
ws = Node(JuliaSyntax.SyntaxHead(K"Whitespace", JuliaSyntax.TRIVIA_FLAG), 1)
comma = Node(JuliaSyntax.SyntaxHead(K",", JuliaSyntax.TRIVIA_FLAG), 1)
# Find the opening and closing leafs
if kind(node) in KSet"tuple call dotcall"
opening_leaf_idx = findfirst(x -> kind(x) === K"(", kids)
if opening_leaf_idx === nothing
# TODO: Implicit tuple without (), for example arguments in a do-block
return nothing
end
closing_leaf_idx = findnext(x -> kind(x) === K")", kids, opening_leaf_idx + 1)::Int
closing_leaf_idx == opening_leaf_idx + 1 && return nothing # empty
else
@assert kind(node) === K"parameters"
opening_leaf_idx = findfirst(x -> kind(x) === K";", kids)::Int
closing_leaf_idx = lastindex(kids) + 1
end
n_items = count(
x -> !(JuliaSyntax.is_whitespace(x) || kind(x) === K","),
@view(kids[opening_leaf_idx + 1:closing_leaf_idx - 1]),
)
last_item_idx = findprev(x -> !(JuliaSyntax.is_whitespace(x) || kind(x) in KSet", ;"), kids, closing_leaf_idx - 1)
if last_item_idx <= opening_leaf_idx
last_item_idx = nothing
end
last_comma_idx = findprev(x -> kind(x) === K",", kids, closing_leaf_idx - 1)
if last_comma_idx !== nothing && last_comma_idx <= opening_leaf_idx
last_comma_idx = nothing
end
# A trailing comma is required if
# - node is a single item tuple (Julia-requirement)
# - the closing token is not on the same line as the last item (Runic-requirement)
require_trailing_comma = false
if kind(node) === K"tuple" && n_items == 1
require_trailing_comma = true
elseif kind(node) === K"parameters"
# For parameters the trailing comma is configured from the parent
require_trailing_comma = has_tag(node, TAG_TRAILING_COMMA)
elseif n_items > 0
require_trailing_comma = any(
x -> kind(x) === K"NewlineWs", @view(kids[last_item_idx + 1:closing_leaf_idx - 1]),
) || has_newline_after_non_whitespace(kids[last_item_idx])
end
expect_trailing_comma = require_trailing_comma
# Helper to compute the new state after a given item
function state_after_item(i)
@assert i <= last_item_idx
if i < last_item_idx
return :expect_comma
elseif i == last_item_idx && expect_trailing_comma
return :expect_comma
else
return :expect_closing
end
end
# Keep track of the state
state = kind(node) === K"parameters" ? (:expect_space) :
n_items > 0 ? (:expect_item) :
(:expect_closing)
any_kid_changed = false
pos = position(ctx.fmt_io)
# Accept kids up until the opening leaf
for i in 1:opening_leaf_idx
accept_node!(ctx, kids[i])
end
# Loop over the kids between the opening/closing tokens.
for i in (opening_leaf_idx + 1):(closing_leaf_idx - 1)
kid′ = kids[i]
this_kid_changed = false
if state === :expect_item
if kind(kid′) === K"Whitespace" && peek(i) !== K"Comment"
# Delete whitespace unless followed by a comment
replace_bytes!(ctx, "", span(kid′))
this_kid_changed = true
if kids′ === kids
kids′ = kids[1:i - 1]
end
elseif kind(kid′) === K"NewlineWs" ||
(kind(kid′) === K"Whitespace" && peek(i) === K"Comment")
# Newline here can happen if this kid is just after the opening leaf or if
# there is an empty line between items. No state change.
accept_node!(ctx, kid′)
any_kid_changed && push!(kids′, kid′)
elseif kind(kid′) === K"Comment"
accept_node!(ctx, kid′)
any_kid_changed && push!(kids′, kid′)
state = :expect_space # To ensure space after the comment
else
# This is an item (probably?).
# Make sure it doesn't have leading or trailing whitespace.
if kind(first_leaf(kid′)) === K"Whitespace" && kind(second_leaf(kid′)) !== K"Comment"
# Delete the whitespace leaf
kid_ws = first_leaf(kid′)
replace_bytes!(ctx, "", span(kid_ws))
kid′ = replace_first_leaf(kid′, nullnode)
this_kid_changed = true
end
if kind(last_leaf(kid′)) === K"Whitespace"
@assert false # Unreachable?
end
# Kid is now acceptable
any_kid_changed |= this_kid_changed
if any_kid_changed
if kids′ === kids
kids′ = kids[1:i - 1]
end
push!(kids′, kid′)
end
accept_node!(ctx, kid′)
# Transition to the next state
state = state_after_item(i)
end
elseif state === :expect_comma
if kind(kid′) === K","
before_last_item = i < last_item_idx
if before_last_item || expect_trailing_comma
# Nice, just accept it.
accept_node!(ctx, kid′)
any_kid_changed && push!(kids′, kid′)
else
@assert false # Unreachable?
end
# Transition to the next state
state = before_last_item ? (:expect_space) : (:expect_closing)
elseif kind(kid′) === K"Whitespace" && peek(i) !== K"Comment"
# Delete space (unless followed by a comment) and hope next is still comma
# (no state change)
this_kid_changed = true
if kids′ === kids
kids′ = kids[1:i - 1]
end
replace_bytes!(ctx, "", span(kid′))
elseif kind(kid′) === K"NewlineWs" ||
(kind(kid′) === K"Whitespace" && peek(i) === K"Comment") ||
kind(kid′) === K"Comment"
# This branch can be reached if:
# - we have passed the last item and there is no trailing comma
# - there is a comma coming but it is on the next line (weird)
# - there is a comment with no space before it
next_non_ws_idx = findnext(
!JuliaSyntax.is_whitespace, @view(kids[1:closing_leaf_idx - 1]), i + 1,
)
next_kind = next_non_ws_idx === nothing ? nothing : kind(kids[next_non_ws_idx])
# Insert a comma
if next_kind !== K","
@assert expect_trailing_comma
this_kid_changed = true
if kids′ === kids
kids′ = kids[1:i - 1]
end
replace_bytes!(ctx, ",", 0)
push!(kids′, comma)
accept_node!(ctx, comma)
state = :expect_closing
else
@assert false # Unreachable?
end
any_kid_changed |= this_kid_changed
# Accept the newline
accept_node!(ctx, kid′)
any_kid_changed && push!(kids′, kid′)
elseif kind(kid′) === K"parameters"
@assert kind(node) in KSet"call dotcall" # TODO: Can this happen for named tuples?
@assert i === last_item_idx
@assert findnext(
!JuliaSyntax.is_whitespace, @view(kids[1:closing_leaf_idx - 1]), i + 1,
) === nothing
if kind(first_leaf(kid′)) === K"Whitespace"
# Delete the whitespace leaf
kid_ws = first_leaf(kid′)
replace_bytes!(ctx, "", span(kid_ws))
kid′ = replace_first_leaf(kid′, nullnode)
this_kid_changed = true
# if kids′ === kids
# kids′ = kids[1:i - 1]
# end
end
if expect_trailing_comma && !has_tag(kid′, TAG_TRAILING_COMMA)
# Tag the parameters node to require a trailing comma
kid′ = add_tag(kid′, TAG_TRAILING_COMMA)
this_kid_changed = true
# if kids′ === kids
# kids′ = kids[1:i - 1]
# end
end
# TODO: Tag for requiring trailing comma.
any_kid_changed |= this_kid_changed
accept_node!(ctx, kid′)
if any_kid_changed
if kids′ === kids
kids′ = kids[1:i - 1]
end
push!(kids′, kid′)
end
state = :expect_closing # parameters must be the last item(?)
else
@assert false # Unreachable?
end
elseif state === :expect_space
if (kind(kid′) === K"Whitespace" && span(kid′) == 1) ||
(kind(kid′) === K"Whitespace" && peek(i) === K"Comment")
# Whitespace with correct span
# Whitespace before a comment
accept_node!(ctx, kid′)
any_kid_changed && push!(kids′, kid′)
state = :expect_item
elseif kind(kid′) === K"Whitespace"
# Wrong span, replace it
this_kid_changed = true
if kids′ === kids
kids′ = kids[1:i - 1]
end
replace_bytes!(ctx, " ", span(kid′))
accept_node!(ctx, ws)
push!(kids′, ws)
# Transition to the next state
state = :expect_item
elseif kind(kid′) === K"NewlineWs"
# NewlineWs are accepted and accounts for a space
accept_node!(ctx, kid′)
any_kid_changed && push!(kids′, kid′)
state = :expect_item
elseif kind(kid′) === K"Comment"
# Comments are accepted, state stays the same
# TODO: Make sure there is a space before the comment? Maybe that's not the
# responsibility of this function though.
accept_node!(ctx, kid′)
any_kid_changed && push!(kids′, kid′)
else
# Probably a list item, look for leading whitespace, or insert.
@assert !(kind(kid′) in KSet", ;")
if kind(first_leaf(kid′)) === K"NewlineWs" ||
kind(first_leaf(kid′)) === K"Comment" ||
(kind(first_leaf(kid′)) === K"Whitespace" && kind(second_leaf(kid′)) === K"Comment")
# Newline, comment, or whitespace followed by comment
accept_node!(ctx, kid′)
any_kid_changed && push!(kids′, kid′)
state = state_after_item(i)
elseif kind(first_leaf(kid′)) === K"Whitespace"
ws_node = first_leaf(kid′)
if span(ws_node) == 1
accept_node!(ctx, kid′)
any_kid_changed && push!(kids′, kid′)
else
kid′ = replace_first_leaf(kid′, ws)
this_kid_changed = true
if kids′ === kids
kids′ = kids[1:i - 1]
end
replace_bytes!(ctx, " ", span(ws_node))
accept_node!(ctx, kid′)
push!(kids′, kid′)
end
state = state_after_item(i)
else
# Insert a standalone space kid and then accept the current node
this_kid_changed = true
if kids′ === kids
kids′ = kids[1:i - 1]
end
replace_bytes!(ctx, " ", 0)
push!(kids′, ws)
accept_node!(ctx, ws)
push!(kids′, kid′)
accept_node!(ctx, kid′)
# Here we inserted a space and consumed the next item, moving on to comma
state = state_after_item(i)
end
end
else
@assert state === :expect_closing
if kind(kid′) === K"," ||
(kind(kid′) === K"Whitespace" && peek(i) !== K"Comment")
# Trailing comma (when not wanted) and space not followed by a comment are
# removed
this_kid_changed = true
if kids′ === kids
kids′ = kids[1:i - 1]
end
replace_bytes!(ctx, "", span(kid′))
elseif kind(kid′) === K"NewlineWs" ||
(kind(kid′) === K"Whitespace" && peek(i) === K"Comment") ||
kind(kid′) === K"Comment"
# Newlines, whitespace followed by comment, and comments are accepted.
accept_node!(ctx, kid′)
any_kid_changed && push!(kids′, kid′)
else
@assert false # Unreachable?
end
end # if-state
any_kid_changed |= this_kid_changed
end
if state !== :expect_closing
if state === :expect_comma
# Need to add a trailing comma if it is expected
@assert state === :expect_comma
@assert expect_trailing_comma
any_kid_changed = true
if kids′ === kids
kids′ = kids[1:closing_leaf_idx - 1]
end
replace_bytes!(ctx, ",", 0)
push!(kids′, comma)
accept_node!(ctx, comma)
state = :expect_closing
else
@assert false # Unreachable?
end
end
@assert state === :expect_closing
# Accept kids after the closing leaf
for i in closing_leaf_idx:length(kids)
accept_node!(ctx, kids[i])
any_kid_changed && push!(kids′, kids[i])
end
# Reset stream
seek(ctx.fmt_io, pos)
# Create a new node if any kids changed
if any_kid_changed
n = make_node(node, kids′)
return n
else
@assert kids === kids′
return nothing
end
end
# This pass handles spaces around infix operator calls, comparison chains, and # This pass handles spaces around infix operator calls, comparison chains, and
# <: and >: operators. # <: and >: operators.
function spaces_around_operators(ctx::Context, node::Node) function spaces_around_operators(ctx::Context, node::Node)
@ -1226,7 +1582,8 @@ function insert_delete_mark_newlines(ctx::Context, node::Node)
return indent_let(ctx, node) return indent_let(ctx, node)
elseif is_begin_block(node) elseif is_begin_block(node)
return indent_begin(ctx, node) return indent_begin(ctx, node)
elseif kind(node) in KSet"call dotcall" && flags(node) == 0 # TODO: Why the flag check? elseif kind(node) in KSet"call dotcall" &&
flags(node) == 0 # Flag check rules out op-calls
return indent_call(ctx, node) return indent_call(ctx, node)
elseif is_infix_op_call(node) elseif is_infix_op_call(node)
return indent_op_call(ctx, node) return indent_op_call(ctx, node)
@ -1266,7 +1623,8 @@ function insert_delete_mark_newlines(ctx::Context, node::Node)
return indent_comparison(ctx, node) return indent_comparison(ctx, node)
elseif kind(node) === K"toplevel" elseif kind(node) === K"toplevel"
return indent_toplevel(ctx, node) return indent_toplevel(ctx, node)
elseif kind(node) === K"module" && findlast(x -> x === K"module", ctx.lineage_kinds) !== nothing elseif kind(node) === K"module" &&
findlast(x -> x === K"module", ctx.lineage_kinds) !== nothing
return indent_module(ctx, node) return indent_module(ctx, node)
end end
return nothing return nothing

73
test/runtests.jl

@ -14,7 +14,7 @@ using JuliaSyntax:
@test sprint(show, node) == "Node({head: {kind: K\"toplevel\", flags: \"\"}, span: 10, tags: \"\"})" @test sprint(show, node) == "Node({head: {kind: K\"toplevel\", flags: \"\"}, span: 10, tags: \"\"})"
# JuliaSyntax duck-typing # JuliaSyntax duck-typing
for n in (node, Runic.verified_kids(node)...,) for n in (node, Runic.verified_kids(node)...)
@test Runic.head(n) === JuliaSyntax.head(n) === n.head @test Runic.head(n) === JuliaSyntax.head(n) === n.head
@test Runic.kind(n) === JuliaSyntax.kind(n) === n.head.kind @test Runic.kind(n) === JuliaSyntax.kind(n) === n.head.kind
@test Runic.flags(n) === JuliaSyntax.flags(n) === n.head.flags @test Runic.flags(n) === JuliaSyntax.flags(n) === n.head.flags
@ -198,6 +198,63 @@ end
end end
end end
@testset "spaces in lists" begin
for sp in ("", " ", " "), a in ("a", "a + a", "a(x)"), b in ("b", "b + b", "b(y)")
# tuple, call, dotcall
for f in ("", "f", "f.")
# single line
@test format_string("$(f)($(sp))") == "$(f)()"
@test format_string("$(f)($(sp)$(a)$(sp),$(sp)$(b)$(sp))") ==
format_string("$(f)($(sp)$(a)$(sp),$(sp)$(b)$(sp),$(sp))") ==
"$(f)($(a), $(b))"
# line break in between items
@test format_string("$(f)($(sp)$(a)$(sp),\n$(sp)$(b)$(sp))") ==
format_string("$(f)($(sp)$(a)$(sp),\n$(sp)$(b)$(sp),$(sp))") ==
"$(f)($(a),\n $(b))"
# line break after opening token
@test format_string("$(f)(\n$(sp)$(a)$(sp),$(sp)$(b)$(sp))") ==
format_string("$(f)(\n$(sp)$(a)$(sp),$(sp)$(b)$(sp),)") ==
"$(f)(\n $(a), $(b))"
# line break before closing token
@test format_string("$(f)($(sp)$(a)$(sp),$(sp)$(b)\n)") ==
format_string("$(f)($(sp)$(a)$(sp),$(sp)$(b),\n)") ==
"$(f)($(a), $(b),\n)"
# line break after opening and before closing token
@test format_string("$(f)(\n$(sp)$(a)$(sp),$(sp)$(b)\n)") ==
format_string("$(f)(\n$(sp)$(a)$(sp),$(sp)$(b),\n)") ==
"$(f)(\n $(a), $(b),\n)"
# line break after opening and before closing token and between items
@test format_string("$(f)(\n$(sp)$(a)$(sp),\n$(sp)$(b)\n)") ==
format_string("$(f)(\n$(sp)$(a)$(sp),\n$(sp)$(b),\n)") ==
"$(f)(\n $(a),\n $(b),\n)"
# trailing comments
@test format_string("$(f)($(sp)# x\n$(sp)$(a)$(sp),$(sp)# a\n$(sp)$(b)$(sp)# b\n)") ==
format_string("$(f)($(sp)# x\n$(sp)$(a)$(sp),$(sp)# a\n$(sp)$(b),$(sp)# b\n)") ==
"$(f)($(sp)# x\n $(a),$(sp)# a\n $(b),$(sp)# b\n)"
# comments on separate lines between items
@test format_string("$(f)(\n# a\n$(a)$(sp),\n# b\n$(b)\n)") ==
format_string("$(f)(\n# a\n$(a)$(sp),\n# b\n$(b)$(sp),\n)") ==
"$(f)(\n # a\n $(a),\n # b\n $(b),\n)"
end
# Single item
@test format_string("($(sp)$(a)$(sp),$(sp))") == "($(a),)"
@test format_string("f($(sp)$(a)$(sp),$(sp))") == "f($(a))"
# Keyword arguments
@test format_string("f($(sp)$(a)$(sp);$(sp)$(b)$(sp))") ==
format_string("f($(sp)$(a)$(sp);$(sp)$(b)$(sp),$(sp))") ==
"f($(a); $(b))"
@test format_string("f(\n$(sp)$(a)$(sp);\n$(sp)$(b)$(sp)\n)") ==
format_string("f(\n$(sp)$(a)$(sp);\n$(sp)$(b)$(sp),$(sp)\n)") ==
"f(\n $(a);\n $(b),\n)"
end
# Splatting
for sp in ("", " ", " ")
@test format_string("($(sp)a$(sp)...,$(sp))") == "(a$(sp)...,)"
@test format_string("f($(sp)a$(sp)...,$(sp))") == "f(a$(sp)...)"
@test format_string("f($(sp)a$(sp)...;$(sp)b$(sp)...$(sp))") == "f(a$(sp)...; b$(sp)...)"
end
end
@testset "whitespace around ->" begin @testset "whitespace around ->" begin
for sp in ("", " ", " ") for sp in ("", " ", " ")
@test format_string("a$(sp)->$(sp)b") == "a -> b" @test format_string("a$(sp)->$(sp)b") == "a -> b"
@ -330,7 +387,7 @@ end
# if-elseif-elseif-else-end # if-elseif-elseif-else-end
@test format_string( @test format_string(
"if a\n$(sp)x\n$(sp)elseif b\n$(sp)y\n$(sp)elseif " * "if a\n$(sp)x\n$(sp)elseif b\n$(sp)y\n$(sp)elseif " *
"c\n$(sp)z\n$(sp)else\n$(sp)u\n$(sp)end" "c\n$(sp)z\n$(sp)else\n$(sp)u\n$(sp)end",
) == ) ==
"if a\n x\nelseif b\n y\nelseif c\n z\nelse\n u\nend" "if a\n x\nelseif b\n y\nelseif c\n z\nelse\n u\nend"
# begin-end # begin-end
@ -420,15 +477,17 @@ end
for sp in ("", " ", " ", " ") for sp in ("", " ", " ", " ")
# tuple # tuple
@test format_string("(a,\n$(sp)b)") == "(a,\n b)" @test format_string("(a,\n$(sp)b)") == "(a,\n b)"
@test format_string("(a,\n$(sp)b\n$(sp))") == "(a,\n b\n)" @test format_string("(a,\n$(sp)b\n$(sp))") ==
@test format_string("(a,\n$(sp)b,\n$(sp))") == "(a,\n b,\n)" format_string("(a,\n$(sp)b,\n$(sp))") == "(a,\n b,\n)"
@test format_string("(\n$(sp)a,\n$(sp)b,\n$(sp))") == "(\n a,\n b,\n)" @test format_string("(\n$(sp)a,\n$(sp)b,\n$(sp))") == "(\n a,\n b,\n)"
# call, dotcall # call, dotcall
for sep in (",", ";"), d in ("", ".") for sep in (",", ";"), d in ("", ".")
@test format_string("f$(d)(a$(sep)\n$(sp)b)") == "f$(d)(a$(sep)\n b)" @test format_string("f$(d)(a$(sep)\n$(sp)b)") == "f$(d)(a$(sep)\n b)"
@test format_string("f$(d)(a$(sep)\n$(sp)b\n$(sp))") == "f$(d)(a$(sep)\n b\n)" @test format_string("f$(d)(a$(sep)\n$(sp)b\n$(sp))") ==
@test format_string("f$(d)(a$(sep)\n$(sp)b,\n$(sp))") == "f$(d)(a$(sep)\n b,\n)" format_string("f$(d)(a$(sep)\n$(sp)b,\n$(sp))") ==
@test format_string("f$(d)(\n$(sp)a$(sep)\n$(sp)b,\n$(sp))") == "f$(d)(\n a$(sep)\n b,\n)" "f$(d)(a$(sep)\n b,\n)"
@test format_string("f$(d)(\n$(sp)a$(sep)\n$(sp)b,\n$(sp))") ==
"f$(d)(\n a$(sep)\n b,\n)"
end end
# op-call, dot-op-call # op-call, dot-op-call
for d in ("", ".") for d in ("", ".")

Loading…
Cancel
Save