]>
git.saurik.com Git - wxWidgets.git/blob - contrib/src/stc/scintilla/src/LexPython.cxx
221859035e353cca2f3e6377be2c48fadf5bc8c2
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.
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);
55 else if (ch
== 'u' || ch
== 'U') {
56 if (chNext
== 'r' || chNext
== 'R')
60 ch
= styler
.SafeGetCharAt(i
);
61 chNext
= styler
.SafeGetCharAt(i
+ 1);
64 if (ch
!= '"' && ch
!= '\'') {
69 if (ch
== chNext
&& ch
== styler
.SafeGetCharAt(i
+ 2)) {
73 return SCE_P_TRIPLEDOUBLE
;
82 return SCE_P_CHARACTER
;
86 inline bool IsAWordChar(int ch
) {
87 return (ch
< 0x80) && (isalnum(ch
) || ch
== '.' || ch
== '_');
90 inline bool IsAWordStart(int ch
) {
91 return (ch
< 0x80) && (isalnum(ch
) || ch
== '_');
94 static void ColourisePyDoc(unsigned int startPos
, int length
, int initStyle
,
95 WordList
*keywordlists
[], Accessor
&styler
) {
97 int endPos
= startPos
+ length
;
99 // Backtrack to previous line in case need to fix its tab whinging
100 int lineCurrent
= styler
.GetLine(startPos
);
102 if (lineCurrent
> 0) {
103 startPos
= styler
.LineStart(lineCurrent
-1);
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
);
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
);
161 // Check for a state end
162 if (sc
.state
== SCE_P_OPERATOR
) {
164 sc
.SetState(SCE_C_DEFAULT
);
165 } else if (sc
.state
== SCE_P_NUMBER
) {
166 if (!IsAWordChar(sc
.ch
)) {
167 sc
.SetState(SCE_P_DEFAULT
);
169 } else if (sc
.state
== SCE_P_WORD
) {
170 if ((sc
.ch
== '.') || (!IsAWordChar(sc
.ch
))) {
172 sc
.GetCurrent(s
, sizeof(s
));
173 int style
= SCE_P_IDENTIFIER
;
174 if ((kwLast
== kwImport
) && (strcmp(s
, "as") == 0)) {
176 } else if (keywords
.InList(s
)) {
178 } else if (kwLast
== kwClass
) {
179 style
= SCE_P_CLASSNAME
;
180 } else if (kwLast
== kwDef
) {
181 style
= SCE_P_DEFNAME
;
183 sc
.ChangeState(style
);
184 sc
.SetState(SCE_P_DEFAULT
);
185 if (style
== SCE_P_WORD
) {
186 if (0 == strcmp(s
, "class"))
188 else if (0 == strcmp(s
, "def"))
190 else if (0 == strcmp(s
, "import"))
194 } else if (style
== SCE_P_CLASSNAME
) {
196 } else if (style
== SCE_P_DEFNAME
) {
200 } else if ((sc
.state
== SCE_P_COMMENTLINE
) || (sc
.state
== SCE_P_COMMENTBLOCK
)) {
201 if (sc
.ch
== '\r' || sc
.ch
== '\n') {
202 sc
.SetState(SCE_P_DEFAULT
);
204 } else if ((sc
.state
== SCE_P_STRING
) || (sc
.state
== SCE_P_CHARACTER
)) {
206 if ((sc
.chNext
== '\r') && (sc
.GetRelative(2) == '\n')) {
210 } else if ((sc
.state
== SCE_P_STRING
) && (sc
.ch
== '\"')) {
211 sc
.ForwardSetState(SCE_P_DEFAULT
);
212 } else if ((sc
.state
== SCE_P_CHARACTER
) && (sc
.ch
== '\'')) {
213 sc
.ForwardSetState(SCE_P_DEFAULT
);
215 } else if (sc
.state
== SCE_P_TRIPLE
) {
218 } else if (sc
.Match("\'\'\'")) {
221 sc
.ForwardSetState(SCE_P_DEFAULT
);
223 } else if (sc
.state
== SCE_P_TRIPLEDOUBLE
) {
226 } else if (sc
.Match("\"\"\"")) {
229 sc
.ForwardSetState(SCE_P_DEFAULT
);
233 // Check for a new state starting character
234 if (sc
.state
== SCE_P_DEFAULT
) {
235 if (isascii(sc
.ch
) && isoperator(static_cast<char>(sc
.ch
)) || sc
.ch
== '`') {
236 sc
.SetState(SCE_P_OPERATOR
);
237 } else if (sc
.ch
== '#') {
238 sc
.SetState(sc
.chNext
== '#' ? SCE_P_COMMENTBLOCK
: SCE_P_COMMENTLINE
);
239 } else if (IsADigit(sc
.ch
) || (sc
.ch
== '.' && IsADigit(sc
.chNext
))) {
240 sc
.SetState(SCE_P_NUMBER
);
241 } else if (IsPyStringStart(sc
.ch
, sc
.chNext
, sc
.GetRelative(2))) {
243 sc
.SetState(GetPyStringState(styler
, sc
.currentPos
, &nextIndex
));
244 while (nextIndex
> (sc
.currentPos
+1)) {
247 } else if (IsAWordStart(sc
.ch
)) {
248 sc
.SetState(SCE_P_WORD
);
255 static bool IsCommentLine(int line
, Accessor
&styler
) {
256 int pos
= styler
.LineStart(line
);
257 int eol_pos
= styler
.LineStart(line
+1) - 1;
258 for (int i
= pos
; i
< eol_pos
; i
++) {
262 else if (ch
!= ' ' && ch
!= '\t')
268 static bool IsQuoteLine(int line
, Accessor
&styler
) {
269 int style
= styler
.StyleAt(styler
.LineStart(line
)) & 31;
270 return ((style
== SCE_P_TRIPLE
) || (style
== SCE_P_TRIPLEDOUBLE
));
274 static void FoldPyDoc(unsigned int startPos
, int length
, int /*initStyle - unused*/,
275 WordList
*[], Accessor
&styler
) {
276 const int maxPos
= startPos
+ length
;
277 const int maxLines
= styler
.GetLine(maxPos
-1); // Requested last line
278 const int docLines
= styler
.GetLine(styler
.Length() - 1); // Available last line
279 const bool foldComment
= styler
.GetPropertyInt("fold.comment.python");
280 const bool foldQuotes
= styler
.GetPropertyInt("fold.quotes.python");
282 // Backtrack to previous non-blank line so we can determine indent level
283 // for any white space lines (needed esp. within triple quoted strings)
284 // and so we can fix any preceding fold level (which is why we go back
285 // at least one line in all cases)
287 int lineCurrent
= styler
.GetLine(startPos
);
288 int indentCurrent
= styler
.IndentAmount(lineCurrent
, &spaceFlags
, NULL
);
289 while (lineCurrent
> 0) {
291 indentCurrent
= styler
.IndentAmount(lineCurrent
, &spaceFlags
, NULL
);
292 if (!(indentCurrent
& SC_FOLDLEVELWHITEFLAG
) &&
293 (!IsCommentLine(lineCurrent
, styler
)) &&
294 (!IsQuoteLine(lineCurrent
, styler
)))
297 int indentCurrentLevel
= indentCurrent
& SC_FOLDLEVELNUMBERMASK
;
299 // Set up initial loop state
300 startPos
= styler
.LineStart(lineCurrent
);
301 int prev_state
= SCE_P_DEFAULT
& 31;
302 if (lineCurrent
>= 1)
303 prev_state
= styler
.StyleAt(startPos
-1) & 31;
304 int prevQuote
= foldQuotes
&& ((prev_state
== SCE_P_TRIPLE
) || (prev_state
== SCE_P_TRIPLEDOUBLE
));
306 if (lineCurrent
>= 1)
307 prevComment
= foldComment
&& IsCommentLine(lineCurrent
- 1, styler
);
309 // Process all characters to end of requested range or end of any triple quote
310 // or comment that hangs over the end of the range. Cap processing in all cases
311 // to end of document (in case of unclosed quote or comment at end).
312 while ((lineCurrent
<= docLines
) && ((lineCurrent
<= maxLines
) || prevQuote
|| prevComment
)) {
315 int lev
= indentCurrent
;
316 int lineNext
= lineCurrent
+ 1;
317 int indentNext
= indentCurrent
;
319 if (lineNext
<= docLines
) {
320 // Information about next line is only available if not at end of document
321 indentNext
= styler
.IndentAmount(lineNext
, &spaceFlags
, NULL
);
322 int style
= styler
.StyleAt(styler
.LineStart(lineNext
)) & 31;
323 quote
= foldQuotes
&& ((style
== SCE_P_TRIPLE
) || (style
== SCE_P_TRIPLEDOUBLE
));
325 const int quote_start
= (quote
&& !prevQuote
);
326 const int quote_continue
= (quote
&& prevQuote
);
327 const int comment
= foldComment
&& IsCommentLine(lineCurrent
, styler
);
328 const int comment_start
= (comment
&& !prevComment
&& (lineNext
<= docLines
) &&
329 IsCommentLine(lineNext
, styler
) && (lev
> SC_FOLDLEVELBASE
));
330 const int comment_continue
= (comment
&& prevComment
);
331 if ((!quote
|| !prevQuote
) && !comment
)
332 indentCurrentLevel
= indentCurrent
& SC_FOLDLEVELNUMBERMASK
;
334 indentNext
= indentCurrentLevel
;
335 if (indentNext
& SC_FOLDLEVELWHITEFLAG
)
336 indentNext
= SC_FOLDLEVELWHITEFLAG
| indentCurrentLevel
;
339 // Place fold point at start of triple quoted string
340 lev
|= SC_FOLDLEVELHEADERFLAG
;
341 } else if (quote_continue
|| prevQuote
) {
342 // Add level to rest of lines in the string
344 } else if (comment_start
) {
345 // Place fold point at start of a block of comments
346 lev
|= SC_FOLDLEVELHEADERFLAG
;
347 } else if (comment_continue
) {
348 // Add level to rest of lines in the block
352 // Skip past any blank lines for next indent level info; we skip also comments
353 // starting in column 0 which effectively folds them into surrounding code rather
354 // than screwing up folding.
355 const int saveIndentNext
= indentNext
;
357 (lineNext
< docLines
) &&
358 ((indentNext
& SC_FOLDLEVELWHITEFLAG
) ||
359 (lineNext
<= docLines
&& styler
[styler
.LineStart(lineNext
)] == '#'))) {
362 indentNext
= styler
.IndentAmount(lineNext
, &spaceFlags
, NULL
);
365 // Next compute max indent level of current line and next non-blank line.
366 // This is the level to which we set all the intervening blank or comment lines.
367 const int skip_level
= Platform::Maximum(indentCurrentLevel
,
368 indentNext
& SC_FOLDLEVELNUMBERMASK
);
370 // Now set all the indent levels on the lines we skipped
371 int skipLine
= lineCurrent
+ 1;
372 int skipIndentNext
= saveIndentNext
;
373 while (skipLine
< lineNext
) {
374 int skipLineLevel
= skip_level
;
375 if (skipIndentNext
& SC_FOLDLEVELWHITEFLAG
)
376 skipLineLevel
= SC_FOLDLEVELWHITEFLAG
| skipLineLevel
;
377 styler
.SetLevel(skipLine
, skipLineLevel
);
379 skipIndentNext
= styler
.IndentAmount(skipLine
, &spaceFlags
, NULL
);
382 // Set fold header on non-quote/non-comment line
383 if (!quote
&& !comment
&& !(indentCurrent
& SC_FOLDLEVELWHITEFLAG
) ) {
384 if ((indentCurrent
& SC_FOLDLEVELNUMBERMASK
) < (indentNext
& SC_FOLDLEVELNUMBERMASK
))
385 lev
|= SC_FOLDLEVELHEADERFLAG
;
388 // Keep track of triple quote and block comment state of previous line
390 prevComment
= comment_start
|| comment_continue
;
392 // Set fold level for this line and move to next line
393 styler
.SetLevel(lineCurrent
, lev
);
394 indentCurrent
= indentNext
;
395 lineCurrent
= lineNext
;
398 // NOTE: Cannot set level of last line here because indentCurrent doesn't have
399 // header flag set; the loop above is crafted to take care of this case!
400 //styler.SetLevel(lineCurrent, indentCurrent);
403 LexerModule
lmPython(SCLEX_PYTHON
, ColourisePyDoc
, "python", FoldPyDoc
);