📄 lexruby.cxx
字号:
// Scintilla source code edit control/** @file LexRuby.cxx ** Lexer for Ruby. **/// Copyright 2001- by Clemens Wyss <wys@helbling.ch>// The License.txt file describes the conditions under which this software may be distributed.#include <stdlib.h>#include <string.h>#include <ctype.h>#include <stdio.h>#include <stdarg.h>#include "Platform.h"#include "PropSet.h"#include "Accessor.h"#include "KeyWords.h"#include "Scintilla.h"#include "SciLexer.h"#ifdef SCI_NAMESPACEusing namespace Scintilla;#endif//XXX Identical to Perl, put in common areastatic inline bool isEOLChar(char ch) { return (ch == '\r') || (ch == '\n');}#define isSafeASCII(ch) ((unsigned int)(ch) <= 127)// This one's redundant, but makes for more readable code#define isHighBitChar(ch) ((unsigned int)(ch) > 127)static inline bool isSafeAlpha(char ch) { return (isSafeASCII(ch) && isalpha(ch)) || ch == '_';}static inline bool isSafeAlnum(char ch) { return (isSafeASCII(ch) && isalnum(ch)) || ch == '_';}static inline bool isSafeAlnumOrHigh(char ch) { return isHighBitChar(ch) || isalnum(ch) || ch == '_';}static inline bool isSafeDigit(char ch) { return isSafeASCII(ch) && isdigit(ch);}static inline bool isSafeWordcharOrHigh(char ch) { return isHighBitChar(ch) || iswordchar(ch);}static bool inline iswhitespace(char ch) { return ch == ' ' || ch == '\t';}#define MAX_KEYWORD_LENGTH 200#define STYLE_MASK 63#define actual_style(style) (style & STYLE_MASK)static bool followsDot(unsigned int pos, Accessor &styler) { styler.Flush(); for (; pos >= 1; --pos) { int style = actual_style(styler.StyleAt(pos)); char ch; switch (style) { case SCE_RB_DEFAULT: ch = styler[pos]; if (ch == ' ' || ch == '\t') { //continue } else { return false; } break; case SCE_RB_OPERATOR: return styler[pos] == '.'; default: return false; } } return false;}// Forward declarationsstatic bool keywordIsAmbiguous(const char *prevWord);static bool keywordDoStartsLoop(int pos, Accessor &styler);static bool keywordIsModifier(const char *word, int pos, Accessor &styler);static int ClassifyWordRb(unsigned int start, unsigned int end, WordList &keywords, Accessor &styler, char *prevWord) { char s[100]; unsigned int i, j; unsigned int lim = end - start + 1; // num chars to copy if (lim >= MAX_KEYWORD_LENGTH) { lim = MAX_KEYWORD_LENGTH - 1; } for (i = start, j = 0; j < lim; i++, j++) { s[j] = styler[i]; } s[j] = '\0'; int chAttr; if (0 == strcmp(prevWord, "class")) chAttr = SCE_RB_CLASSNAME; else if (0 == strcmp(prevWord, "module")) chAttr = SCE_RB_MODULE_NAME; else if (0 == strcmp(prevWord, "def")) chAttr = SCE_RB_DEFNAME; else if (keywords.InList(s) && !followsDot(start - 1, styler)) { if (keywordIsAmbiguous(s) && keywordIsModifier(s, start, styler)) { // Demoted keywords are colored as keywords, // but do not affect changes in indentation. // // Consider the word 'if': // 1. <<if test ...>> : normal // 2. <<stmt if test>> : demoted // 3. <<lhs = if ...>> : normal: start a new indent level // 4. <<obj.if = 10>> : color as identifer, since it follows '.' chAttr = SCE_RB_WORD_DEMOTED; } else { chAttr = SCE_RB_WORD; } } else chAttr = SCE_RB_IDENTIFIER; styler.ColourTo(end, chAttr); if (chAttr == SCE_RB_WORD) { strcpy(prevWord, s); } else { prevWord[0] = 0; } return chAttr;}//XXX Identical to Perl, put in common areastatic bool isMatch(Accessor &styler, int lengthDoc, int pos, const char *val) { if ((pos + static_cast<int>(strlen(val))) >= lengthDoc) { return false; } while (*val) { if (*val != styler[pos++]) { return false; } val++; } return true;}// Do Ruby better -- find the end of the line, work back,// and then check for leading white space// Precondition: the here-doc target can be indentedstatic bool lookingAtHereDocDelim(Accessor &styler, int pos, int lengthDoc, const char *HereDocDelim){ if (!isMatch(styler, lengthDoc, pos, HereDocDelim)) { return false; } while (--pos > 0) { char ch = styler[pos]; if (isEOLChar(ch)) { return true; } else if (ch != ' ' && ch != '\t') { return false; } } return false;}//XXX Identical to Perl, put in common areastatic char opposite(char ch) { if (ch == '(') return ')'; if (ch == '[') return ']'; if (ch == '{') return '}'; if (ch == '<') return '>'; return ch;}// Null transitions when we see we've reached the end// and need to relex the curr char.static void redo_char(int &i, char &ch, char &chNext, char &chNext2, int &state) { i--; chNext2 = chNext; chNext = ch; state = SCE_RB_DEFAULT;}static void advance_char(int &i, char &ch, char &chNext, char &chNext2) { i++; ch = chNext; chNext = chNext2;}// precondition: startPos points to one after the EOL charstatic bool currLineContainsHereDelims(int& startPos, Accessor &styler) { if (startPos <= 1) return false; int pos; for (pos = startPos - 1; pos > 0; pos--) { char ch = styler.SafeGetCharAt(pos); if (isEOLChar(ch)) { // Leave the pointers where they are -- there are no // here doc delims on the current line, even if // the EOL isn't default style return false; } else { styler.Flush(); if (actual_style(styler.StyleAt(pos)) == SCE_RB_HERE_DELIM) { break; } } } if (pos == 0) { return false; } // Update the pointers so we don't have to re-analyze the string startPos = pos; return true;}static bool isEmptyLine(int pos, Accessor &styler) { int spaceFlags = 0; int lineCurrent = styler.GetLine(pos); int indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags, NULL); return (indentCurrent & SC_FOLDLEVELWHITEFLAG) != 0;}static bool RE_CanFollowKeyword(const char *keyword) { if (!strcmp(keyword, "and") || !strcmp(keyword, "begin") || !strcmp(keyword, "break") || !strcmp(keyword, "case") || !strcmp(keyword, "do") || !strcmp(keyword, "else") || !strcmp(keyword, "elsif") || !strcmp(keyword, "if") || !strcmp(keyword, "next") || !strcmp(keyword, "return") || !strcmp(keyword, "when") || !strcmp(keyword, "unless") || !strcmp(keyword, "until") || !strcmp(keyword, "not") || !strcmp(keyword, "or")) { return true; } return false;}// Look at chars up to but not including endPos// Don't look at styles in case we're looking forwardstatic 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -