📄 lexruby.cxx
字号:
bool preferRE = true;
int state = initStyle;
int lengthDoc = startPos + length;
char prevWord[MAX_KEYWORD_LENGTH + 1]; // 1 byte for zero
prevWord[0] = '\0';
if (length == 0)
return;
char chPrev = styler.SafeGetCharAt(startPos - 1);
char chNext = styler.SafeGetCharAt(startPos);
bool is_real_number = true; // Differentiate between constants and ?-sequences.
// Ruby uses a different mask because bad indentation is marked by oring with 32
styler.StartAt(startPos, 127);
styler.StartSegment(startPos);
static int q_states[] = {SCE_RB_STRING_Q,
SCE_RB_STRING_QQ,
SCE_RB_STRING_QR,
SCE_RB_STRING_QW,
SCE_RB_STRING_QW,
SCE_RB_STRING_QX};
static const char* q_chars = "qQrwWx";
// In most cases a value of 2 should be ample for the code in the
// Ruby library, and the code the user is likely to enter.
// For example,
// fu_output_message "mkdir #{options[:mode] ? ('-m %03o ' % options[:mode]) : ''}#{list.join ' '}"
// if options[:verbose]
// from fileutils.rb nests to a level of 2
// If the user actually hits a 6th occurrence of '#{' in a double-quoted
// string (including regex'es, %Q, %<sym>, %w, and other strings
// that interpolate), it will stay as a string. The problem with this
// is that quotes might flip, a 7th '#{' will look like a comment,
// and code-folding might be wrong.
// If anyone runs into this problem, I recommend raising this
// value slightly higher to replacing the fixed array with a linked
// list. Keep in mind this code will be called everytime the lexer
// is invoked.
#define INNER_STRINGS_MAX_COUNT 5
// These vars track our instances of "...#{,,,%Q<..#{,,,}...>,,,}..."
int inner_string_types[INNER_STRINGS_MAX_COUNT];
// Track # braces when we push a new #{ thing
int inner_expn_brace_counts[INNER_STRINGS_MAX_COUNT];
QuoteCls inner_quotes[INNER_STRINGS_MAX_COUNT];
int inner_string_count = 0;
int brace_counts = 0; // Number of #{ ... } things within an expression
int i;
for (i = 0; i < INNER_STRINGS_MAX_COUNT; i++) {
inner_string_types[i] = 0;
inner_expn_brace_counts[i] = 0;
}
for (i = startPos; i < lengthDoc; i++) {
char ch = chNext;
chNext = styler.SafeGetCharAt(i + 1);
char chNext2 = styler.SafeGetCharAt(i + 2);
if (styler.IsLeadByte(ch)) {
chNext = chNext2;
chPrev = ' ';
i += 1;
continue;
}
// skip on DOS/Windows
//No, don't, because some things will get tagged on,
// so we won't recognize keywords, for example
#if 0
if (ch == '\r' && chNext == '\n') {
continue;
}
#endif
if (HereDoc.State == 1 && isEOLChar(ch)) {
// Begin of here-doc (the line after the here-doc delimiter):
HereDoc.State = 2;
styler.ColourTo(i-1, state);
// Don't check for a missing quote, just jump into
// the here-doc state
state = SCE_RB_HERE_Q;
}
// Regular transitions
if (state == SCE_RB_DEFAULT) {
if (isSafeDigit(ch)) {
styler.ColourTo(i - 1, state);
state = SCE_RB_NUMBER;
is_real_number = true;
numDots = 0;
} else if (isHighBitChar(ch) || iswordstart(ch)) {
styler.ColourTo(i - 1, state);
state = SCE_RB_WORD;
} else if (ch == '#') {
styler.ColourTo(i - 1, state);
state = SCE_RB_COMMENTLINE;
} else if (ch == '=') {
// =begin indicates the start of a comment (doc) block
if (i == 0 || isEOLChar(chPrev)
&& chNext == 'b'
&& styler.SafeGetCharAt(i + 2) == 'e'
&& styler.SafeGetCharAt(i + 3) == 'g'
&& styler.SafeGetCharAt(i + 4) == 'i'
&& styler.SafeGetCharAt(i + 5) == 'n'
&& !isSafeWordcharOrHigh(styler.SafeGetCharAt(i + 6))) {
styler.ColourTo(i - 1, state);
state = SCE_RB_POD;
} else {
styler.ColourTo(i - 1, state);
styler.ColourTo(i, SCE_RB_OPERATOR);
preferRE = true;
}
} else if (ch == '"') {
styler.ColourTo(i - 1, state);
state = SCE_RB_STRING;
Quote.New();
Quote.Open(ch);
} else if (ch == '\'') {
styler.ColourTo(i - 1, state);
state = SCE_RB_CHARACTER;
Quote.New();
Quote.Open(ch);
} else if (ch == '`') {
styler.ColourTo(i - 1, state);
state = SCE_RB_BACKTICKS;
Quote.New();
Quote.Open(ch);
} else if (ch == '@') {
// Instance or class var
styler.ColourTo(i - 1, state);
if (chNext == '@') {
state = SCE_RB_CLASS_VAR;
advance_char(i, ch, chNext, chNext2); // pass by ref
} else {
state = SCE_RB_INSTANCE_VAR;
}
} else if (ch == '$') {
// Check for a builtin global
styler.ColourTo(i - 1, state);
// Recognize it bit by bit
state = SCE_RB_GLOBAL;
} else if (ch == '/' && preferRE) {
// Ambigous operator
styler.ColourTo(i - 1, state);
state = SCE_RB_REGEX;
Quote.New();
Quote.Open(ch);
} else if (ch == '<' && chNext == '<' && chNext2 != '=') {
// Recognise the '<<' symbol - either a here document or a binary op
styler.ColourTo(i - 1, state);
i++;
chNext = chNext2;
styler.ColourTo(i, SCE_RB_OPERATOR);
if (! (strchr("\"\'`_-", chNext2) || isSafeAlpha(chNext2))) {
// It's definitely not a here-doc,
// based on Ruby's lexer/parser in the
// heredoc_identifier routine.
// Nothing else to do.
} else if (preferRE) {
if (sureThisIsHeredoc(i - 1, styler, prevWord)) {
state = SCE_RB_HERE_DELIM;
HereDoc.State = 0;
}
// else leave it in default state
} else {
if (sureThisIsNotHeredoc(i - 1, styler)) {
// leave state as default
// We don't have all the heuristics Perl has for indications
// of a here-doc, because '<<' is overloadable and used
// for so many other classes.
} else {
state = SCE_RB_HERE_DELIM;
HereDoc.State = 0;
}
}
preferRE = (state != SCE_RB_HERE_DELIM);
} else if (ch == ':') {
styler.ColourTo(i - 1, state);
if (chNext == ':') {
// Mark "::" as an operator, not symbol start
styler.ColourTo(i + 1, SCE_RB_OPERATOR);
advance_char(i, ch, chNext, chNext2); // pass by ref
state = SCE_RB_DEFAULT;
preferRE = false;
} else if (isSafeWordcharOrHigh(chNext)) {
state = SCE_RB_SYMBOL;
} else if (strchr("[*!~+-*/%=<>&^|", chNext)) {
// Do the operator analysis in-line, looking ahead
// Based on the table in pickaxe 2nd ed., page 339
bool doColoring = true;
switch (chNext) {
case '[':
if (chNext2 == ']' ) {
char ch_tmp = styler.SafeGetCharAt(i + 3);
if (ch_tmp == '=') {
i += 3;
ch = ch_tmp;
chNext = styler.SafeGetCharAt(i + 1);
} else {
i += 2;
ch = chNext2;
chNext = ch_tmp;
}
} else {
doColoring = false;
}
break;
case '*':
if (chNext2 == '*') {
i += 2;
ch = chNext2;
chNext = styler.SafeGetCharAt(i + 1);
} else {
advance_char(i, ch, chNext, chNext2);
}
break;
case '!':
if (chNext2 == '=' || chNext2 == '~') {
i += 2;
ch = chNext2;
chNext = styler.SafeGetCharAt(i + 1);
} else {
advance_char(i, ch, chNext, chNext2);
}
break;
case '<':
if (chNext2 == '<') {
i += 2;
ch = chNext2;
chNext = styler.SafeGetCharAt(i + 1);
} else if (chNext2 == '=') {
char ch_tmp = styler.SafeGetCharAt(i + 3);
if (ch_tmp == '>') { // <=> operator
i += 3;
ch = ch_tmp;
chNext = styler.SafeGetCharAt(i + 1);
} else {
i += 2;
ch = chNext2;
chNext = ch_tmp;
}
} else {
advance_char(i, ch, chNext, chNext2);
}
break;
default:
// Simple one-character operators
advance_char(i, ch, chNext, chNext2);
break;
}
if (doColoring) {
styler.ColourTo(i, SCE_RB_SYMBOL);
state = SCE_RB_DEFAULT;
}
} else if (!preferRE) {
// Don't color symbol strings (yet)
// Just color the ":" and color rest as string
styler.ColourTo(i, SCE_RB_SYMBOL);
state = SCE_RB_DEFAULT;
} else {
styler.ColourTo(i, SCE_RB_OPERATOR);
state = SCE_RB_DEFAULT;
preferRE = true;
}
} else if (ch == '%') {
styler.ColourTo(i - 1, state);
bool have_string = false;
if (strchr(q_chars, chNext) && !isSafeWordcharOrHigh(chNext2)) {
Quote.New();
const char *hit = strchr(q_chars, chNext);
if (hit != NULL) {
state = q_states[hit - q_chars];
Quote.Open(chNext2);
i += 2;
ch = chNext2;
chNext = styler.SafeGetCharAt(i + 1);
have_string = true;
}
} else if (preferRE && !isSafeWordcharOrHigh(chNext)) {
// Ruby doesn't allow high bit chars here,
// but the editor host might
state = SCE_RB_STRING_QQ;
Quote.Open(chNext);
advance_char(i, ch, chNext, chNext2); // pass by ref
have_string = true;
}
if (!have_string) {
styler.ColourTo(i, SCE_RB_OPERATOR);
// stay in default
preferRE = true;
}
} else if (ch == '?') {
styler.ColourTo(i - 1, state);
if (iswhitespace(chNext) || chNext == '\n' || chNext == '\r') {
styler.ColourTo(i, SCE_RB_OPERATOR);
} else {
// It's the start of a character code escape sequence
// Color it as a number.
state = SCE_RB_NUMBER;
is_real_number = false;
}
} else if (isoperator(ch) || ch == '.') {
styler.ColourTo(i - 1, state);
styler.ColourTo(i, SCE_RB_OPERATOR);
// If we're ending an expression or block,
// assume it ends an object, and the ambivalent
// constructs are binary operators
//
// So if we don't have one of these chars,
// we aren't ending an object exp'n, and ops
// like : << / are unary operators.
if (ch == '{') {
++brace_counts;
preferRE = true;
} else if (ch == '}' && --brace_counts < 0
&& inner_string_count > 0) {
styler.ColourTo(i, SCE_RB_OPERATOR);
exitInnerExpression(inner_string_types,
inner_expn_brace_counts,
inner_quotes,
inner_string_count,
state, brace_counts, Quote);
} else {
preferRE = (strchr(")}].", ch) == NULL);
}
// Stay in default state
} else if (isEOLChar(ch)) {
// Make sure it's a true line-end, with no backslash
if ((ch == '\r' || (ch == '\n' && chPrev != '\r'))
&& chPrev != '\\') {
// Assume we've hit the end of the statement.
preferRE = true;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -