📄 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) { // Error: scintilla's KeyWords.h includes '.' as a word-char // we want to separate things that can take methods from the // methods. return isHighBitChar(ch) || isalnum(ch) || 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[MAX_KEYWORD_LENGTH]; 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;}// This class is used by the enter and exit methods, so it needs// to be hoisted out of the function.class QuoteCls { public: int Count; char Up; char Down; QuoteCls() { this->New(); } void New() { Count = 0; Up = '\0'; Down = '\0'; } void Open(char u) { Count++; Up = u; Down = opposite(Up); } QuoteCls(const QuoteCls& q) { // copy constructor -- use this for copying in Count = q.Count; Up = q.Up; Down = q.Down; } QuoteCls& operator=(const QuoteCls& q) { // assignment constructor if (this != &q) { Count = q.Count; Up = q.Up; Down = q.Down; } return *this; } };static void enterInnerExpression(int *p_inner_string_types, int *p_inner_expn_brace_counts, QuoteCls *p_inner_quotes, int& inner_string_count, int& state, int& brace_counts, QuoteCls curr_quote ) { p_inner_string_types[inner_string_count] = state; state = SCE_RB_DEFAULT; p_inner_expn_brace_counts[inner_string_count] = brace_counts; brace_counts = 0; p_inner_quotes[inner_string_count] = curr_quote; ++inner_string_count;}static void exitInnerExpression(int *p_inner_string_types, int *p_inner_expn_brace_counts, QuoteCls *p_inner_quotes, int& inner_string_count, int& state, int& brace_counts, QuoteCls& curr_quote ) { --inner_string_count; state = p_inner_string_types[inner_string_count]; brace_counts = p_inner_expn_brace_counts[inner_string_count]; curr_quote = p_inner_quotes[inner_string_count];}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 forward
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -