📄 lexruby.cxx
字号:
// for now, handle as comment styler.ColourTo(i - 1, state); bool inEscape = false; while (++i < lengthDoc) { ch = styler.SafeGetCharAt(i); if (ch == '\\') { inEscape = true; } else if (isEOLChar(ch)) { // Comment inside a regex styler.ColourTo(i - 1, SCE_RB_COMMENTLINE); break; } else if (inEscape) { inEscape = false; // don't look at char } else if (ch == Quote.Down) { // Have the regular handler deal with this // to get trailing modifiers. i--; ch = styler[i]; break; } } chNext = styler.SafeGetCharAt(i + 1); chNext2 = styler.SafeGetCharAt(i + 2); } // Quotes of all kinds... } else if (state == SCE_RB_STRING_Q || state == SCE_RB_STRING_QQ || state == SCE_RB_STRING_QX || state == SCE_RB_STRING_QW || state == SCE_RB_STRING || state == SCE_RB_CHARACTER || state == SCE_RB_BACKTICKS) { if (!Quote.Down && !isspacechar(ch)) { Quote.Open(ch); } else if (ch == '\\' && Quote.Up != '\\') { //Riddle me this: Is it safe to skip *every* escaped char? advance_char(i, ch, chNext, chNext2); } else if (ch == Quote.Down) { Quote.Count--; if (Quote.Count == 0) { styler.ColourTo(i, state); state = SCE_RB_DEFAULT; preferRE = false; } } else if (ch == Quote.Up) { Quote.Count++; } } if (state == SCE_RB_ERROR) { break; } chPrev = ch; } if (state == SCE_RB_WORD) { // We've ended on a word, possibly at EOF, and need to // classify it. (void) ClassifyWordRb(styler.GetStartSegment(), lengthDoc - 1, keywords, styler, prevWord); } else { styler.ColourTo(lengthDoc - 1, state); }}// Helper functions for folding, disambiguation keywords// Assert that there are no high-bit chars static void getPrevWord(int pos, char *prevWord, Accessor &styler, int word_state){ int i; styler.Flush(); for (i = pos - 1; i > 0; i--) { if (actual_style(styler.StyleAt(i)) != word_state) { i++; break; } } if (i < pos - MAX_KEYWORD_LENGTH) // overflow i = pos - MAX_KEYWORD_LENGTH; char *dst = prevWord; for (; i <= pos; i++) { *dst++ = styler[i]; } *dst = 0;}static bool keywordIsAmbiguous(const char *prevWord){ // Order from most likely used to least likely // Lots of ways to do a loop in Ruby besides 'while/until' if (!strcmp(prevWord, "if") || !strcmp(prevWord, "do") || !strcmp(prevWord, "while") || !strcmp(prevWord, "unless") || !strcmp(prevWord, "until")) { return true; } else { return false; }}// Demote keywords in the following conditions:// if, while, unless, until modify a statement// do after a while or until, as a noise word (like then after if) static bool keywordIsModifier(const char *word, int pos, Accessor &styler){ if (word[0] == 'd' && word[1] == 'o' && !word[2]) { return keywordDoStartsLoop(pos, styler); } char ch; int style = SCE_RB_DEFAULT; int lineStart = styler.GetLine(pos); int lineStartPosn = styler.LineStart(lineStart); styler.Flush(); while (--pos >= lineStartPosn) { style = actual_style(styler.StyleAt(pos)); if (style == SCE_RB_DEFAULT) { if (iswhitespace(ch = styler[pos])) { //continue } else if (ch == '\r' || ch == '\n') { // Scintilla's LineStart() and GetLine() routines aren't // platform-independent, so if we have text prepared with // a different system we can't rely on it. return false; } } else { break; } } if (pos < lineStartPosn) { return false; //XXX not quite right if the prev line is a continuation } // First things where the action is unambiguous switch (style) { case SCE_RB_DEFAULT: case SCE_RB_COMMENTLINE: case SCE_RB_POD: case SCE_RB_CLASSNAME: case SCE_RB_DEFNAME: case SCE_RB_MODULE_NAME: return false; case SCE_RB_OPERATOR: break; case SCE_RB_WORD: // Watch out for uses of 'else if' //XXX: Make a list of other keywords where 'if' isn't a modifier // and can appear legitimately // Formulate this to avoid warnings from most compilers if (strcmp(word, "if") == 0) { char prevWord[MAX_KEYWORD_LENGTH + 1]; getPrevWord(pos, prevWord, styler, SCE_RB_WORD); return strcmp(prevWord, "else") != 0; } return true; default: return true; } // Assume that if the keyword follows an operator, // usually it's a block assignment, like // a << if x then y else z ch = styler[pos]; switch (ch) { case ')': case ']': case '}': return true; default: return false; }}#define WHILE_BACKWARDS "elihw"#define UNTIL_BACKWARDS "litnu"// Nothing fancy -- look to see if we follow a while/until somewhere// on the current linestatic bool keywordDoStartsLoop(int pos, Accessor &styler){ char ch; int style; int lineStart = styler.GetLine(pos); int lineStartPosn = styler.LineStart(lineStart); styler.Flush(); while (--pos >= lineStartPosn) { style = actual_style(styler.StyleAt(pos)); if (style == SCE_RB_DEFAULT) { if ((ch = styler[pos]) == '\r' || ch == '\n') { // Scintilla's LineStart() and GetLine() routines aren't // platform-independent, so if we have text prepared with // a different system we can't rely on it. return false; } } else if (style == SCE_RB_WORD) { // Check for while or until, but write the word in backwards char prevWord[MAX_KEYWORD_LENGTH + 1]; // 1 byte for zero char *dst = prevWord; int wordLen = 0; int start_word; for (start_word = pos; start_word >= lineStartPosn && actual_style(styler.StyleAt(start_word)) == SCE_RB_WORD; start_word--) { if (++wordLen < MAX_KEYWORD_LENGTH) { *dst++ = styler[start_word]; } } *dst = 0; // Did we see our keyword? if (!strcmp(prevWord, WHILE_BACKWARDS) || !strcmp(prevWord, UNTIL_BACKWARDS)) { return true; } // We can move pos to the beginning of the keyword, and then // accept another decrement, as we can never have two contiguous // keywords: // word1 word2 // ^ // <- move to start_word // ^ // <- loop decrement // ^ # pointing to end of word1 is fine pos = start_word; } } return false;}/* * Folding Ruby * * The language is quite complex to analyze without a full parse. * For example, this line shouldn't affect fold level: * * print "hello" if feeling_friendly? * * Neither should this: * * print "hello" \ * if feeling_friendly? * * * But this should: * * if feeling_friendly? #++ * print "hello" \ * print "goodbye" * end #-- * * So we cheat, by actually looking at the existing indentation * levels for each line, and just echoing it back. Like Python. * Then if we get better at it, we'll take braces into consideration, * which always affect folding levels. * How the keywords should work: * No effect: * __FILE__ __LINE__ BEGIN END alias and * defined? false in nil not or self super then * true undef * Always increment: * begin class def do for module when { * * Always decrement: * end } * * Increment if these start a statement * if unless until while -- do nothing if they're modifiers * These end a block if there's no modifier, but don't bother * break next redo retry return yield * * These temporarily de-indent, but re-indent * case else elsif ensure rescue * * This means that the folder reflects indentation rather * than setting it. The language-service updates indentation * when users type return and finishes entering de-denters. * * Later offer to fold POD, here-docs, strings, and blocks of comments */static void FoldRbDoc(unsigned int startPos, int length, int initStyle, WordList *[], Accessor &styler) { const bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0; bool foldComment = styler.GetPropertyInt("fold.comment") != 0; synchronizeDocStart(startPos, length, initStyle, styler, // ref args false); unsigned int endPos = startPos + length; int visibleChars = 0; int lineCurrent = styler.GetLine(startPos); int levelPrev = startPos == 0 ? 0 : (styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK & ~SC_FOLDLEVELBASE); int levelCurrent = levelPrev; char chNext = styler[startPos]; int styleNext = styler.StyleAt(startPos); int stylePrev = startPos <= 1 ? SCE_RB_DEFAULT : styler.StyleAt(startPos - 1); bool buffer_ends_with_eol = false; for (unsigned int i = startPos; i < endPos; i++) { char ch = chNext; chNext = styler.SafeGetCharAt(i + 1); int style = styleNext; styleNext = styler.StyleAt(i + 1); bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n'); if (style == SCE_RB_COMMENTLINE) { if (foldComment && stylePrev != SCE_RB_COMMENTLINE) { if (chNext == '{') { levelCurrent++; } else if (chNext == '}') { levelCurrent--; } } } else if (style == SCE_RB_OPERATOR) { if (strchr("[{(", ch)) { levelCurrent++; } else if (strchr(")}]", ch)) { // Don't decrement below 0 if (levelCurrent > 0) levelCurrent--; } } else if (style == SCE_RB_WORD && styleNext != SCE_RB_WORD) { // Look at the keyword on the left and decide what to do char prevWord[MAX_KEYWORD_LENGTH + 1]; // 1 byte for zero prevWord[0] = 0; getPrevWord(i, prevWord, styler, SCE_RB_WORD); if (!strcmp(prevWord, "end")) { // Don't decrement below 0 if (levelCurrent > 0) levelCurrent--; } else if ( !strcmp(prevWord, "if") || !strcmp(prevWord, "def") || !strcmp(prevWord, "class") || !strcmp(prevWord, "module") || !strcmp(prevWord, "begin") || !strcmp(prevWord, "case") || !strcmp(prevWord, "do") || !strcmp(prevWord, "while") || !strcmp(prevWord, "unless") || !strcmp(prevWord, "until") || !strcmp(prevWord, "for") ) { levelCurrent++; } } if (atEOL) { int lev = levelPrev; if (visibleChars == 0 && foldCompact) lev |= SC_FOLDLEVELWHITEFLAG; if ((levelCurrent > levelPrev) && (visibleChars > 0)) lev |= SC_FOLDLEVELHEADERFLAG; styler.SetLevel(lineCurrent, lev|SC_FOLDLEVELBASE); lineCurrent++; levelPrev = levelCurrent; visibleChars = 0; buffer_ends_with_eol = true; } else if (!isspacechar(ch)) { visibleChars++; buffer_ends_with_eol = false; } } // Fill in the real level of the next line, keeping the current flags as they will be filled in later if (!buffer_ends_with_eol) { lineCurrent++; int new_lev = levelCurrent; if (visibleChars == 0 && foldCompact) new_lev |= SC_FOLDLEVELWHITEFLAG; if ((levelCurrent > levelPrev) && (visibleChars > 0)) new_lev |= SC_FOLDLEVELHEADERFLAG; levelCurrent = new_lev; } styler.SetLevel(lineCurrent, levelCurrent|SC_FOLDLEVELBASE);}static const char * const rubyWordListDesc[] = { "Keywords", 0};LexerModule lmRuby(SCLEX_RUBY, ColouriseRbDoc, "ruby", FoldRbDoc, rubyWordListDesc);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -