Skip to content

Commit

Permalink
update readme + make sure single args don't nest parens,braces,etc
Browse files Browse the repository at this point in the history
  • Loading branch information
domluna committed Apr 8, 2019
1 parent 1987e79 commit 4e6b9e0
Show file tree
Hide file tree
Showing 7 changed files with 548 additions and 51 deletions.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
*.jl
108 changes: 100 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,20 @@ Width-sensitive formatter for Julia code. Inspired by gofmt and refmt.
`JLFmt` exports a singular function:

```julia
format(text::String; indent_width=4, max_width=80)
format(text::String; indent_size=4, print_width=80)
```

* `indent_width` - the number of spaces used for an indentation.
* `indent_size` - the number of spaces used for an indentation.
* `print_width` - the maximum number of characters of code on a single line. Lines over
the limit will be nested if possible.

### How It Works
## How It Works

`JLFmt` parses the Julia file using [`CSTParser`](https://github.com/ZacLN/CSTParser.jl).
`JLFmt` parses the `.jl` source file into a Concrete Syntax Tree (CST) using [`CSTParser`](https://github.com/ZacLN/CSTParser.jl).

Next the CST is _prettified_ using `pretty`, creating a `PTree`. The printing output of a `PTree` is a canonical representation of the code removing unnecessary whitespace and joining or separating lines of code. The [`pretty` testset](./test/runtests.jl) displays these transformations.
### Pass 1: Prettify

The CST is _prettified_ using `pretty`, creating a `PTree`. The printing output of a `PTree` is a canonical representation of the code removing unnecessary whitespace and joining or separating lines of code. The [`pretty` testset](./test/runtests.jl) displays these transformations.

Example 1:

Expand Down Expand Up @@ -56,12 +58,102 @@ for cond
end
```

Once the `PTree` is created it's then nested using `nest!` to disjoin lines going over the
maximum width. The `PTree` is modified in-place. All expressions are nested front to back with the exception of binary operations, where operations, and conditionals.
### Pass 2: Nesting

`PTree` is nested using `nest!` to disjoin lines going over the print width.
The `PTree` is modified in-place. All expressions are nested front to back with the exception of binary
operations and conditionals.

**Binary Ops**

```julia
arg1 && arg2

->

arg1 &&
arg2
```

```julia
foo() = body

->

# The indentation of the body is based on `indent_size`
foo() =
body
```

**Conditionals**

```julia
cond ? e1 : e2

->

cond ? e1 :
e2

->

cond ?
e1 :
e2
```

**Calls** (also applies to {}, (), [], etc)

```julia
f(arg1, arg2, arg3)

->

f(
arg1,
arg2,
arg3
)
```

```julia
function longfunctionname_that_is_long(lots, of, args, even, more, args)
..code..
end

->

# The arg is placed `indent_size` spaces after the start of the
# function name or one space after the opening parenthesis.
function longfunctionname_that_is_long(
lots,
of,
args,
even,
more,
args
)
..code..
end
```

```julia
S <: Union{Type1,Type2,Type3}

->

S <: Union{
Type1,
Type2,
Type3
}
```

### Part 3: Printing

Finally, the `PTree` to an `IOBuffer` with `print_tree` which is then consumed as a `String`.

### Known Limitations
### Known Limitation(s)

If a comment is at the end of a line of code it will be removed.

Expand Down
4 changes: 2 additions & 2 deletions src/JLFmt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ function file_line_ranges(text::String)
push!(ranges, s:offset+nl)
end
elseif t.kind == Tokens.COMMENT
#= @info "comment token" t =#
# @info "comment token" t
end

if (t.kind == Tokens.TRIPLE_STRING || t.kind == Tokens.STRING)
Expand All @@ -45,7 +45,7 @@ struct Document
# mapping the offset in the file to the raw literal
# string and what lines it starts and ends at.
lit_strings::Dict{Int, Tuple{Int, Int, String}}
#= inline_commments::Vector{LitString} =#
# inline_commments::Vector{LitString}
end
Document(s::String) = Document(s, file_line_ranges(s)...)

Expand Down
23 changes: 3 additions & 20 deletions src/nest.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# This is done by replacing `Placeholder` nodes with `Newline`
# nodes and updating the PTree's indent.
#
# `extra_width` provides additional length to consider from
# `extra_width` provides additional width to consider from
# the top-level node.
#
# Example:
Expand Down Expand Up @@ -40,14 +40,15 @@ function nest!(x::PTree, s::State; extra_width=0)
s.line_offset = x.nodes[i+1].indent
elseif n === newline
s.line_offset = x.indent
elseif n isa NotCode && x.nodes[i+1] isa PTree{CSTParser.EXPR{CSTParser.Block}}
s.line_offset = x.nodes[i+1].indent
else
nest!(n, s, extra_width=extra_width)
end
end
end

function nest!(x::PTree{CSTParser.EXPR{T}}, s::State; extra_width=0) where T <: Union{CSTParser.Import,CSTParser.Using,CSTParser.Export}
#= @info "ENTER" typeof(x) s.line_offset x =#
line_width = s.line_offset + length(x) + extra_width
idx = findfirst(n -> is_placeholder(n), x.nodes)
if idx !== nothing && line_width > s.print_width
Expand Down Expand Up @@ -76,7 +77,6 @@ function nest!(x::PTree{CSTParser.EXPR{T}}, s::State; extra_width=0) where T <:
end
end
end
#= @info "EXIT" typeof(x) s.line_offset x =#
end

# TODO: skip nesting anything in InvisBrackets?
Expand All @@ -86,7 +86,6 @@ end
function nest!(x::PTree{CSTParser.EXPR{T}}, s::State; extra_width=0) where T <: Union{CSTParser.TupleH,CSTParser.Vect,CSTParser.Braces,CSTParser.Parameters}
line_width = s.line_offset + length(x) + extra_width
idx = findlast(n -> is_placeholder(n), x.nodes)
#= @info "ENTER" typeof(x) s.line_offset line_width =#
if idx !== nothing && line_width > s.print_width
line_offset = s.line_offset
x.indent = is_opener(x.nodes[1]) ? s.line_offset + 1 : s.line_offset
Expand Down Expand Up @@ -115,21 +114,12 @@ end
function nest!(x::PTree{CSTParser.EXPR{T}}, s::State; extra_width=0) where T <: Union{CSTParser.Curly,CSTParser.Call}
line_width = s.line_offset + length(x) + extra_width
idx = findlast(n -> is_placeholder(n), x.nodes)
# @info "ENTER" typeof(x) s.line_offset line_width extra_width
if idx !== nothing && line_width > s.print_width
line_offset = s.line_offset
name_width = length(x.nodes[1]) + length(x.nodes[2])
x.indent = s.line_offset + min(name_width, s.indent_size)

for (i, n) in enumerate(x.nodes)
# closing punctuation should be aligned with the
# call name:
#
# foo(
# a,
# b,
# c
# )
if n === newline
s.line_offset = x.indent
elseif is_placeholder(n)
Expand All @@ -154,7 +144,6 @@ end
function nest!(x::PTree{CSTParser.EXPR{CSTParser.MacroCall}}, s::State; extra_width=0)
line_width = s.line_offset + length(x) + extra_width
idx = findlast(n -> is_placeholder(n), x.nodes)
#= @info "ENTER" typeof(x) s.line_offset line_width extra_width =#
if idx !== nothing && line_width > s.print_width && !(x.nodes[1] isa PTree{CSTParser.EXPR{CSTParser.GlobalRefDoc}})
line_offset = s.line_offset
name_width = length(x.nodes[1]) + length(x.nodes[2])
Expand Down Expand Up @@ -184,13 +173,11 @@ function nest!(x::PTree{CSTParser.EXPR{CSTParser.MacroCall}}, s::State; extra_wi
end

function nest!(x::PTree{CSTParser.WhereOpCall}, s::State; extra_width=0)
#= @info "ENTER" typeof(x) s.line_offset x =#
line_width = s.line_offset + length(x) + extra_width
if line_width > s.print_width
line_offset = s.line_offset
# after "A where "
idx = findfirst(n -> is_placeholder(n), x.nodes)
#= Blens = length.(x.nodes[idx+1:end]) =#
Blens = remaining_lengths(x.nodes[idx+1:end])

# A, +7 is the length of " where "
Expand All @@ -206,7 +193,6 @@ function nest!(x::PTree{CSTParser.WhereOpCall}, s::State; extra_width=0)
# where B
over = s.line_offset + Blens[1] > s.print_width
for (i, n) in enumerate(x.nodes[idx+1:end])
@info "" s.line_offset n
if n === newline
s.line_offset = x.indent
elseif is_placeholder(n) && over
Expand All @@ -228,7 +214,6 @@ function nest!(x::PTree{CSTParser.WhereOpCall}, s::State; extra_width=0)
end
end
end
#= @info "EXIT" typeof(x) s.line_offset =#
end

# C ? E1 : E2
Expand All @@ -242,7 +227,6 @@ end
# E1 :
# E2
function nest!(x::PTree{CSTParser.ConditionalOpCall}, s::State; extra_width=0)
#= @info "ENTER" typeof(x) s.line_offset =#
line_width = s.line_offset + length(x) + extra_width
if line_width > s.print_width
idx1 = findfirst(n -> is_placeholder(n), x.nodes)
Expand Down Expand Up @@ -296,7 +280,6 @@ end
# arg2
function nest!(x::PTree{T}, s::State; extra_width=0) where T <: Union{CSTParser.BinaryOpCall,CSTParser.BinarySyntaxOpCall}
# If there's no placeholder the binary call is not nestable
#= @info "ENTER" typeof(x) s.line_offset x.indent length(x) =#
idx = findlast(n -> is_placeholder(n), x.nodes)
line_width = s.line_offset + length(x) + extra_width
if idx !== nothing && line_width > s.print_width
Expand Down
29 changes: 13 additions & 16 deletions src/pretty.jl
Original file line number Diff line number Diff line change
Expand Up @@ -213,17 +213,13 @@ function pretty(x::CSTParser.LITERAL, s::State; include_quotes=true)

startline, endline, str = s.doc.lit_strings[s.offset-1]
s.offset += x.fullspan
#= @info "" startline endline include_quotes str =#

if !include_quotes
idx = startswith(str, "\"\"\"") ? 4 : 2
str = str[idx:end-idx+1]
#= @info "after removing quotes" str =#
str = strip(str, ' ')
#= @info "after stripping surrounding whitespace" str =#
str[1] == '\n' && (str = str[2:end])
str[end] == '\n' && (str = str[1:end-1])
#= @info "after removing extra newlines" str =#
end

lines = split(str, "\n")
Expand All @@ -239,17 +235,13 @@ end
function pretty(x::CSTParser.EXPR{CSTParser.StringH}, s::State; include_quotes=true)
startline, endline, str = s.doc.lit_strings[s.offset-1]
s.offset += x.fullspan
#= @info "" startline endline include_quotes str =#

if !include_quotes
idx = startswith(str, "\"\"\"") ? 4 : 2
str = str[idx:end-idx+1]
#= @info "after removing quotes" str =#
str = strip(str, ' ')
#= @info "after stripping surrounding whitespace" str =#
str[1] == '\n' && (str = str[2:end])
str[end] == '\n' && (str = str[1:end-1])
#= @info "after removing extra newlines" str =#
end

lines = split(str, "\n")
Expand Down Expand Up @@ -636,12 +628,13 @@ function pretty(x::CSTParser.WhereOpCall, s::State)
# Used to mark where `B` starts.
add_node!(t, placeholder)

multi_arg = length(CSTParser.get_where_params(x)) > 1
for a in x.args
n = pretty(a, s)
if is_opener(n)
if is_opener(n) && multi_arg
add_node!(t, n, join_lines=true)
add_node!(t, placeholder)
elseif is_closer(n)
elseif is_closer(n) && multi_arg
add_node!(t, placeholder)
add_node!(t, n, join_lines=true)
elseif CSTParser.is_comma(a)
Expand Down Expand Up @@ -690,11 +683,15 @@ function pretty(x::CSTParser.EXPR{T}, s::State) where T <: Union{CSTParser.Curly
add_node!(t, pretty(x.args[2], s), join_lines=true)

curly = x isa CSTParser.EXPR{CSTParser.Curly}
no_args = length(x.args) < 4
if curly
multi_arg = length(CSTParser.get_curly_params(x)) > 1
else
multi_arg = length(CSTParser.get_args(x)) > 1
end

!no_args && (add_node!(t, placeholder))
multi_arg && (add_node!(t, placeholder))
for (i, a) in enumerate(x.args[3:end])
if i + 2 == length(x) && !no_args
if i + 2 == length(x) && multi_arg
add_node!(t, placeholder)
add_node!(t, pretty(a, s), join_lines=true)
elseif CSTParser.is_comma(a) && i < length(x) - 3 && !(x.args[i+1] isa CSTParser.PUNCTUATION)
Expand All @@ -718,14 +715,14 @@ end
function pretty(x::CSTParser.EXPR{T}, s::State) where T <: Union{CSTParser.TupleH,CSTParser.Braces,CSTParser.Vect}
t = PTree(x, nspaces(s))
braces = CSTParser.is_lbrace(x.args[1])
no_args = length(x.args) < 3
multi_arg = length(x.args) > 4

for (i, a) in enumerate(x)
n = pretty(a, s)
if is_opener(n) && !no_args
if is_opener(n) && multi_arg
add_node!(t, n, join_lines=true)
add_node!(t, placeholder)
elseif is_closer(n) && !no_args
elseif is_closer(n) && multi_arg
add_node!(t, placeholder)
add_node!(t, n, join_lines=true)
elseif CSTParser.is_comma(a) && i < length(x) && !(x.args[i+1] isa CSTParser.PUNCTUATION)
Expand Down
Loading

0 comments on commit 4e6b9e0

Please sign in to comment.