diff options
Diffstat (limited to 'src/LexPython.cxx')
-rw-r--r-- | src/LexPython.cxx | 137 |
1 files changed, 101 insertions, 36 deletions
diff --git a/src/LexPython.cxx b/src/LexPython.cxx index 50ef6a84b..c3a9ab2f8 100644 --- a/src/LexPython.cxx +++ b/src/LexPython.cxx @@ -320,56 +320,121 @@ static void ColourisePyDoc(unsigned int startPos, int length, int initStyle, } } -static void FoldPyDoc(unsigned int startPos, int length, int initStyle, - WordList *[], Accessor &styler) { - int lengthDoc = startPos + length; +static bool IsCommentLine(int line, Accessor &styler) { + int pos = styler.LineStart(line); + int eol_pos = styler.LineStart(line+1) - 1; + for (int i = pos; i < eol_pos; i++) { + char ch = styler[i]; + if (ch == '#') + return true; + else if (ch != ' ' && ch != '\t') + return false; + } + return false; +} - // Backtrack to previous line in case need to fix its fold status +static void FoldPyDoc(unsigned int startPos, int length, int /*initStyle - unused*/, + WordList *[], Accessor &styler) { + int maxPos = startPos + length; + int maxLines = styler.GetLine(maxPos-1); + int lengthDoc = styler.Length(); + + // Backtrack to previous non-blank line so we can determine indent level + // for any white space lines (needed esp. within triple quoted strings) + // and so we can fix any preceding fold level (which is why we go back + // at least one line in all cases) + int spaceFlags = 0; int lineCurrent = styler.GetLine(startPos); - if (startPos > 0) { - if (lineCurrent > 0) { - lineCurrent--; - startPos = styler.LineStart(lineCurrent); - if (startPos == 0) - initStyle = SCE_P_DEFAULT; - else - initStyle = styler.StyleAt(startPos-1); - } + int indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags, NULL); + bool first = true; + while ((startPos > 0) && (lineCurrent > 0) && (first || indentCurrent & SC_FOLDLEVELWHITEFLAG)) { + lineCurrent--; + startPos = styler.LineStart(lineCurrent); + indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags, NULL); + first = false; } - int state = initStyle & 31; - int spaceFlags = 0; - int indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags, IsPyComment); - if ((state == SCE_P_TRIPLE) || (state == SCE_P_TRIPLEDOUBLE)) - indentCurrent |= SC_FOLDLEVELWHITEFLAG; + + // Set up initial state + int prev_state = SCE_P_DEFAULT & 31; + if (lineCurrent >= 1) + prev_state = styler.StyleAt(startPos-1) & 31; + int prevQuote = ((prev_state == SCE_P_TRIPLE) || (prev_state == SCE_P_TRIPLEDOUBLE)); + int prevComment = 0; + if (lineCurrent >= 1) + prevComment = IsCommentLine(lineCurrent - 1, styler); char chNext = styler[startPos]; - for (int i = startPos; i < lengthDoc; i++) { + + // Process all characters to end of requested range or end of any triple quote + // or comment that hangs over the end of the range + for (int i = startPos; i < lengthDoc && ((lineCurrent < maxLines) || prevQuote || prevComment); i++) { + + // Next character char ch = chNext; chNext = styler.SafeGetCharAt(i + 1); - if ((ch == '\r' && chNext != '\n') || (ch == '\n') || (i == lengthDoc)) { + // Process line change (also if reach very end of document) + if ((ch == '\r' && chNext != '\n') || (ch == '\n') || (i == lengthDoc-1)) { + + // Gather info int lev = indentCurrent; int style = styler.StyleAt(i) & 31; - int indentNext = styler.IndentAmount(lineCurrent + 1, &spaceFlags, IsPyComment); - if ((style == SCE_P_TRIPLE) || (style== SCE_P_TRIPLEDOUBLE)) - indentNext |= SC_FOLDLEVELWHITEFLAG; - if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG)) { - // Only non whitespace lines can be headers - if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext & SC_FOLDLEVELNUMBERMASK)) { + int lineNext = lineCurrent + 1; + int indentNext = styler.IndentAmount(lineNext, &spaceFlags, NULL); + int quote = ((style == SCE_P_TRIPLE) || (style== SCE_P_TRIPLEDOUBLE)); + int quote_start = (quote && !prevQuote); + int quote_continue = (quote && prevQuote); + int comment = IsCommentLine(lineCurrent, styler); + int comment_start = (comment && !prevComment && IsCommentLine(lineNext, styler)); + int comment_continue = (comment && prevComment); + + if (quote_start) { + // Place fold point at start of triple quoted string + lev |= SC_FOLDLEVELHEADERFLAG; + } else if (quote_continue || prevQuote) { + // Add level to rest of lines in the string + lev = lev + 1; + } else if (comment_start) { + // Place fold point at start of a block of comments + lev |= SC_FOLDLEVELHEADERFLAG; + } else if (comment_continue) { + // Add level to rest of lines in the block + lev = lev + 1; + } + + // Skip past any blank lines for next indent level info; we skip also comments + // starting in column 0 which effectively folds them into surrounding code + // rather than screwing up folding. Then set indent level on the lines + // we skipped to be same as maximum of current and next indent. This approach + // does a reasonable job of collapsing white space into surrounding code + // without getting confused by white space at the start of an indented level. + while (!quote && + ((indentNext & SC_FOLDLEVELWHITEFLAG) || styler[styler.LineStart(lineNext)] == '#') && + (lineNext < maxLines)) { + styler.SetLevel(lineNext, Platform::Maximum(indentCurrent, indentNext)); + lineNext++; + indentNext = styler.IndentAmount(lineNext, &spaceFlags, NULL); + } + + // Set fold header on non-quote/non-comment line + if (!quote && !comment && !(indentCurrent & SC_FOLDLEVELWHITEFLAG) ) { + if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext & SC_FOLDLEVELNUMBERMASK)) lev |= SC_FOLDLEVELHEADERFLAG; - } else if (indentNext & SC_FOLDLEVELWHITEFLAG) { - // Line after is blank so check the next - maybe should continue further? - int spaceFlags2 = 0; - int indentNext2 = styler.IndentAmount(lineCurrent + 2, &spaceFlags2, IsPyComment); - if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext2 & SC_FOLDLEVELNUMBERMASK)) { - lev |= SC_FOLDLEVELHEADERFLAG; - } - } } - indentCurrent = indentNext; + + // Keep track of triple quote and block comment state of previous line + prevQuote = quote; + prevComment = comment_start || comment_continue; + + // Set fold level for this line and move to next line styler.SetLevel(lineCurrent, lev); - lineCurrent++; + indentCurrent = indentNext; + lineCurrent = lineNext; + i = styler.LineStart(lineNext); } } + + // Make sure last line indent level is set too + styler.SetLevel(lineCurrent, indentCurrent); } LexerModule lmPython(SCLEX_PYTHON, ColourisePyDoc, "python", FoldPyDoc); |