*** empty log message ***
[anjuta-git-plugin.git] / scintilla / LexRuby.cxx
blob692a47adefe6c02def9653cb8944c5e0f49d2bc7
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.
8 #include <stdlib.h>
9 #include <string.h>
10 #include <ctype.h>
11 #include <stdio.h>
12 #include <stdarg.h>
14 #include "Platform.h"
16 #include "PropSet.h"
17 #include "Accessor.h"
18 #include "KeyWords.h"
19 #include "Scintilla.h"
20 #include "SciLexer.h"
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]) != 0;
25 for (unsigned int i = 0; i < end - start + 1 && i < 30; i++) {
26 s[i] = styler[start + i];
27 s[i + 1] = '\0';
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);
47 styler.ColourTo(end, chAttr);
48 strcpy(prevWord, s);
51 static bool IsRbComment(Accessor &styler, int pos, int len) {
52 return len>0 && styler[pos]=='#';
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;
64 if ((ch == 'r' || ch == 'R') && (chNext == '"' || chNext == '\''))
65 return true;
67 return false;
70 static bool IsRbWordStart(char ch, char chNext, char chNext2) {
71 return (iswordchar(ch) && !IsRbStringStart(ch, chNext, chNext2));
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);
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);
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);
94 if (ch != '"' && ch != '\'') {
95 *nextIndex = i + 1;
96 return SCE_P_DEFAULT;
99 if (i>0 && styler.SafeGetCharAt(i-1) == '$') {
100 *nextIndex = i + 1;
101 return SCE_P_DEFAULT;
104 if (ch == chNext && ch == styler.SafeGetCharAt(i + 2)) {
105 *nextIndex = i + 3;
107 if (ch == '"')
108 return SCE_P_TRIPLEDOUBLE;
109 else
110 return SCE_P_TRIPLE;
111 } else {
112 *nextIndex = i + 1;
114 if (ch == '"')
115 return SCE_P_STRING;
116 else
117 return SCE_P_CHARACTER;
121 static void ColouriseRbDoc(unsigned int startPos, int length, int initStyle,
122 WordList *keywordlists[], Accessor &styler) {
124 int lengthDoc = startPos + length;
126 // Backtrack to previous line in case need to fix its tab whinging
127 if (startPos > 0) {
128 int lineCurrent = styler.GetLine(startPos);
129 if (lineCurrent > 0) {
130 startPos = styler.LineStart(lineCurrent-1);
131 if (startPos == 0)
132 initStyle = SCE_P_DEFAULT;
133 else
134 initStyle = styler.StyleAt(startPos-1);
138 // Ruby uses a different mask because bad indentation is marked by oring with 32
139 styler.StartAt(startPos, 127);
141 WordList &keywords = *keywordlists[0];
143 int whingeLevel = styler.GetPropertyInt("tab.timmy.whinge.level");
144 char prevWord[200];
145 prevWord[0] = '\0';
146 if (length == 0)
147 return ;
149 int state = initStyle & 31;
151 int nextIndex = 0;
152 char chPrev = ' ';
153 char chPrev2 = ' ';
154 char chNext = styler[startPos];
155 styler.StartSegment(startPos);
156 bool atStartLine = true;
157 int spaceFlags = 0;
158 for (int i = startPos; i < lengthDoc; i++) {
160 if (atStartLine) {
161 char chBad = static_cast<char>(64);
162 char chGood = static_cast<char>(0);
163 char chFlags = chGood;
164 if (whingeLevel == 1) {
165 chFlags = (spaceFlags & wsInconsistent) ? chBad : chGood;
166 } else if (whingeLevel == 2) {
167 chFlags = (spaceFlags & wsSpaceTab) ? chBad : chGood;
168 } else if (whingeLevel == 3) {
169 chFlags = (spaceFlags & wsSpace) ? chBad : chGood;
170 } else if (whingeLevel == 4) {
171 chFlags = (spaceFlags & wsTab) ? chBad : chGood;
173 styler.SetFlags(chFlags, static_cast<char>(state));
174 atStartLine = false;
177 char ch = chNext;
178 chNext = styler.SafeGetCharAt(i + 1);
179 char chNext2 = styler.SafeGetCharAt(i + 2);
181 if ((ch == '\r' && chNext != '\n') || (ch == '\n') || (i == lengthDoc)) {
182 if ((state == SCE_P_DEFAULT) || (state == SCE_P_TRIPLE) || (state == SCE_P_TRIPLEDOUBLE)) {
183 // Perform colourisation of white space and triple quoted strings at end of each line to allow
184 // tab marking to work inside white space and triple quoted strings
185 styler.ColourTo(i, state);
187 atStartLine = true;
190 if (styler.IsLeadByte(ch)) {
191 chNext = styler.SafeGetCharAt(i + 2);
192 chPrev = ' ';
193 chPrev2 = ' ';
194 i += 1;
195 continue;
198 if (state == SCE_P_STRINGEOL) {
199 if (ch != '\r' && ch != '\n') {
200 styler.ColourTo(i - 1, state);
201 state = SCE_P_DEFAULT;
204 if (state == SCE_P_DEFAULT) {
205 if (IsRbWordStart(ch, chNext, chNext2)) {
206 styler.ColourTo(i - 1, state);
207 state = SCE_P_WORD;
208 } else if (ch == '#') {
209 styler.ColourTo(i - 1, state);
210 state = chNext == '#' ? SCE_P_COMMENTBLOCK : SCE_P_COMMENTLINE;
211 } else if (ch == '=' && chNext == 'b') {
212 // =begin indicates the start of a comment (doc) block
213 if(styler.SafeGetCharAt(i + 2) == 'e' && styler.SafeGetCharAt(i + 3) == 'g' && styler.SafeGetCharAt(i + 4) == 'i' && styler.SafeGetCharAt(i + 5) == 'n') {
214 styler.ColourTo(i - 1, state);
215 state = SCE_P_TRIPLEDOUBLE; //SCE_C_COMMENT;
217 } else if (IsRbStringStart(ch, chNext, chNext2)) {
218 styler.ColourTo(i - 1, state);
219 state = GetRbStringState(styler, i, &nextIndex);
220 if (nextIndex != i + 1) {
221 i = nextIndex - 1;
222 ch = ' ';
223 chPrev = ' ';
224 chNext = styler.SafeGetCharAt(i + 1);
226 } else if (isoperator(ch)) {
227 styler.ColourTo(i - 1, state);
228 styler.ColourTo(i, SCE_P_OPERATOR);
230 } else if (state == SCE_P_WORD) {
231 if (!iswordchar(ch)) {
232 ClassifyWordRb(styler.GetStartSegment(), i - 1, keywords, styler, prevWord);
233 state = SCE_P_DEFAULT;
234 if (ch == '#') {
235 state = chNext == '#' ? SCE_P_COMMENTBLOCK : SCE_P_COMMENTLINE;
236 } else if (IsRbStringStart(ch, chNext, chNext2)) {
237 styler.ColourTo(i - 1, state);
238 state = GetRbStringState(styler, i, &nextIndex);
239 if (nextIndex != i + 1) {
240 i = nextIndex - 1;
241 ch = ' ';
242 chPrev = ' ';
243 chNext = styler.SafeGetCharAt(i + 1);
245 } else if (isoperator(ch)) {
246 styler.ColourTo(i, SCE_P_OPERATOR);
249 } else {
250 if (state == SCE_P_COMMENTLINE || state == SCE_P_COMMENTBLOCK) {
251 if (ch == '\r' || ch == '\n') {
252 styler.ColourTo(i - 1, state);
253 state = SCE_P_DEFAULT;
255 } else if (state == SCE_P_STRING) {
256 if ((ch == '\r' || ch == '\n') && (chPrev != '\\')) {
257 styler.ColourTo(i - 1, state);
258 state = SCE_P_STRINGEOL;
259 } else if (ch == '\\') {
260 if (chNext == '\"' || chNext == '\'' || chNext == '\\') {
261 i++;
262 ch = chNext;
263 chNext = styler.SafeGetCharAt(i + 1);
265 } else if (ch == '\"') {
266 styler.ColourTo(i, state);
267 state = SCE_P_DEFAULT;
269 } else if (state == SCE_P_CHARACTER) {
270 if ((ch == '\r' || ch == '\n') && (chPrev != '\\')) {
271 styler.ColourTo(i - 1, state);
272 state = SCE_P_STRINGEOL;
273 } else if (ch == '\\') {
274 if (chNext == '\"' || chNext == '\'' || chNext == '\\') {
275 i++;
276 ch = chNext;
277 chNext = styler.SafeGetCharAt(i + 1);
279 } else if (ch == '\'') {
280 styler.ColourTo(i, state);
281 state = SCE_P_DEFAULT;
283 } else if (state == SCE_P_TRIPLE) {
284 if (ch == '\'' && chPrev == '\'' && chPrev2 == '\'') {
285 styler.ColourTo(i, state);
286 state = SCE_P_DEFAULT;
288 } else if (state == SCE_P_TRIPLEDOUBLE) {
289 // =end terminates the comment block
290 if (ch == 'd' && chPrev == 'n' && chPrev2 == 'e') {
291 if (styler.SafeGetCharAt(i - 3) == '=') {
292 styler.ColourTo(i, state);
293 state = SCE_P_DEFAULT;
298 chPrev2 = chPrev;
299 chPrev = ch;
301 if (state == SCE_P_WORD) {
302 ClassifyWordRb(styler.GetStartSegment(), lengthDoc-1, keywords, styler, prevWord);
303 } else {
304 styler.ColourTo(lengthDoc-1, state);
308 static void FoldRbDoc(unsigned int startPos, int length, int initStyle,
309 WordList *[], Accessor &styler) {
310 int lengthDoc = startPos + length;
312 // Backtrack to previous line in case need to fix its fold status
313 int lineCurrent = styler.GetLine(startPos);
314 if (startPos > 0) {
315 if (lineCurrent > 0) {
316 lineCurrent--;
317 startPos = styler.LineStart(lineCurrent);
318 if (startPos == 0)
319 initStyle = SCE_P_DEFAULT;
320 else
321 initStyle = styler.StyleAt(startPos-1);
324 int state = initStyle & 31;
325 int spaceFlags = 0;
326 int indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags, IsRbComment);
327 if ((state == SCE_P_TRIPLE) || (state == SCE_P_TRIPLEDOUBLE))
328 indentCurrent |= SC_FOLDLEVELWHITEFLAG;
329 char chNext = styler[startPos];
330 for (int i = startPos; i < lengthDoc; i++) {
331 char ch = chNext;
332 chNext = styler.SafeGetCharAt(i + 1);
333 int style = styler.StyleAt(i) & 31;
335 if ((ch == '\r' && chNext != '\n') || (ch == '\n') || (i == lengthDoc)) {
336 int lev = indentCurrent;
337 int indentNext = styler.IndentAmount(lineCurrent + 1, &spaceFlags, IsRbComment);
338 if ((style == SCE_P_TRIPLE) || (style== SCE_P_TRIPLEDOUBLE))
339 indentNext |= SC_FOLDLEVELWHITEFLAG;
340 if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG)) {
341 // Only non whitespace lines can be headers
342 if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext & SC_FOLDLEVELNUMBERMASK)) {
343 lev |= SC_FOLDLEVELHEADERFLAG;
344 } else if (indentNext & SC_FOLDLEVELWHITEFLAG) {
345 // Line after is blank so check the next - maybe should continue further?
346 int spaceFlags2 = 0;
347 int indentNext2 = styler.IndentAmount(lineCurrent + 2, &spaceFlags2, IsRbComment);
348 if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext2 & SC_FOLDLEVELNUMBERMASK)) {
349 lev |= SC_FOLDLEVELHEADERFLAG;
353 indentCurrent = indentNext;
354 styler.SetLevel(lineCurrent, lev);
355 lineCurrent++;
360 static const char * const rubyWordListDesc[] = {
361 "Keywords",
365 LexerModule lmRuby(SCLEX_RUBY, ColouriseRbDoc, "ruby", FoldRbDoc, rubyWordListDesc);