📄 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 line
static 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 + -