1 // Scintilla source code edit control
2 /** @file LexFlagShip.cxx
3  ** Lexer for FlagShip
4  ** (Syntactically compatible to other XBase dialects, like dBase, Clipper, Fox etc.)
5  **/
6 // Copyright 2005 by Randy Butler
7 // Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
8 // The License.txt file describes the conditions under which this software may be distributed.
9 
10 #include <stdlib.h>
11 #include <string.h>
12 #include <ctype.h>
13 #include <stdio.h>
14 #include <stdarg.h>
15 
16 #include "Platform.h"
17 
18 #include "PropSet.h"
19 #include "Accessor.h"
20 #include "StyleContext.h"
21 #include "KeyWords.h"
22 #include "Scintilla.h"
23 #include "SciLexer.h"
24 
IsFlagShipComment(Accessor & styler,int pos,int len)25 static bool IsFlagShipComment(Accessor &styler, int pos, int len) {
26 	return len>0 && styler[pos]=='\'';
27 }
28 
IsTypeCharacter(int ch)29 static inline bool IsTypeCharacter(int ch) {
30 	return ch == '%' || ch == '&' || ch == '@' || ch == '!' || ch == '#' || ch == '$';
31 }
32 
33 // Extended to accept accented characters
IsAWordChar(int ch)34 static inline bool IsAWordChar(int ch) {
35 	return ch >= 0x80 ||
36 	       (isalnum(ch) || ch == '.' || ch == '_');
37 }
38 
IsAWordStart(int ch)39 static inline bool IsAWordStart(int ch) {
40 	return ch >= 0x80 ||
41 	       (isalnum(ch) || ch == '_');
42 }
43 
IsADateCharacter(const int ch)44 static inline bool IsADateCharacter(const int ch) {
45 	return (ch < 0x80) &&
46 		(isalnum(ch) || ch == '|' || ch == '-' || ch == '/' || ch == ':' || ch == ' ' || ch == '\t');
47 }
48 
49 
ColouriseFlagShipDoc(unsigned int startPos,int length,int initStyle,WordList * keywordlists[],Accessor & styler)50 static void ColouriseFlagShipDoc(unsigned int startPos, int length, int initStyle,
51                            WordList *keywordlists[], Accessor &styler) {
52 
53 	//bool FSScriptSyntax = true;
54 	WordList &keywords = *keywordlists[0];
55 	WordList &keywords2 = *keywordlists[1];
56 	WordList &keywords3 = *keywordlists[2];
57 	WordList &keywords4 = *keywordlists[3];
58 
59 	styler.StartAt(startPos);
60 
61 	int visibleChars = 0;
62 
63 	StyleContext sc(startPos, length, initStyle, styler);
64 
65 	for (; sc.More(); sc.Forward()) {
66 
67 		if (sc.state == SCE_FS_OPERATOR) {
68 			sc.SetState(SCE_FS_DEFAULT);
69 		} else if (sc.state == SCE_FS_IDENTIFIER) {
70 			if (!IsAWordChar(sc.ch)) {
71 				char s[100];
72 				sc.GetCurrentLowered(s, sizeof(s));
73 				if (keywords.InList(s)) {
74 					sc.ChangeState(SCE_FS_KEYWORD);
75 				} else if (keywords2.InList(s)) {
76 					sc.ChangeState(SCE_FS_KEYWORD2);
77 				} else if (keywords3.InList(s)) {
78 					sc.ChangeState(SCE_FS_KEYWORD3);
79 				} else if (keywords4.InList(s)) {
80 					sc.ChangeState(SCE_FS_KEYWORD4);
81 				}// Else, it is really an identifier...
82 				sc.SetState(SCE_FS_DEFAULT);
83 			}
84 		} else if (sc.state == SCE_FS_NUMBER) {
85 			if (!IsAWordChar(sc.ch)) {
86 				sc.SetState(SCE_FS_DEFAULT);
87 			}
88 		} else if (sc.state == SCE_FS_STRING) {
89 			// VB doubles quotes to preserve them, so just end this string
90 			// state now as a following quote will start again
91 			if (sc.ch == '\"') {
92 				if (tolower(sc.chNext) == 'c') {
93 					sc.Forward();
94 				}
95 				sc.ForwardSetState(SCE_FS_DEFAULT);
96 			} else if (sc.atLineEnd) {
97 				sc.ChangeState(SCE_FS_STRINGEOL);
98 				sc.ForwardSetState(SCE_FS_DEFAULT);
99 			}
100 		} else if (sc.state == SCE_FS_COMMENT) {
101 			if (sc.Match('*', '/')) {   // new code
102 				sc.Forward();
103 				sc.ForwardSetState(SCE_FS_DEFAULT);
104 			//if (sc.atLineEnd) {       // old code
105 			//	sc.SetState(SCE_FS_DEFAULT);
106 			}
107 		} else if (sc.state == SCE_FS_COMMENTLINE) {  //new code
108 			if (sc.ch == '\r' || sc.ch == '\n') {
109 				sc.SetState(SCE_FS_DEFAULT);
110 				visibleChars = 0;
111 			}
112 		} else if (sc.state == SCE_FS_PREPROCESSOR) {
113 			if (sc.atLineEnd) {
114 				sc.SetState(SCE_FS_DEFAULT);
115 			}
116 		} else if (sc.state == SCE_FS_DATE) {
117 			if (sc.ch == '#' || !IsADateCharacter(sc.chNext)) {
118 				sc.ForwardSetState(SCE_FS_DEFAULT);
119 			}
120 		}
121 
122 		// Determine if a new state should be entered.
123 		if (sc.state == SCE_FS_DEFAULT) {
124 			if (sc.Match('/', '*')) {  // New code
125 				sc.SetState(SCE_FS_COMMENT);
126 				sc.Forward();	// Eat the * so it isn't used for the end of the comment
127 			//if (sc.ch == '\'') {  // Old code
128 			//	sc.SetState(SCE_FS_COMMENT); // old code
129 			} else if (sc.Match('/', '/')) { // New code
130 				sc.SetState(SCE_FS_COMMENTLINE);
131 			} else if (sc.ch == '\"') {
132 				sc.SetState(SCE_FS_STRING);
133 			} else if (sc.ch == '#' && visibleChars == 0) {
134 				// Preprocessor commands are alone on their line
135 				sc.SetState(SCE_FS_PREPROCESSOR);
136 			} else if (sc.ch == '#') {
137 				int n = 1;
138 				int chSeek = ' ';
139 				while ((n < 100) && (chSeek == ' ' || chSeek == '\t')) {
140 					chSeek = sc.GetRelative(n);
141 					n++;
142 				}
143 				if (IsADigit(chSeek)) {
144 					sc.SetState(SCE_FS_DATE);
145 				} else {
146 					sc.SetState(SCE_FS_OPERATOR);
147 				}
148 			} else if (sc.ch == '&' && tolower(sc.chNext) == 'h') {
149 				sc.SetState(SCE_FS_NUMBER);
150 			} else if (sc.ch == '&' && tolower(sc.chNext) == 'o') {
151 				sc.SetState(SCE_FS_NUMBER);
152 			} else if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
153 				sc.SetState(SCE_FS_NUMBER);
154 			} else if (IsAWordStart(sc.ch) || (sc.ch == '[')) {
155 				sc.SetState(SCE_FS_IDENTIFIER);
156 			} else if (isoperator(static_cast<char>(sc.ch)) || (sc.ch == '\\')) {
157 				sc.SetState(SCE_FS_OPERATOR);
158 			}
159 		}
160 
161 		if (sc.atLineEnd) {
162 			visibleChars = 0;
163 		}
164 		if (!IsASpace(sc.ch)) {
165 			visibleChars++;
166 		}
167 	}
168 	sc.Complete();
169 }
170 
FoldFlagShipDoc(unsigned int startPos,int length,int,WordList * [],Accessor & styler)171 static void FoldFlagShipDoc(unsigned int startPos, int length, int,
172 						   WordList *[], Accessor &styler) {
173 
174 	int endPos = startPos + length;
175 
176 	// Backtrack to previous line in case need to fix its fold status
177 	int lineCurrent = styler.GetLine(startPos);
178 	if (startPos > 0) {
179 		if (lineCurrent > 0) {
180 			lineCurrent--;
181 			startPos = styler.LineStart(lineCurrent);
182 		}
183 	}
184 	int spaceFlags = 0;
185 	int indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags, IsFlagShipComment);
186 	char chNext = styler[startPos];
187 	for (int i = startPos; i < endPos; i++) {
188 		char ch = chNext;
189 		chNext = styler.SafeGetCharAt(i + 1);
190 
191 		if ((ch == '\r' && chNext != '\n') || (ch == '\n') || (i == endPos)) {
192 			int lev = indentCurrent;
193 			int indentNext = styler.IndentAmount(lineCurrent + 1, &spaceFlags, IsFlagShipComment);
194 			if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG)) {
195 				// Only non whitespace lines can be headers
196 				if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext & SC_FOLDLEVELNUMBERMASK)) {
197 					lev |= SC_FOLDLEVELHEADERFLAG;
198 				} else if (indentNext & SC_FOLDLEVELWHITEFLAG) {
199 					// Line after is blank so check the next - maybe should continue further?
200 					int spaceFlags2 = 0;
201 					int indentNext2 = styler.IndentAmount(lineCurrent + 2, &spaceFlags2, IsFlagShipComment);
202 					if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext2 & SC_FOLDLEVELNUMBERMASK)) {
203 						lev |= SC_FOLDLEVELHEADERFLAG;
204 					}
205 				}
206 			}
207 			indentCurrent = indentNext;
208 			styler.SetLevel(lineCurrent, lev);
209 			lineCurrent++;
210 		}
211 	}
212 }
213 
214 
215 static const char * const FSWordListDesc[] = {
216 	"Keywords",
217 	"functions",
218 	"user2",
219 	"user3",
220 	0
221 };
222 
223 LexerModule lmFlagShip(SCLEX_FLAGSHIP, ColouriseFlagShipDoc, "flagship", FoldFlagShipDoc, FSWordListDesc);
224 
225 
226 
227