Merge pull request #2212 from TwlyY29/bibtex-parser
[geany-mirror.git] / scintilla / lexers / LexTxt2tags.cxx
blob6d1a954e01f52f182662079718c532f770c3ada2
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 using namespace Scintilla;
41 static inline bool IsNewline(const int ch) {
42 return (ch == '\n' || ch == '\r');
45 // True if can follow ch down to the end with possibly trailing whitespace
46 static bool FollowToLineEnd(const int ch, const int state, const Sci_PositionU endPos, StyleContext &sc) {
47 Sci_PositionU i = 0;
48 while (sc.GetRelative(++i) == ch)
50 // Skip over whitespace
51 while (IsASpaceOrTab(sc.GetRelative(i)) && sc.currentPos + i < endPos)
52 ++i;
53 if (IsNewline(sc.GetRelative(i)) || sc.currentPos + i == endPos) {
54 sc.Forward(i);
55 sc.ChangeState(state);
56 sc.SetState(SCE_TXT2TAGS_LINE_BEGIN);
57 return true;
59 else return false;
62 // Does the previous line have more than spaces and tabs?
63 static bool HasPrevLineContent(StyleContext &sc) {
64 Sci_Position i = 0;
65 // Go back to the previous newline
66 while ((--i + sc.currentPos) && !IsNewline(sc.GetRelative(i)))
68 while (--i + sc.currentPos) {
69 if (IsNewline(sc.GetRelative(i)))
70 break;
71 if (!IsASpaceOrTab(sc.GetRelative(i)))
72 return true;
74 return false;
77 // Separator line
78 static bool IsValidHrule(const Sci_PositionU endPos, StyleContext &sc) {
79 int count = 1;
80 Sci_PositionU i = 0;
81 for (;;) {
82 ++i;
83 int c = sc.GetRelative(i);
84 if (c == sc.ch)
85 ++count;
86 // hit a terminating character
87 else if (!IsASpaceOrTab(c) || sc.currentPos + i == endPos) {
88 // Are we a valid HRULE
89 if ((IsNewline(c) || sc.currentPos + i == endPos) &&
90 count >= 20 && !HasPrevLineContent(sc)) {
91 sc.SetState(SCE_TXT2TAGS_HRULE);
92 sc.Forward(i);
93 sc.SetState(SCE_TXT2TAGS_LINE_BEGIN);
94 return true;
96 else {
97 sc.SetState(SCE_TXT2TAGS_DEFAULT);
98 return false;
104 static void ColorizeTxt2tagsDoc(Sci_PositionU startPos, Sci_Position length, int initStyle,
105 WordList **, Accessor &styler) {
106 Sci_PositionU endPos = startPos + length;
107 int precharCount = 0;
108 // Don't advance on a new loop iteration and retry at the same position.
109 // Useful in the corner case of having to start at the beginning file position
110 // in the default state.
111 bool freezeCursor = false;
113 StyleContext sc(startPos, length, initStyle, styler);
115 while (sc.More()) {
116 // Skip past escaped characters
117 if (sc.ch == '\\') {
118 sc.Forward();
119 continue;
122 // A blockquotes resets the line semantics
123 if (sc.state == SCE_TXT2TAGS_BLOCKQUOTE){
124 sc.Forward(2);
125 sc.SetState(SCE_TXT2TAGS_LINE_BEGIN);
127 // An option colors the whole line
128 if (sc.state == SCE_TXT2TAGS_OPTION){
129 FollowToLineEnd('%', SCE_TXT2TAGS_OPTION, endPos, sc);
131 if (sc.state == SCE_TXT2TAGS_POSTPROC){
132 FollowToLineEnd('%', SCE_TXT2TAGS_POSTPROC, endPos, sc);
134 if (sc.state == SCE_TXT2TAGS_PREPROC){
135 FollowToLineEnd('%', SCE_TXT2TAGS_PREPROC, endPos, sc);
137 // A comment colors the whole line
138 if (sc.state == SCE_TXT2TAGS_COMMENT){
139 FollowToLineEnd('%', SCE_TXT2TAGS_COMMENT, endPos, sc);
141 // Conditional state-based actions
142 if (sc.state == SCE_TXT2TAGS_CODE2) {
143 if (IsNewline(sc.ch))
144 sc.SetState(SCE_TXT2TAGS_LINE_BEGIN);
145 if (sc.Match("``") && sc.GetRelative(-2) != ' ') {
146 sc.Forward(2);
147 sc.SetState(SCE_TXT2TAGS_DEFAULT);
150 // Table
151 else if (sc.state == SCE_TXT2TAGS_CODE) {
152 if (IsNewline(sc.ch))
153 sc.SetState(SCE_TXT2TAGS_LINE_BEGIN);
154 if (sc.ch == '|' && sc.chPrev != ' ')
155 sc.ForwardSetState(SCE_TXT2TAGS_DEFAULT);
157 // Strong
158 else if (sc.state == SCE_TXT2TAGS_STRONG1) {
159 if (IsNewline(sc.ch))
160 sc.SetState(SCE_TXT2TAGS_LINE_BEGIN);
161 if (sc.Match("**") && sc.chPrev != ' ') {
162 sc.Forward(2);
163 sc.SetState(SCE_TXT2TAGS_DEFAULT);
166 // Emphasis
167 else if (sc.state == SCE_TXT2TAGS_EM1) {
168 if (IsNewline(sc.ch))
169 sc.SetState(SCE_TXT2TAGS_LINE_BEGIN);
170 if (sc.Match("//") && sc.chPrev != ' ') {
171 sc.Forward(2);
172 sc.ForwardSetState(SCE_TXT2TAGS_DEFAULT);
175 // Underline
176 else if (sc.state == SCE_TXT2TAGS_EM2) {
177 if (IsNewline(sc.ch))
178 sc.SetState(SCE_TXT2TAGS_LINE_BEGIN);
179 if (sc.Match("__") && sc.chPrev != ' ') {
180 sc.Forward(2);
181 sc.ForwardSetState(SCE_TXT2TAGS_DEFAULT);
184 // codeblock
185 else if (sc.state == SCE_TXT2TAGS_CODEBK) {
186 if (IsNewline(sc.ch))
187 sc.SetState(SCE_TXT2TAGS_LINE_BEGIN);
188 if (sc.atLineStart && sc.Match("```")) {
189 Sci_Position i = 1;
190 while (!IsNewline(sc.GetRelative(i)) && sc.currentPos + i < endPos)
191 i++;
192 sc.Forward(i);
193 sc.SetState(SCE_TXT2TAGS_DEFAULT);
196 // strikeout
197 else if (sc.state == SCE_TXT2TAGS_STRIKEOUT) {
198 if (IsNewline(sc.ch))
199 sc.SetState(SCE_TXT2TAGS_LINE_BEGIN);
200 if (sc.Match("--") && sc.chPrev != ' ') {
201 sc.Forward(2);
202 sc.SetState(SCE_TXT2TAGS_DEFAULT);
205 // Headers
206 else if (sc.state == SCE_TXT2TAGS_LINE_BEGIN) {
207 if (sc.Match("======"))
209 sc.SetState(SCE_TXT2TAGS_HEADER6);
210 sc.Forward();
212 else if (sc.Match("====="))
214 sc.SetState(SCE_TXT2TAGS_HEADER5);
215 sc.Forward();
217 else if (sc.Match("===="))
219 sc.SetState(SCE_TXT2TAGS_HEADER4);
220 sc.Forward();
222 else if (sc.Match("==="))
224 sc.SetState(SCE_TXT2TAGS_HEADER3);
225 sc.Forward();
227 //SetStateAndZoom(SCE_TXT2TAGS_HEADER3, 3, '=', sc);
228 else if (sc.Match("==")) {
229 sc.SetState(SCE_TXT2TAGS_HEADER2);
230 sc.Forward();
232 //SetStateAndZoom(SCE_TXT2TAGS_HEADER2, 2, '=', sc);
233 else if (sc.Match("=")) {
234 // Catch the special case of an unordered list
235 if (sc.chNext == '.' && IsASpaceOrTab(sc.GetRelative(2))) {
236 precharCount = 0;
237 sc.SetState(SCE_TXT2TAGS_PRECHAR);
239 else
241 sc.SetState(SCE_TXT2TAGS_HEADER1);
242 sc.Forward();
244 //SetStateAndZoom(SCE_TXT2TAGS_HEADER1, 1, '=', sc);
247 // Numbered title
248 else if (sc.Match("++++++"))
250 sc.SetState(SCE_TXT2TAGS_HEADER6);
251 sc.Forward();
253 else if (sc.Match("+++++"))
255 sc.SetState(SCE_TXT2TAGS_HEADER5);
256 sc.Forward();
258 else if (sc.Match("++++"))
260 sc.SetState(SCE_TXT2TAGS_HEADER4);
261 sc.Forward();
263 else if (sc.Match("+++"))
265 sc.SetState(SCE_TXT2TAGS_HEADER3);
266 sc.Forward();
268 //SetStateAndZoom(SCE_TXT2TAGS_HEADER3, 3, '+', sc);
269 else if (sc.Match("++")) {
270 sc.SetState(SCE_TXT2TAGS_HEADER2);
271 sc.Forward();
273 //SetStateAndZoom(SCE_TXT2TAGS_HEADER2, 2, '+', sc);
274 else if (sc.Match("+")) {
275 // Catch the special case of an unordered list
276 if (sc.chNext == ' ' && IsASpaceOrTab(sc.GetRelative(1))) {
277 // if (IsNewline(sc.ch)) {
278 //precharCount = 0;
279 // sc.SetState(SCE_TXT2TAGS_LINE_BEGIN);
280 //sc.SetState(SCE_TXT2TAGS_PRECHAR);
281 // }
282 // else {
283 // precharCount = 0;
284 sc.SetState(SCE_TXT2TAGS_OLIST_ITEM);
285 sc.Forward(2);
286 sc.SetState(SCE_TXT2TAGS_DEFAULT);
287 // sc.SetState(SCE_TXT2TAGS_PRECHAR);
288 // }
290 else
292 sc.SetState(SCE_TXT2TAGS_HEADER1);
293 sc.Forward();
298 // Codeblock
299 else if (sc.Match("```")) {
300 if (!HasPrevLineContent(sc))
301 // if (!FollowToLineEnd(sc))
302 sc.SetState(SCE_TXT2TAGS_CODEBK);
303 else
304 sc.SetState(SCE_TXT2TAGS_DEFAULT);
307 // Preproc
308 else if (sc.Match("%!preproc")) {
309 sc.SetState(SCE_TXT2TAGS_PREPROC);
311 // Postproc
312 else if (sc.Match("%!postproc")) {
313 sc.SetState(SCE_TXT2TAGS_POSTPROC);
315 // Option
316 else if (sc.Match("%!")) {
317 sc.SetState(SCE_TXT2TAGS_OPTION);
320 // Comment
321 else if (sc.ch == '%') {
322 sc.SetState(SCE_TXT2TAGS_COMMENT);
324 // list
325 else if (sc.ch == '-') {
326 precharCount = 0;
327 sc.SetState(SCE_TXT2TAGS_PRECHAR);
329 // def list
330 else if (sc.ch == ':') {
331 precharCount = 0;
332 sc.SetState(SCE_TXT2TAGS_OLIST_ITEM);
333 sc.Forward(1);
334 sc.SetState(SCE_TXT2TAGS_PRECHAR);
336 else if (IsNewline(sc.ch))
337 sc.SetState(SCE_TXT2TAGS_LINE_BEGIN);
338 else {
339 precharCount = 0;
340 sc.SetState(SCE_TXT2TAGS_PRECHAR);
344 // The header lasts until the newline
345 else if (sc.state == SCE_TXT2TAGS_HEADER1 || sc.state == SCE_TXT2TAGS_HEADER2 ||
346 sc.state == SCE_TXT2TAGS_HEADER3 || sc.state == SCE_TXT2TAGS_HEADER4 ||
347 sc.state == SCE_TXT2TAGS_HEADER5 || sc.state == SCE_TXT2TAGS_HEADER6) {
348 if (IsNewline(sc.ch))
349 sc.SetState(SCE_TXT2TAGS_LINE_BEGIN);
352 // New state only within the initial whitespace
353 if (sc.state == SCE_TXT2TAGS_PRECHAR) {
354 // Blockquote
355 if (sc.Match("\"\"\"") && precharCount < 5){
357 sc.SetState(SCE_TXT2TAGS_BLOCKQUOTE);
358 sc.Forward(1);
361 // Begin of code block
362 else if (!HasPrevLineContent(sc) && (sc.chPrev == '\t' || precharCount >= 4))
363 sc.SetState(SCE_TXT2TAGS_CODEBK);
365 // HRule - Total of 20 or more hyphens, asterisks, or underscores
366 // on a line by themselves
367 else if ((sc.ch == '-' ) && IsValidHrule(endPos, sc))
369 // Unordered list
370 else if ((sc.ch == '-') && IsASpaceOrTab(sc.chNext)) {
371 sc.SetState(SCE_TXT2TAGS_ULIST_ITEM);
372 sc.ForwardSetState(SCE_TXT2TAGS_DEFAULT);
374 // Ordered list
375 else if (IsADigit(sc.ch)) {
376 Sci_Position digitCount = 0;
377 while (IsADigit(sc.GetRelative(++digitCount)))
379 if (sc.GetRelative(digitCount) == '.' &&
380 IsASpaceOrTab(sc.GetRelative(digitCount + 1))) {
381 sc.SetState(SCE_TXT2TAGS_OLIST_ITEM);
382 sc.Forward(digitCount + 1);
383 sc.SetState(SCE_TXT2TAGS_DEFAULT);
386 // Alternate Ordered list
387 else if (sc.ch == '+' && sc.chNext == ' ' && IsASpaceOrTab(sc.GetRelative(2))) {
388 // sc.SetState(SCE_TXT2TAGS_OLIST_ITEM);
389 // sc.Forward(2);
390 // sc.SetState(SCE_TXT2TAGS_DEFAULT);
392 else if (sc.ch != ' ' || precharCount > 2)
393 sc.SetState(SCE_TXT2TAGS_DEFAULT);
394 else
395 ++precharCount;
398 // New state anywhere in doc
399 if (sc.state == SCE_TXT2TAGS_DEFAULT) {
400 // if (sc.atLineStart && sc.ch == '#') {
401 // sc.SetState(SCE_TXT2TAGS_LINE_BEGIN);
402 // freezeCursor = true;
403 // }
404 // Links and Images
405 if (sc.Match("![") || sc.ch == '[') {
406 Sci_Position i = 0, j = 0, k = 0;
407 Sci_Position len = endPos - sc.currentPos;
408 while (i < len && (sc.GetRelative(++i) != ']' || sc.GetRelative(i - 1) == '\\'))
410 if (sc.GetRelative(i) == ']') {
411 j = i;
412 if (sc.GetRelative(++i) == '(') {
413 while (i < len && (sc.GetRelative(++i) != '(' || sc.GetRelative(i - 1) == '\\'))
415 if (sc.GetRelative(i) == '(')
416 k = i;
419 else if (sc.GetRelative(i) == '[' || sc.GetRelative(++i) == '[') {
420 while (i < len && (sc.GetRelative(++i) != ']' || sc.GetRelative(i - 1) == '\\'))
422 if (sc.GetRelative(i) == ']')
423 k = i;
426 // At least a link text
427 if (j) {
428 sc.SetState(SCE_TXT2TAGS_LINK);
429 sc.Forward(j);
430 // Also has a URL or reference portion
431 if (k)
432 sc.Forward(k - j);
433 sc.ForwardSetState(SCE_TXT2TAGS_DEFAULT);
436 // Code - also a special case for alternate inside spacing
437 if (sc.Match("``") && sc.GetRelative(3) != ' ') {
438 sc.SetState(SCE_TXT2TAGS_CODE2);
439 sc.Forward();
441 else if (sc.ch == '|' && sc.GetRelative(3) != ' ') {
442 sc.SetState(SCE_TXT2TAGS_CODE);
444 // Strong
445 else if (sc.Match("**") && sc.GetRelative(2) != ' ') {
446 sc.SetState(SCE_TXT2TAGS_STRONG1);
447 sc.Forward();
449 // Emphasis
450 else if (sc.Match("//") && sc.GetRelative(2) != ' ') {
451 sc.SetState(SCE_TXT2TAGS_EM1);
452 sc.Forward();
454 else if (sc.Match("__") && sc.GetRelative(2) != ' ') {
455 sc.SetState(SCE_TXT2TAGS_EM2);
456 sc.Forward();
458 // Strikeout
459 else if (sc.Match("--") && sc.GetRelative(2) != ' ') {
460 sc.SetState(SCE_TXT2TAGS_STRIKEOUT);
461 sc.Forward();
464 // Beginning of line
465 else if (IsNewline(sc.ch))
466 sc.SetState(SCE_TXT2TAGS_LINE_BEGIN);
468 // Advance if not holding back the cursor for this iteration.
469 if (!freezeCursor)
470 sc.Forward();
471 freezeCursor = false;
473 sc.Complete();
476 LexerModule lmTxt2tags(SCLEX_TXT2TAGS, ColorizeTxt2tagsDoc, "txt2tags");