]>
git.saurik.com Git - wxWidgets.git/blob - contrib/src/stc/scintilla/src/LexPython.cxx
bfaa08f0e9daba30327b4f269832a9c0abdc5085
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
, 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) {
102 startPos
= styler
.LineStart(lineCurrent
- 1);
104 initStyle
= SCE_P_DEFAULT
;
106 initStyle
= styler
.StyleAt(startPos
- 1);
110 WordList
&keywords
= *keywordlists
[0];
112 const int whingeLevel
= styler
.GetPropertyInt("tab.timmy.whinge.level");
114 initStyle
= initStyle
& 31;
115 if (initStyle
== SCE_P_STRINGEOL
) {
116 initStyle
= SCE_P_DEFAULT
;
119 kwType kwLast
= kwOther
;
121 styler
.IndentAmount(lineCurrent
, &spaceFlags
, IsPyComment
);
122 bool hexadecimal
= false;
124 // Python uses a different mask because bad indentation is marked by oring with 32
125 StyleContext
sc(startPos
, endPos
- startPos
, initStyle
, styler
, 0x7f);
127 for (; sc
.More(); sc
.Forward()) {
129 if (sc
.atLineStart
) {
130 const char chBad
= static_cast<char>(64);
131 const char chGood
= static_cast<char>(0);
132 char chFlags
= chGood
;
133 if (whingeLevel
== 1) {
134 chFlags
= (spaceFlags
& wsInconsistent
) ? chBad
: chGood
;
135 } else if (whingeLevel
== 2) {
136 chFlags
= (spaceFlags
& wsSpaceTab
) ? chBad
: chGood
;
137 } else if (whingeLevel
== 3) {
138 chFlags
= (spaceFlags
& wsSpace
) ? chBad
: chGood
;
139 } else if (whingeLevel
== 4) {
140 chFlags
= (spaceFlags
& wsTab
) ? chBad
: chGood
;
142 styler
.SetFlags(chFlags
, static_cast<char>(sc
.state
));
146 if ((sc
.state
== SCE_P_DEFAULT
) ||
147 (sc
.state
== SCE_P_TRIPLE
) ||
148 (sc
.state
== SCE_P_TRIPLEDOUBLE
)) {
149 // Perform colourisation of white space and triple quoted strings at end of each line to allow
150 // tab marking to work inside white space and triple quoted strings
151 sc
.ForwardSetState(sc
.state
);
154 styler
.IndentAmount(lineCurrent
, &spaceFlags
, IsPyComment
);
155 if ((sc
.state
== SCE_P_STRING
) || (sc
.state
== SCE_P_CHARACTER
)) {
156 sc
.ChangeState(SCE_P_STRINGEOL
);
157 sc
.ForwardSetState(SCE_P_DEFAULT
);
163 // Check for a state end
164 if (sc
.state
== SCE_P_OPERATOR
) {
166 sc
.SetState(SCE_P_DEFAULT
);
167 } else if (sc
.state
== SCE_P_NUMBER
) {
168 if (!IsAWordChar(sc
.ch
) &&
169 !(!hexadecimal
&& ((sc
.ch
== '+' || sc
.ch
== '-') && (sc
.chPrev
== 'e' || sc
.chPrev
== 'E')))) {
170 sc
.SetState(SCE_P_DEFAULT
);
172 } else if (sc
.state
== SCE_P_IDENTIFIER
) {
173 if ((sc
.ch
== '.') || (!IsAWordChar(sc
.ch
))) {
175 sc
.GetCurrent(s
, sizeof(s
));
176 int style
= SCE_P_IDENTIFIER
;
177 if ((kwLast
== kwImport
) && (strcmp(s
, "as") == 0)) {
179 } else if (keywords
.InList(s
)) {
181 } else if (kwLast
== kwClass
) {
182 style
= SCE_P_CLASSNAME
;
183 } else if (kwLast
== kwDef
) {
184 style
= SCE_P_DEFNAME
;
186 sc
.ChangeState(style
);
187 sc
.SetState(SCE_P_DEFAULT
);
188 if (style
== SCE_P_WORD
) {
189 if (0 == strcmp(s
, "class"))
191 else if (0 == strcmp(s
, "def"))
193 else if (0 == strcmp(s
, "import"))
197 } else if (style
== SCE_P_CLASSNAME
) {
199 } else if (style
== SCE_P_DEFNAME
) {
203 } else if ((sc
.state
== SCE_P_COMMENTLINE
) || (sc
.state
== SCE_P_COMMENTBLOCK
)) {
204 if (sc
.ch
== '\r' || sc
.ch
== '\n') {
205 sc
.SetState(SCE_P_DEFAULT
);
207 } else if ((sc
.state
== SCE_P_STRING
) || (sc
.state
== SCE_P_CHARACTER
)) {
209 if ((sc
.chNext
== '\r') && (sc
.GetRelative(2) == '\n')) {
213 } else if ((sc
.state
== SCE_P_STRING
) && (sc
.ch
== '\"')) {
214 sc
.ForwardSetState(SCE_P_DEFAULT
);
215 } else if ((sc
.state
== SCE_P_CHARACTER
) && (sc
.ch
== '\'')) {
216 sc
.ForwardSetState(SCE_P_DEFAULT
);
218 } else if (sc
.state
== SCE_P_TRIPLE
) {
221 } else if (sc
.Match("\'\'\'")) {
224 sc
.ForwardSetState(SCE_P_DEFAULT
);
226 } else if (sc
.state
== SCE_P_TRIPLEDOUBLE
) {
229 } else if (sc
.Match("\"\"\"")) {
232 sc
.ForwardSetState(SCE_P_DEFAULT
);
236 // Check for a new state starting character
237 if (sc
.state
== SCE_P_DEFAULT
) {
238 if (IsADigit(sc
.ch
) || (sc
.ch
== '.' && IsADigit(sc
.chNext
))) {
239 if (sc
.ch
== '0' && (sc
.chNext
== 'x' || sc
.chNext
== 'X')) {
244 sc
.SetState(SCE_P_NUMBER
);
245 } else if (isascii(sc
.ch
) && isoperator(static_cast<char>(sc
.ch
)) || sc
.ch
== '`') {
246 sc
.SetState(SCE_P_OPERATOR
);
247 } else if (sc
.ch
== '#') {
248 sc
.SetState(sc
.chNext
== '#' ? SCE_P_COMMENTBLOCK
: SCE_P_COMMENTLINE
);
249 } else if (IsPyStringStart(sc
.ch
, sc
.chNext
, sc
.GetRelative(2))) {
251 sc
.SetState(GetPyStringState(styler
, sc
.currentPos
, &nextIndex
));
252 while (nextIndex
> (sc
.currentPos
+ 1)) {
255 } else if (IsAWordStart(sc
.ch
)) {
256 sc
.SetState(SCE_P_IDENTIFIER
);
263 static bool IsCommentLine(int line
, Accessor
&styler
) {
264 int pos
= styler
.LineStart(line
);
265 int eol_pos
= styler
.LineStart(line
+ 1) - 1;
266 for (int i
= pos
; i
< eol_pos
; i
++) {
270 else if (ch
!= ' ' && ch
!= '\t')
276 static bool IsQuoteLine(int line
, Accessor
&styler
) {
277 int style
= styler
.StyleAt(styler
.LineStart(line
)) & 31;
278 return ((style
== SCE_P_TRIPLE
) || (style
== SCE_P_TRIPLEDOUBLE
));
282 static void FoldPyDoc(unsigned int startPos
, int length
, int /*initStyle - unused*/,
283 WordList
*[], Accessor
&styler
) {
284 const int maxPos
= startPos
+ length
;
285 const int maxLines
= styler
.GetLine(maxPos
- 1); // Requested last line
286 const int docLines
= styler
.GetLine(styler
.Length() - 1); // Available last line
287 const bool foldComment
= styler
.GetPropertyInt("fold.comment.python") != 0;
288 const bool foldQuotes
= styler
.GetPropertyInt("fold.quotes.python") != 0;
290 // Backtrack to previous non-blank line so we can determine indent level
291 // for any white space lines (needed esp. within triple quoted strings)
292 // and so we can fix any preceding fold level (which is why we go back
293 // at least one line in all cases)
295 int lineCurrent
= styler
.GetLine(startPos
);
296 int indentCurrent
= styler
.IndentAmount(lineCurrent
, &spaceFlags
, NULL
);
297 while (lineCurrent
> 0) {
299 indentCurrent
= styler
.IndentAmount(lineCurrent
, &spaceFlags
, NULL
);
300 if (!(indentCurrent
& SC_FOLDLEVELWHITEFLAG
) &&
301 (!IsCommentLine(lineCurrent
, styler
)) &&
302 (!IsQuoteLine(lineCurrent
, styler
)))
305 int indentCurrentLevel
= indentCurrent
& SC_FOLDLEVELNUMBERMASK
;
307 // Set up initial loop state
308 startPos
= styler
.LineStart(lineCurrent
);
309 int prev_state
= SCE_P_DEFAULT
& 31;
310 if (lineCurrent
>= 1)
311 prev_state
= styler
.StyleAt(startPos
- 1) & 31;
312 int prevQuote
= foldQuotes
&& ((prev_state
== SCE_P_TRIPLE
) || (prev_state
== SCE_P_TRIPLEDOUBLE
));
314 if (lineCurrent
>= 1)
315 prevComment
= foldComment
&& IsCommentLine(lineCurrent
- 1, styler
);
317 // Process all characters to end of requested range or end of any triple quote
318 // or comment that hangs over the end of the range. Cap processing in all cases
319 // to end of document (in case of unclosed quote or comment at end).
320 while ((lineCurrent
<= docLines
) && ((lineCurrent
<= maxLines
) || prevQuote
|| prevComment
)) {
323 int lev
= indentCurrent
;
324 int lineNext
= lineCurrent
+ 1;
325 int indentNext
= indentCurrent
;
327 if (lineNext
<= docLines
) {
328 // Information about next line is only available if not at end of document
329 indentNext
= styler
.IndentAmount(lineNext
, &spaceFlags
, NULL
);
330 int style
= styler
.StyleAt(styler
.LineStart(lineNext
)) & 31;
331 quote
= foldQuotes
&& ((style
== SCE_P_TRIPLE
) || (style
== SCE_P_TRIPLEDOUBLE
));
333 const int quote_start
= (quote
&& !prevQuote
);
334 const int quote_continue
= (quote
&& prevQuote
);
335 const int comment
= foldComment
&& IsCommentLine(lineCurrent
, styler
);
336 const int comment_start
= (comment
&& !prevComment
&& (lineNext
<= docLines
) &&
337 IsCommentLine(lineNext
, styler
) && (lev
> SC_FOLDLEVELBASE
));
338 const int comment_continue
= (comment
&& prevComment
);
339 if ((!quote
|| !prevQuote
) && !comment
)
340 indentCurrentLevel
= indentCurrent
& SC_FOLDLEVELNUMBERMASK
;
342 indentNext
= indentCurrentLevel
;
343 if (indentNext
& SC_FOLDLEVELWHITEFLAG
)
344 indentNext
= SC_FOLDLEVELWHITEFLAG
| indentCurrentLevel
;
347 // Place fold point at start of triple quoted string
348 lev
|= SC_FOLDLEVELHEADERFLAG
;
349 } else if (quote_continue
|| prevQuote
) {
350 // Add level to rest of lines in the string
352 } else if (comment_start
) {
353 // Place fold point at start of a block of comments
354 lev
|= SC_FOLDLEVELHEADERFLAG
;
355 } else if (comment_continue
) {
356 // Add level to rest of lines in the block
360 // Skip past any blank lines for next indent level info; we skip also comments
361 // starting in column 0 which effectively folds them into surrounding code rather
362 // than screwing up folding.
363 const int saveIndentNext
= indentNext
;
365 (lineNext
< docLines
) &&
366 ((indentNext
& SC_FOLDLEVELWHITEFLAG
) ||
367 (lineNext
<= docLines
&& styler
[styler
.LineStart(lineNext
)] == '#'))) {
370 indentNext
= styler
.IndentAmount(lineNext
, &spaceFlags
, NULL
);
373 // Next compute max indent level of current line and next non-blank line.
374 // This is the level to which we set all the intervening blank or comment lines.
375 const int skip_level
= Platform::Maximum(indentCurrentLevel
,
376 indentNext
& SC_FOLDLEVELNUMBERMASK
);
378 // Now set all the indent levels on the lines we skipped
379 int skipLine
= lineCurrent
+ 1;
380 int skipIndentNext
= saveIndentNext
;
381 while (skipLine
< lineNext
) {
382 int skipLineLevel
= skip_level
;
383 if (skipIndentNext
& SC_FOLDLEVELWHITEFLAG
)
384 skipLineLevel
= SC_FOLDLEVELWHITEFLAG
| skipLineLevel
;
385 styler
.SetLevel(skipLine
, skipLineLevel
);
387 skipIndentNext
= styler
.IndentAmount(skipLine
, &spaceFlags
, NULL
);
390 // Set fold header on non-quote/non-comment line
391 if (!quote
&& !comment
&& !(indentCurrent
& SC_FOLDLEVELWHITEFLAG
) ) {
392 if ((indentCurrent
& SC_FOLDLEVELNUMBERMASK
) < (indentNext
& SC_FOLDLEVELNUMBERMASK
))
393 lev
|= SC_FOLDLEVELHEADERFLAG
;
396 // Keep track of triple quote and block comment state of previous line
398 prevComment
= comment_start
|| comment_continue
;
400 // Set fold level for this line and move to next line
401 styler
.SetLevel(lineCurrent
, lev
);
402 indentCurrent
= indentNext
;
403 lineCurrent
= lineNext
;
406 // NOTE: Cannot set level of last line here because indentCurrent doesn't have
407 // header flag set; the loop above is crafted to take care of this case!
408 //styler.SetLevel(lineCurrent, indentCurrent);
411 static const char * const pythonWordListDesc
[] = {
416 LexerModule
lmPython(SCLEX_PYTHON
, ColourisePyDoc
, "python", FoldPyDoc
,