1
0
mirror of https://github.com/SpaceVim/SpaceVim.git synced 2025-02-03 13:50:04 +08:00
SpaceVim/bundle/incsearch.vim/autoload/incsearch/util.vim

287 lines
7.9 KiB
VimL
Raw Normal View History

"=============================================================================
" FILE: autoload/incsearch/util.vim
" AUTHOR: haya14busa
" License: MIT license {{{
" Permission is hereby granted, free of charge, to any person obtaining
" a copy of this software and associated documentation files (the
" "Software"), to deal in the Software without restriction, including
" without limitation the rights to use, copy, modify, merge, publish,
" distribute, sublicense, and/or sell copies of the Software, and to
" permit persons to whom the Software is furnished to do so, subject to
" the following conditions:
"
" The above copyright notice and this permission notice shall be included
" in all copies or substantial portions of the Software.
"
" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
" OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
" IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
" CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
" TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
" SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
" }}}
"=============================================================================
scriptencoding utf-8
" Saving 'cpoptions' {{{
let s:save_cpo = &cpo
set cpo&vim
" }}}
let s:TRUE = !0
let s:FALSE = 0
" Public Utilities:
function! incsearch#util#deepextend(...) abort
return call(function('s:deepextend'), a:000)
endfunction
" Utilities:
function! incsearch#util#import() abort
let prefix = '<SNR>' . s:SID() . '_'
let module = {}
for func in s:functions
let module[func] = function(prefix . func)
endfor
return copy(module)
endfunction
function! s:SID() abort
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze_SID$')
endfunction
let s:functions = [
\ 'is_visual'
\ , 'get_max_col'
\ , 'is_pos_less_equal'
\ , 'is_pos_more_equal'
\ , 'sort_num'
\ , 'sort_pos'
\ , 'count_pattern'
\ , 'silent_feedkeys'
\ , 'deepextend'
\ , 'dictfunction'
\ , 'regexp_join'
\ ]
function! s:is_visual(mode) abort
return a:mode =~# "[vV\<C-v>]"
endfunction
" Return max column number of given line expression
" expr: similar to line(), col()
function! s:get_max_col(expr) abort
return strlen(getline(a:expr)) + 1
endfunction
" return (x <= y)
function! s:is_pos_less_equal(x, y) abort
return (a:x[0] == a:y[0]) ? a:x[1] <= a:y[1] : a:x[0] < a:y[0]
endfunction
" return (x > y)
function! s:is_pos_more_equal(x, y) abort
return ! s:is_pos_less_equal(a:x, a:y)
endfunction
" x < y -> -1
" x = y -> 0
" x > y -> 1
function! s:compare_pos(x, y) abort
return max([-1, min([1,(a:x[0] == a:y[0]) ? a:x[1] - a:y[1] : a:x[0] - a:y[0]])])
endfunction
function! s:sort_num(xs) abort
" 7.4.341
" http://ftp.vim.org/vim/patches/7.4/7.4.341
if v:version > 704 || v:version == 704 && has('patch341')
return sort(a:xs, 'n')
else
return sort(a:xs, 's:_sort_num_func')
endif
endfunction
function! s:_sort_num_func(x, y) abort
return a:x - a:y
endfunction
function! s:sort_pos(pos_list) abort
" pos_list: [ [x1, y1], [x2, y2] ]
return sort(a:pos_list, 's:compare_pos')
endfunction
" Return the number of matched patterns in the current buffer or the specified
" region with `from` and `to` positions
" parameter: pattern, from, to
function! s:count_pattern(pattern, ...) abort
let w = winsaveview()
let [from, to] = [
\ get(a:, 1, [1, 1]),
\ get(a:, 2, [line('$'), s:get_max_col('$')])
\ ]
let ignore_at_cursor_pos = get(a:, 3, 0)
" direction flag
let d_flag = s:compare_pos(from, to) > 0 ? 'b' : ''
call cursor(from)
let cnt = 0
let base_flag = d_flag . 'W'
try
" first: accept a match at the cursor position
let pos = searchpos(a:pattern, (ignore_at_cursor_pos ? '' : 'c' ) . base_flag)
while (pos != [0, 0] && s:compare_pos(pos, to) isnot# (d_flag is# 'b' ? -1 : 1))
let cnt += 1
let pos = searchpos(a:pattern, base_flag)
endwhile
finally
call winrestview(w)
endtry
return cnt
endfunction
" NOTE: support vmap?
" It doesn't handle feedkeys() on incsert or command-line mode
function! s:silent_feedkeys(expr, name, ...) abort
" Ref:
" https://github.com/osyo-manga/vim-over/blob/d51b028c29661d4a5f5b79438ad6d69266753711/autoload/over.vim#L6
let mode = get(a:, 1, 'm')
let name = 'incsearch-' . a:name
let map = printf('<Plug>(%s)', name)
if mode ==# 'n'
let command = 'nnoremap'
else
let command = 'nmap'
endif
execute command '<silent>' map printf('%s:nunmap %s<CR>', a:expr, map)
if mode(1) !=# 'ce'
" FIXME: mode(1) !=# 'ce' exists only for the test
" :h feedkeys() doesn't work while runnning a test script
" https://github.com/kana/vim-vspec/issues/27
call feedkeys(printf("\<Plug>(%s)", name))
endif
endfunction
" deepextend (nest: 1)
function! s:deepextend(expr1, expr2) abort
let expr2 = copy(a:expr2)
for [k, V] in items(a:expr1)
if (type(V) is type({}) || type(V) is type([])) && has_key(expr2, k)
let a:expr1[k] = extend(a:expr1[k], expr2[k])
unlet expr2[k]
endif
unlet V
endfor
return extend(a:expr1, expr2)
endfunction
let s:funcmanage = {}
function! s:funcmanage() abort
return s:funcmanage
endfunction
function! s:dictfunction(dictfunc, dict) abort
if has('patch-7.4.1842')
let funcname = '_' . get(a:dictfunc, 'name')
else
let funcname = '_' . matchstr(string(a:dictfunc), '\d\+')
endif
let s:funcmanage[funcname] = {
\ 'func': a:dictfunc,
\ 'dict': a:dict
\ }
let prefix = '<SNR>' . s:SID() . '_'
let fm = printf("%sfuncmanage()['%s']", prefix, funcname)
execute join([
\ printf('function! s:%s(...) abort', funcname),
\ printf(" return call(%s['func'], a:000, %s['dict'])", fm, fm),
\ 'endfunction'
\ ], "\n")
return function(printf('%s%s', prefix, funcname))
endfunction
"--- regexp
let s:escaped_backslash = '\m\%(^\|[^\\]\)\%(\\\\\)*\zs'
function! s:regexp_join(ps) abort
let rs = map(filter(copy(a:ps), 's:_is_valid_regexp(v:val)'), 's:escape_unbalanced_left_r(v:val)')
return printf('\m\%%(%s\m\)', join(rs, '\m\|'))
endfunction
function! s:_is_valid_regexp(pattern) abort
try
if '' =~# a:pattern
endif
return s:TRUE
catch
return s:FALSE
endtry
endfunction
" \m,\v: [ -> \[
" \M,\V: \[ -> [
function! s:escape_unbalanced_left_r(pattern) abort
let rs = []
let cs = split(a:pattern, '\zs')
" escape backslash (\, \\\, \\\\\, ...)
let escape_bs = s:FALSE
let flag = &magic ? 'm' : 'M'
let i = 0
while i < len(cs)
let c = cs[i]
" characters to add to rs
let addcs = [c]
if escape_bs && s:_is_flag(c)
let flag = c
elseif c is# '[' && s:_may_replace_left_r_cond(escape_bs, flag)
let idx = s:_find_right_r(cs, i)
if idx is# -1
if s:_is_flag(flag, 'MV')
" Remove `\` before unbalanced `[`
let rs = rs[:-2]
else
" Escape unbalanced `[`
let addcs = ['\' . c]
endif
else
let addcs = cs[(i):(i+idx)]
let i += idx
endif
endif
let escape_bs = (escape_bs || c isnot# '\') ? s:FALSE : s:TRUE
let rs += addcs
let i += 1
endwhile
return join(rs, '')
endfunction
" @ return boolean
function! s:_is_flag(flag, ...) abort
let chars = get(a:, 1, 'mMvV')
return a:flag =~# printf('\m[%s]', chars)
endfunction
" @ return boolean
function! s:_may_replace_left_r_cond(escape_bs, flag) abort
return (a:escape_bs && s:_is_flag(a:flag, 'MV')) || (!a:escape_bs && s:_is_flag(a:flag, 'mv'))
endfunction
" @return index
function! s:_find_right_r(cs, i) abort
return match(join(a:cs[(a:i+1):], ''), s:escaped_backslash . ']')
endfunction
"--- end of regexp
" Restore 'cpoptions' {{{
let &cpo = s:save_cpo
unlet s:save_cpo
" }}}
" __END__ {{{
" vim: expandtab softtabstop=2 shiftwidth=2
" vim: foldmethod=marker
" }}}