1 // Scintilla source code edit control
2 // Encoding: UTF-8
3 /** @file LexJulia.cxx
4 ** Lexer for Julia.
5 ** Reusing code from LexMatlab, LexPython and LexRust
6 **
7 ** Written by Bertrand Lacoste
8 **
9 **/
10 // Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
11 // The License.txt file describes the conditions under which this software may be distributed.
12
13 #include <cstdlib>
14 #include <cassert>
15 #include <cstring>
16
17 #include <string>
18 #include <string_view>
19 #include <vector>
20 #include <map>
21 #include <algorithm>
22 #include <functional>
23
24 #include "ILexer.h"
25 #include "Scintilla.h"
26 #include "SciLexer.h"
27
28 #include "StringCopy.h"
29 #include "PropSetSimple.h"
30 #include "StringCopy.h"
31 #include "WordList.h"
32 #include "LexAccessor.h"
33 #include "Accessor.h"
34 #include "StyleContext.h"
35 #include "CharacterSet.h"
36 #include "CharacterCategory.h"
37 #include "LexerModule.h"
38 #include "OptionSet.h"
39 #include "DefaultLexer.h"
40
41 using namespace Scintilla;
42 // Geany still uses Scintilla v3.5
43 //using namespace Lexilla;
44
45 static const int MAX_JULIA_IDENT_CHARS = 1023;
46
47 // Options used for LexerJulia
48 struct OptionsJulia {
49 bool fold;
50 bool foldComment;
51 bool foldCompact;
52 bool foldDocstring;
53 bool foldSyntaxBased;
54 bool highlightTypeannotation;
55 bool highlightLexerror;
OptionsJuliaOptionsJulia56 OptionsJulia() {
57 fold = true;
58 foldComment = true;
59 foldCompact = false;
60 foldDocstring = true;
61 foldSyntaxBased = true;
62 highlightTypeannotation = false;
63 highlightLexerror = false;
64 }
65 };
66
67 const char * const juliaWordLists[] = {
68 "Primary keywords and identifiers",
69 "Built in types",
70 "Other keywords",
71 "Built in functions",
72 0,
73 };
74
75 struct OptionSetJulia : public OptionSet<OptionsJulia> {
OptionSetJuliaOptionSetJulia76 OptionSetJulia() {
77 DefineProperty("fold", &OptionsJulia::fold);
78
79 DefineProperty("fold.compact", &OptionsJulia::foldCompact);
80
81 DefineProperty("fold.comment", &OptionsJulia::foldComment);
82
83 DefineProperty("fold.julia.docstring", &OptionsJulia::foldDocstring,
84 "Fold multiline triple-doublequote strings, usually used to document a function or type above the definition.");
85
86 DefineProperty("fold.julia.syntax.based", &OptionsJulia::foldSyntaxBased,
87 "Set this property to 0 to disable syntax based folding.");
88
89 DefineProperty("lexer.julia.highlight.typeannotation", &OptionsJulia::highlightTypeannotation,
90 "This option enables highlighting of the type identifier after `::`.");
91
92 DefineProperty("lexer.julia.highlight.lexerror", &OptionsJulia::highlightLexerror,
93 "This option enables highlighting of syntax error int character or number definition.");
94
95 DefineWordListSets(juliaWordLists);
96 }
97 };
98
99 LexicalClass juliaLexicalClasses[] = {
100 // Lexer Julia SCLEX_JULIA SCE_JULIA_:
101 0, "SCE_JULIA_DEFAULT", "default", "White space",
102 1, "SCE_JULIA_COMMENT", "comment", "Comment",
103 2, "SCE_JULIA_NUMBER", "literal numeric", "Number",
104 3, "SCE_JULIA_KEYWORD1", "keyword", "Reserved keywords",
105 4, "SCE_JULIA_KEYWORD2", "identifier", "Builtin type names",
106 5, "SCE_JULIA_KEYWORD3", "identifier", "Constants",
107 6, "SCE_JULIA_CHAR", "literal string character", "Single quoted string",
108 7, "SCE_JULIA_OPERATOR", "operator", "Operator",
109 8, "SCE_JULIA_BRACKET", "bracket operator", "Bracket operator",
110 9, "SCE_JULIA_IDENTIFIER", "identifier", "Identifier",
111 10, "SCE_JULIA_STRING", "literal string", "Double quoted String",
112 11, "SCE_JULIA_SYMBOL", "literal string symbol", "Symbol",
113 12, "SCE_JULIA_MACRO", "macro preprocessor", "Macro",
114 13, "SCE_JULIA_STRINGINTERP", "literal string interpolated", "String interpolation",
115 14, "SCE_JULIA_DOCSTRING", "literal string documentation", "Docstring",
116 15, "SCE_JULIA_STRINGLITERAL", "literal string", "String literal prefix",
117 16, "SCE_JULIA_COMMAND", "literal string command", "Command",
118 17, "SCE_JULIA_COMMANDLITERAL", "literal string command", "Command literal prefix",
119 18, "SCE_JULIA_TYPEANNOT", "identifier type", "Type annotation identifier",
120 19, "SCE_JULIA_LEXERROR", "lexer error", "Lexing error",
121 20, "SCE_JULIA_KEYWORD4", "identifier", "Builtin function names",
122 21, "SCE_JULIA_TYPEOPERATOR", "operator type", "Type annotation operator",
123 };
124
125 class LexerJulia : public DefaultLexer {
126 WordList keywords;
127 WordList identifiers2;
128 WordList identifiers3;
129 WordList identifiers4;
130 OptionsJulia options;
131 OptionSetJulia osJulia;
132 public:
LexerJulia()133 explicit LexerJulia() :
134 DefaultLexer("julia", SCLEX_JULIA, juliaLexicalClasses, ELEMENTS(juliaLexicalClasses)) {
135 }
~LexerJulia()136 virtual ~LexerJulia() {
137 }
Release()138 void SCI_METHOD Release() override {
139 delete this;
140 }
Version() const141 int SCI_METHOD Version() const override {
142 // Geany still uses Scintilla v3.5
143 //return lvRelease5;
144 return lvIdentity;
145 }
PropertyNames()146 const char * SCI_METHOD PropertyNames() override {
147 return osJulia.PropertyNames();
148 }
PropertyType(const char * name)149 int SCI_METHOD PropertyType(const char *name) override {
150 return osJulia.PropertyType(name);
151 }
DescribeProperty(const char * name)152 const char * SCI_METHOD DescribeProperty(const char *name) override {
153 return osJulia.DescribeProperty(name);
154 }
155 Sci_Position SCI_METHOD PropertySet(const char *key, const char *val) override;
PropertyGet(const char * key)156 const char * SCI_METHOD PropertyGet(const char *key) override {
157 return osJulia.PropertyGet(key);
158 }
DescribeWordListSets()159 const char * SCI_METHOD DescribeWordListSets() override {
160 return osJulia.DescribeWordListSets();
161 }
162 Sci_Position SCI_METHOD WordListSet(int n, const char *wl) override;
163 void SCI_METHOD Lex(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess) override;
164 void SCI_METHOD Fold(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess) override;
PrivateCall(int,void *)165 void * SCI_METHOD PrivateCall(int, void *) override {
166 return 0;
167 }
168
169 // Geany still uses Scintilla v3.5
170 //static ILexer5 *LexerFactoryJulia() {
LexerFactoryJulia()171 static ILexer *LexerFactoryJulia() {
172 return new LexerJulia();
173 }
174 };
175
PropertySet(const char * key,const char * val)176 Sci_Position SCI_METHOD LexerJulia::PropertySet(const char *key, const char *val) {
177 if (osJulia.PropertySet(&options, key, val)) {
178 return 0;
179 }
180 return -1;
181 }
182
WordListSet(int n,const char * wl)183 Sci_Position SCI_METHOD LexerJulia::WordListSet(int n, const char *wl) {
184 WordList *wordListN = nullptr;
185 switch (n) {
186 case 0:
187 wordListN = &keywords;
188 break;
189 case 1:
190 wordListN = &identifiers2;
191 break;
192 case 2:
193 wordListN = &identifiers3;
194 break;
195 case 3:
196 wordListN = &identifiers4;
197 break;
198 }
199 Sci_Position firstModification = -1;
200 if (wordListN) {
201 WordList wlNew;
202 wlNew.Set(wl);
203 if (*wordListN != wlNew) {
204 wordListN->Set(wl);
205 firstModification = 0;
206 }
207 }
208 return firstModification;
209 }
210
IsJuliaOperator(int ch)211 static inline bool IsJuliaOperator(int ch) {
212 if (ch == '%' || ch == '^' || ch == '&' || ch == '*' ||
213 ch == '-' || ch == '+' || ch == '=' || ch == '|' ||
214 ch == '<' || ch == '>' || ch == '/' || ch == '~' ||
215 ch == '\\' ) {
216 return true;
217 }
218 return false;
219 }
220
221 // The list contains non-ascii unary operators
IsJuliaUnaryOperator(int ch)222 static inline bool IsJuliaUnaryOperator (int ch) {
223 if (ch == 0x00ac || ch == 0x221a || ch == 0x221b ||
224 ch == 0x221c || ch == 0x22c6 || ch == 0x00b1 ||
225 ch == 0x2213 ) {
226 return true;
227 }
228 return false;
229 }
230
IsJuliaParen(int ch)231 static inline bool IsJuliaParen (int ch) {
232 if (ch == '(' || ch == ')' || ch == '{' || ch == '}' ||
233 ch == '[' || ch == ']' ) {
234 return true;
235 }
236 return false;
237 }
238
239 // Unicode parsing from Julia source code:
240 // https://github.com/JuliaLang/julia/blob/master/src/flisp/julia_extensions.c
241 // keep the same function name to be easy to find again
is_wc_cat_id_start(uint32_t wc)242 static int is_wc_cat_id_start(uint32_t wc) {
243 const CharacterCategory cat = CategoriseCharacter((int) wc);
244
245 return (cat == ccLu || cat == ccLl ||
246 cat == ccLt || cat == ccLm ||
247 cat == ccLo || cat == ccNl ||
248 cat == ccSc || // allow currency symbols
249 // other symbols, but not arrows or replacement characters
250 (cat == ccSo && !(wc >= 0x2190 && wc <= 0x21FF) &&
251 wc != 0xfffc && wc != 0xfffd &&
252 wc != 0x233f && // notslash
253 wc != 0x00a6) || // broken bar
254
255 // math symbol (category Sm) whitelist
256 (wc >= 0x2140 && wc <= 0x2a1c &&
257 ((wc >= 0x2140 && wc <= 0x2144) || // ⅀, ⅁, ⅂, ⅃, ⅄
258 wc == 0x223f || wc == 0x22be || wc == 0x22bf || // ∿, ⊾, ⊿
259 wc == 0x22a4 || wc == 0x22a5 || // ⊤ ⊥
260
261 (wc >= 0x2202 && wc <= 0x2233 &&
262 (wc == 0x2202 || wc == 0x2205 || wc == 0x2206 || // ∂, ∅, ∆
263 wc == 0x2207 || wc == 0x220e || wc == 0x220f || // ∇, ∎, ∏
264 wc == 0x2210 || wc == 0x2211 || // ∐, ∑
265 wc == 0x221e || wc == 0x221f || // ∞, ∟
266 wc >= 0x222b)) || // ∫, ∬, ∭, ∮, ∯, ∰, ∱, ∲, ∳
267
268 (wc >= 0x22c0 && wc <= 0x22c3) || // N-ary big ops: ⋀, ⋁, ⋂, ⋃
269 (wc >= 0x25F8 && wc <= 0x25ff) || // ◸, ◹, ◺, ◻, ◼, ◽, ◾, ◿
270
271 (wc >= 0x266f &&
272 (wc == 0x266f || wc == 0x27d8 || wc == 0x27d9 || // ♯, ⟘, ⟙
273 (wc >= 0x27c0 && wc <= 0x27c1) || // ⟀, ⟁
274 (wc >= 0x29b0 && wc <= 0x29b4) || // ⦰, ⦱, ⦲, ⦳, ⦴
275 (wc >= 0x2a00 && wc <= 0x2a06) || // ⨀, ⨁, ⨂, ⨃, ⨄, ⨅, ⨆
276 (wc >= 0x2a09 && wc <= 0x2a16) || // ⨉, ⨊, ⨋, ⨌, ⨍, ⨎, ⨏, ⨐, ⨑, ⨒, ⨓, ⨔, ⨕, ⨖
277 wc == 0x2a1b || wc == 0x2a1c)))) || // ⨛, ⨜
278
279 (wc >= 0x1d6c1 && // variants of \nabla and \partial
280 (wc == 0x1d6c1 || wc == 0x1d6db ||
281 wc == 0x1d6fb || wc == 0x1d715 ||
282 wc == 0x1d735 || wc == 0x1d74f ||
283 wc == 0x1d76f || wc == 0x1d789 ||
284 wc == 0x1d7a9 || wc == 0x1d7c3)) ||
285
286 // super- and subscript +-=()
287 (wc >= 0x207a && wc <= 0x207e) ||
288 (wc >= 0x208a && wc <= 0x208e) ||
289
290 // angle symbols
291 (wc >= 0x2220 && wc <= 0x2222) || // ∠, ∡, ∢
292 (wc >= 0x299b && wc <= 0x29af) || // ⦛, ⦜, ⦝, ⦞, ⦟, ⦠, ⦡, ⦢, ⦣, ⦤, ⦥, ⦦, ⦧, ⦨, ⦩, ⦪, ⦫, ⦬, ⦭, ⦮, ⦯
293
294 // Other_ID_Start
295 wc == 0x2118 || wc == 0x212E || // ℘, ℮
296 (wc >= 0x309B && wc <= 0x309C) || // katakana-hiragana sound marks
297
298 // bold-digits and double-struck digits
299 (wc >= 0x1D7CE && wc <= 0x1D7E1)); // through (inclusive), through (inclusive)
300 }
301
IsIdentifierFirstCharacter(int ch)302 static inline bool IsIdentifierFirstCharacter (int ch) {
303 if (IsASCII(ch)) {
304 return (bool) (isalpha(ch) || ch == '_');
305 }
306 if (ch < 0xA1 || ch > 0x10ffff) {
307 return false;
308 }
309
310 return is_wc_cat_id_start((uint32_t) ch);
311 }
312
IsIdentifierCharacter(int ch)313 static inline bool IsIdentifierCharacter (int ch) {
314 if (IsASCII(ch)) {
315 return (bool) (isalnum(ch) || ch == '_' || ch == '!');
316 }
317 if (ch < 0xA1 || ch > 0x10ffff) {
318 return false;
319 }
320
321 if (is_wc_cat_id_start((uint32_t) ch)) {
322 return true;
323 }
324
325 const CharacterCategory cat = CategoriseCharacter(ch);
326
327 if (cat == ccMn || cat == ccMc ||
328 cat == ccNd || cat == ccPc ||
329 cat == ccSk || cat == ccMe ||
330 cat == ccNo ||
331 // primes (single, double, triple, their reverses, and quadruple)
332 (ch >= 0x2032 && ch <= 0x2037) || (ch == 0x2057)) {
333 return true;
334 }
335 return false;
336 }
337
338 // keep the same function name to be easy to find again
339 static const uint32_t opsuffs[] = {
340 0x00b2, // ²
341 0x00b3, // ³
342 0x00b9, // ¹
343 0x02b0, // ʰ
344 0x02b2, // ʲ
345 0x02b3, // ʳ
346 0x02b7, // ʷ
347 0x02b8, // ʸ
348 0x02e1, // ˡ
349 0x02e2, // ˢ
350 0x02e3, // ˣ
351 0x1d2c, // ᴬ
352 0x1d2e, // ᴮ
353 0x1d30, // ᴰ
354 0x1d31, // ᴱ
355 0x1d33, // ᴳ
356 0x1d34, // ᴴ
357 0x1d35, // ᴵ
358 0x1d36, // ᴶ
359 0x1d37, // ᴷ
360 0x1d38, // ᴸ
361 0x1d39, // ᴹ
362 0x1d3a, // ᴺ
363 0x1d3c, // ᴼ
364 0x1d3e, // ᴾ
365 0x1d3f, // ᴿ
366 0x1d40, // ᵀ
367 0x1d41, // ᵁ
368 0x1d42, // ᵂ
369 0x1d43, // ᵃ
370 0x1d47, // ᵇ
371 0x1d48, // ᵈ
372 0x1d49, // ᵉ
373 0x1d4d, // ᵍ
374 0x1d4f, // ᵏ
375 0x1d50, // ᵐ
376 0x1d52, // ᵒ
377 0x1d56, // ᵖ
378 0x1d57, // ᵗ
379 0x1d58, // ᵘ
380 0x1d5b, // ᵛ
381 0x1d5d, // ᵝ
382 0x1d5e, // ᵞ
383 0x1d5f, // ᵟ
384 0x1d60, // ᵠ
385 0x1d61, // ᵡ
386 0x1d62, // ᵢ
387 0x1d63, // ᵣ
388 0x1d64, // ᵤ
389 0x1d65, // ᵥ
390 0x1d66, // ᵦ
391 0x1d67, // ᵧ
392 0x1d68, // ᵨ
393 0x1d69, // ᵩ
394 0x1d6a, // ᵪ
395 0x1d9c, // ᶜ
396 0x1da0, // ᶠ
397 0x1da5, // ᶥ
398 0x1da6, // ᶦ
399 0x1dab, // ᶫ
400 0x1db0, // ᶰ
401 0x1db8, // ᶸ
402 0x1dbb, // ᶻ
403 0x1dbf, // ᶿ
404 0x2032, // ′
405 0x2033, // ″
406 0x2034, // ‴
407 0x2035, // ‵
408 0x2036, // ‶
409 0x2037, // ‷
410 0x2057, // ⁗
411 0x2070, // ⁰
412 0x2071, // ⁱ
413 0x2074, // ⁴
414 0x2075, // ⁵
415 0x2076, // ⁶
416 0x2077, // ⁷
417 0x2078, // ⁸
418 0x2079, // ⁹
419 0x207a, // ⁺
420 0x207b, // ⁻
421 0x207c, // ⁼
422 0x207d, // ⁽
423 0x207e, // ⁾
424 0x207f, // ⁿ
425 0x2080, // ₀
426 0x2081, // ₁
427 0x2082, // ₂
428 0x2083, // ₃
429 0x2084, // ₄
430 0x2085, // ₅
431 0x2086, // ₆
432 0x2087, // ₇
433 0x2088, // ₈
434 0x2089, // ₉
435 0x208a, // ₊
436 0x208b, // ₋
437 0x208c, // ₌
438 0x208d, // ₍
439 0x208e, // ₎
440 0x2090, // ₐ
441 0x2091, // ₑ
442 0x2092, // ₒ
443 0x2093, // ₓ
444 0x2095, // ₕ
445 0x2096, // ₖ
446 0x2097, // ₗ
447 0x2098, // ₘ
448 0x2099, // ₙ
449 0x209a, // ₚ
450 0x209b, // ₛ
451 0x209c, // ₜ
452 0x2c7c, // ⱼ
453 0x2c7d, // ⱽ
454 0xa71b, // ꜛ
455 0xa71c, // ꜜ
456 0xa71d // ꜝ
457 };
458 static const size_t opsuffs_len = sizeof(opsuffs) / (sizeof(uint32_t));
459
460 // keep the same function name to be easy to find again
jl_op_suffix_char(uint32_t wc)461 static bool jl_op_suffix_char(uint32_t wc) {
462 if (wc < 0xA1 || wc > 0x10ffff) {
463 return false;
464 }
465 const CharacterCategory cat = CategoriseCharacter((int) wc);
466 if (cat == ccMn || cat == ccMc ||
467 cat == ccMe) {
468 return true;
469 }
470
471 for (size_t i = 0; i < opsuffs_len; ++i) {
472 if (wc == opsuffs[i]) {
473 return true;
474 }
475 }
476 return false;
477 }
478
479 // keep the same function name to be easy to find again
never_id_char(uint32_t wc)480 static bool never_id_char(uint32_t wc) {
481 const CharacterCategory cat = CategoriseCharacter((int) wc);
482 return (
483 // spaces and control characters:
484 (cat >= ccZs && cat <= ccCs) ||
485
486 // ASCII and Latin1 non-connector punctuation
487 (wc < 0xff &&
488 cat >= ccPd && cat <= ccPo) ||
489
490 wc == '`' ||
491
492 // mathematical brackets
493 (wc >= 0x27e6 && wc <= 0x27ef) ||
494 // angle, corner, and lenticular brackets
495 (wc >= 0x3008 && wc <= 0x3011) ||
496 // tortoise shell, square, and more lenticular brackets
497 (wc >= 0x3014 && wc <= 0x301b) ||
498 // fullwidth parens
499 (wc == 0xff08 || wc == 0xff09) ||
500 // fullwidth square brackets
501 (wc == 0xff3b || wc == 0xff3d));
502 }
503
504
IsOperatorFirstCharacter(int ch)505 static bool IsOperatorFirstCharacter (int ch) {
506 if (IsASCII(ch)) {
507 if (IsJuliaOperator(ch) ||
508 ch == '!' || ch == '?' ||
509 ch == ':' || ch == ';' ||
510 ch == ',' || ch == '.' ) {
511 return true;
512 }else {
513 return false;
514 }
515 } else if (is_wc_cat_id_start((uint32_t) ch)) {
516 return false;
517 } else if (IsJuliaUnaryOperator(ch) ||
518 ! never_id_char((uint32_t) ch)) {
519 return true;
520 }
521 return false;
522 }
523
IsOperatorCharacter(int ch)524 static bool IsOperatorCharacter (int ch) {
525 if (IsOperatorFirstCharacter(ch) ||
526 (!IsASCII(ch) && jl_op_suffix_char((uint32_t) ch)) ) {
527 return true;
528 }
529 return false;
530 }
531
CheckBoundsIndexing(char * str)532 static bool CheckBoundsIndexing(char *str) {
533 if (strcmp("begin", str) == 0 || strcmp("end", str) == 0 ) {
534 return true;
535 }
536 return false;
537 }
538
CheckKeywordFoldPoint(char * str)539 static int CheckKeywordFoldPoint(char *str) {
540 if (strcmp ("if", str) == 0 ||
541 strcmp ("for", str) == 0 ||
542 strcmp ("while", str) == 0 ||
543 strcmp ("try", str) == 0 ||
544 strcmp ("do", str) == 0 ||
545 strcmp ("begin", str) == 0 ||
546 strcmp ("let", str) == 0 ||
547 strcmp ("baremodule", str) == 0 ||
548 strcmp ("quote", str) == 0 ||
549 strcmp ("module", str) == 0 ||
550 strcmp ("struct", str) == 0 ||
551 strcmp ("type", str) == 0 ||
552 strcmp ("macro", str) == 0 ||
553 strcmp ("function", str) == 0) {
554 return 1;
555 }
556 if (strcmp("end", str) == 0) {
557 return -1;
558 }
559 return 0;
560 }
561
IsNumberExpon(int ch,int base)562 static bool IsNumberExpon(int ch, int base) {
563 if ((base == 10 && (ch == 'e' || ch == 'E' || ch == 'f')) ||
564 (base == 16 && (ch == 'p' || ch == 'P'))) {
565 return true;
566 }
567 return false;
568 }
569
570 /* Scans a sequence of digits, returning true if it found any. */
ScanDigits(StyleContext & sc,int base,bool allow_sep)571 static bool ScanDigits(StyleContext& sc, int base, bool allow_sep) {
572 bool found = false;
573 for (;;) {
574 if (IsADigit(sc.chNext, base) || (allow_sep && sc.chNext == '_')) {
575 found = true;
576 sc.Forward();
577 } else {
578 break;
579 }
580 }
581 return found;
582 }
583
ScanNHexas(StyleContext & sc,int max)584 static inline bool ScanNHexas(StyleContext &sc, int max) {
585 int n = 0;
586 bool error = false;
587
588 sc.Forward();
589 if (!IsADigit(sc.ch, 16)) {
590 error = true;
591 } else {
592 while (IsADigit(sc.ch, 16) && n < max) {
593 sc.Forward();
594 n++;
595 }
596 }
597 return error;
598 }
599
resumeCharacter(StyleContext & sc,bool lexerror)600 static void resumeCharacter(StyleContext &sc, bool lexerror) {
601 bool error = false;
602
603 // ''' case
604 if (sc.chPrev == '\'' && sc.ch == '\'' && sc.chNext == '\'') {
605 sc.Forward();
606 sc.ForwardSetState(SCE_JULIA_DEFAULT);
607 return;
608 } else if (lexerror && sc.chPrev == '\'' && sc.ch == '\'') {
609 sc.ChangeState(SCE_JULIA_LEXERROR);
610 sc.ForwardSetState(SCE_JULIA_DEFAULT);
611
612 // Escape characters
613 } else if (sc.ch == '\\') {
614 sc.Forward();
615 if (sc.ch == '\'' || sc.ch == '\\' ) {
616 sc.Forward();
617 } else if (sc.ch == 'n' || sc.ch == 't' || sc.ch == 'a' ||
618 sc.ch == 'b' || sc.ch == 'e' || sc.ch == 'f' ||
619 sc.ch == 'r' || sc.ch == 'v' ) {
620 sc.Forward();
621 } else if (sc.ch == 'x') {
622 error |= ScanNHexas(sc, 2);
623 } else if (sc.ch == 'u') {
624 error |= ScanNHexas(sc, 4);
625 } else if (sc.ch == 'U') {
626 error |= ScanNHexas(sc, 8);
627 } else if (IsADigit(sc.ch, 8)) {
628 int n = 1;
629 int max = 3;
630 sc.Forward();
631 while (IsADigit(sc.ch, 8) && n < max) {
632 sc.Forward();
633 n++;
634 }
635 }
636
637 if (lexerror) {
638 if (sc.ch != '\'') {
639 error = true;
640 while (sc.ch != '\'' &&
641 sc.ch != '\r' &&
642 sc.ch != '\n') {
643 sc.Forward();
644 }
645 }
646
647 if (error) {
648 sc.ChangeState(SCE_JULIA_LEXERROR);
649 sc.ForwardSetState(SCE_JULIA_DEFAULT);
650 }
651 }
652 } else if (lexerror) {
653 if (sc.ch < 0x20 || sc.ch > 0x10ffff) {
654 error = true;
655 } else {
656 // single character
657 sc.Forward();
658
659 if (sc.ch != '\'') {
660 error = true;
661 while (sc.ch != '\'' &&
662 sc.ch != '\r' &&
663 sc.ch != '\n') {
664 sc.Forward();
665 }
666 }
667 }
668
669 if (error) {
670 sc.ChangeState(SCE_JULIA_LEXERROR);
671 sc.ForwardSetState(SCE_JULIA_DEFAULT);
672 }
673 }
674
675 // closing quote
676 if (sc.ch == '\'') {
677 if (sc.chNext == '\'') {
678 sc.Forward();
679 } else {
680 sc.ForwardSetState(SCE_JULIA_DEFAULT);
681 }
682 }
683 }
684
IsACharacter(StyleContext & sc)685 static inline bool IsACharacter(StyleContext &sc) {
686 return (sc.chPrev == '\'' && sc.chNext == '\'');
687 }
688
ScanParenInterpolation(StyleContext & sc)689 static void ScanParenInterpolation(StyleContext &sc) {
690 // TODO: no syntax highlighting inside a string interpolation
691
692 // Level of nested parenthesis
693 int interp_level = 0;
694
695 // If true, it is inside a string and parenthesis are not counted.
696 bool allow_paren_string = false;
697
698
699 // check for end of states
700 for (; sc.More(); sc.Forward()) {
701 // TODO: check corner cases for nested string interpolation
702 // TODO: check corner cases with Command inside interpolation
703
704 if ( sc.ch == '\"' && sc.chPrev != '\\') {
705 // Toggle the string environment (parenthesis are not counted inside a string)
706 allow_paren_string = !allow_paren_string;
707 } else if ( !allow_paren_string ) {
708 if ( sc.ch == '(' && !IsACharacter(sc) ) {
709 interp_level ++;
710 } else if ( sc.ch == ')' && !IsACharacter(sc) && interp_level > 0 ) {
711 interp_level --;
712 if (interp_level == 0) {
713 // Exit interpolation
714 return;
715 }
716 }
717 }
718 }
719 }
720 /*
721 * Start parsing a number, parse the base.
722 */
initNumber(StyleContext & sc,int & base,bool & with_dot)723 static void initNumber (StyleContext &sc, int &base, bool &with_dot) {
724 base = 10;
725 with_dot = false;
726 sc.SetState(SCE_JULIA_NUMBER);
727 if (sc.ch == '0') {
728 if (sc.chNext == 'x') {
729 sc.Forward();
730 base = 16;
731 if (sc.chNext == '.') {
732 sc.Forward();
733 with_dot = true;
734 }
735 } else if (sc.chNext == 'o') {
736 sc.Forward();
737 base = 8;
738 } else if (sc.chNext == 'b') {
739 sc.Forward();
740 base = 2;
741 }
742 } else if (sc.ch == '.') {
743 with_dot = true;
744 }
745 }
746
747 /*
748 * Resume parsing a String or Command, bounded by the `quote` character (\" or \`)
749 * The `triple` argument specifies if it is a triple-quote String or Command.
750 * Interpolation is detected (with `$`), and parsed if `allow_interp` is true.
751 */
resumeStringLike(StyleContext & sc,int quote,bool triple,bool allow_interp,bool full_highlight)752 static void resumeStringLike(StyleContext &sc, int quote, bool triple, bool allow_interp, bool full_highlight) {
753 int stylePrev = sc.state;
754 bool checkcurrent = false;
755
756 // Escape characters
757 if (sc.ch == '\\') {
758 if (sc.chNext == quote || sc.chNext == '\\' || sc.chNext == '$') {
759 sc.Forward();
760 }
761 } else if (allow_interp && sc.ch == '$') {
762 // If the interpolation is only of a variable, do not change state
763 if (sc.chNext == '(') {
764 if (full_highlight) {
765 sc.SetState(SCE_JULIA_STRINGINTERP);
766 } else {
767 sc.ForwardSetState(SCE_JULIA_STRINGINTERP);
768 }
769 ScanParenInterpolation(sc);
770 sc.ForwardSetState(stylePrev);
771
772 checkcurrent = true;
773
774 } else if (full_highlight && IsIdentifierFirstCharacter(sc.chNext)) {
775 sc.SetState(SCE_JULIA_STRINGINTERP);
776 sc.Forward();
777 sc.Forward();
778 for (; sc.More(); sc.Forward()) {
779 if (! IsIdentifierCharacter(sc.ch)) {
780 break;
781 }
782 }
783 sc.SetState(stylePrev);
784
785 checkcurrent = true;
786 }
787
788 if (checkcurrent) {
789 // Check that the current character is not a special char,
790 // otherwise it will be skipped
791 resumeStringLike(sc, quote, triple, allow_interp, full_highlight);
792 }
793
794 } else if (sc.ch == quote) {
795 if (triple) {
796 if (sc.chNext == quote && sc.GetRelativeCharacter(2) == quote) {
797 // Move to the end of the triple quotes
798 Sci_PositionU nextIndex = sc.currentPos + 2;
799 while (nextIndex > sc.currentPos && sc.More()) {
800 sc.Forward();
801 }
802 sc.ForwardSetState(SCE_JULIA_DEFAULT);
803 }
804 } else {
805 sc.ForwardSetState(SCE_JULIA_DEFAULT);
806 }
807 }
808 }
809
resumeCommand(StyleContext & sc,bool triple,bool allow_interp)810 static void resumeCommand(StyleContext &sc, bool triple, bool allow_interp) {
811 return resumeStringLike(sc, '`', triple, allow_interp, true);
812 }
813
resumeString(StyleContext & sc,bool triple,bool allow_interp)814 static void resumeString(StyleContext &sc, bool triple, bool allow_interp) {
815 return resumeStringLike(sc, '"', triple, allow_interp, true);
816 }
817
resumeNumber(StyleContext & sc,int base,bool & with_dot,bool lexerror)818 static void resumeNumber (StyleContext &sc, int base, bool &with_dot, bool lexerror) {
819 if (IsNumberExpon(sc.ch, base)) {
820 if (IsADigit(sc.chNext) || sc.chNext == '+' || sc.chNext == '-') {
821 sc.Forward();
822 // Capture all digits
823 ScanDigits(sc, 10, false);
824 sc.Forward();
825 }
826 sc.SetState(SCE_JULIA_DEFAULT);
827 } else if (sc.ch == '.' && sc.chNext == '.') {
828 // Interval operator `..`
829 sc.SetState(SCE_JULIA_OPERATOR);
830 sc.Forward();
831 sc.ForwardSetState(SCE_JULIA_DEFAULT);
832 } else if (sc.ch == '.' && !with_dot) {
833 with_dot = true;
834 ScanDigits(sc, base, true);
835 } else if (IsADigit(sc.ch, base) || sc.ch == '_') {
836 ScanDigits(sc, base, true);
837 } else if (IsADigit(sc.ch) && !IsADigit(sc.ch, base)) {
838 if (lexerror) {
839 sc.ChangeState(SCE_JULIA_LEXERROR);
840 }
841 ScanDigits(sc, 10, false);
842 sc.ForwardSetState(SCE_JULIA_DEFAULT);
843 } else {
844 sc.SetState(SCE_JULIA_DEFAULT);
845 }
846 }
847
resumeOperator(StyleContext & sc)848 static void resumeOperator (StyleContext &sc) {
849 if (sc.chNext == ':' && (sc.ch == ':' || sc.ch == '<' ||
850 (sc.ch == '>' && (sc.chPrev != '-' && sc.chPrev != '=')))) {
851 // Case `:a=>:b`
852 sc.Forward();
853 sc.ForwardSetState(SCE_JULIA_DEFAULT);
854 } else if (sc.ch == ':') {
855 // Case `foo(:baz,:baz)` or `:one+:two`
856 // Let the default case switch decide if it is a symbol
857 sc.SetState(SCE_JULIA_DEFAULT);
858 } else if (sc.ch == '\'') {
859 sc.SetState(SCE_JULIA_DEFAULT);
860 } else if ((sc.ch == '.' && sc.chPrev != '.') || IsIdentifierFirstCharacter(sc.ch) ||
861 (! (sc.chPrev == '.' && IsOperatorFirstCharacter(sc.ch)) &&
862 ! IsOperatorCharacter(sc.ch)) ) {
863 sc.SetState(SCE_JULIA_DEFAULT);
864 }
865 }
866
Lex(Sci_PositionU startPos,Sci_Position length,int initStyle,IDocument * pAccess)867 void SCI_METHOD LexerJulia::Lex(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess) {
868 PropSetSimple props;
869 Accessor styler(pAccess, &props);
870
871 Sci_Position pos = startPos;
872 styler.StartAt(pos);
873 styler.StartSegment(pos);
874
875 // use the line state of each line to store block/multiline states
876 Sci_Position curLine = styler.GetLine(startPos);
877 // Default is false for everything and 0 counters.
878 int lineState = (curLine > 0) ? styler.GetLineState(curLine-1) : 0;
879
880 bool transpose = (lineState >> 0) & 0x01; // 1 bit to know if ' is allowed to mean transpose
881 bool istripledocstring = (lineState >> 1) & 0x01; // 1 bit to know if we are in a triple doublequotes string
882 bool triple_backtick = (lineState >> 2) & 0x01; // 1 bit to know if we are in a triple backtick command
883 bool israwstring = (lineState >> 3) & 0x01; // 1 bit to know if we are in a raw string
884 int indexing_level = (int)((lineState >> 4) & 0x0F); // 4 bits of bracket nesting counter
885 int list_comprehension = (int)((lineState >> 8) & 0x0F); // 4 bits of parenthesis nesting counter
886 int commentDepth = (int)((lineState >> 12) & 0x0F); // 4 bits of nested comment counter
887
888 // base for parsing number
889 int base = 10;
890 // number has a float dot ?
891 bool with_dot = false;
892
893 StyleContext sc(startPos, length, initStyle, styler);
894
895 for (; sc.More(); sc.Forward()) {
896
897 //// check for end of states
898 switch (sc.state) {
899 case SCE_JULIA_BRACKET:
900 sc.SetState(SCE_JULIA_DEFAULT);
901 break;
902 case SCE_JULIA_OPERATOR:
903 resumeOperator(sc);
904 break;
905 case SCE_JULIA_TYPEOPERATOR:
906 sc.SetState(SCE_JULIA_DEFAULT);
907 break;
908 case SCE_JULIA_TYPEANNOT:
909 if (! IsIdentifierCharacter(sc.ch)) {
910 sc.SetState(SCE_JULIA_DEFAULT);
911 }
912 break;
913 case SCE_JULIA_IDENTIFIER:
914 // String literal
915 if (sc.ch == '\"') {
916 // If the string literal has a prefix, interpolation is disabled
917 israwstring = true;
918 sc.ChangeState(SCE_JULIA_STRINGLITERAL);
919 sc.SetState(SCE_JULIA_DEFAULT);
920
921 } else if (sc.ch == '`') {
922 // If the string literal has a prefix, interpolation is disabled
923 israwstring = true;
924 sc.ChangeState(SCE_JULIA_COMMANDLITERAL);
925 sc.SetState(SCE_JULIA_DEFAULT);
926
927 // Continue if the character is an identifier character
928 } else if (! IsIdentifierCharacter(sc.ch)) {
929 char s[MAX_JULIA_IDENT_CHARS + 1];
930 sc.GetCurrent(s, sizeof(s));
931
932 // Treat the keywords differently if we are indexing or not
933 if ( indexing_level > 0 && CheckBoundsIndexing(s)) {
934 // Inside [], (), `begin` and `end` are numbers not block keywords
935 sc.ChangeState(SCE_JULIA_NUMBER);
936 transpose = false;
937
938 } else {
939 if (keywords.InList(s)) {
940 sc.ChangeState(SCE_JULIA_KEYWORD1);
941 transpose = false;
942 } else if (identifiers2.InList(s)) {
943 sc.ChangeState(SCE_JULIA_KEYWORD2);
944 transpose = false;
945 } else if (identifiers3.InList(s)) {
946 sc.ChangeState(SCE_JULIA_KEYWORD3);
947 transpose = false;
948 } else if (identifiers4.InList(s)) {
949 sc.ChangeState(SCE_JULIA_KEYWORD4);
950 // These identifiers can be used for variable names also,
951 // so transpose is not forbidden.
952 //transpose = false;
953 }
954 }
955 sc.SetState(SCE_JULIA_DEFAULT);
956
957 // TODO: recognize begin-end blocks inside list comprehension
958 // b = [(begin n%2; n*2 end) for n in 1:10]
959 // TODO: recognize better comprehension for-if to avoid problem with code-folding
960 // c = [(if isempty(a); missing else first(b) end) for (a, b) in zip(l1, l2)]
961 }
962 break;
963 case SCE_JULIA_NUMBER:
964 resumeNumber(sc, base, with_dot, options.highlightLexerror);
965 break;
966 case SCE_JULIA_CHAR:
967 resumeCharacter(sc, options.highlightLexerror);
968 break;
969 case SCE_JULIA_DOCSTRING:
970 resumeString(sc, true, !israwstring);
971 if (sc.state == SCE_JULIA_DEFAULT && israwstring) {
972 israwstring = false;
973 }
974 break;
975 case SCE_JULIA_STRING:
976 resumeString(sc, false, !israwstring);
977 if (sc.state == SCE_JULIA_DEFAULT && israwstring) {
978 israwstring = false;
979 }
980 break;
981 case SCE_JULIA_COMMAND:
982 resumeCommand(sc, triple_backtick, !israwstring);
983 break;
984 case SCE_JULIA_MACRO:
985 if (IsASpace(sc.ch) || ! IsIdentifierCharacter(sc.ch)) {
986 sc.SetState(SCE_JULIA_DEFAULT);
987 }
988 break;
989 case SCE_JULIA_SYMBOL:
990 if (! IsIdentifierCharacter(sc.ch)) {
991 sc.SetState(SCE_JULIA_DEFAULT);
992 }
993 break;
994 case SCE_JULIA_COMMENT:
995 if( commentDepth > 0 ) {
996 // end or start of a nested a block comment
997 if ( sc.ch == '=' && sc.chNext == '#') {
998 commentDepth --;
999 sc.Forward();
1000
1001 if (commentDepth == 0) {
1002 sc.ForwardSetState(SCE_JULIA_DEFAULT);
1003 }
1004 } else if( sc.ch == '#' && sc.chNext == '=') {
1005 commentDepth ++;
1006 sc.Forward();
1007 }
1008 } else {
1009 // single line comment
1010 if (sc.atLineEnd || sc.ch == '\r' || sc.ch == '\n') {
1011 sc.SetState(SCE_JULIA_DEFAULT);
1012 transpose = false;
1013 }
1014 }
1015 break;
1016 }
1017
1018 // check start of a new state
1019 if (sc.state == SCE_JULIA_DEFAULT) {
1020 if (sc.ch == '#') {
1021 sc.SetState(SCE_JULIA_COMMENT);
1022 // increment depth if we are a block comment
1023 if(sc.chNext == '=') {
1024 commentDepth ++;
1025 sc.Forward();
1026 }
1027 } else if (sc.ch == '!') {
1028 sc.SetState(SCE_JULIA_OPERATOR);
1029 } else if (sc.ch == '\'') {
1030 if (transpose) {
1031 sc.SetState(SCE_JULIA_OPERATOR);
1032 } else {
1033 sc.SetState(SCE_JULIA_CHAR);
1034 }
1035 } else if (sc.ch == '\"') {
1036 istripledocstring = (sc.chNext == '\"' && sc.GetRelativeCharacter(2) == '\"');
1037 if (istripledocstring) {
1038 sc.SetState(SCE_JULIA_DOCSTRING);
1039 // Move to the end of the triple quotes
1040 Sci_PositionU nextIndex = sc.currentPos + 2;
1041 while (nextIndex > sc.currentPos && sc.More()) {
1042 sc.Forward();
1043 }
1044 } else {
1045 sc.SetState(SCE_JULIA_STRING);
1046 }
1047 } else if (sc.ch == '`') {
1048 triple_backtick = (sc.chNext == '`' && sc.GetRelativeCharacter(2) == '`');
1049 sc.SetState(SCE_JULIA_COMMAND);
1050 if (triple_backtick) {
1051 // Move to the end of the triple backticks
1052 Sci_PositionU nextIndex = sc.currentPos + 2;
1053 while (nextIndex > sc.currentPos && sc.More()) {
1054 sc.Forward();
1055 }
1056 }
1057 } else if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
1058 initNumber(sc, base, with_dot);
1059 } else if (IsIdentifierFirstCharacter(sc.ch)) {
1060 sc.SetState(SCE_JULIA_IDENTIFIER);
1061 transpose = true;
1062 } else if (sc.ch == '@') {
1063 sc.SetState(SCE_JULIA_MACRO);
1064 transpose = false;
1065
1066 // Several parsing of operators, should keep the order of `if` blocks
1067 } else if ((sc.ch == ':' || sc.ch == '<' || sc.ch == '>') && sc.chNext == ':') {
1068 sc.SetState(SCE_JULIA_TYPEOPERATOR);
1069 sc.Forward();
1070 // Highlight the next identifier, if option is set
1071 if (options.highlightTypeannotation &&
1072 IsIdentifierFirstCharacter(sc.chNext)) {
1073 sc.ForwardSetState(SCE_JULIA_TYPEANNOT);
1074 }
1075 } else if (sc.ch == ':') {
1076 // TODO: improve detection of range
1077 // should be solved with begin-end parsing
1078 // `push!(arr, s1 :s2)` and `a[begin :end]
1079 if (IsIdentifierFirstCharacter(sc.chNext) &&
1080 ! IsIdentifierCharacter(sc.chPrev) &&
1081 sc.chPrev != ')' && sc.chPrev != ']' ) {
1082 sc.SetState(SCE_JULIA_SYMBOL);
1083 } else {
1084 sc.SetState(SCE_JULIA_OPERATOR);
1085 }
1086 } else if (IsJuliaParen(sc.ch)) {
1087 if (sc.ch == '[') {
1088 list_comprehension ++;
1089 indexing_level ++;
1090 } else if (sc.ch == ']' && (indexing_level > 0)) {
1091 list_comprehension --;
1092 indexing_level --;
1093 } else if (sc.ch == '(') {
1094 list_comprehension ++;
1095 } else if (sc.ch == ')' && (list_comprehension > 0)) {
1096 list_comprehension --;
1097 }
1098
1099 if (sc.ch == ')' || sc.ch == ']' || sc.ch == '}') {
1100 transpose = true;
1101 } else {
1102 transpose = false;
1103 }
1104 sc.SetState(SCE_JULIA_BRACKET);
1105 } else if (IsOperatorFirstCharacter(sc.ch)) {
1106 transpose = false;
1107 sc.SetState(SCE_JULIA_OPERATOR);
1108 } else {
1109 transpose = false;
1110 }
1111 }
1112
1113 // update the line information (used for line-by-line lexing and folding)
1114 if (sc.atLineEnd) {
1115 // set the line state to the current state
1116 curLine = styler.GetLine(sc.currentPos);
1117
1118 lineState = ((transpose ? 1 : 0) << 0) |
1119 ((istripledocstring ? 1 : 0) << 1) |
1120 ((triple_backtick ? 1 : 0) << 2) |
1121 ((israwstring ? 1 : 0) << 3) |
1122 ((indexing_level & 0x0F) << 4) |
1123 ((list_comprehension & 0x0F) << 8) |
1124 ((commentDepth & 0x0F) << 12);
1125 styler.SetLineState(curLine, lineState);
1126 }
1127 }
1128 sc.Complete();
1129 }
1130
Fold(Sci_PositionU startPos,Sci_Position length,int initStyle,IDocument * pAccess)1131 void SCI_METHOD LexerJulia::Fold(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess) {
1132
1133 if (!options.fold)
1134 return;
1135
1136 LexAccessor styler(pAccess);
1137
1138 Sci_PositionU endPos = startPos + length;
1139 int visibleChars = 0;
1140 Sci_Position lineCurrent = styler.GetLine(startPos);
1141 int levelCurrent = SC_FOLDLEVELBASE;
1142 int lineState = 0;
1143 if (lineCurrent > 0) {
1144 levelCurrent = styler.LevelAt(lineCurrent-1) >> 16;
1145 lineState = styler.GetLineState(lineCurrent-1);
1146 }
1147
1148 // level of nested brackets
1149 int indexing_level = (int)((lineState >> 4) & 0x0F); // 4 bits of bracket nesting counter
1150 // level of nested parenthesis or brackets
1151 int list_comprehension = (int)((lineState >> 8) & 0x0F); // 4 bits of parenthesis nesting counter
1152 //int commentDepth = (int)((lineState >> 12) & 0x0F); // 4 bits of nested comment counter
1153
1154 Sci_PositionU lineStartNext = styler.LineStart(lineCurrent+1);
1155 int levelNext = levelCurrent;
1156 char chNext = styler[startPos];
1157 int stylePrev = styler.StyleAt(startPos - 1);
1158 int styleNext = styler.StyleAt(startPos);
1159 int style = initStyle;
1160 char word[100];
1161 int wordlen = 0;
1162 for (Sci_PositionU i = startPos; i < endPos; i++) {
1163 char ch = chNext;
1164 chNext = styler.SafeGetCharAt(i + 1);
1165 style = styleNext;
1166 styleNext = styler.StyleAt(i + 1);
1167 bool atEOL = i == (lineStartNext-1);
1168
1169 // a start/end of comment block
1170 if (options.foldComment && style == SCE_JULIA_COMMENT) {
1171 // start of block comment
1172 if (ch == '#' && chNext == '=') {
1173 levelNext ++;
1174 }
1175 // end of block comment
1176 if (ch == '=' && chNext == '#' && levelNext > 0) {
1177 levelNext --;
1178 }
1179 }
1180
1181 // Syntax based folding, accounts for list comprehension
1182 if (options.foldSyntaxBased) {
1183 // list comprehension allow `for`, `if` and `begin` without `end`
1184 if (style == SCE_JULIA_BRACKET) {
1185 if (ch == '[') {
1186 list_comprehension ++;
1187 indexing_level ++;
1188 levelNext ++;
1189 } else if (ch == ']') {
1190 list_comprehension --;
1191 indexing_level --;
1192 levelNext --;
1193 } else if (ch == '(') {
1194 list_comprehension ++;
1195 levelNext ++;
1196 } else if (ch == ')') {
1197 list_comprehension --;
1198 levelNext --;
1199 }
1200 // check non-negative
1201 if (indexing_level < 0) {
1202 indexing_level = 0;
1203 }
1204 if (list_comprehension < 0) {
1205 list_comprehension = 0;
1206 }
1207 }
1208
1209 // keyword
1210 if (style == SCE_JULIA_KEYWORD1) {
1211 word[wordlen++] = static_cast<char>(ch);
1212 if (wordlen == 100) { // prevent overflow
1213 word[0] = '\0';
1214 wordlen = 1;
1215 }
1216 if (styleNext != SCE_JULIA_KEYWORD1) {
1217 word[wordlen] = '\0';
1218 wordlen = 0;
1219 if (list_comprehension <= 0 && indexing_level <= 0) {
1220 levelNext += CheckKeywordFoldPoint(word);
1221 }
1222 }
1223 }
1224 }
1225
1226 // Docstring
1227 if (options.foldDocstring) {
1228 if (stylePrev != SCE_JULIA_DOCSTRING && style == SCE_JULIA_DOCSTRING) {
1229 levelNext ++;
1230 } else if (style == SCE_JULIA_DOCSTRING && styleNext != SCE_JULIA_DOCSTRING) {
1231 levelNext --;
1232 }
1233 }
1234
1235 // check non-negative level
1236 if (levelNext < 0) {
1237 levelNext = 0;
1238 }
1239
1240 if (!IsASpace(ch)) {
1241 visibleChars++;
1242 }
1243 stylePrev = style;
1244
1245 if (atEOL || (i == endPos-1)) {
1246 int levelUse = levelCurrent;
1247 int lev = levelUse | levelNext << 16;
1248 if (visibleChars == 0 && options.foldCompact) {
1249 lev |= SC_FOLDLEVELWHITEFLAG;
1250 }
1251 if (levelUse < levelNext) {
1252 lev |= SC_FOLDLEVELHEADERFLAG;
1253 }
1254 if (lev != styler.LevelAt(lineCurrent)) {
1255 styler.SetLevel(lineCurrent, lev);
1256 }
1257 lineCurrent++;
1258 lineStartNext = styler.LineStart(lineCurrent+1);
1259 levelCurrent = levelNext;
1260 if (atEOL && (i == static_cast<Sci_PositionU>(styler.Length() - 1))) {
1261 // There is an empty line at end of file so give it same level and empty
1262 styler.SetLevel(lineCurrent, (levelCurrent | levelCurrent << 16) | SC_FOLDLEVELWHITEFLAG);
1263 }
1264 visibleChars = 0;
1265 }
1266 }
1267 }
1268
1269 LexerModule lmJulia(SCLEX_JULIA, LexerJulia::LexerFactoryJulia, "julia", juliaWordLists);
1270