2333 lines
94 KiB
C++
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);
|