aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--src/KeyWords.cxx2
-rw-r--r--src/LexRuby.cxx376
2 files changed, 378 insertions, 0 deletions
diff --git a/src/KeyWords.cxx b/src/KeyWords.cxx
index f28fcecfb..9ac71491b 100644
--- a/src/KeyWords.cxx
+++ b/src/KeyWords.cxx
@@ -105,6 +105,7 @@ int wxForceScintillaLexers(void) {
extern LexerModule lmPython;
extern LexerModule lmSQL;
extern LexerModule lmVB;
+ extern LexerModule lmRuby;
if (
&lmAda
@@ -119,6 +120,7 @@ int wxForceScintillaLexers(void) {
&& &lmPython
&& &lmSQL
&& &lmVB
+ && &lmRuby
)
{
return 1;
diff --git a/src/LexRuby.cxx b/src/LexRuby.cxx
new file mode 100644
index 000000000..722bf4261
--- /dev/null
+++ b/src/LexRuby.cxx
@@ -0,0 +1,376 @@
+// 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"
+
+/* Returns true if the "as" word that begins at start follows an import statement */
+static bool IsImportAs(unsigned int start, Accessor &styler) {
+ unsigned int i;
+ unsigned int j;
+ char s[10];
+
+ /* Find any import before start but after any statement terminator or quote */
+ i = start;
+ while (i > 0) {
+ char ch = styler[i - 1];
+
+ if (ch == '\n' || ch == '\r' || ch == ';' || ch == '\'' || ch == '"' || ch == '`')
+ break;
+ if (ch == 't' && i > 5) {
+ for (j = 0; j < 6; j++)
+ s[j] = styler[(i - 6) + j];
+ s[j] = '\0';
+ if (strcmp(s, "require") == 0)
+ return true;
+ // if (strcmp(s, "include") == 0)
+ // return true;
+ }
+ i--;
+ }
+
+ return false;
+}
+
+static void ClassifyWordRb(unsigned int start, unsigned int end, WordList &keywords, Accessor &styler, char *prevWord) {
+ char s[100];
+ bool wordIsNumber = isdigit(styler[start]);
+ for (unsigned int i = 0; i < end - start + 1 && i < 30; i++) {
+ s[i] = styler[start + i];
+ s[i + 1] = '\0';
+ }
+ char chAttr = SCE_P_IDENTIFIER;
+ if (0 == strcmp(prevWord, "class"))
+ chAttr = SCE_P_CLASSNAME;
+ else if (0 == strcmp(prevWord, "module"))
+ chAttr = SCE_P_CLASSNAME;
+ else if (0 == strcmp(prevWord, "def"))
+ chAttr = SCE_P_DEFNAME;
+ else if (wordIsNumber)
+ chAttr = SCE_P_NUMBER;
+ else if (keywords.InList(s))
+ chAttr = SCE_P_WORD;
+ else if (strcmp(s, "as") == 0 && IsImportAs(start, styler))
+ chAttr = SCE_P_WORD;
+ // make sure that dot-qualifiers inside the word are lexed correct
+ else for (unsigned int i = 0; i < end - start + 1; i++) {
+ if (styler[start + i] == '.') {
+ styler.ColourTo(start + i - 1, chAttr);
+ styler.ColourTo(start + i, SCE_P_OPERATOR);
+ }
+ }
+ styler.ColourTo(end, chAttr);
+ strcpy(prevWord, s);
+}
+
+static bool IsRbComment(Accessor &styler, int pos, int len) {
+ return len>0 && styler[pos]=='#';
+}
+
+static bool IsRbStringStart(char ch, char chNext, char chNext2) {
+ if (ch == '\'' || ch == '"')
+ return true;
+ if (ch == 'u' || ch == 'U') {
+ if (chNext == '"' || chNext == '\'')
+ return true;
+ if ((chNext == 'r' || chNext == 'R') && (chNext2 == '"' || chNext2 == '\''))
+ return true;
+ }
+ if ((ch == 'r' || ch == 'R') && (chNext == '"' || chNext == '\''))
+ return true;
+
+ return false;
+}
+
+static bool IsRbWordStart(char ch, char chNext, char chNext2) {
+ return (iswordchar(ch) && !IsRbStringStart(ch, chNext, chNext2));
+}
+
+/* Return the state to use for the string starting at i; *nextIndex will be set to the first index following the quote(s) */
+static int GetRbStringState(Accessor &styler, int i, int *nextIndex) {
+ char ch = styler.SafeGetCharAt(i);
+ char chNext = styler.SafeGetCharAt(i + 1);
+
+ // Advance beyond r, u, or ur prefix, but bail if there are any unexpected chars
+ if (ch == 'r' || ch == 'R') {
+ i++;
+ ch = styler.SafeGetCharAt(i);
+ chNext = styler.SafeGetCharAt(i + 1);
+ }
+ else if (ch == 'u' || ch == 'U') {
+ if (chNext == 'r' || chNext == 'R')
+ i += 2;
+ else
+ i += 1;
+ ch = styler.SafeGetCharAt(i);
+ chNext = styler.SafeGetCharAt(i + 1);
+ }
+
+ if (ch != '"' && ch != '\'') {
+ *nextIndex = i + 1;
+ return SCE_P_DEFAULT;
+ }
+
+ if (ch == chNext && ch == styler.SafeGetCharAt(i + 2)) {
+ *nextIndex = i + 3;
+
+ if (ch == '"')
+ return SCE_P_TRIPLEDOUBLE;
+ else
+ return SCE_P_TRIPLE;
+ } else {
+ *nextIndex = i + 1;
+
+ if (ch == '"')
+ return SCE_P_STRING;
+ else
+ return SCE_P_CHARACTER;
+ }
+}
+
+static void ColouriseRbDoc(unsigned int startPos, int length, int initStyle,
+ WordList *keywordlists[], Accessor &styler) {
+
+ int lengthDoc = startPos + length;
+
+ // Backtrack to previous line in case need to fix its tab whinging
+ if (startPos > 0) {
+ int lineCurrent = styler.GetLine(startPos);
+ if (lineCurrent > 0) {
+ startPos = styler.LineStart(lineCurrent-1);
+ if (startPos == 0)
+ initStyle = SCE_P_DEFAULT;
+ else
+ initStyle = styler.StyleAt(startPos-1);
+ }
+ }
+
+ // Ruby uses a different mask because bad indentation is marked by oring with 32
+ styler.StartAt(startPos, 127);
+
+ WordList &keywords = *keywordlists[0];
+
+ int whingeLevel = styler.GetPropertyInt("tab.timmy.whinge.level");
+ char prevWord[200];
+ prevWord[0] = '\0';
+ if (length == 0)
+ return ;
+
+ int state = initStyle & 31;
+
+ int nextIndex = 0;
+ char chPrev = ' ';
+ char chPrev2 = ' ';
+ char chNext = styler[startPos];
+ styler.StartSegment(startPos);
+ bool atStartLine = true;
+ int spaceFlags = 0;
+ for (int i = startPos; i < lengthDoc; i++) {
+
+ if (atStartLine) {
+ char chBad = static_cast<char>(64);
+ char chGood = static_cast<char>(0);
+ char chFlags = chGood;
+ if (whingeLevel == 1) {
+ chFlags = (spaceFlags & wsInconsistent) ? chBad : chGood;
+ } else if (whingeLevel == 2) {
+ chFlags = (spaceFlags & wsSpaceTab) ? chBad : chGood;
+ } else if (whingeLevel == 3) {
+ chFlags = (spaceFlags & wsSpace) ? chBad : chGood;
+ } else if (whingeLevel == 4) {
+ chFlags = (spaceFlags & wsTab) ? chBad : chGood;
+ }
+ styler.SetFlags(chFlags, static_cast<char>(state));
+ atStartLine = false;
+ }
+
+ char ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+ char chNext2 = styler.SafeGetCharAt(i + 2);
+
+ if ((ch == '\r' && chNext != '\n') || (ch == '\n') || (i == lengthDoc)) {
+ if ((state == SCE_P_DEFAULT) || (state == SCE_P_TRIPLE) || (state == SCE_P_TRIPLEDOUBLE)) {
+ // Perform colourisation of white space and triple quoted strings at end of each line to allow
+ // tab marking to work inside white space and triple quoted strings
+ styler.ColourTo(i, state);
+ }
+ atStartLine = true;
+ }
+
+ if (styler.IsLeadByte(ch)) {
+ chNext = styler.SafeGetCharAt(i + 2);
+ chPrev = ' ';
+ chPrev2 = ' ';
+ i += 1;
+ continue;
+ }
+
+ if (state == SCE_P_STRINGEOL) {
+ if (ch != '\r' && ch != '\n') {
+ styler.ColourTo(i - 1, state);
+ state = SCE_P_DEFAULT;
+ }
+ }
+ if (state == SCE_P_DEFAULT) {
+ if (IsRbWordStart(ch, chNext, chNext2)) {
+ styler.ColourTo(i - 1, state);
+ state = SCE_P_WORD;
+ } else if (ch == '#') {
+ styler.ColourTo(i - 1, state);
+ state = chNext == '#' ? SCE_P_COMMENTBLOCK : SCE_P_COMMENTLINE;
+ } else if (IsRbStringStart(ch, chNext, chNext2)) {
+ styler.ColourTo(i - 1, state);
+ state = GetRbStringState(styler, i, &nextIndex);
+ if (nextIndex != i + 1) {
+ i = nextIndex - 1;
+ ch = ' ';
+ chPrev = ' ';
+ chNext = styler.SafeGetCharAt(i + 1);
+ }
+ } else if (isoperator(ch)) {
+ styler.ColourTo(i - 1, state);
+ styler.ColourTo(i, SCE_P_OPERATOR);
+ }
+ } else if (state == SCE_P_WORD) {
+ if (!iswordchar(ch)) {
+ ClassifyWordRb(styler.GetStartSegment(), i - 1, keywords, styler, prevWord);
+ state = SCE_P_DEFAULT;
+ if (ch == '#') {
+ state = chNext == '#' ? SCE_P_COMMENTBLOCK : SCE_P_COMMENTLINE;
+ } else if (IsRbStringStart(ch, chNext, chNext2)) {
+ styler.ColourTo(i - 1, state);
+ state = GetRbStringState(styler, i, &nextIndex);
+ if (nextIndex != i + 1) {
+ i = nextIndex - 1;
+ ch = ' ';
+ chPrev = ' ';
+ chNext = styler.SafeGetCharAt(i + 1);
+ }
+ } else if (isoperator(ch)) {
+ styler.ColourTo(i, SCE_P_OPERATOR);
+ }
+ }
+ } else {
+ if (state == SCE_P_COMMENTLINE || state == SCE_P_COMMENTBLOCK) {
+ if (ch == '\r' || ch == '\n') {
+ styler.ColourTo(i - 1, state);
+ state = SCE_P_DEFAULT;
+ }
+ } else if (state == SCE_P_STRING) {
+ if ((ch == '\r' || ch == '\n') && (chPrev != '\\')) {
+ styler.ColourTo(i - 1, state);
+ state = SCE_P_STRINGEOL;
+ } else if (ch == '\\') {
+ if (chNext == '\"' || chNext == '\'' || chNext == '\\') {
+ i++;
+ ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+ }
+ } else if (ch == '\"') {
+ styler.ColourTo(i, state);
+ state = SCE_P_DEFAULT;
+ }
+ } else if (state == SCE_P_CHARACTER) {
+ if ((ch == '\r' || ch == '\n') && (chPrev != '\\')) {
+ styler.ColourTo(i - 1, state);
+ state = SCE_P_STRINGEOL;
+ } else if (ch == '\\') {
+ if (chNext == '\"' || chNext == '\'' || chNext == '\\') {
+ i++;
+ ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+ }
+ } else if (ch == '\'') {
+ styler.ColourTo(i, state);
+ state = SCE_P_DEFAULT;
+ }
+ } else if (state == SCE_P_TRIPLE) {
+ if (ch == '\'' && chPrev == '\'' && chPrev2 == '\'') {
+ styler.ColourTo(i, state);
+ state = SCE_P_DEFAULT;
+ }
+ } else if (state == SCE_P_TRIPLEDOUBLE) {
+ if (ch == '\"' && chPrev == '\"' && chPrev2 == '\"') {
+ styler.ColourTo(i, state);
+ state = SCE_P_DEFAULT;
+ }
+ }
+ }
+ chPrev2 = chPrev;
+ chPrev = ch;
+ }
+ if (state == SCE_P_WORD) {
+ ClassifyWordRb(styler.GetStartSegment(), lengthDoc, keywords, styler, prevWord);
+ } else {
+ styler.ColourTo(lengthDoc, state);
+ }
+}
+
+static void FoldRbDoc(unsigned int startPos, int length, int initStyle,
+ WordList *[], Accessor &styler) {
+ int lengthDoc = startPos + length;
+
+ // Backtrack to previous line in case need to fix its fold status
+ int lineCurrent = styler.GetLine(startPos);
+ if (startPos > 0) {
+ if (lineCurrent > 0) {
+ lineCurrent--;
+ startPos = styler.LineStart(lineCurrent);
+ if (startPos == 0)
+ initStyle = SCE_P_DEFAULT;
+ else
+ initStyle = styler.StyleAt(startPos-1);
+ }
+ }
+ int state = initStyle & 31;
+ int spaceFlags = 0;
+ int indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags, IsRbComment);
+ if ((state == SCE_P_TRIPLE) || (state == SCE_P_TRIPLEDOUBLE))
+ indentCurrent |= SC_FOLDLEVELWHITEFLAG;
+ char chNext = styler[startPos];
+ for (int i = startPos; i < lengthDoc; i++) {
+ char ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+ int style = styler.StyleAt(i) & 31;
+
+ if ((ch == '\r' && chNext != '\n') || (ch == '\n') || (i == lengthDoc)) {
+ int lev = indentCurrent;
+ int indentNext = styler.IndentAmount(lineCurrent + 1, &spaceFlags, IsRbComment);
+ if ((style == SCE_P_TRIPLE) || (style== SCE_P_TRIPLEDOUBLE))
+ indentNext |= SC_FOLDLEVELWHITEFLAG;
+ if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG)) {
+ // Only non whitespace lines can be headers
+ if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext & SC_FOLDLEVELNUMBERMASK)) {
+ lev |= SC_FOLDLEVELHEADERFLAG;
+ } else if (indentNext & SC_FOLDLEVELWHITEFLAG) {
+ // Line after is blank so check the next - maybe should continue further?
+ int spaceFlags2 = 0;
+ int indentNext2 = styler.IndentAmount(lineCurrent + 2, &spaceFlags2, IsRbComment);
+ if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext2 & SC_FOLDLEVELNUMBERMASK)) {
+ lev |= SC_FOLDLEVELHEADERFLAG;
+ }
+ }
+ }
+ indentCurrent = indentNext;
+ styler.SetLevel(lineCurrent, lev);
+ lineCurrent++;
+ }
+ }
+}
+
+LexerModule lmRuby(SCLEX_RUBY, ColouriseRbDoc, "ruby", FoldRbDoc);