1// Scintilla source code edit control
2// @file LexASY.cxx
3//Author: instanton (email: soft_share<at>126<dot>com)
4// The License.txt file describes the conditions under which this software may be distributed.
5
6#include <stdlib.h>
7#include <string.h>
8#include <stdio.h>
9#include <stdarg.h>
10#include <assert.h>
11
12#include <string>
13#include <string_view>
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
26using namespace Lexilla;
27
28static void ColouriseAsyDoc(Sci_PositionU startPos, Sci_Position length, int initStyle,
29 WordList *keywordlists[], Accessor &styler) {
30
31 WordList &keywords = *keywordlists[0];
32 WordList &keywords2 = *keywordlists[1];
33
34 CharacterSet setWordStart(CharacterSet::setAlpha, "_", 0x80, true);
35 CharacterSet setWord(CharacterSet::setAlphaNum, "._", 0x80, true);
36
37 int visibleChars = 0;
38
39 StyleContext sc(startPos, length, initStyle, styler);
40
41 for (; sc.More(); sc.Forward()) {
42
43 if (sc.atLineStart) {
44 if (sc.state == SCE_ASY_STRING) {
45 sc.SetState(SCE_ASY_STRING);
46 }
47 visibleChars = 0;
48 }
49
50 if (sc.ch == '\\') {
51 if (sc.chNext == '\n' || sc.chNext == '\r') {
52 sc.Forward();
53 if (sc.ch == '\r' && sc.chNext == '\n') {
54 sc.Forward();
55 }
56// continuationLine = true;
57 continue;
58 }
59 }
60
61 // Determine if the current state should terminate.
62 switch (sc.state) {
63 case SCE_ASY_OPERATOR:
64 sc.SetState(SCE_ASY_DEFAULT);
65 break;
66 case SCE_ASY_NUMBER:
67 if (!setWord.Contains(sc.ch)) {
68 sc.SetState(SCE_ASY_DEFAULT);
69 }
70 break;
71 case SCE_ASY_IDENTIFIER:
72 if (!setWord.Contains(sc.ch) || (sc.ch == '.')) {
73 char s[1000];
74 sc.GetCurrentLowered(s, sizeof(s));
75 if (keywords.InList(s)) {
76 sc.ChangeState(SCE_ASY_WORD);
77 } else if (keywords2.InList(s)) {
78 sc.ChangeState(SCE_ASY_WORD2);
79 }
80 sc.SetState(SCE_ASY_DEFAULT);
81 }
82 break;
83 case SCE_ASY_COMMENT:
84 if (sc.Match('*', '/')) {
85 sc.Forward();
86 sc.ForwardSetState(SCE_ASY_DEFAULT);
87 }
88 break;
89 case SCE_ASY_COMMENTLINE:
90 if (sc.atLineStart) {
91 sc.SetState(SCE_ASY_DEFAULT);
92 }
93 break;
94 case SCE_ASY_STRING:
95 if (sc.atLineEnd) {
96 sc.ChangeState(SCE_ASY_STRINGEOL);
97 } else if (sc.ch == '\\') {
98 if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
99 sc.Forward();
100 }
101 } else if (sc.ch == '\"') {
102 sc.ForwardSetState(SCE_ASY_DEFAULT);
103 }
104 break;
105 case SCE_ASY_CHARACTER:
106 if (sc.atLineEnd) {
107 sc.ChangeState(SCE_ASY_STRINGEOL);
108 } else if (sc.ch == '\\') {
109 if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
110 sc.Forward();
111 }
112 } else if (sc.ch == '\'') {
113 sc.ForwardSetState(SCE_ASY_DEFAULT);
114 }
115 break;
116 }
117
118 // Determine if a new state should be entered.
119 if (sc.state == SCE_ASY_DEFAULT) {
120 if (setWordStart.Contains(sc.ch) || (sc.ch == '@')) {
121 sc.SetState(SCE_ASY_IDENTIFIER);
122 } else if (sc.Match('/', '*')) {
123 sc.SetState(SCE_ASY_COMMENT);
124 sc.Forward(); //
125 } else if (sc.Match('/', '/')) {
126 sc.SetState(SCE_ASY_COMMENTLINE);
127 } else if (sc.ch == '\"') {
128 sc.SetState(SCE_ASY_STRING);
129 } else if (sc.ch == '\'') {
130 sc.SetState(SCE_ASY_CHARACTER);
131 } else if (sc.ch == '#' && visibleChars == 0) {
132 do {
133 sc.Forward();
134 } while ((sc.ch == ' ' || sc.ch == '\t') && sc.More());
135 if (sc.atLineEnd) {
136 sc.SetState(SCE_ASY_DEFAULT);
137 }
138 } else if (isoperator(static_cast<char>(sc.ch))) {
139 sc.SetState(SCE_ASY_OPERATOR);
140 }
141 }
142
143 }
144 sc.Complete();
145}
146
147static bool IsAsyCommentStyle(int style) {
148 return style == SCE_ASY_COMMENT;
149}
150
151
152static inline bool isASYidentifier(int ch) {
153 return
154 ((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z')) ;
155}
156
157static int ParseASYWord(Sci_PositionU pos, Accessor &styler, char *word)
158{
159 int length=0;
160 char ch=styler.SafeGetCharAt(pos);
161 *word=0;
162
163 while(isASYidentifier(ch) && length<100){
164 word[length]=ch;
165 length++;
166 ch=styler.SafeGetCharAt(pos+length);
167 }
168 word[length]=0;
169 return length;
170}
171
172static bool IsASYDrawingLine(Sci_Position line, Accessor &styler) {
173 Sci_Position pos = styler.LineStart(line);
174 Sci_Position eol_pos = styler.LineStart(line + 1) - 1;
175
176 Sci_Position startpos = pos;
177 char buffer[100]="";
178
179 while (startpos<eol_pos){
180 char ch = styler[startpos];
181 ParseASYWord(startpos,styler,buffer);
182 bool drawcommands = strncmp(buffer,"draw",4)==0||
183 strncmp(buffer,"pair",4)==0||strncmp(buffer,"label",5)==0;
184 if (!drawcommands && ch!=' ') return false;
185 else if (drawcommands) return true;
186 startpos++;
187 }
188 return false;
189}
190
191static void FoldAsyDoc(Sci_PositionU startPos, Sci_Position length, int initStyle,
192 WordList *[], Accessor &styler) {
193 bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
194 bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
195 bool foldAtElse = styler.GetPropertyInt("fold.at.else", 0) != 0;
196 Sci_PositionU endPos = startPos + length;
197 int visibleChars = 0;
198 Sci_Position lineCurrent = styler.GetLine(startPos);
199 int levelCurrent = SC_FOLDLEVELBASE;
200 if (lineCurrent > 0)
201 levelCurrent = styler.LevelAt(lineCurrent-1) >> 16;
202 int levelMinCurrent = levelCurrent;
203 int levelNext = levelCurrent;
204 char chNext = styler[startPos];
205 int styleNext = styler.StyleAt(startPos);
206 int style = initStyle;
207 for (Sci_PositionU i = startPos; i < endPos; i++) {
208 char ch = chNext;
209 chNext = styler.SafeGetCharAt(i + 1);
210 int stylePrev = style;
211 style = styleNext;
212 styleNext = styler.StyleAt(i + 1);
213 bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
214 if (foldComment && IsAsyCommentStyle(style)) {
215 if (!IsAsyCommentStyle(stylePrev) && (stylePrev != SCE_ASY_COMMENTLINEDOC)) {
216 levelNext++;
217 } else if (!IsAsyCommentStyle(styleNext) && (styleNext != SCE_ASY_COMMENTLINEDOC) && !atEOL) {
218 levelNext--;
219 }
220 }
221 if (style == SCE_ASY_OPERATOR) {
222 if (ch == '{') {
223 if (levelMinCurrent > levelNext) {
224 levelMinCurrent = levelNext;
225 }
226 levelNext++;
227 } else if (ch == '}') {
228 levelNext--;
229 }
230 }
231
232 if (atEOL && IsASYDrawingLine(lineCurrent, styler)){
233 if (lineCurrent==0 && IsASYDrawingLine(lineCurrent + 1, styler))
234 levelNext++;
235 else if (lineCurrent!=0 && !IsASYDrawingLine(lineCurrent - 1, styler)
236 && IsASYDrawingLine(lineCurrent + 1, styler)
237 )
238 levelNext++;
239 else if (lineCurrent!=0 && IsASYDrawingLine(lineCurrent - 1, styler) &&
240 !IsASYDrawingLine(lineCurrent+1, styler))
241 levelNext--;
242 }
243
244 if (atEOL) {
245 int levelUse = levelCurrent;
246 if (foldAtElse) {
247 levelUse = levelMinCurrent;
248 }
249 int lev = levelUse | levelNext << 16;
250 if (visibleChars == 0 && foldCompact)
251 lev |= SC_FOLDLEVELWHITEFLAG;
252 if (levelUse < levelNext)
253 lev |= SC_FOLDLEVELHEADERFLAG;
254 if (lev != styler.LevelAt(lineCurrent)) {
255 styler.SetLevel(lineCurrent, lev);
256 }
257 lineCurrent++;
258 levelCurrent = levelNext;
259 levelMinCurrent = levelCurrent;
260 visibleChars = 0;
261 }
262 if (!IsASpace(ch))
263 visibleChars++;
264 }
265}
266
267static const char * const asyWordLists[] = {
268 "Primary keywords and identifiers",
269 "Secondary keywords and identifiers",
270 0,
271 };
272
273LexerModule lmASY(SCLEX_ASYMPTOTE, ColouriseAsyDoc, "asy", FoldAsyDoc, asyWordLists);
274