]>
git.saurik.com Git - wxWidgets.git/blob - src/stc/scintilla/lexers/LexTxt2tags.cxx
1 /******************************************************************
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 *****************************************************************/
27 #include "Scintilla.h"
31 #include "LexAccessor.h"
33 #include "StyleContext.h"
34 #include "CharacterSet.h"
35 #include "LexerModule.h"
38 using namespace Scintilla
;
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 unsigned int endPos
, StyleContext
& sc
) {
50 while ( sc
. GetRelative (++ i
) == ch
)
52 // Skip over whitespace
53 while ( IsASpaceOrTab ( sc
. GetRelative ( i
)) && sc
. currentPos
+ i
< endPos
)
55 if ( IsNewline ( sc
. GetRelative ( i
)) || sc
. currentPos
+ i
== endPos
) {
57 sc
. ChangeState ( state
);
58 sc
. SetState ( SCE_TXT2TAGS_LINE_BEGIN
);
64 // Does the previous line have more than spaces and tabs?
65 static bool HasPrevLineContent ( StyleContext
& sc
) {
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
)))
73 if (! IsASpaceOrTab ( sc
. GetRelative ( i
)))
80 static bool IsValidHrule ( const unsigned int endPos
, StyleContext
& sc
) {
84 c
= sc
. GetRelative ( i
);
87 // hit a terminating character
88 else if (! IsASpaceOrTab ( c
) || sc
. currentPos
+ i
== endPos
) {
89 // Are we a valid HRULE
90 if (( IsNewline ( c
) || sc
. currentPos
+ i
== endPos
) &&
91 count
>= 20 && ! HasPrevLineContent ( sc
)) {
92 sc
. SetState ( SCE_TXT2TAGS_HRULE
);
94 sc
. SetState ( SCE_TXT2TAGS_LINE_BEGIN
);
98 sc
. SetState ( SCE_TXT2TAGS_DEFAULT
);
106 static void ColorizeTxt2tagsDoc ( unsigned int startPos
, int length
, int initStyle
,
107 WordList
**, Accessor
& styler
) {
108 unsigned int 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
);
118 // Skip past escaped characters
124 // A blockquotes resets the line semantics
125 if ( sc
. state
== SCE_TXT2TAGS_BLOCKQUOTE
){
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 ) != ' ' ) {
149 sc
. SetState ( SCE_TXT2TAGS_DEFAULT
);
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
);
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
!= ' ' ) {
165 sc
. SetState ( SCE_TXT2TAGS_DEFAULT
);
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
!= ' ' ) {
174 sc
. ForwardSetState ( SCE_TXT2TAGS_DEFAULT
);
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
!= ' ' ) {
183 sc
. ForwardSetState ( SCE_TXT2TAGS_DEFAULT
);
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 ( "```" )) {
192 while (! IsNewline ( sc
. GetRelative ( i
)) && sc
. currentPos
+ i
< endPos
)
195 sc
. SetState ( SCE_TXT2TAGS_DEFAULT
);
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
!= ' ' ) {
204 sc
. SetState ( SCE_TXT2TAGS_DEFAULT
);
208 else if ( sc
. state
== SCE_TXT2TAGS_LINE_BEGIN
) {
209 if ( sc
. Match ( "======" ))
211 sc
. SetState ( SCE_TXT2TAGS_HEADER6
);
214 else if ( sc
. Match ( "=====" ))
216 sc
. SetState ( SCE_TXT2TAGS_HEADER5
);
219 else if ( sc
. Match ( "====" ))
221 sc
. SetState ( SCE_TXT2TAGS_HEADER4
);
224 else if ( sc
. Match ( "===" ))
226 sc
. SetState ( SCE_TXT2TAGS_HEADER3
);
229 //SetStateAndZoom(SCE_TXT2TAGS_HEADER3, 3, '=', sc);
230 else if ( sc
. Match ( "==" )) {
231 sc
. SetState ( SCE_TXT2TAGS_HEADER2
);
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 ))) {
239 sc
. SetState ( SCE_TXT2TAGS_PRECHAR
);
243 sc
. SetState ( SCE_TXT2TAGS_HEADER1
);
246 //SetStateAndZoom(SCE_TXT2TAGS_HEADER1, 1, '=', sc);
250 else if ( sc
. Match ( "++++++" ))
252 sc
. SetState ( SCE_TXT2TAGS_HEADER6
);
255 else if ( sc
. Match ( "+++++" ))
257 sc
. SetState ( SCE_TXT2TAGS_HEADER5
);
260 else if ( sc
. Match ( "++++" ))
262 sc
. SetState ( SCE_TXT2TAGS_HEADER4
);
265 else if ( sc
. Match ( "+++" ))
267 sc
. SetState ( SCE_TXT2TAGS_HEADER3
);
270 //SetStateAndZoom(SCE_TXT2TAGS_HEADER3, 3, '+', sc);
271 else if ( sc
. Match ( "++" )) {
272 sc
. SetState ( SCE_TXT2TAGS_HEADER2
);
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)) {
281 // sc.SetState(SCE_TXT2TAGS_LINE_BEGIN);
282 //sc.SetState(SCE_TXT2TAGS_PRECHAR);
286 sc
. SetState ( SCE_TXT2TAGS_OLIST_ITEM
);
288 sc
. SetState ( SCE_TXT2TAGS_DEFAULT
);
289 // sc.SetState(SCE_TXT2TAGS_PRECHAR);
294 sc
. SetState ( SCE_TXT2TAGS_HEADER1
);
301 else if ( sc
. Match ( "```" )) {
302 if (! HasPrevLineContent ( sc
))
303 // if (!FollowToLineEnd(sc))
304 sc
. SetState ( SCE_TXT2TAGS_CODEBK
);
306 sc
. SetState ( SCE_TXT2TAGS_DEFAULT
);
310 else if ( sc
. Match ( "%!preproc" )) {
311 sc
. SetState ( SCE_TXT2TAGS_PREPROC
);
314 else if ( sc
. Match ( "%!postproc" )) {
315 sc
. SetState ( SCE_TXT2TAGS_POSTPROC
);
318 else if ( sc
. Match ( "%!" )) {
319 sc
. SetState ( SCE_TXT2TAGS_OPTION
);
323 else if ( sc
. ch
== '%' ) {
324 sc
. SetState ( SCE_TXT2TAGS_COMMENT
);
327 else if ( sc
. ch
== '-' ) {
329 sc
. SetState ( SCE_TXT2TAGS_PRECHAR
);
332 else if ( sc
. ch
== ':' ) {
334 sc
. SetState ( SCE_TXT2TAGS_OLIST_ITEM
);
336 sc
. SetState ( SCE_TXT2TAGS_PRECHAR
);
338 else if ( IsNewline ( sc
. ch
))
339 sc
. SetState ( SCE_TXT2TAGS_LINE_BEGIN
);
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
) {
357 if ( sc
. Match ( " \"\"\" " ) && precharCount
< 5 ){
359 sc
. SetState ( SCE_TXT2TAGS_BLOCKQUOTE
);
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
))
372 else if (( sc
. ch
== '-' ) && IsASpaceOrTab ( sc
. chNext
)) {
373 sc
. SetState ( SCE_TXT2TAGS_ULIST_ITEM
);
374 sc
. ForwardSetState ( SCE_TXT2TAGS_DEFAULT
);
377 else if ( IsADigit ( sc
. ch
)) {
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);
392 // sc.SetState(SCE_TXT2TAGS_DEFAULT);
394 else if ( sc
. ch
!= ' ' || precharCount
> 2 )
395 sc
. SetState ( SCE_TXT2TAGS_DEFAULT
);
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;
407 if ( sc
. Match ( "![" ) || sc
. ch
== '[' ) {
408 int i
= 0 , j
= 0 , k
= 0 ;
409 int len
= endPos
- sc
. currentPos
;
410 while ( i
< len
&& ( sc
. GetRelative (++ i
) != ']' || sc
. GetRelative ( i
- 1 ) == ' \\ ' ))
412 if ( sc
. GetRelative ( i
) == ']' ) {
414 if ( sc
. GetRelative (++ i
) == '(' ) {
415 while ( i
< len
&& ( sc
. GetRelative (++ i
) != '(' || sc
. GetRelative ( i
- 1 ) == ' \\ ' ))
417 if ( sc
. GetRelative ( 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
) == ']' )
428 // At least a link text
430 sc
. SetState ( SCE_TXT2TAGS_LINK
);
432 // Also has a URL or reference portion
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
);
443 else if ( sc
. ch
== '|' && sc
. GetRelative ( 3 ) != ' ' ) {
444 sc
. SetState ( SCE_TXT2TAGS_CODE
);
447 else if ( sc
. Match ( "**" ) && sc
. GetRelative ( 2 ) != ' ' ) {
448 sc
. SetState ( SCE_TXT2TAGS_STRONG1
);
452 else if ( sc
. Match ( "//" ) && sc
. GetRelative ( 2 ) != ' ' ) {
453 sc
. SetState ( SCE_TXT2TAGS_EM1
);
456 else if ( sc
. Match ( "__" ) && sc
. GetRelative ( 2 ) != ' ' ) {
457 sc
. SetState ( SCE_TXT2TAGS_EM2
);
461 else if ( sc
. Match ( "--" ) && sc
. GetRelative ( 2 ) != ' ' ) {
462 sc
. SetState ( SCE_TXT2TAGS_STRIKEOUT
);
467 else if ( IsNewline ( sc
. ch
))
468 sc
. SetState ( SCE_TXT2TAGS_LINE_BEGIN
);
470 // Advance if not holding back the cursor for this iteration.
473 freezeCursor
= false ;
478 LexerModule
lmTxt2tags ( SCLEX_TXT2TAGS
, ColorizeTxt2tagsDoc
, "txt2tags" );