1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810 |
- // Scintilla source code edit control
- /** @file LexPerl.cxx
- ** Lexer for Perl.
- ** Converted to lexer object by "Udo Lechner" <dlchnr(at)gmx(dot)net>
- **/
- // Copyright 1998-2008 by Neil Hodgson <neilh@scintilla.org>
- // Lexical analysis fixes by Kein-Hong Man <mkh@pl.jaring.my>
- // 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 <map>
- #include "ILexer.h"
- #include "Scintilla.h"
- #include "SciLexer.h"
- #include "WordList.h"
- #include "LexAccessor.h"
- #include "StyleContext.h"
- #include "CharacterSet.h"
- #include "LexerModule.h"
- #include "OptionSet.h"
- #ifdef SCI_NAMESPACE
- using namespace Scintilla;
- #endif
- // Info for HERE document handling from perldata.pod (reformatted):
- // ----------------------------------------------------------------
- // A line-oriented form of quoting is based on the shell ``here-doc'' syntax.
- // Following a << you specify a string to terminate the quoted material, and
- // all lines following the current line down to the terminating string are
- // the value of the item.
- // * The terminating string may be either an identifier (a word), or some
- // quoted text.
- // * If quoted, the type of quotes you use determines the treatment of the
- // text, just as in regular quoting.
- // * An unquoted identifier works like double quotes.
- // * There must be no space between the << and the identifier.
- // (If you put a space it will be treated as a null identifier,
- // which is valid, and matches the first empty line.)
- // (This is deprecated, -w warns of this syntax)
- // * The terminating string must appear by itself (unquoted and
- // with no surrounding whitespace) on the terminating line.
- #define HERE_DELIM_MAX 256 // maximum length of HERE doc delimiter
- #define PERLNUM_BINARY 1 // order is significant: 1-3 cannot have a dot
- #define PERLNUM_OCTAL 2
- #define PERLNUM_FLOAT_EXP 3 // exponent part only
- #define PERLNUM_HEX 4 // may be a hex float
- #define PERLNUM_DECIMAL 5 // 1-5 are numbers; 6-7 are strings
- #define PERLNUM_VECTOR 6
- #define PERLNUM_V_VECTOR 7
- #define PERLNUM_BAD 8
- #define BACK_NONE 0 // lookback state for bareword disambiguation:
- #define BACK_OPERATOR 1 // whitespace/comments are insignificant
- #define BACK_KEYWORD 2 // operators/keywords are needed for disambiguation
- #define SUB_BEGIN 0 // states for subroutine prototype scan:
- #define SUB_HAS_PROTO 1 // only 'prototype' attribute allows prototypes
- #define SUB_HAS_ATTRIB 2 // other attributes can exist leftward
- #define SUB_HAS_MODULE 3 // sub name can have a ::identifier part
- #define SUB_HAS_SUB 4 // 'sub' keyword
- // all interpolated styles are different from their parent styles by a constant difference
- // we also assume SCE_PL_STRING_VAR is the interpolated style with the smallest value
- #define INTERPOLATE_SHIFT (SCE_PL_STRING_VAR - SCE_PL_STRING)
- static bool isPerlKeyword(Sci_PositionU start, Sci_PositionU end, WordList &keywords, LexAccessor &styler) {
- // old-style keyword matcher; needed because GetCurrent() needs
- // current segment to be committed, but we may abandon early...
- char s[100];
- Sci_PositionU i, len = end - start;
- if (len > 30) { len = 30; }
- for (i = 0; i < len; i++, start++) s[i] = styler[start];
- s[i] = '\0';
- return keywords.InList(s);
- }
- static int disambiguateBareword(LexAccessor &styler, Sci_PositionU bk, Sci_PositionU fw,
- int backFlag, Sci_PositionU backPos, Sci_PositionU endPos) {
- // identifiers are recognized by Perl as barewords under some
- // conditions, the following attempts to do the disambiguation
- // by looking backward and forward; result in 2 LSB
- int result = 0;
- bool moreback = false; // true if passed newline/comments
- bool brace = false; // true if opening brace found
- // if BACK_NONE, neither operator nor keyword, so skip test
- if (backFlag == BACK_NONE)
- return result;
- // first look backwards past whitespace/comments to set EOL flag
- // (some disambiguation patterns must be on a single line)
- if (backPos <= static_cast<Sci_PositionU>(styler.LineStart(styler.GetLine(bk))))
- moreback = true;
- // look backwards at last significant lexed item for disambiguation
- bk = backPos - 1;
- int ch = static_cast<unsigned char>(styler.SafeGetCharAt(bk));
- if (ch == '{' && !moreback) {
- // {bareword: possible variable spec
- brace = true;
- } else if ((ch == '&' && styler.SafeGetCharAt(bk - 1) != '&')
- // &bareword: subroutine call
- || styler.Match(bk - 1, "->")
- // ->bareword: part of variable spec
- || styler.Match(bk - 1, "::")
- // ::bareword: part of module spec
- || styler.Match(bk - 2, "sub")) {
- // sub bareword: subroutine declaration
- // (implied BACK_KEYWORD, no keywords end in 'sub'!)
- result |= 1;
- }
- // next, scan forward after word past tab/spaces only;
- // if ch isn't one of '[{(,' we can skip the test
- if ((ch == '{' || ch == '(' || ch == '['|| ch == ',')
- && fw < endPos) {
- while (ch = static_cast<unsigned char>(styler.SafeGetCharAt(fw)),
- IsASpaceOrTab(ch) && fw < endPos) {
- fw++;
- }
- if ((ch == '}' && brace)
- // {bareword}: variable spec
- || styler.Match(fw, "=>")) {
- // [{(, bareword=>: hash literal
- result |= 2;
- }
- }
- return result;
- }
- static void skipWhitespaceComment(LexAccessor &styler, Sci_PositionU &p) {
- // when backtracking, we need to skip whitespace and comments
- int style;
- while ((p > 0) && (style = styler.StyleAt(p),
- style == SCE_PL_DEFAULT || style == SCE_PL_COMMENTLINE))
- p--;
- }
- static int findPrevLexeme(LexAccessor &styler, Sci_PositionU &bk, int &style) {
- // scan backward past whitespace and comments to find a lexeme
- skipWhitespaceComment(styler, bk);
- if (bk == 0)
- return 0;
- int sz = 1;
- style = styler.StyleAt(bk);
- while (bk > 0) { // find extent of lexeme
- if (styler.StyleAt(bk - 1) == style) {
- bk--; sz++;
- } else
- break;
- }
- return sz;
- }
- static int styleBeforeBracePair(LexAccessor &styler, Sci_PositionU bk) {
- // backtrack to find open '{' corresponding to a '}', balanced
- // return significant style to be tested for '/' disambiguation
- int braceCount = 1;
- if (bk == 0)
- return SCE_PL_DEFAULT;
- while (--bk > 0) {
- if (styler.StyleAt(bk) == SCE_PL_OPERATOR) {
- int bkch = static_cast<unsigned char>(styler.SafeGetCharAt(bk));
- if (bkch == ';') { // early out
- break;
- } else if (bkch == '}') {
- braceCount++;
- } else if (bkch == '{') {
- if (--braceCount == 0) break;
- }
- }
- }
- if (bk > 0 && braceCount == 0) {
- // balanced { found, bk > 0, skip more whitespace/comments
- bk--;
- skipWhitespaceComment(styler, bk);
- return styler.StyleAt(bk);
- }
- return SCE_PL_DEFAULT;
- }
- static int styleCheckIdentifier(LexAccessor &styler, Sci_PositionU bk) {
- // backtrack to classify sub-styles of identifier under test
- // return sub-style to be tested for '/' disambiguation
- if (styler.SafeGetCharAt(bk) == '>') // inputsymbol, like <foo>
- return 1;
- // backtrack to check for possible "->" or "::" before identifier
- while (bk > 0 && styler.StyleAt(bk) == SCE_PL_IDENTIFIER) {
- bk--;
- }
- while (bk > 0) {
- int bkstyle = styler.StyleAt(bk);
- if (bkstyle == SCE_PL_DEFAULT
- || bkstyle == SCE_PL_COMMENTLINE) {
- // skip whitespace, comments
- } else if (bkstyle == SCE_PL_OPERATOR) {
- // test for "->" and "::"
- if (styler.Match(bk - 1, "->") || styler.Match(bk - 1, "::"))
- return 2;
- } else
- return 3; // bare identifier
- bk--;
- }
- return 0;
- }
- static int podLineScan(LexAccessor &styler, Sci_PositionU &pos, Sci_PositionU endPos) {
- // forward scan the current line to classify line for POD style
- int state = -1;
- while (pos < endPos) {
- int ch = static_cast<unsigned char>(styler.SafeGetCharAt(pos));
- if (ch == '\n' || ch == '\r') {
- if (ch == '\r' && styler.SafeGetCharAt(pos + 1) == '\n') pos++;
- break;
- }
- if (IsASpaceOrTab(ch)) { // whitespace, take note
- if (state == -1)
- state = SCE_PL_DEFAULT;
- } else if (state == SCE_PL_DEFAULT) { // verbatim POD line
- state = SCE_PL_POD_VERB;
- } else if (state != SCE_PL_POD_VERB) { // regular POD line
- state = SCE_PL_POD;
- }
- pos++;
- }
- if (state == -1)
- state = SCE_PL_DEFAULT;
- return state;
- }
- static bool styleCheckSubPrototype(LexAccessor &styler, Sci_PositionU bk) {
- // backtrack to identify if we're starting a subroutine prototype
- // we also need to ignore whitespace/comments, format is like:
- // sub abc::pqr :const :prototype(...)
- // lexemes are tested in pairs, e.g. '::'+'pqr', ':'+'const', etc.
- // and a state machine generates legal subroutine syntax matches
- styler.Flush();
- int state = SUB_BEGIN;
- do {
- // find two lexemes, lexeme 2 follows lexeme 1
- int style2 = SCE_PL_DEFAULT;
- Sci_PositionU pos2 = bk;
- int len2 = findPrevLexeme(styler, pos2, style2);
- int style1 = SCE_PL_DEFAULT;
- Sci_PositionU pos1 = pos2;
- if (pos1 > 0) pos1--;
- int len1 = findPrevLexeme(styler, pos1, style1);
- if (len1 == 0 || len2 == 0) // lexeme pair must exist
- break;
- // match parts of syntax, if invalid subroutine syntax, break off
- if (style1 == SCE_PL_OPERATOR && len1 == 1 &&
- styler.SafeGetCharAt(pos1) == ':') { // ':'
- if (style2 == SCE_PL_IDENTIFIER || style2 == SCE_PL_WORD) {
- if (len2 == 9 && styler.Match(pos2, "prototype")) { // ':' 'prototype'
- if (state == SUB_BEGIN) {
- state = SUB_HAS_PROTO;
- } else
- break;
- } else { // ':' <attribute>
- if (state == SUB_HAS_PROTO || state == SUB_HAS_ATTRIB) {
- state = SUB_HAS_ATTRIB;
- } else
- break;
- }
- } else
- break;
- } else if (style1 == SCE_PL_OPERATOR && len1 == 2 &&
- styler.Match(pos1, "::")) { // '::'
- if (style2 == SCE_PL_IDENTIFIER) { // '::' <identifier>
- state = SUB_HAS_MODULE;
- } else
- break;
- } else if (style1 == SCE_PL_WORD && len1 == 3 &&
- styler.Match(pos1, "sub")) { // 'sub'
- if (style2 == SCE_PL_IDENTIFIER) { // 'sub' <identifier>
- state = SUB_HAS_SUB;
- } else
- break;
- } else
- break;
- bk = pos1; // set position for finding next lexeme pair
- if (bk > 0) bk--;
- } while (state != SUB_HAS_SUB);
- return (state == SUB_HAS_SUB);
- }
- static int actualNumStyle(int numberStyle) {
- if (numberStyle == PERLNUM_VECTOR || numberStyle == PERLNUM_V_VECTOR) {
- return SCE_PL_STRING;
- } else if (numberStyle == PERLNUM_BAD) {
- return SCE_PL_ERROR;
- }
- return SCE_PL_NUMBER;
- }
- static int opposite(int ch) {
- if (ch == '(') return ')';
- if (ch == '[') return ']';
- if (ch == '{') return '}';
- if (ch == '<') return '>';
- return ch;
- }
- static bool IsCommentLine(Sci_Position line, LexAccessor &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];
- int style = styler.StyleAt(i);
- if (ch == '#' && style == SCE_PL_COMMENTLINE)
- return true;
- else if (!IsASpaceOrTab(ch))
- return false;
- }
- return false;
- }
- static bool IsPackageLine(Sci_Position line, LexAccessor &styler) {
- Sci_Position pos = styler.LineStart(line);
- int style = styler.StyleAt(pos);
- if (style == SCE_PL_WORD && styler.Match(pos, "package")) {
- return true;
- }
- return false;
- }
- static int PodHeadingLevel(Sci_Position pos, LexAccessor &styler) {
- int lvl = static_cast<unsigned char>(styler.SafeGetCharAt(pos + 5));
- if (lvl >= '1' && lvl <= '4') {
- return lvl - '0';
- }
- return 0;
- }
- // An individual named option for use in an OptionSet
- // Options used for LexerPerl
- struct OptionsPerl {
- bool fold;
- bool foldComment;
- bool foldCompact;
- // Custom folding of POD and packages
- bool foldPOD; // fold.perl.pod
- // Enable folding Pod blocks when using the Perl lexer.
- bool foldPackage; // fold.perl.package
- // Enable folding packages when using the Perl lexer.
- bool foldCommentExplicit;
- bool foldAtElse;
- OptionsPerl() {
- fold = false;
- foldComment = false;
- foldCompact = true;
- foldPOD = true;
- foldPackage = true;
- foldCommentExplicit = true;
- foldAtElse = false;
- }
- };
- static const char *const perlWordListDesc[] = {
- "Keywords",
- 0
- };
- struct OptionSetPerl : public OptionSet<OptionsPerl> {
- OptionSetPerl() {
- DefineProperty("fold", &OptionsPerl::fold);
- DefineProperty("fold.comment", &OptionsPerl::foldComment);
- DefineProperty("fold.compact", &OptionsPerl::foldCompact);
- DefineProperty("fold.perl.pod", &OptionsPerl::foldPOD,
- "Set to 0 to disable folding Pod blocks when using the Perl lexer.");
- DefineProperty("fold.perl.package", &OptionsPerl::foldPackage,
- "Set to 0 to disable folding packages when using the Perl lexer.");
- DefineProperty("fold.perl.comment.explicit", &OptionsPerl::foldCommentExplicit,
- "Set to 0 to disable explicit folding.");
- DefineProperty("fold.perl.at.else", &OptionsPerl::foldAtElse,
- "This option enables Perl folding on a \"} else {\" line of an if statement.");
- DefineWordListSets(perlWordListDesc);
- }
- };
- class LexerPerl : public ILexer {
- CharacterSet setWordStart;
- CharacterSet setWord;
- CharacterSet setSpecialVar;
- CharacterSet setControlVar;
- WordList keywords;
- OptionsPerl options;
- OptionSetPerl osPerl;
- public:
- LexerPerl() :
- setWordStart(CharacterSet::setAlpha, "_", 0x80, true),
- setWord(CharacterSet::setAlphaNum, "_", 0x80, true),
- setSpecialVar(CharacterSet::setNone, "\"$;<>&`'+,./\\%:=~!?@[]"),
- setControlVar(CharacterSet::setNone, "ACDEFHILMNOPRSTVWX") {
- }
- virtual ~LexerPerl() {
- }
- void SCI_METHOD Release() {
- delete this;
- }
- int SCI_METHOD Version() const {
- return lvOriginal;
- }
- const char *SCI_METHOD PropertyNames() {
- return osPerl.PropertyNames();
- }
- int SCI_METHOD PropertyType(const char *name) {
- return osPerl.PropertyType(name);
- }
- const char *SCI_METHOD DescribeProperty(const char *name) {
- return osPerl.DescribeProperty(name);
- }
- Sci_Position SCI_METHOD PropertySet(const char *key, const char *val);
- const char *SCI_METHOD DescribeWordListSets() {
- return osPerl.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;
- }
- static ILexer *LexerFactoryPerl() {
- return new LexerPerl();
- }
- int InputSymbolScan(StyleContext &sc);
- void InterpolateSegment(StyleContext &sc, int maxSeg, bool isPattern=false);
- };
- Sci_Position SCI_METHOD LexerPerl::PropertySet(const char *key, const char *val) {
- if (osPerl.PropertySet(&options, key, val)) {
- return 0;
- }
- return -1;
- }
- Sci_Position SCI_METHOD LexerPerl::WordListSet(int n, const char *wl) {
- WordList *wordListN = 0;
- switch (n) {
- case 0:
- wordListN = &keywords;
- break;
- }
- Sci_Position firstModification = -1;
- if (wordListN) {
- WordList wlNew;
- wlNew.Set(wl);
- if (*wordListN != wlNew) {
- wordListN->Set(wl);
- firstModification = 0;
- }
- }
- return firstModification;
- }
- int LexerPerl::InputSymbolScan(StyleContext &sc) {
- // forward scan for matching > on same line; file handles
- int c, sLen = 0;
- while ((c = sc.GetRelativeCharacter(++sLen)) != 0) {
- if (c == '\r' || c == '\n') {
- return 0;
- } else if (c == '>') {
- if (sc.Match("<=>")) // '<=>' case
- return 0;
- return sLen;
- }
- }
- return 0;
- }
- void LexerPerl::InterpolateSegment(StyleContext &sc, int maxSeg, bool isPattern) {
- // interpolate a segment (with no active backslashes or delimiters within)
- // switch in or out of an interpolation style or continue current style
- // commit variable patterns if found, trim segment, repeat until done
- while (maxSeg > 0) {
- bool isVar = false;
- int sLen = 0;
- if ((maxSeg > 1) && (sc.ch == '$' || sc.ch == '@')) {
- // $#[$]*word [$@][$]*word (where word or {word} is always present)
- bool braces = false;
- sLen = 1;
- if (sc.ch == '$' && sc.chNext == '#') { // starts with $#
- sLen++;
- }
- while ((maxSeg > sLen) && (sc.GetRelativeCharacter(sLen) == '$')) // >0 $ dereference within
- sLen++;
- if ((maxSeg > sLen) && (sc.GetRelativeCharacter(sLen) == '{')) { // { start for {word}
- sLen++;
- braces = true;
- }
- if (maxSeg > sLen) {
- int c = sc.GetRelativeCharacter(sLen);
- if (setWordStart.Contains(c)) { // word (various)
- sLen++;
- isVar = true;
- while (maxSeg > sLen) {
- if (!setWord.Contains(sc.GetRelativeCharacter(sLen)))
- break;
- sLen++;
- }
- } else if (braces && IsADigit(c) && (sLen == 2)) { // digit for ${digit}
- sLen++;
- isVar = true;
- }
- }
- if (braces) {
- if ((maxSeg > sLen) && (sc.GetRelativeCharacter(sLen) == '}')) { // } end for {word}
- sLen++;
- } else
- isVar = false;
- }
- }
- if (!isVar && (maxSeg > 1)) { // $- or @-specific variable patterns
- int c = sc.chNext;
- if (sc.ch == '$') {
- sLen = 1;
- if (IsADigit(c)) { // $[0-9] and slurp trailing digits
- sLen++;
- isVar = true;
- while ((maxSeg > sLen) && IsADigit(sc.GetRelativeCharacter(sLen)))
- sLen++;
- } else if (setSpecialVar.Contains(c)) { // $ special variables
- sLen++;
- isVar = true;
- } else if (!isPattern && ((c == '(') || (c == ')') || (c == '|'))) { // $ additional
- sLen++;
- isVar = true;
- } else if (c == '^') { // $^A control-char style
- sLen++;
- if ((maxSeg > sLen) && setControlVar.Contains(sc.GetRelativeCharacter(sLen))) {
- sLen++;
- isVar = true;
- }
- }
- } else if (sc.ch == '@') {
- sLen = 1;
- if (!isPattern && ((c == '+') || (c == '-'))) { // @ specials non-pattern
- sLen++;
- isVar = true;
- }
- }
- }
- if (isVar) { // commit as interpolated variable or normal character
- if (sc.state < SCE_PL_STRING_VAR)
- sc.SetState(sc.state + INTERPOLATE_SHIFT);
- sc.Forward(sLen);
- maxSeg -= sLen;
- } else {
- if (sc.state >= SCE_PL_STRING_VAR)
- sc.SetState(sc.state - INTERPOLATE_SHIFT);
- sc.Forward();
- maxSeg--;
- }
- }
- if (sc.state >= SCE_PL_STRING_VAR)
- sc.SetState(sc.state - INTERPOLATE_SHIFT);
- }
- void SCI_METHOD LexerPerl::Lex(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess) {
- LexAccessor styler(pAccess);
- // keywords that forces /PATTERN/ at all times; should track vim's behaviour
- WordList reWords;
- reWords.Set("elsif if split while");
- // charset classes
- CharacterSet setSingleCharOp(CharacterSet::setNone, "rwxoRWXOezsfdlpSbctugkTBMAC");
- // lexing of "%*</" operators is non-trivial; these are missing in the set below
- CharacterSet setPerlOperator(CharacterSet::setNone, "^&\\()-+=|{}[]:;>,?!.~");
- CharacterSet setQDelim(CharacterSet::setNone, "qrwx");
- CharacterSet setModifiers(CharacterSet::setAlpha);
- CharacterSet setPreferRE(CharacterSet::setNone, "*/<%");
- // setArray and setHash also accepts chars for special vars like $_,
- // which are then truncated when the next char does not match setVar
- CharacterSet setVar(CharacterSet::setAlphaNum, "#$_'", 0x80, true);
- CharacterSet setArray(CharacterSet::setAlpha, "#$_+-", 0x80, true);
- CharacterSet setHash(CharacterSet::setAlpha, "#$_!^+-", 0x80, true);
- CharacterSet &setPOD = setModifiers;
- CharacterSet setNonHereDoc(CharacterSet::setDigits, "=$@");
- CharacterSet setHereDocDelim(CharacterSet::setAlphaNum, "_");
- CharacterSet setSubPrototype(CharacterSet::setNone, "\\[$@%&*+];_ \t");
- CharacterSet setRepetition(CharacterSet::setDigits, ")\"'");
- // for format identifiers
- CharacterSet setFormatStart(CharacterSet::setAlpha, "_=");
- CharacterSet &setFormat = setHereDocDelim;
- // Lexer for perl often has to backtrack to start of current style to determine
- // which characters are being used as quotes, how deeply nested is the
- // start position and what the termination string is for HERE documents.
- class HereDocCls { // Class to manage HERE doc sequence
- public:
- int State;
- // 0: '<<' encountered
- // 1: collect the delimiter
- // 2: here doc text (lines after the delimiter)
- int Quote; // the char after '<<'
- bool Quoted; // true if Quote in ('\'','"','`')
- int DelimiterLength; // strlen(Delimiter)
- char Delimiter[HERE_DELIM_MAX]; // the Delimiter
- HereDocCls() {
- State = 0;
- Quote = 0;
- Quoted = false;
- DelimiterLength = 0;
- Delimiter[0] = '\0';
- }
- void Append(int ch) {
- Delimiter[DelimiterLength++] = static_cast<char>(ch);
- Delimiter[DelimiterLength] = '\0';
- }
- ~HereDocCls() {
- }
- };
- HereDocCls HereDoc; // TODO: FIFO for stacked here-docs
- class QuoteCls { // Class to manage quote pairs
- public:
- int Rep;
- int Count;
- int Up, Down;
- QuoteCls() {
- New(1);
- }
- void New(int r = 1) {
- Rep = r;
- Count = 0;
- Up = '\0';
- Down = '\0';
- }
- void Open(int u) {
- Count++;
- Up = u;
- Down = opposite(Up);
- }
- };
- QuoteCls Quote;
- // additional state for number lexing
- int numState = PERLNUM_DECIMAL;
- int dotCount = 0;
- Sci_PositionU endPos = startPos + length;
- // Backtrack to beginning of style if required...
- // If in a long distance lexical state, backtrack to find quote characters.
- // Includes strings (may be multi-line), numbers (additional state), format
- // bodies, as well as POD sections.
- if (initStyle == SCE_PL_HERE_Q
- || initStyle == SCE_PL_HERE_QQ
- || initStyle == SCE_PL_HERE_QX
- || initStyle == SCE_PL_FORMAT
- || initStyle == SCE_PL_HERE_QQ_VAR
- || initStyle == SCE_PL_HERE_QX_VAR
- ) {
- // backtrack through multiple styles to reach the delimiter start
- int delim = (initStyle == SCE_PL_FORMAT) ? SCE_PL_FORMAT_IDENT:SCE_PL_HERE_DELIM;
- while ((startPos > 1) && (styler.StyleAt(startPos) != delim)) {
- startPos--;
- }
- startPos = styler.LineStart(styler.GetLine(startPos));
- initStyle = styler.StyleAt(startPos - 1);
- }
- if (initStyle == SCE_PL_STRING
- || initStyle == SCE_PL_STRING_QQ
- || initStyle == SCE_PL_BACKTICKS
- || initStyle == SCE_PL_STRING_QX
- || initStyle == SCE_PL_REGEX
- || initStyle == SCE_PL_STRING_QR
- || initStyle == SCE_PL_REGSUBST
- || initStyle == SCE_PL_STRING_VAR
- || initStyle == SCE_PL_STRING_QQ_VAR
- || initStyle == SCE_PL_BACKTICKS_VAR
- || initStyle == SCE_PL_STRING_QX_VAR
- || initStyle == SCE_PL_REGEX_VAR
- || initStyle == SCE_PL_STRING_QR_VAR
- || initStyle == SCE_PL_REGSUBST_VAR
- ) {
- // for interpolation, must backtrack through a mix of two different styles
- int otherStyle = (initStyle >= SCE_PL_STRING_VAR) ?
- initStyle - INTERPOLATE_SHIFT : initStyle + INTERPOLATE_SHIFT;
- while (startPos > 1) {
- int st = styler.StyleAt(startPos - 1);
- if ((st != initStyle) && (st != otherStyle))
- break;
- startPos--;
- }
- initStyle = SCE_PL_DEFAULT;
- } else if (initStyle == SCE_PL_STRING_Q
- || initStyle == SCE_PL_STRING_QW
- || initStyle == SCE_PL_XLAT
- || initStyle == SCE_PL_CHARACTER
- || initStyle == SCE_PL_NUMBER
- || initStyle == SCE_PL_IDENTIFIER
- || initStyle == SCE_PL_ERROR
- || initStyle == SCE_PL_SUB_PROTOTYPE
- ) {
- while ((startPos > 1) && (styler.StyleAt(startPos - 1) == initStyle)) {
- startPos--;
- }
- initStyle = SCE_PL_DEFAULT;
- } else if (initStyle == SCE_PL_POD
- || initStyle == SCE_PL_POD_VERB
- ) {
- // POD backtracking finds preceding blank lines and goes back past them
- Sci_Position ln = styler.GetLine(startPos);
- if (ln > 0) {
- initStyle = styler.StyleAt(styler.LineStart(--ln));
- if (initStyle == SCE_PL_POD || initStyle == SCE_PL_POD_VERB) {
- while (ln > 0 && styler.GetLineState(ln) == SCE_PL_DEFAULT)
- ln--;
- }
- startPos = styler.LineStart(++ln);
- initStyle = styler.StyleAt(startPos - 1);
- } else {
- startPos = 0;
- initStyle = SCE_PL_DEFAULT;
- }
- }
- // backFlag, backPos are additional state to aid identifier corner cases.
- // Look backwards past whitespace and comments in order to detect either
- // operator or keyword. Later updated as we go along.
- int backFlag = BACK_NONE;
- Sci_PositionU backPos = startPos;
- if (backPos > 0) {
- backPos--;
- skipWhitespaceComment(styler, backPos);
- if (styler.StyleAt(backPos) == SCE_PL_OPERATOR)
- backFlag = BACK_OPERATOR;
- else if (styler.StyleAt(backPos) == SCE_PL_WORD)
- backFlag = BACK_KEYWORD;
- backPos++;
- }
- StyleContext sc(startPos, endPos - startPos, initStyle, styler);
- for (; sc.More(); sc.Forward()) {
- // Determine if the current state should terminate.
- switch (sc.state) {
- case SCE_PL_OPERATOR:
- sc.SetState(SCE_PL_DEFAULT);
- backFlag = BACK_OPERATOR;
- backPos = sc.currentPos;
- break;
- case SCE_PL_IDENTIFIER: // identifier, bareword, inputsymbol
- if ((!setWord.Contains(sc.ch) && sc.ch != '\'')
- || sc.Match('.', '.')
- || sc.chPrev == '>') { // end of inputsymbol
- sc.SetState(SCE_PL_DEFAULT);
- }
- break;
- case SCE_PL_WORD: // keyword, plus special cases
- if (!setWord.Contains(sc.ch)) {
- char s[100];
- sc.GetCurrent(s, sizeof(s));
- if ((strcmp(s, "__DATA__") == 0) || (strcmp(s, "__END__") == 0)) {
- sc.ChangeState(SCE_PL_DATASECTION);
- } else {
- if ((strcmp(s, "format") == 0)) {
- sc.SetState(SCE_PL_FORMAT_IDENT);
- HereDoc.State = 0;
- } else {
- sc.SetState(SCE_PL_DEFAULT);
- }
- backFlag = BACK_KEYWORD;
- backPos = sc.currentPos;
- }
- }
- break;
- case SCE_PL_SCALAR:
- case SCE_PL_ARRAY:
- case SCE_PL_HASH:
- case SCE_PL_SYMBOLTABLE:
- if (sc.Match(':', ':')) { // skip ::
- sc.Forward();
- } else if (!setVar.Contains(sc.ch)) {
- if (sc.LengthCurrent() == 1) {
- // Special variable: $(, $_ etc.
- sc.Forward();
- }
- sc.SetState(SCE_PL_DEFAULT);
- }
- break;
- case SCE_PL_NUMBER:
- // if no early break, number style is terminated at "(go through)"
- if (sc.ch == '.') {
- if (sc.chNext == '.') {
- // double dot is always an operator (go through)
- } else if (numState <= PERLNUM_FLOAT_EXP) {
- // non-decimal number or float exponent, consume next dot
- sc.SetState(SCE_PL_OPERATOR);
- break;
- } else { // decimal or vectors allows dots
- dotCount++;
- if (numState == PERLNUM_DECIMAL) {
- if (dotCount <= 1) // number with one dot in it
- break;
- if (IsADigit(sc.chNext)) { // really a vector
- numState = PERLNUM_VECTOR;
- break;
- }
- // number then dot (go through)
- } else if (numState == PERLNUM_HEX) {
- if (dotCount <= 1 && IsADigit(sc.chNext, 16)) {
- break; // hex with one dot is a hex float
- } else {
- sc.SetState(SCE_PL_OPERATOR);
- break;
- }
- // hex then dot (go through)
- } else if (IsADigit(sc.chNext)) // vectors
- break;
- // vector then dot (go through)
- }
- } else if (sc.ch == '_') {
- // permissive underscoring for number and vector literals
- break;
- } else if (numState == PERLNUM_DECIMAL) {
- if (sc.ch == 'E' || sc.ch == 'e') { // exponent, sign
- numState = PERLNUM_FLOAT_EXP;
- if (sc.chNext == '+' || sc.chNext == '-') {
- sc.Forward();
- }
- break;
- } else if (IsADigit(sc.ch))
- break;
- // number then word (go through)
- } else if (numState == PERLNUM_HEX) {
- if (sc.ch == 'P' || sc.ch == 'p') { // hex float exponent, sign
- numState = PERLNUM_FLOAT_EXP;
- if (sc.chNext == '+' || sc.chNext == '-') {
- sc.Forward();
- }
- break;
- } else if (IsADigit(sc.ch, 16))
- break;
- // hex or hex float then word (go through)
- } else if (numState == PERLNUM_VECTOR || numState == PERLNUM_V_VECTOR) {
- if (IsADigit(sc.ch)) // vector
- break;
- if (setWord.Contains(sc.ch) && dotCount == 0) { // change to word
- sc.ChangeState(SCE_PL_IDENTIFIER);
- break;
- }
- // vector then word (go through)
- } else if (IsADigit(sc.ch)) {
- if (numState == PERLNUM_FLOAT_EXP) {
- break;
- } else if (numState == PERLNUM_OCTAL) {
- if (sc.ch <= '7') break;
- } else if (numState == PERLNUM_BINARY) {
- if (sc.ch <= '1') break;
- }
- // mark invalid octal, binary numbers (go through)
- numState = PERLNUM_BAD;
- break;
- }
- // complete current number or vector
- sc.ChangeState(actualNumStyle(numState));
- sc.SetState(SCE_PL_DEFAULT);
- break;
- case SCE_PL_COMMENTLINE:
- if (sc.atLineEnd) {
- sc.SetState(SCE_PL_DEFAULT);
- }
- break;
- case SCE_PL_HERE_DELIM:
- if (HereDoc.State == 0) { // '<<' encountered
- int delim_ch = sc.chNext;
- Sci_Position ws_skip = 0;
- HereDoc.State = 1; // pre-init HERE doc class
- HereDoc.Quote = sc.chNext;
- HereDoc.Quoted = false;
- HereDoc.DelimiterLength = 0;
- HereDoc.Delimiter[HereDoc.DelimiterLength] = '\0';
- if (IsASpaceOrTab(delim_ch)) {
- // skip whitespace; legal only for quoted delimiters
- Sci_PositionU i = sc.currentPos + 1;
- while ((i < endPos) && IsASpaceOrTab(delim_ch)) {
- i++;
- delim_ch = static_cast<unsigned char>(styler.SafeGetCharAt(i));
- }
- ws_skip = i - sc.currentPos - 1;
- }
- if (delim_ch == '\'' || delim_ch == '"' || delim_ch == '`') {
- // a quoted here-doc delimiter; skip any whitespace
- sc.Forward(ws_skip + 1);
- HereDoc.Quote = delim_ch;
- HereDoc.Quoted = true;
- } else if ((ws_skip == 0 && setNonHereDoc.Contains(sc.chNext))
- || ws_skip > 0) {
- // left shift << or <<= operator cases
- // restore position if operator
- sc.ChangeState(SCE_PL_OPERATOR);
- sc.ForwardSetState(SCE_PL_DEFAULT);
- backFlag = BACK_OPERATOR;
- backPos = sc.currentPos;
- HereDoc.State = 0;
- } else {
- // specially handle initial '\' for identifier
- if (ws_skip == 0 && HereDoc.Quote == '\\')
- sc.Forward();
- // an unquoted here-doc delimiter, no special handling
- // (cannot be prefixed by spaces/tabs), or
- // symbols terminates; deprecated zero-length delimiter
- }
- } else if (HereDoc.State == 1) { // collect the delimiter
- backFlag = BACK_NONE;
- if (HereDoc.Quoted) { // a quoted here-doc delimiter
- if (sc.ch == HereDoc.Quote) { // closing quote => end of delimiter
- sc.ForwardSetState(SCE_PL_DEFAULT);
- } else if (!sc.atLineEnd) {
- if (sc.Match('\\', static_cast<char>(HereDoc.Quote))) { // escaped quote
- sc.Forward();
- }
- if (sc.ch != '\r') { // skip CR if CRLF
- int i = 0; // else append char, possibly an extended char
- while (i < sc.width) {
- HereDoc.Append(static_cast<unsigned char>(styler.SafeGetCharAt(sc.currentPos + i)));
- i++;
- }
- }
- }
- } else { // an unquoted here-doc delimiter, no extended charsets
- if (setHereDocDelim.Contains(sc.ch)) {
- HereDoc.Append(sc.ch);
- } else {
- sc.SetState(SCE_PL_DEFAULT);
- }
- }
- if (HereDoc.DelimiterLength >= HERE_DELIM_MAX - 1) {
- sc.SetState(SCE_PL_ERROR);
- HereDoc.State = 0;
- }
- }
- break;
- case SCE_PL_HERE_Q:
- case SCE_PL_HERE_QQ:
- case SCE_PL_HERE_QX:
- // also implies HereDoc.State == 2
- sc.Complete();
- if (HereDoc.DelimiterLength == 0 || sc.Match(HereDoc.Delimiter)) {
- int c = sc.GetRelative(HereDoc.DelimiterLength);
- if (c == '\r' || c == '\n') { // peek first, do not consume match
- sc.ForwardBytes(HereDoc.DelimiterLength);
- sc.SetState(SCE_PL_DEFAULT);
- backFlag = BACK_NONE;
- HereDoc.State = 0;
- if (!sc.atLineEnd)
- sc.Forward();
- break;
- }
- }
- if (sc.state == SCE_PL_HERE_Q) { // \EOF and 'EOF' non-interpolated
- while (!sc.atLineEnd)
- sc.Forward();
- break;
- }
- while (!sc.atLineEnd) { // "EOF" and `EOF` interpolated
- int c, sLen = 0, endType = 0;
- while ((c = sc.GetRelativeCharacter(sLen)) != 0) {
- // scan to break string into segments
- if (c == '\\') {
- endType = 1; break;
- } else if (c == '\r' || c == '\n') {
- endType = 2; break;
- }
- sLen++;
- }
- if (sLen > 0) // process non-empty segments
- InterpolateSegment(sc, sLen);
- if (endType == 1) {
- sc.Forward();
- // \ at end-of-line does not appear to have any effect, skip
- if (sc.ch != '\r' && sc.ch != '\n')
- sc.Forward();
- } else if (endType == 2) {
- if (!sc.atLineEnd)
- sc.Forward();
- }
- }
- break;
- case SCE_PL_POD:
- case SCE_PL_POD_VERB: {
- Sci_PositionU fw = sc.currentPos;
- Sci_Position ln = styler.GetLine(fw);
- if (sc.atLineStart && sc.Match("=cut")) { // end of POD
- sc.SetState(SCE_PL_POD);
- sc.Forward(4);
- sc.SetState(SCE_PL_DEFAULT);
- styler.SetLineState(ln, SCE_PL_POD);
- break;
- }
- int pod = podLineScan(styler, fw, endPos); // classify POD line
- styler.SetLineState(ln, pod);
- if (pod == SCE_PL_DEFAULT) {
- if (sc.state == SCE_PL_POD_VERB) {
- Sci_PositionU fw2 = fw;
- while (fw2 < (endPos - 1) && pod == SCE_PL_DEFAULT) {
- fw = fw2++; // penultimate line (last blank line)
- pod = podLineScan(styler, fw2, endPos);
- styler.SetLineState(styler.GetLine(fw2), pod);
- }
- if (pod == SCE_PL_POD) { // truncate verbatim POD early
- sc.SetState(SCE_PL_POD);
- } else
- fw = fw2;
- }
- } else {
- if (pod == SCE_PL_POD_VERB // still part of current paragraph
- && (styler.GetLineState(ln - 1) == SCE_PL_POD)) {
- pod = SCE_PL_POD;
- styler.SetLineState(ln, pod);
- } else if (pod == SCE_PL_POD
- && (styler.GetLineState(ln - 1) == SCE_PL_POD_VERB)) {
- pod = SCE_PL_POD_VERB;
- styler.SetLineState(ln, pod);
- }
- sc.SetState(pod);
- }
- sc.ForwardBytes(fw - sc.currentPos); // commit style
- }
- break;
- case SCE_PL_REGEX:
- case SCE_PL_STRING_QR:
- if (Quote.Rep <= 0) {
- if (!setModifiers.Contains(sc.ch))
- sc.SetState(SCE_PL_DEFAULT);
- } else if (!Quote.Up && !IsASpace(sc.ch)) {
- Quote.Open(sc.ch);
- } else {
- int c, sLen = 0, endType = 0;
- while ((c = sc.GetRelativeCharacter(sLen)) != 0) {
- // scan to break string into segments
- if (IsASpace(c)) {
- break;
- } else if (c == '\\' && Quote.Up != '\\') {
- endType = 1; break;
- } else if (c == Quote.Down) {
- Quote.Count--;
- if (Quote.Count == 0) {
- Quote.Rep--;
- break;
- }
- } else if (c == Quote.Up)
- Quote.Count++;
- sLen++;
- }
- if (sLen > 0) { // process non-empty segments
- if (Quote.Up != '\'') {
- InterpolateSegment(sc, sLen, true);
- } else // non-interpolated path
- sc.Forward(sLen);
- }
- if (endType == 1)
- sc.Forward();
- }
- break;
- case SCE_PL_REGSUBST:
- case SCE_PL_XLAT:
- if (Quote.Rep <= 0) {
- if (!setModifiers.Contains(sc.ch))
- sc.SetState(SCE_PL_DEFAULT);
- } else if (!Quote.Up && !IsASpace(sc.ch)) {
- Quote.Open(sc.ch);
- } else {
- int c, sLen = 0, endType = 0;
- bool isPattern = (Quote.Rep == 2);
- while ((c = sc.GetRelativeCharacter(sLen)) != 0) {
- // scan to break string into segments
- if (c == '\\' && Quote.Up != '\\') {
- endType = 2; break;
- } else if (Quote.Count == 0 && Quote.Rep == 1) {
- // We matched something like s(...) or tr{...}, Perl 5.10
- // appears to allow almost any character for use as the
- // next delimiters. Whitespace and comments are accepted in
- // between, but we'll limit to whitespace here.
- // For '#', if no whitespace in between, it's a delimiter.
- if (IsASpace(c)) {
- // Keep going
- } else if (c == '#' && IsASpaceOrTab(sc.GetRelativeCharacter(sLen - 1))) {
- endType = 3;
- } else
- Quote.Open(c);
- break;
- } else if (c == Quote.Down) {
- Quote.Count--;
- if (Quote.Count == 0) {
- Quote.Rep--;
- endType = 1;
- }
- if (Quote.Up == Quote.Down)
- Quote.Count++;
- if (endType == 1)
- break;
- } else if (c == Quote.Up) {
- Quote.Count++;
- } else if (IsASpace(c))
- break;
- sLen++;
- }
- if (sLen > 0) { // process non-empty segments
- if (sc.state == SCE_PL_REGSUBST && Quote.Up != '\'') {
- InterpolateSegment(sc, sLen, isPattern);
- } else // non-interpolated path
- sc.Forward(sLen);
- }
- if (endType == 2) {
- sc.Forward();
- } else if (endType == 3)
- sc.SetState(SCE_PL_DEFAULT);
- }
- break;
- case SCE_PL_STRING_Q:
- case SCE_PL_STRING_QQ:
- case SCE_PL_STRING_QX:
- case SCE_PL_STRING_QW:
- case SCE_PL_STRING:
- case SCE_PL_CHARACTER:
- case SCE_PL_BACKTICKS:
- if (!Quote.Down && !IsASpace(sc.ch)) {
- Quote.Open(sc.ch);
- } else {
- int c, sLen = 0, endType = 0;
- while ((c = sc.GetRelativeCharacter(sLen)) != 0) {
- // scan to break string into segments
- if (IsASpace(c)) {
- break;
- } else if (c == '\\' && Quote.Up != '\\') {
- endType = 2; break;
- } else if (c == Quote.Down) {
- Quote.Count--;
- if (Quote.Count == 0) {
- endType = 3; break;
- }
- } else if (c == Quote.Up)
- Quote.Count++;
- sLen++;
- }
- if (sLen > 0) { // process non-empty segments
- switch (sc.state) {
- case SCE_PL_STRING:
- case SCE_PL_STRING_QQ:
- case SCE_PL_BACKTICKS:
- InterpolateSegment(sc, sLen);
- break;
- case SCE_PL_STRING_QX:
- if (Quote.Up != '\'') {
- InterpolateSegment(sc, sLen);
- break;
- }
- // (continued for ' delim)
- default: // non-interpolated path
- sc.Forward(sLen);
- }
- }
- if (endType == 2) {
- sc.Forward();
- } else if (endType == 3)
- sc.ForwardSetState(SCE_PL_DEFAULT);
- }
- break;
- case SCE_PL_SUB_PROTOTYPE: {
- int i = 0;
- // forward scan; must all be valid proto characters
- while (setSubPrototype.Contains(sc.GetRelative(i)))
- i++;
- if (sc.GetRelative(i) == ')') { // valid sub prototype
- sc.ForwardBytes(i);
- sc.ForwardSetState(SCE_PL_DEFAULT);
- } else {
- // abandon prototype, restart from '('
- sc.ChangeState(SCE_PL_OPERATOR);
- sc.SetState(SCE_PL_DEFAULT);
- }
- }
- break;
- case SCE_PL_FORMAT: {
- sc.Complete();
- if (sc.Match('.')) {
- sc.Forward();
- if (sc.atLineEnd || ((sc.ch == '\r' && sc.chNext == '\n')))
- sc.SetState(SCE_PL_DEFAULT);
- }
- while (!sc.atLineEnd)
- sc.Forward();
- }
- break;
- case SCE_PL_ERROR:
- break;
- }
- // Needed for specific continuation styles (one follows the other)
- switch (sc.state) {
- // continued from SCE_PL_WORD
- case SCE_PL_FORMAT_IDENT:
- // occupies HereDoc state 3 to avoid clashing with HERE docs
- if (IsASpaceOrTab(sc.ch)) { // skip whitespace
- sc.ChangeState(SCE_PL_DEFAULT);
- while (IsASpaceOrTab(sc.ch) && !sc.atLineEnd)
- sc.Forward();
- sc.SetState(SCE_PL_FORMAT_IDENT);
- }
- if (setFormatStart.Contains(sc.ch)) { // identifier or '='
- if (sc.ch != '=') {
- do {
- sc.Forward();
- } while (setFormat.Contains(sc.ch));
- }
- while (IsASpaceOrTab(sc.ch) && !sc.atLineEnd)
- sc.Forward();
- if (sc.ch == '=') {
- sc.ForwardSetState(SCE_PL_DEFAULT);
- HereDoc.State = 3;
- } else {
- // invalid identifier; inexact fallback, but hey
- sc.ChangeState(SCE_PL_IDENTIFIER);
- sc.SetState(SCE_PL_DEFAULT);
- }
- } else {
- sc.ChangeState(SCE_PL_DEFAULT); // invalid identifier
- }
- backFlag = BACK_NONE;
- break;
- }
- // Must check end of HereDoc states here before default state is handled
- if (HereDoc.State == 1 && sc.atLineEnd) {
- // Begin of here-doc (the line after the here-doc delimiter):
- // Lexically, the here-doc starts from the next line after the >>, but the
- // first line of here-doc seem to follow the style of the last EOL sequence
- int st_new = SCE_PL_HERE_QQ;
- HereDoc.State = 2;
- if (HereDoc.Quoted) {
- if (sc.state == SCE_PL_HERE_DELIM) {
- // Missing quote at end of string! We are stricter than perl.
- // Colour here-doc anyway while marking this bit as an error.
- sc.ChangeState(SCE_PL_ERROR);
- }
- switch (HereDoc.Quote) {
- case '\'':
- st_new = SCE_PL_HERE_Q;
- break;
- case '"' :
- st_new = SCE_PL_HERE_QQ;
- break;
- case '`' :
- st_new = SCE_PL_HERE_QX;
- break;
- }
- } else {
- if (HereDoc.Quote == '\\')
- st_new = SCE_PL_HERE_Q;
- }
- sc.SetState(st_new);
- }
- if (HereDoc.State == 3 && sc.atLineEnd) {
- // Start of format body.
- HereDoc.State = 0;
- sc.SetState(SCE_PL_FORMAT);
- }
- // Determine if a new state should be entered.
- if (sc.state == SCE_PL_DEFAULT) {
- if (IsADigit(sc.ch) ||
- (IsADigit(sc.chNext) && (sc.ch == '.' || sc.ch == 'v'))) {
- sc.SetState(SCE_PL_NUMBER);
- backFlag = BACK_NONE;
- numState = PERLNUM_DECIMAL;
- dotCount = 0;
- if (sc.ch == '0') { // hex,bin,octal
- if (sc.chNext == 'x' || sc.chNext == 'X') {
- numState = PERLNUM_HEX;
- } else if (sc.chNext == 'b' || sc.chNext == 'B') {
- numState = PERLNUM_BINARY;
- } else if (IsADigit(sc.chNext)) {
- numState = PERLNUM_OCTAL;
- }
- if (numState != PERLNUM_DECIMAL) {
- sc.Forward();
- }
- } else if (sc.ch == 'v') { // vector
- numState = PERLNUM_V_VECTOR;
- }
- } else if (setWord.Contains(sc.ch)) {
- // if immediately prefixed by '::', always a bareword
- sc.SetState(SCE_PL_WORD);
- if (sc.chPrev == ':' && sc.GetRelative(-2) == ':') {
- sc.ChangeState(SCE_PL_IDENTIFIER);
- }
- Sci_PositionU bk = sc.currentPos;
- Sci_PositionU fw = sc.currentPos + 1;
- // first check for possible quote-like delimiter
- if (sc.ch == 's' && !setWord.Contains(sc.chNext)) {
- sc.ChangeState(SCE_PL_REGSUBST);
- Quote.New(2);
- } else if (sc.ch == 'm' && !setWord.Contains(sc.chNext)) {
- sc.ChangeState(SCE_PL_REGEX);
- Quote.New();
- } else if (sc.ch == 'q' && !setWord.Contains(sc.chNext)) {
- sc.ChangeState(SCE_PL_STRING_Q);
- Quote.New();
- } else if (sc.ch == 'y' && !setWord.Contains(sc.chNext)) {
- sc.ChangeState(SCE_PL_XLAT);
- Quote.New(2);
- } else if (sc.Match('t', 'r') && !setWord.Contains(sc.GetRelative(2))) {
- sc.ChangeState(SCE_PL_XLAT);
- Quote.New(2);
- sc.Forward();
- fw++;
- } else if (sc.ch == 'q' && setQDelim.Contains(sc.chNext)
- && !setWord.Contains(sc.GetRelative(2))) {
- if (sc.chNext == 'q') sc.ChangeState(SCE_PL_STRING_QQ);
- else if (sc.chNext == 'x') sc.ChangeState(SCE_PL_STRING_QX);
- else if (sc.chNext == 'r') sc.ChangeState(SCE_PL_STRING_QR);
- else sc.ChangeState(SCE_PL_STRING_QW); // sc.chNext == 'w'
- Quote.New();
- sc.Forward();
- fw++;
- } else if (sc.ch == 'x' && (sc.chNext == '=' || // repetition
- !setWord.Contains(sc.chNext) ||
- (setRepetition.Contains(sc.chPrev) && IsADigit(sc.chNext)))) {
- sc.ChangeState(SCE_PL_OPERATOR);
- }
- // if potentially a keyword, scan forward and grab word, then check
- // if it's really one; if yes, disambiguation test is performed
- // otherwise it is always a bareword and we skip a lot of scanning
- if (sc.state == SCE_PL_WORD) {
- while (setWord.Contains(static_cast<unsigned char>(styler.SafeGetCharAt(fw))))
- fw++;
- if (!isPerlKeyword(styler.GetStartSegment(), fw, keywords, styler)) {
- sc.ChangeState(SCE_PL_IDENTIFIER);
- }
- }
- // if already SCE_PL_IDENTIFIER, then no ambiguity, skip this
- // for quote-like delimiters/keywords, attempt to disambiguate
- // to select for bareword, change state -> SCE_PL_IDENTIFIER
- if (sc.state != SCE_PL_IDENTIFIER && bk > 0) {
- if (disambiguateBareword(styler, bk, fw, backFlag, backPos, endPos))
- sc.ChangeState(SCE_PL_IDENTIFIER);
- }
- backFlag = BACK_NONE;
- } else if (sc.ch == '#') {
- sc.SetState(SCE_PL_COMMENTLINE);
- } else if (sc.ch == '\"') {
- sc.SetState(SCE_PL_STRING);
- Quote.New();
- Quote.Open(sc.ch);
- backFlag = BACK_NONE;
- } else if (sc.ch == '\'') {
- if (sc.chPrev == '&' && setWordStart.Contains(sc.chNext)) {
- // Archaic call
- sc.SetState(SCE_PL_IDENTIFIER);
- } else {
- sc.SetState(SCE_PL_CHARACTER);
- Quote.New();
- Quote.Open(sc.ch);
- }
- backFlag = BACK_NONE;
- } else if (sc.ch == '`') {
- sc.SetState(SCE_PL_BACKTICKS);
- Quote.New();
- Quote.Open(sc.ch);
- backFlag = BACK_NONE;
- } else if (sc.ch == '$') {
- sc.SetState(SCE_PL_SCALAR);
- if (sc.chNext == '{') {
- sc.ForwardSetState(SCE_PL_OPERATOR);
- } else if (IsASpace(sc.chNext)) {
- sc.ForwardSetState(SCE_PL_DEFAULT);
- } else {
- sc.Forward();
- if (sc.Match('`', '`') || sc.Match(':', ':')) {
- sc.Forward();
- }
- }
- backFlag = BACK_NONE;
- } else if (sc.ch == '@') {
- sc.SetState(SCE_PL_ARRAY);
- if (setArray.Contains(sc.chNext)) {
- // no special treatment
- } else if (sc.chNext == ':' && sc.GetRelative(2) == ':') {
- sc.ForwardBytes(2);
- } else if (sc.chNext == '{' || sc.chNext == '[') {
- sc.ForwardSetState(SCE_PL_OPERATOR);
- } else {
- sc.ChangeState(SCE_PL_OPERATOR);
- }
- backFlag = BACK_NONE;
- } else if (setPreferRE.Contains(sc.ch)) {
- // Explicit backward peeking to set a consistent preferRE for
- // any slash found, so no longer need to track preferRE state.
- // Find first previous significant lexed element and interpret.
- // A few symbols shares this code for disambiguation.
- bool preferRE = false;
- bool isHereDoc = sc.Match('<', '<');
- bool hereDocSpace = false; // for: SCALAR [whitespace] '<<'
- Sci_PositionU bk = (sc.currentPos > 0) ? sc.currentPos - 1: 0;
- sc.Complete();
- styler.Flush();
- if (styler.StyleAt(bk) == SCE_PL_DEFAULT)
- hereDocSpace = true;
- skipWhitespaceComment(styler, bk);
- if (bk == 0) {
- // avoid backward scanning breakage
- preferRE = true;
- } else {
- int bkstyle = styler.StyleAt(bk);
- int bkch = static_cast<unsigned char>(styler.SafeGetCharAt(bk));
- switch (bkstyle) {
- case SCE_PL_OPERATOR:
- preferRE = true;
- if (bkch == ')' || bkch == ']') {
- preferRE = false;
- } else if (bkch == '}') {
- // backtrack by counting balanced brace pairs
- // needed to test for variables like ${}, @{} etc.
- bkstyle = styleBeforeBracePair(styler, bk);
- if (bkstyle == SCE_PL_SCALAR
- || bkstyle == SCE_PL_ARRAY
- || bkstyle == SCE_PL_HASH
- || bkstyle == SCE_PL_SYMBOLTABLE
- || bkstyle == SCE_PL_OPERATOR) {
- preferRE = false;
- }
- } else if (bkch == '+' || bkch == '-') {
- if (bkch == static_cast<unsigned char>(styler.SafeGetCharAt(bk - 1))
- && bkch != static_cast<unsigned char>(styler.SafeGetCharAt(bk - 2)))
- // exceptions for operators: unary suffixes ++, --
- preferRE = false;
- }
- break;
- case SCE_PL_IDENTIFIER:
- preferRE = true;
- bkstyle = styleCheckIdentifier(styler, bk);
- if ((bkstyle == 1) || (bkstyle == 2)) {
- // inputsymbol or var with "->" or "::" before identifier
- preferRE = false;
- } else if (bkstyle == 3) {
- // bare identifier, test cases follows:
- if (sc.ch == '/') {
- // if '/', /PATTERN/ unless digit/space immediately after '/'
- // if '//', always expect defined-or operator to follow identifier
- if (IsASpace(sc.chNext) || IsADigit(sc.chNext) || sc.chNext == '/')
- preferRE = false;
- } else if (sc.ch == '*' || sc.ch == '%') {
- if (IsASpace(sc.chNext) || IsADigit(sc.chNext) || sc.Match('*', '*'))
- preferRE = false;
- } else if (sc.ch == '<') {
- if (IsASpace(sc.chNext) || sc.chNext == '=')
- preferRE = false;
- }
- }
- break;
- case SCE_PL_SCALAR: // for $var<< case:
- if (isHereDoc && hereDocSpace) // if SCALAR whitespace '<<', *always* a HERE doc
- preferRE = true;
- break;
- case SCE_PL_WORD:
- preferRE = true;
- // for HERE docs, always true
- if (sc.ch == '/') {
- // adopt heuristics similar to vim-style rules:
- // keywords always forced as /PATTERN/: split, if, elsif, while
- // everything else /PATTERN/ unless digit/space immediately after '/'
- // for '//', defined-or favoured unless special keywords
- Sci_PositionU bkend = bk + 1;
- while (bk > 0 && styler.StyleAt(bk - 1) == SCE_PL_WORD) {
- bk--;
- }
- if (isPerlKeyword(bk, bkend, reWords, styler))
- break;
- if (IsASpace(sc.chNext) || IsADigit(sc.chNext) || sc.chNext == '/')
- preferRE = false;
- } else if (sc.ch == '*' || sc.ch == '%') {
- if (IsASpace(sc.chNext) || IsADigit(sc.chNext) || sc.Match('*', '*'))
- preferRE = false;
- } else if (sc.ch == '<') {
- if (IsASpace(sc.chNext) || sc.chNext == '=')
- preferRE = false;
- }
- break;
- // other styles uses the default, preferRE=false
- case SCE_PL_POD:
- case SCE_PL_HERE_Q:
- case SCE_PL_HERE_QQ:
- case SCE_PL_HERE_QX:
- preferRE = true;
- break;
- }
- }
- backFlag = BACK_NONE;
- if (isHereDoc) { // handle '<<', HERE doc
- if (sc.Match("<<>>")) { // double-diamond operator (5.22)
- sc.SetState(SCE_PL_OPERATOR);
- sc.Forward(3);
- } else if (preferRE) {
- sc.SetState(SCE_PL_HERE_DELIM);
- HereDoc.State = 0;
- } else { // << operator
- sc.SetState(SCE_PL_OPERATOR);
- sc.Forward();
- }
- } else if (sc.ch == '*') { // handle '*', typeglob
- if (preferRE) {
- sc.SetState(SCE_PL_SYMBOLTABLE);
- if (sc.chNext == ':' && sc.GetRelative(2) == ':') {
- sc.ForwardBytes(2);
- } else if (sc.chNext == '{') {
- sc.ForwardSetState(SCE_PL_OPERATOR);
- } else {
- sc.Forward();
- }
- } else {
- sc.SetState(SCE_PL_OPERATOR);
- if (sc.chNext == '*') // exponentiation
- sc.Forward();
- }
- } else if (sc.ch == '%') { // handle '%', hash
- if (preferRE) {
- sc.SetState(SCE_PL_HASH);
- if (setHash.Contains(sc.chNext)) {
- sc.Forward();
- } else if (sc.chNext == ':' && sc.GetRelative(2) == ':') {
- sc.ForwardBytes(2);
- } else if (sc.chNext == '{') {
- sc.ForwardSetState(SCE_PL_OPERATOR);
- } else {
- sc.ChangeState(SCE_PL_OPERATOR);
- }
- } else {
- sc.SetState(SCE_PL_OPERATOR);
- }
- } else if (sc.ch == '<') { // handle '<', inputsymbol
- if (preferRE) {
- // forward scan
- int i = InputSymbolScan(sc);
- if (i > 0) {
- sc.SetState(SCE_PL_IDENTIFIER);
- sc.Forward(i);
- } else {
- sc.SetState(SCE_PL_OPERATOR);
- }
- } else {
- sc.SetState(SCE_PL_OPERATOR);
- }
- } else { // handle '/', regexp
- if (preferRE) {
- sc.SetState(SCE_PL_REGEX);
- Quote.New();
- Quote.Open(sc.ch);
- } else { // / and // operators
- sc.SetState(SCE_PL_OPERATOR);
- if (sc.chNext == '/') {
- sc.Forward();
- }
- }
- }
- } else if (sc.ch == '=' // POD
- && setPOD.Contains(sc.chNext)
- && sc.atLineStart) {
- sc.SetState(SCE_PL_POD);
- backFlag = BACK_NONE;
- } else if (sc.ch == '-' && setWordStart.Contains(sc.chNext)) { // extended '-' cases
- Sci_PositionU bk = sc.currentPos;
- Sci_PositionU fw = 2;
- if (setSingleCharOp.Contains(sc.chNext) && // file test operators
- !setWord.Contains(sc.GetRelative(2))) {
- sc.SetState(SCE_PL_WORD);
- } else {
- // nominally a minus and bareword; find extent of bareword
- while (setWord.Contains(sc.GetRelative(fw)))
- fw++;
- sc.SetState(SCE_PL_OPERATOR);
- }
- // force to bareword for hash key => or {variable literal} cases
- if (disambiguateBareword(styler, bk, bk + fw, backFlag, backPos, endPos) & 2) {
- sc.ChangeState(SCE_PL_IDENTIFIER);
- }
- backFlag = BACK_NONE;
- } else if (sc.ch == '(' && sc.currentPos > 0) { // '(' or subroutine prototype
- sc.Complete();
- if (styleCheckSubPrototype(styler, sc.currentPos - 1)) {
- sc.SetState(SCE_PL_SUB_PROTOTYPE);
- backFlag = BACK_NONE;
- } else {
- sc.SetState(SCE_PL_OPERATOR);
- }
- } else if (setPerlOperator.Contains(sc.ch)) { // operators
- sc.SetState(SCE_PL_OPERATOR);
- if (sc.Match('.', '.')) { // .. and ...
- sc.Forward();
- if (sc.chNext == '.') sc.Forward();
- }
- } else if (sc.ch == 4 || sc.ch == 26) { // ^D and ^Z ends valid perl source
- sc.SetState(SCE_PL_DATASECTION);
- } else {
- // keep colouring defaults
- sc.Complete();
- }
- }
- }
- sc.Complete();
- if (sc.state == SCE_PL_HERE_Q
- || sc.state == SCE_PL_HERE_QQ
- || sc.state == SCE_PL_HERE_QX
- || sc.state == SCE_PL_FORMAT) {
- styler.ChangeLexerState(sc.currentPos, styler.Length());
- }
- sc.Complete();
- }
- #define PERL_HEADFOLD_SHIFT 4
- #define PERL_HEADFOLD_MASK 0xF0
- void SCI_METHOD LexerPerl::Fold(Sci_PositionU startPos, Sci_Position length, int /* initStyle */, IDocument *pAccess) {
- if (!options.fold)
- return;
- LexAccessor styler(pAccess);
- Sci_PositionU endPos = startPos + length;
- int visibleChars = 0;
- Sci_Position lineCurrent = styler.GetLine(startPos);
- // Backtrack to previous line in case need to fix its fold status
- if (startPos > 0) {
- if (lineCurrent > 0) {
- lineCurrent--;
- startPos = styler.LineStart(lineCurrent);
- }
- }
- int levelPrev = SC_FOLDLEVELBASE;
- if (lineCurrent > 0)
- levelPrev = styler.LevelAt(lineCurrent - 1) >> 16;
- int levelCurrent = levelPrev;
- char chNext = styler[startPos];
- char chPrev = styler.SafeGetCharAt(startPos - 1);
- int styleNext = styler.StyleAt(startPos);
- // Used at end of line to determine if the line was a package definition
- bool isPackageLine = false;
- int podHeading = 0;
- for (Sci_PositionU i = startPos; i < endPos; i++) {
- char ch = chNext;
- chNext = styler.SafeGetCharAt(i + 1);
- int style = styleNext;
- styleNext = styler.StyleAt(i + 1);
- int stylePrevCh = (i) ? styler.StyleAt(i - 1):SCE_PL_DEFAULT;
- bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
- bool atLineStart = ((chPrev == '\r') || (chPrev == '\n')) || i == 0;
- // Comment folding
- if (options.foldComment && atEOL && IsCommentLine(lineCurrent, styler)) {
- if (!IsCommentLine(lineCurrent - 1, styler)
- && IsCommentLine(lineCurrent + 1, styler))
- levelCurrent++;
- else if (IsCommentLine(lineCurrent - 1, styler)
- && !IsCommentLine(lineCurrent + 1, styler))
- levelCurrent--;
- }
- // {} [] block folding
- if (style == SCE_PL_OPERATOR) {
- if (ch == '{') {
- if (options.foldAtElse && levelCurrent < levelPrev)
- --levelPrev;
- levelCurrent++;
- } else if (ch == '}') {
- levelCurrent--;
- }
- if (ch == '[') {
- if (options.foldAtElse && levelCurrent < levelPrev)
- --levelPrev;
- levelCurrent++;
- } else if (ch == ']') {
- levelCurrent--;
- }
- }
- // POD folding
- if (options.foldPOD && atLineStart) {
- if (style == SCE_PL_POD) {
- if (stylePrevCh != SCE_PL_POD && stylePrevCh != SCE_PL_POD_VERB)
- levelCurrent++;
- else if (styler.Match(i, "=cut"))
- levelCurrent = (levelCurrent & ~PERL_HEADFOLD_MASK) - 1;
- else if (styler.Match(i, "=head"))
- podHeading = PodHeadingLevel(i, styler);
- } else if (style == SCE_PL_DATASECTION) {
- if (ch == '=' && IsASCII(chNext) && isalpha(chNext) && levelCurrent == SC_FOLDLEVELBASE)
- levelCurrent++;
- else if (styler.Match(i, "=cut") && levelCurrent > SC_FOLDLEVELBASE)
- levelCurrent = (levelCurrent & ~PERL_HEADFOLD_MASK) - 1;
- else if (styler.Match(i, "=head"))
- podHeading = PodHeadingLevel(i, styler);
- // if package used or unclosed brace, level > SC_FOLDLEVELBASE!
- // reset needed as level test is vs. SC_FOLDLEVELBASE
- else if (stylePrevCh != SCE_PL_DATASECTION)
- levelCurrent = SC_FOLDLEVELBASE;
- }
- }
- // package folding
- if (options.foldPackage && atLineStart) {
- if (IsPackageLine(lineCurrent, styler)
- && !IsPackageLine(lineCurrent + 1, styler))
- isPackageLine = true;
- }
- //heredoc folding
- switch (style) {
- case SCE_PL_HERE_QQ :
- case SCE_PL_HERE_Q :
- case SCE_PL_HERE_QX :
- switch (stylePrevCh) {
- case SCE_PL_HERE_QQ :
- case SCE_PL_HERE_Q :
- case SCE_PL_HERE_QX :
- //do nothing;
- break;
- default :
- levelCurrent++;
- break;
- }
- break;
- default:
- switch (stylePrevCh) {
- case SCE_PL_HERE_QQ :
- case SCE_PL_HERE_Q :
- case SCE_PL_HERE_QX :
- levelCurrent--;
- break;
- default :
- //do nothing;
- break;
- }
- break;
- }
- //explicit folding
- if (options.foldCommentExplicit && style == SCE_PL_COMMENTLINE && ch == '#') {
- if (chNext == '{') {
- levelCurrent++;
- } else if (levelCurrent > SC_FOLDLEVELBASE && chNext == '}') {
- levelCurrent--;
- }
- }
- if (atEOL) {
- int lev = levelPrev;
- // POD headings occupy bits 7-4, leaving some breathing room for
- // non-standard practice -- POD sections stuck in blocks, etc.
- if (podHeading > 0) {
- levelCurrent = (lev & ~PERL_HEADFOLD_MASK) | (podHeading << PERL_HEADFOLD_SHIFT);
- lev = levelCurrent - 1;
- lev |= SC_FOLDLEVELHEADERFLAG;
- podHeading = 0;
- }
- // Check if line was a package declaration
- // because packages need "special" treatment
- if (isPackageLine) {
- lev = SC_FOLDLEVELBASE | SC_FOLDLEVELHEADERFLAG;
- levelCurrent = SC_FOLDLEVELBASE + 1;
- isPackageLine = false;
- }
- lev |= levelCurrent << 16;
- if (visibleChars == 0 && options.foldCompact)
- lev |= SC_FOLDLEVELWHITEFLAG;
- if ((levelCurrent > levelPrev) && (visibleChars > 0))
- lev |= SC_FOLDLEVELHEADERFLAG;
- if (lev != styler.LevelAt(lineCurrent)) {
- styler.SetLevel(lineCurrent, lev);
- }
- lineCurrent++;
- levelPrev = levelCurrent;
- visibleChars = 0;
- }
- if (!isspacechar(ch))
- visibleChars++;
- chPrev = ch;
- }
- // Fill in the real level of the next line, keeping the current flags as they will be filled in later
- int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK;
- styler.SetLevel(lineCurrent, levelPrev | flagsNext);
- }
- LexerModule lmPerl(SCLEX_PERL, LexerPerl::LexerFactoryPerl, "perl", perlWordListDesc);
|