1 // Scintilla source code edit control
2 /** @file LexEiffel.cxx
3  ** Lexer for Eiffel.
4  **/
5 // Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
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 <stdio.h>
11 #include <stdarg.h>
12 #include <assert.h>
13 #include <ctype.h>
14 
15 #include "ILexer.h"
16 #include "Scintilla.h"
17 #include "SciLexer.h"
18 
19 #include "WordList.h"
20 #include "LexAccessor.h"
21 #include "Accessor.h"
22 #include "StyleContext.h"
23 #include "CharacterSet.h"
24 #include "LexerModule.h"
25 
26 using namespace Scintilla;
27 
isEiffelOperator(unsigned int ch)28 static inline bool isEiffelOperator(unsigned int ch) {
29 	// '.' left out as it is used to make up numbers
30 	return ch == '*' || ch == '/' || ch == '\\' || ch == '-' || ch == '+' ||
31 	        ch == '(' || ch == ')' || ch == '=' ||
32 	        ch == '{' || ch == '}' || ch == '~' ||
33 	        ch == '[' || ch == ']' || ch == ';' ||
34 	        ch == '<' || ch == '>' || ch == ',' ||
35 	        ch == '.' || ch == '^' || ch == '%' || ch == ':' ||
36 		ch == '!' || ch == '@' || ch == '?';
37 }
38 
IsAWordChar(unsigned int ch)39 static inline bool IsAWordChar(unsigned int  ch) {
40 	return (ch < 0x80) && (isalnum(ch) || ch == '_');
41 }
42 
IsAWordStart(unsigned int ch)43 static inline bool IsAWordStart(unsigned int ch) {
44 	return (ch < 0x80) && (isalnum(ch) || ch == '_');
45 }
46 
ColouriseEiffelDoc(Sci_PositionU startPos,Sci_Position length,int initStyle,WordList * keywordlists[],Accessor & styler)47 static void ColouriseEiffelDoc(Sci_PositionU startPos,
48                             Sci_Position length,
49                             int initStyle,
50                             WordList *keywordlists[],
51                             Accessor &styler) {
52 
53 	WordList &keywords = *keywordlists[0];
54 
55 	StyleContext sc(startPos, length, initStyle, styler);
56 
57 	for (; sc.More(); sc.Forward()) {
58 
59 		if (sc.state == SCE_EIFFEL_STRINGEOL) {
60 			if (sc.ch != '\r' && sc.ch != '\n') {
61 				sc.SetState(SCE_EIFFEL_DEFAULT);
62 			}
63 		} else if (sc.state == SCE_EIFFEL_OPERATOR) {
64 			sc.SetState(SCE_EIFFEL_DEFAULT);
65 		} else if (sc.state == SCE_EIFFEL_WORD) {
66 			if (!IsAWordChar(sc.ch)) {
67 				char s[100];
68 				sc.GetCurrentLowered(s, sizeof(s));
69 				if (!keywords.InList(s)) {
70 					sc.ChangeState(SCE_EIFFEL_IDENTIFIER);
71 				}
72 				sc.SetState(SCE_EIFFEL_DEFAULT);
73 			}
74 		} else if (sc.state == SCE_EIFFEL_NUMBER) {
75 			if (!IsAWordChar(sc.ch)) {
76 				sc.SetState(SCE_EIFFEL_DEFAULT);
77 			}
78 		} else if (sc.state == SCE_EIFFEL_COMMENTLINE) {
79 			if (sc.ch == '\r' || sc.ch == '\n') {
80 				sc.SetState(SCE_EIFFEL_DEFAULT);
81 			}
82 		} else if (sc.state == SCE_EIFFEL_STRING) {
83 			if (sc.ch == '%') {
84 				sc.Forward();
85 			} else if (sc.ch == '\"') {
86 				sc.Forward();
87 				sc.SetState(SCE_EIFFEL_DEFAULT);
88 			}
89 		} else if (sc.state == SCE_EIFFEL_CHARACTER) {
90 			if (sc.ch == '\r' || sc.ch == '\n') {
91 				sc.SetState(SCE_EIFFEL_STRINGEOL);
92 			} else if (sc.ch == '%') {
93 				sc.Forward();
94 			} else if (sc.ch == '\'') {
95 				sc.Forward();
96 				sc.SetState(SCE_EIFFEL_DEFAULT);
97 			}
98 		}
99 
100 		if (sc.state == SCE_EIFFEL_DEFAULT) {
101 			if (sc.ch == '-' && sc.chNext == '-') {
102 				sc.SetState(SCE_EIFFEL_COMMENTLINE);
103 			} else if (sc.ch == '\"') {
104 				sc.SetState(SCE_EIFFEL_STRING);
105 			} else if (sc.ch == '\'') {
106 				sc.SetState(SCE_EIFFEL_CHARACTER);
107 			} else if (IsADigit(sc.ch) || (sc.ch == '.')) {
108 				sc.SetState(SCE_EIFFEL_NUMBER);
109 			} else if (IsAWordStart(sc.ch)) {
110 				sc.SetState(SCE_EIFFEL_WORD);
111 			} else if (isEiffelOperator(sc.ch)) {
112 				sc.SetState(SCE_EIFFEL_OPERATOR);
113 			}
114 		}
115 	}
116 	sc.Complete();
117 }
118 
IsEiffelComment(Accessor & styler,Sci_Position pos,Sci_Position len)119 static bool IsEiffelComment(Accessor &styler, Sci_Position pos, Sci_Position len) {
120 	return len>1 && styler[pos]=='-' && styler[pos+1]=='-';
121 }
122 
FoldEiffelDocIndent(Sci_PositionU startPos,Sci_Position length,int,WordList * [],Accessor & styler)123 static void FoldEiffelDocIndent(Sci_PositionU startPos, Sci_Position length, int,
124 						   WordList *[], Accessor &styler) {
125 	Sci_Position lengthDoc = startPos + length;
126 
127 	// Backtrack to previous line in case need to fix its fold status
128 	Sci_Position lineCurrent = styler.GetLine(startPos);
129 	if (startPos > 0) {
130 		if (lineCurrent > 0) {
131 			lineCurrent--;
132 			startPos = styler.LineStart(lineCurrent);
133 		}
134 	}
135 	int spaceFlags = 0;
136 	int indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags, IsEiffelComment);
137 	char chNext = styler[startPos];
138 	for (Sci_Position i = startPos; i < lengthDoc; i++) {
139 		char ch = chNext;
140 		chNext = styler.SafeGetCharAt(i + 1);
141 
142 		if ((ch == '\r' && chNext != '\n') || (ch == '\n') || (i == lengthDoc)) {
143 			int lev = indentCurrent;
144 			int indentNext = styler.IndentAmount(lineCurrent + 1, &spaceFlags, IsEiffelComment);
145 			if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG)) {
146 				// Only non whitespace lines can be headers
147 				if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext & SC_FOLDLEVELNUMBERMASK)) {
148 					lev |= SC_FOLDLEVELHEADERFLAG;
149 				} else if (indentNext & SC_FOLDLEVELWHITEFLAG) {
150 					// Line after is blank so check the next - maybe should continue further?
151 					int spaceFlags2 = 0;
152 					int indentNext2 = styler.IndentAmount(lineCurrent + 2, &spaceFlags2, IsEiffelComment);
153 					if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext2 & SC_FOLDLEVELNUMBERMASK)) {
154 						lev |= SC_FOLDLEVELHEADERFLAG;
155 					}
156 				}
157 			}
158 			indentCurrent = indentNext;
159 			styler.SetLevel(lineCurrent, lev);
160 			lineCurrent++;
161 		}
162 	}
163 }
164 
FoldEiffelDocKeyWords(Sci_PositionU startPos,Sci_Position length,int,WordList * [],Accessor & styler)165 static void FoldEiffelDocKeyWords(Sci_PositionU startPos, Sci_Position length, int /* initStyle */, WordList *[],
166                        Accessor &styler) {
167 	Sci_PositionU lengthDoc = startPos + length;
168 	int visibleChars = 0;
169 	Sci_Position lineCurrent = styler.GetLine(startPos);
170 	int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
171 	int levelCurrent = levelPrev;
172 	char chNext = styler[startPos];
173 	int stylePrev = 0;
174 	int styleNext = styler.StyleAt(startPos);
175 	// lastDeferred should be determined by looking back to last keyword in case
176 	// the "deferred" is on a line before "class"
177 	bool lastDeferred = false;
178 	for (Sci_PositionU i = startPos; i < lengthDoc; i++) {
179 		char ch = chNext;
180 		chNext = styler.SafeGetCharAt(i + 1);
181 		int style = styleNext;
182 		styleNext = styler.StyleAt(i + 1);
183 		bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
184 		if ((stylePrev != SCE_EIFFEL_WORD) && (style == SCE_EIFFEL_WORD)) {
185 			char s[20];
186 			Sci_PositionU j = 0;
187 			while ((j < (sizeof(s) - 1)) && (iswordchar(styler[i + j]))) {
188 				s[j] = styler[i + j];
189 				j++;
190 			}
191 			s[j] = '\0';
192 
193 			if (
194 				(strcmp(s, "check") == 0) ||
195 				(strcmp(s, "debug") == 0) ||
196 				(strcmp(s, "deferred") == 0) ||
197 				(strcmp(s, "do") == 0) ||
198 				(strcmp(s, "from") == 0) ||
199 				(strcmp(s, "if") == 0) ||
200 				(strcmp(s, "inspect") == 0) ||
201 				(strcmp(s, "once") == 0)
202 			)
203 				levelCurrent++;
204 			if (!lastDeferred && (strcmp(s, "class") == 0))
205 				levelCurrent++;
206 			if (strcmp(s, "end") == 0)
207 				levelCurrent--;
208 			lastDeferred = strcmp(s, "deferred") == 0;
209 		}
210 
211 		if (atEOL) {
212 			int lev = levelPrev;
213 			if (visibleChars == 0)
214 				lev |= SC_FOLDLEVELWHITEFLAG;
215 			if ((levelCurrent > levelPrev) && (visibleChars > 0))
216 				lev |= SC_FOLDLEVELHEADERFLAG;
217 			if (lev != styler.LevelAt(lineCurrent)) {
218 				styler.SetLevel(lineCurrent, lev);
219 			}
220 			lineCurrent++;
221 			levelPrev = levelCurrent;
222 			visibleChars = 0;
223 		}
224 		if (!isspacechar(ch))
225 			visibleChars++;
226 		stylePrev = style;
227 	}
228 	// Fill in the real level of the next line, keeping the current flags as they will be filled in later
229 	int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK;
230 	styler.SetLevel(lineCurrent, levelPrev | flagsNext);
231 }
232 
233 static const char * const eiffelWordListDesc[] = {
234 	"Keywords",
235 	0
236 };
237 
238 LexerModule lmEiffel(SCLEX_EIFFEL, ColouriseEiffelDoc, "eiffel", FoldEiffelDocIndent, eiffelWordListDesc);
239 LexerModule lmEiffelkw(SCLEX_EIFFELKW, ColouriseEiffelDoc, "eiffelkw", FoldEiffelDocKeyWords, eiffelWordListDesc);
240