]> 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:

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