Improve API docs related to keybindings configuration file
[geany-mirror.git] / scintilla / lexers / LexBatch.cxx
blob377b2cbf1eab68a87e082cae425a023ac63e8cae
1 // Scintilla source code edit control
2 /** @file LexBatch.cxx
3 ** Lexer for batch files.
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.
8 #include <stdlib.h>
9 #include <string.h>
10 #include <stdio.h>
11 #include <stdarg.h>
12 #include <assert.h>
13 #include <ctype.h>
15 #include "ILexer.h"
16 #include "Scintilla.h"
17 #include "SciLexer.h"
19 #include "WordList.h"
20 #include "LexAccessor.h"
21 #include "Accessor.h"
22 #include "StyleContext.h"
23 #include "CharacterSet.h"
24 #include "LexerModule.h"
26 #ifdef SCI_NAMESPACE
27 using namespace Scintilla;
28 #endif
30 static bool Is0To9(char ch) {
31 return (ch >= '0') && (ch <= '9');
34 static bool IsAlphabetic(int ch) {
35 return IsASCII(ch) && isalpha(ch);
38 static inline bool AtEOL(Accessor &styler, Sci_PositionU i) {
39 return (styler[i] == '\n') ||
40 ((styler[i] == '\r') && (styler.SafeGetCharAt(i + 1) != '\n'));
43 // Tests for BATCH Operators
44 static bool IsBOperator(char ch) {
45 return (ch == '=') || (ch == '+') || (ch == '>') || (ch == '<') ||
46 (ch == '|') || (ch == '?') || (ch == '*');
49 // Tests for BATCH Separators
50 static bool IsBSeparator(char ch) {
51 return (ch == '\\') || (ch == '.') || (ch == ';') ||
52 (ch == '\"') || (ch == '\'') || (ch == '/');
55 static void ColouriseBatchLine(
56 char *lineBuffer,
57 Sci_PositionU lengthLine,
58 Sci_PositionU startLine,
59 Sci_PositionU endPos,
60 WordList *keywordlists[],
61 Accessor &styler) {
63 Sci_PositionU offset = 0; // Line Buffer Offset
64 Sci_PositionU cmdLoc; // External Command / Program Location
65 char wordBuffer[81]; // Word Buffer - large to catch long paths
66 Sci_PositionU wbl; // Word Buffer Length
67 Sci_PositionU wbo; // Word Buffer Offset - also Special Keyword Buffer Length
68 WordList &keywords = *keywordlists[0]; // Internal Commands
69 WordList &keywords2 = *keywordlists[1]; // External Commands (optional)
71 // CHOICE, ECHO, GOTO, PROMPT and SET have Default Text that may contain Regular Keywords
72 // Toggling Regular Keyword Checking off improves readability
73 // Other Regular Keywords and External Commands / Programs might also benefit from toggling
74 // Need a more robust algorithm to properly toggle Regular Keyword Checking
75 bool continueProcessing = true; // Used to toggle Regular Keyword Checking
76 // Special Keywords are those that allow certain characters without whitespace after the command
77 // Examples are: cd. cd\ md. rd. dir| dir> echo: echo. path=
78 // Special Keyword Buffer used to determine if the first n characters is a Keyword
79 char sKeywordBuffer[10]; // Special Keyword Buffer
80 bool sKeywordFound; // Exit Special Keyword for-loop if found
82 // Skip initial spaces
83 while ((offset < lengthLine) && (isspacechar(lineBuffer[offset]))) {
84 offset++;
86 // Colorize Default Text
87 styler.ColourTo(startLine + offset - 1, SCE_BAT_DEFAULT);
88 // Set External Command / Program Location
89 cmdLoc = offset;
91 // Check for Fake Label (Comment) or Real Label - return if found
92 if (lineBuffer[offset] == ':') {
93 if (lineBuffer[offset + 1] == ':') {
94 // Colorize Fake Label (Comment) - :: is similar to REM, see http://content.techweb.com/winmag/columns/explorer/2000/21.htm
95 styler.ColourTo(endPos, SCE_BAT_COMMENT);
96 } else {
97 // Colorize Real Label
98 styler.ColourTo(endPos, SCE_BAT_LABEL);
100 return;
101 // Check for Drive Change (Drive Change is internal command) - return if found
102 } else if ((IsAlphabetic(lineBuffer[offset])) &&
103 (lineBuffer[offset + 1] == ':') &&
104 ((isspacechar(lineBuffer[offset + 2])) ||
105 (((lineBuffer[offset + 2] == '\\')) &&
106 (isspacechar(lineBuffer[offset + 3]))))) {
107 // Colorize Regular Keyword
108 styler.ColourTo(endPos, SCE_BAT_WORD);
109 return;
112 // Check for Hide Command (@ECHO OFF/ON)
113 if (lineBuffer[offset] == '@') {
114 styler.ColourTo(startLine + offset, SCE_BAT_HIDE);
115 offset++;
117 // Skip next spaces
118 while ((offset < lengthLine) && (isspacechar(lineBuffer[offset]))) {
119 offset++;
122 // Read remainder of line word-at-a-time or remainder-of-word-at-a-time
123 while (offset < lengthLine) {
124 if (offset > startLine) {
125 // Colorize Default Text
126 styler.ColourTo(startLine + offset - 1, SCE_BAT_DEFAULT);
128 // Copy word from Line Buffer into Word Buffer
129 wbl = 0;
130 for (; offset < lengthLine && wbl < 80 &&
131 !isspacechar(lineBuffer[offset]); wbl++, offset++) {
132 wordBuffer[wbl] = static_cast<char>(tolower(lineBuffer[offset]));
134 wordBuffer[wbl] = '\0';
135 wbo = 0;
137 // Check for Comment - return if found
138 if (CompareCaseInsensitive(wordBuffer, "rem") == 0) {
139 styler.ColourTo(endPos, SCE_BAT_COMMENT);
140 return;
142 // Check for Separator
143 if (IsBSeparator(wordBuffer[0])) {
144 // Check for External Command / Program
145 if ((cmdLoc == offset - wbl) &&
146 ((wordBuffer[0] == ':') ||
147 (wordBuffer[0] == '\\') ||
148 (wordBuffer[0] == '.'))) {
149 // Reset Offset to re-process remainder of word
150 offset -= (wbl - 1);
151 // Colorize External Command / Program
152 if (!keywords2) {
153 styler.ColourTo(startLine + offset - 1, SCE_BAT_COMMAND);
154 } else if (keywords2.InList(wordBuffer)) {
155 styler.ColourTo(startLine + offset - 1, SCE_BAT_COMMAND);
156 } else {
157 styler.ColourTo(startLine + offset - 1, SCE_BAT_DEFAULT);
159 // Reset External Command / Program Location
160 cmdLoc = offset;
161 } else {
162 // Reset Offset to re-process remainder of word
163 offset -= (wbl - 1);
164 // Colorize Default Text
165 styler.ColourTo(startLine + offset - 1, SCE_BAT_DEFAULT);
167 // Check for Regular Keyword in list
168 } else if ((keywords.InList(wordBuffer)) &&
169 (continueProcessing)) {
170 // ECHO, GOTO, PROMPT and SET require no further Regular Keyword Checking
171 if ((CompareCaseInsensitive(wordBuffer, "echo") == 0) ||
172 (CompareCaseInsensitive(wordBuffer, "goto") == 0) ||
173 (CompareCaseInsensitive(wordBuffer, "prompt") == 0) ||
174 (CompareCaseInsensitive(wordBuffer, "set") == 0)) {
175 continueProcessing = false;
177 // Identify External Command / Program Location for ERRORLEVEL, and EXIST
178 if ((CompareCaseInsensitive(wordBuffer, "errorlevel") == 0) ||
179 (CompareCaseInsensitive(wordBuffer, "exist") == 0)) {
180 // Reset External Command / Program Location
181 cmdLoc = offset;
182 // Skip next spaces
183 while ((cmdLoc < lengthLine) &&
184 (isspacechar(lineBuffer[cmdLoc]))) {
185 cmdLoc++;
187 // Skip comparison
188 while ((cmdLoc < lengthLine) &&
189 (!isspacechar(lineBuffer[cmdLoc]))) {
190 cmdLoc++;
192 // Skip next spaces
193 while ((cmdLoc < lengthLine) &&
194 (isspacechar(lineBuffer[cmdLoc]))) {
195 cmdLoc++;
197 // Identify External Command / Program Location for CALL, DO, LOADHIGH and LH
198 } else if ((CompareCaseInsensitive(wordBuffer, "call") == 0) ||
199 (CompareCaseInsensitive(wordBuffer, "do") == 0) ||
200 (CompareCaseInsensitive(wordBuffer, "loadhigh") == 0) ||
201 (CompareCaseInsensitive(wordBuffer, "lh") == 0)) {
202 // Reset External Command / Program Location
203 cmdLoc = offset;
204 // Skip next spaces
205 while ((cmdLoc < lengthLine) &&
206 (isspacechar(lineBuffer[cmdLoc]))) {
207 cmdLoc++;
210 // Colorize Regular keyword
211 styler.ColourTo(startLine + offset - 1, SCE_BAT_WORD);
212 // No need to Reset Offset
213 // Check for Special Keyword in list, External Command / Program, or Default Text
214 } else if ((wordBuffer[0] != '%') &&
215 (wordBuffer[0] != '!') &&
216 (!IsBOperator(wordBuffer[0])) &&
217 (continueProcessing)) {
218 // Check for Special Keyword
219 // Affected Commands are in Length range 2-6
220 // Good that ERRORLEVEL, EXIST, CALL, DO, LOADHIGH, and LH are unaffected
221 sKeywordFound = false;
222 for (Sci_PositionU keywordLength = 2; keywordLength < wbl && keywordLength < 7 && !sKeywordFound; keywordLength++) {
223 wbo = 0;
224 // Copy Keyword Length from Word Buffer into Special Keyword Buffer
225 for (; wbo < keywordLength; wbo++) {
226 sKeywordBuffer[wbo] = static_cast<char>(wordBuffer[wbo]);
228 sKeywordBuffer[wbo] = '\0';
229 // Check for Special Keyword in list
230 if ((keywords.InList(sKeywordBuffer)) &&
231 ((IsBOperator(wordBuffer[wbo])) ||
232 (IsBSeparator(wordBuffer[wbo])))) {
233 sKeywordFound = true;
234 // ECHO requires no further Regular Keyword Checking
235 if (CompareCaseInsensitive(sKeywordBuffer, "echo") == 0) {
236 continueProcessing = false;
238 // Colorize Special Keyword as Regular Keyword
239 styler.ColourTo(startLine + offset - 1 - (wbl - wbo), SCE_BAT_WORD);
240 // Reset Offset to re-process remainder of word
241 offset -= (wbl - wbo);
244 // Check for External Command / Program or Default Text
245 if (!sKeywordFound) {
246 wbo = 0;
247 // Check for External Command / Program
248 if (cmdLoc == offset - wbl) {
249 // Read up to %, Operator or Separator
250 while ((wbo < wbl) &&
251 (wordBuffer[wbo] != '%') &&
252 (wordBuffer[wbo] != '!') &&
253 (!IsBOperator(wordBuffer[wbo])) &&
254 (!IsBSeparator(wordBuffer[wbo]))) {
255 wbo++;
257 // Reset External Command / Program Location
258 cmdLoc = offset - (wbl - wbo);
259 // Reset Offset to re-process remainder of word
260 offset -= (wbl - wbo);
261 // CHOICE requires no further Regular Keyword Checking
262 if (CompareCaseInsensitive(wordBuffer, "choice") == 0) {
263 continueProcessing = false;
265 // Check for START (and its switches) - What follows is External Command \ Program
266 if (CompareCaseInsensitive(wordBuffer, "start") == 0) {
267 // Reset External Command / Program Location
268 cmdLoc = offset;
269 // Skip next spaces
270 while ((cmdLoc < lengthLine) &&
271 (isspacechar(lineBuffer[cmdLoc]))) {
272 cmdLoc++;
274 // Reset External Command / Program Location if command switch detected
275 if (lineBuffer[cmdLoc] == '/') {
276 // Skip command switch
277 while ((cmdLoc < lengthLine) &&
278 (!isspacechar(lineBuffer[cmdLoc]))) {
279 cmdLoc++;
281 // Skip next spaces
282 while ((cmdLoc < lengthLine) &&
283 (isspacechar(lineBuffer[cmdLoc]))) {
284 cmdLoc++;
288 // Colorize External Command / Program
289 if (!keywords2) {
290 styler.ColourTo(startLine + offset - 1, SCE_BAT_COMMAND);
291 } else if (keywords2.InList(wordBuffer)) {
292 styler.ColourTo(startLine + offset - 1, SCE_BAT_COMMAND);
293 } else {
294 styler.ColourTo(startLine + offset - 1, SCE_BAT_DEFAULT);
296 // No need to Reset Offset
297 // Check for Default Text
298 } else {
299 // Read up to %, Operator or Separator
300 while ((wbo < wbl) &&
301 (wordBuffer[wbo] != '%') &&
302 (wordBuffer[wbo] != '!') &&
303 (!IsBOperator(wordBuffer[wbo])) &&
304 (!IsBSeparator(wordBuffer[wbo]))) {
305 wbo++;
307 // Colorize Default Text
308 styler.ColourTo(startLine + offset - 1 - (wbl - wbo), SCE_BAT_DEFAULT);
309 // Reset Offset to re-process remainder of word
310 offset -= (wbl - wbo);
313 // Check for Argument (%n), Environment Variable (%x...%) or Local Variable (%%a)
314 } else if (wordBuffer[0] == '%') {
315 // Colorize Default Text
316 styler.ColourTo(startLine + offset - 1 - wbl, SCE_BAT_DEFAULT);
317 wbo++;
318 // Search to end of word for second % (can be a long path)
319 while ((wbo < wbl) &&
320 (wordBuffer[wbo] != '%') &&
321 (!IsBOperator(wordBuffer[wbo])) &&
322 (!IsBSeparator(wordBuffer[wbo]))) {
323 wbo++;
325 // Check for Argument (%n) or (%*)
326 if (((Is0To9(wordBuffer[1])) || (wordBuffer[1] == '*')) &&
327 (wordBuffer[wbo] != '%')) {
328 // Check for External Command / Program
329 if (cmdLoc == offset - wbl) {
330 cmdLoc = offset - (wbl - 2);
332 // Colorize Argument
333 styler.ColourTo(startLine + offset - 1 - (wbl - 2), SCE_BAT_IDENTIFIER);
334 // Reset Offset to re-process remainder of word
335 offset -= (wbl - 2);
336 // Check for Expanded Argument (%~...) / Variable (%%~...)
337 } else if (((wbl > 1) && (wordBuffer[1] == '~')) ||
338 ((wbl > 2) && (wordBuffer[1] == '%') && (wordBuffer[2] == '~'))) {
339 // Check for External Command / Program
340 if (cmdLoc == offset - wbl) {
341 cmdLoc = offset - (wbl - wbo);
343 // Colorize Expanded Argument / Variable
344 styler.ColourTo(startLine + offset - 1 - (wbl - wbo), SCE_BAT_IDENTIFIER);
345 // Reset Offset to re-process remainder of word
346 offset -= (wbl - wbo);
347 // Check for Environment Variable (%x...%)
348 } else if ((wordBuffer[1] != '%') &&
349 (wordBuffer[wbo] == '%')) {
350 wbo++;
351 // Check for External Command / Program
352 if (cmdLoc == offset - wbl) {
353 cmdLoc = offset - (wbl - wbo);
355 // Colorize Environment Variable
356 styler.ColourTo(startLine + offset - 1 - (wbl - wbo), SCE_BAT_IDENTIFIER);
357 // Reset Offset to re-process remainder of word
358 offset -= (wbl - wbo);
359 // Check for Local Variable (%%a)
360 } else if (
361 (wbl > 2) &&
362 (wordBuffer[1] == '%') &&
363 (wordBuffer[2] != '%') &&
364 (!IsBOperator(wordBuffer[2])) &&
365 (!IsBSeparator(wordBuffer[2]))) {
366 // Check for External Command / Program
367 if (cmdLoc == offset - wbl) {
368 cmdLoc = offset - (wbl - 3);
370 // Colorize Local Variable
371 styler.ColourTo(startLine + offset - 1 - (wbl - 3), SCE_BAT_IDENTIFIER);
372 // Reset Offset to re-process remainder of word
373 offset -= (wbl - 3);
375 // Check for Environment Variable (!x...!)
376 } else if (wordBuffer[0] == '!') {
377 // Colorize Default Text
378 styler.ColourTo(startLine + offset - 1 - wbl, SCE_BAT_DEFAULT);
379 wbo++;
380 // Search to end of word for second ! (can be a long path)
381 while ((wbo < wbl) &&
382 (wordBuffer[wbo] != '!') &&
383 (!IsBOperator(wordBuffer[wbo])) &&
384 (!IsBSeparator(wordBuffer[wbo]))) {
385 wbo++;
387 if (wordBuffer[wbo] == '!') {
388 wbo++;
389 // Check for External Command / Program
390 if (cmdLoc == offset - wbl) {
391 cmdLoc = offset - (wbl - wbo);
393 // Colorize Environment Variable
394 styler.ColourTo(startLine + offset - 1 - (wbl - wbo), SCE_BAT_IDENTIFIER);
395 // Reset Offset to re-process remainder of word
396 offset -= (wbl - wbo);
398 // Check for Operator
399 } else if (IsBOperator(wordBuffer[0])) {
400 // Colorize Default Text
401 styler.ColourTo(startLine + offset - 1 - wbl, SCE_BAT_DEFAULT);
402 // Check for Comparison Operator
403 if ((wordBuffer[0] == '=') && (wordBuffer[1] == '=')) {
404 // Identify External Command / Program Location for IF
405 cmdLoc = offset;
406 // Skip next spaces
407 while ((cmdLoc < lengthLine) &&
408 (isspacechar(lineBuffer[cmdLoc]))) {
409 cmdLoc++;
411 // Colorize Comparison Operator
412 styler.ColourTo(startLine + offset - 1 - (wbl - 2), SCE_BAT_OPERATOR);
413 // Reset Offset to re-process remainder of word
414 offset -= (wbl - 2);
415 // Check for Pipe Operator
416 } else if (wordBuffer[0] == '|') {
417 // Reset External Command / Program Location
418 cmdLoc = offset - wbl + 1;
419 // Skip next spaces
420 while ((cmdLoc < lengthLine) &&
421 (isspacechar(lineBuffer[cmdLoc]))) {
422 cmdLoc++;
424 // Colorize Pipe Operator
425 styler.ColourTo(startLine + offset - 1 - (wbl - 1), SCE_BAT_OPERATOR);
426 // Reset Offset to re-process remainder of word
427 offset -= (wbl - 1);
428 // Check for Other Operator
429 } else {
430 // Check for > Operator
431 if (wordBuffer[0] == '>') {
432 // Turn Keyword and External Command / Program checking back on
433 continueProcessing = true;
435 // Colorize Other Operator
436 styler.ColourTo(startLine + offset - 1 - (wbl - 1), SCE_BAT_OPERATOR);
437 // Reset Offset to re-process remainder of word
438 offset -= (wbl - 1);
440 // Check for Default Text
441 } else {
442 // Read up to %, Operator or Separator
443 while ((wbo < wbl) &&
444 (wordBuffer[wbo] != '%') &&
445 (wordBuffer[wbo] != '!') &&
446 (!IsBOperator(wordBuffer[wbo])) &&
447 (!IsBSeparator(wordBuffer[wbo]))) {
448 wbo++;
450 // Colorize Default Text
451 styler.ColourTo(startLine + offset - 1 - (wbl - wbo), SCE_BAT_DEFAULT);
452 // Reset Offset to re-process remainder of word
453 offset -= (wbl - wbo);
455 // Skip next spaces - nothing happens if Offset was Reset
456 while ((offset < lengthLine) && (isspacechar(lineBuffer[offset]))) {
457 offset++;
460 // Colorize Default Text for remainder of line - currently not lexed
461 styler.ColourTo(endPos, SCE_BAT_DEFAULT);
464 static void ColouriseBatchDoc(
465 Sci_PositionU startPos,
466 Sci_Position length,
467 int /*initStyle*/,
468 WordList *keywordlists[],
469 Accessor &styler) {
471 char lineBuffer[1024];
473 styler.StartAt(startPos);
474 styler.StartSegment(startPos);
475 Sci_PositionU linePos = 0;
476 Sci_PositionU startLine = startPos;
477 for (Sci_PositionU i = startPos; i < startPos + length; i++) {
478 lineBuffer[linePos++] = styler[i];
479 if (AtEOL(styler, i) || (linePos >= sizeof(lineBuffer) - 1)) {
480 // End of line (or of line buffer) met, colourise it
481 lineBuffer[linePos] = '\0';
482 ColouriseBatchLine(lineBuffer, linePos, startLine, i, keywordlists, styler);
483 linePos = 0;
484 startLine = i + 1;
487 if (linePos > 0) { // Last line does not have ending characters
488 lineBuffer[linePos] = '\0';
489 ColouriseBatchLine(lineBuffer, linePos, startLine, startPos + length - 1,
490 keywordlists, styler);
494 static const char *const batchWordListDesc[] = {
495 "Internal Commands",
496 "External Commands",
500 LexerModule lmBatch(SCLEX_BATCH, ColouriseBatchDoc, "batch", 0, batchWordListDesc);