]> git.saurik.com Git - wxWidgets.git/blob - src/stc/scintilla/src/LexRuby.cxx
Upgraded to version 1.39 of Scintilla, and upated wxStyledTextCtrl
[wxWidgets.git] / src / stc / scintilla / src / LexRuby.cxx
1 // Scintilla source code edit control
2 /** @file LexRuby.cxx
3 ** Lexer for Ruby.
4 **/
5 // Copyright 2001- by Clemens Wyss <wys@helbling.ch>
6 // The License.txt file describes the conditions under which this software may be distributed.
7
8 #include <stdlib.h>
9 #include <string.h>
10 #include <ctype.h>
11 #include <stdio.h>
12 #include <stdarg.h>
13
14 #include "Platform.h"
15
16 #include "PropSet.h"
17 #include "Accessor.h"
18 #include "KeyWords.h"
19 #include "Scintilla.h"
20 #include "SciLexer.h"
21
22 static void ClassifyWordRb(unsigned int start, unsigned int end, WordList &keywords, Accessor &styler, char *prevWord) {
23 char s[100];
24 bool wordIsNumber = isdigit(styler[start]);
25 for (unsigned int i = 0; i < end - start + 1 && i < 30; i++) {
26 s[i] = styler[start + i];
27 s[i + 1] = '\0';
28 }
29 char chAttr = SCE_P_IDENTIFIER;
30 if (0 == strcmp(prevWord, "class"))
31 chAttr = SCE_P_CLASSNAME;
32 else if (0 == strcmp(prevWord, "module"))
33 chAttr = SCE_P_CLASSNAME;
34 else if (0 == strcmp(prevWord, "def"))
35 chAttr = SCE_P_DEFNAME;
36 else if (wordIsNumber)
37 chAttr = SCE_P_NUMBER;
38 else if (keywords.InList(s))
39 chAttr = SCE_P_WORD;
40 // make sure that dot-qualifiers inside the word are lexed correct
41 else for (unsigned int i = 0; i < end - start + 1; i++) {
42 if (styler[start + i] == '.') {
43 styler.ColourTo(start + i - 1, chAttr);
44 styler.ColourTo(start + i, SCE_P_OPERATOR);
45 }
46 }
47 styler.ColourTo(end, chAttr);
48 strcpy(prevWord, s);
49 }
50
51 static bool IsRbComment(Accessor &styler, int pos, int len) {
52 return len>0 && styler[pos]=='#';
53 }
54
55 static bool IsRbStringStart(char ch, char chNext, char chNext2) {
56 if (ch == '\'' || ch == '"')
57 return true;
58 if (ch == 'u' || ch == 'U') {
59 if (chNext == '"' || chNext == '\'')
60 return true;
61 if ((chNext == 'r' || chNext == 'R') && (chNext2 == '"' || chNext2 == '\''))
62 return true;
63 }
64 if ((ch == 'r' || ch == 'R') && (chNext == '"' || chNext == '\''))
65 return true;
66
67 return false;
68 }
69
70 static bool IsRbWordStart(char ch, char chNext, char chNext2) {
71 return (iswordchar(ch) && !IsRbStringStart(ch, chNext, chNext2));
72 }
73
74 /* Return the state to use for the string starting at i; *nextIndex will be set to the first index following the quote(s) */
75 static int GetRbStringState(Accessor &styler, int i, int *nextIndex) {
76 char ch = styler.SafeGetCharAt(i);
77 char chNext = styler.SafeGetCharAt(i + 1);
78
79 // Advance beyond r, u, or ur prefix, but bail if there are any unexpected chars
80 if (ch == 'r' || ch == 'R') {
81 i++;
82 ch = styler.SafeGetCharAt(i);
83 chNext = styler.SafeGetCharAt(i + 1);
84 }
85 else if (ch == 'u' || ch == 'U') {
86 if (chNext == 'r' || chNext == 'R')
87 i += 2;
88 else
89 i += 1;
90 ch = styler.SafeGetCharAt(i);
91 chNext = styler.SafeGetCharAt(i + 1);
92 }
93
94 if (ch != '"' && ch != '\'') {
95 *nextIndex = i + 1;
96 return SCE_P_DEFAULT;
97 }
98
99 if (ch == chNext && ch == styler.SafeGetCharAt(i + 2)) {
100 *nextIndex = i + 3;
101
102 if (ch == '"')
103 return SCE_P_TRIPLEDOUBLE;
104 else
105 return SCE_P_TRIPLE;
106 } else {
107 *nextIndex = i + 1;
108
109 if (ch == '"')
110 return SCE_P_STRING;
111 else
112 return SCE_P_CHARACTER;
113 }
114 }
115
116 static void ColouriseRbDoc(unsigned int startPos, int length, int initStyle,
117 WordList *keywordlists[], Accessor &styler) {
118
119 int lengthDoc = startPos + length;
120
121 // Backtrack to previous line in case need to fix its tab whinging
122 if (startPos > 0) {
123 int lineCurrent = styler.GetLine(startPos);
124 if (lineCurrent > 0) {
125 startPos = styler.LineStart(lineCurrent-1);
126 if (startPos == 0)
127 initStyle = SCE_P_DEFAULT;
128 else
129 initStyle = styler.StyleAt(startPos-1);
130 }
131 }
132
133 // Ruby uses a different mask because bad indentation is marked by oring with 32
134 styler.StartAt(startPos, 127);
135
136 WordList &keywords = *keywordlists[0];
137
138 int whingeLevel = styler.GetPropertyInt("tab.timmy.whinge.level");
139 char prevWord[200];
140 prevWord[0] = '\0';
141 if (length == 0)
142 return ;
143
144 int state = initStyle & 31;
145
146 int nextIndex = 0;
147 char chPrev = ' ';
148 char chPrev2 = ' ';
149 char chNext = styler[startPos];
150 styler.StartSegment(startPos);
151 bool atStartLine = true;
152 int spaceFlags = 0;
153 for (int i = startPos; i < lengthDoc; i++) {
154
155 if (atStartLine) {
156 char chBad = static_cast<char>(64);
157 char chGood = static_cast<char>(0);
158 char chFlags = chGood;
159 if (whingeLevel == 1) {
160 chFlags = (spaceFlags & wsInconsistent) ? chBad : chGood;
161 } else if (whingeLevel == 2) {
162 chFlags = (spaceFlags & wsSpaceTab) ? chBad : chGood;
163 } else if (whingeLevel == 3) {
164 chFlags = (spaceFlags & wsSpace) ? chBad : chGood;
165 } else if (whingeLevel == 4) {
166 chFlags = (spaceFlags & wsTab) ? chBad : chGood;
167 }
168 styler.SetFlags(chFlags, static_cast<char>(state));
169 atStartLine = false;
170 }
171
172 char ch = chNext;
173 chNext = styler.SafeGetCharAt(i + 1);
174 char chNext2 = styler.SafeGetCharAt(i + 2);
175
176 if ((ch == '\r' && chNext != '\n') || (ch == '\n') || (i == lengthDoc)) {
177 if ((state == SCE_P_DEFAULT) || (state == SCE_P_TRIPLE) || (state == SCE_P_TRIPLEDOUBLE)) {
178 // Perform colourisation of white space and triple quoted strings at end of each line to allow
179 // tab marking to work inside white space and triple quoted strings
180 styler.ColourTo(i, state);
181 }
182 atStartLine = true;
183 }
184
185 if (styler.IsLeadByte(ch)) {
186 chNext = styler.SafeGetCharAt(i + 2);
187 chPrev = ' ';
188 chPrev2 = ' ';
189 i += 1;
190 continue;
191 }
192
193 if (state == SCE_P_STRINGEOL) {
194 if (ch != '\r' && ch != '\n') {
195 styler.ColourTo(i - 1, state);
196 state = SCE_P_DEFAULT;
197 }
198 }
199 if (state == SCE_P_DEFAULT) {
200 if (IsRbWordStart(ch, chNext, chNext2)) {
201 styler.ColourTo(i - 1, state);
202 state = SCE_P_WORD;
203 } else if (ch == '#') {
204 styler.ColourTo(i - 1, state);
205 state = chNext == '#' ? SCE_P_COMMENTBLOCK : SCE_P_COMMENTLINE;
206 } else if (ch == '=' && chNext == 'b') {
207 // =begin indicates the start of a comment (doc) block
208 if(styler.SafeGetCharAt(i + 2) == 'e' && styler.SafeGetCharAt(i + 3) == 'g' && styler.SafeGetCharAt(i + 4) == 'i' && styler.SafeGetCharAt(i + 5) == 'n') {
209 styler.ColourTo(i - 1, state);
210 state = SCE_P_TRIPLEDOUBLE; //SCE_C_COMMENT;
211 }
212 } else if (IsRbStringStart(ch, chNext, chNext2)) {
213 styler.ColourTo(i - 1, state);
214 state = GetRbStringState(styler, i, &nextIndex);
215 if (nextIndex != i + 1) {
216 i = nextIndex - 1;
217 ch = ' ';
218 chPrev = ' ';
219 chNext = styler.SafeGetCharAt(i + 1);
220 }
221 } else if (isoperator(ch)) {
222 styler.ColourTo(i - 1, state);
223 styler.ColourTo(i, SCE_P_OPERATOR);
224 }
225 } else if (state == SCE_P_WORD) {
226 if (!iswordchar(ch)) {
227 ClassifyWordRb(styler.GetStartSegment(), i - 1, keywords, styler, prevWord);
228 state = SCE_P_DEFAULT;
229 if (ch == '#') {
230 state = chNext == '#' ? SCE_P_COMMENTBLOCK : SCE_P_COMMENTLINE;
231 } else if (IsRbStringStart(ch, chNext, chNext2)) {
232 styler.ColourTo(i - 1, state);
233 state = GetRbStringState(styler, i, &nextIndex);
234 if (nextIndex != i + 1) {
235 i = nextIndex - 1;
236 ch = ' ';
237 chPrev = ' ';
238 chNext = styler.SafeGetCharAt(i + 1);
239 }
240 } else if (isoperator(ch)) {
241 styler.ColourTo(i, SCE_P_OPERATOR);
242 }
243 }
244 } else {
245 if (state == SCE_P_COMMENTLINE || state == SCE_P_COMMENTBLOCK) {
246 if (ch == '\r' || ch == '\n') {
247 styler.ColourTo(i - 1, state);
248 state = SCE_P_DEFAULT;
249 }
250 } else if (state == SCE_P_STRING) {
251 if ((ch == '\r' || ch == '\n') && (chPrev != '\\')) {
252 styler.ColourTo(i - 1, state);
253 state = SCE_P_STRINGEOL;
254 } else if (ch == '\\') {
255 if (chNext == '\"' || chNext == '\'' || chNext == '\\') {
256 i++;
257 ch = chNext;
258 chNext = styler.SafeGetCharAt(i + 1);
259 }
260 } else if (ch == '\"') {
261 styler.ColourTo(i, state);
262 state = SCE_P_DEFAULT;
263 }
264 } else if (state == SCE_P_CHARACTER) {
265 if ((ch == '\r' || ch == '\n') && (chPrev != '\\')) {
266 styler.ColourTo(i - 1, state);
267 state = SCE_P_STRINGEOL;
268 } else if (ch == '\\') {
269 if (chNext == '\"' || chNext == '\'' || chNext == '\\') {
270 i++;
271 ch = chNext;
272 chNext = styler.SafeGetCharAt(i + 1);
273 }
274 } else if (ch == '\'') {
275 styler.ColourTo(i, state);
276 state = SCE_P_DEFAULT;
277 }
278 } else if (state == SCE_P_TRIPLE) {
279 if (ch == '\'' && chPrev == '\'' && chPrev2 == '\'') {
280 styler.ColourTo(i, state);
281 state = SCE_P_DEFAULT;
282 }
283 } else if (state == SCE_P_TRIPLEDOUBLE) {
284 // =end terminates the comment block
285 if (ch == 'd' && chPrev == 'n' && chPrev2 == 'e') {
286 if (styler.SafeGetCharAt(i - 3) == '=') {
287 styler.ColourTo(i, state);
288 state = SCE_P_DEFAULT;
289 }
290 }
291 }
292 }
293 chPrev2 = chPrev;
294 chPrev = ch;
295 }
296 if (state == SCE_P_WORD) {
297 ClassifyWordRb(styler.GetStartSegment(), lengthDoc, keywords, styler, prevWord);
298 } else {
299 styler.ColourTo(lengthDoc, state);
300 }
301 }
302
303 static void FoldRbDoc(unsigned int startPos, int length, int initStyle,
304 WordList *[], Accessor &styler) {
305 int lengthDoc = startPos + length;
306
307 // Backtrack to previous line in case need to fix its fold status
308 int lineCurrent = styler.GetLine(startPos);
309 if (startPos > 0) {
310 if (lineCurrent > 0) {
311 lineCurrent--;
312 startPos = styler.LineStart(lineCurrent);
313 if (startPos == 0)
314 initStyle = SCE_P_DEFAULT;
315 else
316 initStyle = styler.StyleAt(startPos-1);
317 }
318 }
319 int state = initStyle & 31;
320 int spaceFlags = 0;
321 int indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags, IsRbComment);
322 if ((state == SCE_P_TRIPLE) || (state == SCE_P_TRIPLEDOUBLE))
323 indentCurrent |= SC_FOLDLEVELWHITEFLAG;
324 char chNext = styler[startPos];
325 for (int i = startPos; i < lengthDoc; i++) {
326 char ch = chNext;
327 chNext = styler.SafeGetCharAt(i + 1);
328 int style = styler.StyleAt(i) & 31;
329
330 if ((ch == '\r' && chNext != '\n') || (ch == '\n') || (i == lengthDoc)) {
331 int lev = indentCurrent;
332 int indentNext = styler.IndentAmount(lineCurrent + 1, &spaceFlags, IsRbComment);
333 if ((style == SCE_P_TRIPLE) || (style== SCE_P_TRIPLEDOUBLE))
334 indentNext |= SC_FOLDLEVELWHITEFLAG;
335 if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG)) {
336 // Only non whitespace lines can be headers
337 if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext & SC_FOLDLEVELNUMBERMASK)) {
338 lev |= SC_FOLDLEVELHEADERFLAG;
339 } else if (indentNext & SC_FOLDLEVELWHITEFLAG) {
340 // Line after is blank so check the next - maybe should continue further?
341 int spaceFlags2 = 0;
342 int indentNext2 = styler.IndentAmount(lineCurrent + 2, &spaceFlags2, IsRbComment);
343 if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext2 & SC_FOLDLEVELNUMBERMASK)) {
344 lev |= SC_FOLDLEVELHEADERFLAG;
345 }
346 }
347 }
348 indentCurrent = indentNext;
349 styler.SetLevel(lineCurrent, lev);
350 lineCurrent++;
351 }
352 }
353 }
354
355 LexerModule lmRuby(SCLEX_RUBY, ColouriseRbDoc, "ruby", FoldRbDoc);