neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

define.vim (8373B)


      1 function! remote#define#CommandOnHost(host, method, sync, name, opts)
      2  let prefix = ''
      3 
      4  if has_key(a:opts, 'range')
      5    if a:opts.range == '' || a:opts.range == '%'
      6      " -range or -range=%, pass the line range in a list
      7      let prefix = '<line1>,<line2>'
      8    elseif matchstr(a:opts.range, '\d') != ''
      9      " -range=N, pass the count
     10      let prefix = '<count>'
     11    endif
     12  elseif has_key(a:opts, 'count')
     13    let prefix = '<count>'
     14  endif
     15 
     16  let forward_args = [prefix.a:name]
     17 
     18  if has_key(a:opts, 'bang')
     19    call add(forward_args, '<bang>')
     20  endif
     21 
     22  if has_key(a:opts, 'register')
     23    call add(forward_args, ' <register>')
     24  endif
     25 
     26  if has_key(a:opts, 'nargs')
     27    call add(forward_args, ' " . <q-args> . "')
     28  endif
     29 
     30  exe s:GetCommandPrefix(a:name, a:opts)
     31        \ .' call remote#define#CommandBootstrap("'.a:host.'"'
     32        \ .                                ', "'.a:method.'"'
     33        \ .                                ', '.string(a:sync)
     34        \ .                                ', "'.a:name.'"'
     35        \ .                                ', '.string(a:opts).''
     36        \ .                                ', "'.join(forward_args, '').'"'
     37        \ .                                ')'
     38 endfunction
     39 
     40 
     41 function! remote#define#CommandBootstrap(host, method, sync, name, opts, forward)
     42  let channel = remote#host#Require(a:host)
     43 
     44  if channel
     45    call remote#define#CommandOnChannel(channel, a:method, a:sync, a:name, a:opts)
     46    exe a:forward
     47  else
     48    exe 'delcommand '.a:name
     49    echoerr 'Host "'a:host.'" is not available, deleting command "'.a:name.'"'
     50  endif
     51 endfunction
     52 
     53 
     54 function! remote#define#CommandOnChannel(channel, method, sync, name, opts)
     55  let rpcargs = [a:channel, '"'.a:method.'"']
     56  if has_key(a:opts, 'nargs')
     57    " -nargs, pass arguments in a list
     58    call add(rpcargs, '[<f-args>]')
     59  endif
     60 
     61  if has_key(a:opts, 'range')
     62    if a:opts.range == '' || a:opts.range == '%'
     63      " -range or -range=%, pass the line range in a list
     64      call add(rpcargs, '[<line1>, <line2>]')
     65    elseif matchstr(a:opts.range, '\d') != ''
     66      " -range=N, pass the count
     67      call add(rpcargs, '<count>')
     68    endif
     69  elseif has_key(a:opts, 'count')
     70    " count
     71    call add(rpcargs, '<count>')
     72  endif
     73 
     74  if has_key(a:opts, 'bang')
     75    " bang
     76    call add(rpcargs, '<q-bang> == "!"')
     77  endif
     78 
     79  if has_key(a:opts, 'register')
     80    " register
     81    call add(rpcargs, '<q-reg>')
     82  endif
     83 
     84  call s:AddEval(rpcargs, a:opts)
     85  exe s:GetCommandPrefix(a:name, a:opts)
     86        \ . ' call '.s:GetRpcFunction(a:sync).'('.join(rpcargs, ', ').')'
     87 endfunction
     88 
     89 
     90 function! remote#define#AutocmdOnHost(host, method, sync, name, opts)
     91  let group = s:GetNextAutocmdGroup()
     92  let forward = '"doau '.group.' '.a:name.' ".'
     93        \ . 'fnameescape(expand("<amatch>"))'
     94  let a:opts.group = group
     95  let bootstrap_def = s:GetAutocmdPrefix(a:name, a:opts)
     96        \ .' call remote#define#AutocmdBootstrap("'.a:host.'"'
     97        \ .                                ', "'.a:method.'"'
     98        \ .                                ', '.string(a:sync)
     99        \ .                                ', "'.a:name.'"'
    100        \ .                                ', '.string(a:opts).''
    101        \ .                                ', "'.escape(forward, '"').'"'
    102        \ .                                ')'
    103  exe bootstrap_def
    104 endfunction
    105 
    106 
    107 function! remote#define#AutocmdBootstrap(host, method, sync, name, opts, forward)
    108  let channel = remote#host#Require(a:host)
    109 
    110  exe 'autocmd! '.a:opts.group
    111  if channel
    112    call remote#define#AutocmdOnChannel(channel, a:method, a:sync, a:name,
    113          \ a:opts)
    114    exe eval(a:forward)
    115  else
    116    exe 'augroup! '.a:opts.group
    117    echoerr 'Host "'a:host.'" for "'.a:name.'" autocmd is not available'
    118  endif
    119 endfunction
    120 
    121 
    122 function! remote#define#AutocmdOnChannel(channel, method, sync, name, opts)
    123  let rpcargs = [a:channel, '"'.a:method.'"']
    124  call s:AddEval(rpcargs, a:opts)
    125 
    126  let autocmd_def = s:GetAutocmdPrefix(a:name, a:opts)
    127        \ . ' call '.s:GetRpcFunction(a:sync).'('.join(rpcargs, ', ').')'
    128  exe autocmd_def
    129 endfunction
    130 
    131 
    132 function! remote#define#FunctionOnHost(host, method, sync, name, opts)
    133  let group = s:GetNextAutocmdGroup()
    134  exe 'autocmd! '.group.' FuncUndefined '.a:name
    135        \ .' call remote#define#FunctionBootstrap("'.a:host.'"'
    136        \ .                                 ', "'.a:method.'"'
    137        \ .                                 ', '.string(a:sync)
    138        \ .                                 ', "'.a:name.'"'
    139        \ .                                 ', '.string(a:opts)
    140        \ .                                 ', "'.group.'"'
    141        \ .                                 ')'
    142 endfunction
    143 
    144 
    145 function! remote#define#FunctionBootstrap(host, method, sync, name, opts, group)
    146  let channel = remote#host#Require(a:host)
    147 
    148  exe 'autocmd! '.a:group
    149  exe 'augroup! '.a:group
    150  if channel
    151    call remote#define#FunctionOnChannel(channel, a:method, a:sync, a:name,
    152          \ a:opts)
    153  else
    154    echoerr 'Host "'a:host.'" for "'.a:name.'" function is not available'
    155  endif
    156 endfunction
    157 
    158 
    159 function! remote#define#FunctionOnChannel(channel, method, sync, name, opts)
    160  let rpcargs = [a:channel, '"'.a:method.'"', 'a:000']
    161  if has_key(a:opts, 'range')
    162    call add(rpcargs, '[a:firstline, a:lastline]')
    163  endif
    164  call s:AddEval(rpcargs, a:opts)
    165 
    166  let function_def = s:GetFunctionPrefix(a:name, a:opts)
    167        \ . 'return '.s:GetRpcFunction(a:sync).'('.join(rpcargs, ', ').')'
    168        \ . "\nendfunction"
    169  exe function_def
    170 endfunction
    171 
    172 let s:busy = {}
    173 let s:pending_notifications = {}
    174 
    175 function! s:GetRpcFunction(sync)
    176  if a:sync ==# 'urgent'
    177    return 'rpcnotify'
    178  elseif a:sync
    179    return 'remote#define#request'
    180  endif
    181  return 'remote#define#notify'
    182 endfunction
    183 
    184 function! remote#define#notify(chan, ...)
    185  if get(s:busy, a:chan, 0) > 0
    186    let pending = get(s:pending_notifications, a:chan, [])
    187    call add(pending, deepcopy(a:000))
    188    let s:pending_notifications[a:chan] = pending
    189  else
    190    call call('rpcnotify', [a:chan] + a:000)
    191  endif
    192 endfunction
    193 
    194 function! remote#define#request(chan, ...)
    195  let s:busy[a:chan] = get(s:busy, a:chan, 0)+1
    196  let val = call('rpcrequest', [a:chan]+a:000)
    197  let s:busy[a:chan] -= 1
    198  if s:busy[a:chan] == 0
    199    for msg in get(s:pending_notifications, a:chan, [])
    200      call call('rpcnotify', [a:chan] + msg)
    201    endfor
    202    let s:pending_notifications[a:chan] = []
    203  endif
    204  return val
    205 endfunction
    206 
    207 function! s:GetCommandPrefix(name, opts)
    208  return 'command!'.s:StringifyOpts(a:opts, ['nargs', 'complete', 'range',
    209        \ 'count', 'bang', 'bar', 'register']).' '.a:name
    210 endfunction
    211 
    212 
    213 " Each msgpack-rpc autocommand has it's own unique group, which is derived
    214 " from an autoincrementing gid(group id). This is required for replacing the
    215 " autocmd implementation with the lazy-load mechanism
    216 let s:next_gid = 1
    217 function! s:GetNextAutocmdGroup()
    218  let gid = s:next_gid
    219  let s:next_gid += 1
    220 
    221  let group_name = 'RPC_DEFINE_AUTOCMD_GROUP_'.gid
    222  " Ensure the group is defined
    223  exe 'augroup '.group_name.' | augroup END'
    224  return group_name
    225 endfunction
    226 
    227 
    228 function! s:GetAutocmdPrefix(name, opts)
    229  if has_key(a:opts, 'group')
    230    let group = a:opts.group
    231  else
    232    let group = s:GetNextAutocmdGroup()
    233  endif
    234  let rv = ['autocmd!', group, a:name]
    235 
    236  if has_key(a:opts, 'pattern')
    237    call add(rv, a:opts.pattern)
    238  else
    239    call add(rv, '*')
    240  endif
    241 
    242  if has_key(a:opts, 'nested') && a:opts.nested
    243    call add(rv, '++nested')
    244  endif
    245 
    246  if has_key(a:opts, 'once') && a:opts.once
    247    call add(rv, '++once')
    248  endif
    249 
    250  return join(rv, ' ')
    251 endfunction
    252 
    253 
    254 function! s:GetFunctionPrefix(name, opts)
    255  let res = "function! ".a:name."(...)"
    256  if has_key(a:opts, 'range')
    257    let res = res." range"
    258  endif
    259  return res."\n"
    260 endfunction
    261 
    262 
    263 function! s:StringifyOpts(opts, keys)
    264  let rv = []
    265  for key in a:keys
    266    if has_key(a:opts, key)
    267      call add(rv, ' -'.key)
    268      let val = a:opts[key]
    269      if type(val) != type('') || val != ''
    270        call add(rv, '='.val)
    271      endif
    272    endif
    273  endfor
    274  return join(rv, '')
    275 endfunction
    276 
    277 
    278 function! s:AddEval(rpcargs, opts)
    279  if has_key(a:opts, 'eval')
    280    if type(a:opts.eval) != type('') || a:opts.eval == ''
    281      throw "Eval option must be a non-empty string"
    282    endif
    283    " evaluate an expression and pass as argument
    284    call add(a:rpcargs, 'eval("'.escape(a:opts.eval, '"').'")')
    285  endif
    286 endfunction