notepad-plus-plus/lexilla/lexers/LexUser.cxx

2333 lines
94 KiB
C++

/*------------------------------------------------------------------------------------
this file is part of notepad++
Copyright (C)2003 Don HO < donho@altern.org >
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
----------------------------------------------------------------------------------------*/
#include <string>
#include <map>
#include <vector>
#include <assert.h>
#include <windows.h>
#include "ILexer.h"
#include "LexAccessor.h"
#include "Accessor.h"
#include "StyleContext.h"
#include "WordList.h"
#include "Scintilla.h"
#include "SciLexer.h"
#include "LexerModule.h"
#include "PropSetSimple.h"
using namespace Lexilla;
#define CL_CURRENT 0x1
#define CL_PREV 0x2
#define CL_PREVPREV 0x4
#define FOLD_NONE 0
#define FOLD_OPEN 1
#define FOLD_MIDDLE 2
#define FOLD_CLOSE 3
#define COMMENTLINE_NO 0
#define COMMENTLINE_YES 1
#define COMMENTLINE_SKIP_TESTING 2
#define SEPARATOR_DOT 0
#define SEPARATOR_COMMA 1
#define SEPARATOR_BOTH 2
#define NI_OPEN 0
#define NI_CLOSE 1
#define NO_DELIMITER 0
#define FORWARD_WHITESPACE_FOUND 1
#define FORWARD_KEYWORD_FOUND 2
#define SC_ISCOMMENTLINE 0x8000
#define MULTI_PART_LIMIT 100
#define PURE_LC_NONE 0 // must be in synch with the same values in PowerEditor/src/Parameters.h
#define PURE_LC_BOL 1
#define PURE_LC_WSP 2
#define EOL_DEFAULT_VALUE 0
#define EOL_SKIP_CHECK 1
#define EOL_FORCE_CHECK 2
#define MAPPER_TOTAL 15
#define FW_VECTORS_TOTAL SCE_USER_TOTAL_DELIMITERS + 9
const int maskMapper[MAPPER_TOTAL] =
{
SCE_USER_MASK_NESTING_OPERATORS2,
SCE_USER_MASK_NESTING_FOLDERS_IN_CODE2_OPEN,
SCE_USER_MASK_NESTING_FOLDERS_IN_CODE2_MIDDLE,
SCE_USER_MASK_NESTING_FOLDERS_IN_CODE2_CLOSE,
SCE_USER_MASK_NESTING_FOLDERS_IN_COMMENT_OPEN,
SCE_USER_MASK_NESTING_FOLDERS_IN_COMMENT_MIDDLE,
SCE_USER_MASK_NESTING_FOLDERS_IN_COMMENT_CLOSE,
SCE_USER_MASK_NESTING_KEYWORD1,
SCE_USER_MASK_NESTING_KEYWORD2,
SCE_USER_MASK_NESTING_KEYWORD3,
SCE_USER_MASK_NESTING_KEYWORD4,
SCE_USER_MASK_NESTING_KEYWORD5,
SCE_USER_MASK_NESTING_KEYWORD6,
SCE_USER_MASK_NESTING_KEYWORD7,
SCE_USER_MASK_NESTING_KEYWORD8
};
const int styleMapper[MAPPER_TOTAL] =
{
SCE_USER_STYLE_OPERATOR,
SCE_USER_STYLE_FOLDER_IN_CODE2,
SCE_USER_STYLE_FOLDER_IN_CODE2,
SCE_USER_STYLE_FOLDER_IN_CODE2,
SCE_USER_STYLE_FOLDER_IN_COMMENT,
SCE_USER_STYLE_FOLDER_IN_COMMENT,
SCE_USER_STYLE_FOLDER_IN_COMMENT,
SCE_USER_STYLE_KEYWORD1,
SCE_USER_STYLE_KEYWORD2,
SCE_USER_STYLE_KEYWORD3,
SCE_USER_STYLE_KEYWORD4,
SCE_USER_STYLE_KEYWORD5,
SCE_USER_STYLE_KEYWORD6,
SCE_USER_STYLE_KEYWORD7,
SCE_USER_STYLE_KEYWORD8
};
const int foldingtMapper[MAPPER_TOTAL] =
{
FOLD_NONE,
FOLD_OPEN,
FOLD_MIDDLE,
FOLD_CLOSE,
FOLD_OPEN,
FOLD_MIDDLE,
FOLD_CLOSE,
FOLD_NONE,
FOLD_NONE,
FOLD_NONE,
FOLD_NONE,
FOLD_NONE,
FOLD_NONE,
FOLD_NONE,
FOLD_NONE
};
using namespace std;
typedef vector<vector<string>> vvstring;
// static vector<int> * foldVectorStatic; // foldVectorStatic is used for debugging only, it should be commented out in production code !
struct forwardStruct
{
vvstring * _vec;
int _sceID;
int _maskID;
forwardStruct(): _vec(0), _sceID(0), _maskID(0) {}; // constructor, useless but obligatory
forwardStruct * Set (vvstring * vec, int sceID, int maskID) {
_vec = vec;
_sceID = sceID;
_maskID = maskID;
return this;
}
}FWS; // just one instance
struct nestedInfo {
size_t _position;
int _nestedLevel;
int _index;
int _state;
int _opener;
// constructor, useless but obligatory
nestedInfo():_position(0), _nestedLevel(0), _index(0), _state(0), _opener(0) {};
nestedInfo * Set (size_t position, int nestedLevel, int index, int state, int opener) {
_position = position;
_nestedLevel = nestedLevel;
_index = index;
_state = state;
_opener = opener;
return this;
}
};
static nestedInfo NI; // also just one instance
struct udlKeywordsMapStruct
{
vvstring commentLineOpen, commentLineContinue, commentLineClose;
vvstring commentOpen, commentClose;
vvstring delim1Open, delim1Escape, delim1Close;
vvstring delim2Open, delim2Escape, delim2Close;
vvstring delim3Open, delim3Escape, delim3Close;
vvstring delim4Open, delim4Escape, delim4Close;
vvstring delim5Open, delim5Escape, delim5Close;
vvstring delim6Open, delim6Escape, delim6Close;
vvstring delim7Open, delim7Escape, delim7Close;
vvstring delim8Open, delim8Escape, delim8Close;
vvstring operators1;
vvstring foldersInCode1Open, foldersInCode1Middle, foldersInCode1Close;
vvstring foldersInCode2Open, foldersInCode2Middle, foldersInCode2Close;
vector<string> prefixTokens1;
vector<string> prefixTokens2;
vector<string> suffixTokens1;
vector<string> suffixTokens2;
vector<string> extrasTokens1;
vector<string> extrasTokens2;
vector<string> rangeTokens;
vector<string> negativePrefixTokens1;
vector<string> negativePrefixTokens2;
vector<string> negativeExtrasTokens2;
};
// key value is of type "int" so it could receive pointer value !!
// UDL name is defined as "const char *" in UserLangContainer class
// so, map will use pointer value (not value pointed to!) as the key
typedef map<int, udlKeywordsMapStruct> udlMapType;
static udlMapType udlKeywordsMap;
// key value is of type "int" so it could receive pointer value !!
// currentBufferID is defined as "Buffer *" in ScintillaEditView class
// so, map will use pointer value (not value pointed to!) as the key
typedef map<int, vector<nestedInfo> > nestedMapType;
static nestedMapType nestedMap;
static inline bool isWhiteSpace(const int ch)
{
return (ch > 0 && ch < 0x21);
}
static inline bool isWhiteSpace2(unsigned char ch, int & nlCount, unsigned char excludeNewLine=0, unsigned char chNext=0)
{
// multi-part keywords come in two flavors:
// 1. "else if" (internally mapped to "else\vif") where '\v' can be replaced by spaces, tabs and new lines
// 2. 'else if" (internally mapped to "else\bif") where '\b' can be replaced by spaces, tabs but not new lines
// 'excludeNewLine' parameter is used to differentiate the two
if ((ch == '\n') || (ch == '\r' && chNext != '\n'))
++nlCount;
if (excludeNewLine == '\b')
return (ch == ' ') || (ch == '\t');
else
return isWhiteSpace(ch);
}
static bool isInListForward2(vvstring * fwEndVectors[], int totalVectors, StyleContext & sc, bool ignoreCase, int forward)
{
// forward check for multi-part keywords and numbers
// this is differnt from 'isInListForward' function because
// search for keyword is not performed at sc.currentPos but rather
// at some position forward of sc.currentPos
vvstring::iterator iter1;// = openVector.begin();
vector<string>::iterator iter2;
string::iterator iter3;
int index = 0;
int a = 0;
int b = 0;
for (int i=0; i<totalVectors; ++i)
{
if (fwEndVectors[i] && !fwEndVectors[i]->empty())
{
index = 0;
a = 0;
b = 0;
for (iter1 = fwEndVectors[i]->begin(); iter1 != fwEndVectors[i]->end(); ++iter1)
{
iter2 = iter1->begin();
for (; iter2 != iter1->end(); ++iter2)
{
iter3 = iter2->begin();
index = 0;
for (; ; ++iter3)
{
a = ignoreCase?toupper(*iter3):*iter3;
b = ignoreCase?toupper(sc.GetRelative(forward + index++)):sc.GetRelative(forward + index++);
if (a != b)
break;
if (iter3 != iter2->end())
return true;
}
}
}
}
}
return false;
}
static bool isInListForward3(vector<string> * tokens, StyleContext & sc, bool ignoreCase, Sci_Position offset, size_t & moveForward)
{
// forward check for vector<string> keywords, with offset
moveForward = 0;
unsigned char a = 0;
unsigned char b = 0;
Sci_Position indexb = 0;
bool isFound = false;
vector<string>::iterator iter1;
string::iterator iter2;
for (iter1 = tokens->begin(); iter1 != tokens->end(); ++iter1)
{
a = 0;
b = 0;
indexb = 0;
isFound = true;
for (iter2 = iter1->begin(); iter2 != iter1->end(); ++iter2)
{
a = static_cast<unsigned char>(ignoreCase?toupper(*iter2):*iter2);
b = static_cast<unsigned char>(ignoreCase?toupper(sc.GetRelative(offset + indexb++)):sc.GetRelative(offset + indexb++));
if (a != b)
{
isFound = false;
break;
}
}
if (isFound == true)
{
moveForward += iter1->length();
break;
}
}
return isFound;
}
static inline bool IsADigit(char ch)
{
return isascii(ch) && isdigit(ch);
}
static bool IsNumber(StyleContext & sc, vector<string> * numberTokens[], vvstring * fwEndVectors[],
bool ignoreCase, int decSeparator, size_t & moveForward )
{
moveForward = 0;
bool hasDot = false;
bool hasPrefix1 = false;
bool hasPrefix2 = false;
bool hasSuffix1 = false;
bool hasSuffix2 = false;
bool hasExtras2 = false;
bool hasRange = false;
bool hasExp = false;
bool previousWasRange = false;
Sci_Position offset = 0;
vector<string> * prefixTokens1 = numberTokens[0];
vector<string> * prefixTokens2 = numberTokens[1];
vector<string> * extrasTokens1 = numberTokens[2];
vector<string> * extrasTokens2 = numberTokens[3];
vector<string> * suffixTokens1 = numberTokens[4];
vector<string> * suffixTokens2 = numberTokens[5];
vector<string> * rangeTokens = numberTokens[6];
vector<string> * negativePrefixTokens1 = numberTokens[7];
vector<string> * negativePrefixTokens2 = numberTokens[8];
vector<string> * negativeExtrasTokens2 = numberTokens[9];
// treat .1234 as correct number sequence
if (((decSeparator == SEPARATOR_BOTH || decSeparator == SEPARATOR_DOT) && sc.ch == '.') ||
((decSeparator == SEPARATOR_BOTH || decSeparator == SEPARATOR_COMMA) && sc.ch == ','))
{
if (IsADigit(sc.chNext))
{
hasDot = true;
offset = 2;
}
}
else
{
// or is it a prefixed number?
vector<string>::iterator iter = prefixTokens2->begin();
vector<string>::iterator last = prefixTokens2->end();
if (sc.ch == '-')
{
iter = negativePrefixTokens2->begin();
last = negativePrefixTokens2->end();
}
for (; iter != last; ++iter)
{
if (ignoreCase?sc.MatchIgnoreCase2(iter->c_str()) : sc.Match(iter->c_str()))
break;
}
if (iter != last)
{
// prefix2 is styled as number only if followed by an actual number or NBR_EXTRA_CHAR
size_t skipForward = 0;
if (isInListForward3(extrasTokens1, sc, ignoreCase, iter->length(), skipForward))
{
offset += iter->length() + skipForward;
hasPrefix2 = true;
hasExp = true; // can't be a scientific E notation
}
else if (IsADigit(sc.GetRelative(iter->length())))
{
offset += iter->length() + 1;
hasPrefix2 = true;
hasExp = true; // can't be a scientific E notation
}
}
if (hasPrefix2 == false)
{
// or is it a prefixed1 number?
vector<string>::iterator iter = prefixTokens1->begin();
vector<string>::iterator last = prefixTokens1->end();
if (sc.ch == '-')
{
iter = negativePrefixTokens1->begin();
last = negativePrefixTokens1->end();
}
for (; iter != last; ++iter)
{
if (ignoreCase?sc.MatchIgnoreCase2(iter->c_str()) : sc.Match(iter->c_str()))
break;
}
if (iter != last)
{
// prefix1 is styled as number only if followed by an actual number (decimal digit)
if (IsADigit(sc.GetRelative(iter->length())))
{
offset += iter->length() + 1;
hasPrefix1 = true;
hasPrefix2 = false; // can't have any EXTRA_CHARs
hasExp = true; // can't be a scientific E notation
}
}
}
if (hasPrefix1 == false && hasPrefix2 == false)
{
// or is it a suffixed1 number with extras2?
vector<string>::iterator iter = extrasTokens2->begin();
vector<string>::iterator last = extrasTokens2->end();
if (sc.ch == '-')
{
iter = negativeExtrasTokens2->begin();
last = negativeExtrasTokens2->end();
}
for (; iter != last; ++iter)
{
if (ignoreCase?sc.MatchIgnoreCase2(iter->c_str()) : sc.Match(iter->c_str()))
break;
}
if (iter != last)
{
offset += iter->length();
hasExtras2 = true;
hasExp = true; // can't be a scientific E notation
}
}
}
// is it a simple digit?
if (offset == 0)
{
if (IsADigit(sc.ch))
{
offset = 1;
}
// or prefixed simple digit?
else if ((sc.ch == '-' || sc.ch == '+') && IsADigit(sc.chNext) && !IsADigit(sc.chPrev))
{
offset = 2;
}
if (offset == 0)
return false;
}
size_t skipForward = 0;
for (;;)
{
skipForward = 0;
// if (isInListForward2(fwEndVectors, (*fwEndVectors)->size(), sc, ignoreCase, offset) || isWhiteSpace(sc.GetRelative(offset)))
if (isWhiteSpace(sc.GetRelative(offset)) || isInListForward2(fwEndVectors, 12, sc, ignoreCase, offset))
{
if (hasExtras2 == true && hasSuffix1 == false)
return false;
moveForward = offset;
return true; // yay, finally we have a number
}
if (hasRange == false)
{
if (isInListForward3(rangeTokens, sc, ignoreCase, offset, skipForward))
{
if (hasExtras2 == true && hasSuffix1 == false)
return false;
offset += skipForward;
hasSuffix1 = false;
hasSuffix2 = false;
hasDot = false;
hasRange = true;
hasExp = false;
hasExtras2 = false;
previousWasRange = true;
continue;
}
}
if (hasSuffix2 == true) // only RANGE_CHARs are allowed after SUFFIX_CHARs
return false;
if (hasPrefix2 == true)
{
if (isInListForward3(extrasTokens1, sc, ignoreCase, offset, skipForward))
{
offset += skipForward;
continue;
}
}
if (hasSuffix1 == false && hasPrefix1 == false && hasPrefix2 == false)
{
if (isInListForward3(suffixTokens1, sc, ignoreCase, offset, skipForward))
{
offset += skipForward;
hasExtras2 = false;
hasSuffix1 = true;
continue;
}
if (isInListForward3(extrasTokens2, sc, ignoreCase, offset, skipForward))
{
offset += skipForward;
hasExtras2 = true;
hasExp = true; // can't be a scientific E notation
continue;
}
}
if (hasSuffix2 == false)
{
if (isInListForward3(suffixTokens2, sc, ignoreCase, offset, skipForward))
{
offset += skipForward;
hasSuffix2 = true;
continue;
}
}
if (previousWasRange == true) // prefix in the middle is an error case, so any number is treated as if it had a prefix
{ // the only acceptable position for prefix is immediatelly after range char, e.g. 0x10--0x15
if (isInListForward3(prefixTokens2, sc, ignoreCase, offset, skipForward))
{
offset += skipForward;
hasExp = false;
hasPrefix2 = true;
continue;
}
if (isInListForward3(prefixTokens1, sc, ignoreCase, offset, skipForward))
{
offset += skipForward;
hasExp = false;
hasPrefix1 = true;
continue;
}
}
if (IsADigit(sc.GetRelative(offset)))
{
offset += 1;
continue;
}
if (hasDot == false)
{
// treat .1234 (or ,1234) as correct number sequence
if (((decSeparator == SEPARATOR_BOTH || decSeparator == SEPARATOR_DOT) &&
(sc.GetRelative(offset) == '.'))
||
((decSeparator == SEPARATOR_BOTH || decSeparator == SEPARATOR_COMMA) &&
(sc.GetRelative(offset) == ',')))
{
if (IsADigit(sc.GetRelative(offset + 1)))
{
if (IsADigit(sc.GetRelative(offset - 1)) || previousWasRange == true)
{
offset += 2;
hasDot = true;
continue;
}
}
}
}
if (hasExp == false)
{
if (toupper(sc.GetRelative(offset)) == 'E') // treat E as scientific notation only if it does not match extra chars!!
{
unsigned char chPrev = sc.GetRelative(offset - 1);
unsigned char chNext = sc.GetRelative(offset + 1);
unsigned char chNextNext = sc.GetRelative(offset + 2);
if (IsADigit(chPrev))
{
int move = 0;
if (IsADigit(chNext))
{
move = 1;
}
else if ((chNext == '+' || chNext == '-') && IsADigit(chNextNext))
{
move = 2;
}
if (move > 0)
{
offset += move;
hasPrefix2 = false; // EXTRA_CHARs are not allowed in E notation
hasDot = false;
hasExp = true;
continue;
}
}
}
}
// not a number
return false;
}
}
static inline void SubGroup(const char * s, vvstring & vec, bool group=false)
{
size_t length = strlen(s);
char * temp = new char[length+1] {};
unsigned int index = 0;
vector<string> subvector;
unsigned int i = 0;
for (unsigned int j=0; j<length+1; ++j)
temp[j] = 0;
if (length >= 2 && s[0] == '(' && s[1] == '(')
{
i = 2;
group = true;
}
if (length >= 2 && s[length - 1] == ')' && s[length - 2] == ')')
length -= 2;
if (!group && *s)
{
subvector.push_back(s);
}
else
{
for (; i<length; ++i)
{
if (s[i] == ' ')
{
if (*temp)
{
if (!strcmp(temp, "EOL"))
{
subvector.push_back("\r\n");
subvector.push_back("\n");
subvector.push_back("\r");
}
else
subvector.push_back(temp);
index = 0;
for (unsigned int j=0; j<length; ++j)
temp[j] = 0;
}
}
else if (i == length-1)
{
temp[index++] = s[i];
if (*temp)
{
if (!strcmp(temp, "EOL"))
{
subvector.push_back("\r\n");
subvector.push_back("\n");
subvector.push_back("\r");
}
else
subvector.push_back(temp);
}
}
else
{
temp[index++] = s[i];
}
}
}
if (!subvector.empty())
vec.push_back(subvector);
delete [] temp;
}
static inline void GenerateVector(vvstring & vec, const char * s, const char * prefix, size_t minLength)
{
size_t length = strlen(s);
char * temp = new char[length] {};
unsigned int index = 0;
bool copy = false;
bool inGroup = false;
for (unsigned int j=0; j<length; ++j)
temp[j] = 0;
vec.clear();
for (unsigned int i=0; i<length; ++i)
{
if (copy && !inGroup && s[i] == ' ')
{
SubGroup(temp, vec, inGroup);
index = 0;
copy = false;
for (unsigned int j=0; j<length; ++j)
temp[j] = 0;
}
if ( (s[i] == ' ' && s[i+1] == prefix[0] && s[i+2] == prefix[1] && s[i+3] != ' ') ||
( i == 0 && s[0] == prefix[0] && s[1] == prefix[1] && s[i+2] != ' ') )
{
if (i > 0) i += 1; // skip space
i += 2; // skip prefix
copy = true;
if (s[i] == ' ')
continue;
if (s[i] == '(' && s[i+1] == '(')
inGroup = true;
}
if (inGroup && s[i] == ')' && s[i+1] == ')')
inGroup = false;
if (copy)
temp[index++] = s[i];
}
if (length)
SubGroup(temp, vec, inGroup);
vector<string> emptyVector;
for (size_t i = vec.size(); i < minLength; ++i)
{
vec.push_back(emptyVector);
}
delete [] temp;
}
static inline void StringToVector(char * original, vector<string> & tokenVector, bool negative=false)
{
// this is rarely used, so I chose std::string for simplicity reasons
// for better performance C-strings could be used
string temp = "";
char * pch = original;
while (*pch != '\0')
{
if (*pch != ' ')
temp += *pch; //
else if (temp.size() > 0)
{
if (negative)
tokenVector.push_back("-" + temp);
else
tokenVector.push_back(temp);
temp = "";
}
++pch;
}
if (temp.size() > 0)
{
if (negative)
tokenVector.push_back("-" + temp);
else
tokenVector.push_back(temp);
}
}
static inline void ReColoringCheck(Sci_PositionU & startPos, int & nestedLevel, int & initStyle, int & openIndex,
int & isCommentLine, bool & isInComment, Accessor & styler, vector<nestedInfo> & lastNestedGroup,
vector<nestedInfo> & nestedVector, /* vector<int> & foldVector, */ int & continueCommentBlock)
{
if (startPos > 0)
{
// re-coloring always starts at line beginning !!
// special exception for multipart keywords
initStyle = styler.StyleAt(startPos-1); // check style of previous new line character
if ( (initStyle >= SCE_USER_STYLE_KEYWORD1 && initStyle < (SCE_USER_STYLE_KEYWORD1+SCE_USER_TOTAL_KEYWORD_GROUPS)) // keywords1-8
|| initStyle == SCE_USER_STYLE_FOLDER_IN_COMMENT
|| initStyle == SCE_USER_STYLE_FOLDER_IN_CODE2 )
{
// we are in middle of multi-part keyword that contains newline characters, go back until current style ends
while (startPos > 0 && styler.StyleAt(--startPos) == initStyle);
}
}
if (startPos > 0)
{
// go back until first EOL char
char ch = 0;
do
{
ch = styler.SafeGetCharAt(--startPos);
}
while(ch != '\r' && ch != '\n' && startPos > 0);
if (startPos > 0)
startPos += 1; // compensate for decrement operation
}
if (startPos == 0)
{
// foldVector.clear();
nestedVector.clear();
lastNestedGroup.clear();
initStyle = SCE_USER_STYLE_IDENTIFIER;
return;
}
// clear all data on positions forward of 'startPos' as we
// are about to re-color that part of the document.
vector<nestedInfo>::iterator iter = nestedVector.begin();
for (; iter != nestedVector.end(); ++iter)
{
if (iter->_position >= startPos)
{
nestedVector.erase(iter, nestedVector.end());
break;
}
}
if (!nestedVector.empty())
{
// go back to last nesting level '1' (or beginning of vector if no level '1' is found)
iter = --nestedVector.end();
lastNestedGroup.clear();
while (iter->_nestedLevel > 1 && iter != nestedVector.begin())
--iter;
}
else
{
iter = nestedVector.end();
}
// recreate lastNestedGroup, skip adjecent OPEN/CLOSE pairs
// nesting group is something like:
// "first delimiter 'nested delimiter 1 /*nested delimiter 2*/ delimiter 1 again' first delimiter again"
// if user is editing somewhere inside this group, than 'lastNestedGroup' provides info about nesting
// this is much more convinient that trying to obtain the same info from 'nestedVector'
vector<nestedInfo>::iterator last;
while (iter != nestedVector.end())
{
if (iter->_opener == NI_OPEN)
lastNestedGroup.push_back(*iter);
else if (iter->_opener == NI_CLOSE && !lastNestedGroup.empty())
{
last = --lastNestedGroup.end();
if (last->_opener == NI_OPEN)
if (last->_nestedLevel == iter->_nestedLevel)
if (last->_state == iter->_state)
if (last->_index == iter->_index)
lastNestedGroup.erase(last);
}
++iter;
}
if (!lastNestedGroup.empty())
{
last = --lastNestedGroup.end();
initStyle = last->_state;
openIndex = last->_index;
nestedLevel = last->_nestedLevel;
// are we nested somewhere in comment?
for (; ; --last)
{
if (last->_state == SCE_USER_STYLE_COMMENT)
{
isInComment = true;
isCommentLine = COMMENTLINE_YES;
}
if (last->_state == SCE_USER_STYLE_COMMENTLINE)
{
isCommentLine = COMMENTLINE_YES;
}
if (last == lastNestedGroup.begin())
break;
}
}
else
{
initStyle = SCE_USER_STYLE_IDENTIFIER;
openIndex = -1;
nestedLevel = 0;
}
// are we in fold block of comment lines?
int lineCurrent = styler.GetLine(startPos);
if ((styler.LevelAt(lineCurrent) & SC_ISCOMMENTLINE) != 0)
continueCommentBlock |= CL_CURRENT;
if (lineCurrent >= 1)
if ((styler.LevelAt(lineCurrent - 1) & SC_ISCOMMENTLINE) != 0)
continueCommentBlock |= CL_PREV;
if (lineCurrent >= 2)
if ((styler.LevelAt(lineCurrent - 2) & SC_ISCOMMENTLINE) != 0)
if (continueCommentBlock & CL_PREV)
continueCommentBlock |= CL_PREVPREV;
// foldVector.erase(foldVector.begin() + lineCurrent, foldVector.end());
}
static bool isInListForward(vvstring & openVector, StyleContext & sc, bool ignoreCase, int & openIndex, size_t & skipForward)
{
// forward check for standard (sigle part) keywords
skipForward = 0;
vector<vector<string>>::iterator iter1 = openVector.begin();
vector<string>::iterator iter2;
for (; iter1 != openVector.end(); ++iter1)
{
iter2 = iter1->begin();
for (; iter2 != iter1->end(); ++iter2)
{
if (ignoreCase?sc.MatchIgnoreCase2(iter2->c_str()):sc.Match(iter2->c_str()))
{
openIndex = iter1 - openVector.begin();
skipForward = iter2->length();
return true;
}
}
}
return false;
}
static bool isInListBackward(WordList & list, StyleContext & sc, bool specialMode, bool ignoreCase,
int & moveForward, vvstring * fwEndVectors[], int & nlCount, size_t docLength)
{
// backward search
// this function compares last identified 'word' (text surrounded by spaces or other forward keywords)
// with all keywords within 'WordList' object
// 'isInListBackward' can search for multi-part keywords too. Such keywords have variable length,
// in case 'isInListBackward' finds such keywords it will set 'moveForward' parameter so algorythm could adjust position
if (!list.Length())
return false;
moveForward = 0;
int offset = -1 * sc.LengthCurrent(); // length of 'word' that needs to be investigated
unsigned char a = 0; // iterator for user defined keywords
unsigned char b = 0; // iterator for text in the file
unsigned char bNext = 0;
unsigned char wsChar = 0;
unsigned char firstChar = sc.GetRelative(offset);
int fwDelimiterFound = NO_DELIMITER;
int nlCountTemp = 0;
int indexa = 0;
int indexb = 0;
int i = list.StartAt(firstChar);
bool doUpperLoop = ignoreCase;
if (ignoreCase)
{
i = list.StartAt(tolower(firstChar));
if (i == -1)
{
i = list.StartAt(toupper(firstChar));
if (i == -1)
return false;
doUpperLoop = false;
}
}
while (i >= 0)
{
while (static_cast<unsigned char>(ignoreCase?toupper(list.WordAt(i)[0]):list.WordAt(i)[0]) == (ignoreCase?toupper(firstChar):firstChar))
{
a = 0;
b = 0;
bNext = 0;
indexa = 0;
indexb = 0;
wsChar = 0;
fwDelimiterFound = NO_DELIMITER;
do
{
a = static_cast<unsigned char>(ignoreCase?toupper(list.WordAt(i)[indexa++]):list.WordAt(i)[indexa++]);
if (a == '\v' || a == '\b')
{
wsChar = a;
b = sc.GetRelative(offset + indexb++);
bNext = sc.GetRelative(offset + indexb);
if (isWhiteSpace2(b, nlCountTemp, wsChar, bNext))
{
do {
b = sc.GetRelative(offset + indexb++);
bNext = sc.GetRelative(offset + indexb);
}
while((sc.currentPos + offset + indexb) <= docLength && isWhiteSpace2(b, nlCountTemp, wsChar, bNext));
a = static_cast<unsigned char>(ignoreCase?toupper(list.WordAt(i)[indexa++]):list.WordAt(i)[indexa++]);
}
b = ignoreCase?toupper(b):b;
}
else
b = ignoreCase?toupper(sc.GetRelative(offset + indexb++)):sc.GetRelative(offset + indexb++);
}
while (a && (a == b));
if (!a)
{
--indexb; // decrement indexb to compensate for comparing with '\0' in previous loop
if (wsChar)
{
// multi-part keyword is found,
// but it must be followed by whitespace (or 'forward' keyword)
// otherwise "else if" might wrongly match "else iff"
bNext = sc.GetRelative(indexb + offset);
if (isWhiteSpace(bNext))
fwDelimiterFound = FORWARD_WHITESPACE_FOUND;
if (fwDelimiterFound == NO_DELIMITER)
{
if (isInListForward2(fwEndVectors, FW_VECTORS_TOTAL, sc, ignoreCase, indexb + offset))
{
fwDelimiterFound = FORWARD_KEYWORD_FOUND;
}
}
// special case when multi-part keywords have 'prefix' option enabled
// then the next word in the text file must be treated as part of multi-part keyword
// e.g. prefixed "else if" matches "else if nextWord", but not "else iffy"
if (specialMode)
{
if (fwDelimiterFound == FORWARD_WHITESPACE_FOUND) // there must be a white space !!
{
// skip whitespace (all of it)
int savedPosition = indexb; // return here if whitespace is not followed by another word
for (;;)
{
if ((sc.currentPos + offset + indexb) > docLength)
break;
if (!isWhiteSpace2(sc.GetRelative(offset + indexb), nlCountTemp, wsChar, sc.GetRelative(offset + indexb + 1)))
break;
++indexb;
}
// skip next "word" (if next word is not found, go back to end of multi-part keyword)
// it is not necessary to check EOF position here, because sc.GetRelative returns '\0' beyond EOF by default
// return ' ' on EOF instead to not loop indefinitely
bool nextWordFound = false;
while (!isWhiteSpace2(sc.GetRelative(indexb + offset, ' '), nlCountTemp, wsChar, sc.GetRelative(offset + indexb + 1, ' ')))
{
if (isInListForward2(fwEndVectors, FW_VECTORS_TOTAL, sc, ignoreCase, indexb + offset))
{
break;
}
++indexb;
nextWordFound = true;
}
if (nextWordFound == false)
indexb = savedPosition;
}
}
}
// keyword is read fully, decide if we can leave this function
nlCount += nlCountTemp;
moveForward = indexb + offset; // offset is already negative
if (wsChar)
{
if (fwDelimiterFound != NO_DELIMITER)
return true; // multi part keyword found
}
else if (moveForward == 0)
return true; // single part keyword found
else if (specialMode)
return true; // prefixed single part keyword found
}
nlCountTemp = 0;
++i;
}
// run one more time for capital letter version
if (doUpperLoop)
{
i = list.StartAt(toupper(firstChar));
doUpperLoop = false;
}
else
break;
}
return false;
}
static void setBackwards(WordList * kwLists[], StyleContext & sc, bool prefixes[], bool ignoreCase,
int nestedKey, vvstring * fwEndVectors[], int & levelMinCurrent,
int & levelNext, int & nlCount, bool & dontMove, size_t docLength)
{
if (sc.LengthCurrent() == 0)
return;
int folding = FOLD_NONE;
int moveForward = 0;
for (int i=0; i<MAPPER_TOTAL; ++i)
{
if (nestedKey & maskMapper[i])
{
if (isInListBackward(*kwLists[i], sc, prefixes[i], ignoreCase, moveForward, fwEndVectors, nlCount, docLength))
{
folding = foldingtMapper[i];
if (moveForward > 0)
{
sc.Forward(moveForward);
dontMove = true;
}
sc.ChangeState(styleMapper[i]);
break;
}
}
}
if (folding == FOLD_MIDDLE)
{
// treat middle point as a sequence of: FOLD_CLOSE followed by FOLD_OPEN
levelNext--;
folding = FOLD_OPEN;
}
if (folding == FOLD_OPEN)
{
if (levelMinCurrent > levelNext)
levelMinCurrent = levelNext;
levelNext++;
}
else if (folding == FOLD_CLOSE)
{
levelNext--;
}
}
static bool isInListNested(int nestedKey, vector<forwardStruct> & forwards, StyleContext & sc,
bool ignoreCase, int & openIndex, size_t & skipForward, int & newState, int pureLC,
bool visibleChars, vector<string> * numberTokens[], vvstring ** numberDelims, int decSeparator)
{
// check if some other delimiter is nested within current delimiter
// all delimiters are freely checked but line comments must be synched with property 'pureLC'
int backup = openIndex;
vector<forwardStruct>::iterator iter = forwards.begin();
for (; iter != forwards.end(); ++iter)
{
if (nestedKey & iter->_maskID)
{
if ((iter->_maskID != SCE_USER_MASK_NESTING_COMMENT_LINE) ||
(iter->_maskID == SCE_USER_MASK_NESTING_COMMENT_LINE &&
((pureLC == PURE_LC_NONE) ||
(pureLC == PURE_LC_BOL && (sc.chPrev == '\r' || sc.chPrev == '\n')) ||
(pureLC == PURE_LC_WSP && visibleChars == false))))
{
if (isInListForward(*(iter->_vec), sc, ignoreCase, openIndex, skipForward))
{
newState = iter->_sceID;
return true;
}
}
}
}
if (nestedKey & SCE_USER_MASK_NESTING_NUMBERS)
{
if (IsNumber(sc, numberTokens, numberDelims, ignoreCase, decSeparator, skipForward))
{
newState = SCE_USER_STYLE_NUMBER;
return true;
}
}
openIndex = backup;
return false;
}
static void readLastNested(vector<nestedInfo> & lastNestedGroup, int & newState, int & openIndex)
{
// after delimiter ends we need to determine whether we are entering some other delimiter (in case of nesting)
// or do we simply start over from default style.
newState = SCE_USER_STYLE_IDENTIFIER;
openIndex = -1;
if (!lastNestedGroup.empty())
{
lastNestedGroup.erase(lastNestedGroup.end()-1);
if (!lastNestedGroup.empty())
{
newState = (--lastNestedGroup.end())->_state;
openIndex = (--lastNestedGroup.end())->_index;
}
}
}
static void ColouriseUserDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *kwLists[], Accessor &styler)
{
bool foldComments = styler.GetPropertyInt("userDefine.allowFoldOfComments", 0) != 0;
bool ignoreCase = styler.GetPropertyInt("userDefine.isCaseIgnored", 0) != 0;
bool foldCompact = styler.GetPropertyInt("userDefine.foldCompact", 0) != 0;
int pureLC = styler.GetPropertyInt("userDefine.forcePureLC", 0);
bool prefixes[MAPPER_TOTAL];
for (int i=0; i<MAPPER_TOTAL; ++i) // only KEYWORDS1-8 can be prefixed
prefixes[i] = false;
// positions are hardcoded and they must be in synch with positions in "styleMapper" array!!
prefixes[7] = styler.GetPropertyInt("userDefine.prefixKeywords1", 0) != 0;
prefixes[8] = styler.GetPropertyInt("userDefine.prefixKeywords2", 0) != 0;
prefixes[9] = styler.GetPropertyInt("userDefine.prefixKeywords3", 0) != 0;
prefixes[10] = styler.GetPropertyInt("userDefine.prefixKeywords4", 0) != 0;
prefixes[11] = styler.GetPropertyInt("userDefine.prefixKeywords5", 0) != 0;
prefixes[12] = styler.GetPropertyInt("userDefine.prefixKeywords6", 0) != 0;
prefixes[13] = styler.GetPropertyInt("userDefine.prefixKeywords7", 0) != 0;
prefixes[14] = styler.GetPropertyInt("userDefine.prefixKeywords8", 0) != 0;
char nestingBuffer[] = "userDefine.nesting.00"; // "00" is only a placeholder, the actual number is set by _itoa
_itoa(SCE_USER_STYLE_COMMENT, (nestingBuffer+20), 10); int commentNesting = styler.GetPropertyInt(nestingBuffer, 0);
_itoa(SCE_USER_STYLE_COMMENTLINE, (nestingBuffer+20), 10); int lineCommentNesting = styler.GetPropertyInt(nestingBuffer, 0);
_itoa(SCE_USER_STYLE_DELIMITER1, (nestingBuffer+19), 10); int delim1Nesting = styler.GetPropertyInt(nestingBuffer, 0); // one byte difference
_itoa(SCE_USER_STYLE_DELIMITER2, (nestingBuffer+19), 10); int delim2Nesting = styler.GetPropertyInt(nestingBuffer, 0); // for two-digit numbers
_itoa(SCE_USER_STYLE_DELIMITER3, (nestingBuffer+19), 10); int delim3Nesting = styler.GetPropertyInt(nestingBuffer, 0);
_itoa(SCE_USER_STYLE_DELIMITER4, (nestingBuffer+19), 10); int delim4Nesting = styler.GetPropertyInt(nestingBuffer, 0);
_itoa(SCE_USER_STYLE_DELIMITER5, (nestingBuffer+19), 10); int delim5Nesting = styler.GetPropertyInt(nestingBuffer, 0);
_itoa(SCE_USER_STYLE_DELIMITER6, (nestingBuffer+19), 10); int delim6Nesting = styler.GetPropertyInt(nestingBuffer, 0);
_itoa(SCE_USER_STYLE_DELIMITER7, (nestingBuffer+19), 10); int delim7Nesting = styler.GetPropertyInt(nestingBuffer, 0);
_itoa(SCE_USER_STYLE_DELIMITER8, (nestingBuffer+19), 10); int delim8Nesting = styler.GetPropertyInt(nestingBuffer, 0);
commentNesting |= SCE_USER_MASK_NESTING_FOLDERS_IN_COMMENT_OPEN
| SCE_USER_MASK_NESTING_FOLDERS_IN_COMMENT_MIDDLE
| SCE_USER_MASK_NESTING_FOLDERS_IN_COMMENT_CLOSE;
lineCommentNesting |= SCE_USER_MASK_NESTING_FOLDERS_IN_COMMENT_OPEN
| SCE_USER_MASK_NESTING_FOLDERS_IN_COMMENT_MIDDLE
| SCE_USER_MASK_NESTING_FOLDERS_IN_COMMENT_CLOSE;
const int bwNesting = SCE_USER_MASK_NESTING_KEYWORD1
| SCE_USER_MASK_NESTING_KEYWORD2
| SCE_USER_MASK_NESTING_KEYWORD3
| SCE_USER_MASK_NESTING_KEYWORD4
| SCE_USER_MASK_NESTING_KEYWORD5
| SCE_USER_MASK_NESTING_KEYWORD6
| SCE_USER_MASK_NESTING_KEYWORD7
| SCE_USER_MASK_NESTING_KEYWORD8
| SCE_USER_MASK_NESTING_OPERATORS2
| SCE_USER_MASK_NESTING_FOLDERS_IN_COMMENT_OPEN
| SCE_USER_MASK_NESTING_FOLDERS_IN_COMMENT_MIDDLE
| SCE_USER_MASK_NESTING_FOLDERS_IN_COMMENT_CLOSE
| SCE_USER_MASK_NESTING_FOLDERS_IN_CODE2_OPEN
| SCE_USER_MASK_NESTING_FOLDERS_IN_CODE2_MIDDLE
| SCE_USER_MASK_NESTING_FOLDERS_IN_CODE2_CLOSE;
// creation of vvstring (short for vector<vector<string>>) objects is expensive,
// therefore these objects are created only at beginning of file, and saved to
// global std::map objects udlKeywordsMap and nestedMap
int currentBufferID = styler.GetPropertyInt("userDefine.currentBufferID", 0);
if (nestedMap.find(currentBufferID) == nestedMap.end())
{
nestedMap[currentBufferID] = vector<nestedInfo>();
}
vector<nestedInfo> & nestedVector = nestedMap[currentBufferID];
int sUdlName = styler.GetPropertyInt("userDefine.udlName", 0);
if (udlKeywordsMap.find(sUdlName) == udlKeywordsMap.end())
{
udlKeywordsMap[sUdlName] = udlKeywordsMapStruct();
}
vvstring & commentLineOpen = udlKeywordsMap[sUdlName].commentLineOpen;
vvstring & commentLineContinue = udlKeywordsMap[sUdlName].commentLineContinue;
vvstring & commentLineClose = udlKeywordsMap[sUdlName].commentLineClose;
vvstring & commentOpen = udlKeywordsMap[sUdlName].commentOpen;
vvstring & commentClose = udlKeywordsMap[sUdlName].commentClose;
vvstring & delim1Open = udlKeywordsMap[sUdlName].delim1Open;
vvstring & delim1Escape = udlKeywordsMap[sUdlName].delim1Escape;
vvstring & delim1Close = udlKeywordsMap[sUdlName].delim1Close;
vvstring & delim2Open = udlKeywordsMap[sUdlName].delim2Open;
vvstring & delim2Escape = udlKeywordsMap[sUdlName].delim2Escape;
vvstring & delim2Close = udlKeywordsMap[sUdlName].delim2Close;
vvstring & delim3Open = udlKeywordsMap[sUdlName].delim3Open;
vvstring & delim3Escape = udlKeywordsMap[sUdlName].delim3Escape;
vvstring & delim3Close = udlKeywordsMap[sUdlName].delim3Close;
vvstring & delim4Open = udlKeywordsMap[sUdlName].delim4Open;
vvstring & delim4Escape = udlKeywordsMap[sUdlName].delim4Escape;
vvstring & delim4Close = udlKeywordsMap[sUdlName].delim4Close;
vvstring & delim5Open = udlKeywordsMap[sUdlName].delim5Open;
vvstring & delim5Escape = udlKeywordsMap[sUdlName].delim5Escape;
vvstring & delim5Close = udlKeywordsMap[sUdlName].delim5Close;
vvstring & delim6Open = udlKeywordsMap[sUdlName].delim6Open;
vvstring & delim6Escape = udlKeywordsMap[sUdlName].delim6Escape;
vvstring & delim6Close = udlKeywordsMap[sUdlName].delim6Close;
vvstring & delim7Open = udlKeywordsMap[sUdlName].delim7Open;
vvstring & delim7Escape = udlKeywordsMap[sUdlName].delim7Escape;
vvstring & delim7Close = udlKeywordsMap[sUdlName].delim7Close;
vvstring & delim8Open = udlKeywordsMap[sUdlName].delim8Open;
vvstring & delim8Escape = udlKeywordsMap[sUdlName].delim8Escape;
vvstring & delim8Close = udlKeywordsMap[sUdlName].delim8Close;
vvstring & operators1 = udlKeywordsMap[sUdlName].operators1;
vvstring & foldersInCode1Open = udlKeywordsMap[sUdlName].foldersInCode1Open;
vvstring & foldersInCode1Middle = udlKeywordsMap[sUdlName].foldersInCode1Middle;
vvstring & foldersInCode1Close = udlKeywordsMap[sUdlName].foldersInCode1Close;
vector<string> & prefixTokens1 = udlKeywordsMap[sUdlName].prefixTokens1;
vector<string> & prefixTokens2 = udlKeywordsMap[sUdlName].prefixTokens2;
vector<string> & extrasTokens1 = udlKeywordsMap[sUdlName].extrasTokens1;
vector<string> & extrasTokens2 = udlKeywordsMap[sUdlName].extrasTokens2;
vector<string> & suffixTokens1 = udlKeywordsMap[sUdlName].suffixTokens1;
vector<string> & suffixTokens2 = udlKeywordsMap[sUdlName].suffixTokens2;
vector<string> & rangeTokens = udlKeywordsMap[sUdlName].rangeTokens;
vector<string> & negativePrefixTokens1 = udlKeywordsMap[sUdlName].negativePrefixTokens1;
vector<string> & negativePrefixTokens2 = udlKeywordsMap[sUdlName].negativePrefixTokens2;
vector<string> & negativeExtrasTokens2 = udlKeywordsMap[sUdlName].negativeExtrasTokens2;
if (startPos == 0)
{
// in keyword list objects, put longer multi-part string first,
// e.g. "else if" should go in front of "else"
bool equal = true;
bool isMultiPart = false;
bool switchPerformed = true;
while (switchPerformed)
{
switchPerformed = false;
for (int i=0; i<MAPPER_TOTAL; ++i) // for each keyword list object
{
for (int j=0; j<kwLists[i]->Length(); ++j) // for each keyword within object
{
equal = true;
int z = 0;
for (; kwLists[i]->WordAt(j)[z]; ++z) // for each letter within keyword
{
if (kwLists[i]->WordAt(j+1)[z] == '\v' || kwLists[i]->WordAt(j+1)[z] == '\b')
isMultiPart = true;
if (kwLists[i]->WordAt(j)[z] != kwLists[i]->WordAt(j+1)[z])
{
equal = false;
break;
}
}
if (!isMultiPart) // is next word multi part keyword?
{
for (int k=0; kwLists[i]->WordAt(j+1)[k]; ++k)
{
if (kwLists[i]->WordAt(j+1)[k] == '\v' || kwLists[i]->WordAt(j+1)[k] == '\b')
{
isMultiPart = true;
break;
}
}
}
if (equal && isMultiPart && kwLists[i]->WordAt(j+1)[z]) // perform switch only if next word is longer !
{
const char * temp = kwLists[i]->WordAt(j);
kwLists[i]->SetWordAt(j, kwLists[i]->WordAt(j+1));
kwLists[i]->SetWordAt(j+1, temp);
switchPerformed = true;
}
}
}
}
// if this is BOF, re-generate stuff in global map objects (udlKeywordsMap and nestedMap)
const char * sFoldersInCode1Open = styler.pprops->Get("userDefine.foldersInCode1Open");
const char * sFoldersInCode1Middle = styler.pprops->Get("userDefine.foldersInCode1Middle");
const char * sFoldersInCode1Close = styler.pprops->Get("userDefine.foldersInCode1Close");
const char * sDelimiters = styler.pprops->Get("userDefine.delimiters");
const char * sOperators1 = styler.pprops->Get("userDefine.operators1");
const char * sComments = styler.pprops->Get("userDefine.comments");
// 'GenerateVector' converts strings into vvstring objects
GenerateVector(commentLineOpen, sComments, "00", 0);
GenerateVector(commentLineContinue, sComments, "01", commentLineOpen.size());
GenerateVector(commentLineClose, sComments, "02", commentLineOpen.size());
GenerateVector(commentOpen, sComments, "03", 0);
GenerateVector(commentClose, sComments, "04", commentOpen.size());
GenerateVector(delim1Open, sDelimiters, "00", 0);
GenerateVector(delim1Escape, sDelimiters, "01", delim1Open.size());
GenerateVector(delim1Close, sDelimiters, "02", delim1Open.size());
GenerateVector(delim2Open, sDelimiters, "03", 0);
GenerateVector(delim2Escape, sDelimiters, "04", delim2Open.size());
GenerateVector(delim2Close, sDelimiters, "05", delim2Open.size());
GenerateVector(delim3Open, sDelimiters, "06", 0);
GenerateVector(delim3Escape, sDelimiters, "07", delim3Open.size());
GenerateVector(delim3Close, sDelimiters, "08", delim3Open.size());
GenerateVector(delim4Open, sDelimiters, "09", 0);
GenerateVector(delim4Escape, sDelimiters, "10", delim4Open.size());
GenerateVector(delim4Close, sDelimiters, "11", delim4Open.size());
GenerateVector(delim5Open, sDelimiters, "12", 0);
GenerateVector(delim5Escape, sDelimiters, "13", delim5Open.size());
GenerateVector(delim5Close, sDelimiters, "14", delim5Open.size());
GenerateVector(delim6Open, sDelimiters, "15", 0);
GenerateVector(delim6Escape, sDelimiters, "16", delim6Open.size());
GenerateVector(delim6Close, sDelimiters, "17", delim6Open.size());
GenerateVector(delim7Open, sDelimiters, "18", 0);
GenerateVector(delim7Escape, sDelimiters, "19", delim7Open.size());
GenerateVector(delim7Close, sDelimiters, "20", delim7Open.size());
GenerateVector(delim8Open, sDelimiters, "21", 0);
GenerateVector(delim8Escape, sDelimiters, "22", delim8Open.size());
GenerateVector(delim8Close, sDelimiters, "23", delim8Open.size());
operators1.clear();
foldersInCode1Open.clear();
foldersInCode1Middle.clear();
foldersInCode1Close.clear();
SubGroup(sFoldersInCode1Open, foldersInCode1Open, true);
SubGroup(sFoldersInCode1Middle, foldersInCode1Middle, true);
SubGroup(sFoldersInCode1Close, foldersInCode1Close, true);
SubGroup(sOperators1, operators1, true);
char * numberPrefix1 = (char *)styler.pprops->Get("userDefine.numberPrefix1");
char * numberPrefix2 = (char *)styler.pprops->Get("userDefine.numberPrefix2");
char * numberExtras1 = (char *)styler.pprops->Get("userDefine.numberExtras1");
char * numberExtras2 = (char *)styler.pprops->Get("userDefine.numberExtras2");
char * numberSuffix1 = (char *)styler.pprops->Get("userDefine.numberSuffix1");
char * numberSuffix2 = (char *)styler.pprops->Get("userDefine.numberSuffix2");
char * numberRange = (char *)styler.pprops->Get("userDefine.numberRange");
prefixTokens1.clear();
prefixTokens2.clear();
extrasTokens1.clear();
extrasTokens2.clear();
suffixTokens1.clear();
suffixTokens2.clear();
rangeTokens.clear();
negativePrefixTokens1.clear();
negativePrefixTokens2.clear();
negativeExtrasTokens2.clear();
// 'StringToVector' converts strings into vector<string> objects
StringToVector(numberPrefix1, prefixTokens1);
StringToVector(numberPrefix1, negativePrefixTokens1, true);
StringToVector(numberPrefix2, prefixTokens2);
StringToVector(numberPrefix2, negativePrefixTokens2, true);
StringToVector(numberExtras1, extrasTokens1);
StringToVector(numberExtras2, extrasTokens2);
StringToVector(numberExtras2, negativeExtrasTokens2, true);
StringToVector(numberSuffix1, suffixTokens1);
StringToVector(numberSuffix2, suffixTokens2);
StringToVector(numberRange, rangeTokens);
}
// forward strings are actually kept in forwardStruct's, this allows easy access to ScintillaID and MaskID
// FWS is a single global object used only to create temporary forwardStruct objects that are copied into vector
vector<forwardStruct> forwards;
forwards.push_back(*FWS.Set(&delim1Open, SCE_USER_STYLE_DELIMITER1, SCE_USER_MASK_NESTING_DELIMITER1));
forwards.push_back(*FWS.Set(&delim2Open, SCE_USER_STYLE_DELIMITER2, SCE_USER_MASK_NESTING_DELIMITER2));
forwards.push_back(*FWS.Set(&delim3Open, SCE_USER_STYLE_DELIMITER3, SCE_USER_MASK_NESTING_DELIMITER3));
forwards.push_back(*FWS.Set(&delim4Open, SCE_USER_STYLE_DELIMITER4, SCE_USER_MASK_NESTING_DELIMITER4));
forwards.push_back(*FWS.Set(&delim5Open, SCE_USER_STYLE_DELIMITER5, SCE_USER_MASK_NESTING_DELIMITER5));
forwards.push_back(*FWS.Set(&delim6Open, SCE_USER_STYLE_DELIMITER6, SCE_USER_MASK_NESTING_DELIMITER6));
forwards.push_back(*FWS.Set(&delim7Open, SCE_USER_STYLE_DELIMITER7, SCE_USER_MASK_NESTING_DELIMITER7));
forwards.push_back(*FWS.Set(&delim8Open, SCE_USER_STYLE_DELIMITER8, SCE_USER_MASK_NESTING_DELIMITER8));
forwards.push_back(*FWS.Set(&commentOpen, SCE_USER_STYLE_COMMENT, SCE_USER_MASK_NESTING_COMMENT));
forwards.push_back(*FWS.Set(&commentLineOpen, SCE_USER_STYLE_COMMENTLINE, SCE_USER_MASK_NESTING_COMMENT_LINE));
forwards.push_back(*FWS.Set(&operators1, SCE_USER_STYLE_OPERATOR, SCE_USER_MASK_NESTING_OPERATORS1));
// keep delimiter open strings in an array for easier looping
vvstring * delimStart[SCE_USER_TOTAL_DELIMITERS];
delimStart[0] = &delim1Open;
delimStart[1] = &delim2Open;
delimStart[2] = &delim3Open;
delimStart[3] = &delim4Open;
delimStart[4] = &delim5Open;
delimStart[5] = &delim6Open;
delimStart[6] = &delim7Open;
delimStart[7] = &delim8Open;
vvstring * fwEndVectors[FW_VECTORS_TOTAL]; // array of forward end vectors for multi-part forward search
fwEndVectors[0] = &operators1;
fwEndVectors[1] = &commentLineOpen;
fwEndVectors[2] = &commentLineContinue;
fwEndVectors[3] = &commentLineClose;
fwEndVectors[4] = &commentOpen;
fwEndVectors[5] = &commentClose;
fwEndVectors[6] = &foldersInCode1Open;
fwEndVectors[7] = &foldersInCode1Middle;
fwEndVectors[8] = &foldersInCode1Close;
fwEndVectors[9] = &delim1Close;
fwEndVectors[10] = &delim2Close;
fwEndVectors[11] = &delim3Close;
fwEndVectors[12] = &delim4Close;
fwEndVectors[13] = &delim5Close;
fwEndVectors[14] = &delim6Close;
fwEndVectors[15] = &delim7Close;
fwEndVectors[16] = &delim8Close;
// keep delimiter escape/close strings in an array for easier looping
vvstring * delimVectors[(SCE_USER_TOTAL_DELIMITERS+2) * 2];
delimVectors[0] = &delim1Escape;
delimVectors[1] = &delim1Close;
delimVectors[2] = &delim2Escape;
delimVectors[3] = &delim2Close;
delimVectors[4] = &delim3Escape;
delimVectors[5] = &delim3Close;
delimVectors[6] = &delim4Escape;
delimVectors[7] = &delim4Close;
delimVectors[8] = &delim5Escape;
delimVectors[9] = &delim5Close;
delimVectors[10] = &delim6Escape;
delimVectors[11] = &delim6Close;
delimVectors[12] = &delim7Escape;
delimVectors[13] = &delim7Close;
delimVectors[14] = &delim8Escape;
delimVectors[15] = &delim8Close;
// last four are needed just to create numberDelimSeparators
// they are not used anywhere else
delimVectors[16] = NULL;;
delimVectors[17] = &commentClose;
delimVectors[18] = NULL;
delimVectors[19] = NULL;
// again, loops make our lifes easier
int delimNestings[SCE_USER_TOTAL_DELIMITERS+2];
delimNestings[0] = delim1Nesting;
delimNestings[1] = delim2Nesting;
delimNestings[2] = delim3Nesting;
delimNestings[3] = delim4Nesting;
delimNestings[4] = delim5Nesting;
delimNestings[5] = delim6Nesting;
delimNestings[6] = delim7Nesting;
delimNestings[7] = delim8Nesting;
// last two are needed just to create numberDelimSeparators
// they are not used anywhere else
delimNestings[8] = commentNesting;
delimNestings[9] = lineCommentNesting;
vvstring * numberDelimSeparators[SCE_USER_TOTAL_DELIMITERS+6][SCE_USER_TOTAL_DELIMITERS+6]; // TODO: define hardcoded values as constants (syncy also for FW_VECTORS_TOTAL)
for (int i=0; i<SCE_USER_TOTAL_DELIMITERS+2; ++i)
{
numberDelimSeparators[i][0] = delimVectors[i*2 + 1];
numberDelimSeparators[i][1] = (delimNestings[i] & SCE_USER_MASK_NESTING_DELIMITER1) ? delimStart[0] : NULL;
numberDelimSeparators[i][2] = (delimNestings[i] & SCE_USER_MASK_NESTING_DELIMITER2) ? delimStart[1] : NULL;
numberDelimSeparators[i][3] = (delimNestings[i] & SCE_USER_MASK_NESTING_DELIMITER3) ? delimStart[2] : NULL;
numberDelimSeparators[i][4] = (delimNestings[i] & SCE_USER_MASK_NESTING_DELIMITER4) ? delimStart[3] : NULL;
numberDelimSeparators[i][5] = (delimNestings[i] & SCE_USER_MASK_NESTING_DELIMITER5) ? delimStart[4] : NULL;
numberDelimSeparators[i][6] = (delimNestings[i] & SCE_USER_MASK_NESTING_DELIMITER6) ? delimStart[5] : NULL;
numberDelimSeparators[i][7] = (delimNestings[i] & SCE_USER_MASK_NESTING_DELIMITER7) ? delimStart[6] : NULL;
numberDelimSeparators[i][8] = (delimNestings[i] & SCE_USER_MASK_NESTING_DELIMITER8) ? delimStart[7] : NULL;
numberDelimSeparators[i][9] = (delimNestings[i] & SCE_USER_MASK_NESTING_COMMENT) ? &commentOpen : NULL;
numberDelimSeparators[i][10] = (delimNestings[i] & SCE_USER_MASK_NESTING_COMMENT_LINE) ? &commentLineOpen : NULL;
numberDelimSeparators[i][11] = (delimNestings[i] & SCE_USER_MASK_NESTING_OPERATORS1) ? &operators1 : NULL;
}
vector<string> * numberTokens[10];
numberTokens[0] = &prefixTokens1;
numberTokens[1] = &prefixTokens2;
numberTokens[2] = &extrasTokens1;
numberTokens[3] = &extrasTokens2;
numberTokens[4] = &suffixTokens1;
numberTokens[5] = &suffixTokens2;
numberTokens[6] = &rangeTokens;
numberTokens[7] = &negativePrefixTokens1;
numberTokens[8] = &negativePrefixTokens2;
numberTokens[9] = &negativeExtrasTokens2;
int levelCurrent = SC_FOLDLEVELBASE;
int lineCurrent = 0;
int levelMinCurrent = 0;
int levelNext = 0;
int levelPrev = 0;
int lev = 0;
bool visibleChars = false;
bool skipVisibleCheck = false;
bool dontMove = false;
bool finished = true;
int checkEOL = EOL_DEFAULT_VALUE;
int nestedLevel = 0;
int openIndex = 0;
size_t skipForward = 0;
int prevState = 0;
int isCommentLine = COMMENTLINE_NO;
int isPrevLineComment = COMMENTLINE_NO;
bool isInCommentBlock = false;
bool isInComment = false;
int newState = 0;
int nlCount = 0;
int continueCommentBlock = 0;
int decSeparator = SEPARATOR_DOT;
vector<nestedInfo> lastNestedGroup;
vvstring * delimEscape = NULL;
vvstring * delimClose = NULL;
vvstring ** numberDelims = NULL;
int delimNesting = 0;
size_t docLength = startPos + length;
if (startPos == 0)
{
// foldVector.clear();
nestedVector.clear();
lastNestedGroup.clear();
initStyle = SCE_USER_STYLE_IDENTIFIER;
}
else
{
size_t oldStartPos = startPos;
ReColoringCheck(startPos, nestedLevel, initStyle, openIndex, isCommentLine, isInComment,
styler, lastNestedGroup, nestedVector, /* foldVector, */ continueCommentBlock);
// offset move to previous line
length += (oldStartPos - startPos);
docLength = startPos + length;
}
lineCurrent = styler.GetLine(startPos);
if (lineCurrent > 0)
levelCurrent = styler.LevelAt(lineCurrent - 1) >> 16;
levelMinCurrent = levelCurrent;
levelNext = levelCurrent;
StyleContext sc(startPos, length, initStyle, styler);
for (; finished; )
{
dontMove = false;
checkEOL = EOL_DEFAULT_VALUE;
if (sc.More() == false)
finished = false; // colorize last word, even if file does not end with whitespace char
switch (sc.state)
{
case SCE_USER_STYLE_DELIMITER1:
case SCE_USER_STYLE_DELIMITER2:
case SCE_USER_STYLE_DELIMITER3:
case SCE_USER_STYLE_DELIMITER4:
case SCE_USER_STYLE_DELIMITER5:
case SCE_USER_STYLE_DELIMITER6:
case SCE_USER_STYLE_DELIMITER7:
case SCE_USER_STYLE_DELIMITER8:
{
int index = sc.state - SCE_USER_STYLE_DELIMITER1;
delimEscape = delimVectors[index*2];
delimClose = delimVectors[index*2 + 1];
delimNesting = delimNestings[index];
numberDelims = numberDelimSeparators[index];
prevState = sc.state;
newState = sc.state;
// first, check escape sequence
bool loopEscape = true;
vector<string>::iterator iter;
while (loopEscape == true)
{
loopEscape = false;
iter = (*delimEscape)[openIndex].begin();
for (; iter != (*delimEscape)[openIndex].end(); ++iter)
{
if (ignoreCase?sc.MatchIgnoreCase2(iter->c_str()):sc.Match(iter->c_str()))
{
sc.Forward(iter->length() + 1); // escape is found, skip escape string and one char after it.
loopEscape = true;
//break;
}
}
}
// second, check end of delimiter sequence
iter = (*delimClose)[openIndex].begin();
for (; iter != (*delimClose)[openIndex].end(); ++iter)
{
if (ignoreCase ? sc.MatchIgnoreCase2(iter->c_str()):sc.Match(iter->c_str()))
{
// record end of delimiter sequence (NI_CLOSE)
nestedVector.push_back(*NI.Set(sc.currentPos + iter->length() - 1, nestedLevel--, openIndex, sc.state, NI_CLOSE));
// is there anything on the left side? (any backward keyword 'glued' with end of delimiter sequence)
setBackwards(kwLists, sc, prefixes, ignoreCase, delimNesting, fwEndVectors, levelMinCurrent, levelNext, nlCount, dontMove, docLength);
// paint backward keyword
sc.SetState(prevState);
// was current delimiter sequence nested, or do we start over from SCE_USER_STYLE_IDENTIFIER?
readLastNested(lastNestedGroup, newState, openIndex);
// for delimiters that end with ((EOL))
if (newState != SCE_USER_STYLE_COMMENTLINE || (sc.ch != '\r' && sc.ch != '\n'))
sc.Forward(iter->length());
if (sc.atLineStart)
checkEOL = EOL_FORCE_CHECK;
else if (sc.atLineEnd)
checkEOL = EOL_SKIP_CHECK;
// paint end of delimiter sequence
sc.SetState(newState);
dontMove = true;
break; // break out of 'for', not 'case'
}
}
// out of current state?
if (prevState != newState)
break;
// quick replacement for SCE_USER_STYLE_DEFAULT (important for nested keywords)
if (isWhiteSpace(sc.ch) && !isWhiteSpace(sc.chPrev))
{
setBackwards(kwLists, sc, prefixes, ignoreCase, delimNesting, fwEndVectors, levelMinCurrent, levelNext, nlCount, dontMove, docLength);
sc.SetState(prevState);
}
else if ((!isWhiteSpace(sc.ch) && isWhiteSpace(sc.chPrev)))
{
// create new 'compare point' (AKA beginning of nested keyword) before checking for numbers
sc.SetState(prevState);
}
// third, check nested delimiter sequence
if (isInListNested(delimNesting, forwards, sc, ignoreCase, openIndex, skipForward,
newState, pureLC, visibleChars, numberTokens, numberDelims, decSeparator))
{
// any backward keyword 'glued' on the left side?
setBackwards(kwLists, sc, prefixes, ignoreCase, delimNesting, fwEndVectors, levelMinCurrent, levelNext, nlCount, dontMove, docLength);
if (newState != SCE_USER_STYLE_OPERATOR && newState != SCE_USER_STYLE_NUMBER)
{
// record delimiter sequence in BOTH vectors
nestedVector.push_back(*NI.Set(sc.currentPos, ++nestedLevel, openIndex, newState, NI_OPEN));
lastNestedGroup.push_back(NI);
}
sc.SetState(newState); // yes, both 'SetState' calls are needed
sc.Forward(skipForward);
sc.SetState(newState);
if (newState == SCE_USER_STYLE_OPERATOR || newState == SCE_USER_STYLE_NUMBER)
sc.ChangeState(prevState);
dontMove = true;
break;
}
break;
}
case SCE_USER_STYLE_COMMENT:
{
numberDelims = numberDelimSeparators[SCE_USER_TOTAL_DELIMITERS];
// first, check end of comment sequence
vector<string>::iterator iter = commentClose[openIndex].begin();
for (; iter != commentClose[openIndex].end(); ++iter)
{
if (ignoreCase?sc.MatchIgnoreCase2(iter->c_str()):sc.Match(iter->c_str()))
{
// record end of comment sequence (NI_CLOSE)
nestedVector.push_back(*NI.Set(sc.currentPos + iter->length() - 1, nestedLevel--, openIndex, SCE_USER_STYLE_COMMENT, NI_CLOSE));
// is there anything on the left side? (any backward keyword 'glued' with end of comment sequence)
setBackwards(kwLists, sc, prefixes, ignoreCase, commentNesting, fwEndVectors, levelMinCurrent, levelNext, nlCount, dontMove, docLength);
// paint backward keyword and move on
sc.SetState(SCE_USER_STYLE_COMMENT);
sc.Forward(iter->length());
// was current comment sequence nested, or do we start over from SCE_USER_STYLE_IDENTIFIER?
readLastNested(lastNestedGroup, newState, openIndex);
// paint end of comment sequence
sc.SetState(newState);
isInComment = false;
dontMove = true;
break;
}
}
if (sc.state != SCE_USER_STYLE_COMMENT)
break;
// quick replacement for SCE_USER_STYLE_DEFAULT (important for nested keywords)
if (isWhiteSpace(sc.ch) && !isWhiteSpace(sc.chPrev))
{
setBackwards(kwLists, sc, prefixes, ignoreCase, commentNesting, fwEndVectors, levelMinCurrent, levelNext, nlCount, dontMove, docLength);
sc.SetState(SCE_USER_STYLE_COMMENT);
}
else if (!isWhiteSpace(sc.ch) && isWhiteSpace(sc.chPrev))
{
// create new 'compare point' (AKA beginning of nested keyword) before checking for numbers
sc.SetState(SCE_USER_STYLE_COMMENT);
}
// third, check nested delimiter sequence
if (isInListNested(commentNesting, forwards, sc, ignoreCase, openIndex, skipForward,
newState, pureLC, visibleChars, numberTokens, numberDelims, decSeparator))
{
// any backward keyword 'glued' on the left side?
setBackwards(kwLists, sc, prefixes, ignoreCase, commentNesting, fwEndVectors, levelMinCurrent, levelNext, nlCount, dontMove, docLength);
if (newState != SCE_USER_STYLE_OPERATOR && newState != SCE_USER_STYLE_NUMBER)
{
// record delimiter sequence in BOTH vectors
nestedVector.push_back(*NI.Set(sc.currentPos, ++nestedLevel, openIndex, newState, NI_OPEN));
lastNestedGroup.push_back(NI);
}
sc.SetState(newState); // yes, both 'SetState' calls are needed
sc.Forward(skipForward);
sc.SetState(newState);
if (newState == SCE_USER_STYLE_OPERATOR || newState == SCE_USER_STYLE_NUMBER)
sc.ChangeState(SCE_USER_STYLE_COMMENT);
dontMove = true;
break;
}
break;
}
case SCE_USER_STYLE_COMMENTLINE:
{
numberDelims = numberDelimSeparators[SCE_USER_TOTAL_DELIMITERS + 1];
// first, check end of line comment sequence (in rare cases when line comments can end before new line char)
vector<string>::iterator iter = commentLineClose[openIndex].begin();
for (; iter != commentLineClose[openIndex].end(); ++iter)
{
if (ignoreCase?sc.MatchIgnoreCase2(iter->c_str()):sc.Match(iter->c_str()))
{
// record end of line comment sequence (NI_CLOSE)
nestedVector.push_back(*NI.Set(sc.currentPos + iter->length() - 1, nestedLevel--, openIndex, SCE_USER_STYLE_COMMENTLINE, NI_CLOSE));
// is there anything on the left side? (any backward keyword 'glued' with end of line comment sequence)
setBackwards(kwLists, sc, prefixes, ignoreCase, lineCommentNesting, fwEndVectors, levelMinCurrent, levelNext, nlCount, dontMove, docLength);
// paint backward keyword and move on
sc.SetState(SCE_USER_STYLE_COMMENTLINE);
sc.Forward(iter->length());
// was current line comment sequence nested, or do we start over from SCE_USER_STYLE_IDENTIFIER?
readLastNested(lastNestedGroup, newState, openIndex);
// paint end of line comment sequence
sc.SetState(newState);
dontMove = true;
break; // break out of 'for', not 'case'
}
}
if (sc.state != SCE_USER_STYLE_COMMENTLINE)
break;
// quick replacement for SCE_USER_STYLE_DEFAULT (important for nested keywords)
if (isWhiteSpace(sc.ch) && !isWhiteSpace(sc.chPrev))
{
setBackwards(kwLists, sc, prefixes, ignoreCase, lineCommentNesting, fwEndVectors, levelMinCurrent, levelNext, nlCount, dontMove, docLength);
sc.SetState(SCE_USER_STYLE_COMMENTLINE);
}
else if (!isWhiteSpace(sc.ch) && isWhiteSpace(sc.chPrev))
{
// create new 'compare point' (AKA beginning of nested keyword) before checking for numbers
sc.SetState(SCE_USER_STYLE_COMMENTLINE);
}
// second, check line comment continuation
if (sc.atLineEnd)
{
bool lineContinuation = false;
int offset = 0;
if (sc.chPrev == '\r')
offset = 1;
vector<string>::iterator iter = commentLineContinue[openIndex].begin();
for (; iter != commentLineContinue[openIndex].end(); ++iter)
{
size_t length = iter->length();
if (length == 0)
{
if (!dontMove)
sc.Forward();
continue;
}
lineContinuation = true;
for (size_t i = 0; i < length; ++i)
{
if (ignoreCase)
{
if (toupper((*iter)[i]) != toupper(styler.SafeGetCharAt(sc.currentPos - length + i - offset, 0)))
{
lineContinuation = false;
break;
}
}
else if ((*iter)[i] != styler.SafeGetCharAt(sc.currentPos - length + i - offset, 0))
{
lineContinuation = false;
break;
}
}
// if line comment continuation string is found at EOL, treat next line as a comment line
if (lineContinuation)
{
isCommentLine = COMMENTLINE_YES;
break; // break out of 'for', not 'case'
}
}
sc.ChangeState(SCE_USER_STYLE_COMMENTLINE); // no need to paint, only change state for now
if (!lineContinuation)
{
// paint \n character too (or \r for old MAc format)
sc.Forward();
dontMove = true;
checkEOL = EOL_FORCE_CHECK;
// record end of line comment sequence (NI_CLOSE)
nestedVector.push_back(*NI.Set(sc.currentPos - 1, nestedLevel--, openIndex, SCE_USER_STYLE_COMMENTLINE, NI_CLOSE));
// was current line comment sequence nested, or do we start over from SCE_USER_STYLE_IDENTIFIER?
readLastNested(lastNestedGroup, newState, openIndex);
// paint entire line comment sequence in one step
sc.SetState(newState);
}
lineContinuation = false;
break;
}
if (sc.state != SCE_USER_STYLE_COMMENTLINE)
break;
// third, check nested delimiter sequence
if (isInListNested(lineCommentNesting, forwards, sc, ignoreCase, openIndex, skipForward,
newState, pureLC, visibleChars, numberTokens, numberDelims, decSeparator))
{
// any backward keyword 'glued' on the left side?
setBackwards(kwLists, sc, prefixes, ignoreCase, lineCommentNesting, fwEndVectors, levelMinCurrent, levelNext, nlCount, dontMove, docLength);
if (newState != SCE_USER_STYLE_OPERATOR && newState != SCE_USER_STYLE_NUMBER)
{
// record delimiter sequence in BOTH vectors
nestedVector.push_back(*NI.Set(sc.currentPos, ++nestedLevel, openIndex, newState, NI_OPEN));
lastNestedGroup.push_back(NI);
}
sc.SetState(newState); // yes, both 'SetState' calls are needed
sc.Forward(skipForward);
sc.SetState(newState);
if (newState == SCE_USER_STYLE_OPERATOR || newState == SCE_USER_STYLE_NUMBER)
sc.ChangeState(SCE_USER_STYLE_COMMENTLINE);
dontMove = true;
break;
}
break;
}
case SCE_USER_STYLE_DEFAULT:
{
if (isWhiteSpace(sc.ch))
{
setBackwards(kwLists, sc, prefixes, ignoreCase, bwNesting, fwEndVectors, levelMinCurrent, levelNext, nlCount, dontMove, docLength);
sc.SetState(SCE_USER_STYLE_IDENTIFIER);
break;
}
if (!commentLineOpen.empty())
{
if ((pureLC == PURE_LC_NONE) ||
(pureLC == PURE_LC_BOL && (sc.chPrev == '\r' || sc.chPrev == '\n')) ||
(pureLC == PURE_LC_WSP && visibleChars == false) )
{
if (isInListForward(commentLineOpen, sc, ignoreCase, openIndex, skipForward))
{
if (foldComments && isCommentLine != COMMENTLINE_SKIP_TESTING)
isCommentLine = COMMENTLINE_YES;
// any backward keyword 'glued' on the left side?
setBackwards(kwLists, sc, prefixes, ignoreCase, bwNesting, fwEndVectors, levelMinCurrent, levelNext, nlCount, dontMove, docLength);
// paint up to start of line comment sequence
sc.SetState(SCE_USER_STYLE_COMMENTLINE);
// record start of line comment sequence (NI_OPEN) in BOTH vectors
nestedVector.push_back(*NI.Set(sc.currentPos, ++nestedLevel, openIndex, SCE_USER_STYLE_COMMENTLINE, NI_OPEN));
lastNestedGroup.push_back(NI);
// paint start of line comment sequence
sc.Forward(skipForward);
sc.SetState(SCE_USER_STYLE_COMMENTLINE);
dontMove = true;
if (sc.atLineEnd)
checkEOL = EOL_SKIP_CHECK;
break;
}
}
}
if (!commentOpen.empty())
{
if (isInListForward(commentOpen, sc, ignoreCase, openIndex, skipForward))
{
if (foldComments)
{
isInComment = true;
if (isCommentLine != COMMENTLINE_SKIP_TESTING)
isCommentLine = COMMENTLINE_YES;
}
// any backward keyword 'glued' on the left side?
setBackwards(kwLists, sc, prefixes, ignoreCase, bwNesting, fwEndVectors, levelMinCurrent, levelNext, nlCount, dontMove, docLength);
// paint up to start of comment sequence
sc.SetState(SCE_USER_STYLE_COMMENT);
// record start of comment sequence (NI_OPEN) in BOTH nesting vectors
nestedVector.push_back(*NI.Set(sc.currentPos, ++nestedLevel, openIndex, SCE_USER_STYLE_COMMENT, NI_OPEN));
lastNestedGroup.push_back(NI);
// paint start of comment sequence
sc.Forward(skipForward);
sc.SetState(SCE_USER_STYLE_COMMENT);
dontMove = true;
if (sc.atLineEnd)
checkEOL = EOL_SKIP_CHECK;
break;
}
}
for (int i=0; i<SCE_USER_TOTAL_DELIMITERS; ++i)
{
if (!delimStart[i]->empty())
{
if (isInListForward(*delimStart[i], sc, ignoreCase, openIndex, skipForward))
{
// any backward keyword 'glued' on the left side?
setBackwards(kwLists, sc, prefixes, ignoreCase, bwNesting, fwEndVectors, levelMinCurrent, levelNext, nlCount, dontMove, docLength);
// paint up to start of delimiter sequence
sc.SetState(i+SCE_USER_STYLE_DELIMITER1);
// record start of delimiter sequence (NI_OPEN) in BOTH nesting vectors
nestedVector.push_back(*NI.Set(sc.currentPos, ++nestedLevel, openIndex, i+SCE_USER_STYLE_DELIMITER1, NI_OPEN));
lastNestedGroup.push_back(NI);
// paint start of delimiter sequence
sc.Forward(skipForward);
sc.SetState(i+SCE_USER_STYLE_DELIMITER1);
dontMove = true;
if (sc.atLineEnd)
checkEOL = EOL_SKIP_CHECK;
break; // break from nested 'for' loop, not 'case' statement
}
}
}
if (dontMove == true)
break; // delimiter start found, break from case SCE_USER_STYLE_DEFAULT
if (!operators1.empty())
{
if (isInListForward(operators1, sc, ignoreCase, openIndex, skipForward))
{
// any backward keyword 'glued' on the left side?
setBackwards(kwLists, sc, prefixes, ignoreCase, bwNesting, fwEndVectors, levelMinCurrent, levelNext, nlCount, dontMove, docLength);
// paint up to start of sequence
sc.SetState(SCE_USER_STYLE_OPERATOR);
// paint sequence
sc.Forward(skipForward);
//sc.ChangeState(SCE_USER_STYLE_OPERATOR);
// no closing sequence, start over from default
sc.SetState(SCE_USER_STYLE_IDENTIFIER);
dontMove = true;
if (sc.atLineEnd)
checkEOL = EOL_SKIP_CHECK;
break;
}
}
if (!foldersInCode1Open.empty())
{
if (isInListForward(foldersInCode1Open, sc, ignoreCase, openIndex, skipForward))
{
// any backward keyword 'glued' on the left side?
setBackwards(kwLists, sc, prefixes, ignoreCase, bwNesting, fwEndVectors, levelMinCurrent, levelNext, nlCount, dontMove, docLength);
// paint up to start of sequence
sc.SetState(SCE_USER_STYLE_FOLDER_IN_CODE1);
// paint sequence
sc.Forward(skipForward);
//sc.ChangeState(SCE_USER_STYLE_FOLDER_IN_CODE1);
// no closing sequence, start over from default
sc.SetState(SCE_USER_STYLE_IDENTIFIER);
dontMove = true;
if (sc.atLineEnd)
checkEOL = EOL_SKIP_CHECK;
if (levelMinCurrent > levelNext)
levelMinCurrent = levelNext;
levelNext++;
break;
}
}
if (!foldersInCode1Middle.empty())
{
if (isInListForward(foldersInCode1Middle, sc, ignoreCase, openIndex, skipForward))
{
// any backward keyword 'glued' on the left side?
setBackwards(kwLists, sc, prefixes, ignoreCase, bwNesting, fwEndVectors, levelMinCurrent, levelNext, nlCount, dontMove, docLength);
// paint up to start of sequence
sc.SetState(SCE_USER_STYLE_FOLDER_IN_CODE1);
// paint sequence
sc.Forward(skipForward);
//sc.ChangeState(SCE_USER_STYLE_FOLDER_IN_CODE1);
// no closing sequence, start over from default
sc.SetState(SCE_USER_STYLE_IDENTIFIER);
dontMove = true;
if (sc.atLineEnd)
checkEOL = EOL_SKIP_CHECK;
levelNext--;
if (levelMinCurrent > levelNext)
levelMinCurrent = levelNext;
levelNext++;
break;
}
}
if (!foldersInCode1Close.empty())
{
if (isInListForward(foldersInCode1Close, sc, ignoreCase, openIndex, skipForward))
{
// any backward keyword 'glued' on the left side?
setBackwards(kwLists, sc, prefixes, ignoreCase, bwNesting, fwEndVectors, levelMinCurrent, levelNext, nlCount, dontMove, docLength);
// paint up to start of sequence
sc.SetState(SCE_USER_STYLE_FOLDER_IN_CODE1);
// paint sequence
sc.Forward(skipForward);
//sc.ChangeState(SCE_USER_STYLE_FOLDER_IN_CODE1);
// no closing sequence, start over from default
sc.SetState(SCE_USER_STYLE_IDENTIFIER);
if (sc.atLineEnd)
checkEOL = EOL_SKIP_CHECK;
dontMove = true;
levelNext--;
break;
}
}
if (foldComments && isCommentLine != COMMENTLINE_SKIP_TESTING)
isCommentLine = COMMENTLINE_SKIP_TESTING;
break;
}
// determine if a new state should be entered.
case SCE_USER_STYLE_IDENTIFIER:
{
if (isWhiteSpace(sc.ch))
break;
if (IsNumber(sc, numberTokens, fwEndVectors, ignoreCase, decSeparator, skipForward))
{
// paint up to start of sequence
sc.SetState(SCE_USER_STYLE_NUMBER);
// paint sequence
sc.Forward(skipForward);
//sc.ChangeState(SCE_USER_STYLE_NUMBER);
// start over from default
sc.SetState(SCE_USER_STYLE_IDENTIFIER);
if (isWhiteSpace(sc.ch))
break;
}
if (!isWhiteSpace(sc.ch))// && isWhiteSpace(sc.chPrev)) // word start
{
sc.SetState(SCE_USER_STYLE_DEFAULT);
skipVisibleCheck = true;
dontMove = true;
break;
}
break;
}
default:
break;
}
if (foldComments)
if (isInComment == false)
if (isCommentLine == COMMENTLINE_NO)
if (sc.state != SCE_USER_STYLE_COMMENTLINE)
if (sc.state != SCE_USER_STYLE_IDENTIFIER)
if (sc.state != SCE_USER_STYLE_DEFAULT)
if (!isWhiteSpace(sc.ch))
isCommentLine = COMMENTLINE_SKIP_TESTING;
if (skipVisibleCheck == true)
skipVisibleCheck = false;
else if (visibleChars == false && !isWhiteSpace(sc.ch))
visibleChars = true;
if ((sc.atLineEnd == true && checkEOL != EOL_SKIP_CHECK) || (sc.atLineEnd == false && checkEOL == EOL_FORCE_CHECK))
{
if (foldComments == true)
{
if (levelCurrent != levelNext)
isCommentLine = COMMENTLINE_SKIP_TESTING;
if (continueCommentBlock > 0)
{
if (continueCommentBlock & CL_PREVPREV)
{
isInCommentBlock = true;
isPrevLineComment = COMMENTLINE_YES;
if (!(continueCommentBlock & CL_CURRENT))
{
levelNext++;
levelMinCurrent++;
levelCurrent++;
levelPrev = (levelMinCurrent | levelNext << 16) | SC_ISCOMMENTLINE;
}
}
else if (continueCommentBlock & CL_PREV)
{
isPrevLineComment = COMMENTLINE_YES;
if (continueCommentBlock & CL_CURRENT)
{
levelMinCurrent--;
levelNext--;
levelCurrent--;
levelPrev = (levelMinCurrent | levelNext << 16) | SC_ISCOMMENTLINE;
}
}
continueCommentBlock = 0;
}
if (isInCommentBlock && isCommentLine != COMMENTLINE_YES && isPrevLineComment == COMMENTLINE_YES)
{
levelNext--;
levelPrev = (levelMinCurrent | levelNext << 16) | SC_ISCOMMENTLINE;
levelMinCurrent--;
isInCommentBlock = false;
}
if (!isInCommentBlock && isCommentLine == COMMENTLINE_YES && isPrevLineComment == COMMENTLINE_YES)
{
levelNext++;
levelPrev = (levelMinCurrent | levelNext << 16) | SC_FOLDLEVELHEADERFLAG | SC_ISCOMMENTLINE;
levelMinCurrent = levelNext;
isInCommentBlock = true;
}
if (levelPrev != 0)
{
// foldVector[lineCurrent - 1] = levelPrev;
styler.SetLevel(lineCurrent - 1, levelPrev);
levelPrev = 0;
}
}
lev = levelMinCurrent | levelNext << 16;
if (foldComments && isCommentLine == COMMENTLINE_YES)
lev |= SC_ISCOMMENTLINE;
if (visibleChars == false && foldCompact)
lev |= SC_FOLDLEVELWHITEFLAG;
if (levelMinCurrent < levelNext)
lev |= SC_FOLDLEVELHEADERFLAG;
// foldVector.push_back(lev);
styler.SetLevel(lineCurrent, lev);
for (int i=0; i<nlCount; ++i) // multi-line multi-part keyword
{
// foldVector.push_back(levelNext | levelNext << 16); // TODO: what about SC_ISCOMMENTLINE?
styler.SetLevel(lineCurrent++, levelNext | levelNext << 16);
}
nlCount = 0;
lineCurrent++;
levelCurrent = levelNext;
levelMinCurrent = levelCurrent;
visibleChars = false;
if (foldComments)
{
isPrevLineComment = isCommentLine==COMMENTLINE_YES ? COMMENTLINE_YES:COMMENTLINE_NO;
isCommentLine = isInComment ? COMMENTLINE_YES:COMMENTLINE_NO;
}
}
if (!dontMove)
sc.Forward();
}
sc.Complete();
}
static void FoldUserDoc(Sci_PositionU /* startPos */, Sci_Position /* length */, int /*initStyle*/, WordList *[], Accessor & /* styler */)
{
// this function will not be used in final version of the code.
// it should remain commented out as it is useful for debugging purposes !!!
// perhaps ifdef block would be a wiser choice, but commenting out works just fine for the time being
// int lineCurrent = styler.GetLine(startPos);
// vector<int>::iterator iter = foldVectorStatic->begin() + lineCurrent;
// for (; iter != foldVectorStatic->end(); ++iter)
// {
// styler.SetLevel(lineCurrent++, *iter);
// }
}
static const char * const userDefineWordLists[] = {
"Primary keywords and identifiers",
"Secondary keywords and identifiers",
"Documentation comment keywords",
"Fold header keywords",
0,
};
extern const LexerModule lmUserDefine(SCLEX_USER, ColouriseUserDoc, "user", FoldUserDoc, userDefineWordLists);