Автор: frs
Дата сообщения: 24.09.2008 00:52
Переписал блок фолдинга VB лексера на основе лексера au3, извиняюсь, что не удалил закомментированный мусор, т.к. на C я до этого не писал, возможно, что-то потребуется восстановить.
Блок раскрасски не трогал, может кто-нибуди пофиксит баг с непрокрашиванием последнего слова в файле, если после него идёт <eof>, моих знаний не хватило на поиск данного фикса в других лексерах.
LexVB.cxx : [more]
Код: // Scintilla source code edit control
/** @file LexVB.cxx
** Lexer for Visual Basic and VBScript.
**/
// Copyright 1998-2005 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <stdarg.h>
#include "Platform.h"
#include "PropSet.h"
#include "Accessor.h"
#include "StyleContext.h"
#include "KeyWords.h"
#include "Scintilla.h"
#include "SciLexer.h"
#ifdef SCI_NAMESPACE
using namespace Scintilla;
#endif
// Internal state, highlighted as number
#define SCE_B_FILENUMBER SCE_B_DEFAULT+100
/*static bool IsVBComment(Accessor &styler, int pos, int len) {
return len > 0 && styler[pos] == '\'';
}*/
static inline bool IsTypeCharacter(int ch) {
return ch == '%' || ch == '&' || ch == '@' || ch == '!' || ch == '#' || ch == '$';
}
// Extended to accept accented characters
static inline bool IsAWordChar(int ch) {
return ch >= 0x80 ||
(isalnum(ch) || ch == '.' || ch == '_');
}
static inline bool IsAWordStart(int ch) {
return ch >= 0x80 ||
(isalpha(ch) || ch == '_');
}
static inline bool IsANumberChar(int ch) {
// Not exactly following number definition (several dots are seen as OK, etc.)
// but probably enough in most cases.
return (ch < 0x80) &&
(isdigit(ch) || toupper(ch) == 'E' ||
ch == '.' || ch == '-' || ch == '+');
}
static void ColouriseVBDoc(unsigned int startPos, int length, int initStyle,
WordList *keywordlists[], Accessor &styler, bool vbScriptSyntax) {
WordList &keywords = *keywordlists[0];
WordList &keywords2 = *keywordlists[1];
WordList &keywords3 = *keywordlists[2];
WordList &keywords4 = *keywordlists[3];
styler.StartAt(startPos);
int visibleChars = 0;
int fileNbDigits = 0;
// Do not leak onto next line
if (initStyle == SCE_B_STRINGEOL || initStyle == SCE_B_COMMENT || initStyle == SCE_B_PREPROCESSOR) {
initStyle = SCE_B_DEFAULT;
}
StyleContext sc(startPos, length, initStyle, styler);
for (; sc.More(); sc.Forward()) {
if (sc.state == SCE_B_OPERATOR) {
sc.SetState(SCE_B_DEFAULT);
} else if (sc.state == SCE_B_IDENTIFIER) {
if (!IsAWordChar(sc.ch)) {
// In Basic (except VBScript), a variable name or a function name
// can end with a special character indicating the type of the value
// held or returned.
bool skipType = false;
if (!vbScriptSyntax && IsTypeCharacter(sc.ch)) {
sc.Forward(); // Skip it
skipType = true;
}
if (sc.ch == ']') {
sc.Forward();
}
char s[100];
sc.GetCurrentLowered(s, sizeof(s));
if (skipType) {
s[no][[/no]strlen(s) - 1] = '\0';
}
if (strcmp(s, "rem") == 0) {
sc.ChangeState(SCE_B_COMMENT);
} else {
if (keywords.InList(s)) {
sc.ChangeState(SCE_B_KEYWORD);
} else if (keywords2.InList(s)) {
sc.ChangeState(SCE_B_KEYWORD2);
} else if (keywords3.InList(s)) {
sc.ChangeState(SCE_B_KEYWORD3);
} else if (keywords4.InList(s)) {
sc.ChangeState(SCE_B_KEYWORD4);
//!-start-[VBLexerImprovement]
} else if (keywordlists[4]->len) {
for (int wl = 4; wl < KEYWORDSET_MAX; wl++) {
if (keywordlists[wl]->InList(s)) {
sc.ChangeState(SCE_B_KEYWORD4 + wl - 3);
break;
}
}
//!-end-[VBLexerImprovement]
} // Else, it is really an identifier...
sc.SetState(SCE_B_DEFAULT);
}
}
} else if (sc.state == SCE_B_NUMBER) {
// We stop the number definition on non-numerical non-dot non-eE non-sign char
// Also accepts A-F for hex. numbers
if (!IsANumberChar(sc.ch) && !(tolower(sc.ch) >= 'a' && tolower(sc.ch) <= 'f')) {
sc.SetState(SCE_B_DEFAULT);
}
} else if (sc.state == SCE_B_STRING) {
// VB doubles quotes to preserve them, so just end this string
// state now as a following quote will start again
if (sc.ch == '\"') {
if (sc.chNext == '\"') {
sc.Forward();
} else {
if (tolower(sc.chNext) == 'c') {
sc.Forward();
}
sc.ForwardSetState(SCE_B_DEFAULT);
}
} else if (sc.atLineEnd) {
visibleChars = 0;
sc.ChangeState(SCE_B_STRINGEOL);
sc.ForwardSetState(SCE_B_DEFAULT);
}
} else if (sc.state == SCE_B_COMMENT) {
if (sc.atLineEnd) {
visibleChars = 0;
sc.ForwardSetState(SCE_B_DEFAULT);
}
} else if (sc.state == SCE_B_PREPROCESSOR) {
if (sc.atLineEnd) {
visibleChars = 0;
sc.ForwardSetState(SCE_B_DEFAULT);
}
} else if (sc.state == SCE_B_FILENUMBER) {
if (IsADigit(sc.ch)) {
fileNbDigits++;
if (fileNbDigits > 3) {
sc.ChangeState(SCE_B_DATE);
}
} else if (sc.ch == '\r' || sc.ch == '\n' || sc.ch == ',') {
// Regular uses: Close #1; Put #1, ...; Get #1, ... etc.
// Too bad if date is format #27, Oct, 2003# or something like that...
// Use regular number state
sc.ChangeState(SCE_B_NUMBER);
sc.SetState(SCE_B_DEFAULT);
} else if (sc.ch == '#') {
sc.ChangeState(SCE_B_DATE);
sc.ForwardSetState(SCE_B_DEFAULT);
} else {
sc.ChangeState(SCE_B_DATE);
}
if (sc.state != SCE_B_FILENUMBER) {
fileNbDigits = 0;
}
} else if (sc.state == SCE_B_DATE) {
if (sc.atLineEnd) {
visibleChars = 0;
sc.ChangeState(SCE_B_STRINGEOL);
sc.ForwardSetState(SCE_B_DEFAULT);
} else if (sc.ch == '#') {
sc.ForwardSetState(SCE_B_DEFAULT);
}
}
if (sc.state == SCE_B_DEFAULT) {
if (sc.ch == '\'') {
sc.SetState(SCE_B_COMMENT);
} else if (sc.ch == '\"') {
sc.SetState(SCE_B_STRING);
} else if (sc.ch == '#' && visibleChars == 0) {
// Preprocessor commands are alone on their line
sc.SetState(SCE_B_PREPROCESSOR);
} else if (sc.ch == '#') {
// It can be a date literal, ending with #, or a file number, from 1 to 511
// The date literal depends on the locale, so anything can go between #'s.
// Can be #January 1, 1993# or #1 Jan 93# or #05/11/2003#, etc.
// So we set the FILENUMBER state, and switch to DATE if it isn't a file number
sc.SetState(SCE_B_FILENUMBER);
} else if (sc.ch == '&' && tolower(sc.chNext) == 'h') {
// Hexadecimal number
sc.SetState(SCE_B_NUMBER);
sc.Forward();
} else if (sc.ch == '&' && tolower(sc.chNext) == 'o') {
// Octal number
sc.SetState(SCE_B_NUMBER);
sc.Forward();
} else if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
sc.SetState(SCE_B_NUMBER);
} else if (IsAWordStart(sc.ch) || (sc.ch == '[')) {
sc.SetState(SCE_B_IDENTIFIER);
} else if (isoperator(static_cast<char>(sc.ch)) || (sc.ch == '\\')) { // Integer division
sc.SetState(SCE_B_OPERATOR);
}
}
if (sc.atLineEnd) {
visibleChars = 0;
}
if (!IsASpace(sc.ch)) {
visibleChars++;
}
}
sc.Complete();
}
static bool IsStreamCommentStyle(int style) {
return style == SCE_B_COMMENT;
}
//
// Routine to find first none space on the current line and return its Style
// needed for comment lines not starting on pos 1
static int GetStyleFirstWord(unsigned int szLine, Accessor &styler)
{
int nsPos = styler.LineStart(szLine);
int nePos = styler.LineStart(szLine+1) - 1;
while (isspacechar(styler.SafeGetCharAt(nsPos)) && nsPos < nePos)
{
nsPos++; // skip to next char
} // End While
return styler.StyleAt(nsPos);
} // GetStyleFirstWord()
//
// Routine to check the last "none comment" character on a line to see if its a continuation
//
static bool IsContinuationLine(unsigned int szLine, Accessor &styler)
{
int nsPos = styler.LineStart(szLine);
int nePos = styler.LineStart(szLine+1) - 2;
//int stylech = styler.StyleAt(nsPos);
while (nsPos < nePos)
{
//stylech = styler.StyleAt(nePos);
int stylech = styler.StyleAt(nsPos);
if (!(stylech == SCE_B_COMMENT)) {
char ch = styler.SafeGetCharAt(nePos);
if (!isspacechar(ch)) {
if (ch == '_')
return true;
else
return false;
}
}
nePos--; // skip to next char
} // End While
return false;
} // IsContinuationLine()
static void FoldVBDoc(unsigned int startPos, int length, int, WordList *[], Accessor &styler)
{
int endPos = startPos + length;
//***
// get settings from the config files for folding comments and preprocessor lines
bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
bool foldInComment = styler.GetPropertyInt("fold.comment") == 2;
bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
bool foldpreprocessor = styler.GetPropertyInt("fold.preprocessor") != 0;
//***
// Backtrack to previous line in case need to fix its fold status
int lineCurrent = styler.GetLine(startPos);
if (startPos > 0) {
if (lineCurrent > 0) {
lineCurrent--;
startPos = styler.LineStart(lineCurrent);
}
}
//============from au3
// vars for style of previous/current/next lines
int style = GetStyleFirstWord(lineCurrent,styler);
int stylePrev = 0;
// find the first previous line without continuation character at the end
while ((lineCurrent > 0 && IsContinuationLine(lineCurrent,styler)) ||
(lineCurrent > 1 && IsContinuationLine(lineCurrent-1,styler))) {
lineCurrent--;
startPos = styler.LineStart(lineCurrent);
}
if (lineCurrent > 0) {
stylePrev = GetStyleFirstWord(lineCurrent-1,styler);
}
// vars for getting first word to check for keywords
bool FirstWordStart = false;
bool FirstWordEnd = false;
char szKeyword[11]="";
int szKeywordlen = 0;
char szThen[5]="";
int szThenlen = 0;
bool ThenFoundLast = false;
// var for indentlevel
int levelCurrent = SC_FOLDLEVELBASE;
if (lineCurrent > 0)
levelCurrent = styler.LevelAt(lineCurrent-1) >> 16;
int levelNext = levelCurrent;
//
int visibleChars = 0;
char chNext = styler.SafeGetCharAt(startPos);
char chPrev = ' ';
//
for (int i = startPos; i < endPos; i++) {
char ch = chNext;
chNext = styler.SafeGetCharAt(i + 1);
if (IsAWordChar(ch)) {
visibleChars++;
}
// get the syle for the current character neede to check in comment
int stylech = styler.StyleAt(i);
// get first word for the line for indent check max 9 characters
if (FirstWordStart && (!(FirstWordEnd))) {
if (!IsAWordChar(ch)) {
FirstWordEnd = true;
szKeyword[szKeywordlen] = '\0';
}
else {
if (szKeywordlen < 10) {
szKeyword[szKeywordlen++] = static_cast<char>(tolower(ch));
}
}
}
// start the capture of the first word
if (!(FirstWordStart)) {
if (IsAWordChar(ch) || IsAWordStart(ch) || ch == '\'') {
FirstWordStart = true;
szKeyword[szKeywordlen++] = static_cast<char>(tolower(ch));
}
}
// only process this logic when not in comment section
if (!(stylech == SCE_B_COMMENT)) {
if (ThenFoundLast) {
if (IsAWordChar(ch)) {
ThenFoundLast = false;
}
}
// find out if the word "then" is the last on a "if" line
if (FirstWordEnd && strcmp(szKeyword,"if") == 0) {
if (szThenlen == 4) {
szThen[0] = szThen[1];
szThen[1] = szThen[2];
szThen[2] = szThen[3];
szThen[3] = static_cast<char>(tolower(ch));
if (strcmp(szThen,"then") == 0 ) {
ThenFoundLast = true;
}
}
else {
szThen[szThenlen++] = static_cast<char>(tolower(ch));
if (szThenlen == 5) {
szThen[4] = '\0';
}
}
}
}
// End of Line found so process the information
if ((ch == '\r' && chNext != '\n') || (ch == '\n') || (i == endPos)) {
// **************************
// Folding logic for Keywords
// **************************
// if a keyword is found on the current line and the line doesn't end with _ (continuation)
// and we are not inside a commentblock.
if (szKeywordlen > 0 && (!(chPrev == '_')) &&
((!(IsStreamCommentStyle(style)) || foldInComment)) ) {
szKeyword[szKeywordlen] = '\0';
// only fold "if" last keyword is "then" (else its a one line if)
if (strcmp(szKeyword,"if") == 0 && ThenFoundLast) {
levelNext++;
}
// create new fold for these words
if (strcmp(szKeyword,"do") == 0 || strcmp(szKeyword,"for") == 0 ||
strcmp(szKeyword,"function") == 0 || strcmp(szKeyword,"while") == 0||
strcmp(szKeyword,"with") == 0 || strcmp(szKeyword,"sub") == 0 ) {
levelNext++;
}
// create double Fold for select&switch because Case will subtract one of the current level
//if (strcmp(szKeyword,"select") == 0 || strcmp(szKeyword,"switch") == 0) {
if (strcmp(szKeyword,"select") == 0) {
levelNext++;
}
// end the fold for these words before the current line
if (strcmp(szKeyword,"end") == 0 || strcmp(szKeyword,"next") == 0 ||
strcmp(szKeyword,"until") == 0 ||
strcmp(szKeyword,"loop") == 0 || strcmp(szKeyword,"wend") == 0){
levelNext--;
levelCurrent--;
}
// end the fold for these words before the current line and Start new fold
if (strcmp(szKeyword,"case") == 0 || strcmp(szKeyword,"else") == 0 ||
strcmp(szKeyword,"elseif") == 0 ) {
levelCurrent--;
}
// end the double fold for this word before the current line
/*if (strcmp(szKeyword,"endselect") == 0 || strcmp(szKeyword,"endswitch") == 0 ) {
levelNext--;
levelNext--;
levelCurrent--;
levelCurrent--;
}*/
// end the fold for these words on the current line
/*if (strcmp(szKeyword,"#endregion") == 0 ) {
levelNext--;
}*/
}
// Preprocessor and Comment folding
int styleNext = GetStyleFirstWord(lineCurrent + 1,styler);
// *************************************
// Folding logic for preprocessor blocks
// *************************************
// process preprosessor line
if (foldpreprocessor && style == SCE_B_PREPROCESSOR) {
if (!(stylePrev == SCE_B_PREPROCESSOR) && (styleNext == SCE_B_PREPROCESSOR)) {
levelNext++;
}
// fold till the last line for normal comment lines
else if (stylePrev == SCE_B_PREPROCESSOR && !(styleNext == SCE_B_PREPROCESSOR)) {
levelNext--;
}
}
// *********************************
// Folding logic for Comment blocks
// *********************************
if (foldComment && IsStreamCommentStyle(style)) {
// Start of a comment block
if (!(stylePrev==style) && IsStreamCommentStyle(styleNext) && styleNext==style) {
levelNext++;
}
// fold till the last line for normal comment lines
else if (IsStreamCommentStyle(stylePrev)
&& !(styleNext == SCE_B_COMMENT)
&& stylePrev == SCE_B_COMMENT
&& style == SCE_B_COMMENT) {
levelNext--;
}
// fold till the one but last line for Blockcomment lines
/*else if (IsStreamCommentStyle(stylePrev)
// && !(styleNext == SCE_B_COMMENTBLOCK)
// && style == SCE_B_COMMENTBLOCK
)
{
levelNext--;
levelCurrent--;
}*/
}
int levelUse = levelCurrent;
int lev = levelUse | levelNext << 16;
if (visibleChars == 0 && foldCompact)
lev |= SC_FOLDLEVELWHITEFLAG;
if (levelUse < levelNext) {
lev |= SC_FOLDLEVELHEADERFLAG;
}
if (lev != styler.LevelAt(lineCurrent)) {
styler.SetLevel(lineCurrent, lev);
}
// reset values for the next line
lineCurrent++;
stylePrev = style;
style = styleNext;
levelCurrent = levelNext;
visibleChars = 0;
// if the last character is an Underscore then don't reset since the line continues on the next line.
if (!(chPrev == '_')) {
szKeywordlen = 0;
szThenlen = 0;
FirstWordStart = false;
FirstWordEnd = false;
ThenFoundLast = false;
}
}
// save the last processed character
if (!isspacechar(ch)) {
chPrev = ch;
visibleChars++;
}
}
//===========old=================
/*int spaceFlags = 0;
int indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags, IsVBComment);
char chNext = styler[no][[/no]startPos];
for (int i = startPos; i < endPos; i++) {
char ch = chNext;
chNext = styler.SafeGetCharAt(i + 1);
if ((ch == '\r' && chNext != '\n') || (ch == '\n') || (i == endPos)) {
int lev = indentCurrent;
int indentNext = styler.IndentAmount(lineCurrent + 1, &spaceFlags, IsVBComment);
if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG)) {
// Only non whitespace lines can be headers
if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext & SC_FOLDLEVELNUMBERMASK)) {
lev |= SC_FOLDLEVELHEADERFLAG;
} else if (indentNext & SC_FOLDLEVELWHITEFLAG) {
// Line after is blank so check the next - maybe should continue further?
int spaceFlags2 = 0;
int indentNext2 = styler.IndentAmount(lineCurrent + 2, &spaceFlags2, IsVBComment);
if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext2 & SC_FOLDLEVELNUMBERMASK)) {
lev |= SC_FOLDLEVELHEADERFLAG;
}
}
}
indentCurrent = indentNext;
styler.SetLevel(lineCurrent, lev);
lineCurrent++;
}
}*/
}
static void ColouriseVBNetDoc(unsigned int startPos, int length, int initStyle,
WordList *keywordlists[], Accessor &styler) {
ColouriseVBDoc(startPos, length, initStyle, keywordlists, styler, false);
}
static void ColouriseVBScriptDoc(unsigned int startPos, int length, int initStyle,
WordList *keywordlists[], Accessor &styler) {
ColouriseVBDoc(startPos, length, initStyle, keywordlists, styler, true);
}
static const char * const vbWordListDesc[] = {
"Keywords",
"user1",
"user2",
"user3",
0
};
LexerModule lmVB(SCLEX_VB, ColouriseVBNetDoc, "vb", FoldVBDoc, vbWordListDesc);
LexerModule lmVBScript(SCLEX_VBSCRIPT, ColouriseVBScriptDoc, "vbscript", FoldVBDoc, vbWordListDesc);