]>
git.saurik.com Git - wxWidgets.git/blob - src/stc/scintilla/src/LexPython.cxx
6669694d1d6451d9c0c1d56ef6b636359ffb0b52
1 // Scintilla source code edit control
2 /** @file LexPython.cxx
5 // Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
19 #include "Scintilla.h"
22 /* Returns true if the "as" word that begins at start follows an import statement */
23 static bool IsImportAs(unsigned int start
, Accessor
&styler
) {
28 /* Find any import before start but after any statement terminator or quote */
31 char ch
= styler
[i
- 1];
33 if (ch
== '\n' || ch
== '\r' || ch
== ';' || ch
== '\'' || ch
== '"' || ch
== '`')
35 if (ch
== 't' && i
> 5) {
36 for (j
= 0; j
< 6; j
++)
37 s
[j
] = styler
[(i
- 6) + j
];
39 if (strcmp(s
, "import") == 0)
48 static void ClassifyWordPy(unsigned int start
, unsigned int end
, WordList
&keywords
, Accessor
&styler
, char *prevWord
) {
50 bool wordIsNumber
= isdigit(styler
[start
]);
51 for (unsigned int i
= 0; i
< end
- start
+ 1 && i
< 30; i
++) {
52 s
[i
] = styler
[start
+ i
];
55 char chAttr
= SCE_P_IDENTIFIER
;
56 if (0 == strcmp(prevWord
, "class"))
57 chAttr
= SCE_P_CLASSNAME
;
58 else if (0 == strcmp(prevWord
, "def"))
59 chAttr
= SCE_P_DEFNAME
;
60 else if (wordIsNumber
)
61 chAttr
= SCE_P_NUMBER
;
62 else if (keywords
.InList(s
))
64 else if (strcmp(s
, "as") == 0 && IsImportAs(start
, styler
))
66 // make sure that dot-qualifiers inside the word are lexed correct
67 else for (unsigned int i
= 0; i
< end
- start
+ 1; i
++) {
68 if (styler
[start
+ i
] == '.') {
69 styler
.ColourTo(start
+ i
- 1, chAttr
);
70 styler
.ColourTo(start
+ i
, SCE_P_OPERATOR
);
73 styler
.ColourTo(end
, chAttr
);
77 static bool IsPyComment(Accessor
&styler
, int pos
, int len
) {
78 return len
>0 && styler
[pos
]=='#';
81 static bool IsPyStringStart(char ch
, char chNext
, char chNext2
) {
82 if (ch
== '\'' || ch
== '"')
84 if (ch
== 'u' || ch
== 'U') {
85 if (chNext
== '"' || chNext
== '\'')
87 if ((chNext
== 'r' || chNext
== 'R') && (chNext2
== '"' || chNext2
== '\''))
90 if ((ch
== 'r' || ch
== 'R') && (chNext
== '"' || chNext
== '\''))
96 static bool IsPyWordStart(char ch
, char chNext
, char chNext2
) {
97 return (iswordchar(ch
) && !IsPyStringStart(ch
, chNext
, chNext2
));
100 /* Return the state to use for the string starting at i; *nextIndex will be set to the first index following the quote(s) */
101 static int GetPyStringState(Accessor
&styler
, int i
, int *nextIndex
) {
102 char ch
= styler
.SafeGetCharAt(i
);
103 char chNext
= styler
.SafeGetCharAt(i
+ 1);
105 // Advance beyond r, u, or ur prefix, but bail if there are any unexpected chars
106 if (ch
== 'r' || ch
== 'R') {
108 ch
= styler
.SafeGetCharAt(i
);
109 chNext
= styler
.SafeGetCharAt(i
+ 1);
111 else if (ch
== 'u' || ch
== 'U') {
112 if (chNext
== 'r' || chNext
== 'R')
116 ch
= styler
.SafeGetCharAt(i
);
117 chNext
= styler
.SafeGetCharAt(i
+ 1);
120 if (ch
!= '"' && ch
!= '\'') {
122 return SCE_P_DEFAULT
;
125 if (ch
== chNext
&& ch
== styler
.SafeGetCharAt(i
+ 2)) {
129 return SCE_P_TRIPLEDOUBLE
;
138 return SCE_P_CHARACTER
;
142 static void ColourisePyDoc(unsigned int startPos
, int length
, int initStyle
,
143 WordList
*keywordlists
[], Accessor
&styler
) {
145 int lengthDoc
= startPos
+ length
;
147 // Backtrack to previous line in case need to fix its tab whinging
148 int lineCurrent
= styler
.GetLine(startPos
);
150 if (lineCurrent
> 0) {
151 startPos
= styler
.LineStart(lineCurrent
-1);
153 initStyle
= SCE_P_DEFAULT
;
155 initStyle
= styler
.StyleAt(startPos
-1);
159 // Python uses a different mask because bad indentation is marked by oring with 32
160 styler
.StartAt(startPos
, 127);
162 WordList
&keywords
= *keywordlists
[0];
164 int whingeLevel
= styler
.GetPropertyInt("tab.timmy.whinge.level");
170 int state
= initStyle
& 31;
175 char chNext
= styler
[startPos
];
176 styler
.StartSegment(startPos
);
177 bool atStartLine
= true;
179 styler
.IndentAmount(lineCurrent
, &spaceFlags
, IsPyComment
);
180 for (int i
= startPos
; i
< lengthDoc
; i
++) {
183 char chBad
= static_cast<char>(64);
184 char chGood
= static_cast<char>(0);
185 char chFlags
= chGood
;
186 if (whingeLevel
== 1) {
187 chFlags
= (spaceFlags
& wsInconsistent
) ? chBad
: chGood
;
188 } else if (whingeLevel
== 2) {
189 chFlags
= (spaceFlags
& wsSpaceTab
) ? chBad
: chGood
;
190 } else if (whingeLevel
== 3) {
191 chFlags
= (spaceFlags
& wsSpace
) ? chBad
: chGood
;
192 } else if (whingeLevel
== 4) {
193 chFlags
= (spaceFlags
& wsTab
) ? chBad
: chGood
;
195 styler
.SetFlags(chFlags
, static_cast<char>(state
));
200 chNext
= styler
.SafeGetCharAt(i
+ 1);
201 char chNext2
= styler
.SafeGetCharAt(i
+ 2);
203 if ((ch
== '\r' && chNext
!= '\n') || (ch
== '\n') || (i
== lengthDoc
)) {
204 if ((state
== SCE_P_DEFAULT
) || (state
== SCE_P_TRIPLE
) || (state
== SCE_P_TRIPLEDOUBLE
)) {
205 // Perform colourisation of white space and triple quoted strings at end of each line to allow
206 // tab marking to work inside white space and triple quoted strings
207 styler
.ColourTo(i
, state
);
210 styler
.IndentAmount(lineCurrent
, &spaceFlags
, IsPyComment
);
214 if (styler
.IsLeadByte(ch
)) {
215 chNext
= styler
.SafeGetCharAt(i
+ 2);
222 if (state
== SCE_P_STRINGEOL
) {
223 if (ch
!= '\r' && ch
!= '\n') {
224 styler
.ColourTo(i
- 1, state
);
225 state
= SCE_P_DEFAULT
;
228 if (state
== SCE_P_DEFAULT
) {
229 if (IsPyWordStart(ch
, chNext
, chNext2
)) {
230 styler
.ColourTo(i
- 1, state
);
232 } else if (ch
== '#') {
233 styler
.ColourTo(i
- 1, state
);
234 state
= chNext
== '#' ? SCE_P_COMMENTBLOCK
: SCE_P_COMMENTLINE
;
235 } else if (IsPyStringStart(ch
, chNext
, chNext2
)) {
236 styler
.ColourTo(i
- 1, state
);
237 state
= GetPyStringState(styler
, i
, &nextIndex
);
238 if (nextIndex
!= i
+ 1) {
242 chNext
= styler
.SafeGetCharAt(i
+ 1);
244 } else if (isoperator(ch
)) {
245 styler
.ColourTo(i
- 1, state
);
246 styler
.ColourTo(i
, SCE_P_OPERATOR
);
248 } else if (state
== SCE_P_WORD
) {
249 if (!iswordchar(ch
)) {
250 ClassifyWordPy(styler
.GetStartSegment(), i
- 1, keywords
, styler
, prevWord
);
251 state
= SCE_P_DEFAULT
;
253 state
= chNext
== '#' ? SCE_P_COMMENTBLOCK
: SCE_P_COMMENTLINE
;
254 } else if (IsPyStringStart(ch
, chNext
, chNext2
)) {
255 styler
.ColourTo(i
- 1, state
);
256 state
= GetPyStringState(styler
, i
, &nextIndex
);
257 if (nextIndex
!= i
+ 1) {
261 chNext
= styler
.SafeGetCharAt(i
+ 1);
263 } else if (isoperator(ch
)) {
264 styler
.ColourTo(i
, SCE_P_OPERATOR
);
268 if (state
== SCE_P_COMMENTLINE
|| state
== SCE_P_COMMENTBLOCK
) {
269 if (ch
== '\r' || ch
== '\n') {
270 styler
.ColourTo(i
- 1, state
);
271 state
= SCE_P_DEFAULT
;
273 } else if (state
== SCE_P_STRING
) {
274 if ((ch
== '\r' || ch
== '\n') && (chPrev
!= '\\')) {
275 styler
.ColourTo(i
- 1, state
);
276 state
= SCE_P_STRINGEOL
;
277 } else if (ch
== '\\') {
278 if (chNext
== '\"' || chNext
== '\'' || chNext
== '\\') {
281 chNext
= styler
.SafeGetCharAt(i
+ 1);
283 } else if (ch
== '\"') {
284 styler
.ColourTo(i
, state
);
285 state
= SCE_P_DEFAULT
;
287 } else if (state
== SCE_P_CHARACTER
) {
288 if ((ch
== '\r' || ch
== '\n') && (chPrev
!= '\\')) {
289 styler
.ColourTo(i
- 1, state
);
290 state
= SCE_P_STRINGEOL
;
291 } else if (ch
== '\\') {
292 if (chNext
== '\"' || chNext
== '\'' || chNext
== '\\') {
295 chNext
= styler
.SafeGetCharAt(i
+ 1);
297 } else if (ch
== '\'') {
298 styler
.ColourTo(i
, state
);
299 state
= SCE_P_DEFAULT
;
301 } else if (state
== SCE_P_TRIPLE
) {
302 if (ch
== '\'' && chPrev
== '\'' && chPrev2
== '\'') {
303 styler
.ColourTo(i
, state
);
304 state
= SCE_P_DEFAULT
;
306 } else if (state
== SCE_P_TRIPLEDOUBLE
) {
307 if (ch
== '\"' && chPrev
== '\"' && chPrev2
== '\"') {
308 styler
.ColourTo(i
, state
);
309 state
= SCE_P_DEFAULT
;
316 if (state
== SCE_P_WORD
) {
317 ClassifyWordPy(styler
.GetStartSegment(), lengthDoc
, keywords
, styler
, prevWord
);
319 styler
.ColourTo(lengthDoc
, state
);
323 static bool IsCommentLine(int line
, Accessor
&styler
) {
324 int pos
= styler
.LineStart(line
);
325 int eol_pos
= styler
.LineStart(line
+1) - 1;
326 for (int i
= pos
; i
< eol_pos
; i
++) {
330 else if (ch
!= ' ' && ch
!= '\t')
336 static bool IsQuoteLine(int line
, Accessor
&styler
) {
337 int style
= styler
.StyleAt(styler
.LineStart(line
)) & 31;
338 return ((style
== SCE_P_TRIPLE
) || (style
== SCE_P_TRIPLEDOUBLE
));
341 static void FoldPyDoc(unsigned int startPos
, int length
, int /*initStyle - unused*/,
342 WordList
*[], Accessor
&styler
) {
343 int maxPos
= startPos
+ length
;
344 int maxLines
= styler
.GetLine(maxPos
-1);
346 bool foldComment
= styler
.GetPropertyInt("fold.comment.python");
347 bool foldQuotes
= styler
.GetPropertyInt("fold.quotes.python");
349 // Backtrack to previous non-blank line so we can determine indent level
350 // for any white space lines (needed esp. within triple quoted strings)
351 // and so we can fix any preceding fold level (which is why we go back
352 // at least one line in all cases)
354 int lineCurrent
= styler
.GetLine(startPos
);
355 int indentCurrent
= styler
.IndentAmount(lineCurrent
, &spaceFlags
, NULL
);
356 while (lineCurrent
> 0) {
358 indentCurrent
= styler
.IndentAmount(lineCurrent
, &spaceFlags
, NULL
);
359 if (!(indentCurrent
& SC_FOLDLEVELWHITEFLAG
) &&
360 (!IsCommentLine(lineCurrent
, styler
)) &&
361 (!IsQuoteLine(lineCurrent
, styler
)))
364 int indentCurrentLevel
= indentCurrent
& SC_FOLDLEVELNUMBERMASK
;
366 startPos
= styler
.LineStart(lineCurrent
);
367 // Set up initial state
368 int prev_state
= SCE_P_DEFAULT
& 31;
369 if (lineCurrent
>= 1)
370 prev_state
= styler
.StyleAt(startPos
-1) & 31;
371 int prevQuote
= foldQuotes
&& ((prev_state
== SCE_P_TRIPLE
) || (prev_state
== SCE_P_TRIPLEDOUBLE
));
373 if (lineCurrent
>= 1)
374 prevComment
= foldComment
&& IsCommentLine(lineCurrent
- 1, styler
);
376 // Process all characters to end of requested range or end of any triple quote
377 // or comment that hangs over the end of the range
378 while ((lineCurrent
<= maxLines
) || prevQuote
|| prevComment
) {
381 int lev
= indentCurrent
;
382 int lineNext
= lineCurrent
+ 1;
383 int style
= styler
.StyleAt(styler
.LineStart(lineNext
)) & 31;
384 int indentNext
= styler
.IndentAmount(lineNext
, &spaceFlags
, NULL
);
385 int quote
= foldQuotes
&& ((style
== SCE_P_TRIPLE
) || (style
== SCE_P_TRIPLEDOUBLE
));
386 int quote_start
= (quote
&& !prevQuote
);
387 int quote_continue
= (quote
&& prevQuote
);
388 int comment
= foldComment
&& IsCommentLine(lineCurrent
, styler
);
389 int comment_start
= (comment
&& !prevComment
&&
390 IsCommentLine(lineNext
, styler
) && (lev
> SC_FOLDLEVELBASE
));
391 int comment_continue
= (comment
&& prevComment
);
392 if ((!quote
|| !prevQuote
) && !comment
)
393 indentCurrentLevel
= indentCurrent
& SC_FOLDLEVELNUMBERMASK
;
395 indentNext
= indentCurrentLevel
;
396 if (indentNext
& SC_FOLDLEVELWHITEFLAG
)
397 indentNext
= SC_FOLDLEVELWHITEFLAG
| indentCurrentLevel
;
400 // Place fold point at start of triple quoted string
401 lev
|= SC_FOLDLEVELHEADERFLAG
;
402 } else if (quote_continue
|| prevQuote
) {
403 // Add level to rest of lines in the string
405 } else if (comment_start
) {
406 // Place fold point at start of a block of comments
407 lev
|= SC_FOLDLEVELHEADERFLAG
;
408 } else if (comment_continue
) {
409 // Add level to rest of lines in the block
413 // Skip past any blank lines for next indent level info; we skip also comments
414 // starting in column 0 which effectively folds them into surrounding code
415 // rather than screwing up folding. Then set indent level on the lines
416 // we skipped to be same as maximum of current and next indent. This approach
417 // does a reasonable job of collapsing white space into surrounding code
418 // without getting confused by white space at the start of an indented level.
420 ((indentNext
& SC_FOLDLEVELWHITEFLAG
) || styler
[styler
.LineStart(lineNext
)] == '#') &&
421 (lineNext
< maxLines
)) {
422 int level
= Platform::Maximum(indentCurrent
, indentNext
);
423 if (indentNext
& SC_FOLDLEVELWHITEFLAG
)
424 level
= SC_FOLDLEVELWHITEFLAG
| indentCurrentLevel
;
425 styler
.SetLevel(lineNext
, level
);
427 indentNext
= styler
.IndentAmount(lineNext
, &spaceFlags
, NULL
);
430 // Set fold header on non-quote/non-comment line
431 if (!quote
&& !comment
&& !(indentCurrent
& SC_FOLDLEVELWHITEFLAG
) ) {
432 if ((indentCurrent
& SC_FOLDLEVELNUMBERMASK
) < (indentNext
& SC_FOLDLEVELNUMBERMASK
))
433 lev
|= SC_FOLDLEVELHEADERFLAG
;
436 // Keep track of triple quote and block comment state of previous line
438 prevComment
= comment_start
|| comment_continue
;
440 // Set fold level for this line and move to next line
441 styler
.SetLevel(lineCurrent
, lev
);
442 indentCurrent
= indentNext
;
443 lineCurrent
= lineNext
;
446 // Make sure last line indent level is set too
447 styler
.SetLevel(lineCurrent
, indentCurrent
);
450 LexerModule
lmPython(SCLEX_PYTHON
, ColourisePyDoc
, "python", FoldPyDoc
);