Commits

ZyX_I committed 90f7218

@/fwc: Implemented `if' completion

Comments (0)

Files changed (5)

     Filter: replace current argument with the result of evaluating function.
             Same as `|*{func}'.
     In both cases dot argument is set to argument being processed.
+    Completer: completes to nothing, breaking completion.
 
 expr {expr}    (|FWC-{expr}|)                        *FWC-c-expr* *FWC-f-expr*
     Checker: check whether result of evaluating given expression is not 0.
              Same as `?={expr}'
     Filter: replace current argument with the result of evaluating expression.
             Same as `|={expr}'.
+    Completer: completes to nothing, breaking completion.
 
 if {arg} {arg} {arg}   (|FWC-{arg}|)                                *FWC-f-if*
     Filter: if argument matches first {arg}, then it is processed using the 
 
 not {arg}    (|FWC-{arg}|)                                        *FWC-c-not*
     Checker: fails if processing argument using {arg} succeeds.
+    Completer: completes to nothing, breaking completion.
 
 either {arg}*    (|FWC-{arg}|)                                 *FWC-c-either*
     Checker: succeeds if any of given {arg}s succeeds.
     Checks whether argument is a list with a fixed (equal to number of {arg}s) 
     length, then processes all elements of the list using given 
     specification.
+    Completer: undefined.
 
 list {arg}    (|FWC-{arg}|)                          *FWC-c-list* *FWC-f-list*
     Checks whether argument is a list, then processes all its elements using 
     given specification.
+    Completer: undefined.
 
 dict {ddescr}                                        *FWC-c-dict* *FWC-f-dict*
     Checks whether argument is a dictionary matching {ddescr}.
+    Completer: undefined.
+
     Dictionary description is a list of key descriptions and attached argument 
     descriptions.
         (|FWC-{str}|, |FWC-{arg}|, |FWC-{regex}|, |FWC-{func}|, |FWC-{expr}|)
             {matcher} is absent, then it acts like checker and allows argument 
             to be of any type. In other case argument is forced to be 
             a string.
+    Completer: uses given {var} as a list of possible variants. Note that if 
+               {var} expands to a function call, it won't be cached.
                                                        *FWC-c-key* *FWC-f-key*
 key {var} ( "~" {matcher} )?   (|FWC-{var}|, |FWC-{matcher}|)
     Like |FWC-c-in| or |FWC-f-in|, but for dictionaries: dictionary keys are 
     taken as variants.
+    Completer: uses keys from given {var} as a list of possible variants.
+               Note that if {var} expands to a function call, it won't be 
+               cached.
 
 take {var} {matcher}   (|FWC-{var}|, |FWC-{matcher}|)             *FWC-f-take*
     Filter: pick up a value of the key from {var} that matched current 
             argument.
+    Completer: uses keys from given {var} as a list of possible variants.
+               Note that if {var} expands to a function call, it won't be 
+               cached.
 
 substitute {reg} {string}? {string}?                        *FWC-f-substitute*
     Filter: replace current argument with the result of calling substitute 
                     {string} :: "$" {var}                       *FWC-{string}*
                               | {str}
                               | {wordchar}+
+    Completer: completes to nothing, breaking completion.
 
 haskey {string}*   (|FWC-{string}|)                             *FWC-c-haskey*
     Checker: check whether current argument is a dictionary that has all given 
              keys.
+    Completer: undefined.
 
 range {number} {number} {one}   (|FWC-{one}|)                    *FWC-c-range*
     Checker: check whether current argument is a number or float and is in 
                        (|FWC-{float}|, |FWC-{var}|)
                          {number} :: {float}
                                    | "$" {var}
+    Completer: ignored.
 
 match {reg}   (|FWC-{reg}|)                                      *FWC-c-match*
     Checker: check whether current argument matches given regular expression. 
              Matching is done case-sensitively by default.
+    Completer: ignored.
 
 path {pathspec}                                                   *FWC-c-path*
     Checker: checks whether current argument is a path matching given 
 type {typespec}*                                                  *FWC-c-type*
     Checker: checks whether type of the current argument is one of given 
              types.
+    Completer: ignored.
+
     Possible type specifications:
             Type        Short version  Long version (case is ignoreg) ~
             String      '' or ""       string
     Checker: checks whether argument is a name of a callable function or 
              a callable function reference. If optional argument is 1, then 
              only function references are allowed.
+             Names of dictionary items are also allowed, but only if they 
+             start with scope prefix and do use only dotted subscripts 
+             (dict.key).
 
 isreg                                                            *FWC-c-isreg*
     Checker: checks whether argument is a valid regular expression.
             evaluating `!empty({argument})' (see |empty()|),
             otherwise strings "1", "yes", "ok", "true" (case ignored) are 
             replaced with 1 and others are replaced with 0.
+    Completer: completes to nothing, breaking completion.
 
 is {var}   (|FWC-{var}|)                                            *FWC-c-is*
     Checker: checks whether argument is {var} (see |expr-is|).
+    Completer: completes to nothing, breaking completion.
                                                    *FWC-c-value* *FWC-f-value*
 value {var} {arg}   (|FWC-{var}|, |FWC-{arg}|)
     Process {var} using {arg} specification: override current argument with 
     {var} for given {arg}.
     Note that context variables inside {arg} will point to new current 
     argument, as well as @@@ inside expressions.
+    Completer: ignored.
 
 any, _                                                   *FWC-c-any* *FWC-c-_*
     Checker: matches any argument.
+    Completer: ignored.
 
 vim: ft=help:tw=78

plugin/frawor/fwc/compiler.vim

     endif
     return self
 endfunction
-"▶1 compilemsg      :: &self(msgcontext, _)
-function s:compiler.compilemsg(msg, idx)
-    if self.type is 'complete'
+"▶1 compilemsg      :: &self(msgcontext, idx, type)
+function s:compiler.compilemsg(msg, idx, type)
+    if a:type is 'complete'
         return self
     endif
     if a:msg[1] is 0
     call add(self.msgs.own, msgstr)
     return self
 endfunction
-"▶1 compilepipe     :: &self(pipecontext, idx)
-function s:compiler.compilepipe(pipe, idx)
+"▶1 compilepipe     :: &self(pipecontext, idx, type)
+function s:compiler.compilepipe(pipe, idx, type)
     "▶2 `func' pipe
     if a:pipe[1][0] is 'func'
         let curargstr=self.argstr()
-        if self.type is 'check' || self.type is 'filter'
+        if a:type is 'check' || a:type is 'filter' || a:type is 'pipe'
             call self.let(curargstr, self.getfunc(a:pipe[1],0, curargstr))
             let self.typechanged=1
         endif
     "▶2 `expr' pipe
     elseif a:pipe[1][0] is 'expr'
         let curargstr=self.argstr()
-        if self.type is 'check' || self.type is 'filter'
+        if a:type is 'check' || a:type is 'filter' || a:type is 'pipe'
             call self.let(curargstr, self.getexpr(a:pipe[1], curargstr))
             let self.typechanged=1
         endif
     "▶2 Built-in pipes
     elseif a:pipe[1][0] is 'intfunc'
         let desc=a:pipe[1][1:]
-        if self.type is 'check' || self.type is 'filter'
-            if has_key(s:_r.FWC_intfuncs[desc[0]], 'pipe')
-                call call(s:_r.FWC_intfuncs[desc[0]].pipe, [desc,a:idx, 'pipe'],
+        let dname=desc[0]
+        if a:type is 'check' || a:type is 'filter' || a:type is 'pipe'
+            if has_key(s:_r.FWC_intfuncs[dname], 'pipe')
+                call call(s:_r.FWC_intfuncs[dname].pipe,  [desc,a:idx, 'pipe'],
                             \self)
             else
-                call call(s:_r.FWC_intfuncs[desc[0]].check,[desc,a:idx, 'pipe'],
+                call call(s:_r.FWC_intfuncs[dname].check, [desc,a:idx, 'pipe'],
                             \self)
             endif
-            if has_key(s:_r.FWC_intfuncs[desc[0]], 'optimize')
-                call call(s:_r.FWC_intfuncs[desc[0]].optimize, [a:idx], self)
+            if has_key(s:_r.FWC_intfuncs[dname], 'optimize')
+                call call(s:_r.FWC_intfuncs[dname].optimize, [a:idx], self)
             endif
         elseif has_key(s:_r.FWC_intfuncs[dname], 'complete')
             call call(s:_r.FWC_intfuncs[dname].complete, [desc, a:idx, 'pipe'],
     return self
 endfunction
 let s:compiler.compilefilter=s:compiler.compilepipe
-"▶1 compilecheck    :: &self(checkcontext, idx)
-function s:compiler.compilecheck(check, idx)
+"▶1 compilecheck    :: &self(checkcontext, idx, type)
+function s:compiler.compilecheck(check, idx, type)
     "▶2 `func' check
     if a:check[1][0] is 'func'
-        if self.type is 'check' || self.type is 'filter'
+        if a:type is 'check' || a:type is 'filter' || a:type is 'pipe'
             call self.nextthrow(self.getfunc(a:check[1], 0, self.argstr()).
                         \                                               ' is 0',
                         \       'funcfail', a:idx)
         endif
     "▶2 `expr' check
     elseif a:check[1][0] is 'expr'
-        if self.type is 'check' || self.type is 'filter'
+        if a:type is 'check' || a:type is 'filter' || a:type is 'pipe'
             call self.nextthrow(self.getexpr(a:check[1], self.argstr()).' is 0',
                         \       'exprfail', a:idx)
         endif
     else
         let desc=a:check[1][1:]
         let dname=desc[0]
-        if self.type is 'check' || self.type is 'filter'
+        if a:type is 'check' || a:type is 'filter' || a:type is 'pipe'
             if !has_key(s:_r.FWC_intfuncs[dname], 'check')
                 call s:_f.throw('ucheck', dname)
             endif
     "▲2
     return self
 endfunction
-"▶1 compilecomplete :: &self(completecontext, idx)
-function s:compiler.compilecomplete(complete, idx)
-    if self.type is 'complete' && !(a:complete[1][0] is 'func' ||
-                \                   a:complete[1][0] is 'expr')
+"▶1 compilecomplete :: &self(completecontext, idx, type)
+function s:compiler.compilecomplete(complete, idx, type)
+    if a:type is 'complete' && !(a:complete[1][0] is 'func' ||
+                \                a:complete[1][0] is 'expr')
         let desc=a:complete[1][1:]
         let dname=desc[0]
         if has_key(s:_r.FWC_intfuncs[dname], 'complete')
     let savedonlystrings=self.o.onlystrings
     for proc in arg
         let i+=1
-        let compargs=[proc, a:idx.'.'.i]
+        let compargs=[proc, a:idx.'.'.i, type]
         let comptype=proc[0]
         if comptype is 'intfunc'
             let comptype=type
             let compargs[0]=[comptype, compargs[0]]
-            if self.type is 'complete' &&
-                        \get(s:_r.FWC_intfuncs[proc[1]], 'stopcomp', 0)
-                break
-            endif
         elseif comptype is 'defval'
             continue
         endif
+        if type is 'complete' && compargs[0][1][0] is 'intfunc' &&
+                    \get(s:_r.FWC_intfuncs[compargs[0][1][1]], 'breakscomp', 0)
+            break
+        endif
         call call(self['compile'.comptype], compargs, self)
         if self.typechanged
             let self.o.onlystrings=0
             let self.typechanged=0
         endif
+        if type is 'complete' && compargs[0][1][0] is 'intfunc' &&
+                    \get(s:_r.FWC_intfuncs[compargs[0][1][1]], 'stopscomp', 0)
+            break
+        endif
     endfor
     let self.o.onlystrings=savedonlystrings
     if len(self.msgs.own)>pmsgnum
     "▶2 Length checks, lagsstr and nextsub variables
     if !empty(self.subs)
         let largsstr=self.getlargsstr()
-        if self.type is 'check' || self.type is 'filter'
+        if self.type is 'check' || self.type is 'filter' || self.type is 'pipe'
             if !has_key(a:adescr, 'minimum')
                 call s:F.getlenrange(a:adescr)
             endif

plugin/frawor/fwc/intfuncs.vim

 let s:cfstr=string(s:cf)
 let s:cfreg='\v^'.s:cf.'$'
 "▶1 `func', `eval'
-let s:r.func={'args': ['func']}
+let s:r.func={'args': ['func'], 'breakscomp': 1}
 " Checks whether result of running {func}({argument}) isnot 0
 function s:r.func.check(desc, idx, type)
-    return self['compile'.a:type](a:desc, a:idx)
+    return self['compile'.a:type](a:desc, a:idx, a:type)
 endfunction
 " Replaces {argument} with the result of running {func}({argument})
 let s:r.func.pipe=s:r.func.check
                 \.let(condstr, 0)
             \.up()
     if len(a:desc[2])>1
-        call self.addif(condstr).compilearg(a:desc[2],a:idx.'(if)','pipe')
+        call self.addif(condstr).compilearg(a:desc[2],a:idx.'(if)', a:type)
                     \.up()
         if len(a:desc[3])>1
             call self.addif()
-                        \.compilearg(a:desc[3], a:idx.'(else)', 'pipe')
+                        \.compilearg(a:desc[3], a:idx.'(else)', a:type)
                         \.up()
         endif
     else
         call self.addif('!'.condstr)
-                    \.compilearg(a:desc[3], a:idx.'(else)', 'pipe')
+                    \.compilearg(a:desc[3], a:idx.'(else)', a:type)
                     \.up()
     endif
     return self.up()
 endfunction
+let s:r.if.complete=s:r.if.pipe
 "▶1 `run'
 " Replaces {argument} with the result of calling itself with {var} as argument 
 " list
 let s:r.run={'args': ['var']}
 function s:r.run.pipe(desc, idx, type)
     let curargstr=self.argstr()
-    call self.compilecheck(['check', ['intfunc', 'isfunc', 0]], a:idx)
+    call call(s:r.isfunc.check, [['isfunc', 0], a:idx, 'check'], self)
             \.try()
                 \.let(curargstr, 'call('.curargstr.', '.
                 \                                self.getvar(a:desc[1]).', {})')
     return self
 endfunction
 "▶1 `not'
-let s:r.not={'args': ['arg']}
+let s:r.not={'args': ['arg'], 'breakscomp': 1}
 "▶2 optimize
 " XXX low-level hacks here
 function s:r.not.optimize(idx)
 function s:r.not.check(desc, idx, type)
     return self.try()
                 \.pushms('throwignore')
-                \.compilearg(a:desc[1], a:idx.'(not)')
+                \.compilearg(a:desc[1], a:idx.'(not)', 'check')
                 \.popms()
                 \.throw("'NOTFAIL'")
             \.catch(s:cfreg).up()
     call self.let(sucstr, 1).addsavemsgs().pushms('throw')
     if !empty(a:desc[1])
         call self.try()
-                    \.compilearg(a:desc[1][0], a:idx.'(either).0',
-                    \            'check')
+                    \.compilearg(a:desc[1][0], a:idx.'(either).0', 'check')
                 \.up().catch(s:cfreg)
                     \.let(sucstr, 0).up()
     endif
         call self.addif('!'.sucstr)
                     \.let(sucstr, 1)
                     \.try()
-                        \.compilearg(arg, a:idx.'(either).'.i,'check')
+                        \.compilearg(arg, a:idx.'(either).'.i, 'check')
                     \.up().catch(s:cfreg)
                         \.let(sucstr, 0)
                     \.up()
     let self.joinlists+=1
     let i=0
     for arg in a:desc[1]
-        call self.compilearg(arg, a:idx.'(either).'.i, 'check')
+        call self.compilearg(arg, a:idx.'(either).'.i, 'complete')
         let i+=1
     endfor
     let self.joinlists-=1
 unlet s:r.first.complete
 function s:r.first.complete(desc, idx, type)
     if !empty(a:desc[1])
-        call self.compilearg(a:desc[1][0], a:idx.'(either).0', 'check')
+        call self.compilearg(a:desc[1][0], a:idx.'(either).0', 'complete')
     endif
     let i=1
     for arg in a:desc[1][1:]
         call self.if('empty(@-@)')
-                    \.compilearg(arg, a:idx.'(either).'.i, 'check')
+                    \.compilearg(arg, a:idx.'(either).'.i, 'complete')
                 \.up().endif()
         let i+=1
     endfor
 " Picks up first element from {var}::List that matches {argument}. If {matcher} 
 " is absent then {argument} may be of any type. In other case it should be 
 " string.
-function s:r.in.pipe(desc, idx, type)
+function s:r.in.pipe(desc, idx, type, ...)
     let curargstr=self.argstr()
     if len(a:desc)==2 || (a:desc[2][1][0] is 'intfunc' &&
                 \         a:desc[2][1][1] is 'exact' &&
                 \         a:desc[2][1][2] is 0)
-        return self.compilecheck(['check', ['intfunc']+a:desc], a:idx)
+        return call(s:r[get(a:000, 0, 'in')].check, [a:desc, a:idx, a:type],
+                    \self)
     else
         let matchstr=self.getlvarid('match')
         return self.addtypecond([type('')], a:idx)
                 \           'nindict', a:idx, curargstr)
 endfunction
 " Picks up first key from {var}::Dictionary that matches {argument}
-let s:r.key.pipe=s:r.in.pipe
+function s:r.key.pipe(...)
+    return call(s:r.in.pipe, a:000+['key'], self)
+endfunction
 function s:r.key.complete(desc, idx, type)
     return self.addmatches(self.getvar(a:desc[1]), type({}))
 endfunction
 "▶1 `take'
 " Replaces {argument} with value of the first key from {var}::Dictionary that 
 " matches {argument}
-let s:r.take={'args': ['var', 'matcher']}
+let s:r.take={'args': ['var', 'matcher'], 'stopscomp': 1}
 function s:r.take.pipe(desc, idx, type)
     let curargstr=self.argstr()
     let varstr=self.getvar(a:desc[1])
 let s:r.take.complete=s:r.key.complete
 "▶1 `substitute'
 " Runs substitute on {argument}
-let s:r.substitute={'args': ['reg', 'string', 'string']}
+let s:r.substitute={'args': ['reg', 'string', 'string'], 'breakscomp': 1}
 function s:r.substitute.pipe(desc, idx, type)
     let curargstr=self.argstr()
     return self.addtypecond([type('')], a:idx)
                     \          frefpref.' isnot "s:") '.
                     \        '|| (type('.curargstr.')=='.type('').
                     \            '&& '.curargstr.'=~#'.
-                    \             '''\v^%(%([sla]@!\w:)?%(\w|\.)+|'.
-                    \                    '%(s@!\w:)?\w+|'.
-                    \                    '%(<SNR>\w+))$'''.
+                    \             '''\v^%([sla]@!\w:%(\w|\.)+|'.
+                    \                    '%(<SNR>|s@!\w:)?\w+)$'''.
                     \            '&& exists("*".'.curargstr.')))',
                     \       'nsfunc', a:idx, 'string('.curargstr.')')
     endif
                 \.catch().addthrow('nreg', 1, a:idx, curargstr, 'v:exception')
 endfunction
 "▶1 `bool'
-let s:r.bool={'args': []}
+let s:r.bool={'args': [], 'breakscomp': 1}
 " Checks whether {argument} is either 0 or 1
 function s:r.bool.check(desc, idx, type)
     let curargstr=self.argstr()
 endfunction
 "▶1 `is'
 " Checks whether {argument} is {var}
-let s:r.is={'args': ['var']}
+let s:r.is={'args': ['var'], 'breakscomp': 1}
 function s:r.is.check(desc, idx, type)
     let var=self.getvar(a:desc[1])
     let curargstr=self.argstr()
         if index(a:ld, a:str)!=-1
             return ((a:acceptfirst is 2)?([a:str]):(a:str))
         elseif a:ignorecase
-            let list=a:ld
+            let list=filter(copy(a:ld), 'type(v:val)=='.type(''))
         else
             return ((a:acceptfirst is 2)?([]):(0))
         endif

test/fwccomplete.ok

 ::: Section <Built-in completion functions/take>
 ::: Section <Built-in completion functions/either>
 ::: Section <Built-in completion functions/first>
+::: Section <Built-in completion functions/if>
 ::: Section <Different sections/{required}>
 ::: Section <Different sections/{actions}>
 <<< messages

test/fwccompletetests.dat

 !complete
 :let s:list=['abc', 'ab-c', 'def-ghi', 'adb']
 :let s:list2=['abc', 'def-b', 'geh']
+:let s:list3=['foo', 'bar', 'baz']
 :let s:dict={'abc': 1, 'adb': 1, 'aef': 1}
 #▶1 Built-in completion functions
 #▶2 in
 
   @ae
   aef
+#▶2 if
+`if match/^a/
++   in dict
++   in list3
+  @+
+  =s:list3
+
+  @a
+  =sort(keys(s:dict))
 #▶1 Different sections
 #▶2 {required}
 `in list