]>
git.saurik.com Git - wxWidgets.git/blob - contrib/src/stc/scintilla/src/LexPython.cxx
d4d4f46fedb436e41403995919a5abf698c39725
1 // Scintilla source code edit control
2 /** @file LexPython.cxx
5 // Copyright 1998-2002 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
18 #include "StyleContext.h"
20 #include "Scintilla.h"
23 enum kwType
{ kwOther
, kwClass
, kwDef
, kwImport
};
25 static bool IsPyComment(Accessor
&styler
, int pos
, int len
) {
26 return len
> 0 && styler
[pos
] == '#';
29 static bool IsPyStringStart(int ch
, int chNext
, int chNext2
) {
30 if (ch
== '\'' || ch
== '"')
32 if (ch
== 'u' || ch
== 'U') {
33 if (chNext
== '"' || chNext
== '\'')
35 if ((chNext
== 'r' || chNext
== 'R') && (chNext2
== '"' || chNext2
== '\''))
38 if ((ch
== 'r' || ch
== 'R') && (chNext
== '"' || chNext
== '\''))
44 /* Return the state to use for the string starting at i; *nextIndex will be set to the first index following the quote(s) */
45 static int GetPyStringState(Accessor
&styler
, int i
, unsigned int *nextIndex
) {
46 char ch
= styler
.SafeGetCharAt(i
);
47 char chNext
= styler
.SafeGetCharAt(i
+ 1);
49 // Advance beyond r, u, or ur prefix, but bail if there are any unexpected chars
50 if (ch
== 'r' || ch
== 'R') {
52 ch
= styler
.SafeGetCharAt(i
);
53 chNext
= styler
.SafeGetCharAt(i
+ 1);
54 } else if (ch
== 'u' || ch
== 'U') {
55 if (chNext
== 'r' || chNext
== 'R')
59 ch
= styler
.SafeGetCharAt(i
);
60 chNext
= styler
.SafeGetCharAt(i
+ 1);
63 if (ch
!= '"' && ch
!= '\'') {
68 if (ch
== chNext
&& ch
== styler
.SafeGetCharAt(i
+ 2)) {
72 return SCE_P_TRIPLEDOUBLE
;
81 return SCE_P_CHARACTER
;
85 static inline bool IsAWordChar(int ch
) {
86 return (ch
< 0x80) && (isalnum(ch
) || ch
== '.' || ch
== '_');
89 static inline bool IsAWordStart(int ch
) {
90 return (ch
< 0x80) && (isalnum(ch
) || ch
== '_');
93 static void ColourisePyDoc(unsigned int startPos
, int length
, int initStyle
,
94 WordList
*keywordlists
[], Accessor
&styler
) {
96 int endPos
= startPos
+ length
;
98 // Backtrack to previous line in case need to fix its tab whinging
99 int lineCurrent
= styler
.GetLine(startPos
);
101 if (lineCurrent
> 0) {
103 startPos
= styler
.LineStart(lineCurrent
);
105 initStyle
= SCE_P_DEFAULT
;
107 initStyle
= styler
.StyleAt(startPos
- 1);
111 WordList
&keywords
= *keywordlists
[0];
113 const int whingeLevel
= styler
.GetPropertyInt("tab.timmy.whinge.level");
115 initStyle
= initStyle
& 31;
116 if (initStyle
== SCE_P_STRINGEOL
) {
117 initStyle
= SCE_P_DEFAULT
;
120 kwType kwLast
= kwOther
;
122 styler
.IndentAmount(lineCurrent
, &spaceFlags
, IsPyComment
);
123 bool hexadecimal
= false;
125 // Python uses a different mask because bad indentation is marked by oring with 32
126 StyleContext
sc(startPos
, endPos
- startPos
, initStyle
, styler
, 0x7f);
128 for (; sc
.More(); sc
.Forward()) {
130 if (sc
.atLineStart
) {
131 const char chBad
= static_cast<char>(64);
132 const char chGood
= static_cast<char>(0);
133 char chFlags
= chGood
;
134 if (whingeLevel
== 1) {
135 chFlags
= (spaceFlags
& wsInconsistent
) ? chBad
: chGood
;
136 } else if (whingeLevel
== 2) {
137 chFlags
= (spaceFlags
& wsSpaceTab
) ? chBad
: chGood
;
138 } else if (whingeLevel
== 3) {
139 chFlags
= (spaceFlags
& wsSpace
) ? chBad
: chGood
;
140 } else if (whingeLevel
== 4) {
141 chFlags
= (spaceFlags
& wsTab
) ? chBad
: chGood
;
143 sc
.SetState(sc
.state
);
144 styler
.SetFlags(chFlags
, static_cast<char>(sc
.state
));
148 if ((sc
.state
== SCE_P_DEFAULT
) ||
149 (sc
.state
== SCE_P_TRIPLE
) ||
150 (sc
.state
== SCE_P_TRIPLEDOUBLE
)) {
151 // Perform colourisation of white space and triple quoted strings at end of each line to allow
152 // tab marking to work inside white space and triple quoted strings
153 sc
.SetState(sc
.state
);
156 styler
.IndentAmount(lineCurrent
, &spaceFlags
, IsPyComment
);
157 if ((sc
.state
== SCE_P_STRING
) || (sc
.state
== SCE_P_CHARACTER
)) {
158 sc
.ChangeState(SCE_P_STRINGEOL
);
159 sc
.ForwardSetState(SCE_P_DEFAULT
);
165 bool needEOLCheck
= false;
167 // Check for a state end
168 if (sc
.state
== SCE_P_OPERATOR
) {
170 sc
.SetState(SCE_P_DEFAULT
);
171 } else if (sc
.state
== SCE_P_NUMBER
) {
172 if (!IsAWordChar(sc
.ch
) &&
173 !(!hexadecimal
&& ((sc
.ch
== '+' || sc
.ch
== '-') && (sc
.chPrev
== 'e' || sc
.chPrev
== 'E')))) {
174 sc
.SetState(SCE_P_DEFAULT
);
176 } else if (sc
.state
== SCE_P_IDENTIFIER
) {
177 if ((sc
.ch
== '.') || (!IsAWordChar(sc
.ch
))) {
179 sc
.GetCurrent(s
, sizeof(s
));
180 int style
= SCE_P_IDENTIFIER
;
181 if ((kwLast
== kwImport
) && (strcmp(s
, "as") == 0)) {
183 } else if (keywords
.InList(s
)) {
185 } else if (kwLast
== kwClass
) {
186 style
= SCE_P_CLASSNAME
;
187 } else if (kwLast
== kwDef
) {
188 style
= SCE_P_DEFNAME
;
190 sc
.ChangeState(style
);
191 sc
.SetState(SCE_P_DEFAULT
);
192 if (style
== SCE_P_WORD
) {
193 if (0 == strcmp(s
, "class"))
195 else if (0 == strcmp(s
, "def"))
197 else if (0 == strcmp(s
, "import"))
201 } else if (style
== SCE_P_CLASSNAME
) {
203 } else if (style
== SCE_P_DEFNAME
) {
207 } else if ((sc
.state
== SCE_P_COMMENTLINE
) || (sc
.state
== SCE_P_COMMENTBLOCK
)) {
208 if (sc
.ch
== '\r' || sc
.ch
== '\n') {
209 sc
.SetState(SCE_P_DEFAULT
);
211 } else if ((sc
.state
== SCE_P_STRING
) || (sc
.state
== SCE_P_CHARACTER
)) {
213 if ((sc
.chNext
== '\r') && (sc
.GetRelative(2) == '\n')) {
217 } else if ((sc
.state
== SCE_P_STRING
) && (sc
.ch
== '\"')) {
218 sc
.ForwardSetState(SCE_P_DEFAULT
);
220 } else if ((sc
.state
== SCE_P_CHARACTER
) && (sc
.ch
== '\'')) {
221 sc
.ForwardSetState(SCE_P_DEFAULT
);
224 } else if (sc
.state
== SCE_P_TRIPLE
) {
227 } else if (sc
.Match("\'\'\'")) {
230 sc
.ForwardSetState(SCE_P_DEFAULT
);
233 } else if (sc
.state
== SCE_P_TRIPLEDOUBLE
) {
236 } else if (sc
.Match("\"\"\"")) {
239 sc
.ForwardSetState(SCE_P_DEFAULT
);
244 // State exit code may have moved on to end of line
245 if (needEOLCheck
&& sc
.atLineEnd
) {
247 styler
.IndentAmount(lineCurrent
, &spaceFlags
, IsPyComment
);
252 // Check for a new state starting character
253 if (sc
.state
== SCE_P_DEFAULT
) {
254 if (IsADigit(sc
.ch
) || (sc
.ch
== '.' && IsADigit(sc
.chNext
))) {
255 if (sc
.ch
== '0' && (sc
.chNext
== 'x' || sc
.chNext
== 'X')) {
260 sc
.SetState(SCE_P_NUMBER
);
261 } else if (isascii(sc
.ch
) && isoperator(static_cast<char>(sc
.ch
)) || sc
.ch
== '`') {
262 sc
.SetState(SCE_P_OPERATOR
);
263 } else if (sc
.ch
== '#') {
264 sc
.SetState(sc
.chNext
== '#' ? SCE_P_COMMENTBLOCK
: SCE_P_COMMENTLINE
);
265 } else if (IsPyStringStart(sc
.ch
, sc
.chNext
, sc
.GetRelative(2))) {
266 unsigned int nextIndex
= 0;
267 sc
.SetState(GetPyStringState(styler
, sc
.currentPos
, &nextIndex
));
268 while (nextIndex
> (sc
.currentPos
+ 1) && sc
.More()) { sc
.Forward();
270 } else if (IsAWordStart(sc
.ch
)) {
271 sc
.SetState(SCE_P_IDENTIFIER
);
278 static bool IsCommentLine(int line
, Accessor
&styler
) {
279 int pos
= styler
.LineStart(line
);
280 int eol_pos
= styler
.LineStart(line
+ 1) - 1;
281 for (int i
= pos
; i
< eol_pos
; i
++) {
285 else if (ch
!= ' ' && ch
!= '\t')
291 static bool IsQuoteLine(int line
, Accessor
&styler
) {
292 int style
= styler
.StyleAt(styler
.LineStart(line
)) & 31;
293 return ((style
== SCE_P_TRIPLE
) || (style
== SCE_P_TRIPLEDOUBLE
));
297 static void FoldPyDoc(unsigned int startPos
, int length
, int /*initStyle - unused*/,
298 WordList
*[], Accessor
&styler
) {
299 const int maxPos
= startPos
+ length
;
300 const int maxLines
= styler
.GetLine(maxPos
- 1); // Requested last line
301 const int docLines
= styler
.GetLine(styler
.Length() - 1); // Available last line
302 const bool foldComment
= styler
.GetPropertyInt("fold.comment.python") != 0;
303 const bool foldQuotes
= styler
.GetPropertyInt("fold.quotes.python") != 0;
305 // Backtrack to previous non-blank line so we can determine indent level
306 // for any white space lines (needed esp. within triple quoted strings)
307 // and so we can fix any preceding fold level (which is why we go back
308 // at least one line in all cases)
310 int lineCurrent
= styler
.GetLine(startPos
);
311 int indentCurrent
= styler
.IndentAmount(lineCurrent
, &spaceFlags
, NULL
);
312 while (lineCurrent
> 0) {
314 indentCurrent
= styler
.IndentAmount(lineCurrent
, &spaceFlags
, NULL
);
315 if (!(indentCurrent
& SC_FOLDLEVELWHITEFLAG
) &&
316 (!IsCommentLine(lineCurrent
, styler
)) &&
317 (!IsQuoteLine(lineCurrent
, styler
)))
320 int indentCurrentLevel
= indentCurrent
& SC_FOLDLEVELNUMBERMASK
;
322 // Set up initial loop state
323 startPos
= styler
.LineStart(lineCurrent
);
324 int prev_state
= SCE_P_DEFAULT
& 31;
325 if (lineCurrent
>= 1)
326 prev_state
= styler
.StyleAt(startPos
- 1) & 31;
327 int prevQuote
= foldQuotes
&& ((prev_state
== SCE_P_TRIPLE
) || (prev_state
== SCE_P_TRIPLEDOUBLE
));
329 if (lineCurrent
>= 1)
330 prevComment
= foldComment
&& IsCommentLine(lineCurrent
- 1, styler
);
332 // Process all characters to end of requested range or end of any triple quote
333 // or comment that hangs over the end of the range. Cap processing in all cases
334 // to end of document (in case of unclosed quote or comment at end).
335 while ((lineCurrent
<= docLines
) && ((lineCurrent
<= maxLines
) || prevQuote
|| prevComment
)) {
338 int lev
= indentCurrent
;
339 int lineNext
= lineCurrent
+ 1;
340 int indentNext
= indentCurrent
;
342 if (lineNext
<= docLines
) {
343 // Information about next line is only available if not at end of document
344 indentNext
= styler
.IndentAmount(lineNext
, &spaceFlags
, NULL
);
345 int style
= styler
.StyleAt(styler
.LineStart(lineNext
)) & 31;
346 quote
= foldQuotes
&& ((style
== SCE_P_TRIPLE
) || (style
== SCE_P_TRIPLEDOUBLE
));
348 const int quote_start
= (quote
&& !prevQuote
);
349 const int quote_continue
= (quote
&& prevQuote
);
350 const int comment
= foldComment
&& IsCommentLine(lineCurrent
, styler
);
351 const int comment_start
= (comment
&& !prevComment
&& (lineNext
<= docLines
) &&
352 IsCommentLine(lineNext
, styler
) && (lev
> SC_FOLDLEVELBASE
));
353 const int comment_continue
= (comment
&& prevComment
);
354 if ((!quote
|| !prevQuote
) && !comment
)
355 indentCurrentLevel
= indentCurrent
& SC_FOLDLEVELNUMBERMASK
;
357 indentNext
= indentCurrentLevel
;
358 if (indentNext
& SC_FOLDLEVELWHITEFLAG
)
359 indentNext
= SC_FOLDLEVELWHITEFLAG
| indentCurrentLevel
;
362 // Place fold point at start of triple quoted string
363 lev
|= SC_FOLDLEVELHEADERFLAG
;
364 } else if (quote_continue
|| prevQuote
) {
365 // Add level to rest of lines in the string
367 } else if (comment_start
) {
368 // Place fold point at start of a block of comments
369 lev
|= SC_FOLDLEVELHEADERFLAG
;
370 } else if (comment_continue
) {
371 // Add level to rest of lines in the block
375 // Skip past any blank lines for next indent level info; we skip also
376 // comments (all comments, not just those starting in column 0)
377 // which effectively folds them into surrounding code rather
378 // than screwing up folding.
381 (lineNext
< docLines
) &&
382 ((indentNext
& SC_FOLDLEVELWHITEFLAG
) ||
383 (lineNext
<= docLines
&& IsCommentLine(lineNext
, styler
)))) {
386 indentNext
= styler
.IndentAmount(lineNext
, &spaceFlags
, NULL
);
389 const int levelAfterComments
= indentNext
& SC_FOLDLEVELNUMBERMASK
;
390 const int levelBeforeComments
= Platform::Maximum(indentCurrentLevel
,levelAfterComments
);
392 // Now set all the indent levels on the lines we skipped
393 // Do this from end to start. Once we encounter one line
394 // which is indented more than the line after the end of
395 // the comment-block, use the level of the block before
397 int skipLine
= lineNext
;
398 int skipLevel
= levelAfterComments
;
400 while (--skipLine
> lineCurrent
) {
401 int skipLineIndent
= styler
.IndentAmount(skipLine
, &spaceFlags
, NULL
);
403 if ((skipLineIndent
& SC_FOLDLEVELNUMBERMASK
) > levelAfterComments
)
404 skipLevel
= levelBeforeComments
;
406 int whiteFlag
= skipLineIndent
& SC_FOLDLEVELWHITEFLAG
;
408 styler
.SetLevel(skipLine
, skipLevel
| whiteFlag
);
411 // Set fold header on non-quote/non-comment line
412 if (!quote
&& !comment
&& !(indentCurrent
& SC_FOLDLEVELWHITEFLAG
) ) {
413 if ((indentCurrent
& SC_FOLDLEVELNUMBERMASK
) < (indentNext
& SC_FOLDLEVELNUMBERMASK
))
414 lev
|= SC_FOLDLEVELHEADERFLAG
;
417 // Keep track of triple quote and block comment state of previous line
419 prevComment
= comment_start
|| comment_continue
;
421 // Set fold level for this line and move to next line
422 styler
.SetLevel(lineCurrent
, lev
);
423 indentCurrent
= indentNext
;
424 lineCurrent
= lineNext
;
427 // NOTE: Cannot set level of last line here because indentCurrent doesn't have
428 // header flag set; the loop above is crafted to take care of this case!
429 //styler.SetLevel(lineCurrent, indentCurrent);
432 static const char * const pythonWordListDesc
[] = {
437 LexerModule
lmPython(SCLEX_PYTHON
, ColourisePyDoc
, "python", FoldPyDoc
,