Commits

ZyX_I committed 45b7e30

@/fwc/intfuncs: Added `idof' check

Comments (0)

Files changed (12)

              keys.
     Completer: undefined.
 
+idof {idspec}                                                     *FWC-c-idof*
+    Checker: check whether current argument is an identifier of the given 
+             essence.
+                {idspec} :: "variable"  | "var"                 *FWC-{idspec}*
+                          | "highlight" | "hl"
+                          | "command"   | "cmd"
+                          | "function"  | "func"
+                          | "option"    | "opt"
+                          | "event"
+                          | "augroup"
+                          | "sign"
+    Completer: completes list of possible variants.
+
 range {number} {number} {one}   (|FWC-{one}|)                    *FWC-c-range*
     Checker: check whether current argument is a number or float and is in 
              given range. Floating-point values are not allowed unless one of 
          keys.
 @/table:
     0.1: Posted strdisplaywidth function
+@/fwc:
+    0.1: (intfuncs-0.1) Added `idof' checker
 
 vim: ft=help:tw=78

plugin/frawor/fwc.vim

 "▶1 Header
 scriptencoding utf-8
-execute frawor#Setup('0.0', {'@/fwc/compiler': '0.0',
+execute frawor#Setup('0.1', {'@/fwc/compiler': '0.0',
             \                '@/decorators'  : '0.0'}, 1)
 "▶1 Define messages
 if v:lang=~?'ru'

plugin/frawor/fwc/compiler.vim

             \   'ninlist': 'argument is not in list',
             \ 'invlstlen': 'invalid list length: expected %u, but got %u',
             \'eitherfail': 'all alternatives failed',
+            \      'nohl': 'unknown highlight group: %s',
+            \     'nocmd': 'no such command: %s',
+            \    'nofunc': 'no such function: %s',
+            \     'noopt': 'no such option: %s',
+            \   'noevent': 'event %s is not supported',
+            \     'noaug': 'autocmd group %s is not defined',
+            \    'nosign': 'unknown sign: %s',
+            \     'novar': 'no such variable: %s',
             \     'isdir': 'not a directory: %s',
             \    'isfile': 'directories are not accepted: %s',
             \    'nwrite': '%s is not writable',
 call s:_f.postresource('fwc_compile', s:F.compstr)
 "▶1
 " TODO implement recursive structures checking
+" TODO cache compilation results
 call frawor#Lockvar(s:, '')
 " vim: fmr=▶,▲ sw=4 ts=4 sts=4 et tw=80

plugin/frawor/fwc/intfuncs.vim

 "▶1 Header
 scriptencoding utf-8
-execute frawor#Setup('0.0', {'@/resources': '0.0',
-            \                '@/os':        '0.0'}, 1)
+execute frawor#Setup('0.1', {'@/resources': '0.0',
+            \                '@/os':        '0.0',
+            \                '@/signs':     '0.0',}, 1)
 let s:r={}
 let s:cf='CHECKFAILED'
 let s:cfstr=string(s:cf)
 let s:cfreg='\v^'.s:cf.'$'
+let s:strfuncregstr='''\v^%([sla]@!\w:%(\w|\.)+|%(\V<SNR>\v|s@!\w:)?\w+)$'''
 "▲1
 "Completion  -------------------------------------------------------------------
 "▶1 Path
     return s:F.recupglob(filter(glist, 's:_r.os.path.isdir(v:val)'),
                 \        a:fragments, a:i+1)
 endfunction
+"▶1 getfromhelp :: intvar, helpfroot, sectstr, init, Procline → ? + s:{intvar}
+function s:F.getfromhelp(intvar, helpfile, sectstr, init, Procline)
+    if has_key(s:, a:intvar)
+        return copy(s:{a:intvar})
+    endif
+    let s:{a:intvar}=a:init
+    let helpfile=s:_r.os.path.join($VIMRUNTIME, 'doc', a:helpfile.'.txt')
+    if !filereadable(helpfile)
+        return a:init
+    endif
+    let help=readfile(helpfile)
+    let ruler=repeat('=', 78)
+    let slen=len(a:sectstr)-1
+    while !empty(help)
+        if remove(help, 0) is# ruler
+            if remove(help, 0)[:(slen)] is# a:sectstr
+                while !empty(help)
+                    let line=remove(help, 0)
+                    let r=call(a:Procline, [line, s:{a:intvar}], {})
+                    if r is 0
+                        break
+                    endif
+                endwhile
+                break
+            endif
+        endif
+    endwhile
+    lockvar! s:{a:intvar}
+    return copy(s:{a:intvar})
+endfunction
 "▶1 getuserfunctions :: () → [[fname, fargs*]]
 " TODO cache results
 function s:F.getuserfunctions()
                 \'[v:val[0]]+split(v:val[1], ", ")')
 endfunction
 "▶1 getinternalfunctions :: () + s:vimintfuncs → {fname: [length]}
+function s:F.getintfunc(line, intfuncs)
+    if empty(a:line) && !empty(a:intfuncs)
+        return 0
+    endif
+    let match=matchlist(a:line, '\v(\w+)\((.{-})\)')
+    if empty(match)
+        return -1
+    endif
+    let fname=match[1]
+    if !exists('*'.fname)
+        return -1
+    endif
+    let fargs=substitute(match[2], '\s', '', 'g')
+    let lengths=[]
+    if stridx(fargs, '...')!=-1
+        call add(lengths, -1)
+    endif
+    let bidx=stridx(fargs, '[')
+    if bidx!=-1
+        while bidx!=-1
+            call add(lengths, len(split(fargs[:(bidx)], ','))+get(lengths,-1,0))
+            let fargs=fargs[(bidx+1):]
+            let bidx=stridx(fargs, '[')
+        endwhile
+    else
+        call add(lengths, len(split(fargs, ',')))
+    endif
+    let a:intfuncs[fname]=lengths
+    return 1
+endfunction
 function s:F.getinternalfunctions()
-    if exists('s:vimintfuncs')
-        return copy(s:vimintfuncs)
+    return s:F.getfromhelp('vimintfuncs', 'eval', '4. Builtin Functions', {},
+                \          s:F.getintfunc)
+endfunction
+"▶1 getusercommands :: () + :command → [String]
+function s:F.getusercommands()
+    redir => commands
+    silent command
+    redir END
+    return map(split(commands, "\n")[1:], 'matchstr(v:val, "\\v\\w+")')
+endfunction
+"▶1 getinternalcommands :: () + s:vimintfuncs → [String]
+function s:F.getintcmd(line, intcmds)
+    if empty(a:line) && !empty(a:intcmds)
+        return 0
+    elseif a:line[:1] isnot# '|:'
+        return -1
     endif
-    let s:vimintfuncs={}
-    let helpfile=s:_r.os.path.join($VIMRUNTIME, 'doc', 'eval.txt')
-    if !filereadable(helpfile)
-        return copy(s:vimintfuncs)
+    let cmd=matchstr(a:line, '\v\s\:\S+')[2:]
+    if !empty(cmd)
+        let cmd=substitute(cmd, '\W', '', 'g')
+        if exists(':'.cmd)
+            call add(a:intcmds, cmd)
+        endif
     endif
-    let help=readfile(helpfile)
-    let ruler=repeat('=', 78)
-    let section=''
-    while !empty(help)
-        if remove(help, 0) is# ruler
-            if remove(help, 0)[:19] is# '4. Builtin Functions'
-                while !empty(help)
-                    let line=remove(help, 0)
-                    if empty(line) && !empty(s:vimintfuncs)
-                        break
+    return 1
+endfunction
+function s:F.getinternalcommands()
+    return s:F.getfromhelp('vimintcommands', 'index', '5. EX commands', [],
+                \          s:F.getintcmd)
+endfunction
+"▶1 getevents :: () + s:vimintfuncs → [String]
+function s:F.getevent(line, events)
+    if a:line[0] isnot# '|'
+        return -1
+    elseif a:line[-20:] is# '*autocmd-events-abc*'
+        return 0
+    endif
+    let event=matchstr(a:line, '\v^\|\u\w+\|')[1:-2]
+    if !empty(event) && exists('##'.event)
+        call add(a:events, event)
+    endif
+    return 1
+endfunction
+function s:F.getevents()
+    return s:F.getfromhelp('vimevents', 'autocmd', '5. Events', [],
+                \          s:F.getevent)
+endfunction
+"▶1 getsigns :: () → [String]
+function s:F.getsigns()
+    if !has('signs')
+        return []
+    endif
+    redir => signs
+    silent sign list
+    redir END
+    return map(split(signs, "\n"), 'v:val[5:(stridx(v:val, " ", 5)-1)]')
+endfunction
+"▶1 getaugroups :: () → [String]
+function s:F.getaugroups()
+    if !has('autocmd')
+        return []
+    endif
+    redir => augroups
+    silent augroup
+    redir END
+    return split(split(augroups, "\n")[0])
+endfunction
+"▶1 gethighlights :: () → [String]
+function s:F.gethighlights()
+    if !has('syntax')
+        return []
+    endif
+    redir => hls
+    silent hi
+    redir END
+    return map(filter(split(hls, "\n"), 'v:val[0] isnot# " "'),
+                \'v:val[:(stridx(v:val, " ")-1)]')
+endfunction
+"▶1 getoptions :: () + :set → [(String, 0|1|2)]
+" 0: Boolean option
+" 1: Number option
+" 2: String option
+function s:F.getoptions()
+    if exists('s:vimoptions')
+        return copy(s:vimoptions)
+    endif
+    redir => options
+    silent set all
+    redir END
+    let s:vimoptions=[]
+    let ismanyoptline=1
+    for line in split(options, "\n")[1:]
+        if ismanyoptline
+            let addedoptions=0
+            while !empty(line)
+                let line=substitute(line, '\v^\s+', '', '')
+                let option=''
+                if line=~#'\v\l+\='
+                    let option=line[:(stridx(line, '=')-1)]
+                    let bool=0
+                else
+                    let option=matchstr(line, '\v^\l+')
+                    if option[:1] is# 'no' && !exists('&'.option)
+                        let option=option[2:]
                     endif
-                    let match=matchlist(line, '\v(\w+)\((.{-})\)')
-                    if empty(match)
-                        continue
-                    endif
-                    let fname=match[1]
-                    let fargs=substitute(match[2], '\s', '', 'g')
-                    let lengths=[]
-                    if stridx(fargs, '...')!=-1
-                        call add(lengths, -1)
-                    endif
-                    let bidx=stridx(fargs, '[')
-                    if bidx!=-1
-                        while bidx!=-1
-                            call add(lengths, len(split(fargs[:(bidx)], ','))
-                                        \                  +get(lengths, -1, 0))
-                            let fargs=fargs[(bidx+1):]
-                            let bidx=stridx(fargs, '[')
-                        endwhile
-                    else
-                        call add(lengths, len(split(fargs, ',')))
-                    endif
-                    let s:vimintfuncs[fname]=lengths
-                endwhile
-                call filter(s:vimintfuncs, 'exists("*".v:key)')
-                break
+                    let bool=1
+                endif
+                if exists('+'.option)
+                    let type=((bool)?(0):
+                                \    ((type(eval('&'.option))==type(0))?(1):
+                                \                                       (2)))
+                    call add(s:vimoptions, [option, type])
+                endif
+                let line=substitute(line, '\v^\S+', '', '')
+                let addedoptions+=1
+            endwhile
+            if addedoptions==1
+                let ismanyoptline=0
+            endif
+        else
+            let option=matchstr(line, '\v\l+')
+            if exists('+'.option)
+                let type=((type(eval('&'.option))==type(0))?(1):(2))
+                call add(s:vimoptions, [option, type])
             endif
         endif
-    endwhile
-    lockvar s:vimintfuncs
-    return copy(s:vimintfuncs)
+    endfor
+    lockvar! s:vimoptions
+    return copy(s:vimoptions)
 endfunction
 "▲1
 "Filters/checkers --------------------------------------------------------------
                     \         'keymis', a:idx, keystr)
     endif
 endfunction
+"▶1 `idof'
+" Check whether argument is an identifier
+let s:r.idof={'args': ['get']}
+let s:idab={
+            \ 'var': 'variable',
+            \  'hl': 'highlight',
+            \ 'cmd': 'command',
+            \'func': 'function',
+            \ 'opt': 'option',
+        \}
+let s:ids=values(s:idab)+['event', 'augroup', 'sign']
+"▶2 idof.get :: &self!
+" Input: "variable"  | "var"
+"      | "highlight" | "hl"
+"      | "command"   | "cmd"
+"      | "function"  | "func"
+"      | "option"    | "opt"
+"      | "event"
+"      | "augroup"
+"      | "sign"
+" Output: add({idspec})
+function s:r.idof.get()
+    let c=self.readc()
+    if has_key(s:idab, c)
+        call self.add(s:idab[c])
+    elseif index(s:ids, c)!=-1
+        call self.add(c)
+    else
+        call self.throw('invid', c)
+    endif
+    return self
+endfunction
+"▶2 check
+function s:r.idof.check(desc, idx, type)
+    let curargstr=self.argstr()
+    call self.addtypecond([type('')], a:idx)
+    let spec=a:desc[1]
+    if spec is# 'highlight'
+        call self.nextthrow('!hlexists('.curargstr.')',
+                    \       'nohl', a:idx, curargstr)
+    elseif spec is# 'command'
+        call self.nextthrow('!exists(":".'.curargstr.')',
+                    \       'nocmd', a:idx, curargstr)
+    elseif spec is# 'function'
+        call self.nextthrow('!('.curargstr.'=~#'.s:strfuncregstr.
+                    \        ' && exists("*".'.curargstr.'))',
+                    \       'nofunc', a:idx, curargstr)
+    elseif spec is# 'option'
+        call self.nextthrow('!exists("+".'.curargstr.')',
+                    \       'noopt', a:idx, curargstr)
+    elseif spec is# 'event'
+        call self.nextthrow('!exists("##".'.curargstr.')',
+                    \       'noevent', a:idx, curargstr)
+    elseif spec is# 'augroup'
+        call self.nextthrow('stridx('.curargstr.', "#")!=-1 || '.
+                    \       '!exists("#".'.curargstr.')',
+                    \       'noaug', a:idx, curargstr)
+    elseif spec is# 'sign'
+        let signexistsstr=self.getfunstatvar('sign', s:_r.sign.exists, 'exists')
+        call self.nextthrow('!'.signexistsstr.'('.curargstr.')',
+                    \       'nosign', a:idx, curargstr)
+    elseif spec is# 'variable'
+        call self.nextthrow(curargstr.'!~#''\v^[als]@!\l\:\w*$'' || '.
+                    \       '!exists('.curargstr.')',
+                    \       'novar', a:idx, curargstr)
+    endif
+    return self
+endfunction
+"▶2 complete
+let s:varsstr=join(map(split('vgbwt', '\v.@='),
+            \          '"map(keys(".v:val.":), \"''".v:val.":''.v:val\")"'),
+            \      '+')
+let s:idofcompletes={'highlight': 1, 'event': 1, 'augroup': 1, 'sign': 1}
+function s:r.idof.complete(desc, idx, type)
+    let spec=a:desc[1]
+    if has_key(s:idofcompletes, spec)
+        let getvariantsstr=self.getfunstatvar('completers', s:F['get'.spec.'s'],
+                    \                         spec.'s').'()'
+        return self.addmatches(getvariantsstr, type([]))
+    elseif spec is# 'command'
+        let intcmdsstr=self.getfunstatvar('completers', s:F.getinternalcommands,
+                    \                     'commands').'()'
+        let usercmdsstr=self.getfunstatvar('completers', s:F.getusercommands,
+                    \                      'ucommands').'()'
+        return self.addmatches(intcmdsstr.'+'.usercmdsstr, type([]))
+    elseif spec is# 'function'
+        let userfunctionsstr='map('.self.getfunstatvar('completers',
+                    \                                  s:F.getuserfunctions,
+                    \                                  'userfunctions').'(), '.
+                    \            '"v:val[0]")'
+        let intfuncsstr='keys('.self.getfunstatvar('completers',
+                    \                              s:F.getinternalfunctions,
+                    \                              'vimfunctions').'())'
+        return self.addmatches(userfunctionsstr.'+'.intfuncsstr, type([]))
+    elseif spec is# 'option'
+        let intoptsstr='map('.self.getfunstatvar('completers', s:F.getoptions,
+                    \                            'options').'(), "v:val[0]")'
+        return self.addmatches(intoptsstr, type([]))
+    elseif spec is# 'variable'
+        return self.addmatches(s:varsstr, type([]))
+    endif
+endfunction
+"▲2
 "▶1 `range'
 " Checks whether {argument} is in given range
 let s:r.range={'args': ['number', 'number', '?one']}
                     \          frefpref .' isnot# "s:" && '.
                     \          frefpref2.'    !=? "<SID>") '.
                     \        '|| (type('.curargstr.')=='.type('').
-                    \            '&& '.curargstr.'=~#'.
-                    \             '''\v^%([sla]@!\w:%(\w|\.)+|'.
-                    \                    '%(<SNR>|s@!\w:)?\w+)$'''.
+                    \            '&& '.curargstr.'=~#'.s:strfuncregstr.
                     \            '&& exists("*".'.curargstr.')))',
                     \       'nsfunc', a:idx, 'string('.curargstr.')')
     endif

plugin/frawor/fwc/parser.vim

             \  'actmis': 'missing arguments description',
             \ 'typemis': 'missing type description',
             \ 'invtype': 'invalid type description: %s',
+            \   'invid': 'invalid identifier description: %s',
             \  'invreg': 'invalid regular expression: %s',
             \ 'wordend': 'regular expression cannot end with %s',
             \  'noexpr': 'expected expression, but got nothing',
 plugin/frawor/fwc/compiler:nregmatch
 plugin/frawor/fwc/compiler:typefail
 plugin/frawor/fwc/compiler:nregmatch
+::: Section <Checks/Built-in checks/idof>
+plugin/frawor/fwc/compiler:typefail
+plugin/frawor/fwc/compiler:novar
+plugin/frawor/fwc/compiler:nofunc
+plugin/frawor/fwc/compiler:nofunc
+plugin/frawor/fwc/compiler:noopt
+plugin/frawor/fwc/compiler:noopt
+plugin/frawor/fwc/compiler:nohl
+plugin/frawor/fwc/compiler:nohl
+plugin/frawor/fwc/compiler:noevent
+plugin/frawor/fwc/compiler:noevent
+plugin/frawor/fwc/compiler:noaug
+plugin/frawor/fwc/compiler:noaug
+plugin/frawor/fwc/compiler:nosign
+plugin/frawor/fwc/compiler:nosign
+sign Bar text=<>
+sign 1 text=<>
+sign 1 text=<>
 ::: Section <Checks/Built-in checks/type>
 plugin/frawor/fwc/compiler:typefail
 plugin/frawor/fwc/compiler:typefail

test/fwccomplete-redir.ok

 >>> messages
 ::: Section <run>
+::: Section <idof>
 <<< messages

test/fwccompletetests-withredir.dat

 `run [1 2 3 4 5 6 7 8]
   @p
   printf
+#▶1 idof
+:command -nargs=1 WriteFile W <args>
+:sign define usersign text=<>
+:sign define 0001 text=<>
+:augroup Test | augroup END
+`idof variable
+  @testf
+  g:testfile
+
+`idof hl
+  @Tit
+  Title
+
+`idof command
+  @Wr
+  WriteFile
+
+  @rite
+  write WriteFile
+
+`idof function
+  @W
+  WriteFile
+
+  @rite
+  WriteFile writefile
+
+`idof option
+  @backs
+  backspace
+
+`idof event
+  @U
+  User
+
+`idof sign
+  @u
+  usersign
+
+  @+
+  usersign 1
+
+`idof augroup
+  @T
+  Test
 
 # vim: cms=#%s fmr=▶,▲ sw=2 ts=2 sts=2 et

test/fwctests.dat

 ['match/\vb@<!a/',  'check'], [0],                  0
 ['match$regex',     'check'], [''],                 0
 ['match$regex',     'check'], ['abc'],              1
+#▶3 idof
+:augroup Foo | augroup END
+:sign define Bar text=<>
+:sign define 0001 text=<>
+['idof var',       'check'], [0],           0
+['idof var',       'check'], ['outfile'],   0
+['idof var',       'check'], ['g:outfile'], 1
+['idof variable',  'check'], ['g:outfile'], 1
+['idof func',      'check'], [':'],         0
+['idof func',      'check'], ['e'],         0
+['idof func',      'check'], ['eval'],      1
+['idof func',      'check'], ['WriteFile'], 1
+['idof function',  'check'], ['WriteFile'], 1
+['idof opt',       'check'], ['_'],         0
+['idof opt',       'check'], ['-'],         0
+['idof opt',       'check'], ['bs'],        1
+['idof opt',       'check'], ['backspace'], 1
+['idof option',    'check'], ['backspace'], 1
+['idof hl',        'check'], ['__'],        0
+['idof hl',        'check'], ['/'],         0
+['idof hl',        'check'], ['Title'],     1
+['idof highlight', 'check'], ['Title'],     1
+['idof event',     'check'], ['*'],         0
+['idof event',     'check'], ['XXX'],       0
+['idof event',     'check'], ['BufAdd'],    1
+['idof augroup',   'check'], ['*'],         0
+['idof augroup',   'check'], ['Bar'],       0
+['idof augroup',   'check'], ['Foo'],       1
+['idof sign',      'check'], ['*'],         0
+['idof sign',      'check'], ['0'],         0
+['idof sign',      'check'], ['Bar'],       1
+['idof sign',      'check'], ['001'],       1
+['idof sign',      'check'], ['1'],         1
 #▶3 type
 ['type string',     'check'], [''],                 1
 ['type string',     'check'], [0],                  0

test/reload-frawor.ok

 unloadpre: plugin/frawor/perl
 unloadpre: plugin/frawor/python
 unloadpre: plugin/frawor/ruby
-unloadpre: plugin/frawor/signs
 unloadpre: plugin/frawor/table
 unloadpre: plugin/frawor/tcl
 unloadpre: plugin/ignoredeps-feature
 unloadpre: plugin/frawor/fwc/parser
 unloadpre: plugin/frawor/fwc/intfuncs
 unloadpre: plugin/frawor/fwc/topconstructs
+unloadpre: plugin/frawor/signs
 unloadpre: plugin/frawor/os
 unloadpre: plugin/frawor/checks
 unloadpre: plugin/frawor/fwc
 unload: plugin/frawor/perl
 unload: plugin/frawor/python
 unload: plugin/frawor/ruby
-unload: plugin/frawor/signs
 unload: plugin/frawor/table
 unload: plugin/frawor/tcl
 unload: plugin/ignoredeps-feature
 load: plugin/frawor/fwc/parser
 register: plugin/frawor
 load: plugin/frawor
+register: plugin/frawor/signs
+load: plugin/frawor/signs
 register: plugin/frawor/decorators
 load: plugin/frawor/decorators
 register: plugin/frawor/os
 register: plugin/frawor/tcl
 load: plugin/frawor/tcl
 register: plugin/frawor/table
-register: plugin/frawor/signs
-load: plugin/frawor/signs
 register: plugin/frawor/ruby
 load: plugin/frawor/ruby
 register: plugin/frawor/python

Binary file modified.

Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.