"============================================================================= " 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 = '' . 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(''), '\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\]" 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('(%s)', name) if mode ==# 'n' let command = 'nnoremap' else let command = 'nmap' endif execute command '' map printf('%s:nunmap %s', 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("\(%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 = '' . 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 " }}}