]> git.madduck.net Git - etc/vim.git/blob - autoload/explainpat.vim

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

updated to v0.7 (2013)
[etc/vim.git] / autoload / explainpat.vim
1 " File:         explainpat.vim
2 " Created:      2011 Nov 02
3 " Last Change:  2013 Dec 30
4 " Rev Days:     12
5 " Author:       Andy Wokula <anwoku@yahoo.de>
6 " License:      Vim License, see :h license
7 " Version:      0.7
8
9 " Implements :ExplainPattern [pattern]
10
11 " History: "{{{
12 " 2013 Jun 21   AND/OR text is confusing, removed
13 " 2013 Apr 20   ...
14 "}}}
15
16 " TODO {{{
17 " - add something like "(empty) ... match everywhere" ... example: '\v(&&|str)'
18 "   Pattern: \v(&&|str)
19 "   Magic Pattern: \(\&\&\|str\)
20 "     \(         start of first capturing group
21 "     |                  (empty) match everywhere
22 "     |   \&         AND
23 "     |                  (empty) match everywhere
24 "     |   \&         AND
25 "     |                  (empty) match everywhere
26 "     | \|         OR
27 "     |   str        literal string (3 atom(s))
28 "     \)         end of group
29 " - more testing, completeness check
30 " ? detailed collections
31 " ? literal string: also print the unescaped magic items
32 " ? literal string: show leading/trailing spaces
33 "
34 "}}}
35
36 " Init Folklore {{{
37 let s:cpo_save = &cpo
38 set cpo&vim
39 let g:explainpat#loaded = 1
40 "}}}
41
42 func! explainpat#ExplainPattern(cmd_arg, ...) "{{{
43     " {a:1}     alternative help printer object (caution, no sanity check)
44     "           (for test running)
45     if a:cmd_arg == ""
46         " let pattern_str = nwo#vis#Get()
47         echo "(usage) :ExplainPattern [{register} | {pattern}]"
48         return
49     elseif strlen(a:cmd_arg) == 1 && a:cmd_arg =~ '["@0-9a-z\-:.*+/]'
50         echo 'Register:' a:cmd_arg
51         let pattern_str = getreg(a:cmd_arg)
52     else
53         let pattern_str = a:cmd_arg
54     endif
55
56     echo printf('Pattern: %s', pattern_str)
57     let magicpat = nwo#magic#MakeMagic(pattern_str)
58     if magicpat !=# pattern_str
59         echo printf('Magic Pattern: %s', magicpat)
60     endif
61
62     " we need state:
63     " set flag when in `\%[ ... ]' (optionally matched atoms):
64     let s:in_opt_atoms = 0
65     " counter for `\(':
66     let s:capture_group_nr = 0
67     " >=1 at pos 0 or after '\|', '\&', '\(', '\%(' or '\n'; else 0 or less:
68     let s:at_begin_of_pat = 1
69
70     let hulit = a:0>=1 && type(a:1)==s:DICT ? a:1 : s:NewHelpPrinter()
71     call hulit.AddIndent('  ')
72     let bull = s:NewTokenBiter(magicpat)
73     while !bull.AtEnd()
74         let item = bull.Bite(s:magic_item_pattern)
75         if item != ''
76             let Doc = get(s:doc, item, '')
77             if empty(Doc)
78                 call hulit.AddLiteral(item)
79             elseif type(Doc) == s:STRING
80                 call hulit.Print(item, Doc)
81             elseif type(Doc) == s:FUNCREF
82                 call call(Doc, [bull, hulit, item])
83             elseif type(Doc) == s:LIST
84                 call call(Doc[0], [bull, hulit, item, Doc[1]])
85             endif
86             let s:at_begin_of_pat -= 1
87         else
88             echoerr printf('ExplainPattern: cannot parse "%s"', bull.Rest())
89             break
90         endif
91         unlet Doc
92     endwhile
93     call hulit.FlushLiterals()
94 endfunc "}}}
95
96 " s: types {{{
97 let s:STRING = type("")
98 let s:DICT   = type({})
99 let s:FUNCREF = type(function("tr"))
100 let s:LIST = type([])
101 " }}}
102
103 let s:magic_item_pattern = '\C^\%(\\\%(@<\|%#=\|%[dxouU[(^$V#<>]\=\|z[1-9se(]\|@[>=!]\|_[[^$.]\=\|.\)\|.\)'
104
105 let s:doc = {} " {{{
106 " this is all the help data ...
107 "   strings, funcrefs and intermixed s:DocFoo() functions
108 " strongly depends on s:magic_item_pattern
109
110 func! s:DocOrBranch(bull, hulit, item) "{{{
111     call a:hulit.RemIndent()
112     call a:hulit.Print(a:item, "OR")
113     call a:hulit.AddIndent('  ')
114     let s:at_begin_of_pat = 2
115 endfunc "}}}
116
117 let s:doc['\|'] = function("s:DocOrBranch")
118
119 func! s:DocBeginOfPat(bull, hulit, item, msg) "{{{
120     call a:hulit.Print(a:item, a:msg)
121     let s:at_begin_of_pat = 2
122 endfunc "}}}
123
124 let s:doc['\&'] = [function("s:DocBeginOfPat"), "AND"]
125
126 let s:ord = split('n first second third fourth fifth sixth seventh eighth ninth')
127
128 func! s:DocGroupStart(bull, hulit, item) "{{{
129     if a:item == '\%('
130         call a:hulit.Print(a:item, "start of non-capturing group")
131     elseif a:item == '\('
132         let s:capture_group_nr += 1
133         call a:hulit.Print(a:item, printf("start of %s capturing group", get(s:ord, s:capture_group_nr, '(invalid)')))
134     else " a:item == '\z('
135         call a:hulit.Print(a:item, 'start of "external" group (only usable in :syn-region)')
136     endif
137     call a:hulit.AddIndent('| ', '  ')
138     let s:at_begin_of_pat = 2
139 endfunc "}}}
140 func! s:DocGroupEnd(bull, hulit, item) "{{{
141     call a:hulit.RemIndent(2)
142     call a:hulit.Print(a:item, "end of group")
143 endfunc "}}}
144
145 let s:doc['\('] = function("s:DocGroupStart")
146 let s:doc['\%('] = function("s:DocGroupStart")
147 let s:doc['\)'] =  function("s:DocGroupEnd")
148 " let s:doc['\z('] = "only in syntax scripts"
149 let s:doc['\z('] = function("s:DocGroupStart")
150
151 func! s:DocStar(bull, hulit, item) "{{{
152     if s:at_begin_of_pat >= 1
153         " call a:hulit.Print(a:item, "(at begin of pattern) literal `*'")
154         call a:hulit.AddLiteral(a:item)
155     else
156         call a:hulit.Print(a:item, "(multi) zero or more of the preceding atom")
157     endif
158 endfunc "}}}
159
160 " let s:doc['*'] = "(multi) zero or more of the preceding atom"
161 let s:doc['*'] = function("s:DocStar")
162
163 let s:doc['\+'] = "(multi) one or more of the preceding atom"
164 let s:doc['\='] = "(multi) zero or one of the preceding atom"
165 let s:doc['\?'] = "(multi) zero or one of the preceding atom"
166 " let s:doc['\{'] = "(multi) N to M, greedy"
167 " let s:doc['\{-'] = "(multi) N to M, non-greedy"
168
169 func! s:DocBraceMulti(bull, hulit, item) "{{{
170     let rest = a:bull.Bite('^-\=\d*\%(,\d*\)\=}')
171     if rest != ""
172         if rest == '-}'
173             call a:hulit.Print(a:item. rest, "non-greedy version of `*'")
174         elseif rest =~ '^-'
175             call a:hulit.Print(a:item. rest, "(multi) N to M, non-greedy")
176         else
177             call a:hulit.Print(a:item. rest, "(multi) N to M, greedy")
178         endif
179     else
180         call a:hulit.Print(a:item, "(invalid) incomplete `\\{...}' item")
181     endif
182 endfunc "}}}
183
184 let s:doc['\{'] = function("s:DocBraceMulti")
185
186 let s:doc['\@>'] = "(multi) match preceding atom like a full pattern"
187 let s:doc['\@='] = "(assertion) require match for preceding atom"
188 let s:doc['\@!'] = "(assertion) forbid match for preceding atom"
189
190 func! s:DocBefore(bull, hulit, item) "{{{
191     let rest = a:bull.Bite('^[=!]')
192     if rest == "="
193         call a:hulit.Print(a:item.rest, "(assertion) require match for preceding atom to the left")
194     elseif rest == "!"
195         call a:hulit.Print(a:item.rest, "(assertion) forbid match for preceding atom to the left")
196     else
197         call a:hulit.Print(a:item.rest, "(invalid) `\\@<' must be followed by `=' or `!'")
198     endif
199 endfunc "}}}
200
201 let s:doc['\@<'] = function("s:DocBefore")
202
203 func! s:DocCircumFlex(bull, hulit, item) "{{{
204     if s:at_begin_of_pat >= 1
205         call a:hulit.Print(a:item, "(assertion) require match at start of line")
206         " after `^' is not at begin of pattern ... handle special case `^*' here:
207         if a:bull.Bite('^\*') == "*"
208             call a:hulit.AddLiteral("*")
209         endif
210     else
211         " call a:hulit.Print(a:item, "(not at begin of pattern) literal `^'")
212         call a:hulit.AddLiteral(a:item)
213     endif
214 endfunc "}}}
215
216 " let s:doc['^'] = "(assertion) require match at start of line"
217 let s:doc['^'] = function("s:DocCircumFlex")
218
219 let s:doc['\_^'] = "(assertion) like `^', allowed anywhere in the pattern"
220
221 func! s:DocDollar(bull, hulit, item) "{{{
222     if a:bull.Rest() =~ '^$\|^\\[&|)n]'
223         call a:hulit.Print(a:item, "(assertion) require match at end of line")
224     else
225         call a:hulit.AddLiteral(a:item)
226     endif
227 endfunc "}}}
228
229 " let s:doc['$'] = "(assertion) require match at end of line"
230 let s:doc['$'] = function("s:DocDollar")
231
232 let s:doc['\_$'] = "(assertion) like `$', allowed anywhere in the pattern"
233 let s:doc['.'] = "match any character"
234 let s:doc['\_.'] = "match any character or newline"
235
236 func! s:DocUnderscore(bull, hulit, item) "{{{
237     let cclass = a:bull.Bite('^\a')
238     if cclass != ''
239         let cclass_doc = get(s:doc, '\'. cclass, '(invalid character class)')
240         call a:hulit.Print(a:item. cclass, printf('%s or end-of-line', cclass_doc))
241     else
242         call a:hulit.Print(a:item, "(invalid) `\\_' should be followed by a letter or `[...]'")
243         " echoerr printf('ExplainPattern: cannot parse %s', a:item. matchstr(a:bull.Rest(), '.'))
244     endif
245 endfunc "}}}
246
247 let s:doc['\_'] = function("s:DocUnderscore")
248 let s:doc['\<'] = "(assertion) require match at begin of word, :h word"
249 let s:doc['\>'] = "(assertion) require match at end of word, :h word"
250 let s:doc['\zs'] = "set begin of match here"
251 let s:doc['\ze'] = "set end of match here"
252 let s:doc['\%^'] = "(assertion) match at begin of buffer"
253 let s:doc['\%$'] = "(assertion) match at end of buffer"
254 let s:doc['\%V'] = "(assertion) match within the Visual area"
255 let s:doc['\%#'] = "(assertion) match with cursor position"
256
257 func! s:DocRegexEngine(bull, hulit, item) "{{{
258     let engine = a:bull.Bite('^[012]')
259     if engine == "0"
260         call a:hulit.Print(a:item.engine, 'Force automatic selection of the regexp engine (since v7.3.970).')
261     elseif engine == "1" 
262         call a:hulit.Print(a:item.engine, 'Force using the old engine (since v7.3.970).')
263     elseif engine == "2"
264         call a:hulit.Print(a:item.engine, 'Force using the NFA engine (since v7.3.970).')
265     else
266         call a:hulit.Print(a:item, '(invalid) \%#= can only be followed by 0, 1, or 2')
267     endif
268 endfunc "}}}
269
270 let s:doc['\%#='] = function("s:DocRegexEngine")
271
272 " \%'m   \%<'m   \%>'m
273 " \%23l  \%<23l  \%>23l
274 " \%23c  \%<23c  \%>23c
275 " \%23v  \%<23v  \%>23v
276 " backslash percent at/before/after
277 func! s:DocBspercAt(bull, hulit, item) "{{{
278     let rest = a:bull.Bite('^\%(''.\|\d\+[lvc]\)\C')
279     if rest[0] == "'"
280         call a:hulit.Print(a:item.rest, "(assertion) match with position of mark ". rest[1])
281     else
282         let number = rest[:-2]
283         let type = rest[-1:]
284         if type ==# "l"
285             call a:hulit.Print(a:item.rest, "match in line ". number)
286         elseif type ==# "c"
287             call a:hulit.Print(a:item.rest, "match in column ". number)
288         elseif type ==# "v"
289             call a:hulit.Print(a:item.rest, "match in virtual column ". number)
290         else
291             call a:hulit.Print(a:item.rest, "(invalid) incomplete `\\%' item")
292             " echoerr printf('ExplainPattern: incomplete item %s', a:item. rest)
293         endif
294     endif
295 endfunc "}}}
296 func! s:DocBspercBefore(bull, hulit, item) "{{{
297     let rest = a:bull.Bite('^\%(''.\|\d\+[lvc]\)\C')
298     if rest[0] == "'"
299         call a:hulit.Print(a:item.rest, "(assertion) match before position of mark ". rest[1])
300     else
301         let number = rest[:-2]
302         let type = rest[-1:]
303         if type ==# "l"
304             call a:hulit.Print(a:item.rest, printf("match above line %d (towards start of buffer)", number))
305         elseif type ==# "c"
306             call a:hulit.Print(a:item.rest, "match before column ". number)
307         elseif type ==# "v"
308             call a:hulit.Print(a:item.rest, "match before virtual column ". number)
309         else
310             call a:hulit.Print(a:item.rest, "(invalid) incomplete `\\%<' item")
311             " echoerr printf('ExplainPattern: incomplete item %s', a:item. rest)
312         endif
313     endif
314 endfunc "}}}
315 func! s:DocBspercAfter(bull, hulit, item) "{{{
316     let rest = a:bull.Bite('^\%(''.\|\d\+[lvc]\)\C')
317     if rest[0] == "'"
318         call a:hulit.Print(a:item.rest, "(assertion) match after position of mark ". rest[1])
319     else
320         let number = rest[:-2]
321         let type = rest[-1:]
322         if type ==# "l"
323             call a:hulit.Print(a:item.rest, printf("match below line %d (towards end of buffer)", number))
324         elseif type ==# "c"
325             call a:hulit.Print(a:item.rest, "match after column ". number)
326         elseif type ==# "v"
327             call a:hulit.Print(a:item.rest, "match after virtual column ". number)
328         else
329             call a:hulit.Print(a:item.rest, "(invalid) incomplete `\\%>' item")
330             " echoerr printf('ExplainPattern: incomplete item %s', a:item. rest)
331         endif
332     endif
333 endfunc "}}}
334
335 let s:doc['\%'] = function("s:DocBspercAt")
336 let s:doc['\%<'] = function("s:DocBspercBefore")
337 let s:doc['\%>'] = function("s:DocBspercAfter")
338
339 let s:doc['\i'] = "identifier character (see 'isident' option)"
340 let s:doc['\I'] = "like \"\\i\", but excluding digits"
341 let s:doc['\k'] = "keyword character (see 'iskeyword' option)"
342 let s:doc['\K'] = "like \"\\k\", but excluding digits"
343 let s:doc['\f'] = "file name character (see 'isfname' option)"
344 let s:doc['\F'] = "like \"\\f\", but excluding digits"
345 let s:doc['\p'] = "printable character (see 'isprint' option)"
346 let s:doc['\P'] = "like \"\\p\", but excluding digits"
347 let s:doc['\s'] = "whitespace character: <Space> and <Tab>"
348 let s:doc['\S'] = "non-whitespace character; opposite of \\s"
349 let s:doc['\d'] = "digit: [0-9]"
350 let s:doc['\D'] = "non-digit: [^0-9]"
351 let s:doc['\x'] = "hex digit: [0-9A-Fa-f]"
352 let s:doc['\X'] = "non-hex digit: [^0-9A-Fa-f]"
353 let s:doc['\o'] = "octal digit: [0-7]"
354 let s:doc['\O'] = "non-octal digit: [^0-7]"
355 let s:doc['\w'] = "word character: [0-9A-Za-z_]"
356 let s:doc['\W'] = "non-word character: [^0-9A-Za-z_]"
357 let s:doc['\h'] = "head of word character: [A-Za-z_]"
358 let s:doc['\H'] = "non-head of word character: [^A-Za-z_]"
359 let s:doc['\a'] = "alphabetic character: [A-Za-z]"
360 let s:doc['\A'] = "non-alphabetic character: [^A-Za-z]"
361 let s:doc['\l'] = "lowercase character: [a-z]"
362 let s:doc['\L'] = "non-lowercase character: [^a-z]"
363 let s:doc['\u'] = "uppercase character: [A-Z]"
364 let s:doc['\U'] = "non-uppercase character: [^A-Z]"
365
366 let s:doc['\e'] = "match <Esc>"
367 let s:doc['\t'] = "match <Tab>"
368 let s:doc['\r'] = "match <CR>"
369 let s:doc['\b'] = "match CTRL-H"
370 let s:doc['\n'] = [function("s:DocBeginOfPat"), "match a newline"]
371 let s:doc['~'] = "match the last given substitute string"
372 let s:doc['\1'] = "match first captured string"
373 let s:doc['\2'] = "match second captured string"
374 let s:doc['\3'] = "match third captured string"
375 let s:doc['\4'] = "match fourth captured string "
376 let s:doc['\5'] = "match fifth captured string"
377 let s:doc['\6'] = "match sixth captured string"
378 let s:doc['\7'] = "match seventh captured string"
379 let s:doc['\8'] = "match eighth captured string"
380 let s:doc['\9'] = "match ninth captured string"
381
382 let s:doc['\z1'] = 'match same string matched by first "external" group'
383 let s:doc['\z2'] = 'match same string matched by second "external" group'
384 let s:doc['\z3'] = 'match same string matched by third "external" group'
385 let s:doc['\z4'] = 'match same string matched by fourth "external" group '
386 let s:doc['\z5'] = 'match same string matched by fifth "external" group'
387 let s:doc['\z6'] = 'match same string matched by sixth "external" group'
388 let s:doc['\z7'] = 'match same string matched by seventh "external" group'
389 let s:doc['\z8'] = 'match same string matched by eighth "external" group'
390 let s:doc['\z9'] = 'match same string matched by ninth "external" group'
391
392 " from MakeMagic()
393 " skip the rest of a collection
394 let s:coll_skip_pat = '^\^\=]\=\%(\%(\\[\^\]\-\\bertn]\|\[:\w\+:]\|\[=.=]\|\[\..\.]\|[^\]]\)\@>\)*]'
395
396 func! s:DocCollection(bull, hulit, item) "{{{
397     let collstr = a:bull.Bite(s:coll_skip_pat)
398     if collstr == "" || collstr == "]"
399         call a:hulit.AddLiteral('['. collstr)
400     else
401         let inverse = collstr =~ '^\^'
402         let with_nl = a:item == '\_['
403         let descr = inverse ? printf('collection not matching [%s', collstr[1:]) : 'collection'
404         let descr_nl = printf("%s%s", (inverse && with_nl ? ', but' : ''), (with_nl ? ' with end-of-line added' : ''))
405         call a:hulit.Print(a:item. collstr, descr. descr_nl)
406     endif
407 endfunc "}}}
408
409 let s:doc['['] = function("s:DocCollection")
410 let s:doc['\_['] = function("s:DocCollection")
411
412 func! s:DocOptAtoms(bull, hulit, item) "{{{
413     if a:item == '\%['
414         call a:hulit.Print(a:item, "start a sequence of optionally matched atoms")
415         let s:in_opt_atoms = 1
416         call a:hulit.AddIndent('. ')
417     else " a:item == ']'
418         if s:in_opt_atoms
419             call a:hulit.RemIndent()
420             call a:hulit.Print(a:item, "end of optionally matched atoms")
421             let s:in_opt_atoms = 0
422         else
423             call a:hulit.AddLiteral(a:item)
424         endif
425     endif
426 endfunc "}}}
427
428 " let s:doc['\%['] = "start a sequence of optionally matched atoms"
429 let s:doc['\%['] = function("s:DocOptAtoms")
430 let s:doc[']'] = function("s:DocOptAtoms")
431
432 func! s:DocAnywhere(bull, hulit, item, msg) "{{{
433     call a:hulit.Print(a:item, a:msg)
434     " keep state:
435     let s:at_begin_of_pat += 1
436 endfunc "}}}
437
438 let s:doc['\c'] = [function("s:DocAnywhere"), "ignore case while matching the pattern"]
439 let s:doc['\C'] = [function("s:DocAnywhere"), "match case while matching the pattern"]
440 let s:doc['\Z'] = [function("s:DocAnywhere"), "ignore composing characters in the pattern"]
441
442 " \%d 123
443 " \%x 2a
444 " \%o 0377
445 " \%u 20AC
446 " \%U 1234abcd
447
448 func! s:DocBspercDecimal(bull, hulit, item) "{{{
449     let number = a:bull.Bite('^\d\{,3}')
450     let char = strtrans(nr2char(str2nr(number)))
451     call a:hulit.Print(a:item. number, printf("match character specified by decimal number %s (%s)", number, char))
452 endfunc "}}}
453 func! s:DocBspercHexTwo(bull, hulit, item) "{{{
454     let number = a:bull.Bite('^\x\{,2}')
455     let char = strtrans(nr2char(str2nr(number,16)))
456     call a:hulit.Print(a:item. number, printf("match character specified with hex number 0x%s (%s)", number, char))
457 endfunc "}}}
458 func! s:DocBspercOctal(bull, hulit, item) "{{{
459     let number = a:bull.Bite('^\o\{,4}')
460     let char = strtrans(nr2char(str2nr(number,8)))
461     call a:hulit.Print(a:item. number, printf("match character specified with octal number 0%s (%s)", substitute(number, '^0*', '', ''), char))
462 endfunc "}}}
463 func! s:DocBspercHexFour(bull, hulit, item) "{{{
464     let number = a:bull.Bite('^\x\{,4}')
465     let char = has("multi_byte_encoding") ? ' ('. strtrans(nr2char(str2nr(number,16))).')' : ''
466     call a:hulit.Print(a:item. number, printf("match character specified with hex number 0x%s%s", number, char))
467 endfunc "}}}
468 func! s:DocBspercHexEight(bull, hulit, item) "{{{
469     let number = a:bull.Bite('^\x\{,8}')
470     let char = has("multi_byte_encoding") ? ' ('. strtrans(nr2char(str2nr(number,16))).')' : ''
471     call a:hulit.Print(a:item. number, printf("match character specified with hex number 0x%s%s", number, char))
472 endfunc "}}}
473
474 let s:doc['\%d'] = function("s:DocBspercDecimal") " 123
475 let s:doc['\%x'] = function("s:DocBspercHexTwo") " 2a
476 let s:doc['\%o'] = function("s:DocBspercOctal") " 0377
477 let s:doc['\%u'] = function("s:DocBspercHexFour") " 20AC
478 let s:doc['\%U'] = function("s:DocBspercHexEight") " 1234abcd
479
480 " \m
481 " \M
482 " \v
483 " \V
484 "}}}
485
486 func! s:NewHelpPrinter() "{{{
487     let obj = {}
488     let obj.literals = ''
489     let obj.indents = []
490     let obj.len = 0         " can be negative (!)
491
492     func! obj.Print(str, ...) "{{{
493         call self.FlushLiterals()
494         let indstr = join(self.indents, '')
495         echohl Comment
496         echo indstr
497         echohl None
498         if a:0 == 0
499             echon a:str
500         else
501             " echo indstr. printf("`%s'   %s", a:str, a:1)
502             echohl PreProc
503             echon printf("%-10s", a:str)
504             echohl None
505             echohl Comment
506             echon printf(" %s", a:1)
507             echohl None
508         endif
509     endfunc "}}}
510
511     func! obj.AddLiteral(item) "{{{
512         let self.literals .= a:item
513     endfunc "}}}
514
515     func! obj.FlushLiterals() "{{{
516         if self.literals == ''
517             return
518         endif
519         let indstr = join(self.indents, '')
520         echohl Comment
521         echo indstr
522         echohl None
523         if self.literals =~ '^\s\|\s$'
524             echon printf("%-10s", '"'. self.literals. '"')
525         else
526             echon printf("%-10s", self.literals)
527         endif
528         echohl Comment
529         echon " literal string"
530         if exists("*strchars")
531             if self.literals =~ '\\'
532                 let self.literals = substitute(self.literals, '\\\(.\)', '\1', 'g')
533             endif
534             let spconly = self.literals =~ '[^ ]' ? '' : ', spaces only'
535             let nlit = strchars(self.literals)
536             echon " (". nlit. (nlit==1 ? " atom" : " atoms"). spconly.")"
537         endif
538         echohl None
539         let self.literals = ''
540     endfunc  "}}}
541
542     func! obj.AddIndent(...) "{{{
543         call self.FlushLiterals()
544         if self.len >= 0
545             call extend(self.indents, copy(a:000))
546         elseif self.len + a:0 >= 1
547             call extend(self.indents, a:000[-(self.len+a:0):])
548         endif
549         let self.len += a:0
550     endfunc "}}}
551
552     func! obj.RemIndent(...) "{{{
553         call self.FlushLiterals()
554         if a:0 == 0
555             if self.len >= 1
556                 call remove(self.indents, -1)
557             endif
558             let self.len -= 1
559         else
560             if self.len > a:1
561                 call remove(self.indents, -a:1, -1)
562             elseif self.len >= 1
563                 call remove(self.indents, 0, -1)
564             endif
565             let self.len -= a:1
566         endif
567     endfunc "}}}
568
569     return obj
570 endfunc "}}}
571
572 func! s:NewTokenBiter(str) "{{{
573     " {str}     string to eat pieces from
574     let obj = {'str': a:str}
575
576     " consume piece from start of input matching {pat}
577     func! obj.Bite(pat) "{{{
578         " {pat}     should start with '^'
579         let bite = matchstr(self.str, a:pat)
580         let self.str = strpart(self.str, strlen(bite))
581         return bite
582     endfunc "}}}
583
584     " get the unparsed rest of input (not consuming)
585     func! obj.Rest() "{{{
586         return self.str
587     endfunc "}}}
588
589     " check if end of input reached
590     func! obj.AtEnd() "{{{
591         return self.str == ""
592     endfunc "}}}
593
594     return obj
595 endfunc "}}}
596
597 " Modeline: {{{1
598 let &cpo = s:cpo_save
599 unlet s:cpo_save
600 " vim:ts=8:fdm=marker: