@ -1,10 +1,14 @@
@@ -1,10 +1,14 @@
# SPDX-License-Identifier: MIT
function dumpnode ( node )
println ( " node: {kind: $ ( kind ( node ) ) , span: $ ( span ( node ) ) , flags: $ ( flags ( node ) ) , nkids: $ ( length ( verified_kids ( node ) ) ) } " )
end
# This is the runestone where all the formatting transformations are implemented.
function trim_trailing_whitespace ( ctx :: Context , node :: JuliaSyntax . GreenNode )
JuliaSyntax . kind ( node ) === K " NewlineWs " || return nothing
@assert ! JuliaSyntax . haschildren ( node )
function trim_trailing_whitespace ( ctx :: Context , node :: Node )
kind ( node ) === K " NewlineWs " || return nothing
@assert is_leaf ( node )
str = String ( read_bytes ( ctx , node ) )
str′ = replace ( str , r " \ h*( \ r \ n| \ r| \ n) " => '\n' )
# If the next sibling is also a NewlineWs we can trim trailing
@ -18,44 +22,44 @@ function trim_trailing_whitespace(ctx::Context, node::JuliaSyntax.GreenNode)
@@ -18,44 +22,44 @@ function trim_trailing_whitespace(ctx::Context, node::JuliaSyntax.GreenNode)
return nothing
end
# Write new bytes and reset the stream
nb = replace_bytes! ( ctx , str′ , JuliaSyntax . span ( node ) )
@assert nb != JuliaSyntax . span ( node )
nb = replace_bytes! ( ctx , str′ , span ( node ) )
@assert nb != span ( node )
# Create new node and return it
node′ = JuliaSyntax . Green Node( JuliaSyntax . head ( node ) , nb , ( ) )
node′ = Node ( head ( node ) , nb , ( ) )
return node′
end
function format_hex_literals ( ctx :: Context , node :: JuliaSyntax . Green Node)
JuliaSyntax . kind ( node ) === K " HexInt " || return nothing
@assert JuliaSyntax . flags ( node ) == 0
@assert ! JuliaSyntax . haschildren ( node )
spa n = JuliaSyntax . span ( node )
@assert spa n > 2 # 0x prefix + something more
function format_hex_literals ( ctx :: Context , node :: Node )
kind ( node ) === K " HexInt " || return nothing
@assert flags ( node ) == 0
@assert is_leaf ( node )
spn = span ( node )
@assert spn > 2 # 0x prefix + something more
# Target spans(0x + maximum chars for formatted UInt8, UInt16, UInt32, UInt64, UInt128)
target_spans = 2 .+ ( 2 , 4 , 8 , 16 , 32 )
if spa n >= 34 || spa n in target_spans
if spn >= 34 || spn in target_spans
# Do nothing: correctly formatted or a BigInt hex literal
return nothing
end
# Insert leading zeros
i = findfirst ( x -> x > spa n , target_spans ) :: Int
i = findfirst ( x -> x > spn , target_spans ) :: Int
bytes = read_bytes ( ctx , node )
while length ( bytes ) < target_spans [ i ]
insert! ( bytes , 3 , '0' )
end
nb = replace_bytes! ( ctx , bytes , spa n )
nb = replace_bytes! ( ctx , bytes , spn )
@assert nb == length ( bytes ) == target_spans [ i ]
# Create new node and return it
node′ = JuliaSyntax . Green Node( JuliaSyntax . head ( node ) , nb , ( ) )
node′ = Node ( head ( node ) , nb , ( ) )
return node′
end
function format_oct_literals ( ctx :: Context , node :: JuliaSyntax . Green Node)
JuliaSyntax . kind ( node ) === K " OctInt " || return nothing
@assert JuliaSyntax . flags ( node ) == 0
@assert ! JuliaSyntax . haschildren ( node )
spa n = JuliaSyntax . span ( node )
@assert spa n > 2 # 0o prefix + something more
function format_oct_literals ( ctx :: Context , node :: Node )
kind ( node ) === K " OctInt " || return nothing
@assert flags ( node ) == 0
@assert is_leaf ( node )
spn = span ( node )
@assert spn > 2 # 0o prefix + something more
# Padding depends on the value of the literal...
str = String ( read_bytes ( ctx , node ) )
n = tryparse ( UInt128 , str )
@ -69,10 +73,10 @@ function format_oct_literals(ctx::Context, node::JuliaSyntax.GreenNode)
@@ -69,10 +73,10 @@ function format_oct_literals(ctx::Context, node::JuliaSyntax.GreenNode)
n <= typemax ( UInt32 ) ? 13 : n <= typemax ( UInt64 ) ? 24 :
n <= typemax ( UInt128 ) ? 45 : error ( " unreachable " )
target_spans = ( 5 , 8 , 13 , 24 , 45 )
i = findfirst ( x -> x >= spa n , target_spans ) :: Int
i = findfirst ( x -> x >= spn , target_spans ) :: Int
target_span_from_source = target_spans [ i ]
target_span = max ( target_span_from_value , target_span_from_source )
if spa n == target_span
if spn == target_span
# Do nothing: correctly formatted oct literal
return nothing
end
@ -81,17 +85,17 @@ function format_oct_literals(ctx::Context, node::JuliaSyntax.GreenNode)
@@ -81,17 +85,17 @@ function format_oct_literals(ctx::Context, node::JuliaSyntax.GreenNode)
while length ( bytes ) < target_span
insert! ( bytes , 3 , '0' )
end
nb = replace_bytes! ( ctx , bytes , spa n )
nb = replace_bytes! ( ctx , bytes , spn )
@assert nb == length ( bytes ) == target_span
# Create new node and return it
node′ = JuliaSyntax . Green Node( JuliaSyntax . head ( node ) , nb , ( ) )
node′ = Node ( head ( node ) , nb , ( ) )
return node′
end
function format_float_literals ( ctx :: Context , node :: JuliaSyntax . Green Node)
JuliaSyntax . kind ( node ) in KSet " Float Float32 " || return nothing
@assert JuliaSyntax . flags ( node ) == 0
@assert ! JuliaSyntax . haschildren ( node )
function format_float_literals ( ctx :: Context , node :: Node )
kind ( node ) in KSet " Float Float32 " || return nothing
@assert flags ( node ) == 0
@assert is_leaf ( node )
str = String ( read_bytes ( ctx , node ) )
# Check and shortcut the happy path first
r = r """
@ -138,129 +142,125 @@ function format_float_literals(ctx::Context, node::JuliaSyntax.GreenNode)
@@ -138,129 +142,125 @@ function format_float_literals(ctx::Context, node::JuliaSyntax.GreenNode)
write ( io , exp_part )
end
bytes = take! ( io )
nb = replace_bytes! ( ctx , bytes , JuliaSyntax . span ( node ) )
nb = replace_bytes! ( ctx , bytes , span ( node ) )
@assert nb == length ( bytes )
# Create new node and return it
node′ = JuliaSyntax . Green Node( JuliaSyntax . head ( node ) , nb , ( ) )
node′ = Node ( head ( node ) , nb , ( ) )
return node′
end
# Insert space around `x`, where `x` can be operators, assignments, etc. with the pattern:
# `<something><space><x><space><something>`, for example the spaces around `+` and `=` in
# `a = x + y`.
function spaces_around_x ( ctx :: Context , node :: JuliaSyntax . Green Node, is_x :: F ) where F
function spaces_around_x ( ctx :: Context , node :: Node , is_x :: F ) where F
# TODO: So much boilerplate here...
@assert JuliaSyntax . haschildren ( node )
@assert ! is_leaf ( node )
children = verified_children ( node )
children′ = children
kids = verified_kids ( node )
kids′ = kids
any_changes = false
pos = position ( ctx . fmt_io )
ws = JuliaSyntax . GreenNode (
JuliaSyntax . SyntaxHead ( K " Whitespace " , JuliaSyntax . TRIVIA_FLAG ) , 1 , ( ) ,
)
ws = Node ( JuliaSyntax . SyntaxHead ( K " Whitespace " , JuliaSyntax . TRIVIA_FLAG ) , 1 , ( ) )
# Toggle for whether we are currently looking for whitespace or not
looking_for_whitespace = false
looking_for_x = false
for ( i , chil d) in pairs ( children )
if JuliaSyntax . kind ( chil d) === K " NewlineWs " ||
( i == 1 && JuliaSyntax . kind ( chil d) === K " Whitespace " )
for ( i , ki d) in pairs ( kids )
if kind ( ki d) === K " NewlineWs " ||
( i == 1 && kind ( ki d) === K " Whitespace " )
# NewlineWs are accepted as is by this pass.
# Whitespace is accepted as is if this is the first chil d even if the span is
# Whitespace is accepted as is if this is the first ki d even if the span is
# larger than we expect since we don't look backwards. It should be cleaned up
# by some other pass.
accept_node! ( ctx , chil d)
any_changes && push! ( children′ , chil d)
accept_node! ( ctx , ki d)
any_changes && push! ( kids′ , ki d)
looking_for_whitespace = false
elseif looking_for_whitespace
if JuliaSyntax . kind ( chil d) === K " Whitespace " && JuliaSyntax . span ( chil d) == 1
if kind ( ki d) === K " Whitespace " && span ( ki d) == 1
# All good, just advance the IO
accept_node! ( ctx , chil d)
any_changes && push! ( children′ , chil d)
accept_node! ( ctx , ki d)
any_changes && push! ( kids′ , ki d)
looking_for_whitespace = false
elseif JuliaSyntax . kind ( chil d) === K " Whitespace "
elseif kind ( ki d) === K " Whitespace "
# Whitespace node but replace since not single space
any_changes = true
if children′ === children
children′ = children [ 1 : i - 1 ]
if kids′ === kids
kids′ = kids [ 1 : i - 1 ]
end
push! ( children ′, ws )
replace_bytes! ( ctx , " " , JuliaSyntax . span ( chil d) )
push! ( kids ′, ws )
replace_bytes! ( ctx , " " , span ( ki d) )
accept_node! ( ctx , ws )
looking_for_whitespace = false
elseif JuliaSyntax . haschildren ( child ) &&
JuliaSyntax . kind ( first_leaf ( child ) ) === K " Whitespace "
# Whitespace found at the beginning of next child.
child_ws = first_leaf ( child )
looking_for_whitespace = JuliaSyntax . kind ( last_leaf ( child ) ) !== K " Whitespace "
@assert ! is_x ( child ) :: Bool
elseif ! is_leaf ( kid ) && kind ( first_leaf ( kid ) ) === K " Whitespace "
# Whitespace found at the beginning of next kid.
kid_ws = first_leaf ( kid )
looking_for_whitespace = kind ( last_leaf ( kid ) ) !== K " Whitespace "
@assert ! is_x ( kid ) :: Bool
looking_for_x = true
if JuliaSyntax . span ( chil d_ws) == 1
if span ( ki d_ws) == 1
# Accept the node
accept_node! ( ctx , chil d)
any_changes && push! ( children′ , chil d)
accept_node! ( ctx , ki d)
any_changes && push! ( kids′ , ki d)
else
# Replace the whitespace node of the chil d
chil d′ = replace_first_leaf ( chil d, ws )
@assert JuliaSyntax . span ( chil d′) == JuliaSyntax . span ( chil d) - JuliaSyntax . span ( chil d_ws) + 1
bytes_to_skip = JuliaSyntax . span ( chil d) - JuliaSyntax . span ( chil d′)
# Replace the whitespace node of the ki d
ki d′ = replace_first_leaf ( ki d, ws )
@assert span ( ki d′) == span ( ki d) - span ( ki d_ws) + 1
bytes_to_skip = span ( ki d) - span ( ki d′)
@assert bytes_to_skip > 0
replace_bytes! ( ctx , " " , bytes_to_skip )
accept_node! ( ctx , chil d′)
accept_node! ( ctx , ki d′)
any_changes = true
if children′ === children
children′ = children [ 1 : i - 1 ]
if kids′ === kids
kids′ = kids [ 1 : i - 1 ]
end
push! ( children′ , chil d′)
push! ( kids′ , ki d′)
end
elseif JuliaSyntax . haschildren ( child ) &&
JuliaSyntax . kind ( first_leaf ( child ) ) === K " NewlineWs "
elseif ! is_leaf ( kid ) && kind ( first_leaf ( kid ) ) === K " NewlineWs "
# NewlineWs have to be accepted as is
# @info " ... chil ds first leaf is NewlineWs I'll take it"
accept_node! ( ctx , chil d)
any_changes && push! ( children′ , chil d)
looking_for_whitespace = JuliaSyntax . kind ( last_leaf ( chil d) ) !== K " Whitespace "
@assert ! is_x ( chil d) :: Bool
# @info " ... ki ds first leaf is NewlineWs I'll take it"
accept_node! ( ctx , ki d)
any_changes && push! ( kids′ , ki d)
looking_for_whitespace = kind ( last_leaf ( ki d) ) !== K " Whitespace "
@assert ! is_x ( ki d) :: Bool
looking_for_x = true
else
# @info " ... no whitespace, inserting" JuliaSyntax.kind(chil d)
# @info " ... no whitespace, inserting" kind(ki d)
# Not a whitespace node, insert one
any_changes = true
if children′ === children
children′ = children [ 1 : i - 1 ]
if kids′ === kids
kids′ = kids [ 1 : i - 1 ]
end
push! ( children ′, ws )
push! ( kids ′, ws )
replace_bytes! ( ctx , " " , 0 )
accept_node! ( ctx , ws )
# Write and accept the node
push! ( children′ , chil d)
accept_node! ( ctx , chil d)
looking_for_whitespace = JuliaSyntax . kind ( last_leaf ( chil d) ) !== K " Whitespace "
push! ( kids′ , ki d)
accept_node! ( ctx , ki d)
looking_for_whitespace = kind ( last_leaf ( ki d) ) !== K " Whitespace "
if looking_for_x
@assert is_x ( chil d) :: Bool
@assert is_x ( ki d) :: Bool
end
# Flip the switch, unless chil d is a comment
looking_for_x = JuliaSyntax . kind ( chil d) === K " Comment " ? looking_for_x : ! looking_for_x
# Flip the switch, unless ki d is a comment
looking_for_x = kind ( ki d) === K " Comment " ? looking_for_x : ! looking_for_x
end
else # !expect_ws
if looking_for_x
@assert is_x ( chil d) :: Bool
@assert is_x ( ki d) :: Bool
end
@assert JuliaSyntax . kind ( chil d) !== K " Whitespace " # This would be weird, I think?
any_changes && push! ( children′ , chil d)
accept_node! ( ctx , chil d)
looking_for_whitespace = JuliaSyntax . kind ( last_leaf ( chil d) ) !== K " Whitespace "
# Flip the switch, unless chil d is a comment
looking_for_x = JuliaSyntax . kind ( chil d) === K " Comment " ? looking_for_x : ! looking_for_x
@assert kind ( ki d) !== K " Whitespace " # This would be weird, I think?
any_changes && push! ( kids′ , ki d)
accept_node! ( ctx , ki d)
looking_for_whitespace = kind ( last_leaf ( ki d) ) !== K " Whitespace "
# Flip the switch, unless ki d is a comment
looking_for_x = kind ( ki d) === K " Comment " ? looking_for_x : ! looking_for_x
end
end
# Reset stream
seek ( ctx . fmt_io , pos )
if any_changes
# Create new node and return it
return make_node ( node , children ′)
return make_node ( node , kids ′)
else
return nothing
end
@ -268,39 +268,39 @@ end
@@ -268,39 +268,39 @@ end
# This pass handles spaces around infix operator calls, comparison chains, and
# <: and >: operators.
function spaces_around_operators ( ctx :: Context , node :: JuliaSyntax . Green Node)
function spaces_around_operators ( ctx :: Context , node :: Node )
if ! (
( is_infix_op_call ( node ) && ! ( JuliaSyntax . kind ( infix_op_call_op ( node ) ) in KSet " : ^ " ) ) ||
( JuliaSyntax . kind ( node ) in KSet " <: >: " && n_children ( node ) == 3 ) ||
( JuliaSyntax . kind ( node ) === K " comparison " && ! JuliaSyntax . is_trivia ( node ) )
( is_infix_op_call ( node ) && ! ( kind ( infix_op_call_op ( node ) ) in KSet " : ^ " ) ) ||
( kind ( node ) in KSet " <: >: " && meta_nargs ( node ) == 3 ) ||
( kind ( node ) === K " comparison " && ! JuliaSyntax . is_trivia ( node ) )
)
return nothing
end
@assert JuliaSyntax . kind ( node ) in KSet " call comparison <: >: "
@assert kind ( node ) in KSet " call comparison <: >: "
is_x = x -> is_operator_leaf ( x ) || is_comparison_leaf ( x )
return spaces_around_x ( ctx , node , is_x )
end
function spaces_around_assignments ( ctx :: Context , node :: JuliaSyntax . Green Node)
function spaces_around_assignments ( ctx :: Context , node :: Node )
if ! ( is_assignment ( node ) && ! is_leaf ( node ) )
return nothing
end
# for-loop nodes are of kind K"=" even when `in` or `∈` is used so we need to
# include these kinds in the predicate too.
is_x = x -> is_assignment ( x ) || JuliaSyntax . kind ( x ) in KSet " in ∈ "
is_x = x -> is_assignment ( x ) || kind ( x ) in KSet " in ∈ "
return spaces_around_x ( ctx , node , is_x )
end
# Opposite of `spaces_around_x`: remove spaces around `x`
function no_spaces_around_x ( ctx :: Context , node :: JuliaSyntax . Green Node, is_x :: F ) where F
@assert JuliaSyntax . haschildren ( node )
function no_spaces_around_x ( ctx :: Context , node :: Node , is_x :: F ) where F
@assert ! is_leaf ( node )
# TODO: Can't handle NewlineWs here right now
if any ( JuliaSyntax . kind ( c ) === K " NewlineWs " for c in JuliaSyntax . children ( node ) )
if any ( kind ( c ) === K " NewlineWs " for c in verified_kids ( node ) )
return nothing
end
children = verified_children ( node )
children′ = children
kids = verified_kids ( node )
kids′ = kids
any_changes = false
pos = position ( ctx . fmt_io )
@ -308,28 +308,28 @@ function no_spaces_around_x(ctx::Context, node::JuliaSyntax.GreenNode, is_x::F)
@@ -308,28 +308,28 @@ function no_spaces_around_x(ctx::Context, node::JuliaSyntax.GreenNode, is_x::F)
# K"::", K"<:", and K">:" are special cases here since they can be used without an LHS
# in e.g. `f(::Int) = ...` and `Vector{<:Real}`.
if JuliaSyntax . kind ( node ) in KSet " :: <: >: "
looking_for_x = is_x ( first_non_whitespace_chil d ( node ) ) :: Bool
if kind ( node ) in KSet " :: <: >: "
looking_for_x = is_x ( first_non_whitespace_ki d ( node ) ) :: Bool
end
for ( i , chil d) in pairs ( children )
if ( i == 1 || i == length ( children ) ) && JuliaSyntax . kind ( chil d) === K " Whitespace "
accept_node! ( ctx , chil d)
any_changes && push! ( children′ , chil d)
elseif JuliaSyntax . kind ( chil d) === K " Whitespace "
# Ignore it but need to copy children and re-write bytes
for ( i , ki d) in pairs ( kids )
if ( i == 1 || i == length ( kids ) ) && kind ( ki d) === K " Whitespace "
accept_node! ( ctx , ki d)
any_changes && push! ( kids′ , ki d)
elseif kind ( ki d) === K " Whitespace "
# Ignore it but need to copy kids and re-write bytes
any_changes = true
if children′ === children
children′ = children [ 1 : i - 1 ]
if kids′ === kids
kids′ = kids [ 1 : i - 1 ]
end
replace_bytes! ( ctx , " " , JuliaSyntax . span ( chil d) )
replace_bytes! ( ctx , " " , span ( ki d) )
else
@assert JuliaSyntax . kind ( chil d) !== K " Whitespace "
@assert kind ( ki d) !== K " Whitespace "
if looking_for_x
@assert is_x ( chil d) :: Bool
@assert is_x ( ki d) :: Bool
end
any_changes && push! ( children′ , chil d)
accept_node! ( ctx , chil d)
any_changes && push! ( kids′ , ki d)
accept_node! ( ctx , ki d)
looking_for_x = ! looking_for_x
end
end
@ -337,8 +337,8 @@ function no_spaces_around_x(ctx::Context, node::JuliaSyntax.GreenNode, is_x::F)
@@ -337,8 +337,8 @@ function no_spaces_around_x(ctx::Context, node::JuliaSyntax.GreenNode, is_x::F)
seek ( ctx . fmt_io , pos )
if any_changes
# Create new node and return it
node′ = make_node ( node , children ′)
@assert JuliaSyntax . span ( node′ ) < JuliaSyntax . span ( node )
node′ = make_node ( node , kids ′)
@assert span ( node′ ) < span ( node )
return node′
else
return nothing
@ -346,127 +346,127 @@ function no_spaces_around_x(ctx::Context, node::JuliaSyntax.GreenNode, is_x::F)
@@ -346,127 +346,127 @@ function no_spaces_around_x(ctx::Context, node::JuliaSyntax.GreenNode, is_x::F)
end
# no spaces around `:`, `^`, and `::`
function no_spaces_around_colon_etc ( ctx :: Context , node :: JuliaSyntax . Green Node)
function no_spaces_around_colon_etc ( ctx :: Context , node :: Node )
if ! (
( is_infix_op_call ( node ) && JuliaSyntax . kind ( infix_op_call_op ( node ) ) in KSet " : ^ " ) ||
( JuliaSyntax . kind ( node ) === K " :: " && ! is_leaf ( node ) ) ||
( JuliaSyntax . kind ( node ) in KSet " <: >: " && n_children ( node ) == 2 )
( is_infix_op_call ( node ) && kind ( infix_op_call_op ( node ) ) in KSet " : ^ " ) ||
( kind ( node ) === K " :: " && ! is_leaf ( node ) ) ||
( kind ( node ) in KSet " <: >: " && meta_nargs ( node ) == 2 )
)
return nothing
end
@assert JuliaSyntax . kind ( node ) in KSet " call :: <: >: "
is_x = x -> is_leaf ( x ) && JuliaSyntax . kind ( x ) in KSet " : ^ :: <: >: "
@assert kind ( node ) in KSet " call :: <: >: "
is_x = x -> is_leaf ( x ) && kind ( x ) in KSet " : ^ :: <: >: "
return no_spaces_around_x ( ctx , node , is_x )
end
# Replace the K"=" operator with `in`
function replace_with_in ( ctx :: Context , node :: JuliaSyntax . Green Node)
@assert JuliaSyntax . kind ( node ) === K " = " && ! is_leaf ( node ) && n_children ( node ) == 3
children = verified_children ( node )
vars_index = findfirst ( ! JuliaSyntax . is_whitespace , children )
function replace_with_in ( ctx :: Context , node :: Node )
@assert kind ( node ) === K " = " && ! is_leaf ( node ) && meta_nargs ( node ) == 3
kids = verified_kids ( node )
vars_index = findfirst ( ! JuliaSyntax . is_whitespace , kids )
# TODO: Need to insert whitespaces around `in` when replacing e.g. `i=I` with `iinI`.
# However, at the moment it looks like the whitespace around operator pass does it's
# thing first? I don't really know how though, because the for loop pass should be
# happening before...
in_index = findnext ( ! JuliaSyntax . is_whitespace , children , vars_index + 1 )
in_node = children [ in_index ]
if JuliaSyntax . kind ( in_node ) === K " in "
in_index = findnext ( ! JuliaSyntax . is_whitespace , kids , vars_index + 1 )
in_node = kids [ in_index ]
if kind ( in_node ) === K " in "
@assert JuliaSyntax . is_trivia ( in_node )
@assert is_leaf ( in_node )
return nothing
end
@assert JuliaSyntax . kind ( in_node ) in KSet " ∈ = "
@assert kind ( in_node ) in KSet " ∈ = "
@assert JuliaSyntax . is_trivia ( in_node )
@assert is_leaf ( in_node )
# Accept nodes to advance the stream
for i in 1 : ( in_index - 1 )
accept_node! ( ctx , children [ i ] )
accept_node! ( ctx , kids [ i ] )
end
# Construct the replacement
nb = replace_bytes! ( ctx , " in " , JuliaSyntax . span ( in_node ) )
in_node′ = JuliaSyntax . Green Node(
nb = replace_bytes! ( ctx , " in " , span ( in_node ) )
in_node′ = Node (
JuliaSyntax . SyntaxHead ( K " in " , JuliaSyntax . TRIVIA_FLAG ) , nb , ( ) ,
)
accept_node! ( ctx , in_node′ )
children ′ = copy ( children )
children ′[ in_index ] = in_node′
# Accept remaining eq_children
for i in ( in_index + 1 ) : length ( children ′)
accept_node! ( ctx , children ′[ i ] )
kids ′ = copy ( kids )
kids ′[ in_index ] = in_node′
# Accept remaining kids
for i in ( in_index + 1 ) : length ( kids ′)
accept_node! ( ctx , kids ′[ i ] )
end
return make_node ( node , children ′)
return make_node ( node , kids ′)
end
function replace_with_in_cartesian ( ctx :: Context , node :: JuliaSyntax . Green Node)
@assert JuliaSyntax . kind ( node ) === K " cartesian_iterator " && ! is_leaf ( node )
children = verified_children ( node )
children′ = children
for ( i , chil d) in pairs ( children )
if JuliaSyntax . kind ( chil d) === K " = "
chil d′ = replace_with_in ( ctx , chil d)
if chil d′ !== nothing
if children′ === children
children ′ = copy ( children )
function replace_with_in_cartesian ( ctx :: Context , node :: Node )
@assert kind ( node ) === K " cartesian_iterator " && ! is_leaf ( node )
kids = verified_kids ( node )
kids′ = kids
for ( i , ki d) in pairs ( kids )
if kind ( ki d) === K " = "
ki d′ = replace_with_in ( ctx , ki d)
if ki d′ !== nothing
if kids′ === kids
kids ′ = copy ( kids )
end
children ′[ i ] = chil d′
kids ′[ i ] = ki d′
else
children ′[ i ] = chil d
accept_node! ( ctx , chil d)
kids ′[ i ] = ki d
accept_node! ( ctx , ki d)
end
else
children ′[ i ] = chil d
accept_node! ( ctx , chil d)
kids ′[ i ] = ki d
accept_node! ( ctx , ki d)
end
end
if children === children ′
if kids === kids ′
return nothing
end
return make_node ( node , children ′)
return make_node ( node , kids ′)
end
# replace `=` and `∈` with `in` in for-loops
function for_loop_use_in ( ctx :: Context , node :: JuliaSyntax . Green Node)
function for_loop_use_in ( ctx :: Context , node :: Node )
if ! (
( JuliaSyntax . kind ( node ) === K " for " && ! is_leaf ( node ) && n_children ( node ) == 4 ) ||
( JuliaSyntax . kind ( node ) === K " generator " && n_children ( node ) == 3 ) # TODO: Unsure about 3.
( kind ( node ) === K " for " && ! is_leaf ( node ) && meta_nargs ( node ) == 4 ) ||
( kind ( node ) === K " generator " && meta_nargs ( node ) == 3 ) # TODO: Unsure about 3.
)
return nothing
end
pos = position ( ctx . fmt_io )
children = verified_children ( node )
for_index = findfirst ( c -> JuliaSyntax . kind ( c ) === K " for " && is_leaf ( c ) , children ) :: Int
for_node = children [ for_index ]
@assert JuliaSyntax . kind ( for_node ) === K " for " && JuliaSyntax . span ( for_node ) == 3 &&
kids = verified_kids ( node )
for_index = findfirst ( c -> kind ( c ) === K " for " && is_leaf ( c ) , kids ) :: Int
for_node = kids [ for_index ]
@assert kind ( for_node ) === K " for " && span ( for_node ) == 3 &&
is_leaf ( for_node ) && JuliaSyntax . is_trivia ( for_node )
for i in 1 : for_index
accept_node! ( ctx , children [ i ] )
accept_node! ( ctx , kids [ i ] )
end
# The for loop specification node can be either K"=" or K"cartesian_iterator"
for_spec_index = for_index + 1
for_spec_node = children [ for_spec_index ]
@assert JuliaSyntax . kind ( for_spec_node ) in KSet " = cartesian_iterator "
if JuliaSyntax . kind ( for_spec_node ) === K " = "
for_spec_node = kids [ for_spec_index ]
@assert kind ( for_spec_node ) in KSet " = cartesian_iterator "
if kind ( for_spec_node ) === K " = "
for_spec_node′ = replace_with_in ( ctx , for_spec_node )
else
@assert JuliaSyntax . kind ( for_spec_node ) === K " cartesian_iterator "
@assert kind ( for_spec_node ) === K " cartesian_iterator "
for_spec_node′ = replace_with_in_cartesian ( ctx , for_spec_node )
end
if for_spec_node′ === nothing
seek ( ctx . fmt_io , pos )
return nothing
end
@assert position ( ctx . fmt_io ) == pos + mapreduce ( JuliaSyntax . span , + , @view ( children [ 1 : for_index ] ) ) + JuliaSyntax . span ( for_spec_node′ )
@assert position ( ctx . fmt_io ) == pos + mapreduce ( span , + , @view ( kids [ 1 : for_index ] ) ) + span ( for_spec_node′ )
# Insert the new for spec node
children ′ = copy ( children )
children ′[ for_spec_index ] = for_spec_node′
kids ′ = copy ( kids )
kids ′[ for_spec_index ] = for_spec_node′
# At this point the eq node is done, just accept any remaining nodes
# TODO: Don't need to do this...
for i in ( for_spec_index + 1 ) : length ( children ′)
accept_node! ( ctx , children ′[ i ] )
for i in ( for_spec_index + 1 ) : length ( kids ′)
accept_node! ( ctx , kids ′[ i ] )
end
# Construct the full node and return
node′ = make_node ( node , children ′)
@assert position ( ctx . fmt_io ) == pos + JuliaSyntax . span ( node′ )
node′ = make_node ( node , kids ′)
@assert position ( ctx . fmt_io ) == pos + span ( node′ )
seek ( ctx . fmt_io , pos ) # reset
return node′
end