1 // Scintilla source code edit control
2 /** @file LexFlagShip.cxx
3 ** Lexer for Harbour and FlagShip.
4 ** (Syntactically compatible to other xBase dialects, like Clipper, dBase, Clip, FoxPro etc.)
6 // Copyright 2005 by Randy Butler
7 // Copyright 2010 by Xavi <jarabal/at/gmail.com> (Harbour)
8 // Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
9 // The License.txt file describes the conditions under which this software may be distributed.
19 #include "Scintilla.h"
23 #include "LexAccessor.h"
25 #include "StyleContext.h"
26 #include "CharacterSet.h"
27 #include "LexerModule.h"
30 using namespace Scintilla
;
33 // Extended to accept accented characters
34 static inline bool IsAWordChar(int ch
)
37 (isalnum(ch
) || ch
== '_');
40 static void ColouriseFlagShipDoc(unsigned int startPos
, int length
, int initStyle
,
41 WordList
*keywordlists
[], Accessor
&styler
)
44 WordList
&keywords
= *keywordlists
[0];
45 WordList
&keywords2
= *keywordlists
[1];
46 WordList
&keywords3
= *keywordlists
[2];
47 WordList
&keywords4
= *keywordlists
[3];
48 WordList
&keywords5
= *keywordlists
[4];
50 // property lexer.flagship.styling.within.preprocessor
51 // For Harbour code, determines whether all preprocessor code is styled in the preprocessor style (0) or only from the
52 // initial # to the end of the command word(1, the default). It also determines how to present text, dump, and disabled code.
53 bool stylingWithinPreprocessor
= styler
.GetPropertyInt("lexer.flagship.styling.within.preprocessor", 1) != 0;
55 CharacterSet
setDoxygen(CharacterSet::setAlpha
, "$@\\&<>#{}[]");
58 int closeStringChar
= 0;
59 int styleBeforeDCKeyword
= SCE_FS_DEFAULT
;
60 bool bEnableCode
= initStyle
< SCE_FS_DISABLEDCODE
;
62 StyleContext
sc(startPos
, length
, initStyle
, styler
);
64 for (; sc
.More(); sc
.Forward()) {
66 // Determine if the current state should terminate.
69 case SCE_FS_OPERATOR_C
:
70 case SCE_FS_WORDOPERATOR
:
71 sc
.SetState(bEnableCode
? SCE_FS_DEFAULT
: SCE_FS_DEFAULT_C
);
73 case SCE_FS_IDENTIFIER
:
74 case SCE_FS_IDENTIFIER_C
:
75 if (!IsAWordChar(sc
.ch
)) {
77 sc
.GetCurrentLowered(s
, sizeof(s
));
78 if (keywords
.InList(s
)) {
79 sc
.ChangeState(bEnableCode
? SCE_FS_KEYWORD
: SCE_FS_KEYWORD_C
);
80 } else if (keywords2
.InList(s
)) {
81 sc
.ChangeState(bEnableCode
? SCE_FS_KEYWORD2
: SCE_FS_KEYWORD2_C
);
82 } else if (bEnableCode
&& keywords3
.InList(s
)) {
83 sc
.ChangeState(SCE_FS_KEYWORD3
);
84 } else if (bEnableCode
&& keywords4
.InList(s
)) {
85 sc
.ChangeState(SCE_FS_KEYWORD4
);
86 }// Else, it is really an identifier...
87 sc
.SetState(bEnableCode
? SCE_FS_DEFAULT
: SCE_FS_DEFAULT_C
);
91 if (!IsAWordChar(sc
.ch
) && !(sc
.ch
== '.' && IsADigit(sc
.chNext
))) {
92 sc
.SetState(SCE_FS_DEFAULT
);
96 if (!IsAWordChar(sc
.ch
) && sc
.ch
!= '.') {
97 sc
.SetState(SCE_FS_DEFAULT_C
);
100 case SCE_FS_CONSTANT
:
101 if (!IsAWordChar(sc
.ch
)) {
102 sc
.SetState(SCE_FS_DEFAULT
);
106 case SCE_FS_STRING_C
:
107 if (sc
.ch
== closeStringChar
) {
108 sc
.ForwardSetState(bEnableCode
? SCE_FS_DEFAULT
: SCE_FS_DEFAULT_C
);
109 } else if (sc
.atLineEnd
) {
110 sc
.ChangeState(bEnableCode
? SCE_FS_STRINGEOL
: SCE_FS_STRINGEOL_C
);
113 case SCE_FS_STRINGEOL
:
114 case SCE_FS_STRINGEOL_C
:
115 if (sc
.atLineStart
) {
116 sc
.SetState(bEnableCode
? SCE_FS_DEFAULT
: SCE_FS_DEFAULT_C
);
119 case SCE_FS_COMMENTDOC
:
120 case SCE_FS_COMMENTDOC_C
:
121 if (sc
.Match('*', '/')) {
123 sc
.ForwardSetState(bEnableCode
? SCE_FS_DEFAULT
: SCE_FS_DEFAULT_C
);
124 } else if (sc
.ch
== '@' || sc
.ch
== '\\') { // JavaDoc and Doxygen support
125 // Verify that we have the conditions to mark a comment-doc-keyword
126 if ((IsASpace(sc
.chPrev
) || sc
.chPrev
== '*') && (!IsASpace(sc
.chNext
))) {
127 styleBeforeDCKeyword
= bEnableCode
? SCE_FS_COMMENTDOC
: SCE_FS_COMMENTDOC_C
;
128 sc
.SetState(SCE_FS_COMMENTDOCKEYWORD
);
133 case SCE_FS_COMMENTLINE
:
134 if (sc
.atLineStart
) {
135 sc
.SetState(SCE_FS_DEFAULT
);
138 case SCE_FS_COMMENTLINEDOC
:
139 case SCE_FS_COMMENTLINEDOC_C
:
140 if (sc
.atLineStart
) {
141 sc
.SetState(bEnableCode
? SCE_FS_DEFAULT
: SCE_FS_DEFAULT_C
);
142 } else if (sc
.ch
== '@' || sc
.ch
== '\\') { // JavaDoc and Doxygen support
143 // Verify that we have the conditions to mark a comment-doc-keyword
144 if ((IsASpace(sc
.chPrev
) || sc
.chPrev
== '/' || sc
.chPrev
== '!') && (!IsASpace(sc
.chNext
))) {
145 styleBeforeDCKeyword
= bEnableCode
? SCE_FS_COMMENTLINEDOC
: SCE_FS_COMMENTLINEDOC_C
;
146 sc
.SetState(SCE_FS_COMMENTDOCKEYWORD
);
150 case SCE_FS_COMMENTDOCKEYWORD
:
151 if ((styleBeforeDCKeyword
== SCE_FS_COMMENTDOC
|| styleBeforeDCKeyword
== SCE_FS_COMMENTDOC_C
) &&
152 sc
.Match('*', '/')) {
153 sc
.ChangeState(SCE_FS_COMMENTDOCKEYWORDERROR
);
155 sc
.ForwardSetState(bEnableCode
? SCE_FS_DEFAULT
: SCE_FS_DEFAULT_C
);
156 } else if (!setDoxygen
.Contains(sc
.ch
)) {
158 sc
.GetCurrentLowered(s
, sizeof(s
));
159 if (!IsASpace(sc
.ch
) || !keywords5
.InList(s
+ 1)) {
160 sc
.ChangeState(SCE_FS_COMMENTDOCKEYWORDERROR
);
162 sc
.SetState(styleBeforeDCKeyword
);
165 case SCE_FS_PREPROCESSOR
:
166 case SCE_FS_PREPROCESSOR_C
:
168 if (!(sc
.chPrev
== ';' || sc
.GetRelative(-2) == ';')) {
169 sc
.SetState(bEnableCode
? SCE_FS_DEFAULT
: SCE_FS_DEFAULT_C
);
171 } else if (stylingWithinPreprocessor
) {
172 if (IsASpaceOrTab(sc
.ch
)) {
173 sc
.SetState(bEnableCode
? SCE_FS_DEFAULT
: SCE_FS_DEFAULT_C
);
175 } else if (sc
.Match('/', '*') || sc
.Match('/', '/') || sc
.Match('&', '&')) {
176 sc
.SetState(bEnableCode
? SCE_FS_DEFAULT
: SCE_FS_DEFAULT_C
);
179 case SCE_FS_DISABLEDCODE
:
180 if (sc
.ch
== '#' && visibleChars
== 0) {
181 sc
.SetState(bEnableCode
? SCE_FS_PREPROCESSOR
: SCE_FS_PREPROCESSOR_C
);
182 do { // Skip whitespace between # and preprocessor word
184 } while (IsASpaceOrTab(sc
.ch
) && sc
.More());
185 if (sc
.MatchIgnoreCase("pragma")) {
187 do { // Skip more whitespace until keyword
189 } while (IsASpaceOrTab(sc
.ch
) && sc
.More());
190 if (sc
.MatchIgnoreCase("enddump") || sc
.MatchIgnoreCase("__endtext")) {
192 sc
.SetState(SCE_FS_DISABLEDCODE
);
193 sc
.Forward(sc
.ch
== '_' ? 8 : 6);
194 sc
.ForwardSetState(SCE_FS_DEFAULT
);
196 sc
.ChangeState(SCE_FS_DISABLEDCODE
);
199 sc
.ChangeState(SCE_FS_DISABLEDCODE
);
205 sc
.ForwardSetState(SCE_FS_DEFAULT
);
206 } else if (sc
.atLineEnd
) {
207 sc
.ChangeState(SCE_FS_STRINGEOL
);
211 // Determine if a new state should be entered.
212 if (sc
.state
== SCE_FS_DEFAULT
|| sc
.state
== SCE_FS_DEFAULT_C
) {
214 (sc
.MatchIgnoreCase(".and.") || sc
.MatchIgnoreCase(".not."))) {
215 sc
.SetState(SCE_FS_WORDOPERATOR
);
217 } else if (bEnableCode
&& sc
.MatchIgnoreCase(".or.")) {
218 sc
.SetState(SCE_FS_WORDOPERATOR
);
220 } else if (bEnableCode
&&
221 (sc
.MatchIgnoreCase(".t.") || sc
.MatchIgnoreCase(".f.") ||
222 (!IsAWordChar(sc
.GetRelative(3)) && sc
.MatchIgnoreCase("nil")))) {
223 sc
.SetState(SCE_FS_CONSTANT
);
225 } else if (sc
.Match('/', '*')) {
226 sc
.SetState(bEnableCode
? SCE_FS_COMMENTDOC
: SCE_FS_COMMENTDOC_C
);
228 } else if (bEnableCode
&& sc
.Match('&', '&')) {
229 sc
.SetState(SCE_FS_COMMENTLINE
);
231 } else if (sc
.Match('/', '/')) {
232 sc
.SetState(bEnableCode
? SCE_FS_COMMENTLINEDOC
: SCE_FS_COMMENTLINEDOC_C
);
234 } else if (bEnableCode
&& sc
.ch
== '*' && visibleChars
== 0) {
235 sc
.SetState(SCE_FS_COMMENT
);
236 } else if (sc
.ch
== '\"' || sc
.ch
== '\'') {
237 sc
.SetState(bEnableCode
? SCE_FS_STRING
: SCE_FS_STRING_C
);
238 closeStringChar
= sc
.ch
;
239 } else if (closeStringChar
== '>' && sc
.ch
== '<') {
240 sc
.SetState(bEnableCode
? SCE_FS_STRING
: SCE_FS_STRING_C
);
241 } else if (sc
.ch
== '#' && visibleChars
== 0) {
242 sc
.SetState(bEnableCode
? SCE_FS_PREPROCESSOR
: SCE_FS_PREPROCESSOR_C
);
243 do { // Skip whitespace between # and preprocessor word
245 } while (IsASpaceOrTab(sc
.ch
) && sc
.More());
247 sc
.SetState(bEnableCode
? SCE_FS_DEFAULT
: SCE_FS_DEFAULT_C
);
248 } else if (sc
.MatchIgnoreCase("include")) {
249 if (stylingWithinPreprocessor
) {
250 closeStringChar
= '>';
252 } else if (sc
.MatchIgnoreCase("pragma")) {
254 do { // Skip more whitespace until keyword
256 } while (IsASpaceOrTab(sc
.ch
) && sc
.More());
257 if (sc
.MatchIgnoreCase("begindump") || sc
.MatchIgnoreCase("__cstream")) {
259 if (stylingWithinPreprocessor
) {
260 sc
.SetState(SCE_FS_DISABLEDCODE
);
262 sc
.ForwardSetState(SCE_FS_DEFAULT_C
);
264 sc
.SetState(SCE_FS_DISABLEDCODE
);
266 } else if (sc
.MatchIgnoreCase("enddump") || sc
.MatchIgnoreCase("__endtext")) {
268 sc
.SetState(SCE_FS_DISABLEDCODE
);
269 sc
.Forward(sc
.ch
== '_' ? 8 : 6);
270 sc
.ForwardSetState(SCE_FS_DEFAULT
);
273 } else if (bEnableCode
&& sc
.ch
== '{') {
276 unsigned int endPos(startPos
+ length
);
277 do { // Skip whitespace
278 chSeek
= sc
.GetRelative(++p
);
279 } while (IsASpaceOrTab(chSeek
) && (sc
.currentPos
+ p
< endPos
));
281 sc
.SetState(SCE_FS_DATE
);
283 sc
.SetState(SCE_FS_OPERATOR
);
285 } else if (IsADigit(sc
.ch
) || (sc
.ch
== '.' && IsADigit(sc
.chNext
))) {
286 sc
.SetState(bEnableCode
? SCE_FS_NUMBER
: SCE_FS_NUMBER_C
);
287 } else if (IsAWordChar(sc
.ch
)) {
288 sc
.SetState(bEnableCode
? SCE_FS_IDENTIFIER
: SCE_FS_IDENTIFIER_C
);
289 } else if (isoperator(static_cast<char>(sc
.ch
)) || (bEnableCode
&& sc
.ch
== '@')) {
290 sc
.SetState(bEnableCode
? SCE_FS_OPERATOR
: SCE_FS_OPERATOR_C
);
298 if (!IsASpace(sc
.ch
)) {
305 static void FoldFlagShipDoc(unsigned int startPos
, int length
, int,
306 WordList
*[], Accessor
&styler
)
309 int endPos
= startPos
+ length
;
311 // Backtrack to previous line in case need to fix its fold status
312 int lineCurrent
= styler
.GetLine(startPos
);
313 if (startPos
> 0 && lineCurrent
> 0) {
315 startPos
= styler
.LineStart(lineCurrent
);
318 int indentCurrent
= styler
.IndentAmount(lineCurrent
, &spaceFlags
);
319 char chNext
= styler
[startPos
];
320 for (int i
= startPos
; i
< endPos
; i
++) {
322 chNext
= styler
.SafeGetCharAt(i
+ 1);
324 if ((ch
== '\r' && chNext
!= '\n') || (ch
== '\n') || (i
== endPos
-1)) {
325 int lev
= indentCurrent
;
326 int indentNext
= styler
.IndentAmount(lineCurrent
+ 1, &spaceFlags
);
327 if (!(indentCurrent
& SC_FOLDLEVELWHITEFLAG
)) {
328 if ((indentCurrent
& SC_FOLDLEVELNUMBERMASK
) < (indentNext
& SC_FOLDLEVELNUMBERMASK
)) {
329 lev
|= SC_FOLDLEVELHEADERFLAG
;
330 } else if (indentNext
& SC_FOLDLEVELWHITEFLAG
) {
332 int indentNext2
= styler
.IndentAmount(lineCurrent
+ 2, &spaceFlags2
);
333 if ((indentCurrent
& SC_FOLDLEVELNUMBERMASK
) < (indentNext2
& SC_FOLDLEVELNUMBERMASK
)) {
334 lev
|= SC_FOLDLEVELHEADERFLAG
;
338 indentCurrent
= indentNext
;
339 styler
.SetLevel(lineCurrent
, lev
);
345 static const char * const FSWordListDesc
[] = {
347 "Std Library Functions",
348 "Procedure, return, exit",
354 LexerModule
lmFlagShip(SCLEX_FLAGSHIP
, ColouriseFlagShipDoc
, "flagship", FoldFlagShipDoc
, FSWordListDesc
);