]> git.madduck.net Git - etc/vim.git/blobdiff - 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:

added \{...\}
[etc/vim.git] / autoload / explainpat.vim
index c0545eae682c3fd62c5a7faec3ff0cbc782e6ddf..566273266fc981d00b81d37c22cb182946766377 100644 (file)
@@ -1,24 +1,35 @@
 " File:         explainpat.vim
 " Created:      2011 Nov 02
-" Last Change:  2012 Dec 19
-" Rev Days:     3
+" Last Change:  2017 Oct 20
+" Version:     0.7
 " Author:      Andy Wokula <anwoku@yahoo.de>
 " License:     Vim License, see :h license
-" Version:     0.2
 
 " Implements :ExplainPattern [pattern]
 
+" History: "{{{
+" 2013 Jun 21  AND/OR text is confusing, removed
+" 2013 Apr 20  ...
+"}}}
+
 " TODO {{{
+" - add something like "(empty) ... match everywhere" ... example: '\v(&&|str)'
+"   Pattern: \v(&&|str)
+"   Magic Pattern: \(\&\&\|str\)
+"     \(         start of first capturing group
+"     |                  (empty) match everywhere
+"     |   \&         AND
+"     |                  (empty) match everywhere
+"     |   \&         AND
+"     |                  (empty) match everywhere
+"     | \|         OR
+"     |   str        literal string (3 atom(s))
+"     \)         end of group
 " - more testing, completeness check
-" - detailed collections
-" - \z
+" ? detailed collections
 " ? literal string: also print the unescaped magic items
 " ? literal string: show leading/trailing spaces
-" + start of "first" capturing group, start of 2nd ...
-" + `\|' should get less indent than the branches, do we need to create an
-"   AST?       ! no, keep it straight forward
-" + \%[...]
-" + \{, \{-
+"
 "}}}
 
 " Init Folklore {{{
@@ -27,7 +38,9 @@ set cpo&vim
 let g:explainpat#loaded = 1
 "}}}
 
-func! explainpat#ExplainPattern(cmd_arg) "{{{
+func! explainpat#ExplainPattern(cmd_arg, ...) "{{{
+    " {a:1}    alternative help printer object (caution, no sanity check)
+    "          (for test running)
     if a:cmd_arg == ""
        " let pattern_str = nwo#vis#Get()
        echo "(usage) :ExplainPattern [{register} | {pattern}]"
@@ -45,13 +58,17 @@ func! explainpat#ExplainPattern(cmd_arg) "{{{
        echo printf('Magic Pattern: %s', magicpat)
     endif
 
-    " hmm, we need state for \%[ ... ]
+    " we need state:
+    " set flag when in `\%[ ... ]' (optionally matched atoms):
     let s:in_opt_atoms = 0
+    " counter for `\(':
     let s:capture_group_nr = 0
+    " >=1 at pos 0 or after '\|', '\&', '\(', '\%(' or '\n'; else 0 or less:
+    let s:at_begin_of_pat = 1
 
-    let hulit = s:NewHelpPrinter()
+    let hulit = a:0>=1 && type(a:1)==s:DICT ? a:1 : s:NewHelpPrinter()
     call hulit.AddIndent('  ')
-    let bull = s:NewTokenBiter(magicpat, '')
+    let bull = s:NewTokenBiter(magicpat)
     while !bull.AtEnd()
        let item = bull.Bite(s:magic_item_pattern)
        if item != ''
@@ -62,7 +79,10 @@ func! explainpat#ExplainPattern(cmd_arg) "{{{
                call hulit.Print(item, Doc)
            elseif type(Doc) == s:FUNCREF
                call call(Doc, [bull, hulit, item])
+           elseif type(Doc) == s:LIST
+               call call(Doc[0], [bull, hulit, item, Doc[1]])
            endif
+           let s:at_begin_of_pat -= 1
        else
            echoerr printf('ExplainPattern: cannot parse "%s"', bull.Rest())
            break
@@ -76,9 +96,10 @@ endfunc "}}}
 let s:STRING = type("")
 let s:DICT   = type({})
 let s:FUNCREF = type(function("tr"))
+let s:LIST = type([])
 " }}}
 
-let s:magic_item_pattern = '\C^\%(\\\%(@<.\|%[dxouU[(^$V#<>]\=\|z[1-9se(]\|{\|@[>=!]\|_[[^$.]\=\|.\)\|.\)'
+let s:magic_item_pattern = '\C^\%(\\\%(@<\|%#=\|%[dxouU[(^$V#<>]\=\|z[1-9se(]\|@[>=!]\|_[[^$.]\=\|.\)\|.\)'
 
 let s:doc = {} " {{{
 " this is all the help data ...
@@ -87,23 +108,33 @@ let s:doc = {} " {{{
 
 func! s:DocOrBranch(bull, hulit, item) "{{{
     call a:hulit.RemIndent()
-    call a:hulit.Print(a:item, "OR branch")
+    call a:hulit.Print(a:item, "OR")
     call a:hulit.AddIndent('  ')
+    let s:at_begin_of_pat = 2
 endfunc "}}}
 
 let s:doc['\|'] = function("s:DocOrBranch")
-let s:doc['\&'] = "AND branch"
+
+func! s:DocBeginOfPat(bull, hulit, item, msg) "{{{
+    call a:hulit.Print(a:item, a:msg)
+    let s:at_begin_of_pat = 2
+endfunc "}}}
+
+let s:doc['\&'] = [function("s:DocBeginOfPat"), "AND"]
 
 let s:ord = split('n first second third fourth fifth sixth seventh eighth ninth')
 
 func! s:DocGroupStart(bull, hulit, item) "{{{
     if a:item == '\%('
        call a:hulit.Print(a:item, "start of non-capturing group")
-    else " a:item == '\('
+    elseif a:item == '\('
        let s:capture_group_nr += 1
        call a:hulit.Print(a:item, printf("start of %s capturing group", get(s:ord, s:capture_group_nr, '(invalid)')))
+    else " a:item == '\z('
+       call a:hulit.Print(a:item, 'start of "external" group (only usable in :syn-region)')
     endif
     call a:hulit.AddIndent('| ', '  ')
+    let s:at_begin_of_pat = 2
 endfunc "}}}
 func! s:DocGroupEnd(bull, hulit, item) "{{{
     call a:hulit.RemIndent(2)
@@ -113,9 +144,21 @@ endfunc "}}}
 let s:doc['\('] = function("s:DocGroupStart")
 let s:doc['\%('] = function("s:DocGroupStart")
 let s:doc['\)'] =  function("s:DocGroupEnd")
+" let s:doc['\z('] = "only in syntax scripts"
+let s:doc['\z('] = function("s:DocGroupStart")
+
+func! s:DocStar(bull, hulit, item) "{{{
+    if s:at_begin_of_pat >= 1
+       " call a:hulit.Print(a:item, "(at begin of pattern) literal `*'")
+       call a:hulit.AddLiteral(a:item)
+    else
+       call a:hulit.Print(a:item, "(multi) zero or more of the preceding atom")
+    endif
+endfunc "}}}
+
+" let s:doc['*'] = "(multi) zero or more of the preceding atom"
+let s:doc['*'] = function("s:DocStar")
 
-let s:doc['\z('] = "only in syntax scripts"
-let s:doc['*'] = "(multi) zero or more of the preceding atom"
 let s:doc['\+'] = "(multi) one or more of the preceding atom"
 let s:doc['\='] = "(multi) zero or one of the preceding atom"
 let s:doc['\?'] = "(multi) zero or one of the preceding atom"
@@ -123,7 +166,7 @@ let s:doc['\?'] = "(multi) zero or one of the preceding atom"
 " let s:doc['\{-'] = "(multi) N to M, non-greedy"
 
 func! s:DocBraceMulti(bull, hulit, item) "{{{
-    let rest = a:bull.Bite('^-\=\d*\%(,\d*\)\=}')
+    let rest = a:bull.Bite('^-\=\d*\%(,\d*\)\=\\\=}')
     if rest != ""
        if rest == '-}'
            call a:hulit.Print(a:item. rest, "non-greedy version of `*'")
@@ -133,7 +176,7 @@ func! s:DocBraceMulti(bull, hulit, item) "{{{
            call a:hulit.Print(a:item. rest, "(multi) N to M, greedy")
        endif
     else
-       echoerr printf('ExplainPattern: cannot parse %s', a:item. a:bull.Rest())
+       call a:hulit.Print(a:item, "(invalid) incomplete `\\{...}' item")
     endif
 endfunc "}}}
 
@@ -142,11 +185,49 @@ let s:doc['\{'] = function("s:DocBraceMulti")
 let s:doc['\@>'] = "(multi) match preceding atom like a full pattern"
 let s:doc['\@='] = "(assertion) require match for preceding atom"
 let s:doc['\@!'] = "(assertion) forbid match for preceding atom"
-let s:doc['\@<='] = "(assertion) require match for preceding atom to the left"
-let s:doc['\@<!'] = "(assertion) forbid match for preceding atom to the left"
-let s:doc['^'] = "(assertion) require match at start of line"
+
+func! s:DocBefore(bull, hulit, item) "{{{
+    let rest = a:bull.Bite('^[=!]')
+    if rest == "="
+       call a:hulit.Print(a:item.rest, "(assertion) require match for preceding atom to the left")
+    elseif rest == "!"
+       call a:hulit.Print(a:item.rest, "(assertion) forbid match for preceding atom to the left")
+    else
+       call a:hulit.Print(a:item.rest, "(invalid) `\\@<' must be followed by `=' or `!'")
+    endif
+endfunc "}}}
+
+let s:doc['\@<'] = function("s:DocBefore")
+
+func! s:DocCircumFlex(bull, hulit, item) "{{{
+    if s:at_begin_of_pat >= 1
+       call a:hulit.Print(a:item, "(assertion) require match at start of line")
+       " after `^' is not at begin of pattern ... handle special case `^*' here:
+       if a:bull.Bite('^\*') == "*"
+           call a:hulit.AddLiteral("*")
+       endif
+    else
+       " call a:hulit.Print(a:item, "(not at begin of pattern) literal `^'")
+       call a:hulit.AddLiteral(a:item)
+    endif
+endfunc "}}}
+
+" let s:doc['^'] = "(assertion) require match at start of line"
+let s:doc['^'] = function("s:DocCircumFlex")
+
 let s:doc['\_^'] = "(assertion) like `^', allowed anywhere in the pattern"
-let s:doc['$'] = "(assertion) require match at end of line"
+
+func! s:DocDollar(bull, hulit, item) "{{{
+    if a:bull.Rest() =~ '^$\|^\\[&|)n]'
+       call a:hulit.Print(a:item, "(assertion) require match at end of line")
+    else
+       call a:hulit.AddLiteral(a:item)
+    endif
+endfunc "}}}
+
+" let s:doc['$'] = "(assertion) require match at end of line"
+let s:doc['$'] = function("s:DocDollar")
+
 let s:doc['\_$'] = "(assertion) like `$', allowed anywhere in the pattern"
 let s:doc['.'] = "match any character"
 let s:doc['\_.'] = "match any character or newline"
@@ -157,7 +238,8 @@ func! s:DocUnderscore(bull, hulit, item) "{{{
        let cclass_doc = get(s:doc, '\'. cclass, '(invalid character class)')
        call a:hulit.Print(a:item. cclass, printf('%s or end-of-line', cclass_doc))
     else
-       echoerr printf('ExplainPattern: cannot parse %s', a:item. matchstr(a:bull.Rest(), '.'))
+       call a:hulit.Print(a:item, "(invalid) `\\_' should be followed by a letter or `[...]'")
+       " echoerr printf('ExplainPattern: cannot parse %s', a:item. matchstr(a:bull.Rest(), '.'))
     endif
 endfunc "}}}
 
@@ -171,6 +253,21 @@ let s:doc['\%$'] = "(assertion) match at end of buffer"
 let s:doc['\%V'] = "(assertion) match within the Visual area"
 let s:doc['\%#'] = "(assertion) match with cursor position"
 
+func! s:DocRegexEngine(bull, hulit, item) "{{{
+    let engine = a:bull.Bite('^[012]')
+    if engine == "0"
+       call a:hulit.Print(a:item.engine, 'Force automatic selection of the regexp engine (since v7.3.970).')
+    elseif engine == "1" 
+       call a:hulit.Print(a:item.engine, 'Force using the old engine (since v7.3.970).')
+    elseif engine == "2"
+       call a:hulit.Print(a:item.engine, 'Force using the NFA engine (since v7.3.970).')
+    else
+       call a:hulit.Print(a:item, '(invalid) \%#= can only be followed by 0, 1, or 2')
+    endif
+endfunc "}}}
+
+let s:doc['\%#='] = function("s:DocRegexEngine")
+
 " \%'m   \%<'m   \%>'m
 " \%23l  \%<23l  \%>23l
 " \%23c  \%<23c  \%>23c
@@ -190,7 +287,8 @@ func! s:DocBspercAt(bull, hulit, item) "{{{
        elseif type ==# "v"
            call a:hulit.Print(a:item.rest, "match in virtual column ". number)
        else
-           echoerr printf('ExplainPattern: incomplete item %s', a:item. rest)
+           call a:hulit.Print(a:item.rest, "(invalid) incomplete `\\%' item")
+           " echoerr printf('ExplainPattern: incomplete item %s', a:item. rest)
        endif
     endif
 endfunc "}}}
@@ -208,7 +306,8 @@ func! s:DocBspercBefore(bull, hulit, item) "{{{
        elseif type ==# "v"
            call a:hulit.Print(a:item.rest, "match before virtual column ". number)
        else
-           echoerr printf('ExplainPattern: incomplete item %s', a:item. rest)
+           call a:hulit.Print(a:item.rest, "(invalid) incomplete `\\%<' item")
+           " echoerr printf('ExplainPattern: incomplete item %s', a:item. rest)
        endif
     endif
 endfunc "}}}
@@ -226,7 +325,8 @@ func! s:DocBspercAfter(bull, hulit, item) "{{{
        elseif type ==# "v"
            call a:hulit.Print(a:item.rest, "match after virtual column ". number)
        else
-           echoerr printf('ExplainPattern: incomplete item %s', a:item. rest)
+           call a:hulit.Print(a:item.rest, "(invalid) incomplete `\\%>' item")
+           " echoerr printf('ExplainPattern: incomplete item %s', a:item. rest)
        endif
     endif
 endfunc "}}}
@@ -266,7 +366,7 @@ let s:doc['\e'] = "match <Esc>"
 let s:doc['\t'] = "match <Tab>"
 let s:doc['\r'] = "match <CR>"
 let s:doc['\b'] = "match CTRL-H"
-let s:doc['\n'] = "match a newline"
+let s:doc['\n'] = [function("s:DocBeginOfPat"), "match a newline"]
 let s:doc['~'] = "match the last given substitute string"
 let s:doc['\1'] = "match first captured string"
 let s:doc['\2'] = "match second captured string"
@@ -278,21 +378,31 @@ let s:doc['\7'] = "match seventh captured string"
 let s:doc['\8'] = "match eighth captured string"
 let s:doc['\9'] = "match ninth captured string"
 
-" \z1
-" \z2
-" \z9
+let s:doc['\z1'] = 'match same string matched by first "external" group'
+let s:doc['\z2'] = 'match same string matched by second "external" group'
+let s:doc['\z3'] = 'match same string matched by third "external" group'
+let s:doc['\z4'] = 'match same string matched by fourth "external" group '
+let s:doc['\z5'] = 'match same string matched by fifth "external" group'
+let s:doc['\z6'] = 'match same string matched by sixth "external" group'
+let s:doc['\z7'] = 'match same string matched by seventh "external" group'
+let s:doc['\z8'] = 'match same string matched by eighth "external" group'
+let s:doc['\z9'] = 'match same string matched by ninth "external" group'
 
 " from MakeMagic()
 " skip the rest of a collection
-let s:coll_skip_pat = '^\^\=]\=\%(\%(\\[\^\]\-\\bertn]\|\[:\w\+:]\|[^\]]\)\@>\)*]'
+let s:coll_skip_pat = '^\^\=]\=\%(\%(\\[\^\]\-\\bertn]\|\[:\w\+:]\|\[=.=]\|\[\..\.]\|[^\]]\)\@>\)*]'
 
 func! s:DocCollection(bull, hulit, item) "{{{
     let collstr = a:bull.Bite(s:coll_skip_pat)
-    let inverse = collstr =~ '^\^'
-    let with_nl = a:item == '\_['
-    let descr = inverse ? printf('collection not matching [%s', collstr[1:]) : 'collection'
-    let descr_nl = printf("%s%s", (inverse && with_nl ? ', but' : ''), (with_nl ? ' with end-of-line added' : ''))
-    call a:hulit.Print(a:item. collstr, descr. descr_nl)
+    if collstr == "" || collstr == "]"
+       call a:hulit.AddLiteral('['. collstr)
+    else
+       let inverse = collstr =~ '^\^'
+       let with_nl = a:item == '\_['
+       let descr = inverse ? printf('collection not matching [%s', collstr[1:]) : 'collection'
+       let descr_nl = printf("%s%s", (inverse && with_nl ? ', but' : ''), (with_nl ? ' with end-of-line added' : ''))
+       call a:hulit.Print(a:item. collstr, descr. descr_nl)
+    endif
 endfunc "}}}
 
 let s:doc['['] = function("s:DocCollection")
@@ -318,9 +428,15 @@ endfunc "}}}
 let s:doc['\%['] = function("s:DocOptAtoms")
 let s:doc[']'] = function("s:DocOptAtoms")
 
-let s:doc['\c'] = "ignore case while matching the pattern"
-let s:doc['\C'] = "match case while matching the pattern"
-let s:doc['\Z'] = "ignore composing characters in the pattern"
+func! s:DocAnywhere(bull, hulit, item, msg) "{{{
+    call a:hulit.Print(a:item, a:msg)
+    " keep state:
+    let s:at_begin_of_pat += 1
+endfunc "}}}
+
+let s:doc['\c'] = [function("s:DocAnywhere"), "ignore case while matching the pattern"]
+let s:doc['\C'] = [function("s:DocAnywhere"), "match case while matching the pattern"]
+let s:doc['\Z'] = [function("s:DocAnywhere"), "ignore composing characters in the pattern"]
 
 " \%d 123
 " \%x 2a
@@ -414,8 +530,9 @@ func! s:NewHelpPrinter() "{{{
            if self.literals =~ '\\'
                let self.literals = substitute(self.literals, '\\\(.\)', '\1', 'g')
            endif
-           let so = self.literals =~ '[^ ]' ? '' : ', spaces only'
-           echon " (". strchars(self.literals). " atom(s)". so.")"
+           let spconly = self.literals =~ '[^ ]' ? '' : ', spaces only'
+           let nlit = strchars(self.literals)
+           echon " (". nlit. (nlit==1 ? " atom" : " atoms"). spconly.")"
        endif
        echohl None
        let self.literals = ''
@@ -451,22 +568,19 @@ func! s:NewHelpPrinter() "{{{
     return obj
 endfunc "}}}
 
-func! s:NewTokenBiter(str, ...) "{{{
+func! s:NewTokenBiter(str) "{{{
     " {str}    string to eat pieces from
-    " {a:1}    pattern to skip separators
-    let whitepat = a:0>=1 ? a:1 : '^\s*'
-    let obj = {'str': a:str, 'whitepat': whitepat}
+    let obj = {'str': a:str}
 
     " consume piece from start of input matching {pat}
     func! obj.Bite(pat) "{{{
        " {pat}     should start with '^'
-       let skip = matchend(self.str, self.whitepat)
-       let bite = matchstr(self.str, a:pat, skip)
-       let self.str = strpart(self.str, matchend(self.str, self.whitepat, skip + strlen(bite)))
+       let bite = matchstr(self.str, a:pat)
+       let self.str = strpart(self.str, strlen(bite))
        return bite
     endfunc "}}}
 
-    " get the unparsed rest of input
+    " get the unparsed rest of input (not consuming)
     func! obj.Rest() "{{{
        return self.str
     endfunc "}}}