123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747 |
- // Scintilla source code edit control
- /** @file LexPython.cxx
- ** Lexer for Python.
- **/
- // Copyright 1998-2002 by Neil Hodgson <neilh@scintilla.org>
- // The License.txt file describes the conditions under which this software may be distributed.
- #include <stdlib.h>
- #include <string.h>
- #include <stdio.h>
- #include <stdarg.h>
- #include <assert.h>
- #include <ctype.h>
- #include <string>
- #include <vector>
- #include <map>
- #include "ILexer.h"
- #include "Scintilla.h"
- #include "SciLexer.h"
- #include "WordList.h"
- #include "LexAccessor.h"
- #include "Accessor.h"
- #include "StyleContext.h"
- #include "CharacterSet.h"
- #include "LexerModule.h"
- #include "OptionSet.h"
- #include "SubStyles.h"
- #ifdef SCI_NAMESPACE
- using namespace Scintilla;
- #endif
- namespace {
- // Use an unnamed namespace to protect the functions and classes from name conflicts
- /* kwCDef, kwCTypeName only used for Cython */
- enum kwType { kwOther, kwClass, kwDef, kwImport, kwCDef, kwCTypeName, kwCPDef };
- enum literalsAllowed { litNone = 0, litU = 1, litB = 2 };
- const int indicatorWhitespace = 1;
- bool IsPyComment(Accessor &styler, Sci_Position pos, Sci_Position len) {
- return len > 0 && styler[pos] == '#';
- }
- bool IsPyStringTypeChar(int ch, literalsAllowed allowed) {
- return
- ((allowed & litB) && (ch == 'b' || ch == 'B')) ||
- ((allowed & litU) && (ch == 'u' || ch == 'U'));
- }
- bool IsPyStringStart(int ch, int chNext, int chNext2, literalsAllowed allowed) {
- if (ch == '\'' || ch == '"')
- return true;
- if (IsPyStringTypeChar(ch, allowed)) {
- if (chNext == '"' || chNext == '\'')
- return true;
- if ((chNext == 'r' || chNext == 'R') && (chNext2 == '"' || chNext2 == '\''))
- return true;
- }
- if ((ch == 'r' || ch == 'R') && (chNext == '"' || chNext == '\''))
- return true;
- return false;
- }
- /* Return the state to use for the string starting at i; *nextIndex will be set to the first index following the quote(s) */
- int GetPyStringState(Accessor &styler, Sci_Position i, Sci_PositionU *nextIndex, literalsAllowed allowed) {
- char ch = styler.SafeGetCharAt(i);
- char chNext = styler.SafeGetCharAt(i + 1);
- // Advance beyond r, u, or ur prefix (or r, b, or br in Python 3.0), but bail if there are any unexpected chars
- if (ch == 'r' || ch == 'R') {
- i++;
- ch = styler.SafeGetCharAt(i);
- chNext = styler.SafeGetCharAt(i + 1);
- } else if (IsPyStringTypeChar(ch, allowed)) {
- if (chNext == 'r' || chNext == 'R')
- i += 2;
- else
- i += 1;
- ch = styler.SafeGetCharAt(i);
- chNext = styler.SafeGetCharAt(i + 1);
- }
- if (ch != '"' && ch != '\'') {
- *nextIndex = i + 1;
- return SCE_P_DEFAULT;
- }
- if (ch == chNext && ch == styler.SafeGetCharAt(i + 2)) {
- *nextIndex = i + 3;
- if (ch == '"')
- return SCE_P_TRIPLEDOUBLE;
- else
- return SCE_P_TRIPLE;
- } else {
- *nextIndex = i + 1;
- if (ch == '"')
- return SCE_P_STRING;
- else
- return SCE_P_CHARACTER;
- }
- }
- inline bool IsAWordChar(int ch) {
- return (ch < 0x80) && (isalnum(ch) || ch == '.' || ch == '_');
- }
- inline bool IsAWordStart(int ch) {
- return (ch < 0x80) && (isalnum(ch) || ch == '_');
- }
- static bool IsFirstNonWhitespace(Sci_Position pos, Accessor &styler) {
- Sci_Position line = styler.GetLine(pos);
- Sci_Position start_pos = styler.LineStart(line);
- for (Sci_Position i = start_pos; i < pos; i++) {
- char ch = styler[i];
- if (!(ch == ' ' || ch == '\t'))
- return false;
- }
- return true;
- }
- // Options used for LexerPython
- struct OptionsPython {
- int whingeLevel;
- bool base2or8Literals;
- bool stringsU;
- bool stringsB;
- bool stringsOverNewline;
- bool keywords2NoSubIdentifiers;
- bool fold;
- bool foldQuotes;
- bool foldCompact;
- OptionsPython() {
- whingeLevel = 0;
- base2or8Literals = true;
- stringsU = true;
- stringsB = true;
- stringsOverNewline = false;
- keywords2NoSubIdentifiers = false;
- fold = false;
- foldQuotes = false;
- foldCompact = false;
- }
- literalsAllowed AllowedLiterals() const {
- literalsAllowed allowedLiterals = stringsU ? litU : litNone;
- if (stringsB)
- allowedLiterals = static_cast<literalsAllowed>(allowedLiterals | litB);
- return allowedLiterals;
- }
- };
- static const char *const pythonWordListDesc[] = {
- "Keywords",
- "Highlighted identifiers",
- 0
- };
- struct OptionSetPython : public OptionSet<OptionsPython> {
- OptionSetPython() {
- DefineProperty("tab.timmy.whinge.level", &OptionsPython::whingeLevel,
- "For Python code, checks whether indenting is consistent. "
- "The default, 0 turns off indentation checking, "
- "1 checks whether each line is potentially inconsistent with the previous line, "
- "2 checks whether any space characters occur before a tab character in the indentation, "
- "3 checks whether any spaces are in the indentation, and "
- "4 checks for any tab characters in the indentation. "
- "1 is a good level to use.");
- DefineProperty("lexer.python.literals.binary", &OptionsPython::base2or8Literals,
- "Set to 0 to not recognise Python 3 binary and octal literals: 0b1011 0o712.");
- DefineProperty("lexer.python.strings.u", &OptionsPython::stringsU,
- "Set to 0 to not recognise Python Unicode literals u\"x\" as used before Python 3.");
- DefineProperty("lexer.python.strings.b", &OptionsPython::stringsB,
- "Set to 0 to not recognise Python 3 bytes literals b\"x\".");
- DefineProperty("lexer.python.strings.over.newline", &OptionsPython::stringsOverNewline,
- "Set to 1 to allow strings to span newline characters.");
- DefineProperty("lexer.python.keywords2.no.sub.identifiers", &OptionsPython::keywords2NoSubIdentifiers,
- "When enabled, it will not style keywords2 items that are used as a sub-identifier. "
- "Example: when set, will not highlight \"foo.open\" when \"open\" is a keywords2 item.");
- DefineProperty("fold", &OptionsPython::fold);
- DefineProperty("fold.quotes.python", &OptionsPython::foldQuotes,
- "This option enables folding multi-line quoted strings when using the Python lexer.");
- DefineProperty("fold.compact", &OptionsPython::foldCompact);
- DefineWordListSets(pythonWordListDesc);
- }
- };
- const char styleSubable[] = { SCE_P_IDENTIFIER, 0 };
- }
- class LexerPython : public ILexerWithSubStyles {
- WordList keywords;
- WordList keywords2;
- OptionsPython options;
- OptionSetPython osPython;
- enum { ssIdentifier };
- SubStyles subStyles;
- public:
- explicit LexerPython() :
- subStyles(styleSubable, 0x80, 0x40, 0) {
- }
- virtual ~LexerPython() {
- }
- void SCI_METHOD Release() {
- delete this;
- }
- int SCI_METHOD Version() const {
- return lvSubStyles;
- }
- const char * SCI_METHOD PropertyNames() {
- return osPython.PropertyNames();
- }
- int SCI_METHOD PropertyType(const char *name) {
- return osPython.PropertyType(name);
- }
- const char * SCI_METHOD DescribeProperty(const char *name) {
- return osPython.DescribeProperty(name);
- }
- Sci_Position SCI_METHOD PropertySet(const char *key, const char *val);
- const char * SCI_METHOD DescribeWordListSets() {
- return osPython.DescribeWordListSets();
- }
- Sci_Position SCI_METHOD WordListSet(int n, const char *wl);
- void SCI_METHOD Lex(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess);
- void SCI_METHOD Fold(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess);
- void * SCI_METHOD PrivateCall(int, void *) {
- return 0;
- }
- int SCI_METHOD LineEndTypesSupported() {
- return SC_LINE_END_TYPE_UNICODE;
- }
- int SCI_METHOD AllocateSubStyles(int styleBase, int numberStyles) {
- return subStyles.Allocate(styleBase, numberStyles);
- }
- int SCI_METHOD SubStylesStart(int styleBase) {
- return subStyles.Start(styleBase);
- }
- int SCI_METHOD SubStylesLength(int styleBase) {
- return subStyles.Length(styleBase);
- }
- int SCI_METHOD StyleFromSubStyle(int subStyle) {
- int styleBase = subStyles.BaseStyle(subStyle);
- return styleBase;
- }
- int SCI_METHOD PrimaryStyleFromStyle(int style) {
- return style;
- }
- void SCI_METHOD FreeSubStyles() {
- subStyles.Free();
- }
- void SCI_METHOD SetIdentifiers(int style, const char *identifiers) {
- subStyles.SetIdentifiers(style, identifiers);
- }
- int SCI_METHOD DistanceToSecondaryStyles() {
- return 0;
- }
- const char * SCI_METHOD GetSubStyleBases() {
- return styleSubable;
- }
- static ILexer *LexerFactoryPython() {
- return new LexerPython();
- }
- };
- Sci_Position SCI_METHOD LexerPython::PropertySet(const char *key, const char *val) {
- if (osPython.PropertySet(&options, key, val)) {
- return 0;
- }
- return -1;
- }
- Sci_Position SCI_METHOD LexerPython::WordListSet(int n, const char *wl) {
- WordList *wordListN = 0;
- switch (n) {
- case 0:
- wordListN = &keywords;
- break;
- case 1:
- wordListN = &keywords2;
- break;
- }
- Sci_Position firstModification = -1;
- if (wordListN) {
- WordList wlNew;
- wlNew.Set(wl);
- if (*wordListN != wlNew) {
- wordListN->Set(wl);
- firstModification = 0;
- }
- }
- return firstModification;
- }
- void SCI_METHOD LexerPython::Lex(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess) {
- Accessor styler(pAccess, NULL);
- const Sci_Position endPos = startPos + length;
- // Backtrack to previous line in case need to fix its tab whinging
- Sci_Position lineCurrent = styler.GetLine(startPos);
- if (startPos > 0) {
- if (lineCurrent > 0) {
- lineCurrent--;
- // Look for backslash-continued lines
- while (lineCurrent > 0) {
- Sci_Position eolPos = styler.LineStart(lineCurrent) - 1;
- int eolStyle = styler.StyleAt(eolPos);
- if (eolStyle == SCE_P_STRING
- || eolStyle == SCE_P_CHARACTER
- || eolStyle == SCE_P_STRINGEOL) {
- lineCurrent -= 1;
- } else {
- break;
- }
- }
- startPos = styler.LineStart(lineCurrent);
- }
- initStyle = startPos == 0 ? SCE_P_DEFAULT : styler.StyleAt(startPos - 1);
- }
- const literalsAllowed allowedLiterals = options.AllowedLiterals();
- initStyle = initStyle & 31;
- if (initStyle == SCE_P_STRINGEOL) {
- initStyle = SCE_P_DEFAULT;
- }
- kwType kwLast = kwOther;
- int spaceFlags = 0;
- styler.IndentAmount(lineCurrent, &spaceFlags, IsPyComment);
- bool base_n_number = false;
- const WordClassifier &classifierIdentifiers = subStyles.Classifier(SCE_P_IDENTIFIER);
- StyleContext sc(startPos, endPos - startPos, initStyle, styler);
- bool indentGood = true;
- Sci_Position startIndicator = sc.currentPos;
- bool inContinuedString = false;
- for (; sc.More(); sc.Forward()) {
- if (sc.atLineStart) {
- styler.IndentAmount(lineCurrent, &spaceFlags, IsPyComment);
- indentGood = true;
- if (options.whingeLevel == 1) {
- indentGood = (spaceFlags & wsInconsistent) == 0;
- } else if (options.whingeLevel == 2) {
- indentGood = (spaceFlags & wsSpaceTab) == 0;
- } else if (options.whingeLevel == 3) {
- indentGood = (spaceFlags & wsSpace) == 0;
- } else if (options.whingeLevel == 4) {
- indentGood = (spaceFlags & wsTab) == 0;
- }
- if (!indentGood) {
- styler.IndicatorFill(startIndicator, sc.currentPos, indicatorWhitespace, 0);
- startIndicator = sc.currentPos;
- }
- }
- if (sc.atLineEnd) {
- if ((sc.state == SCE_P_DEFAULT) ||
- (sc.state == SCE_P_TRIPLE) ||
- (sc.state == SCE_P_TRIPLEDOUBLE)) {
- // Perform colourisation of white space and triple quoted strings at end of each line to allow
- // tab marking to work inside white space and triple quoted strings
- sc.SetState(sc.state);
- }
- lineCurrent++;
- if ((sc.state == SCE_P_STRING) || (sc.state == SCE_P_CHARACTER)) {
- if (inContinuedString || options.stringsOverNewline) {
- inContinuedString = false;
- } else {
- sc.ChangeState(SCE_P_STRINGEOL);
- sc.ForwardSetState(SCE_P_DEFAULT);
- }
- }
- if (!sc.More())
- break;
- }
- bool needEOLCheck = false;
- // Check for a state end
- if (sc.state == SCE_P_OPERATOR) {
- kwLast = kwOther;
- sc.SetState(SCE_P_DEFAULT);
- } else if (sc.state == SCE_P_NUMBER) {
- if (!IsAWordChar(sc.ch) &&
- !(!base_n_number && ((sc.ch == '+' || sc.ch == '-') && (sc.chPrev == 'e' || sc.chPrev == 'E')))) {
- sc.SetState(SCE_P_DEFAULT);
- }
- } else if (sc.state == SCE_P_IDENTIFIER) {
- if ((sc.ch == '.') || (!IsAWordChar(sc.ch))) {
- char s[100];
- sc.GetCurrent(s, sizeof(s));
- int style = SCE_P_IDENTIFIER;
- if ((kwLast == kwImport) && (strcmp(s, "as") == 0)) {
- style = SCE_P_WORD;
- } else if (keywords.InList(s)) {
- style = SCE_P_WORD;
- } else if (kwLast == kwClass) {
- style = SCE_P_CLASSNAME;
- } else if (kwLast == kwDef) {
- style = SCE_P_DEFNAME;
- } else if (kwLast == kwCDef || kwLast == kwCPDef) {
- Sci_Position pos = sc.currentPos;
- unsigned char ch = styler.SafeGetCharAt(pos, '\0');
- while (ch != '\0') {
- if (ch == '(') {
- style = SCE_P_DEFNAME;
- break;
- } else if (ch == ':') {
- style = SCE_P_CLASSNAME;
- break;
- } else if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r') {
- pos++;
- ch = styler.SafeGetCharAt(pos, '\0');
- } else {
- break;
- }
- }
- } else if (keywords2.InList(s)) {
- if (options.keywords2NoSubIdentifiers) {
- // We don't want to highlight keywords2
- // that are used as a sub-identifier,
- // i.e. not open in "foo.open".
- Sci_Position pos = styler.GetStartSegment() - 1;
- if (pos < 0 || (styler.SafeGetCharAt(pos, '\0') != '.'))
- style = SCE_P_WORD2;
- } else {
- style = SCE_P_WORD2;
- }
- } else {
- int subStyle = classifierIdentifiers.ValueFor(s);
- if (subStyle >= 0) {
- style = subStyle;
- }
- }
- sc.ChangeState(style);
- sc.SetState(SCE_P_DEFAULT);
- if (style == SCE_P_WORD) {
- if (0 == strcmp(s, "class"))
- kwLast = kwClass;
- else if (0 == strcmp(s, "def"))
- kwLast = kwDef;
- else if (0 == strcmp(s, "import"))
- kwLast = kwImport;
- else if (0 == strcmp(s, "cdef"))
- kwLast = kwCDef;
- else if (0 == strcmp(s, "cpdef"))
- kwLast = kwCPDef;
- else if (0 == strcmp(s, "cimport"))
- kwLast = kwImport;
- else if (kwLast != kwCDef && kwLast != kwCPDef)
- kwLast = kwOther;
- } else if (kwLast != kwCDef && kwLast != kwCPDef) {
- kwLast = kwOther;
- }
- }
- } else if ((sc.state == SCE_P_COMMENTLINE) || (sc.state == SCE_P_COMMENTBLOCK)) {
- if (sc.ch == '\r' || sc.ch == '\n') {
- sc.SetState(SCE_P_DEFAULT);
- }
- } else if (sc.state == SCE_P_DECORATOR) {
- if (!IsAWordChar(sc.ch)) {
- sc.SetState(SCE_P_DEFAULT);
- }
- } else if ((sc.state == SCE_P_STRING) || (sc.state == SCE_P_CHARACTER)) {
- if (sc.ch == '\\') {
- if ((sc.chNext == '\r') && (sc.GetRelative(2) == '\n')) {
- sc.Forward();
- }
- if (sc.chNext == '\n' || sc.chNext == '\r') {
- inContinuedString = true;
- } else {
- // Don't roll over the newline.
- sc.Forward();
- }
- } else if ((sc.state == SCE_P_STRING) && (sc.ch == '\"')) {
- sc.ForwardSetState(SCE_P_DEFAULT);
- needEOLCheck = true;
- } else if ((sc.state == SCE_P_CHARACTER) && (sc.ch == '\'')) {
- sc.ForwardSetState(SCE_P_DEFAULT);
- needEOLCheck = true;
- }
- } else if (sc.state == SCE_P_TRIPLE) {
- if (sc.ch == '\\') {
- sc.Forward();
- } else if (sc.Match("\'\'\'")) {
- sc.Forward();
- sc.Forward();
- sc.ForwardSetState(SCE_P_DEFAULT);
- needEOLCheck = true;
- }
- } else if (sc.state == SCE_P_TRIPLEDOUBLE) {
- if (sc.ch == '\\') {
- sc.Forward();
- } else if (sc.Match("\"\"\"")) {
- sc.Forward();
- sc.Forward();
- sc.ForwardSetState(SCE_P_DEFAULT);
- needEOLCheck = true;
- }
- }
- if (!indentGood && !IsASpaceOrTab(sc.ch)) {
- styler.IndicatorFill(startIndicator, sc.currentPos, indicatorWhitespace, 1);
- startIndicator = sc.currentPos;
- indentGood = true;
- }
- // One cdef or cpdef line, clear kwLast only at end of line
- if ((kwLast == kwCDef || kwLast == kwCPDef) && sc.atLineEnd) {
- kwLast = kwOther;
- }
- // State exit code may have moved on to end of line
- if (needEOLCheck && sc.atLineEnd) {
- lineCurrent++;
- styler.IndentAmount(lineCurrent, &spaceFlags, IsPyComment);
- if (!sc.More())
- break;
- }
- // Check for a new state starting character
- if (sc.state == SCE_P_DEFAULT) {
- if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
- if (sc.ch == '0' && (sc.chNext == 'x' || sc.chNext == 'X')) {
- base_n_number = true;
- sc.SetState(SCE_P_NUMBER);
- } else if (sc.ch == '0' &&
- (sc.chNext == 'o' || sc.chNext == 'O' || sc.chNext == 'b' || sc.chNext == 'B')) {
- if (options.base2or8Literals) {
- base_n_number = true;
- sc.SetState(SCE_P_NUMBER);
- } else {
- sc.SetState(SCE_P_NUMBER);
- sc.ForwardSetState(SCE_P_IDENTIFIER);
- }
- } else {
- base_n_number = false;
- sc.SetState(SCE_P_NUMBER);
- }
- } else if ((IsASCII(sc.ch) && isoperator(static_cast<char>(sc.ch))) || sc.ch == '`') {
- sc.SetState(SCE_P_OPERATOR);
- } else if (sc.ch == '#') {
- sc.SetState(sc.chNext == '#' ? SCE_P_COMMENTBLOCK : SCE_P_COMMENTLINE);
- } else if (sc.ch == '@') {
- if (IsFirstNonWhitespace(sc.currentPos, styler))
- sc.SetState(SCE_P_DECORATOR);
- else
- sc.SetState(SCE_P_OPERATOR);
- } else if (IsPyStringStart(sc.ch, sc.chNext, sc.GetRelative(2), allowedLiterals)) {
- Sci_PositionU nextIndex = 0;
- sc.SetState(GetPyStringState(styler, sc.currentPos, &nextIndex, allowedLiterals));
- while (nextIndex > (sc.currentPos + 1) && sc.More()) {
- sc.Forward();
- }
- } else if (IsAWordStart(sc.ch)) {
- sc.SetState(SCE_P_IDENTIFIER);
- }
- }
- }
- styler.IndicatorFill(startIndicator, sc.currentPos, indicatorWhitespace, 0);
- sc.Complete();
- }
- static bool IsCommentLine(Sci_Position line, Accessor &styler) {
- Sci_Position pos = styler.LineStart(line);
- Sci_Position eol_pos = styler.LineStart(line + 1) - 1;
- for (Sci_Position i = pos; i < eol_pos; i++) {
- char ch = styler[i];
- if (ch == '#')
- return true;
- else if (ch != ' ' && ch != '\t')
- return false;
- }
- return false;
- }
- static bool IsQuoteLine(Sci_Position line, Accessor &styler) {
- int style = styler.StyleAt(styler.LineStart(line)) & 31;
- return ((style == SCE_P_TRIPLE) || (style == SCE_P_TRIPLEDOUBLE));
- }
- void SCI_METHOD LexerPython::Fold(Sci_PositionU startPos, Sci_Position length, int /*initStyle - unused*/, IDocument *pAccess) {
- if (!options.fold)
- return;
- Accessor styler(pAccess, NULL);
- const Sci_Position maxPos = startPos + length;
- const Sci_Position maxLines = (maxPos == styler.Length()) ? styler.GetLine(maxPos) : styler.GetLine(maxPos - 1); // Requested last line
- const Sci_Position docLines = styler.GetLine(styler.Length()); // Available last line
- // 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;
- Sci_Position lineCurrent = styler.GetLine(startPos);
- int indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags, NULL);
- while (lineCurrent > 0) {
- lineCurrent--;
- indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags, NULL);
- if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG) &&
- (!IsCommentLine(lineCurrent, styler)) &&
- (!IsQuoteLine(lineCurrent, styler)))
- break;
- }
- int indentCurrentLevel = indentCurrent & SC_FOLDLEVELNUMBERMASK;
- // Set up initial loop state
- startPos = styler.LineStart(lineCurrent);
- int prev_state = SCE_P_DEFAULT & 31;
- if (lineCurrent >= 1)
- prev_state = styler.StyleAt(startPos - 1) & 31;
- int prevQuote = options.foldQuotes && ((prev_state == SCE_P_TRIPLE) || (prev_state == SCE_P_TRIPLEDOUBLE));
- // Process all characters to end of requested range or end of any triple quote
- //that hangs over the end of the range. Cap processing in all cases
- // to end of document (in case of unclosed quote at end).
- while ((lineCurrent <= docLines) && ((lineCurrent <= maxLines) || prevQuote)) {
- // Gather info
- int lev = indentCurrent;
- Sci_Position lineNext = lineCurrent + 1;
- int indentNext = indentCurrent;
- int quote = false;
- if (lineNext <= docLines) {
- // Information about next line is only available if not at end of document
- indentNext = styler.IndentAmount(lineNext, &spaceFlags, NULL);
- Sci_Position lookAtPos = (styler.LineStart(lineNext) == styler.Length()) ? styler.Length() - 1 : styler.LineStart(lineNext);
- int style = styler.StyleAt(lookAtPos) & 31;
- quote = options.foldQuotes && ((style == SCE_P_TRIPLE) || (style == SCE_P_TRIPLEDOUBLE));
- }
- const int quote_start = (quote && !prevQuote);
- const int quote_continue = (quote && prevQuote);
- if (!quote || !prevQuote)
- indentCurrentLevel = indentCurrent & SC_FOLDLEVELNUMBERMASK;
- if (quote)
- indentNext = indentCurrentLevel;
- if (indentNext & SC_FOLDLEVELWHITEFLAG)
- indentNext = SC_FOLDLEVELWHITEFLAG | indentCurrentLevel;
- 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;
- }
- // Skip past any blank lines for next indent level info; we skip also
- // comments (all comments, not just those starting in column 0)
- // which effectively folds them into surrounding code rather
- // than screwing up folding.
- while (!quote &&
- (lineNext < docLines) &&
- ((indentNext & SC_FOLDLEVELWHITEFLAG) ||
- (lineNext <= docLines && IsCommentLine(lineNext, styler)))) {
- lineNext++;
- indentNext = styler.IndentAmount(lineNext, &spaceFlags, NULL);
- }
- const int levelAfterComments = indentNext & SC_FOLDLEVELNUMBERMASK;
- const int levelBeforeComments = Maximum(indentCurrentLevel,levelAfterComments);
- // Now set all the indent levels on the lines we skipped
- // Do this from end to start. Once we encounter one line
- // which is indented more than the line after the end of
- // the comment-block, use the level of the block before
- Sci_Position skipLine = lineNext;
- int skipLevel = levelAfterComments;
- while (--skipLine > lineCurrent) {
- int skipLineIndent = styler.IndentAmount(skipLine, &spaceFlags, NULL);
- if (options.foldCompact) {
- if ((skipLineIndent & SC_FOLDLEVELNUMBERMASK) > levelAfterComments)
- skipLevel = levelBeforeComments;
- int whiteFlag = skipLineIndent & SC_FOLDLEVELWHITEFLAG;
- styler.SetLevel(skipLine, skipLevel | whiteFlag);
- } else {
- if ((skipLineIndent & SC_FOLDLEVELNUMBERMASK) > levelAfterComments &&
- !(skipLineIndent & SC_FOLDLEVELWHITEFLAG) &&
- !IsCommentLine(skipLine, styler))
- skipLevel = levelBeforeComments;
- styler.SetLevel(skipLine, skipLevel);
- }
- }
- // Set fold header on non-quote line
- if (!quote && !(indentCurrent & SC_FOLDLEVELWHITEFLAG)) {
- if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext & SC_FOLDLEVELNUMBERMASK))
- lev |= SC_FOLDLEVELHEADERFLAG;
- }
- // Keep track of triple quote state of previous line
- prevQuote = quote;
- // Set fold level for this line and move to next line
- styler.SetLevel(lineCurrent, options.foldCompact ? lev : lev & ~SC_FOLDLEVELWHITEFLAG);
- indentCurrent = indentNext;
- lineCurrent = lineNext;
- }
- // NOTE: Cannot set level of last line here because indentCurrent doesn't have
- // header flag set; the loop above is crafted to take care of this case!
- //styler.SetLevel(lineCurrent, indentCurrent);
- }
- LexerModule lmPython(SCLEX_PYTHON, LexerPython::LexerFactoryPython, "python",
- pythonWordListDesc);
|