📄 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 targetstatic 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 heredocsstatic 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 constructstatic 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 + -