1 /******************************************************************
4 * A haskell lexer for the scintilla code control.
5 * Some stuff "lended" from LexPython.cxx and LexCPP.cxx.
6 * External lexer stuff inspired from the caml external lexer.
7 * Folder copied from Python's.
9 * Written by Tobias Engvall - tumm at dtek dot chalmers dot se
11 * Several bug fixes by Krasimir Angelov - kr.angelov at gmail.com
13 * Improved by kudah <kudahkukarek@gmail.com>
16 * * A proper lexical folder to fold group declarations, comments, pragmas,
17 * #ifdefs, explicit layout, lists, tuples, quasi-quotes, splces, etc, etc,
20 *****************************************************************/
32 #include "Scintilla.h"
35 #include "PropSetSimple.h"
37 #include "LexAccessor.h"
39 #include "StyleContext.h"
40 #include "CharacterSet.h"
41 #include "CharacterCategory.h"
42 #include "LexerModule.h"
43 #include "OptionSet.h"
46 using namespace Scintilla
;
49 // See https://github.com/ghc/ghc/blob/master/compiler/parser/Lexer.x#L1682
50 // Note, letter modifiers are prohibited.
52 static int u_iswupper (int ch
) {
53 CharacterCategory c
= CategoriseCharacter(ch
);
54 return c
== ccLu
|| c
== ccLt
;
57 static int u_iswalpha (int ch
) {
58 CharacterCategory c
= CategoriseCharacter(ch
);
59 return c
== ccLl
|| c
== ccLu
|| c
== ccLt
|| c
== ccLo
;
62 static int u_iswalnum (int ch
) {
63 CharacterCategory c
= CategoriseCharacter(ch
);
64 return c
== ccLl
|| c
== ccLu
|| c
== ccLt
|| c
== ccLo
65 || c
== ccNd
|| c
== ccNo
;
68 static int u_IsHaskellSymbol(int ch
) {
69 CharacterCategory c
= CategoriseCharacter(ch
);
70 return c
== ccPc
|| c
== ccPd
|| c
== ccPo
71 || c
== ccSm
|| c
== ccSc
|| c
== ccSk
|| c
== ccSo
;
74 static inline bool IsHaskellLetter(const int ch
) {
76 return (ch
>= 'a' && ch
<= 'z')
77 || (ch
>= 'A' && ch
<= 'Z');
79 return u_iswalpha(ch
) != 0;
83 static inline bool IsHaskellAlphaNumeric(const int ch
) {
85 return IsAlphaNumeric(ch
);
87 return u_iswalnum(ch
) != 0;
91 static inline bool IsHaskellUpperCase(const int ch
) {
93 return ch
>= 'A' && ch
<= 'Z';
95 return u_iswupper(ch
) != 0;
99 static inline bool IsAnHaskellOperatorChar(const int ch
) {
102 ( ch
== '!' || ch
== '#' || ch
== '$' || ch
== '%'
103 || ch
== '&' || ch
== '*' || ch
== '+' || ch
== '-'
104 || ch
== '.' || ch
== '/' || ch
== ':' || ch
== '<'
105 || ch
== '=' || ch
== '>' || ch
== '?' || ch
== '@'
106 || ch
== '^' || ch
== '|' || ch
== '~' || ch
== '\\');
108 return u_IsHaskellSymbol(ch
) != 0;
112 static inline bool IsAHaskellWordStart(const int ch
) {
113 return IsHaskellLetter(ch
) || ch
== '_';
116 static inline bool IsAHaskellWordChar(const int ch
) {
117 return ( IsHaskellAlphaNumeric(ch
)
122 static inline bool IsCommentBlockStyle(int style
) {
123 return (style
>= SCE_HA_COMMENTBLOCK
&& style
<= SCE_HA_COMMENTBLOCK3
);
126 static inline bool IsCommentStyle(int style
) {
127 return (style
>= SCE_HA_COMMENTLINE
&& style
<= SCE_HA_COMMENTBLOCK3
)
128 || ( style
== SCE_HA_LITERATE_COMMENT
129 || style
== SCE_HA_LITERATE_CODEDELIM
);
132 // styles which do not belong to Haskell, but to external tools
133 static inline bool IsExternalStyle(int style
) {
134 return ( style
== SCE_HA_PREPROCESSOR
135 || style
== SCE_HA_LITERATE_COMMENT
136 || style
== SCE_HA_LITERATE_CODEDELIM
);
139 static inline int CommentBlockStyleFromNestLevel(const unsigned int nestLevel
) {
140 return SCE_HA_COMMENTBLOCK
+ (nestLevel
% 3);
143 // Mangled version of lexlib/Accessor.cxx IndentAmount.
144 // Modified to treat comment blocks as whitespace
145 // plus special case for commentline/preprocessor.
146 static int HaskellIndentAmount(Accessor
&styler
, const Sci_Position line
) {
148 // Determines the indentation level of the current line
149 // Comment blocks are treated as whitespace
151 Sci_Position pos
= styler
.LineStart(line
);
152 Sci_Position eol_pos
= styler
.LineStart(line
+ 1) - 1;
154 char ch
= styler
[pos
];
155 int style
= styler
.StyleAt(pos
);
158 bool inPrevPrefix
= line
> 0;
160 Sci_Position posPrev
= inPrevPrefix
? styler
.LineStart(line
-1) : 0;
162 while (( ch
== ' ' || ch
== '\t'
163 || IsCommentBlockStyle(style
)
164 || style
== SCE_HA_LITERATE_CODEDELIM
)
165 && (pos
< eol_pos
)) {
167 char chPrev
= styler
[posPrev
++];
168 if (chPrev
!= ' ' && chPrev
!= '\t') {
169 inPrevPrefix
= false;
173 indent
= (indent
/ 8 + 1) * 8;
174 } else { // Space or comment block
179 style
= styler
.StyleAt(pos
);
182 indent
+= SC_FOLDLEVELBASE
;
183 // if completely empty line or the start of a comment or preprocessor...
184 if ( styler
.LineStart(line
) == styler
.Length()
189 || IsCommentStyle(style
)
190 || style
== SCE_HA_PREPROCESSOR
)
191 return indent
| SC_FOLDLEVELWHITEFLAG
;
196 struct OptionsHaskell
{
202 bool stylingWithinPreprocessor
;
208 magicHash
= true; // Widespread use, enabled by default.
209 allowQuotes
= true; // Widespread use, enabled by default.
210 implicitParams
= false; // Fell out of favor, seldom used, disabled.
211 highlightSafe
= true; // Moderately used, doesn't hurt to enable.
212 cpp
= true; // Widespread use, enabled by default;
213 stylingWithinPreprocessor
= false;
221 static const char * const haskellWordListDesc
[] = {
224 "Reserved operators",
228 struct OptionSetHaskell
: public OptionSet
<OptionsHaskell
> {
230 DefineProperty("lexer.haskell.allow.hash", &OptionsHaskell::magicHash
,
231 "Set to 0 to disallow the '#' character at the end of identifiers and "
232 "literals with the haskell lexer "
233 "(GHC -XMagicHash extension)");
235 DefineProperty("lexer.haskell.allow.quotes", &OptionsHaskell::allowQuotes
,
236 "Set to 0 to disable highlighting of Template Haskell name quotations "
237 "and promoted constructors "
238 "(GHC -XTemplateHaskell and -XDataKinds extensions)");
240 DefineProperty("lexer.haskell.allow.questionmark", &OptionsHaskell::implicitParams
,
241 "Set to 1 to allow the '?' character at the start of identifiers "
242 "with the haskell lexer "
243 "(GHC & Hugs -XImplicitParams extension)");
245 DefineProperty("lexer.haskell.import.safe", &OptionsHaskell::highlightSafe
,
246 "Set to 0 to disallow \"safe\" keyword in imports "
247 "(GHC -XSafe, -XTrustworthy, -XUnsafe extensions)");
249 DefineProperty("lexer.haskell.cpp", &OptionsHaskell::cpp
,
250 "Set to 0 to disable C-preprocessor highlighting "
251 "(-XCPP extension)");
253 DefineProperty("styling.within.preprocessor", &OptionsHaskell::stylingWithinPreprocessor
,
254 "For Haskell code, determines whether all preprocessor code is styled in the "
255 "preprocessor style (0, the default) or only from the initial # to the end "
256 "of the command word(1)."
259 DefineProperty("fold", &OptionsHaskell::fold
);
261 DefineProperty("fold.comment", &OptionsHaskell::foldComment
);
263 DefineProperty("fold.compact", &OptionsHaskell::foldCompact
);
265 DefineProperty("fold.haskell.imports", &OptionsHaskell::foldImports
,
266 "Set to 1 to enable folding of import declarations");
268 DefineWordListSets(haskellWordListDesc
);
272 class LexerHaskell
: public ILexer
{
274 Sci_Position firstImportLine
;
275 int firstImportIndent
;
278 WordList reserved_operators
;
279 OptionsHaskell options
;
280 OptionSetHaskell osHaskell
;
290 ,HA_MODE_IMPORT1
= 1 // after "import", before "qualified" or "safe" or package name or module name.
291 ,HA_MODE_IMPORT2
= 2 // after module name, before "as" or "hiding".
292 ,HA_MODE_IMPORT3
= 3 // after "as", before "hiding"
293 ,HA_MODE_MODULE
= 4 // after "module", before module name.
294 ,HA_MODE_FFI
= 5 // after "foreign", before FFI keywords
295 ,HA_MODE_TYPE
= 6 // after "type" or "data", before "family"
299 LITERATE_BIRD
= 0 // if '>' is the first character on the line,
300 // color '>' as a codedelim and the rest of
302 // else if "\begin{code}" is the only word on the
303 // line except whitespace, switch to LITERATE_BLOCK
304 // otherwise color the line as a literate comment.
305 ,LITERATE_BLOCK
= 1 // if the string "\end{code}" is encountered at column
306 // 0 ignoring all later characters, color the line
307 // as a codedelim and switch to LITERATE_BIRD
308 // otherwise color the line as code.
311 struct HaskellLineInfo
{
312 unsigned int nestLevel
; // 22 bits ought to be enough for anybody
313 unsigned int nonexternalStyle
; // 5 bits, widen if number of styles goes
319 HaskellLineInfo(int state
) :
320 nestLevel (state
>> 10)
321 , nonexternalStyle ((state
>> 5) & 0x1F)
322 , pragma ((state
>> 4) & 0x1)
323 , lmode (static_cast<LiterateMode
>((state
>> 3) & 0x1))
324 , mode (static_cast<KeywordMode
>(state
& 0x7))
330 | (nonexternalStyle
<< 5)
337 inline void skipMagicHash(StyleContext
&sc
, const HashCount hashes
) const {
338 if (options
.magicHash
&& sc
.ch
== '#') {
340 if (hashes
== twoHashes
&& sc
.ch
== '#') {
342 } else if (hashes
== unlimitedHashes
) {
343 while (sc
.ch
== '#') {
350 bool LineContainsImport(const Sci_Position line
, Accessor
&styler
) const {
351 if (options
.foldImports
) {
352 Sci_Position currentPos
= styler
.LineStart(line
);
353 int style
= styler
.StyleAt(currentPos
);
355 Sci_Position eol_pos
= styler
.LineStart(line
+ 1) - 1;
357 while (currentPos
< eol_pos
) {
358 int ch
= styler
[currentPos
];
359 style
= styler
.StyleAt(currentPos
);
361 if (ch
== ' ' || ch
== '\t'
362 || IsCommentBlockStyle(style
)
363 || style
== SCE_HA_LITERATE_CODEDELIM
) {
370 return (style
== SCE_HA_KEYWORD
371 && styler
.Match(currentPos
, "import"));
377 inline int IndentAmountWithOffset(Accessor
&styler
, const Sci_Position line
) const {
378 const int indent
= HaskellIndentAmount(styler
, line
);
379 const int indentLevel
= indent
& SC_FOLDLEVELNUMBERMASK
;
380 return indentLevel
<= ((firstImportIndent
- 1) + SC_FOLDLEVELBASE
)
382 : (indentLevel
+ firstImportIndent
) | (indent
& ~SC_FOLDLEVELNUMBERMASK
);
385 inline int IndentLevelRemoveIndentOffset(const int indentLevel
) const {
386 return indentLevel
<= ((firstImportIndent
- 1) + SC_FOLDLEVELBASE
)
388 : indentLevel
- firstImportIndent
;
392 LexerHaskell(bool literate_
)
393 : literate(literate_
)
394 , firstImportLine(-1)
395 , firstImportIndent(0)
397 virtual ~LexerHaskell() {}
399 void SCI_METHOD
Release() override
{
403 int SCI_METHOD
Version() const override
{
407 const char * SCI_METHOD
PropertyNames() override
{
408 return osHaskell
.PropertyNames();
411 int SCI_METHOD
PropertyType(const char *name
) override
{
412 return osHaskell
.PropertyType(name
);
415 const char * SCI_METHOD
DescribeProperty(const char *name
) override
{
416 return osHaskell
.DescribeProperty(name
);
419 Sci_Position SCI_METHOD
PropertySet(const char *key
, const char *val
) override
;
421 const char * SCI_METHOD
DescribeWordListSets() override
{
422 return osHaskell
.DescribeWordListSets();
425 Sci_Position SCI_METHOD
WordListSet(int n
, const char *wl
) override
;
427 void SCI_METHOD
Lex(Sci_PositionU startPos
, Sci_Position length
, int initStyle
, IDocument
*pAccess
) override
;
429 void SCI_METHOD
Fold(Sci_PositionU startPos
, Sci_Position length
, int initStyle
, IDocument
*pAccess
) override
;
431 void * SCI_METHOD
PrivateCall(int, void *) override
{
435 static ILexer
*LexerFactoryHaskell() {
436 return new LexerHaskell(false);
439 static ILexer
*LexerFactoryLiterateHaskell() {
440 return new LexerHaskell(true);
444 Sci_Position SCI_METHOD
LexerHaskell::PropertySet(const char *key
, const char *val
) {
445 if (osHaskell
.PropertySet(&options
, key
, val
)) {
451 Sci_Position SCI_METHOD
LexerHaskell::WordListSet(int n
, const char *wl
) {
452 WordList
*wordListN
= 0;
455 wordListN
= &keywords
;
461 wordListN
= &reserved_operators
;
464 Sci_Position firstModification
= -1;
468 if (*wordListN
!= wlNew
) {
470 firstModification
= 0;
473 return firstModification
;
476 void SCI_METHOD
LexerHaskell::Lex(Sci_PositionU startPos
, Sci_Position length
, int initStyle
477 ,IDocument
*pAccess
) {
478 LexAccessor
styler(pAccess
);
480 Sci_Position lineCurrent
= styler
.GetLine(startPos
);
482 HaskellLineInfo hs
= HaskellLineInfo(lineCurrent
? styler
.GetLineState(lineCurrent
-1) : 0);
484 // Do not leak onto next line
485 if (initStyle
== SCE_HA_STRINGEOL
)
486 initStyle
= SCE_HA_DEFAULT
;
487 else if (initStyle
== SCE_HA_LITERATE_CODEDELIM
)
488 initStyle
= hs
.nonexternalStyle
;
490 StyleContext
sc(startPos
, length
, initStyle
, styler
);
495 bool inDashes
= false;
496 bool alreadyInTheMiddleOfOperator
= false;
498 assert(!(IsCommentBlockStyle(initStyle
) && hs
.nestLevel
== 0));
501 // Check for state end
503 if (!IsExternalStyle(sc
.state
)) {
504 hs
.nonexternalStyle
= sc
.state
;
507 // For lexer to work, states should unconditionally forward at least one
509 // If they don't, they should still check if they are at line end and
511 // If a state forwards more than one character, it should check every time
512 // that it is not a line end and cease forwarding otherwise.
514 // Remember the line state for future incremental lexing
515 styler
.SetLineState(lineCurrent
, hs
.ToLineState());
519 // Handle line continuation generically.
520 if (sc
.ch
== '\\' && (sc
.chNext
== '\n' || sc
.chNext
== '\r')
521 && ( sc
.state
== SCE_HA_STRING
522 || sc
.state
== SCE_HA_PREPROCESSOR
)) {
523 // Remember the line state for future incremental lexing
524 styler
.SetLineState(lineCurrent
, hs
.ToLineState());
528 if (sc
.ch
== '\r' && sc
.chNext
== '\n') {
536 if (sc
.atLineStart
) {
538 if (sc
.state
== SCE_HA_STRING
|| sc
.state
== SCE_HA_CHARACTER
) {
539 // Prevent SCE_HA_STRINGEOL from leaking back to previous line
540 sc
.SetState(sc
.state
);
543 if (literate
&& hs
.lmode
== LITERATE_BIRD
) {
544 if (!IsExternalStyle(sc
.state
)) {
545 sc
.SetState(SCE_HA_LITERATE_COMMENT
);
552 if ( literate
&& hs
.lmode
== LITERATE_BIRD
&& sc
.atLineStart
554 sc
.SetState(SCE_HA_LITERATE_CODEDELIM
);
555 sc
.ForwardSetState(hs
.nonexternalStyle
);
557 else if (literate
&& hs
.lmode
== LITERATE_BIRD
&& sc
.atLineStart
558 && ( sc
.ch
== ' ' || sc
.ch
== '\t'
559 || sc
.Match("\\begin{code}"))) {
560 sc
.SetState(sc
.state
);
562 while ((sc
.ch
== ' ' || sc
.ch
== '\t') && sc
.More())
565 if (sc
.Match("\\begin{code}")) {
566 sc
.Forward(static_cast<int>(strlen("\\begin{code}")));
570 while (!sc
.atLineEnd
&& sc
.More()) {
571 if (sc
.ch
!= ' ' && sc
.ch
!= '\t') {
578 sc
.ChangeState(SCE_HA_LITERATE_CODEDELIM
); // color the line end
579 hs
.lmode
= LITERATE_BLOCK
;
583 else if (literate
&& hs
.lmode
== LITERATE_BLOCK
&& sc
.atLineStart
584 && sc
.Match("\\end{code}")) {
585 sc
.SetState(SCE_HA_LITERATE_CODEDELIM
);
587 sc
.Forward(static_cast<int>(strlen("\\end{code}")));
589 while (!sc
.atLineEnd
&& sc
.More()) {
593 sc
.SetState(SCE_HA_LITERATE_COMMENT
);
594 hs
.lmode
= LITERATE_BIRD
;
597 else if (sc
.atLineStart
&& sc
.ch
== '#' && options
.cpp
598 && (!options
.stylingWithinPreprocessor
|| sc
.state
== SCE_HA_DEFAULT
)) {
599 sc
.SetState(SCE_HA_PREPROCESSOR
);
603 else if (sc
.state
== SCE_HA_LITERATE_COMMENT
) {
606 else if (sc
.state
== SCE_HA_LITERATE_CODEDELIM
) {
607 sc
.ForwardSetState(hs
.nonexternalStyle
);
610 else if (sc
.state
== SCE_HA_PREPROCESSOR
) {
612 sc
.SetState(options
.stylingWithinPreprocessor
614 : hs
.nonexternalStyle
);
615 sc
.Forward(); // prevent double counting a line
616 } else if (options
.stylingWithinPreprocessor
&& !IsHaskellLetter(sc
.ch
)) {
617 sc
.SetState(SCE_HA_DEFAULT
);
624 else if (sc
.state
== SCE_HA_OPERATOR
) {
625 int style
= SCE_HA_OPERATOR
;
628 && !alreadyInTheMiddleOfOperator
630 && !( sc
.chNext
== ':'
631 && !IsAnHaskellOperatorChar(sc
.GetRelative(2)))) {
632 style
= SCE_HA_CAPITAL
;
635 alreadyInTheMiddleOfOperator
= false;
637 while (IsAnHaskellOperatorChar(sc
.ch
))
641 sc
.GetCurrent(s
, sizeof(s
));
643 if (reserved_operators
.InList(s
))
644 style
= SCE_HA_RESERVED_OPERATOR
;
646 sc
.ChangeState(style
);
647 sc
.SetState(SCE_HA_DEFAULT
);
650 else if (sc
.state
== SCE_HA_STRING
) {
652 sc
.ChangeState(SCE_HA_STRINGEOL
);
653 sc
.ForwardSetState(SCE_HA_DEFAULT
);
654 } else if (sc
.ch
== '\"') {
656 skipMagicHash(sc
, oneHash
);
657 sc
.SetState(SCE_HA_DEFAULT
);
658 } else if (sc
.ch
== '\\') {
665 else if (sc
.state
== SCE_HA_CHARACTER
) {
667 sc
.ChangeState(SCE_HA_STRINGEOL
);
668 sc
.ForwardSetState(SCE_HA_DEFAULT
);
669 } else if (sc
.ch
== '\'') {
671 skipMagicHash(sc
, oneHash
);
672 sc
.SetState(SCE_HA_DEFAULT
);
673 } else if (sc
.ch
== '\\') {
680 else if (sc
.state
== SCE_HA_NUMBER
) {
682 sc
.SetState(SCE_HA_DEFAULT
);
683 sc
.Forward(); // prevent double counting a line
684 } else if (IsADigit(sc
.ch
, base
)) {
686 } else if (sc
.ch
=='.' && dot
&& IsADigit(sc
.chNext
, base
)) {
689 } else if ((base
== 10) &&
690 (sc
.ch
== 'e' || sc
.ch
== 'E') &&
691 (IsADigit(sc
.chNext
) || sc
.chNext
== '+' || sc
.chNext
== '-')) {
693 if (sc
.ch
== '+' || sc
.ch
== '-')
696 skipMagicHash(sc
, twoHashes
);
697 sc
.SetState(SCE_HA_DEFAULT
);
700 // Keyword or Identifier
701 else if (sc
.state
== SCE_HA_IDENTIFIER
) {
702 int style
= IsHaskellUpperCase(sc
.ch
) ? SCE_HA_CAPITAL
: SCE_HA_IDENTIFIER
;
704 assert(IsAHaskellWordStart(sc
.ch
));
709 if (IsAHaskellWordChar(sc
.ch
)) {
711 } else if (sc
.ch
== '.' && style
== SCE_HA_CAPITAL
) {
712 if (IsHaskellUpperCase(sc
.chNext
)) {
714 style
= SCE_HA_CAPITAL
;
715 } else if (IsAHaskellWordStart(sc
.chNext
)) {
717 style
= SCE_HA_IDENTIFIER
;
718 } else if (IsAnHaskellOperatorChar(sc
.chNext
)) {
720 style
= sc
.ch
== ':' ? SCE_HA_CAPITAL
: SCE_HA_OPERATOR
;
721 while (IsAnHaskellOperatorChar(sc
.ch
))
732 skipMagicHash(sc
, unlimitedHashes
);
735 sc
.GetCurrent(s
, sizeof(s
));
737 KeywordMode new_mode
= HA_MODE_DEFAULT
;
739 if (keywords
.InList(s
)) {
740 style
= SCE_HA_KEYWORD
;
741 } else if (style
== SCE_HA_CAPITAL
) {
742 if (hs
.mode
== HA_MODE_IMPORT1
|| hs
.mode
== HA_MODE_IMPORT3
) {
743 style
= SCE_HA_MODULE
;
744 new_mode
= HA_MODE_IMPORT2
;
745 } else if (hs
.mode
== HA_MODE_MODULE
) {
746 style
= SCE_HA_MODULE
;
748 } else if (hs
.mode
== HA_MODE_IMPORT1
&&
749 strcmp(s
,"qualified") == 0) {
750 style
= SCE_HA_KEYWORD
;
751 new_mode
= HA_MODE_IMPORT1
;
752 } else if (options
.highlightSafe
&&
753 hs
.mode
== HA_MODE_IMPORT1
&&
754 strcmp(s
,"safe") == 0) {
755 style
= SCE_HA_KEYWORD
;
756 new_mode
= HA_MODE_IMPORT1
;
757 } else if (hs
.mode
== HA_MODE_IMPORT2
) {
758 if (strcmp(s
,"as") == 0) {
759 style
= SCE_HA_KEYWORD
;
760 new_mode
= HA_MODE_IMPORT3
;
761 } else if (strcmp(s
,"hiding") == 0) {
762 style
= SCE_HA_KEYWORD
;
764 } else if (hs
.mode
== HA_MODE_TYPE
) {
765 if (strcmp(s
,"family") == 0)
766 style
= SCE_HA_KEYWORD
;
769 if (hs
.mode
== HA_MODE_FFI
) {
771 style
= SCE_HA_KEYWORD
;
772 new_mode
= HA_MODE_FFI
;
776 sc
.ChangeState(style
);
777 sc
.SetState(SCE_HA_DEFAULT
);
779 if (strcmp(s
,"import") == 0 && hs
.mode
!= HA_MODE_FFI
)
780 new_mode
= HA_MODE_IMPORT1
;
781 else if (strcmp(s
,"module") == 0)
782 new_mode
= HA_MODE_MODULE
;
783 else if (strcmp(s
,"foreign") == 0)
784 new_mode
= HA_MODE_FFI
;
785 else if (strcmp(s
,"type") == 0
786 || strcmp(s
,"data") == 0)
787 new_mode
= HA_MODE_TYPE
;
794 else if (sc
.state
== SCE_HA_COMMENTLINE
) {
796 sc
.SetState(hs
.pragma
? SCE_HA_PRAGMA
: SCE_HA_DEFAULT
);
797 sc
.Forward(); // prevent double counting a line
798 } else if (inDashes
&& sc
.ch
!= '-' && !hs
.pragma
) {
800 if (IsAnHaskellOperatorChar(sc
.ch
)) {
801 alreadyInTheMiddleOfOperator
= true;
802 sc
.ChangeState(SCE_HA_OPERATOR
);
809 else if (IsCommentBlockStyle(sc
.state
)) {
810 if (sc
.Match('{','-')) {
811 sc
.SetState(CommentBlockStyleFromNestLevel(hs
.nestLevel
));
814 } else if (sc
.Match('-','}')) {
816 assert(hs
.nestLevel
> 0);
817 if (hs
.nestLevel
> 0)
821 ? (hs
.pragma
? SCE_HA_PRAGMA
: SCE_HA_DEFAULT
)
822 : CommentBlockStyleFromNestLevel(hs
.nestLevel
- 1));
828 else if (sc
.state
== SCE_HA_PRAGMA
) {
829 if (sc
.Match("#-}")) {
832 sc
.SetState(SCE_HA_DEFAULT
);
833 } else if (sc
.Match('-','-')) {
834 sc
.SetState(SCE_HA_COMMENTLINE
);
837 } else if (sc
.Match('{','-')) {
838 sc
.SetState(CommentBlockStyleFromNestLevel(hs
.nestLevel
));
846 else if (sc
.state
== SCE_HA_DEFAULT
) {
848 if (IsADigit(sc
.ch
)) {
849 hs
.mode
= HA_MODE_DEFAULT
;
851 sc
.SetState(SCE_HA_NUMBER
);
852 if (sc
.ch
== '0' && (sc
.chNext
== 'X' || sc
.chNext
== 'x')) {
853 // Match anything starting with "0x" or "0X", too
857 } else if (sc
.ch
== '0' && (sc
.chNext
== 'O' || sc
.chNext
== 'o')) {
858 // Match anything starting with "0o" or "0O", too
869 else if (sc
.Match("{-#")) {
871 sc
.SetState(SCE_HA_PRAGMA
);
875 else if (sc
.Match('-','-')) {
876 sc
.SetState(SCE_HA_COMMENTLINE
);
881 else if (sc
.Match('{','-')) {
882 sc
.SetState(CommentBlockStyleFromNestLevel(hs
.nestLevel
));
887 else if (sc
.ch
== '\"') {
888 sc
.SetState(SCE_HA_STRING
);
891 // Character or quoted name or promoted term
892 else if (sc
.ch
== '\'') {
893 hs
.mode
= HA_MODE_DEFAULT
;
895 sc
.SetState(SCE_HA_CHARACTER
);
898 if (options
.allowQuotes
) {
900 if (sc
.ch
=='\'' && IsAHaskellWordStart(sc
.chNext
)) {
902 sc
.ChangeState(SCE_HA_IDENTIFIER
);
903 } else if (sc
.chNext
!= '\'') {
904 // Quoted name 'n or promoted constructor 'N
905 if (IsAHaskellWordStart(sc
.ch
)) {
906 sc
.ChangeState(SCE_HA_IDENTIFIER
);
907 // Promoted constructor operator ':~>
908 } else if (sc
.ch
== ':') {
909 alreadyInTheMiddleOfOperator
= false;
910 sc
.ChangeState(SCE_HA_OPERATOR
);
911 // Promoted list or tuple '[T]
912 } else if (sc
.ch
== '[' || sc
.ch
== '(') {
913 sc
.ChangeState(SCE_HA_OPERATOR
);
914 sc
.ForwardSetState(SCE_HA_DEFAULT
);
919 // Operator starting with '?' or an implicit parameter
920 else if (sc
.ch
== '?') {
921 hs
.mode
= HA_MODE_DEFAULT
;
923 alreadyInTheMiddleOfOperator
= false;
924 sc
.SetState(SCE_HA_OPERATOR
);
926 if ( options
.implicitParams
927 && IsAHaskellWordStart(sc
.chNext
)
928 && !IsHaskellUpperCase(sc
.chNext
)) {
930 sc
.ChangeState(SCE_HA_IDENTIFIER
);
934 else if (IsAnHaskellOperatorChar(sc
.ch
)) {
935 hs
.mode
= HA_MODE_DEFAULT
;
937 sc
.SetState(SCE_HA_OPERATOR
);
939 // Braces and punctuation
940 else if (sc
.ch
== ',' || sc
.ch
== ';'
941 || sc
.ch
== '(' || sc
.ch
== ')'
942 || sc
.ch
== '[' || sc
.ch
== ']'
943 || sc
.ch
== '{' || sc
.ch
== '}') {
944 sc
.SetState(SCE_HA_OPERATOR
);
945 sc
.ForwardSetState(SCE_HA_DEFAULT
);
947 // Keyword or Identifier
948 else if (IsAHaskellWordStart(sc
.ch
)) {
949 sc
.SetState(SCE_HA_IDENTIFIER
);
950 // Something we don't care about
955 // This branch should never be reached.
964 void SCI_METHOD
LexerHaskell::Fold(Sci_PositionU startPos
, Sci_Position length
, int // initStyle
965 ,IDocument
*pAccess
) {
969 Accessor
styler(pAccess
, NULL
);
971 Sci_Position lineCurrent
= styler
.GetLine(startPos
);
973 if (lineCurrent
<= firstImportLine
) {
974 firstImportLine
= -1; // readjust first import position
975 firstImportIndent
= 0;
978 const Sci_Position maxPos
= startPos
+ length
;
979 const Sci_Position maxLines
=
980 maxPos
== styler
.Length()
981 ? styler
.GetLine(maxPos
)
982 : styler
.GetLine(maxPos
- 1); // Requested last line
983 const Sci_Position docLines
= styler
.GetLine(styler
.Length()); // Available last line
985 // Backtrack to previous non-blank line so we can determine indent level
986 // for any white space lines
987 // and so we can fix any preceding fold level (which is why we go back
988 // at least one line in all cases)
989 bool importHere
= LineContainsImport(lineCurrent
, styler
);
990 int indentCurrent
= IndentAmountWithOffset(styler
, lineCurrent
);
992 while (lineCurrent
> 0) {
994 importHere
= LineContainsImport(lineCurrent
, styler
);
995 indentCurrent
= IndentAmountWithOffset(styler
, lineCurrent
);
996 if (!(indentCurrent
& SC_FOLDLEVELWHITEFLAG
))
1000 int indentCurrentLevel
= indentCurrent
& SC_FOLDLEVELNUMBERMASK
;
1003 indentCurrentLevel
= IndentLevelRemoveIndentOffset(indentCurrentLevel
);
1004 if (firstImportLine
== -1) {
1005 firstImportLine
= lineCurrent
;
1006 firstImportIndent
= (1 + indentCurrentLevel
) - SC_FOLDLEVELBASE
;
1008 if (firstImportLine
!= lineCurrent
) {
1009 indentCurrentLevel
++;
1013 indentCurrent
= indentCurrentLevel
| (indentCurrent
& ~SC_FOLDLEVELNUMBERMASK
);
1015 // Process all characters to end of requested range
1016 //that hangs over the end of the range. Cap processing in all cases
1017 // to end of document.
1018 while (lineCurrent
<= docLines
&& lineCurrent
<= maxLines
) {
1021 Sci_Position lineNext
= lineCurrent
+ 1;
1023 int indentNext
= indentCurrent
;
1025 if (lineNext
<= docLines
) {
1026 // Information about next line is only available if not at end of document
1027 importHere
= LineContainsImport(lineNext
, styler
);
1028 indentNext
= IndentAmountWithOffset(styler
, lineNext
);
1030 if (indentNext
& SC_FOLDLEVELWHITEFLAG
)
1031 indentNext
= SC_FOLDLEVELWHITEFLAG
| indentCurrentLevel
;
1033 // Skip past any blank lines for next indent level info; we skip also
1034 // comments (all comments, not just those starting in column 0)
1035 // which effectively folds them into surrounding code rather
1036 // than screwing up folding.
1038 while (lineNext
< docLines
&& (indentNext
& SC_FOLDLEVELWHITEFLAG
)) {
1040 importHere
= LineContainsImport(lineNext
, styler
);
1041 indentNext
= IndentAmountWithOffset(styler
, lineNext
);
1044 int indentNextLevel
= indentNext
& SC_FOLDLEVELNUMBERMASK
;
1047 indentNextLevel
= IndentLevelRemoveIndentOffset(indentNextLevel
);
1048 if (firstImportLine
== -1) {
1049 firstImportLine
= lineNext
;
1050 firstImportIndent
= (1 + indentNextLevel
) - SC_FOLDLEVELBASE
;
1052 if (firstImportLine
!= lineNext
) {
1057 indentNext
= indentNextLevel
| (indentNext
& ~SC_FOLDLEVELNUMBERMASK
);
1059 const int levelBeforeComments
= Maximum(indentCurrentLevel
,indentNextLevel
);
1061 // Now set all the indent levels on the lines we skipped
1062 // Do this from end to start. Once we encounter one line
1063 // which is indented more than the line after the end of
1064 // the comment-block, use the level of the block before
1066 Sci_Position skipLine
= lineNext
;
1067 int skipLevel
= indentNextLevel
;
1069 while (--skipLine
> lineCurrent
) {
1070 int skipLineIndent
= IndentAmountWithOffset(styler
, skipLine
);
1072 if (options
.foldCompact
) {
1073 if ((skipLineIndent
& SC_FOLDLEVELNUMBERMASK
) > indentNextLevel
) {
1074 skipLevel
= levelBeforeComments
;
1077 int whiteFlag
= skipLineIndent
& SC_FOLDLEVELWHITEFLAG
;
1079 styler
.SetLevel(skipLine
, skipLevel
| whiteFlag
);
1081 if ( (skipLineIndent
& SC_FOLDLEVELNUMBERMASK
) > indentNextLevel
1082 && !(skipLineIndent
& SC_FOLDLEVELWHITEFLAG
)) {
1083 skipLevel
= levelBeforeComments
;
1086 styler
.SetLevel(skipLine
, skipLevel
);
1090 int lev
= indentCurrent
;
1092 if (!(indentCurrent
& SC_FOLDLEVELWHITEFLAG
)) {
1093 if ((indentCurrent
& SC_FOLDLEVELNUMBERMASK
) < (indentNext
& SC_FOLDLEVELNUMBERMASK
))
1094 lev
|= SC_FOLDLEVELHEADERFLAG
;
1097 // Set fold level for this line and move to next line
1098 styler
.SetLevel(lineCurrent
, options
.foldCompact
? lev
: lev
& ~SC_FOLDLEVELWHITEFLAG
);
1100 indentCurrent
= indentNext
;
1101 indentCurrentLevel
= indentNextLevel
;
1102 lineCurrent
= lineNext
;
1105 // NOTE: Cannot set level of last line here because indentCurrent doesn't have
1106 // header flag set; the loop above is crafted to take care of this case!
1107 //styler.SetLevel(lineCurrent, indentCurrent);
1110 LexerModule
lmHaskell(SCLEX_HASKELL
, LexerHaskell::LexerFactoryHaskell
, "haskell", haskellWordListDesc
);
1111 LexerModule
lmLiterateHaskell(SCLEX_LITERATEHASKELL
, LexerHaskell::LexerFactoryLiterateHaskell
, "literatehaskell", haskellWordListDesc
);