📄 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 + -