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 // 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
ColouriseInnoDoc(Sci_PositionU startPos,Sci_Position length,int,WordList * keywordLists[],Accessor & styler)28 static void ColouriseInnoDoc(Sci_PositionU startPos, Sci_Position length, int, WordList *keywordLists[], Accessor &styler) {
29 int state = SCE_INNO_DEFAULT;
30 char chPrev;
31 char ch = 0;
32 char chNext = styler[startPos];
33 Sci_Position lengthDoc = startPos + length;
34 char *buffer = new char[length+1];
35 Sci_Position bufferCount = 0;
36 bool isBOL, isEOL, isWS, isBOLWS = 0;
37 bool isCStyleComment = false;
38
39 WordList §ionKeywords = *keywordLists[0];
40 WordList &standardKeywords = *keywordLists[1];
41 WordList ¶meterKeywords = *keywordLists[2];
42 WordList &preprocessorKeywords = *keywordLists[3];
43 WordList &pascalKeywords = *keywordLists[4];
44 WordList &userKeywords = *keywordLists[5];
45
46 Sci_Position curLine = styler.GetLine(startPos);
47 int curLineState = curLine > 0 ? styler.GetLineState(curLine - 1) : 0;
48 bool isCode = (curLineState == 1);
49
50 // Go through all provided text segment
51 // using the hand-written state machine shown below
52 styler.StartAt(startPos);
53 styler.StartSegment(startPos);
54 for (Sci_Position i = startPos; i < lengthDoc; i++) {
55 chPrev = ch;
56 ch = chNext;
57 chNext = styler.SafeGetCharAt(i + 1);
58
59 if (styler.IsLeadByte(ch)) {
60 chNext = styler.SafeGetCharAt(i + 2);
61 i++;
62 continue;
63 }
64
65 isBOL = (chPrev == 0) || (chPrev == '\n') || (chPrev == '\r' && ch != '\n');
66 isBOLWS = (isBOL) ? 1 : (isBOLWS && (chPrev == ' ' || chPrev == '\t'));
67 isEOL = (ch == '\n' || ch == '\r');
68 isWS = (ch == ' ' || ch == '\t');
69
70 if ((ch == '\r' && chNext != '\n') || (ch == '\n')) {
71 // Remember the line state for future incremental lexing
72 curLine = styler.GetLine(i);
73 styler.SetLineState(curLine, (isCode ? 1 : 0));
74 }
75
76 switch(state) {
77 case SCE_INNO_DEFAULT:
78 if (!isCode && ch == ';' && isBOLWS) {
79 // Start of a comment
80 state = SCE_INNO_COMMENT;
81 } else if (ch == '[' && isBOLWS) {
82 // Start of a section name
83 bufferCount = 0;
84 state = SCE_INNO_SECTION;
85 } else if (ch == '#' && isBOLWS) {
86 // Start of a preprocessor directive
87 state = SCE_INNO_PREPROC;
88 } else if (!isCode && ch == '{' && chNext != '{' && chPrev != '{') {
89 // Start of an inline expansion
90 state = SCE_INNO_INLINE_EXPANSION;
91 } else if (isCode && (ch == '{' || (ch == '(' && chNext == '*'))) {
92 // Start of a Pascal comment
93 state = SCE_INNO_COMMENT_PASCAL;
94 isCStyleComment = false;
95 } else if (isCode && ch == '/' && chNext == '/') {
96 // Apparently, C-style comments are legal, too
97 state = SCE_INNO_COMMENT_PASCAL;
98 isCStyleComment = true;
99 } else if (ch == '"') {
100 // Start of a double-quote string
101 state = SCE_INNO_STRING_DOUBLE;
102 } else if (ch == '\'') {
103 // Start of a single-quote string
104 state = SCE_INNO_STRING_SINGLE;
105 } else if (IsASCII(ch) && (isalpha(ch) || (ch == '_'))) {
106 // Start of an identifier
107 bufferCount = 0;
108 buffer[bufferCount++] = static_cast<char>(tolower(ch));
109 state = SCE_INNO_IDENTIFIER;
110 } else {
111 // Style it the default style
112 styler.ColourTo(i,SCE_INNO_DEFAULT);
113 }
114 break;
115
116 case SCE_INNO_COMMENT:
117 if (isEOL) {
118 state = SCE_INNO_DEFAULT;
119 styler.ColourTo(i,SCE_INNO_COMMENT);
120 }
121 break;
122
123 case SCE_INNO_IDENTIFIER:
124 if (IsASCII(ch) && (isalnum(ch) || (ch == '_'))) {
125 buffer[bufferCount++] = static_cast<char>(tolower(ch));
126 } else {
127 state = SCE_INNO_DEFAULT;
128 buffer[bufferCount] = '\0';
129
130 // Check if the buffer contains a keyword
131 if (!isCode && standardKeywords.InList(buffer)) {
132 styler.ColourTo(i-1,SCE_INNO_KEYWORD);
133 } else if (!isCode && parameterKeywords.InList(buffer)) {
134 styler.ColourTo(i-1,SCE_INNO_PARAMETER);
135 } else if (isCode && pascalKeywords.InList(buffer)) {
136 styler.ColourTo(i-1,SCE_INNO_KEYWORD_PASCAL);
137 } else if (!isCode && userKeywords.InList(buffer)) {
138 styler.ColourTo(i-1,SCE_INNO_KEYWORD_USER);
139 } else {
140 styler.ColourTo(i-1,SCE_INNO_DEFAULT);
141 }
142
143 // Push back the faulty character
144 chNext = styler[i--];
145 ch = chPrev;
146 }
147 break;
148
149 case SCE_INNO_SECTION:
150 if (ch == ']') {
151 state = SCE_INNO_DEFAULT;
152 buffer[bufferCount] = '\0';
153
154 // Check if the buffer contains a section name
155 if (sectionKeywords.InList(buffer)) {
156 styler.ColourTo(i,SCE_INNO_SECTION);
157 isCode = !CompareCaseInsensitive(buffer, "code");
158 } else {
159 styler.ColourTo(i,SCE_INNO_DEFAULT);
160 }
161 } else if (IsASCII(ch) && (isalnum(ch) || (ch == '_'))) {
162 buffer[bufferCount++] = static_cast<char>(tolower(ch));
163 } else {
164 state = SCE_INNO_DEFAULT;
165 styler.ColourTo(i,SCE_INNO_DEFAULT);
166 }
167 break;
168
169 case SCE_INNO_PREPROC:
170 if (isWS || isEOL) {
171 if (IsASCII(chPrev) && isalpha(chPrev)) {
172 state = SCE_INNO_DEFAULT;
173 buffer[bufferCount] = '\0';
174
175 // Check if the buffer contains a preprocessor directive
176 if (preprocessorKeywords.InList(buffer)) {
177 styler.ColourTo(i-1,SCE_INNO_PREPROC);
178 } else {
179 styler.ColourTo(i-1,SCE_INNO_DEFAULT);
180 }
181
182 // Push back the faulty character
183 chNext = styler[i--];
184 ch = chPrev;
185 }
186 } else if (IsASCII(ch) && isalpha(ch)) {
187 if (chPrev == '#' || chPrev == ' ' || chPrev == '\t')
188 bufferCount = 0;
189 buffer[bufferCount++] = static_cast<char>(tolower(ch));
190 }
191 break;
192
193 case SCE_INNO_STRING_DOUBLE:
194 if (ch == '"' || isEOL) {
195 state = SCE_INNO_DEFAULT;
196 styler.ColourTo(i,SCE_INNO_STRING_DOUBLE);
197 }
198 break;
199
200 case SCE_INNO_STRING_SINGLE:
201 if (ch == '\'' || isEOL) {
202 state = SCE_INNO_DEFAULT;
203 styler.ColourTo(i,SCE_INNO_STRING_SINGLE);
204 }
205 break;
206
207 case SCE_INNO_INLINE_EXPANSION:
208 if (ch == '}') {
209 state = SCE_INNO_DEFAULT;
210 styler.ColourTo(i,SCE_INNO_INLINE_EXPANSION);
211 } else if (isEOL) {
212 state = SCE_INNO_DEFAULT;
213 styler.ColourTo(i,SCE_INNO_DEFAULT);
214 }
215 break;
216
217 case SCE_INNO_COMMENT_PASCAL:
218 if (isCStyleComment) {
219 if (isEOL) {
220 state = SCE_INNO_DEFAULT;
221 styler.ColourTo(i,SCE_INNO_COMMENT_PASCAL);
222 }
223 } else {
224 if (ch == '}' || (ch == ')' && chPrev == '*')) {
225 state = SCE_INNO_DEFAULT;
226 styler.ColourTo(i,SCE_INNO_COMMENT_PASCAL);
227 } else if (isEOL) {
228 state = SCE_INNO_DEFAULT;
229 styler.ColourTo(i,SCE_INNO_DEFAULT);
230 }
231 }
232 break;
233
234 }
235 }
236 delete []buffer;
237 }
238
239 static const char * const innoWordListDesc[] = {
240 "Sections",
241 "Keywords",
242 "Parameters",
243 "Preprocessor directives",
244 "Pascal keywords",
245 "User defined keywords",
246 0
247 };
248
FoldInnoDoc(Sci_PositionU startPos,Sci_Position length,int,WordList * [],Accessor & styler)249 static void FoldInnoDoc(Sci_PositionU startPos, Sci_Position length, int, WordList *[], Accessor &styler) {
250 Sci_PositionU endPos = startPos + length;
251 char chNext = styler[startPos];
252
253 Sci_Position lineCurrent = styler.GetLine(startPos);
254
255 bool sectionFlag = false;
256 int levelPrev = lineCurrent > 0 ? styler.LevelAt(lineCurrent - 1) : SC_FOLDLEVELBASE;
257 int level;
258
259 for (Sci_PositionU i = startPos; i < endPos; i++) {
260 char ch = chNext;
261 chNext = styler[i+1];
262 bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
263 int style = styler.StyleAt(i);
264
265 if (style == SCE_INNO_SECTION)
266 sectionFlag = true;
267
268 if (atEOL || i == endPos - 1) {
269 if (sectionFlag) {
270 level = SC_FOLDLEVELBASE | SC_FOLDLEVELHEADERFLAG;
271 if (level == levelPrev)
272 styler.SetLevel(lineCurrent - 1, levelPrev & ~SC_FOLDLEVELHEADERFLAG);
273 } else {
274 level = levelPrev & SC_FOLDLEVELNUMBERMASK;
275 if (levelPrev & SC_FOLDLEVELHEADERFLAG)
276 level++;
277 }
278
279 styler.SetLevel(lineCurrent, level);
280
281 levelPrev = level;
282 lineCurrent++;
283 sectionFlag = false;
284 }
285 }
286 }
287
288 LexerModule lmInno(SCLEX_INNOSETUP, ColouriseInnoDoc, "inno", FoldInnoDoc, innoWordListDesc);
289