1
1
module JuliaSyntaxHighlighting
2
2
3
3
import Base: JuliaSyntax, AnnotatedString, annotate!
4
- import Base. JuliaSyntax: var" @K_str" , Kind, GreenNode, parseall, kind, flags
4
+ import Base. JuliaSyntax: @K_str , Kind, GreenNode, parseall, kind, flags
5
5
using StyledStrings: Face, addface!
6
6
7
7
public highlight, highlight!
@@ -10,46 +10,57 @@ const MAX_PAREN_HIGHLIGHT_DEPTH = 6
10
10
const RAINBOW_DELIMITERS_ENABLED = Ref (true )
11
11
const UNMATCHED_DELIMITERS_ENABLED = Ref (true )
12
12
13
- const SINGLETON_IDENTIFIERS = (" nothing" , " missing" )
13
+ const SINGLETON_IDENTIFIERS = (:nothing , :missing )
14
+
15
+ const BASE_TYPE_IDENTIFIERS =
16
+ Set ([n for n in names (Base, imported= true ) if getglobal (Base, n) isa Type]) ∪
17
+ Set ([n for n in names (Core, imported= true ) if getglobal (Core, n) isa Type])
18
+
19
+ const BUILTIN_FUNCTIONS =
20
+ Set ([n for n in names (Core) if getglobal (Base, n) isa Core. Builtin])
14
21
15
22
const HIGHLIGHT_FACES = [
16
23
# Julia syntax highlighting faces
17
- :julia_identifier => Face (foreground= :bright_white ),
18
- :julia_singleton_identifier => Face (inherit= :julia_symbol ),
19
24
:julia_macro => Face (foreground= :magenta ),
20
25
:julia_symbol => Face (foreground= :magenta ),
26
+ :julia_singleton_identifier => Face (inherit= :julia_symbol ),
21
27
:julia_type => Face (foreground= :yellow ),
28
+ :julia_typedec => Face (foreground= :bright_blue ),
22
29
:julia_comment => Face (foreground= :grey ),
23
30
:julia_string => Face (foreground= :green ),
31
+ :julia_regex => Face (inherit= :julia_string ),
32
+ :julia_backslash_literal => Face (foreground= :magenta , inherit= :julia_string ),
24
33
:julia_string_delim => Face (foreground= :bright_green ),
25
34
:julia_cmdstring => Face (inherit= :julia_string ),
26
35
:julia_char => Face (inherit= :julia_string ),
27
36
:julia_char_delim => Face (inherit= :julia_string_delim ),
28
- :julia_number => Face (foreground= :bright_red ),
29
- :julia_bool => Face (foreground = :bright_red ),
37
+ :julia_number => Face (foreground= :bright_magenta ),
38
+ :julia_bool => Face (inherit = :julia_number ),
30
39
:julia_funcall => Face (foreground= :cyan ),
31
- :julia_operator => Face (foreground= :cyan ),
32
- :julia_comparator => Face (foreground= :yellow ),
33
- :julia_assignment => Face (foreground= :bright_blue ),
40
+ :julia_broadcast => Face (foreground= :bright_blue , weight= :bold ),
41
+ :julia_builtin => Face (foreground= :bright_blue ),
42
+ :julia_operator => Face (foreground= :blue ),
43
+ :julia_comparator => Face (inherit = :julia_operator ),
44
+ :julia_assignment => Face (foreground= :bright_red ),
34
45
:julia_keyword => Face (foreground= :red ),
35
- :julia_error => Face (background= :red ),
36
46
:julia_parenthetical => Face (),
37
- :julia_unpaired_parenthetical => Face (inherit= :julia_error ),
47
+ :julia_unpaired_parenthetical => Face (inherit= [:julia_error , :julia_parenthetical ]),
48
+ :julia_error => Face (background= :red ),
38
49
# Rainbow delimitors (1-6, (), [], and {})
39
- :julia_rainbow_paren_1 => Face (foreground= :bright_green ),
40
- :julia_rainbow_paren_2 => Face (foreground= :bright_blue ),
41
- :julia_rainbow_paren_3 => Face (foreground= :bright_red ),
50
+ :julia_rainbow_paren_1 => Face (foreground= :bright_green , inherit = :julia_parenthetical ),
51
+ :julia_rainbow_paren_2 => Face (foreground= :bright_blue , inherit = :julia_parenthetical ),
52
+ :julia_rainbow_paren_3 => Face (foreground= :bright_red , inherit = :julia_parenthetical ),
42
53
:julia_rainbow_paren_4 => Face (inherit= :julia_rainbow_paren_1 ),
43
54
:julia_rainbow_paren_5 => Face (inherit= :julia_rainbow_paren_2 ),
44
55
:julia_rainbow_paren_6 => Face (inherit= :julia_rainbow_paren_3 ),
45
- :julia_rainbow_bracket_1 => Face (foreground= :blue ),
46
- :julia_rainbow_bracket_2 => Face (foreground= :bright_magenta ),
56
+ :julia_rainbow_bracket_1 => Face (foreground= :blue , inherit = :julia_parenthetical ),
57
+ :julia_rainbow_bracket_2 => Face (foreground= :bright_magenta , inherit = :julia_parenthetical ),
47
58
:julia_rainbow_bracket_3 => Face (inherit= :julia_rainbow_bracket_1 ),
48
59
:julia_rainbow_bracket_4 => Face (inherit= :julia_rainbow_bracket_2 ),
49
60
:julia_rainbow_bracket_5 => Face (inherit= :julia_rainbow_bracket_1 ),
50
61
:julia_rainbow_bracket_6 => Face (inherit= :julia_rainbow_bracket_2 ),
51
- :julia_rainbow_curly_1 => Face (foreground= :bright_yellow ),
52
- :julia_rainbow_curly_2 => Face (foreground= :yellow ),
62
+ :julia_rainbow_curly_1 => Face (foreground= :bright_yellow , inherit = :julia_parenthetical ),
63
+ :julia_rainbow_curly_2 => Face (foreground= :yellow , inherit = :julia_parenthetical ),
53
64
:julia_rainbow_curly_3 => Face (inherit= :julia_rainbow_curly_1 ),
54
65
:julia_rainbow_curly_4 => Face (inherit= :julia_rainbow_curly_2 ),
55
66
:julia_rainbow_curly_5 => Face (inherit= :julia_rainbow_curly_1 ),
@@ -87,52 +98,68 @@ struct HighlightContext{S <: AbstractString}
87
98
content:: S
88
99
offset:: Int
89
100
lnode:: GreenNode
90
- llnode:: GreenNode
91
101
pdepths:: ParenDepthCounter
92
102
end
93
103
94
104
function _hl_annotations (content:: AbstractString , ast:: GreenNode )
95
105
highlights = Vector {Tuple{UnitRange{Int}, Pair{Symbol, Any}}} ()
96
- ctx = HighlightContext (content, 0 , ast, ast, ParenDepthCounter ())
106
+ ctx = HighlightContext (content, 0 , ast, ParenDepthCounter ())
97
107
_hl_annotations! (highlights, GreenLineage (ast, nothing ), ctx)
98
108
highlights
99
109
end
100
110
101
111
function _hl_annotations! (highlights:: Vector{Tuple{UnitRange{Int}, Pair{Symbol, Any}}} ,
102
112
lineage:: GreenLineage , ctx:: HighlightContext )
103
113
(; node, parent) = lineage
104
- (; content, offset, lnode, llnode, pdepths) = ctx
114
+ (; content, offset, lnode, pdepths) = ctx
105
115
region = firstindex (content)+ offset: node. span+ offset
106
116
nkind = node. head. kind
107
117
pnode = if ! isnothing (parent) parent. node end
108
118
pkind = if ! isnothing (parent) kind (parent. node) end
119
+ ppkind = if ! isnothing (parent) && ! isnothing (parent. parent)
120
+ kind (parent. parent. node) end
121
+ isplainoperator (node) =
122
+ JuliaSyntax. is_operator (node) &&
123
+ ! JuliaSyntax. is_trivia (node) &&
124
+ ! JuliaSyntax. is_prec_assignment (node) &&
125
+ ! JuliaSyntax. is_word_operator (node) &&
126
+ nkind != K " ." && nkind != K " ..." &&
127
+ (JuliaSyntax. is_trivia (node) || ! JuliaSyntax. haschildren (node))
109
128
face = if nkind == K " Identifier"
110
- if pkind == K ":: " && JuliaSyntax . is_trivia (pnode)
129
+ if pkind == K "curly "
111
130
:julia_type
112
- elseif pkind == K " curly" && kind (lnode) == K " curly" && ! isnothing (parent. parent) && kind (parent. parent. node) == K " call"
113
- :julia_identifier
114
- elseif pkind == K " curly"
115
- :julia_type
116
- elseif pkind == K " braces" && lnode != pnode
117
- :julia_type
118
- elseif kind (lnode) == K " ::" && JuliaSyntax. is_trivia (lnode)
119
- :julia_type
120
- elseif kind (lnode) == K " :" && ! JuliaSyntax. is_number (llnode) &&
121
- kind (llnode) ∉ (K " Identifier" , K " )" , K " ]" , K " end" , K " '" )
122
- highlights[end ] = (highlights[end ][1 ], :face => :julia_symbol )
123
- :julia_symbol
124
- elseif view (content, region) in SINGLETON_IDENTIFIERS
125
- :julia_singleton_identifier
126
- elseif view (content, region) == " NaN"
127
- :julia_number
128
131
else
129
- :julia_identifier
132
+ name = Symbol (view (content, region))
133
+ if name in SINGLETON_IDENTIFIERS
134
+ :julia_singleton_identifier
135
+ elseif name == :NaN
136
+ :julia_number
137
+ elseif name in BASE_TYPE_IDENTIFIERS
138
+ :julia_type
139
+ end
130
140
end
131
- elseif nkind == K " @" ; :julia_macro
132
- elseif nkind == K " MacroName" ; :julia_macro
141
+ elseif nkind == K " macrocall" && length (node. args) >= 2 &&
142
+ kind (node. args[1 ]) == K " @" && kind (node. args[2 ]) == K " MacroName"
143
+ region = first (region): first (region)+ node. args[2 ]. span
144
+ :julia_macro
133
145
elseif nkind == K " StringMacroName" ; :julia_macro
134
146
elseif nkind == K " CmdMacroName" ; :julia_macro
135
- elseif nkind == K " ::" ; :julia_type
147
+ elseif nkind == K " ::" ;
148
+ if JuliaSyntax. is_trivia (node)
149
+ :julia_typedec
150
+ else
151
+ literal_typedecl = findfirst (
152
+ c -> kind (c) == K " ::" && JuliaSyntax. is_trivia (c),
153
+ node. args)
154
+ if ! isnothing (literal_typedecl)
155
+ shift = sum (c -> Int (c. span), node. args[1 : literal_typedecl])
156
+ region = first (region)+ shift: last (region)
157
+ :julia_type
158
+ end
159
+ end
160
+ elseif nkind == K " quote" && length (node. args) == 2 &&
161
+ kind (node. args[1 ]) == K " :" && kind (node. args[2 ]) == K " Identifier"
162
+ :julia_symbol
136
163
elseif nkind == K " Comment" ; :julia_comment
137
164
elseif nkind == K " String" ; :julia_string
138
165
elseif JuliaSyntax. is_string_delim (node); :julia_string_delim
@@ -146,22 +173,65 @@ function _hl_annotations!(highlights::Vector{Tuple{UnitRange{Int}, Pair{Symbol,
146
173
elseif nkind == K " true" || nkind == K " false" ; :julia_bool
147
174
elseif JuliaSyntax. is_number (nkind); :julia_number
148
175
elseif JuliaSyntax. is_prec_assignment (nkind) && JuliaSyntax. is_trivia (node);
149
- :julia_assignment
150
- elseif JuliaSyntax. is_word_operator (nkind) && JuliaSyntax. is_trivia (node);
151
- :julia_assignment
176
+ if nkind == K " ="
177
+ ifelse (ppkind == K " for" , :julia_keyword , :julia_assignment )
178
+ else # updating for <op>=
179
+ push! (highlights, (firstindex (content)+ offset: node. span+ offset- 1 , :face => :julia_operator ))
180
+ push! (highlights, (node. span+ offset: node. span+ offset, :face => :julia_assignment ))
181
+ nothing
182
+ end
152
183
elseif nkind == K " ;" && pkind == K " parameters" && pnode == lnode
153
184
:julia_assignment
154
- elseif JuliaSyntax. is_prec_comparison (nkind); :julia_comparator
155
- elseif JuliaSyntax. is_operator (nkind) && ! JuliaSyntax. is_prec_assignment (nkind) &&
156
- ! JuliaSyntax. is_word_operator (nkind) && nkind != K " ." &&
157
- (JuliaSyntax. is_trivia (node) || iszero (flags (node)));
158
- :julia_operator
159
- elseif JuliaSyntax. is_keyword (nkind) && JuliaSyntax. is_trivia (node); :julia_keyword
185
+ elseif (JuliaSyntax. is_keyword (nkind) || nkind == K " ->" ) && JuliaSyntax. is_trivia (node)
186
+ :julia_keyword
187
+ elseif nkind == K " where"
188
+ if JuliaSyntax. is_trivia (node)
189
+ :julia_keyword
190
+ else
191
+ literal_where = findfirst (
192
+ c -> kind (c) == K " where" && JuliaSyntax. is_trivia (c),
193
+ node. args)
194
+ if ! isnothing (literal_where)
195
+ shift = sum (c -> Int (c. span), node. args[1 : literal_where])
196
+ region = first (region)+ shift: last (region)
197
+ :julia_type
198
+ end
199
+ end
200
+ elseif nkind == K " in"
201
+ ifelse (ppkind == K " for" , :julia_keyword , :julia_comparator )
202
+ elseif nkind == K " isa" ; :julia_builtin
203
+ elseif nkind in (K " &&" , K " ||" , K " <:" , K " ===" ) && JuliaSyntax. is_trivia (node)
204
+ :julia_builtin
205
+ elseif JuliaSyntax. is_prec_comparison (nkind) && JuliaSyntax. is_trivia (node);
206
+ :julia_comparator
207
+ elseif isplainoperator (node); :julia_operator
208
+ elseif nkind == K " ..." && JuliaSyntax. is_trivia (node); :julia_operator
209
+ elseif nkind == K " ." && JuliaSyntax. is_trivia (node) && kind (pnode) == K " dotcall" ;
210
+ :julia_broadcast
211
+ elseif nkind in (K " call" , K " dotcall" ) && JuliaSyntax. is_prefix_call (node)
212
+ argoffset, arg1 = 0 , nothing
213
+ for arg in node. args
214
+ argoffset += arg. span
215
+ if ! JuliaSyntax. is_trivia (arg)
216
+ arg1 = arg
217
+ break
218
+ end
219
+ end
220
+ if isnothing (arg1)
221
+ elseif kind (arg1) == K " Identifier"
222
+ region = first (region): first (region)+ argoffset- 1
223
+ name = Symbol (view (content, region))
224
+ ifelse (name in BUILTIN_FUNCTIONS, :julia_builtin , :julia_funcall )
225
+ elseif kind (arg1) == K " ." && length (arg1. args) == 3 &&
226
+ kind (arg1. args[end ]) == K " quote" &&
227
+ length (arg1. args[end ]. args) == 1 &&
228
+ kind (arg1. args[end ]. args[1 ]) == K " Identifier"
229
+ region = first (region)+ argoffset- arg1. args[end ]. args[1 ]. span: first (region)+ argoffset- 1
230
+ name = Symbol (view (content, region))
231
+ ifelse (name in BUILTIN_FUNCTIONS, :julia_builtin , :julia_funcall )
232
+ end
160
233
elseif JuliaSyntax. is_error (nkind); :julia_error
161
234
elseif ((depthchange, ptype) = paren_type (nkind)) |> last != :none
162
- if nkind == K " (" && ! isempty (highlights) && kind (lnode) == K " Identifier" && last (last (highlights[end ])) == :julia_identifier
163
- highlights[end ] = (highlights[end ][1 ], :face => :julia_funcall )
164
- end
165
235
depthref = getfield (pdepths, ptype)[]
166
236
pdepth = if depthchange > 0
167
237
getfield (pdepths, ptype)[] += depthchange
@@ -181,12 +251,26 @@ function _hl_annotations!(highlights::Vector{Tuple{UnitRange{Int}, Pair{Symbol,
181
251
end
182
252
! isnothing (face) &&
183
253
push! (highlights, (region, :face => face))
254
+ if nkind == K " Comment"
255
+ for match in eachmatch (
256
+ r" (?:^|[(\[ {[:space:]-])`([^[:space:]](?:.*?[^[:space:]])?)`(?:$|[!,\- .:;?\[\] [:space:]])" ,
257
+ view (content, region))
258
+ code = first (match. captures)
259
+ push! (highlights, (firstindex (content)+ offset+ code. offset: firstindex (content)+ offset+ code. offset+ code. ncodeunits- 1 ,
260
+ :face => :code ))
261
+ end
262
+ elseif nkind == K " String"
263
+ for match in eachmatch (r" \\ ." , view (content, region))
264
+ push! (highlights, (firstindex (content)+ offset+ match. offset- 1 : firstindex (content)+ offset+ match. offset+ ncodeunits (match. match)- 2 ,
265
+ :face => :julia_backslash_literal ))
266
+ end
267
+ end
184
268
isempty (node. args) && return
185
- llnode, lnode = node, node
269
+ lnode = node
186
270
for child in node. args
187
- cctx = HighlightContext (content, offset, lnode, llnode, pdepths)
271
+ cctx = HighlightContext (content, offset, lnode, pdepths)
188
272
_hl_annotations! (highlights, GreenLineage (child, lineage), cctx)
189
- llnode, lnode = lnode, child
273
+ lnode = child
190
274
offset += child. span
191
275
end
192
276
end
0 commit comments