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