1// Scintilla source code edit control
2/** @file LexMake.cxx
3 ** Lexer for make 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.
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 <string>
16#include <string_view>
17
18#include "ILexer.h"
19#include "Scintilla.h"
20#include "SciLexer.h"
21
22#include "WordList.h"
23#include "LexAccessor.h"
24#include "Accessor.h"
25#include "StyleContext.h"
26#include "CharacterSet.h"
27#include "LexerModule.h"
28
29using namespace Lexilla;
30
31static inline bool AtEOL(Accessor &styler, Sci_PositionU i) {
32 return (styler[i] == '\n') ||
33 ((styler[i] == '\r') && (styler.SafeGetCharAt(i + 1) != '\n'));
34}
35
36static void ColouriseMakeLine(
37 char *lineBuffer,
38 Sci_PositionU lengthLine,
39 Sci_PositionU startLine,
40 Sci_PositionU endPos,
41 Accessor &styler) {
42
43 Sci_PositionU i = 0;
44 Sci_Position lastNonSpace = -1;
45 unsigned int state = SCE_MAKE_DEFAULT;
46 bool bSpecial = false;
47
48 // check for a tab character in column 0 indicating a command
49 bool bCommand = false;
50 if ((lengthLine > 0) && (lineBuffer[0] == '\t'))
51 bCommand = true;
52
53 // Skip initial spaces
54 while ((i < lengthLine) && isspacechar(lineBuffer[i])) {
55 i++;
56 }
57 if (i < lengthLine) {
58 if (lineBuffer[i] == '#') { // Comment
59 styler.ColourTo(endPos, SCE_MAKE_COMMENT);
60 return;
61 }
62 if (lineBuffer[i] == '!') { // Special directive
63 styler.ColourTo(endPos, SCE_MAKE_PREPROCESSOR);
64 return;
65 }
66 }
67 int varCount = 0;
68 while (i < lengthLine) {
69 if (((i + 1) < lengthLine) && (lineBuffer[i] == '$' && lineBuffer[i + 1] == '(')) {
70 styler.ColourTo(startLine + i - 1, state);
71 state = SCE_MAKE_IDENTIFIER;
72 varCount++;
73 } else if (state == SCE_MAKE_IDENTIFIER && lineBuffer[i] == ')') {
74 if (--varCount == 0) {
75 styler.ColourTo(startLine + i, state);
76 state = SCE_MAKE_DEFAULT;
77 }
78 }
79
80 // skip identifier and target styling if this is a command line
81 if (!bSpecial && !bCommand) {
82 if (lineBuffer[i] == ':') {
83 if (((i + 1) < lengthLine) && (lineBuffer[i + 1] == '=')) {
84 // it's a ':=', so style as an identifier
85 if (lastNonSpace >= 0)
86 styler.ColourTo(startLine + lastNonSpace, SCE_MAKE_IDENTIFIER);
87 styler.ColourTo(startLine + i - 1, SCE_MAKE_DEFAULT);
88 styler.ColourTo(startLine + i + 1, SCE_MAKE_OPERATOR);
89 } else {
90 // We should check that no colouring was made since the beginning of the line,
91 // to avoid colouring stuff like /OUT:file
92 if (lastNonSpace >= 0)
93 styler.ColourTo(startLine + lastNonSpace, SCE_MAKE_TARGET);
94 styler.ColourTo(startLine + i - 1, SCE_MAKE_DEFAULT);
95 styler.ColourTo(startLine + i, SCE_MAKE_OPERATOR);
96 }
97 bSpecial = true; // Only react to the first ':' of the line
98 state = SCE_MAKE_DEFAULT;
99 } else if (lineBuffer[i] == '=') {
100 if (lastNonSpace >= 0)
101 styler.ColourTo(startLine + lastNonSpace, SCE_MAKE_IDENTIFIER);
102 styler.ColourTo(startLine + i - 1, SCE_MAKE_DEFAULT);
103 styler.ColourTo(startLine + i, SCE_MAKE_OPERATOR);
104 bSpecial = true; // Only react to the first '=' of the line
105 state = SCE_MAKE_DEFAULT;
106 }
107 }
108 if (!isspacechar(lineBuffer[i])) {
109 lastNonSpace = i;
110 }
111 i++;
112 }
113 if (state == SCE_MAKE_IDENTIFIER) {
114 styler.ColourTo(endPos, SCE_MAKE_IDEOL); // Error, variable reference not ended
115 } else {
116 styler.ColourTo(endPos, SCE_MAKE_DEFAULT);
117 }
118}
119
120static void ColouriseMakeDoc(Sci_PositionU startPos, Sci_Position length, int, WordList *[], Accessor &styler) {
121 char lineBuffer[1024];
122 styler.StartAt(startPos);
123 styler.StartSegment(startPos);
124 Sci_PositionU linePos = 0;
125 Sci_PositionU startLine = startPos;
126 for (Sci_PositionU i = startPos; i < startPos + length; i++) {
127 lineBuffer[linePos++] = styler[i];
128 if (AtEOL(styler, i) || (linePos >= sizeof(lineBuffer) - 1)) {
129 // End of line (or of line buffer) met, colourise it
130 lineBuffer[linePos] = '\0';
131 ColouriseMakeLine(lineBuffer, linePos, startLine, i, styler);
132 linePos = 0;
133 startLine = i + 1;
134 }
135 }
136 if (linePos > 0) { // Last line does not have ending characters
137 ColouriseMakeLine(lineBuffer, linePos, startLine, startPos + length - 1, styler);
138 }
139}
140
141static const char *const emptyWordListDesc[] = {
142 0
143};
144
145LexerModule lmMake(SCLEX_MAKEFILE, ColouriseMakeDoc, "makefile", 0, emptyWordListDesc);
146