⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 lexruby.cxx

📁 Wxpython Implemented on Windows CE, Source code
💻 CXX
📖 第 1 页 / 共 4 页
字号:
// 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) {
    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 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;
}


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

static 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 target
static 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 heredocs

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