xref: /libCEED/julia/LibCEED.jl/.style/ceed_style.jl (revision 2d2e582703ea5bfc3df79438fef1ecdd98bea2ca)
1using JuliaFormatter, CSTParser, Tokenize
2
3for name in names(JuliaFormatter, all=true)
4    if name != :include && name != :eval
5        @eval import JuliaFormatter: $name
6    end
7end
8
9# Same as DefaultStyle, but no space in between operators with precedence CSTParser.TimesOp
10struct CeedStyle <: AbstractStyle end
11
12getstyle(s::CeedStyle) = s
13
14function p_binaryopcall(
15    style::CeedStyle,
16    cst::CSTParser.EXPR,
17    s::State;
18    nonest=false,
19    nospace=false,
20)
21    t = FST(cst, nspaces(s))
22    op = cst[2]
23    nonest = nonest || op.kind === Tokens.COLON
24    if cst.parent.typ === CSTParser.Curly &&
25       op.kind in (Tokens.ISSUBTYPE, Tokens.ISSUPERTYPE) &&
26       !s.opts.whitespace_typedefs
27        nospace = true
28    elseif op.kind === Tokens.COLON
29        nospace = true
30    end
31    nospace_args = s.opts.whitespace_ops_in_indices ? false : nospace
32
33    if is_opcall(cst[1])
34        n = pretty(style, cst[1], s, nonest=nonest, nospace=nospace_args)
35    else
36        n = pretty(style, cst[1], s)
37    end
38
39    if op.kind === Tokens.COLON &&
40       s.opts.whitespace_ops_in_indices &&
41       !is_leaf(cst[1]) &&
42       !is_iterable(cst[1])
43        paren = FST(CSTParser.PUNCTUATION, -1, n.startline, n.startline, "(")
44        add_node!(t, paren, s)
45        add_node!(t, n, s, join_lines=true)
46        paren = FST(CSTParser.PUNCTUATION, -1, n.startline, n.startline, ")")
47        add_node!(t, paren, s, join_lines=true)
48    else
49        add_node!(t, n, s)
50    end
51
52    nrhs = nest_rhs(cst)
53    nrhs && (t.nest_behavior = AlwaysNest)
54    nest = (nestable(style, cst) && !nonest) || nrhs
55
56    if op.fullspan == 0
57        # Do nothing - represents a binary op with no textual representation.
58        # For example: `2a`, which is equivalent to `2 * a`.
59    elseif op.kind === Tokens.EX_OR
60        add_node!(t, Whitespace(1), s)
61        add_node!(t, pretty(style, op, s), s, join_lines=true)
62    elseif (is_number(cst[1]) || op.kind === Tokens.CIRCUMFLEX_ACCENT) && op.dot
63        add_node!(t, Whitespace(1), s)
64        add_node!(t, pretty(style, op, s), s, join_lines=true)
65        nest ? add_node!(t, Placeholder(1), s) : add_node!(t, Whitespace(1), s)
66    elseif op.kind !== Tokens.IN && (
67        nospace || (
68            op.kind !== Tokens.ANON_FUNC && CSTParser.precedence(op) in (
69                CSTParser.ColonOp,
70                CSTParser.PowerOp,
71                CSTParser.DeclarationOp,
72                CSTParser.DotOp,
73                CSTParser.TimesOp,
74            )
75        )
76    )
77        add_node!(t, pretty(style, op, s), s, join_lines=true)
78    else
79        add_node!(t, Whitespace(1), s)
80        add_node!(t, pretty(style, op, s), s, join_lines=true)
81        nest ? add_node!(t, Placeholder(1), s) : add_node!(t, Whitespace(1), s)
82    end
83
84    if is_opcall(cst[3])
85        n = pretty(style, cst[3], s, nonest=nonest, nospace=nospace_args)
86    else
87        n = pretty(style, cst[3], s)
88    end
89
90    if op.kind === Tokens.COLON &&
91       s.opts.whitespace_ops_in_indices &&
92       !is_leaf(cst[3]) &&
93       !is_iterable(cst[3])
94        paren = FST(CSTParser.PUNCTUATION, -1, n.startline, n.startline, "(")
95        add_node!(t, paren, s, join_lines=true)
96        add_node!(t, n, s, join_lines=true)
97        paren = FST(CSTParser.PUNCTUATION, -1, n.startline, n.startline, ")")
98        add_node!(t, paren, s, join_lines=true)
99    else
100        add_node!(t, n, s, join_lines=true)
101    end
102
103    if nest
104        # for indent, will be converted to `indent` if needed
105        insert!(t.nodes, length(t.nodes), Placeholder(0))
106    end
107
108    t
109end
110
111function p_chainopcall(
112    style::CeedStyle,
113    cst::CSTParser.EXPR,
114    s::State;
115    nonest=false,
116    nospace=false,
117)
118    t = FST(cst, nspaces(s))
119
120    # Check if there's a number literal on the LHS of a dot operator.
121    # In this case we need to surround the dot operator with whitespace
122    # in order to avoid ambiguity.
123    for (i, a) in enumerate(cst)
124        if a.typ === CSTParser.OPERATOR && a.dot && is_number(cst[i-1])
125            nospace = false
126            break
127        end
128    end
129
130    nws = nospace ? 0 : 1
131    for (i, a) in enumerate(cst)
132        if a.typ === CSTParser.OPERATOR
133            nws_op = (CSTParser.precedence(a) == CSTParser.TimesOp) ? 0 : nws
134            add_node!(t, Whitespace(nws_op), s)
135            add_node!(t, pretty(style, a, s), s, join_lines=true)
136            if nonest
137                add_node!(t, Whitespace(nws_op), s)
138            else
139                add_node!(t, Placeholder(nws_op), s)
140            end
141        elseif is_opcall(a)
142            add_node!(
143                t,
144                pretty(style, a, s, nospace=nospace, nonest=nonest),
145                s,
146                join_lines=true,
147            )
148        elseif i == length(cst) - 1 && is_punc(a) && is_punc(cst[i+1])
149            add_node!(t, pretty(style, a, s), s, join_lines=true)
150        else
151            add_node!(t, pretty(style, a, s), s, join_lines=true)
152        end
153    end
154    t
155end
156
157prefix_path(fname) = joinpath(@__DIR__, "..", fname)
158format(
159    prefix_path.(["src", "test", "examples", ".style"]),
160    style=CeedStyle(),
161    indent=4,
162    margin=92,
163    remove_extra_newlines=true,
164    whitespace_in_kwargs=false,
165)
166