2018-04-05 13:06:54 +02:00

332 lines
9.1 KiB
VimL

" =============== ============================================================
" Name : rule.vim
" Synopsis : vim script library: rule
" Author : Zhao Cai <caizhaoff@gmail.com>
" HomePage : https://github.com/zhaocai/zl.vim
" Date Created : Sat 03 Sep 2011 03:54:00 PM EDT
" Last Modified : Sat 29 Sep 2012 01:03:24 AM EDT
" Tag : [ vim, rule ]
" Copyright : © 2012 by Zhao Cai,
" Released under current GPL license.
" =============== ============================================================
" ============================================================================
" Rule: [[[1
" ============================================================================
" [TODO]( list for at type ) @zhaocai @start(2012-09-27 08:05)
let s:rule_types = [
\ 'filetype', 'buftype', 'mode' , 'cword',
\ 'bufname' , 'at' , 'syntax', 'expr' ,
\ ]
let s:nrule = {
\ 'eval_order' : copy(s:rule_types),
\ 'eval_negate' : [] ,
\ 'logic' : 'or' ,
\ 'rule' : {} ,
\ }
function! GoldenView#zl#rule#norm(urule, ...)
"--------- ------------------------------------------------
" Desc : normalize rules
"
" Rule :
" - "urule" : "Unnormalized RULE", rules written by users.
" - "nrule" : "Nnormalized RULE", rules completed with
" optional items and internal items.
"
" Args :
" - urule: un-normalized rule
" - opts :
" - eval_order : order in s:rule_types,
" - eval_negate : reverse eval result
" - logic :
" - {or} : 'v:filetype || v:bufname || ...'
" - {and} : 'v:filetype && v:bufname && ...'
" - {string} : similar to v:val for filter()
"
" Return : normalized rules
" Raise :
"
" Example : >
"
" Refer :
"--------- ------------------------------------------------
let nrule = deepcopy(s:nrule)
if a:0 >= 1 && GoldenView#zl#var#is_dict(a:1)
call extend(nrule, a:1)
endif
let type_expr = {
\ 'buftype' : '&buftype' ,
\ 'filetype' : '&ft' ,
\ 'bufname' : "bufname('%')" ,
\ 'cword' : "expand('<cword>')" ,
\ }
let type_pat = {}
for type in ['filetype', 'buftype', 'syntax']
if has_key(a:urule, type)
let type_pat[type] = '^\%(' . join(a:urule[type], '\|') . '\)$'
endif
endfor
for type in ['bufname', 'cword']
if has_key(a:urule, type)
let type_pat[type] = '^\%('
\ . join(map(a:urule[type], 'GoldenView#zl#regex#escape(v:val)'), '\|')
\ . '\)$'
endif
endfor
" normalize each type of rules
for type in ['mode']
if has_key(a:urule, type)
let nrule.rule[type] = a:urule[type]
endif
endfor
for type in ['filetype', 'buftype', 'bufname', 'cword']
if has_key(a:urule, type)
let nrule.rule[type] =
\ {
\ 'eval_expr' : 1 ,
\ 'expr' : type_expr[type] ,
\ 'pat' : type_pat[type] ,
\ }
endif
endfor
for type in ['syntax']
if has_key(a:urule, type)
let nrule.rule[type] = type_pat[type]
endif
endfor
for type in ['mode', 'at']
if has_key(a:urule, type)
let nrule.rule[type] = a:urule[type]
endif
endfor
for type in ['expr']
if has_key(a:urule, type)
try | let nrule.rule[type] =
\ join(
\ map(
\ map(
\ copy(a:urule[type])
\ ,"join(v:val,' || ')"
\ )
\ , "'('.v:val.')'"
\ )
\ ,' && '
\ )
catch /^Vim\%((\a\+)\)\=:E714/ " E714: List required
throw 'zl(rule): expr rule should be written as list of lists.'
endtry
endif
endfor
call filter(
\ nrule['eval_order']
\ , 'has_key(nrule.rule, v:val) && !empty(nrule.rule[v:val])'
\ )
return nrule
endfunction
function! GoldenView#zl#rule#is_true(nrule, ...)
try
return call('GoldenView#zl#rule#logic_'.a:nrule['logic'], [a:nrule] + a:000)
catch /^Vim\%((\a\+)\)\=:E129/
throw 'zl(rule): undefined logic funcref'
endtry
endfunction
function! GoldenView#zl#rule#is_false(nrule, ...)
return !call('GoldenView#zl#rule#is_true', [a:nrule] + a:000)
endfunction
" rule logic
function! s:_return(nrule, type, ret)
return
\ index(a:nrule.eval_negate, a:type) >= 0
\ ? !a:ret
\ : a:ret
endfunction
function! GoldenView#zl#rule#logic_or(nrule, ...)
let opts = {}
if a:0 >= 1 && GoldenView#zl#var#is_dict(a:1)
call extend(opts, a:1)
endif
for type in a:nrule['eval_order']
if s:_return(a:nrule, type, s:eval_{type}(a:nrule['rule'], opts))
return 1
endif
endfor
return 0
endfunction
function! GoldenView#zl#rule#logic_and(nrule, ...)
let opts = {}
if a:0 >= 1 && GoldenView#zl#var#is_dict(a:1)
call extend(opts, a:1)
endif
for type in a:nrule['eval_order']
if !s:_return(a:nrule, type, s:eval_{type}(a:nrule['rule'], opts))
return 0
endif
endfor
return 1
endfunction
function! GoldenView#zl#rule#logic_expr(nrule, ...)
let opts = {}
if a:0 >= 1 && GoldenView#zl#var#is_dict(a:1)
call extend(opts, a:1)
endif
let str = a:nrule['expr']
for type in a:nrule['eval_order']
let str = substitute(str
\ , 'v:'.type
\ , string(
\ s:_return(a:nrule, type,
\ s:eval_{type}(a:nrule['rule'], opts)
\ )
\ )
\ , 'ge'
\ )
endfor
try
return eval(str)
catch /^Vim\%((\a\+)\)\=:E/
throw printf('zl(rule): eval(%s) raises %s', str, v:exception)
endtry
endfunction
" nrule eval
function! s:eval_filetype(nrule, ...)
return call('s:_eval_match', ['filetype', a:nrule] + a:000)
endfunction
function! s:eval_cword(nrule, ...)
return call('s:_eval_match', ['cword', a:nrule] + a:000)
endfunction
function! s:eval_buftype(nrule, ...)
return call('s:_eval_match', ['buftype', a:nrule] + a:000)
endfunction
function! s:eval_bufname(nrule, ...)
return call('s:_eval_match', ['bufname', a:nrule] + a:000)
endfunction
function! s:eval_at(nrule, ...)
return search(get(a:nrule, 'at', '\%#'), 'bcnW')
endfunction
function! s:eval_mode(nrule, ...)
let mode_pat = get(a:nrule, 'mode', [])
let mode_expr =
\ a:0 >= 1 && GoldenView#zl#var#is_dict(a:1)
\ ? get(a:1, 'mode', mode())
\ : mode()
return
\ !empty(
\ filter(
\ mode_pat
\ , 'stridx(mode_expr, v:val) == -1'
\ )
\ )
endfunction
function! s:eval_syntax(nrule, ...)
let pat = get(a:nrule, 'syntax', '')
let opts = {}
if a:0 >= 1 && GoldenView#zl#var#is_dict(a:1)
call extend(opts, a:1)
endif
let syn_names = GoldenView#zl#syntax#synstack_names(opts)
return !empty(filter(syn_names, 'match(v:val, pat) != -1'))
endfunction
function! s:eval_expr(nrule, ...)
try
return eval(get(a:nrule, 'expr', 1))
catch /^Vim\%((\a\+)\)\=:E/
return 0
endtry
endfunction
function! s:_eval_match(type, nrule, ...)
"--------- ------------------------------------------------
" Desc : internal match evluation
" Rule :
" { 'type' :
" {
" 'eval_expr' : (1|0) ,
" 'expr' : {expr} ,
" 'pat' : {pat} ,
" }
" }
"
" Args : [{type}, {nrule}[, {opts}]]
" Return :
" - 0 : false
" - 1 : true
" Raise : zl(rule)
"
" Refer : vimhelp:match()
"--------- ------------------------------------------------
let rule = copy(get(a:nrule, a:type, {}))
if empty(rule)
throw 'zl(rule): ' . v:exception
endif
" opt for {expr} from runtime opts
if a:0 >= 1 && GoldenView#zl#var#is_dict(a:1) && has_key(a:1, a:type)
let rt_rule = a:1[a:type]
if GoldenView#zl#var#is_dict(rt_rule)
call extend(rule, rt_rule)
elseif GoldenView#zl#var#is_string(rt_rule)
let rule['expr'] = rt_rule
let rule['eval_expr'] = 0
endif
endif
if rule['eval_expr']
let rule['expr'] = eval(rule['expr'])
endif
try
return call('match', [rule['expr'], rule['pat']]) != -1
catch /^Vim\%((\a\+)\)\=:E/
throw 'zl(rule): ' . v:exception
endtry
endfunction
" ============================================================================
" Modeline: [[[1
" ============================================================================
" vim: set ft=vim ts=4 sw=4 tw=78 fdm=marker fmr=[[[,]]] fdl=1 :