📄 lexruby.cxx
字号:
static int skipWhitespace(int startPos,
int endPos,
Accessor &styler) {
for (int i = startPos; i < endPos; i++) {
if (!iswhitespace(styler[i])) {
return i;
}
}
return endPos;
}
// This routine looks for false positives like
// undef foo, <<
// There aren't too many.
//
// iPrev points to the start of <<
static bool sureThisIsHeredoc(int iPrev,
Accessor &styler,
char *prevWord) {
// Not so fast, since Ruby's so dynamic. Check the context
// to make sure we're OK.
int prevStyle;
int lineStart = styler.GetLine(iPrev);
int lineStartPosn = styler.LineStart(lineStart);
styler.Flush();
// Find the first word after some whitespace
int firstWordPosn = skipWhitespace(lineStartPosn, iPrev, styler);
if (firstWordPosn >= iPrev) {
// Have something like {^ <<}
//XXX Look at the first previous non-comment non-white line
// to establish the context. Not too likely though.
return true;
} else {
switch (prevStyle = styler.StyleAt(firstWordPosn)) {
case SCE_RB_WORD:
case SCE_RB_WORD_DEMOTED:
case SCE_RB_IDENTIFIER:
break;
default:
return true;
}
}
int firstWordEndPosn = firstWordPosn;
char *dst = prevWord;
for (;;) {
if (firstWordEndPosn >= iPrev ||
styler.StyleAt(firstWordEndPosn) != prevStyle) {
*dst = 0;
break;
}
*dst++ = styler[firstWordEndPosn];
firstWordEndPosn += 1;
}
//XXX Write a style-aware thing to regex scintilla buffer objects
if (!strcmp(prevWord, "undef")
|| !strcmp(prevWord, "def")
|| !strcmp(prevWord, "alias")) {
// These keywords are what we were looking for
return false;
}
return true;
}
// Routine that saves us from allocating a buffer for the here-doc target
// targetEndPos points one past the end of the current target
static bool haveTargetMatch(int currPos,
int lengthDoc,
int targetStartPos,
int targetEndPos,
Accessor &styler) {
if (lengthDoc - currPos < targetEndPos - targetStartPos) {
return false;
}
int i, j;
for (i = targetStartPos, j = currPos;
i < targetEndPos && j < lengthDoc;
i++, j++) {
if (styler[i] != styler[j]) {
return false;
}
}
return true;
}
// We need a check because the form
// [identifier] <<[target]
// is ambiguous. The Ruby lexer/parser resolves it by
// looking to see if [identifier] names a variable or a
// function. If it's the first, it's the start of a here-doc.
// If it's a var, it's an operator. This lexer doesn't
// maintain a symbol table, so it looks ahead to see what's
// going on, in cases where we have
// ^[white-space]*[identifier([.|::]identifier)*][white-space]*<<[target]
//
// If there's no occurrence of [target] on a line, assume we don't.
// return true == yes, we have no heredocs
static bool sureThisIsNotHeredoc(int lt2StartPos,
Accessor &styler) {
int prevStyle;
// Use full document, not just part we're styling
int lengthDoc = styler.Length();
int lineStart = styler.GetLine(lt2StartPos);
int lineStartPosn = styler.LineStart(lineStart);
styler.Flush();
const bool definitely_not_a_here_doc = true;
const bool looks_like_a_here_doc = false;
// Find the first word after some whitespace
int firstWordPosn = skipWhitespace(lineStartPosn, lt2StartPos, styler);
if (firstWordPosn >= lt2StartPos) {
return definitely_not_a_here_doc;
}
prevStyle = styler.StyleAt(firstWordPosn);
// If we have '<<' following a keyword, it's not a heredoc
if (prevStyle != SCE_RB_IDENTIFIER) {
return definitely_not_a_here_doc;
}
int newStyle = prevStyle;
// Some compilers incorrectly warn about uninit newStyle
for (firstWordPosn += 1; firstWordPosn <= lt2StartPos; firstWordPosn += 1) {
// Inner loop looks at the name
for (; firstWordPosn <= lt2StartPos; firstWordPosn += 1) {
newStyle = styler.StyleAt(firstWordPosn);
if (newStyle != prevStyle) {
break;
}
}
// Do we have '::' or '.'?
if (firstWordPosn < lt2StartPos && newStyle == SCE_RB_OPERATOR) {
char ch = styler[firstWordPosn];
if (ch == '.') {
// yes
} else if (ch == ':') {
if (styler.StyleAt(++firstWordPosn) != SCE_RB_OPERATOR) {
return definitely_not_a_here_doc;
} else if (styler[firstWordPosn] != ':') {
return definitely_not_a_here_doc;
}
} else {
break;
}
} else {
break;
}
}
// Skip next batch of white-space
firstWordPosn = skipWhitespace(firstWordPosn, lt2StartPos, styler);
if (firstWordPosn != lt2StartPos) {
// Have [[^ws[identifier]ws[*something_else*]ws<<
return definitely_not_a_here_doc;
}
// OK, now 'j' will point to the current spot moving ahead
int j = firstWordPosn + 1;
if (styler.StyleAt(j) != SCE_RB_OPERATOR || styler[j] != '<') {
// This shouldn't happen
return definitely_not_a_here_doc;
}
int nextLineStartPosn = styler.LineStart(lineStart + 1);
if (nextLineStartPosn >= lengthDoc) {
return definitely_not_a_here_doc;
}
j = skipWhitespace(j + 1, nextLineStartPosn, styler);
if (j >= lengthDoc) {
return definitely_not_a_here_doc;
}
bool allow_indent;
int target_start, target_end;
// From this point on no more styling, since we're looking ahead
if (styler[j] == '-') {
allow_indent = true;
j++;
} else {
allow_indent = false;
}
// Allow for quoted targets.
char target_quote = 0;
switch (styler[j]) {
case '\'':
case '"':
case '`':
target_quote = styler[j];
j += 1;
}
if (isSafeAlnum(styler[j])) {
// Init target_end because some compilers think it won't
// be initialized by the time it's used
target_start = target_end = j;
j++;
} else {
return definitely_not_a_here_doc;
}
for (; j < lengthDoc; j++) {
if (!isSafeAlnum(styler[j])) {
if (target_quote && styler[j] != target_quote) {
// unquoted end
return definitely_not_a_here_doc;
}
// And for now make sure that it's a newline
// don't handle arbitrary expressions yet
target_end = j;
if (target_quote) {
// Now we can move to the character after the string delimiter.
j += 1;
}
j = skipWhitespace(j, lengthDoc, styler);
if (j >= lengthDoc) {
return definitely_not_a_here_doc;
} else {
char ch = styler[j];
if (ch == '#' || isEOLChar(ch)) {
// This is OK, so break and continue;
break;
} else {
return definitely_not_a_here_doc;
}
}
}
}
// Just look at the start of each line
int last_line = styler.GetLine(lengthDoc - 1);
// But don't go too far
if (last_line > lineStart + 50) {
last_line = lineStart + 50;
}
for (int line_num = lineStart + 1; line_num <= last_line; line_num++) {
if (allow_indent) {
j = skipWhitespace(styler.LineStart(line_num), lengthDoc, styler);
} else {
j = styler.LineStart(line_num);
}
// target_end is one past the end
if (haveTargetMatch(j, lengthDoc, target_start, target_end, styler)) {
// We got it
return looks_like_a_here_doc;
}
}
return definitely_not_a_here_doc;
}
//todo: if we aren't looking at a stdio character,
// move to the start of the first line that is not in a
// multi-line construct
static void synchronizeDocStart(unsigned int& startPos,
int &length,
int &initStyle,
Accessor &styler,
bool skipWhiteSpace=false) {
styler.Flush();
int style = actual_style(styler.StyleAt(startPos));
switch (style) {
case SCE_RB_STDIN:
case SCE_RB_STDOUT:
case SCE_RB_STDERR:
// Don't do anything else with these.
return;
}
int pos = startPos;
// Quick way to characterize each line
int lineStart;
for (lineStart = styler.GetLine(pos); lineStart > 0; lineStart--) {
// Now look at the style before the previous line's EOL
pos = styler.LineStart(lineStart) - 1;
if (pos <= 10) {
lineStart = 0;
break;
}
char ch = styler.SafeGetCharAt(pos);
char chPrev = styler.SafeGetCharAt(pos - 1);
if (ch == '\n' && chPrev == '\r') {
pos--;
}
if (styler.SafeGetCharAt(pos - 1) == '\\') {
// Continuation line -- keep going
} else if (actual_style(styler.StyleAt(pos)) != SCE_RB_DEFAULT) {
// Part of multi-line construct -- keep going
} else if (currLineContainsHereDelims(pos, styler)) {
// Keep going, with pos and length now pointing
// at the end of the here-doc delimiter
} else if (skipWhiteSpace && isEmptyLine(pos, styler)) {
// Keep going
} else {
break;
}
}
pos = styler.LineStart(lineStart);
length += (startPos - pos);
startPos = pos;
initStyle = SCE_RB_DEFAULT;
}
static void ColouriseRbDoc(unsigned int startPos, int length, int initStyle,
WordList *keywordlists[], Accessor &styler) {
// Lexer for Ruby often has to backtrack to start of current style to determine
// which characters are being used as quotes, how deeply nested is the
// start position and what the termination string is for here documents
WordList &keywords = *keywordlists[0];
class HereDocCls {
public:
int State;
// States
// 0: '<<' encountered
// 1: collect the delimiter
// 1b: text between the end of the delimiter and the EOL
// 2: here doc text (lines after the delimiter)
char Quote; // the char after '<<'
bool Quoted; // true if Quote in ('\'','"','`')
int DelimiterLength; // strlen(Delimiter)
char Delimiter[256]; // the Delimiter, limit of 256: from Perl
bool CanBeIndented;
HereDocCls() {
State = 0;
DelimiterLength = 0;
Delimiter[0] = '\0';
CanBeIndented = false;
}
};
HereDocCls HereDoc;
QuoteCls Quote;
int numDots = 0; // For numbers --
// Don't start lexing in the middle of a num
synchronizeDocStart(startPos, length, initStyle, styler, // ref args
false);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -