1 | // Scintilla source code edit control |
2 | /** @file LexInno.cxx |
3 | ** Lexer for Inno Setup scripts. |
4 | **/ |
5 | // Written by Friedrich Vedder <fvedd@t-online.de>, using code from LexOthers.cxx. |
6 | // Modified by Michael Heath. |
7 | // The License.txt file describes the conditions under which this software may be distributed. |
8 | |
9 | #include <stdlib.h> |
10 | #include <string.h> |
11 | #include <stdio.h> |
12 | #include <stdarg.h> |
13 | #include <assert.h> |
14 | #include <ctype.h> |
15 | |
16 | #include <string> |
17 | #include <string_view> |
18 | |
19 | #include "ILexer.h" |
20 | #include "Scintilla.h" |
21 | #include "SciLexer.h" |
22 | |
23 | #include "WordList.h" |
24 | #include "LexAccessor.h" |
25 | #include "Accessor.h" |
26 | #include "StyleContext.h" |
27 | #include "CharacterSet.h" |
28 | #include "LexerModule.h" |
29 | |
30 | using namespace Lexilla; |
31 | |
32 | static bool innoIsBlank(int ch) { |
33 | return (ch == ' ') || (ch == '\t'); |
34 | } |
35 | |
36 | static bool innoNextNotBlankIs(Sci_Position i, Accessor &styler, char needle) { |
37 | char ch; |
38 | |
39 | while (i < styler.Length()) { |
40 | ch = styler.SafeGetCharAt(i); |
41 | |
42 | if (ch == needle) |
43 | return true; |
44 | |
45 | if (!innoIsBlank(ch)) |
46 | return false; |
47 | |
48 | i++; |
49 | } |
50 | return false; |
51 | } |
52 | |
53 | static void ColouriseInnoDoc(Sci_PositionU startPos, Sci_Position length, int, WordList *keywordLists[], Accessor &styler) { |
54 | int state = SCE_INNO_DEFAULT; |
55 | char chPrev; |
56 | char ch = 0; |
57 | char chNext = styler[startPos]; |
58 | Sci_Position lengthDoc = startPos + length; |
59 | char *buffer = new char[length+1]; |
60 | Sci_Position bufferCount = 0; |
61 | bool isBOL, isEOL, isWS, isBOLWS = 0; |
62 | bool = false; |
63 | enum section{None, Code, Messages}; |
64 | |
65 | WordList §ionKeywords = *keywordLists[0]; |
66 | WordList &standardKeywords = *keywordLists[1]; |
67 | WordList ¶meterKeywords = *keywordLists[2]; |
68 | WordList &preprocessorKeywords = *keywordLists[3]; |
69 | WordList &pascalKeywords = *keywordLists[4]; |
70 | WordList &userKeywords = *keywordLists[5]; |
71 | |
72 | Sci_Position curLine = styler.GetLine(startPos); |
73 | int curLineState = curLine > 0 ? styler.GetLineState(curLine - 1) : 0; |
74 | bool isCode = (curLineState == section::Code); |
75 | bool isMessages = (curLineState == section::Messages); |
76 | |
77 | // Go through all provided text segment |
78 | // using the hand-written state machine shown below |
79 | styler.StartAt(startPos); |
80 | styler.StartSegment(startPos); |
81 | for (Sci_Position i = startPos; i < lengthDoc; i++) { |
82 | chPrev = ch; |
83 | ch = chNext; |
84 | chNext = styler.SafeGetCharAt(i + 1); |
85 | |
86 | if (styler.IsLeadByte(ch)) { |
87 | chNext = styler.SafeGetCharAt(i + 2); |
88 | i++; |
89 | continue; |
90 | } |
91 | |
92 | isBOL = (chPrev == 0) || (chPrev == '\n') || (chPrev == '\r' && ch != '\n'); |
93 | isBOLWS = (isBOL) ? 1 : (isBOLWS && (chPrev == ' ' || chPrev == '\t')); |
94 | isEOL = (ch == '\n' || ch == '\r'); |
95 | isWS = (ch == ' ' || ch == '\t'); |
96 | |
97 | if ((ch == '\r' && chNext != '\n') || (ch == '\n')) { |
98 | // Remember the line state for future incremental lexing |
99 | curLine = styler.GetLine(i); |
100 | |
101 | if (isCode) { |
102 | styler.SetLineState(curLine, section::Code); |
103 | } else if (isMessages) { |
104 | styler.SetLineState(curLine, section::Messages); |
105 | } else { |
106 | styler.SetLineState(curLine, section::None); |
107 | } |
108 | } |
109 | |
110 | switch(state) { |
111 | case SCE_INNO_DEFAULT: |
112 | if (!isCode && ch == ';' && isBOLWS) { |
113 | // Start of a comment |
114 | state = SCE_INNO_COMMENT; |
115 | } else if (ch == '[' && isBOLWS) { |
116 | // Start of a section name |
117 | bufferCount = 0; |
118 | state = SCE_INNO_SECTION; |
119 | } else if (ch == '#' && isBOLWS) { |
120 | // Start of a preprocessor directive |
121 | state = SCE_INNO_PREPROC; |
122 | } else if (!isCode && ch == '{' && chNext != '{' && chPrev != '{') { |
123 | // Start of an inline expansion |
124 | state = SCE_INNO_INLINE_EXPANSION; |
125 | } else if (isCode && (ch == '{' || (ch == '(' && chNext == '*'))) { |
126 | // Start of a Pascal comment |
127 | state = SCE_INNO_COMMENT_PASCAL; |
128 | isCStyleComment = false; |
129 | } else if (isCode && ch == '/' && chNext == '/') { |
130 | // Apparently, C-style comments are legal, too |
131 | state = SCE_INNO_COMMENT_PASCAL; |
132 | isCStyleComment = true; |
133 | } else if (!isMessages && ch == '"') { |
134 | // Start of a double-quote string |
135 | state = SCE_INNO_STRING_DOUBLE; |
136 | } else if (!isMessages && ch == '\'') { |
137 | // Start of a single-quote string |
138 | state = SCE_INNO_STRING_SINGLE; |
139 | } else if (!isMessages && IsASCII(ch) && (isalpha(ch) || (ch == '_'))) { |
140 | // Start of an identifier |
141 | bufferCount = 0; |
142 | buffer[bufferCount++] = static_cast<char>(tolower(ch)); |
143 | state = SCE_INNO_IDENTIFIER; |
144 | } else { |
145 | // Style it the default style |
146 | styler.ColourTo(i,SCE_INNO_DEFAULT); |
147 | } |
148 | break; |
149 | |
150 | case SCE_INNO_COMMENT: |
151 | if (isEOL) { |
152 | state = SCE_INNO_DEFAULT; |
153 | styler.ColourTo(i-1,SCE_INNO_COMMENT); |
154 | |
155 | // Push back the faulty character |
156 | chNext = styler[i--]; |
157 | ch = chPrev; |
158 | } |
159 | break; |
160 | |
161 | case SCE_INNO_IDENTIFIER: |
162 | if (IsASCII(ch) && (isalnum(ch) || (ch == '_'))) { |
163 | buffer[bufferCount++] = static_cast<char>(tolower(ch)); |
164 | } else { |
165 | state = SCE_INNO_DEFAULT; |
166 | buffer[bufferCount] = '\0'; |
167 | |
168 | // Check if the buffer contains a keyword |
169 | if (!isCode && standardKeywords.InList(buffer) && innoNextNotBlankIs(i, styler, '=')) { |
170 | styler.ColourTo(i-1,SCE_INNO_KEYWORD); |
171 | } else if (!isCode && parameterKeywords.InList(buffer) && innoNextNotBlankIs(i, styler, ':')) { |
172 | styler.ColourTo(i-1,SCE_INNO_PARAMETER); |
173 | } else if (isCode && pascalKeywords.InList(buffer)) { |
174 | styler.ColourTo(i-1,SCE_INNO_KEYWORD_PASCAL); |
175 | } else if (!isCode && userKeywords.InList(buffer)) { |
176 | styler.ColourTo(i-1,SCE_INNO_KEYWORD_USER); |
177 | } else { |
178 | styler.ColourTo(i-1,SCE_INNO_DEFAULT); |
179 | } |
180 | |
181 | // Push back the faulty character |
182 | chNext = styler[i--]; |
183 | ch = chPrev; |
184 | } |
185 | break; |
186 | |
187 | case SCE_INNO_SECTION: |
188 | if (ch == ']') { |
189 | state = SCE_INNO_DEFAULT; |
190 | buffer[bufferCount] = '\0'; |
191 | |
192 | // Check if the buffer contains a section name |
193 | if (sectionKeywords.InList(buffer)) { |
194 | styler.ColourTo(i,SCE_INNO_SECTION); |
195 | isCode = !CompareCaseInsensitive(buffer, "code" ); |
196 | |
197 | isMessages = isCode ? false : ( |
198 | !CompareCaseInsensitive(buffer, "custommessages" ) |
199 | || !CompareCaseInsensitive(buffer, "messages" )); |
200 | } else { |
201 | styler.ColourTo(i,SCE_INNO_DEFAULT); |
202 | } |
203 | } else if (IsASCII(ch) && (isalnum(ch) || (ch == '_'))) { |
204 | buffer[bufferCount++] = static_cast<char>(tolower(ch)); |
205 | } else { |
206 | state = SCE_INNO_DEFAULT; |
207 | styler.ColourTo(i,SCE_INNO_DEFAULT); |
208 | } |
209 | break; |
210 | |
211 | case SCE_INNO_PREPROC: |
212 | if (isWS || isEOL) { |
213 | if (IsASCII(chPrev) && isalpha(chPrev)) { |
214 | state = SCE_INNO_DEFAULT; |
215 | buffer[bufferCount] = '\0'; |
216 | |
217 | // Check if the buffer contains a preprocessor directive |
218 | if (preprocessorKeywords.InList(buffer)) { |
219 | styler.ColourTo(i-1,SCE_INNO_PREPROC); |
220 | } else { |
221 | styler.ColourTo(i-1,SCE_INNO_DEFAULT); |
222 | } |
223 | |
224 | // Push back the faulty character |
225 | chNext = styler[i--]; |
226 | ch = chPrev; |
227 | } |
228 | } else if (IsASCII(ch) && isalpha(ch)) { |
229 | if (chPrev == '#' || chPrev == ' ' || chPrev == '\t') |
230 | bufferCount = 0; |
231 | buffer[bufferCount++] = static_cast<char>(tolower(ch)); |
232 | } |
233 | break; |
234 | |
235 | case SCE_INNO_STRING_DOUBLE: |
236 | if (ch == '"') { |
237 | state = SCE_INNO_DEFAULT; |
238 | styler.ColourTo(i,SCE_INNO_STRING_DOUBLE); |
239 | } else if (isEOL) { |
240 | state = SCE_INNO_DEFAULT; |
241 | styler.ColourTo(i-1,SCE_INNO_STRING_DOUBLE); |
242 | |
243 | // Push back the faulty character |
244 | chNext = styler[i--]; |
245 | ch = chPrev; |
246 | } |
247 | break; |
248 | |
249 | case SCE_INNO_STRING_SINGLE: |
250 | if (ch == '\'') { |
251 | state = SCE_INNO_DEFAULT; |
252 | styler.ColourTo(i,SCE_INNO_STRING_SINGLE); |
253 | } else if (isEOL) { |
254 | state = SCE_INNO_DEFAULT; |
255 | styler.ColourTo(i-1,SCE_INNO_STRING_SINGLE); |
256 | |
257 | // Push back the faulty character |
258 | chNext = styler[i--]; |
259 | ch = chPrev; |
260 | } |
261 | break; |
262 | |
263 | case SCE_INNO_INLINE_EXPANSION: |
264 | if (ch == '}') { |
265 | state = SCE_INNO_DEFAULT; |
266 | styler.ColourTo(i,SCE_INNO_INLINE_EXPANSION); |
267 | } else if (isEOL) { |
268 | state = SCE_INNO_DEFAULT; |
269 | styler.ColourTo(i,SCE_INNO_DEFAULT); |
270 | } |
271 | break; |
272 | |
273 | case SCE_INNO_COMMENT_PASCAL: |
274 | if (isCStyleComment) { |
275 | if (isEOL) { |
276 | state = SCE_INNO_DEFAULT; |
277 | styler.ColourTo(i-1,SCE_INNO_COMMENT_PASCAL); |
278 | |
279 | // Push back the faulty character |
280 | chNext = styler[i--]; |
281 | ch = chPrev; |
282 | } |
283 | } else { |
284 | if (ch == '}' || (ch == ')' && chPrev == '*')) { |
285 | state = SCE_INNO_DEFAULT; |
286 | styler.ColourTo(i,SCE_INNO_COMMENT_PASCAL); |
287 | } else if (isEOL) { |
288 | state = SCE_INNO_DEFAULT; |
289 | styler.ColourTo(i,SCE_INNO_DEFAULT); |
290 | } |
291 | } |
292 | break; |
293 | |
294 | } |
295 | } |
296 | delete []buffer; |
297 | } |
298 | |
299 | static const char * const innoWordListDesc[] = { |
300 | "Sections" , |
301 | "Keywords" , |
302 | "Parameters" , |
303 | "Preprocessor directives" , |
304 | "Pascal keywords" , |
305 | "User defined keywords" , |
306 | 0 |
307 | }; |
308 | |
309 | static void FoldInnoDoc(Sci_PositionU startPos, Sci_Position length, int, WordList *[], Accessor &styler) { |
310 | Sci_PositionU endPos = startPos + length; |
311 | char chNext = styler[startPos]; |
312 | |
313 | Sci_Position lineCurrent = styler.GetLine(startPos); |
314 | |
315 | bool sectionFlag = false; |
316 | int levelPrev = lineCurrent > 0 ? styler.LevelAt(lineCurrent - 1) : SC_FOLDLEVELBASE; |
317 | int level; |
318 | |
319 | for (Sci_PositionU i = startPos; i < endPos; i++) { |
320 | char ch = chNext; |
321 | chNext = styler[i+1]; |
322 | bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n'); |
323 | int style = styler.StyleAt(i); |
324 | |
325 | if (style == SCE_INNO_SECTION) |
326 | sectionFlag = true; |
327 | |
328 | if (atEOL || i == endPos - 1) { |
329 | if (sectionFlag) { |
330 | level = SC_FOLDLEVELBASE | SC_FOLDLEVELHEADERFLAG; |
331 | if (level == levelPrev) |
332 | styler.SetLevel(lineCurrent - 1, levelPrev & ~SC_FOLDLEVELHEADERFLAG); |
333 | } else { |
334 | level = levelPrev & SC_FOLDLEVELNUMBERMASK; |
335 | if (levelPrev & SC_FOLDLEVELHEADERFLAG) |
336 | level++; |
337 | } |
338 | |
339 | styler.SetLevel(lineCurrent, level); |
340 | |
341 | levelPrev = level; |
342 | lineCurrent++; |
343 | sectionFlag = false; |
344 | } |
345 | } |
346 | } |
347 | |
348 | LexerModule lmInno(SCLEX_INNOSETUP, ColouriseInnoDoc, "inno" , FoldInnoDoc, innoWordListDesc); |
349 | |