]>
git.saurik.com Git - wxWidgets.git/blob - src/stc/scintilla/src/LexPython.cxx
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"
24 using namespace Scintilla
;
27 enum kwType
{ kwOther
, kwClass
, kwDef
, kwImport
};
28 static const int indicatorWhitespace
= 1;
30 static bool IsPyComment(Accessor
&styler
, int pos
, int len
) {
31 return len
> 0 && styler
[pos
] == '#';
34 static bool IsPyStringStart(int ch
, int chNext
, int chNext2
) {
35 if (ch
== '\'' || ch
== '"')
37 if (ch
== 'u' || ch
== 'U') {
38 if (chNext
== '"' || chNext
== '\'')
40 if ((chNext
== 'r' || chNext
== 'R') && (chNext2
== '"' || chNext2
== '\''))
43 if ((ch
== 'r' || ch
== 'R') && (chNext
== '"' || chNext
== '\''))
49 /* Return the state to use for the string starting at i; *nextIndex will be set to the first index following the quote(s) */
50 static int GetPyStringState(Accessor
&styler
, int i
, unsigned int *nextIndex
) {
51 char ch
= styler
.SafeGetCharAt(i
);
52 char chNext
= styler
.SafeGetCharAt(i
+ 1);
54 // Advance beyond r, u, or ur prefix, but bail if there are any unexpected chars
55 if (ch
== 'r' || ch
== 'R') {
57 ch
= styler
.SafeGetCharAt(i
);
58 chNext
= styler
.SafeGetCharAt(i
+ 1);
59 } else if (ch
== 'u' || ch
== 'U') {
60 if (chNext
== 'r' || chNext
== 'R')
64 ch
= styler
.SafeGetCharAt(i
);
65 chNext
= styler
.SafeGetCharAt(i
+ 1);
68 if (ch
!= '"' && ch
!= '\'') {
73 if (ch
== chNext
&& ch
== styler
.SafeGetCharAt(i
+ 2)) {
77 return SCE_P_TRIPLEDOUBLE
;
86 return SCE_P_CHARACTER
;
90 static inline bool IsAWordChar(int ch
) {
91 return (ch
< 0x80) && (isalnum(ch
) || ch
== '.' || ch
== '_');
94 static inline bool IsAWordStart(int ch
) {
95 return (ch
< 0x80) && (isalnum(ch
) || ch
== '_');
98 static void ColourisePyDoc(unsigned int startPos
, int length
, int initStyle
,
99 WordList
*keywordlists
[], Accessor
&styler
) {
101 int endPos
= startPos
+ length
;
103 // Backtrack to previous line in case need to fix its tab whinging
104 int lineCurrent
= styler
.GetLine(startPos
);
106 if (lineCurrent
> 0) {
108 startPos
= styler
.LineStart(lineCurrent
);
110 initStyle
= SCE_P_DEFAULT
;
112 initStyle
= styler
.StyleAt(startPos
- 1);
116 WordList
&keywords
= *keywordlists
[0];
117 WordList
&keywords2
= *keywordlists
[1];
119 const int whingeLevel
= styler
.GetPropertyInt("tab.timmy.whinge.level");
121 initStyle
= initStyle
& 31;
122 if (initStyle
== SCE_P_STRINGEOL
) {
123 initStyle
= SCE_P_DEFAULT
;
126 kwType kwLast
= kwOther
;
128 styler
.IndentAmount(lineCurrent
, &spaceFlags
, IsPyComment
);
129 bool hexadecimal
= false;
131 StyleContext
sc(startPos
, endPos
- startPos
, initStyle
, styler
);
133 bool indentGood
= true;
134 int startIndicator
= sc
.currentPos
;
136 for (; sc
.More(); sc
.Forward()) {
138 if (sc
.atLineStart
) {
139 styler
.IndentAmount(lineCurrent
, &spaceFlags
, IsPyComment
);
141 if (whingeLevel
== 1) {
142 indentGood
= (spaceFlags
& wsInconsistent
) == 0;
143 } else if (whingeLevel
== 2) {
144 indentGood
= (spaceFlags
& wsSpaceTab
) == 0;
145 } else if (whingeLevel
== 3) {
146 indentGood
= (spaceFlags
& wsSpace
) == 0;
147 } else if (whingeLevel
== 4) {
148 indentGood
= (spaceFlags
& wsTab
) == 0;
151 styler
.IndicatorFill(startIndicator
, sc
.currentPos
, indicatorWhitespace
, 0);
152 startIndicator
= sc
.currentPos
;
157 if ((sc
.state
== SCE_P_DEFAULT
) ||
158 (sc
.state
== SCE_P_TRIPLE
) ||
159 (sc
.state
== SCE_P_TRIPLEDOUBLE
)) {
160 // Perform colourisation of white space and triple quoted strings at end of each line to allow
161 // tab marking to work inside white space and triple quoted strings
162 sc
.SetState(sc
.state
);
165 if ((sc
.state
== SCE_P_STRING
) || (sc
.state
== SCE_P_CHARACTER
)) {
166 sc
.ChangeState(SCE_P_STRINGEOL
);
167 sc
.ForwardSetState(SCE_P_DEFAULT
);
173 bool needEOLCheck
= false;
175 // Check for a state end
176 if (sc
.state
== SCE_P_OPERATOR
) {
178 sc
.SetState(SCE_P_DEFAULT
);
179 } else if (sc
.state
== SCE_P_NUMBER
) {
180 if (!IsAWordChar(sc
.ch
) &&
181 !(!hexadecimal
&& ((sc
.ch
== '+' || sc
.ch
== '-') && (sc
.chPrev
== 'e' || sc
.chPrev
== 'E')))) {
182 sc
.SetState(SCE_P_DEFAULT
);
184 } else if (sc
.state
== SCE_P_IDENTIFIER
) {
185 if ((sc
.ch
== '.') || (!IsAWordChar(sc
.ch
))) {
187 sc
.GetCurrent(s
, sizeof(s
));
188 int style
= SCE_P_IDENTIFIER
;
189 if ((kwLast
== kwImport
) && (strcmp(s
, "as") == 0)) {
191 } else if (keywords
.InList(s
)) {
193 } else if (kwLast
== kwClass
) {
194 style
= SCE_P_CLASSNAME
;
195 } else if (kwLast
== kwDef
) {
196 style
= SCE_P_DEFNAME
;
197 } else if (keywords2
.InList(s
)) {
200 sc
.ChangeState(style
);
201 sc
.SetState(SCE_P_DEFAULT
);
202 if (style
== SCE_P_WORD
) {
203 if (0 == strcmp(s
, "class"))
205 else if (0 == strcmp(s
, "def"))
207 else if (0 == strcmp(s
, "import"))
215 } else if ((sc
.state
== SCE_P_COMMENTLINE
) || (sc
.state
== SCE_P_COMMENTBLOCK
)) {
216 if (sc
.ch
== '\r' || sc
.ch
== '\n') {
217 sc
.SetState(SCE_P_DEFAULT
);
219 } else if (sc
.state
== SCE_P_DECORATOR
) {
220 if (!IsAWordChar(sc
.ch
)) {
221 sc
.SetState(SCE_P_DEFAULT
);
223 } else if ((sc
.state
== SCE_P_STRING
) || (sc
.state
== SCE_P_CHARACTER
)) {
225 if ((sc
.chNext
== '\r') && (sc
.GetRelative(2) == '\n')) {
229 } else if ((sc
.state
== SCE_P_STRING
) && (sc
.ch
== '\"')) {
230 sc
.ForwardSetState(SCE_P_DEFAULT
);
232 } else if ((sc
.state
== SCE_P_CHARACTER
) && (sc
.ch
== '\'')) {
233 sc
.ForwardSetState(SCE_P_DEFAULT
);
236 } else if (sc
.state
== SCE_P_TRIPLE
) {
239 } else if (sc
.Match("\'\'\'")) {
242 sc
.ForwardSetState(SCE_P_DEFAULT
);
245 } else if (sc
.state
== SCE_P_TRIPLEDOUBLE
) {
248 } else if (sc
.Match("\"\"\"")) {
251 sc
.ForwardSetState(SCE_P_DEFAULT
);
256 if (!indentGood
&& !IsASpaceOrTab(sc
.ch
)) {
257 styler
.IndicatorFill(startIndicator
, sc
.currentPos
, indicatorWhitespace
, 1);
258 startIndicator
= sc
.currentPos
;
262 // State exit code may have moved on to end of line
263 if (needEOLCheck
&& sc
.atLineEnd
) {
265 styler
.IndentAmount(lineCurrent
, &spaceFlags
, IsPyComment
);
270 // Check for a new state starting character
271 if (sc
.state
== SCE_P_DEFAULT
) {
272 if (IsADigit(sc
.ch
) || (sc
.ch
== '.' && IsADigit(sc
.chNext
))) {
273 if (sc
.ch
== '0' && (sc
.chNext
== 'x' || sc
.chNext
== 'X')) {
278 sc
.SetState(SCE_P_NUMBER
);
279 } else if (isascii(sc
.ch
) && isoperator(static_cast<char>(sc
.ch
)) || sc
.ch
== '`') {
280 sc
.SetState(SCE_P_OPERATOR
);
281 } else if (sc
.ch
== '#') {
282 sc
.SetState(sc
.chNext
== '#' ? SCE_P_COMMENTBLOCK
: SCE_P_COMMENTLINE
);
283 } else if (sc
.ch
== '@') {
284 sc
.SetState(SCE_P_DECORATOR
);
285 } else if (IsPyStringStart(sc
.ch
, sc
.chNext
, sc
.GetRelative(2))) {
286 unsigned int nextIndex
= 0;
287 sc
.SetState(GetPyStringState(styler
, sc
.currentPos
, &nextIndex
));
288 while (nextIndex
> (sc
.currentPos
+ 1) && sc
.More()) {
291 } else if (IsAWordStart(sc
.ch
)) {
292 sc
.SetState(SCE_P_IDENTIFIER
);
296 styler
.IndicatorFill(startIndicator
, sc
.currentPos
, indicatorWhitespace
, 0);
300 static bool IsCommentLine(int line
, Accessor
&styler
) {
301 int pos
= styler
.LineStart(line
);
302 int eol_pos
= styler
.LineStart(line
+ 1) - 1;
303 for (int i
= pos
; i
< eol_pos
; i
++) {
307 else if (ch
!= ' ' && ch
!= '\t')
313 static bool IsQuoteLine(int line
, Accessor
&styler
) {
314 int style
= styler
.StyleAt(styler
.LineStart(line
)) & 31;
315 return ((style
== SCE_P_TRIPLE
) || (style
== SCE_P_TRIPLEDOUBLE
));
319 static void FoldPyDoc(unsigned int startPos
, int length
, int /*initStyle - unused*/,
320 WordList
*[], Accessor
&styler
) {
321 const int maxPos
= startPos
+ length
;
322 const int maxLines
= styler
.GetLine(maxPos
- 1); // Requested last line
323 const int docLines
= styler
.GetLine(styler
.Length() - 1); // Available last line
324 const bool foldComment
= styler
.GetPropertyInt("fold.comment.python") != 0;
325 const bool foldQuotes
= styler
.GetPropertyInt("fold.quotes.python") != 0;
327 // Backtrack to previous non-blank line so we can determine indent level
328 // for any white space lines (needed esp. within triple quoted strings)
329 // and so we can fix any preceding fold level (which is why we go back
330 // at least one line in all cases)
332 int lineCurrent
= styler
.GetLine(startPos
);
333 int indentCurrent
= styler
.IndentAmount(lineCurrent
, &spaceFlags
, NULL
);
334 while (lineCurrent
> 0) {
336 indentCurrent
= styler
.IndentAmount(lineCurrent
, &spaceFlags
, NULL
);
337 if (!(indentCurrent
& SC_FOLDLEVELWHITEFLAG
) &&
338 (!IsCommentLine(lineCurrent
, styler
)) &&
339 (!IsQuoteLine(lineCurrent
, styler
)))
342 int indentCurrentLevel
= indentCurrent
& SC_FOLDLEVELNUMBERMASK
;
344 // Set up initial loop state
345 startPos
= styler
.LineStart(lineCurrent
);
346 int prev_state
= SCE_P_DEFAULT
& 31;
347 if (lineCurrent
>= 1)
348 prev_state
= styler
.StyleAt(startPos
- 1) & 31;
349 int prevQuote
= foldQuotes
&& ((prev_state
== SCE_P_TRIPLE
) || (prev_state
== SCE_P_TRIPLEDOUBLE
));
351 if (lineCurrent
>= 1)
352 prevComment
= foldComment
&& IsCommentLine(lineCurrent
- 1, styler
);
354 // Process all characters to end of requested range or end of any triple quote
355 // or comment that hangs over the end of the range. Cap processing in all cases
356 // to end of document (in case of unclosed quote or comment at end).
357 while ((lineCurrent
<= docLines
) && ((lineCurrent
<= maxLines
) || prevQuote
|| prevComment
)) {
360 int lev
= indentCurrent
;
361 int lineNext
= lineCurrent
+ 1;
362 int indentNext
= indentCurrent
;
364 if (lineNext
<= docLines
) {
365 // Information about next line is only available if not at end of document
366 indentNext
= styler
.IndentAmount(lineNext
, &spaceFlags
, NULL
);
367 int style
= styler
.StyleAt(styler
.LineStart(lineNext
)) & 31;
368 quote
= foldQuotes
&& ((style
== SCE_P_TRIPLE
) || (style
== SCE_P_TRIPLEDOUBLE
));
370 const int quote_start
= (quote
&& !prevQuote
);
371 const int quote_continue
= (quote
&& prevQuote
);
372 const int comment
= foldComment
&& IsCommentLine(lineCurrent
, styler
);
373 const int comment_start
= (comment
&& !prevComment
&& (lineNext
<= docLines
) &&
374 IsCommentLine(lineNext
, styler
) && (lev
> SC_FOLDLEVELBASE
));
375 const int comment_continue
= (comment
&& prevComment
);
376 if ((!quote
|| !prevQuote
) && !comment
)
377 indentCurrentLevel
= indentCurrent
& SC_FOLDLEVELNUMBERMASK
;
379 indentNext
= indentCurrentLevel
;
380 if (indentNext
& SC_FOLDLEVELWHITEFLAG
)
381 indentNext
= SC_FOLDLEVELWHITEFLAG
| indentCurrentLevel
;
384 // Place fold point at start of triple quoted string
385 lev
|= SC_FOLDLEVELHEADERFLAG
;
386 } else if (quote_continue
|| prevQuote
) {
387 // Add level to rest of lines in the string
389 } else if (comment_start
) {
390 // Place fold point at start of a block of comments
391 lev
|= SC_FOLDLEVELHEADERFLAG
;
392 } else if (comment_continue
) {
393 // Add level to rest of lines in the block
397 // Skip past any blank lines for next indent level info; we skip also
398 // comments (all comments, not just those starting in column 0)
399 // which effectively folds them into surrounding code rather
400 // than screwing up folding.
403 (lineNext
< docLines
) &&
404 ((indentNext
& SC_FOLDLEVELWHITEFLAG
) ||
405 (lineNext
<= docLines
&& IsCommentLine(lineNext
, styler
)))) {
408 indentNext
= styler
.IndentAmount(lineNext
, &spaceFlags
, NULL
);
411 const int levelAfterComments
= indentNext
& SC_FOLDLEVELNUMBERMASK
;
412 const int levelBeforeComments
= Platform::Maximum(indentCurrentLevel
,levelAfterComments
);
414 // Now set all the indent levels on the lines we skipped
415 // Do this from end to start. Once we encounter one line
416 // which is indented more than the line after the end of
417 // the comment-block, use the level of the block before
419 int skipLine
= lineNext
;
420 int skipLevel
= levelAfterComments
;
422 while (--skipLine
> lineCurrent
) {
423 int skipLineIndent
= styler
.IndentAmount(skipLine
, &spaceFlags
, NULL
);
425 if ((skipLineIndent
& SC_FOLDLEVELNUMBERMASK
) > levelAfterComments
)
426 skipLevel
= levelBeforeComments
;
428 int whiteFlag
= skipLineIndent
& SC_FOLDLEVELWHITEFLAG
;
430 styler
.SetLevel(skipLine
, skipLevel
| whiteFlag
);
433 // Set fold header on non-quote/non-comment line
434 if (!quote
&& !comment
&& !(indentCurrent
& SC_FOLDLEVELWHITEFLAG
) ) {
435 if ((indentCurrent
& SC_FOLDLEVELNUMBERMASK
) < (indentNext
& SC_FOLDLEVELNUMBERMASK
))
436 lev
|= SC_FOLDLEVELHEADERFLAG
;
439 // Keep track of triple quote and block comment state of previous line
441 prevComment
= comment_start
|| comment_continue
;
443 // Set fold level for this line and move to next line
444 styler
.SetLevel(lineCurrent
, lev
);
445 indentCurrent
= indentNext
;
446 lineCurrent
= lineNext
;
449 // NOTE: Cannot set level of last line here because indentCurrent doesn't have
450 // header flag set; the loop above is crafted to take care of this case!
451 //styler.SetLevel(lineCurrent, indentCurrent);
454 static const char * const pythonWordListDesc
[] = {
456 "Highlighted identifiers",
460 LexerModule
lmPython(SCLEX_PYTHON
, ColourisePyDoc
, "python", FoldPyDoc
,