📄 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_NAMESPACE
using namespace Scintilla;
#endif
//XXX Identical to Perl, put in common area
static 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 declarations
static 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 area
static 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 indented
static 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 area
static 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 char
static 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 + -