Update Scintilla to version 3.6.2
[geany-mirror.git] / scintilla / lexers / LexTxt2tags.cxx
blob7e57e118ccde4b188cbee2e5b1ab60cd6494c2a4
1 /******************************************************************
2 * LexTxt2tags.cxx
4 * A simple Txt2tags lexer for scintilla.
7 * Adapted by Eric Forgeot
8 * Based on the LexMarkdown.cxx by Jon Strait - jstrait@moonloop.net
10 * What could be improved:
11 * - Verbatim lines could be like for raw lines : when there is no space between the ``` and the following text, the first letter should be colored so the user would understand there must be a space for a valid tag.
12 * - marks such as bold, italic, strikeout, underline should begin to be highlighted only when they are closed and valid.
13 * - verbatim and raw area should be highlighted too.
15 * The License.txt file describes the conditions under which this
16 * software may be distributed.
18 *****************************************************************/
20 #include <stdlib.h>
21 #include <string.h>
22 #include <stdio.h>
23 #include <stdarg.h>
24 #include <assert.h>
26 #include "ILexer.h"
27 #include "Scintilla.h"
28 #include "SciLexer.h"
30 #include "WordList.h"
31 #include "LexAccessor.h"
32 #include "Accessor.h"
33 #include "StyleContext.h"
34 #include "CharacterSet.h"
35 #include "LexerModule.h"
37 #ifdef SCI_NAMESPACE
38 using namespace Scintilla;
39 #endif
43 static inline bool IsNewline(const int ch) {
44 return (ch == '\n' || ch == '\r');
47 // True if can follow ch down to the end with possibly trailing whitespace
48 static bool FollowToLineEnd(const int ch, const int state, const Sci_PositionU endPos, StyleContext &sc) {
49 Sci_PositionU i = 0;
50 while (sc.GetRelative(++i) == ch)
52 // Skip over whitespace
53 while (IsASpaceOrTab(sc.GetRelative(i)) && sc.currentPos + i < endPos)
54 ++i;
55 if (IsNewline(sc.GetRelative(i)) || sc.currentPos + i == endPos) {
56 sc.Forward(i);
57 sc.ChangeState(state);
58 sc.SetState(SCE_TXT2TAGS_LINE_BEGIN);
59 return true;
61 else return false;
64 // Does the previous line have more than spaces and tabs?
65 static bool HasPrevLineContent(StyleContext &sc) {
66 Sci_Position i = 0;
67 // Go back to the previous newline
68 while ((--i + sc.currentPos) && !IsNewline(sc.GetRelative(i)))
70 while (--i + sc.currentPos) {
71 if (IsNewline(sc.GetRelative(i)))
72 break;
73 if (!IsASpaceOrTab(sc.GetRelative(i)))
74 return true;
76 return false;
79 // Separator line
80 static bool IsValidHrule(const Sci_PositionU endPos, StyleContext &sc) {
81 int count = 1;
82 Sci_PositionU i = 0;
83 for (;;) {
84 ++i;
85 int c = sc.GetRelative(i);
86 if (c == sc.ch)
87 ++count;
88 // hit a terminating character
89 else if (!IsASpaceOrTab(c) || sc.currentPos + i == endPos) {
90 // Are we a valid HRULE
91 if ((IsNewline(c) || sc.currentPos + i == endPos) &&
92 count >= 20 && !HasPrevLineContent(sc)) {
93 sc.SetState(SCE_TXT2TAGS_HRULE);
94 sc.Forward(i);
95 sc.SetState(SCE_TXT2TAGS_LINE_BEGIN);
96 return true;
98 else {
99 sc.SetState(SCE_TXT2TAGS_DEFAULT);
100 return false;
106 static void ColorizeTxt2tagsDoc(Sci_PositionU startPos, Sci_Position length, int initStyle,
107 WordList **, Accessor &styler) {
108 Sci_PositionU endPos = startPos + length;
109 int precharCount = 0;
110 // Don't advance on a new loop iteration and retry at the same position.
111 // Useful in the corner case of having to start at the beginning file position
112 // in the default state.
113 bool freezeCursor = false;
115 StyleContext sc(startPos, length, initStyle, styler);
117 while (sc.More()) {
118 // Skip past escaped characters
119 if (sc.ch == '\\') {
120 sc.Forward();
121 continue;
124 // A blockquotes resets the line semantics
125 if (sc.state == SCE_TXT2TAGS_BLOCKQUOTE){
126 sc.Forward(2);
127 sc.SetState(SCE_TXT2TAGS_LINE_BEGIN);
129 // An option colors the whole line
130 if (sc.state == SCE_TXT2TAGS_OPTION){
131 FollowToLineEnd('%', SCE_TXT2TAGS_OPTION, endPos, sc);
133 if (sc.state == SCE_TXT2TAGS_POSTPROC){
134 FollowToLineEnd('%', SCE_TXT2TAGS_POSTPROC, endPos, sc);
136 if (sc.state == SCE_TXT2TAGS_PREPROC){
137 FollowToLineEnd('%', SCE_TXT2TAGS_PREPROC, endPos, sc);
139 // A comment colors the whole line
140 if (sc.state == SCE_TXT2TAGS_COMMENT){
141 FollowToLineEnd('%', SCE_TXT2TAGS_COMMENT, endPos, sc);
143 // Conditional state-based actions
144 if (sc.state == SCE_TXT2TAGS_CODE2) {
145 if (IsNewline(sc.ch))
146 sc.SetState(SCE_TXT2TAGS_LINE_BEGIN);
147 if (sc.Match("``") && sc.GetRelative(-2) != ' ') {
148 sc.Forward(2);
149 sc.SetState(SCE_TXT2TAGS_DEFAULT);
152 // Table
153 else if (sc.state == SCE_TXT2TAGS_CODE) {
154 if (IsNewline(sc.ch))
155 sc.SetState(SCE_TXT2TAGS_LINE_BEGIN);
156 if (sc.ch == '|' && sc.chPrev != ' ')
157 sc.ForwardSetState(SCE_TXT2TAGS_DEFAULT);
159 // Strong
160 else if (sc.state == SCE_TXT2TAGS_STRONG1) {
161 if (IsNewline(sc.ch))
162 sc.SetState(SCE_TXT2TAGS_LINE_BEGIN);
163 if (sc.Match("**") && sc.chPrev != ' ') {
164 sc.Forward(2);
165 sc.SetState(SCE_TXT2TAGS_DEFAULT);
168 // Emphasis
169 else if (sc.state == SCE_TXT2TAGS_EM1) {
170 if (IsNewline(sc.ch))
171 sc.SetState(SCE_TXT2TAGS_LINE_BEGIN);
172 if (sc.Match("//") && sc.chPrev != ' ') {
173 sc.Forward(2);
174 sc.ForwardSetState(SCE_TXT2TAGS_DEFAULT);
177 // Underline
178 else if (sc.state == SCE_TXT2TAGS_EM2) {
179 if (IsNewline(sc.ch))
180 sc.SetState(SCE_TXT2TAGS_LINE_BEGIN);
181 if (sc.Match("__") && sc.chPrev != ' ') {
182 sc.Forward(2);
183 sc.ForwardSetState(SCE_TXT2TAGS_DEFAULT);
186 // codeblock
187 else if (sc.state == SCE_TXT2TAGS_CODEBK) {
188 if (IsNewline(sc.ch))
189 sc.SetState(SCE_TXT2TAGS_LINE_BEGIN);
190 if (sc.atLineStart && sc.Match("```")) {
191 Sci_Position i = 1;
192 while (!IsNewline(sc.GetRelative(i)) && sc.currentPos + i < endPos)
193 i++;
194 sc.Forward(i);
195 sc.SetState(SCE_TXT2TAGS_DEFAULT);
198 // strikeout
199 else if (sc.state == SCE_TXT2TAGS_STRIKEOUT) {
200 if (IsNewline(sc.ch))
201 sc.SetState(SCE_TXT2TAGS_LINE_BEGIN);
202 if (sc.Match("--") && sc.chPrev != ' ') {
203 sc.Forward(2);
204 sc.SetState(SCE_TXT2TAGS_DEFAULT);
207 // Headers
208 else if (sc.state == SCE_TXT2TAGS_LINE_BEGIN) {
209 if (sc.Match("======"))
211 sc.SetState(SCE_TXT2TAGS_HEADER6);
212 sc.Forward();
214 else if (sc.Match("====="))
216 sc.SetState(SCE_TXT2TAGS_HEADER5);
217 sc.Forward();
219 else if (sc.Match("===="))
221 sc.SetState(SCE_TXT2TAGS_HEADER4);
222 sc.Forward();
224 else if (sc.Match("==="))
226 sc.SetState(SCE_TXT2TAGS_HEADER3);
227 sc.Forward();
229 //SetStateAndZoom(SCE_TXT2TAGS_HEADER3, 3, '=', sc);
230 else if (sc.Match("==")) {
231 sc.SetState(SCE_TXT2TAGS_HEADER2);
232 sc.Forward();
234 //SetStateAndZoom(SCE_TXT2TAGS_HEADER2, 2, '=', sc);
235 else if (sc.Match("=")) {
236 // Catch the special case of an unordered list
237 if (sc.chNext == '.' && IsASpaceOrTab(sc.GetRelative(2))) {
238 precharCount = 0;
239 sc.SetState(SCE_TXT2TAGS_PRECHAR);
241 else
243 sc.SetState(SCE_TXT2TAGS_HEADER1);
244 sc.Forward();
246 //SetStateAndZoom(SCE_TXT2TAGS_HEADER1, 1, '=', sc);
249 // Numbered title
250 else if (sc.Match("++++++"))
252 sc.SetState(SCE_TXT2TAGS_HEADER6);
253 sc.Forward();
255 else if (sc.Match("+++++"))
257 sc.SetState(SCE_TXT2TAGS_HEADER5);
258 sc.Forward();
260 else if (sc.Match("++++"))
262 sc.SetState(SCE_TXT2TAGS_HEADER4);
263 sc.Forward();
265 else if (sc.Match("+++"))
267 sc.SetState(SCE_TXT2TAGS_HEADER3);
268 sc.Forward();
270 //SetStateAndZoom(SCE_TXT2TAGS_HEADER3, 3, '+', sc);
271 else if (sc.Match("++")) {
272 sc.SetState(SCE_TXT2TAGS_HEADER2);
273 sc.Forward();
275 //SetStateAndZoom(SCE_TXT2TAGS_HEADER2, 2, '+', sc);
276 else if (sc.Match("+")) {
277 // Catch the special case of an unordered list
278 if (sc.chNext == ' ' && IsASpaceOrTab(sc.GetRelative(1))) {
279 // if (IsNewline(sc.ch)) {
280 //precharCount = 0;
281 // sc.SetState(SCE_TXT2TAGS_LINE_BEGIN);
282 //sc.SetState(SCE_TXT2TAGS_PRECHAR);
283 // }
284 // else {
285 // precharCount = 0;
286 sc.SetState(SCE_TXT2TAGS_OLIST_ITEM);
287 sc.Forward(2);
288 sc.SetState(SCE_TXT2TAGS_DEFAULT);
289 // sc.SetState(SCE_TXT2TAGS_PRECHAR);
290 // }
292 else
294 sc.SetState(SCE_TXT2TAGS_HEADER1);
295 sc.Forward();
300 // Codeblock
301 else if (sc.Match("```")) {
302 if (!HasPrevLineContent(sc))
303 // if (!FollowToLineEnd(sc))
304 sc.SetState(SCE_TXT2TAGS_CODEBK);
305 else
306 sc.SetState(SCE_TXT2TAGS_DEFAULT);
309 // Preproc
310 else if (sc.Match("%!preproc")) {
311 sc.SetState(SCE_TXT2TAGS_PREPROC);
313 // Postproc
314 else if (sc.Match("%!postproc")) {
315 sc.SetState(SCE_TXT2TAGS_POSTPROC);
317 // Option
318 else if (sc.Match("%!")) {
319 sc.SetState(SCE_TXT2TAGS_OPTION);
322 // Comment
323 else if (sc.ch == '%') {
324 sc.SetState(SCE_TXT2TAGS_COMMENT);
326 // list
327 else if (sc.ch == '-') {
328 precharCount = 0;
329 sc.SetState(SCE_TXT2TAGS_PRECHAR);
331 // def list
332 else if (sc.ch == ':') {
333 precharCount = 0;
334 sc.SetState(SCE_TXT2TAGS_OLIST_ITEM);
335 sc.Forward(1);
336 sc.SetState(SCE_TXT2TAGS_PRECHAR);
338 else if (IsNewline(sc.ch))
339 sc.SetState(SCE_TXT2TAGS_LINE_BEGIN);
340 else {
341 precharCount = 0;
342 sc.SetState(SCE_TXT2TAGS_PRECHAR);
346 // The header lasts until the newline
347 else if (sc.state == SCE_TXT2TAGS_HEADER1 || sc.state == SCE_TXT2TAGS_HEADER2 ||
348 sc.state == SCE_TXT2TAGS_HEADER3 || sc.state == SCE_TXT2TAGS_HEADER4 ||
349 sc.state == SCE_TXT2TAGS_HEADER5 || sc.state == SCE_TXT2TAGS_HEADER6) {
350 if (IsNewline(sc.ch))
351 sc.SetState(SCE_TXT2TAGS_LINE_BEGIN);
354 // New state only within the initial whitespace
355 if (sc.state == SCE_TXT2TAGS_PRECHAR) {
356 // Blockquote
357 if (sc.Match("\"\"\"") && precharCount < 5){
359 sc.SetState(SCE_TXT2TAGS_BLOCKQUOTE);
360 sc.Forward(1);
363 // Begin of code block
364 else if (!HasPrevLineContent(sc) && (sc.chPrev == '\t' || precharCount >= 4))
365 sc.SetState(SCE_TXT2TAGS_CODEBK);
367 // HRule - Total of 20 or more hyphens, asterisks, or underscores
368 // on a line by themselves
369 else if ((sc.ch == '-' ) && IsValidHrule(endPos, sc))
371 // Unordered list
372 else if ((sc.ch == '-') && IsASpaceOrTab(sc.chNext)) {
373 sc.SetState(SCE_TXT2TAGS_ULIST_ITEM);
374 sc.ForwardSetState(SCE_TXT2TAGS_DEFAULT);
376 // Ordered list
377 else if (IsADigit(sc.ch)) {
378 Sci_Position digitCount = 0;
379 while (IsADigit(sc.GetRelative(++digitCount)))
381 if (sc.GetRelative(digitCount) == '.' &&
382 IsASpaceOrTab(sc.GetRelative(digitCount + 1))) {
383 sc.SetState(SCE_TXT2TAGS_OLIST_ITEM);
384 sc.Forward(digitCount + 1);
385 sc.SetState(SCE_TXT2TAGS_DEFAULT);
388 // Alternate Ordered list
389 else if (sc.ch == '+' && sc.chNext == ' ' && IsASpaceOrTab(sc.GetRelative(2))) {
390 // sc.SetState(SCE_TXT2TAGS_OLIST_ITEM);
391 // sc.Forward(2);
392 // sc.SetState(SCE_TXT2TAGS_DEFAULT);
394 else if (sc.ch != ' ' || precharCount > 2)
395 sc.SetState(SCE_TXT2TAGS_DEFAULT);
396 else
397 ++precharCount;
400 // New state anywhere in doc
401 if (sc.state == SCE_TXT2TAGS_DEFAULT) {
402 // if (sc.atLineStart && sc.ch == '#') {
403 // sc.SetState(SCE_TXT2TAGS_LINE_BEGIN);
404 // freezeCursor = true;
405 // }
406 // Links and Images
407 if (sc.Match("![") || sc.ch == '[') {
408 Sci_Position i = 0, j = 0, k = 0;
409 Sci_Position len = endPos - sc.currentPos;
410 while (i < len && (sc.GetRelative(++i) != ']' || sc.GetRelative(i - 1) == '\\'))
412 if (sc.GetRelative(i) == ']') {
413 j = i;
414 if (sc.GetRelative(++i) == '(') {
415 while (i < len && (sc.GetRelative(++i) != '(' || sc.GetRelative(i - 1) == '\\'))
417 if (sc.GetRelative(i) == '(')
418 k = i;
421 else if (sc.GetRelative(i) == '[' || sc.GetRelative(++i) == '[') {
422 while (i < len && (sc.GetRelative(++i) != ']' || sc.GetRelative(i - 1) == '\\'))
424 if (sc.GetRelative(i) == ']')
425 k = i;
428 // At least a link text
429 if (j) {
430 sc.SetState(SCE_TXT2TAGS_LINK);
431 sc.Forward(j);
432 // Also has a URL or reference portion
433 if (k)
434 sc.Forward(k - j);
435 sc.ForwardSetState(SCE_TXT2TAGS_DEFAULT);
438 // Code - also a special case for alternate inside spacing
439 if (sc.Match("``") && sc.GetRelative(3) != ' ') {
440 sc.SetState(SCE_TXT2TAGS_CODE2);
441 sc.Forward();
443 else if (sc.ch == '|' && sc.GetRelative(3) != ' ') {
444 sc.SetState(SCE_TXT2TAGS_CODE);
446 // Strong
447 else if (sc.Match("**") && sc.GetRelative(2) != ' ') {
448 sc.SetState(SCE_TXT2TAGS_STRONG1);
449 sc.Forward();
451 // Emphasis
452 else if (sc.Match("//") && sc.GetRelative(2) != ' ') {
453 sc.SetState(SCE_TXT2TAGS_EM1);
454 sc.Forward();
456 else if (sc.Match("__") && sc.GetRelative(2) != ' ') {
457 sc.SetState(SCE_TXT2TAGS_EM2);
458 sc.Forward();
460 // Strikeout
461 else if (sc.Match("--") && sc.GetRelative(2) != ' ') {
462 sc.SetState(SCE_TXT2TAGS_STRIKEOUT);
463 sc.Forward();
466 // Beginning of line
467 else if (IsNewline(sc.ch))
468 sc.SetState(SCE_TXT2TAGS_LINE_BEGIN);
470 // Advance if not holding back the cursor for this iteration.
471 if (!freezeCursor)
472 sc.Forward();
473 freezeCursor = false;
475 sc.Complete();
478 LexerModule lmTxt2tags(SCLEX_TXT2TAGS, ColorizeTxt2tagsDoc, "txt2tags");