diff --git a/autoload/incsearch.vim b/autoload/incsearch.vim index 51493ae..e825311 100644 --- a/autoload/incsearch.vim +++ b/autoload/incsearch.vim @@ -62,465 +62,24 @@ let g:incsearch#magic = get(g: , 'incsearch#magic' , '') " Debug: let g:incsearch#debug = get(g:, 'incsearch#debug', s:FALSE) -let s:V = vital#of(g:incsearch#debug ? 'vital' : 'incsearch') - -" Utility: -let s:U = incsearch#util#import() - -" Highlight: -let s:hi = g:incsearch#highlight#_hi - -" CommandLine Interface: {{{ -let s:cli = s:V.import('Over.Commandline').make_default("/") -let s:modules = s:V.import('Over.Commandline.Modules') - -" Add modules -call s:cli.connect('BufferComplete') -call s:cli.connect('Cancel') -call s:cli.connect('CursorMove') -call s:cli.connect('Digraphs') -call s:cli.connect('Delete') -call s:cli.connect('DrawCommandline') -call s:cli.connect('ExceptionExit') -call s:cli.connect('LiteralInsert') -" call s:cli.connect('Exit') -" NOTE: -" in {rhs} wil be remapped even after exiting vital-over comman line -" interface, so do not use (exit) -" See also s:cli.keymapping() -let s:incsearch_exit = { -\ "name" : "IncsearchExit", -\ "exit_code" : 0 -\} -function! s:incsearch_exit.on_char_pre(cmdline) abort - if a:cmdline.is_input("\") - \ || a:cmdline.is_input("\") - call a:cmdline.setchar("") - call a:cmdline.exit(self.exit_code) +function! incsearch#vital() abort + if exists('s:V') + return s:V endif + let s:V = vital#of(g:incsearch#debug ? 'vital' : 'incsearch') + return s:V endfunction -call s:cli.connect(s:incsearch_exit) -" Lazy connect -let s:InsertRegister = s:modules.get('InsertRegister').make() +let s:V = incsearch#vital() -call s:cli.connect('Paste') -" XXX: better handling. -if expand("%:p") !=# expand(":p") - let s:Doautocmd = s:modules.get('Doautocmd') - call s:cli.connect(s:Doautocmd.make('IncSearch')) -endif -call s:cli.connect(s:modules.get('ExceptionMessage').make('incsearch.vim: ', 'echom')) -call s:cli.connect(s:modules.get('History').make('/')) -call s:cli.connect(s:modules.get('NoInsert').make_special_chars()) - -" Dynamic Module Loading Management -let s:KeyMapping = s:modules.get('KeyMapping') -let s:emacs_like = s:KeyMapping.make_emacs() -let s:vim_cmap = s:KeyMapping.make_vim_cmdline_mapping() -let s:smartbackword = s:modules.get('IgnoreRegexpBackwardWord').make() -function! s:emacs_like._condition() abort - return g:incsearch#emacs_like_keymap -endfunction -function! s:vim_cmap._condition() abort - return g:incsearch#vim_cmdline_keymap -endfunction -function! s:smartbackword._condition() abort - return g:incsearch#smart_backward_word -endfunction -let s:module_management = { -\ 'name' : 'IncsearchModuleManagement', -\ 'modules' : [ -\ s:emacs_like, s:vim_cmap, s:smartbackword -\ ] -\} -let s:backward_word = s:cli.backward_word -function! s:module_management.on_enter(cmdline) abort - for module in self.modules - if has_key(module, '_condition') && ! module._condition() - call a:cmdline.disconnect(module.name) - if module.name ==# 'IgnoreRegexpBackwardWord' - function! a:cmdline.backward_word(...) abort - return call(s:backward_word, a:000, self) - endfunction - endif - elseif empty(a:cmdline.get_module(module.name)) - call a:cmdline.connect(module) - if has_key(module, 'on_enter') - call module.on_enter(a:cmdline) - endif - endif - endfor -endfunction -function! s:module_management.priority(event) abort - " NOTE: to overwrite backward_word() with default function - return a:event ==# 'on_enter' ? 5 : 0 -endfunction -call s:cli.connect(s:module_management) -unlet s:KeyMapping s:emacs_like s:vim_cmap s:smartbackword s:incsearch_exit - -let s:pattern_saver = { -\ 'name' : 'PatternSaver', -\ 'pattern' : '', -\ 'hlsearch' : &hlsearch -\} -function! s:pattern_saver.on_enter(cmdline) abort - if ! g:incsearch#no_inc_hlsearch - let self.pattern = @/ - let self.hlsearch = &hlsearch - if exists('v:hlsearch') - let self.vhlsearch = v:hlsearch - endif - set hlsearch | nohlsearch - endif -endfunction -function! s:pattern_saver.on_leave(cmdline) abort - if ! g:incsearch#no_inc_hlsearch - let is_cancel = a:cmdline.exit_code() - if is_cancel - let @/ = self.pattern - endif - let &hlsearch = self.hlsearch - if exists('v:hlsearch') - let v:hlsearch = self.vhlsearch - endif - endif -endfunction -call s:cli.connect(s:pattern_saver) - -let s:default_keymappings = { -\ "\" : { -\ "key" : "(incsearch-next)", -\ "noremap" : 1, -\ }, -\ "\" : { -\ "key" : "(incsearch-prev)", -\ "noremap" : 1, -\ }, -\ "\" : { -\ "key" : "(incsearch-scroll-f)", -\ "noremap" : 1, -\ }, -\ "\" : { -\ "key" : "(incsearch-scroll-b)", -\ "noremap" : 1, -\ }, -\ "\" : { -\ "key" : "(buffer-complete)", -\ "noremap" : 1, -\ }, -\ "\" : { -\ "key": "\", -\ "noremap": 1 -\ }, -\ } - -" https://github.com/haya14busa/incsearch.vim/issues/35 -if has('mac') - call extend(s:default_keymappings, { - \ '"+gP' : { - \ 'key': "\+", - \ 'noremap': 1 - \ }, - \ }) -endif - -" FIXME: arguments? -function! s:cli.keymapping(...) abort - return extend(copy(s:default_keymappings), g:incsearch_cli_key_mappings) -endfunction - -let s:inc = { -\ "name" : "incsearch", -\} - -" NOTE: for InsertRegister handling -function! s:inc.priority(event) abort - return a:event is# 'on_char' ? 10 : 0 -endfunction - -function! s:inc.on_enter(cmdline) abort - nohlsearch " disable previous highlight - let s:w = winsaveview() - let hgm = incsearch#highlight#hgm() - let c = hgm.cursor - call s:hi.add(c.group, c.group, '\%#', c.priority) - call incsearch#highlight#update() - - " XXX: Manipulate search history for magic option - " In the first place, I want to omit magic flag when histadd(), but - " when returning cmd as expr mapping and feedkeys() cannot handle it, so - " remove no user intended magic flag at on_enter. - " Maybe I can also handle it with autocmd, should I use autocmd instead? - let hist = histget('/', -1) - if len(hist) > 2 && hist[:1] ==# s:magic() - call histdel('/', -1) - call histadd('/', hist[2:]) - endif -endfunction - -function! s:inc.on_leave(cmdline) abort - call s:hi.disable_all() - call s:hi.delete_all() - " redraw: hide pseud-cursor - redraw " need to redraw for handling non- mappings - if a:cmdline.getline() ==# '' - echo '' - else - echo a:cmdline.get_prompt() . a:cmdline.getline() - endif - " NOTE: - " push rest of keymappings with feedkeys() - " FIXME: assume 'noremap' but it should take care wheter or not the - " mappings should be remapped or not - if a:cmdline.input_key_stack_string() != '' - call feedkeys(a:cmdline.input_key_stack_string(), 'n') - endif -endfunction - -" Avoid search-related error while incremental searching -function! s:on_searching(func, ...) abort - try - return call(a:func, a:000) - catch /E16:/ " E16: Invalid range (with /\_[a- ) - catch /E33:/ " E33: No previous substitute regular expression - catch /E53:/ " E53: Unmatched %( - catch /E54:/ - catch /E55:/ - catch /E62:/ " E62: Nested \= (with /a\=\=) - catch /E63:/ " E63: invalid use of \_ - catch /E64:/ " E64: \@ follows nothing - catch /E65:/ " E65: Illegal back reference - catch /E66:/ " E66: \z( not allowed here - catch /E67:/ " E67: \z1 et al. not allowed here - catch /E68:/ " E68: Invalid character after \z (with /\za & re=1) - catch /E69:/ " E69: Missing ] after \%[ - catch /E70:/ " E70: Empty \%[] - catch /E71:/ " E71: Invalid character after \% - catch /E554:/ - catch /E678:/ " E678: Invalid character after \%[dxouU] - catch /E864:/ " E864: \%#= can only be followed by 0, 1, or 2. The - " automatic engine will be used - catch /E865:/ " E865: (NFA) Regexp end encountered prematurely - catch /E866:/ " E866: (NFA regexp) Misplaced @ - catch /E867:/ " E867: (NFA) Unknown operator - catch /E869:/ " E869: (NFA) Unknown operator '\@m - catch /E870:/ " E870: (NFA regexp) Error reading repetition limits - catch /E871:/ " E871: (NFA regexp) Can't have a multi follow a multi ! - catch /E874:/ " E874: (NFA) Could not pop the stack ! (with \&) - catch /E877:/ " E877: (NFA regexp) Invalid character class: 109 - catch /E888:/ " E888: (NFA regexp) cannot repeat (with /\ze*) - call s:hi.disable_all() - catch - echohl ErrorMsg | echom v:throwpoint . " " . v:exception | echohl None - endtry -endfunction - -function! s:on_char_pre(cmdline) abort - " NOTE: - " `:call a:cmdline.setchar('')` as soon as possible! - let [pattern, offset] = s:cli_parse_pattern(a:cmdline) - - " Interactive :h last-pattern if pattern is empty - if ( a:cmdline.is_input("(incsearch-next)") - \ || a:cmdline.is_input("(incsearch-prev)") - \ ) && empty(pattern) - call a:cmdline.setchar('') - " Use history instead of @/ to work with magic option and converter - call a:cmdline.setline(histget('/', -1) . (empty(offset) ? '' : a:cmdline._base_key) . offset) - " Just insert last-pattern and do not count up, but the incsearch-prev - " should move the cursor to reversed directly, so do not return if the - " command is prev - if a:cmdline.is_input("(incsearch-next)") | return | endif - endif - - if a:cmdline.is_input("(incsearch-next)") - call a:cmdline.setchar('') - if a:cmdline.flag ==# 'n' " exit stay mode - let a:cmdline.flag = '' - else - let a:cmdline._vcount1 += 1 - endif - elseif a:cmdline.is_input("(incsearch-prev)") - call a:cmdline.setchar('') - if a:cmdline.flag ==# 'n' " exit stay mode - let a:cmdline.flag = '' - endif - let a:cmdline._vcount1 -= 1 - if a:cmdline._vcount1 < 1 - let a:cmdline._vcount1 += s:U.count_pattern(pattern) - endif - elseif (a:cmdline.is_input("(incsearch-scroll-f)") - \ && (a:cmdline.flag ==# '' || a:cmdline.flag ==# 'n')) - \ || (a:cmdline.is_input("(incsearch-scroll-b)") && a:cmdline.flag ==# 'b') - call a:cmdline.setchar('') - if a:cmdline.flag ==# 'n' | let a:cmdline.flag = '' | endif - let pos_expr = a:cmdline.is_input("(incsearch-scroll-f)") ? 'w$' : 'w0' - let to_col = a:cmdline.is_input("(incsearch-scroll-f)") - \ ? s:U.get_max_col(pos_expr) : 1 - let [from, to] = [getpos('.')[1:2], [line(pos_expr), to_col]] - let cnt = s:U.count_pattern(pattern, from, to) - let a:cmdline._vcount1 += cnt - elseif (a:cmdline.is_input("(incsearch-scroll-b)") - \ && (a:cmdline.flag ==# '' || a:cmdline.flag ==# 'n')) - \ || (a:cmdline.is_input("(incsearch-scroll-f)") && a:cmdline.flag ==# 'b') - call a:cmdline.setchar('') - if a:cmdline.flag ==# 'n' - let a:cmdline.flag = '' - let a:cmdline._vcount1 -= 1 - endif - let pos_expr = a:cmdline.is_input("(incsearch-scroll-f)") ? 'w$' : 'w0' - let to_col = a:cmdline.is_input("(incsearch-scroll-f)") - \ ? s:U.get_max_col(pos_expr) : 1 - let [from, to] = [getpos('.')[1:2], [line(pos_expr), to_col]] - let cnt = s:U.count_pattern(pattern, from, to) - let a:cmdline._vcount1 -= cnt - if a:cmdline._vcount1 < 1 - let a:cmdline._vcount1 += s:U.count_pattern(pattern) - endif - endif - - " Handle nowrapscan: - " if you `:set nowrapscan`, you can't move to the reversed direction - if &wrapscan == s:FALSE && ( - \ a:cmdline.is_input("(incsearch-next)") - \ || a:cmdline.is_input("(incsearch-prev)") - \ || a:cmdline.is_input("(incsearch-scroll-f)") - \ || a:cmdline.is_input("(incsearch-scroll-b)") - \ ) - call a:cmdline.setchar('') - let [from, to] = [[s:w.lnum, s:w.col], - \ a:cmdline.flag !=# 'b' - \ ? [line('$'), s:U.get_max_col('$')] - \ : [1, 1] - \ ] - let max_cnt = s:U.count_pattern(pattern, from, to) - let a:cmdline._vcount1 = min([max_cnt, a:cmdline._vcount1]) - endif -endfunction - -function! s:on_char(cmdline) abort - let [raw_pattern, offset] = s:cli_parse_pattern(a:cmdline) - - if raw_pattern ==# '' - call s:hi.disable_all() - nohlsearch - return - endif - - " For InsertRegister - if a:cmdline.get_tap_key() ==# "\" - let p = a:cmdline.getpos() - " Remove `"` - let raw_pattern = raw_pattern[:p-1] . raw_pattern[p+1:] - let w = winsaveview() - call cursor(line('.'), col('.') + len(a:cmdline.backward_word())) - call s:InsertRegister.reset() - call winrestview(w) - endif - - let pattern = s:convert(raw_pattern) - - " Improved Incremental cursor move! - call s:move_cursor(a:cmdline, pattern, offset) - - " Improved Incremental highlighing! - " case: because matchadd() doesn't handle 'ignorecase' nor 'smartcase' - let case = incsearch#detect_case(raw_pattern) - let should_separate = g:incsearch#separate_highlight && a:cmdline.flag !=# 'n' - let d = (a:cmdline.flag !=# 'b' ? s:DIRECTION.forward : s:DIRECTION.backward) - call incsearch#highlight#incremental_highlight( - \ pattern . case, should_separate, d, [s:w.lnum, s:w.col]) - - " functional `normal! zz` after scroll for mappings - if ( a:cmdline.is_input("(incsearch-scroll-f)") - \ || a:cmdline.is_input("(incsearch-scroll-b)")) - call winrestview({'topline': max([1, line('.') - winheight(0) / 2])}) - endif -endfunction - -" Caveat: It handle :h last-pattern, so be careful if you want to pass empty -" string as a pattern -function! s:move_cursor(cli, pattern, ...) abort - let offset = get(a:, 1, '') - if a:cli.flag ==# 'n' " skip if stay mode - return - endif - call winrestview(s:w) - " pseud-move cursor position: this is restored afterward if called by - " mappings - if a:cli._is_expr - for _ in range(a:cli._vcount1) - " NOTE: This cannot handle {offset} for cursor position - call search(a:pattern, a:cli.flag) - endfor - else - " More precise cursor position while searching - " Caveat: - " This block contains `normal`, please make sure mappings - " doesn't reach this block - let is_visual_mode = s:U.is_visual(mode(1)) - let cmd = s:with_ignore_foldopen( - \ function('s:build_search_cmd'), - \ a:cli, 'n', s:combine_pattern(a:cli, a:pattern, offset), a:cli._base_key) - " NOTE: - " :silent! - " Shut up errors! because this is just for the cursor emulation - " while searching - silent! call s:execute_search(cmd) - if is_visual_mode - let w = winsaveview() - normal! gv - call winrestview(w) - call incsearch#highlight#emulate_visual_highlight() - endif - endif -endfunction - -function! s:inc.on_char_pre(cmdline) abort - call s:on_searching(function('s:on_char_pre'), a:cmdline) -endfunction - -function! s:inc.on_char(cmdline) abort - call s:on_searching(function('s:on_char'), a:cmdline) -endfunction - -call s:cli.connect(s:inc) - -"" partial deepcopy() for cli.connect(module) instead of copy() -function! s:copy_cli(cli) abort - let cli = copy(a:cli) - let cli.variables = deepcopy(a:cli.variables) - return cli -endfunction - -function! s:make_cli(config) abort - let cli = s:copy_cli(s:cli) - let cli._base_key = a:config.command - let cli._vcount1 = a:config.count1 - let cli._is_expr = a:config.is_expr - let cli._mode = a:config.mode - let cli._pattern = a:config.pattern - for module in a:config.modules - call cli.connect(module) - endfor - call cli.connect(s:InsertRegister) - return cli -endfunction - -"}}} +" Utility: +let s:U = incsearch#util#import() " Main: {{{ " @return vital-over command-line interface object. it's experimental!!! function! incsearch#cli() abort - try - " It returns current cli object - return s:Doautocmd.get_cmdline() - catch /vital-over(_incsearch) Exception/ - " If there are no current cli object, return default one - return s:cli - endtry + return incsearch#cli#get() endfunction "" NOTE: this global variable is only for handling config from go_wrap func @@ -568,14 +127,14 @@ endfunction " `incsearch#go()` have to result in the same cursor move. " @return command: String to search function! incsearch#_go(config) abort - let Search = function(a:config.is_stay ? 'incsearch#stay' : 'incsearch#search') if s:U.is_visual(a:config.mode) && !a:config.is_expr normal! gv endif - let cli = s:make_cli(a:config) - let cmd = Search(cli) + let cli = incsearch#cli#make(a:config) + let l:Search = function(a:config.is_stay ? 'incsearch#stay' : 'incsearch#search') + let cmd = l:Search(cli) if !a:config.is_expr - let should_set_jumplist = (cli.flag !=# 'n') + let should_set_jumplist = (cli._flag !=# 'n') call s:set_search_related_stuff(cli, cmd, should_set_jumplist) if a:config.mode is# 'no' call s:set_vimrepeat(cmd) @@ -598,59 +157,56 @@ endfunction " @expr but sometimes called by non- " @return: command which is excutable with expr-mappings or `exec 'normal!'` function! incsearch#stay(cli) abort - let input = s:get_input(a:cli, '') + let input = s:get_input(a:cli) + let l:F = function(a:cli._flag is# 'n' ? 's:stay' : 's:search') + return l:F(a:cli, input) +endfunction - let [raw_pattern, offset] = s:cli_parse_pattern(a:cli) - let pattern = s:convert(raw_pattern) +let g:incsearch#_view = get(g:, 'incsearch#_view', {}) +noremap (_incsearch-winrestview) +noremap! (_incsearch-winrestview) +nnoremap (_incsearch-winrestview) :call winrestview(g:incsearch#_view) +xnoremap (_incsearch-winrestview) :call winrestview(g:incsearch#_view)gv - " execute histadd manually - if a:cli.flag ==# 'n' && input !=# '' && (a:cli._is_expr || empty(offset)) - call histadd('/', input) - let @/ = pattern - endif +function! s:stay(cli, input) abort + let [raw_pattern, offset] = incsearch#cli_parse_pattern(a:cli) + let pattern = incsearch#convert(raw_pattern) - if a:cli.flag ==# 'n' " stay - " NOTE: do not move cursor but need to handle {offset} for n & N ...! {{{ - " FIXME: cannot set {offset} if in operator-pending mode because this - " have to use feedkeys() - let is_cancel = a:cli.exit_code() - if is_cancel - call s:cleanup_cmdline() - elseif !empty(offset) && mode(1) !=# 'no' - let cmd = s:with_ignore_foldopen( - \ function('s:generate_command'), a:cli, input, '/') - call feedkeys(cmd, 'n') - " XXX: string()... use or ? But it doesn't work well. - call s:U.silent_feedkeys(":\call winrestview(". string(s:w) . ")\", 'winrestview', 'n') - call incsearch#auto_nohlsearch(2) - else - call incsearch#auto_nohlsearch(0) + " NOTE: do not move cursor but need to handle {offset} for n & N ...! {{{ + " FIXME: cannot set {offset} if in operator-pending mode because this + " have to use feedkeys() + let is_cancel = a:cli.exit_code() + if is_cancel + call s:cleanup_cmdline() + elseif !empty(offset) && mode(1) !=# 'no' + let cmd = incsearch#with_ignore_foldopen( + \ function('s:generate_command'), a:cli, a:input) + call feedkeys(cmd, 'n') + let g:incsearch#_view = a:cli._w + call feedkeys("\(_incsearch-winrestview)", 'm') + call incsearch#autocmd#auto_nohlsearch(2) + else + " Handle last-pattern + if a:input isnot# '' + call histadd('/', a:input) + let @/ = pattern endif - " }}} - return s:U.is_visual(a:cli._mode) ? "\gv" : "\" " just exit - else " exit stay mode while searching - call incsearch#auto_nohlsearch(1) - return s:generate_command(a:cli, input, '/') " assume '/' + call incsearch#autocmd#auto_nohlsearch(0) endif + " }}} + return s:U.is_visual(a:cli._mode) ? "\gv" : "\" " just exit endfunction function! incsearch#search(cli) abort - let input = s:get_input(a:cli, a:cli._base_key) - let [pattern, offset] = incsearch#parse_pattern(input, a:cli._base_key) - call incsearch#auto_nohlsearch(1) " NOTE: `.` repeat doesn't handle this - return s:generate_command( - \ a:cli, s:combine_pattern(a:cli, s:convert(pattern), offset), a:cli._base_key) -endfunction - -function! s:get_input(cli, search_key) abort - " if search_key is empty, it means `stay` & do not move cursor - let prompt = a:search_key ==# '' ? '/' : a:search_key - call a:cli.set_prompt(prompt) - let a:cli.flag = a:search_key ==# '/' ? '' - \ : a:search_key ==# '?' ? 'b' - \ : a:search_key ==# '' ? 'n' - \ : '' + return s:search(a:cli, s:get_input(a:cli)) +endfunction + +function! s:search(cli, input) abort + call incsearch#autocmd#auto_nohlsearch(1) " NOTE: `.` repeat doesn't handle this + return s:generate_command(a:cli, a:input) +endfunction +function! s:get_input(cli) abort " Handle visual mode highlight if s:U.is_visual(a:cli._mode) let visual_hl = incsearch#highlight#get_visual_hlobj() @@ -667,23 +223,34 @@ function! s:get_input(cli, search_key) abort return input endfunction -function! s:generate_command(cli, pattern, search_key) abort - if (a:cli.exit_code() == 0) - let v = winsaveview() - try - call winrestview(s:w) - call a:cli.callevent('on_execute_pre') " XXX: side-effect! - finally - call winrestview(v) - endtry - call a:cli.callevent('on_execute') " XXX: side-effect! - return s:build_search_cmd(a:cli, a:cli._mode, a:pattern, a:search_key) - else " Cancel - return s:U.is_visual(a:cli._mode) ? '\gv' : "\" +function! s:generate_command(cli, input) abort + let is_cancel = a:cli.exit_code() + if is_cancel + return s:U.is_visual(a:cli._mode) ? "\gv" : "\" + else + call s:call_execute_event(a:cli) + let [pattern, offset] = incsearch#parse_pattern(a:input, a:cli._base_key) + " TODO: implement convert input method + let p = incsearch#combine_pattern(a:cli, incsearch#convert(pattern), offset) + return a:cli._build_search_cmd(p) endif endfunction -function! s:build_search_cmd(cli, mode, pattern, search_key) abort +"" Call on_execute_pre and on_execute event +" assume current position is the destination and a:cli._w is the position to +" start search +function! s:call_execute_event(cli, ...) abort + let view = get(a:, 1, winsaveview()) + try + call winrestview(a:cli._w) + call a:cli.callevent('on_execute_pre') + finally + call winrestview(view) + endtry + call a:cli.callevent('on_execute') +endfunction + +function! incsearch#build_search_cmd(cli, mode, pattern) abort let op = (a:mode == 'no') ? v:operator \ : s:U.is_visual(a:mode) ? 'gv' \ : '' @@ -694,7 +261,7 @@ function! s:build_search_cmd(cli, mode, pattern, search_key) abort " but, if you do not move the cursor while incremental searching, " there are no need to use . return printf("\\"%s%s%s%s%s\%s", - \ v:register, op, a:cli._vcount1, a:search_key, a:pattern, zv) + \ v:register, op, a:cli._vcount1, a:cli._base_key, a:pattern, zv) endfunction " Assume the cursor move is already done. @@ -707,66 +274,44 @@ function! s:set_search_related_stuff(cli, cmd, ...) abort if is_cancel " Restore cursor position and return " NOTE: Should I request on_cancel event to vital-over and use it? - call winrestview(s:w) + call winrestview(a:cli._w) call s:cleanup_cmdline() return endif - let [raw_pattern, offset] = s:cli_parse_pattern(a:cli) + let [raw_pattern, offset] = incsearch#cli_parse_pattern(a:cli) let should_execute = !empty(offset) || empty(raw_pattern) if should_execute " Execute with feedkeys() to work with " 1. :h {offset} for `n` and `N` " 2. empty input (:h last-pattern) " NOTE: Don't use feedkeys() as much as possible to avoid flickering - call winrestview(s:w) + call winrestview(a:cli._w) call feedkeys(a:cmd, 'n') if g:incsearch#consistent_n_direction - call s:_silent_searchforward(s:DIRECTION.forward) + call feedkeys("\(_incsearch-searchforward)", 'm') endif else " Add history if necessary " Do not save converted pattern to history - let pattern = s:convert(raw_pattern) - let input = s:combine_pattern(a:cli, raw_pattern, offset) + let pattern = incsearch#convert(raw_pattern) + let input = incsearch#combine_pattern(a:cli, raw_pattern, offset) call histadd(a:cli._base_key, input) let @/ = pattern - " Emulate errors, and handling `n` and `N` preparation {{{ let target_view = winsaveview() - call winrestview(s:w) " Get back start position temporarily for emulation + call winrestview(a:cli._w) " Get back start position temporarily for emulation " Set jump list if should_set_jumplist normal! m` endif - let d = (a:cli._base_key == '/' ? s:DIRECTION.forward : s:DIRECTION.backward) - call s:emulate_search_error(d) + " Emulate errors, and handling `n` and `N` preparation + call s:emulate_search_error(a:cli._direction, a:cli._w) + + " winrestview() between error and wraning emulation to avoid flickering call winrestview(target_view) - "}}} - - " Emulate warning {{{ - " NOTE: - " - It should use :h echomsg considering emulation of default - " warning messages remain in the :h message-history, but it'll mess - " up the message-history unnecessary, so it use :h echo - " - Echo warning message after winrestview() to avoid flickering - " - See :h shortmess - if &shortmess !~# 's' && g:incsearch#do_not_save_error_message_history - let from = [s:w.lnum, s:w.col] - let to = [target_view.lnum, target_view.col] - let old_warningmsg = v:warningmsg - let v:warningmsg = - \ ( d == s:DIRECTION.forward && !s:U.is_pos_less_equal(from, to) - \ ? 'search hit BOTTOM, continuing at TOP' - \ : d == s:DIRECTION.backward && s:U.is_pos_less_equal(from, to) - \ ? 'search hit TOP, continuing at BOTTOM' - \ : '' ) - if v:warningmsg !=# '' - call s:Warning(v:warningmsg) - else - let v:warningmsg = old_warningmsg - endif - endif - "}}} + + " Emulate warning + call s:emulate_search_warning(a:cli._direction, a:cli._w, target_view) call s:silent_after_search() @@ -777,37 +322,6 @@ function! s:set_search_related_stuff(cli, cmd, ...) abort endif endfunction -" Make sure move cursor by search related action __after__ calling this -" function because the first move event just set nested autocmd which -" does :nohlsearch -" @expr -function! incsearch#auto_nohlsearch(nest) abort - " NOTE: see this value inside this function in order to toggle auto - " :nohlsearch feature easily with g:incsearch#auto_nohlsearch option - if !g:incsearch#auto_nohlsearch | return '' | endif - let cmd = s:U.is_visual(mode(1)) - \ ? 'call feedkeys(":\nohlsearch\" . (mode(1) =~# "[vV\]" ? "gv" : ""), "n") - \ ' - \ : 'call s:U.silent_feedkeys(":\nohlsearch\" . (mode(1) =~# "[vV\]" ? "gv" : ""), "nohlsearch", "n") - \ ' - " NOTE: :h autocmd-searchpat - " You cannot implement this feature without feedkeys() bacause of - " :h autocmd-searchpat - augroup incsearch-auto-nohlsearch - autocmd! - " NOTE: this break . unit with c{text-object} - " side-effect: InsertLeave & InsertEnter are called with i_CTRL-\_CTRL-O - " autocmd InsertEnter * call feedkeys("\\:nohlsearch\", "n") - " \ | autocmd! incsearch-auto-nohlsearch - execute join([ - \ 'autocmd CursorMoved *' - \ , repeat('autocmd incsearch-auto-nohlsearch CursorMoved * ', a:nest) - \ , cmd - \ , '| autocmd! incsearch-auto-nohlsearch' - \ ], ' ') - augroup END - return '' -endfunction "}}} @@ -833,7 +347,8 @@ function! incsearch#parse_pattern(expr, search_key) abort endfunction " CommandLine Interface parse pattern wrapper -function! s:cli_parse_pattern(cli) abort +" function! s:cli_parse_pattern(cli) abort +function! incsearch#cli_parse_pattern(cli) abort if v:version == 704 && !has('patch421') " Ignore \ze* which clash vim 7.4 without 421 patch " Assume `\m` @@ -845,16 +360,16 @@ function! s:cli_parse_pattern(cli) abort endif endfunction -function! s:combine_pattern(cli, pattern, offset) abort +function! incsearch#combine_pattern(cli, pattern, offset) abort return empty(a:offset) ? a:pattern : a:pattern . a:cli._base_key . a:offset endfunction " convert implementation. assume pattern is not empty function! s:_convert(pattern) abort - return s:magic() . a:pattern + return incsearch#magic() . a:pattern endfunction -function! s:convert(pattern) abort +function! incsearch#convert(pattern) abort " TODO: convert pattern if required in addition to appending magic flag return a:pattern is# '' ? a:pattern : s:_convert(a:pattern) endfunction @@ -882,29 +397,42 @@ endfunction function! s:silent_after_search(...) abort " arg: mode(1) " :h function-search-undo - if get(a:, 1, mode(1)) !=# 'no' " guard for operator-mapping - call s:_silent_hlsearch() - call s:_silent_searchforward() + let m = get(a:, 1, mode(1)) + if m !=# 'no' " guard for operator-mapping + let cmd = join([ + \ (s:U.is_visual(m) ? "\(_incsearch-esc)" : ''), + \ "\(_incsearch-hlsearch)", + \ "\(_incsearch-searchforward)", + \ (s:U.is_visual(m) ? "\(_incsearch-gv)" : '') + \ ], '') + call feedkeys(cmd, 'm') endif endfunction -function! s:_silent_hlsearch() abort - " Handle :set hlsearch - call s:U.silent_feedkeys(":let &hlsearch=&hlsearch\", 'hlsearch', 'n') -endfunction +noremap (_incsearch-gv) +noremap! (_incsearch-gv) +nnoremap (_incsearch-gv) gv + +noremap (_incsearch-esc) +noremap! (_incsearch-esc) +xnoremap (_incsearch-esc) -function! s:_silent_searchforward(...) abort - " NOTE: You have to 'exec normal! `/` or `?`' before calling this - " function to update v:searchforward - let direction = get(a:, 1, - \ (g:incsearch#consistent_n_direction == s:TRUE) - \ ? s:DIRECTION.forward : v:searchforward) - call s:U.silent_feedkeys( - \ ":let v:searchforward=" . direction . "\", - \ 'searchforward', 'n') +noremap (_incsearch-hlsearch) +noremap! (_incsearch-hlsearch) +nnoremap (_incsearch-hlsearch) :let &hlsearch=&hlsearch +xnoremap (_incsearch-hlsearch) :let &hlsearch=&hlsearch + +noremap (_incsearch-searchforward) +noremap! (_incsearch-searchforward) +nnoremap (_incsearch-searchforward) _searchforward_cmd() +xnoremap (_incsearch-searchforward) _searchforward_cmd() +function! s:_searchforward_cmd() abort + let d = (g:incsearch#consistent_n_direction ? s:DIRECTION.forward : v:searchforward) + return printf(":\let v:searchforward=%d\", d) endfunction -function! s:emulate_search_error(direction) abort +function! s:emulate_search_error(direction, ...) abort + let from = get(a:, 1, winsaveview()) let keyseq = (a:direction == s:DIRECTION.forward ? '/' : '?') let old_errmsg = v:errmsg let v:errmsg = '' @@ -913,10 +441,9 @@ function! s:emulate_search_error(direction) abort " - silent!: Do not show error and warning message, because it also " echo v:throwpoint for error and save messages in message-history " - Unlike v:errmsg, v:warningmsg doesn't set if it use :silent! - let w = winsaveview() " Get first error - silent! call s:execute_search(keyseq . "\") - call winrestview(w) + silent! call incsearch#execute_search(keyseq . "\") + call winrestview(from) if g:incsearch#do_not_save_error_message_history if v:errmsg != '' call s:Error(v:errmsg) @@ -928,7 +455,7 @@ function! s:emulate_search_error(direction) abort let last_error = v:errmsg try " Do not use silent! to show warning - call s:execute_search(keyseq . "\") + call incsearch#execute_search(keyseq . "\") catch /^Vim\%((\a\+)\)\=:E/ let first_error = matchlist(v:exception, '\v^Vim%(\(\a+\))=:(E.*)$')[1] call s:Error(first_error, 'echom') @@ -936,7 +463,7 @@ function! s:emulate_search_error(direction) abort call s:Error(last_error, 'echom') endif finally - call winrestview(w) + call winrestview(from) endtry if v:errmsg == '' let v:errmsg = old_errmsg @@ -944,6 +471,31 @@ function! s:emulate_search_error(direction) abort endif endfunction +function! s:emulate_search_warning(direction, from, to) abort + " NOTE: + " - It should use :h echomsg considering emulation of default + " warning messages remain in the :h message-history, but it'll mess + " up the message-history unnecessary, so it use :h echo + " - See :h shortmess + " if &shortmess !~# 's' && g:incsearch#do_not_save_error_message_history + if &shortmess !~# 's' && g:incsearch#do_not_save_error_message_history + let from = [a:from.lnum, a:from.col] + let to = [a:to.lnum, a:to.col] + let old_warningmsg = v:warningmsg + let v:warningmsg = + \ ( a:direction == s:DIRECTION.forward && !s:U.is_pos_less_equal(from, to) + \ ? 'search hit BOTTOM, continuing at TOP' + \ : a:direction == s:DIRECTION.backward && s:U.is_pos_less_equal(from, to) + \ ? 'search hit TOP, continuing at BOTTOM' + \ : '' ) + if v:warningmsg !=# '' + call s:Warning(v:warningmsg) + else + let v:warningmsg = old_warningmsg + endif + endif +endfunction + function! s:cleanup_cmdline() abort redraw | echo '' endfunction @@ -966,7 +518,7 @@ function! s:_echohl(msg, hlgroup, ...) abort endfunction " Not to generate command with zv -function! s:with_ignore_foldopen(F, ...) abort +function! incsearch#with_ignore_foldopen(F, ...) abort let foldopen_save = &foldopen let &foldopen='' try @@ -986,11 +538,11 @@ function! s:_execute_search(cmd) abort execute s:keeppattern 'keepjumps' 'normal!' a:cmd | nohlsearch endfunction if s:has_keeppattern - function! s:execute_search(...) abort + function! incsearch#execute_search(...) abort return call(function('s:_execute_search'), a:000) endfunction else - function! s:execute_search(...) abort + function! incsearch#execute_search(...) abort " keeppattern emulation let p = @/ let r = call(function('s:_execute_search'), a:000) @@ -1002,7 +554,7 @@ else endfunction endif -function! s:magic() abort +function! incsearch#magic() abort let m = g:incsearch#magic return (len(m) == 2 && m =~# '\\[mMvV]' ? m : '') endfunction diff --git a/autoload/incsearch/autocmd.vim b/autoload/incsearch/autocmd.vim new file mode 100644 index 0000000..e4eeb68 --- /dev/null +++ b/autoload/incsearch/autocmd.vim @@ -0,0 +1,55 @@ +"============================================================================= +" FILE: autoload/incsearch/autocmd.vim +" AUTHOR: haya14busa +" License: MIT license +"============================================================================= +scriptencoding utf-8 +let s:save_cpo = &cpo +set cpo&vim + +noremap (_incsearch-nohlsearch) incsearch#autocmd#auto_nohlsearch(0) +noremap! (_incsearch-nohlsearch) incsearch#autocmd#auto_nohlsearch(0) +nnoremap (_incsearch-nohlsearch) :nohlsearch +xnoremap (_incsearch-nohlsearch) :nohlsearchgv + +" Make sure move cursor by search related action __after__ calling this +" function because the first move event just set nested autocmd which +" does :nohlsearch +" @expr +function! incsearch#autocmd#auto_nohlsearch(nest) abort + " NOTE: see this value inside this function in order to toggle auto + " :nohlsearch feature easily with g:incsearch#autocmd#auto_nohlsearch option + if !g:incsearch#auto_nohlsearch | return '' | endif + return s:auto_nohlsearch(a:nest) +endfunction + +function! s:auto_nohlsearch(nest) abort + " NOTE: :h autocmd-searchpat + " You cannot implement this feature without feedkeys() because of + " :h autocmd-searchpat + augroup incsearch-auto-nohlsearch + autocmd! + autocmd InsertEnter * :call attach_on_insert_leave() | autocmd! incsearch-auto-nohlsearch + execute join([ + \ 'autocmd CursorMoved *' + \ , repeat('autocmd incsearch-auto-nohlsearch CursorMoved * ', a:nest) + \ , 'call feedkeys("\(_incsearch-nohlsearch)", "m")' + \ , '| autocmd! incsearch-auto-nohlsearch' + \ ], ' ') + augroup END + return '' +endfunction + +function! s:attach_on_insert_leave() abort + augroup incsearch-auto-nohlsearch-on-insert-leave + autocmd! + autocmd InsertLeave * :call incsearch#autocmd#auto_nohlsearch(1) + \ | autocmd! incsearch-auto-nohlsearch-on-insert-leave + augroup END + return '' +endfunction + +let &cpo = s:save_cpo +unlet s:save_cpo +" __END__ +" vim: expandtab softtabstop=2 shiftwidth=2 foldmethod=marker diff --git a/autoload/incsearch/cli.vim b/autoload/incsearch/cli.vim new file mode 100644 index 0000000..4439234 --- /dev/null +++ b/autoload/incsearch/cli.vim @@ -0,0 +1,152 @@ +"============================================================================= +" FILE: autoload/incsearch/cli.vim +" AUTHOR: haya14busa +" License: MIT license +"============================================================================= +scriptencoding utf-8 +let s:save_cpo = &cpo +set cpo&vim + +let s:DIRECTION = { 'forward': 1, 'backward': 0 } " see :h v:searchforward + +let s:V = incsearch#vital() + +function! incsearch#cli#get() abort + try + " It returns current cli object + return s:Doautocmd.get_cmdline() + catch /vital-over(_incsearch) Exception/ + " If there are no current cli object, return default one + return incsearch#cli#make(incsearch#config#make({})) + endtry +endfunction + +" @config: whole configuration +function! incsearch#cli#make(config) abort + let cli = s:copy_cli(s:cli) + call incsearch#cli#set(cli, a:config) + return cli +endfunction + +" To reuse cli object, you should re-set configuration +" @config: whole configuration +function! incsearch#cli#set(cli, config) abort + let a:cli._base_key = a:config.command + let a:cli._vcount1 = a:config.count1 + let a:cli._is_expr = a:config.is_expr + let a:cli._mode = a:config.mode + let a:cli._pattern = a:config.pattern + let a:cli._prompt = a:config.prompt + let a:cli._flag = a:config.is_stay ? 'n' + \ : a:config.command is# '/' ? '' + \ : a:config.command is# '?' ? 'b' + \ : '' + let a:cli._direction = + \ (a:cli._base_key is# '/' ? s:DIRECTION.forward : s:DIRECTION.backward) + " TODO: provide config? but it may conflict with mapping + " NOTE: _w: default cursor view + let a:cli._w = winsaveview() + for module in a:config.modules + call a:cli.connect(module) + endfor + call a:cli.set_prompt(a:cli._prompt) + return a:cli +endfunction + +"" partial deepcopy() for cli.connect(module) instead of copy() +function! s:copy_cli(cli) abort + let cli = copy(a:cli) + let cli.variables = deepcopy(a:cli.variables) + return cli +endfunction + +let s:cli = s:V.import('Over.Commandline').make_default("/") +let s:modules = s:V.import('Over.Commandline.Modules') + +" Add modules +call s:cli.connect('BufferComplete') +call s:cli.connect('Cancel') +call s:cli.connect('CursorMove') +call s:cli.connect('Digraphs') +call s:cli.connect('Delete') +call s:cli.connect('DrawCommandline') +call s:cli.connect('ExceptionExit') +call s:cli.connect('LiteralInsert') +" call s:cli.connect('Exit') +call s:cli.connect(incsearch#over#modules#exit#make()) +call s:cli.connect('InsertRegister') +call s:cli.connect('Paste') +let s:Doautocmd = s:modules.get('Doautocmd') +call s:cli.connect(s:Doautocmd.make('IncSearch')) +call s:cli.connect(s:modules.get('ExceptionMessage').make('incsearch.vim: ', 'echom')) +call s:cli.connect(s:modules.get('History').make('/')) +call s:cli.connect(s:modules.get('NoInsert').make_special_chars()) + +" Dynamic Module Loading Management +let s:KeyMapping = s:modules.get('KeyMapping') +let s:emacs_like = s:KeyMapping.make_emacs() +let s:vim_cmap = s:KeyMapping.make_vim_cmdline_mapping() +let s:smartbackword = s:modules.get('IgnoreRegexpBackwardWord').make() +function! s:emacs_like._condition() abort + return g:incsearch#emacs_like_keymap +endfunction +function! s:vim_cmap._condition() abort + return g:incsearch#vim_cmdline_keymap +endfunction +function! s:smartbackword._condition() abort + return g:incsearch#smart_backward_word +endfunction +call s:cli.connect(incsearch#over#modules#module_management#make([s:emacs_like, s:vim_cmap, s:smartbackword])) +unlet s:KeyMapping s:emacs_like s:vim_cmap s:smartbackword + +call s:cli.connect(incsearch#over#modules#pattern_saver#make()) +call s:cli.connect(incsearch#over#modules#incsearch#make()) + +let s:default_keymappings = { +\ "\" : { +\ "key" : "(incsearch-next)", +\ "noremap" : 1, +\ }, +\ "\" : { +\ "key" : "(incsearch-prev)", +\ "noremap" : 1, +\ }, +\ "\" : { +\ "key" : "(incsearch-scroll-f)", +\ "noremap" : 1, +\ }, +\ "\" : { +\ "key" : "(incsearch-scroll-b)", +\ "noremap" : 1, +\ }, +\ "\" : { +\ "key" : "(buffer-complete)", +\ "noremap" : 1, +\ }, +\ "\" : { +\ "key": "\", +\ "noremap": 1 +\ }, +\ } + +" https://github.com/haya14busa/incsearch.vim/issues/35 +if has('mac') + call extend(s:default_keymappings, { + \ '"+gP' : { + \ 'key': "\+", + \ 'noremap': 1 + \ }, + \ }) +endif + +" FIXME: arguments? +function! s:cli.keymapping(...) abort + return extend(copy(s:default_keymappings), g:incsearch_cli_key_mappings) +endfunction + +call incsearch#over#extend#enrich(s:cli) + +let &cpo = s:save_cpo +unlet s:save_cpo +" __END__ +" vim: expandtab softtabstop=2 shiftwidth=2 foldmethod=marker diff --git a/autoload/incsearch/config.vim b/autoload/incsearch/config.vim index f2519b2..34ccd2e 100644 --- a/autoload/incsearch/config.vim +++ b/autoload/incsearch/config.vim @@ -23,6 +23,7 @@ let s:config = { \ 'pattern': '', \ 'mode': 'n', \ 'count1': 1, +\ 'prompt': '', \ 'modules': [] \ } @@ -36,6 +37,9 @@ endfunction function! incsearch#config#make(additional) abort let default = extend(copy(s:config), s:lazy_config()) let c = extend(default, a:additional) + if c.prompt is# '' + let c.prompt = c.command + endif return c endfunction diff --git a/autoload/incsearch/over/extend.vim b/autoload/incsearch/over/extend.vim new file mode 100644 index 0000000..b2a8a02 --- /dev/null +++ b/autoload/incsearch/over/extend.vim @@ -0,0 +1,60 @@ +"============================================================================= +" FILE: autoload/incsearch/over/extend.vim +" AUTHOR: haya14busa +" License: MIT license +"============================================================================= +scriptencoding utf-8 +let s:save_cpo = &cpo +set cpo&vim + +let s:U = incsearch#util#import() + +function! incsearch#over#extend#enrich(cli) abort + return extend(a:cli, s:cli) +endfunction + +let s:cli = {} + +function! s:cli._generate_command(input) abort + let is_cancel = self.exit_code() + if is_cancel + return s:U.is_visual(self._mode) ? '\gv' : "\" + else + call self._call_execute_event() + let [pattern, offset] = incsearch#parse_pattern(a:input, self._base_key) + " TODO: implement convert input method + let p = incsearch#combine_pattern(self, incsearch#convert(pattern), offset) + return self._build_search_cmd(p) + endif +endfunction + +" @return search cmd +function! s:cli._build_search_cmd(pattern) abort + let op = (self._mode == 'no') ? v:operator + \ : s:U.is_visual(self._mode) ? 'gv' + \ : '' + let zv = (&foldopen =~# '\vsearch|all' && self._mode !=# 'no' ? 'zv' : '') + " NOTE: + " Should I consider o_v, o_V, and o_CTRL-V cases and do not + " ? exists for flexible v:count with using s:cli._vcount1, + " but, if you do not move the cursor while incremental searching, + " there are no need to use . + return printf("\\"%s%s%s%s%s\%s", + \ v:register, op, self._vcount1, self._base_key, a:pattern, zv) +endfunction + +function! s:cli._call_execute_event(...) abort + let view = get(a:, 1, winsaveview()) + try + call winrestview(self._w) + call self.callevent('on_execute_pre') + finally + call winrestview(view) + endtry + call self.callevent('on_execute') +endfunction + +let &cpo = s:save_cpo +unlet s:save_cpo +" __END__ +" vim: expandtab softtabstop=2 shiftwidth=2 foldmethod=marker diff --git a/autoload/incsearch/over/modules/exit.vim b/autoload/incsearch/over/modules/exit.vim new file mode 100644 index 0000000..a191e1a --- /dev/null +++ b/autoload/incsearch/over/modules/exit.vim @@ -0,0 +1,33 @@ +"============================================================================= +" FILE: autoload/incsearch/over/modules/exit.vim +" AUTHOR: haya14busa +" License: MIT license +"============================================================================= +scriptencoding utf-8 +let s:save_cpo = &cpo +set cpo&vim + +" NOTE: +" in {rhs} wil be remapped even after exiting vital-over command line +" interface, so do not use (exit) +" See also s:cli.keymapping() +let s:incsearch_exit = { +\ 'name' : 'IncsearchExit', +\ 'exit_code' : 0 +\} +function! s:incsearch_exit.on_char_pre(cmdline) abort + if a:cmdline.is_input("\") + \ || a:cmdline.is_input("\") + call a:cmdline.setchar('') + call a:cmdline.exit(self.exit_code) + endif +endfunction + +function! incsearch#over#modules#exit#make() abort + return deepcopy(s:incsearch_exit) +endfunction + +let &cpo = s:save_cpo +unlet s:save_cpo +" __END__ +" vim: expandtab softtabstop=2 shiftwidth=2 foldmethod=marker diff --git a/autoload/incsearch/over/modules/incsearch.vim b/autoload/incsearch/over/modules/incsearch.vim new file mode 100644 index 0000000..d21ece9 --- /dev/null +++ b/autoload/incsearch/over/modules/incsearch.vim @@ -0,0 +1,284 @@ +"============================================================================= +" FILE: autoload/incsearch/over/modules/incsearch.vim +" AUTHOR: haya14busa +" License: MIT license +"============================================================================= +scriptencoding utf-8 +let s:save_cpo = &cpo +set cpo&vim + +let s:TRUE = !0 +let s:FALSE = 0 +let s:DIRECTION = { 'forward': 1, 'backward': 0 } " see :h v:searchforward + + +let s:hi = g:incsearch#highlight#_hi +let s:U = incsearch#util#import() + +let s:inc = { +\ "name" : "incsearch", +\} + +" NOTE: for InsertRegister handling +function! s:inc.priority(event) abort + return a:event is# 'on_char' ? 10 : 0 +endfunction + +function! s:inc.on_enter(cmdline) abort + nohlsearch " disable previous highlight + let a:cmdline._w = winsaveview() + let hgm = incsearch#highlight#hgm() + let c = hgm.cursor + call s:hi.add(c.group, c.group, '\%#', c.priority) + call incsearch#highlight#update() + + " XXX: Manipulate search history for magic option + " In the first place, I want to omit magic flag when histadd(), but + " when returning cmd as expr mapping and feedkeys() cannot handle it, so + " remove no user intended magic flag at on_enter. + " Maybe I can also handle it with autocmd, should I use autocmd instead? + let hist = histget('/', -1) + if len(hist) > 2 && hist[:1] ==# incsearch#magic() + call histdel('/', -1) + call histadd('/', hist[2:]) + endif +endfunction + +function! s:inc.on_leave(cmdline) abort + call s:hi.disable_all() + call s:hi.delete_all() + " redraw: hide pseud-cursor + redraw " need to redraw for handling non- mappings + if a:cmdline.getline() ==# '' + echo '' + else + echo a:cmdline.get_prompt() . a:cmdline.getline() + endif + " NOTE: + " push rest of keymappings with feedkeys() + " FIXME: assume 'noremap' but it should take care wheter or not the + " mappings should be remapped or not + if a:cmdline.input_key_stack_string() != '' + call feedkeys(a:cmdline.input_key_stack_string(), 'n') + endif +endfunction + +" Avoid search-related error while incremental searching +function! s:on_searching(func, ...) abort + try + return call(a:func, a:000) + catch /E16:/ " E16: Invalid range (with /\_[a- ) + catch /E33:/ " E33: No previous substitute regular expression + catch /E53:/ " E53: Unmatched %( + catch /E54:/ + catch /E55:/ + catch /E62:/ " E62: Nested \= (with /a\=\=) + catch /E63:/ " E63: invalid use of \_ + catch /E64:/ " E64: \@ follows nothing + catch /E65:/ " E65: Illegal back reference + catch /E66:/ " E66: \z( not allowed here + catch /E67:/ " E67: \z1 et al. not allowed here + catch /E68:/ " E68: Invalid character after \z (with /\za & re=1) + catch /E69:/ " E69: Missing ] after \%[ + catch /E70:/ " E70: Empty \%[] + catch /E71:/ " E71: Invalid character after \% + catch /E554:/ + catch /E678:/ " E678: Invalid character after \%[dxouU] + catch /E864:/ " E864: \%#= can only be followed by 0, 1, or 2. The + " automatic engine will be used + catch /E865:/ " E865: (NFA) Regexp end encountered prematurely + catch /E866:/ " E866: (NFA regexp) Misplaced @ + catch /E867:/ " E867: (NFA) Unknown operator + catch /E869:/ " E869: (NFA) Unknown operator '\@m + catch /E870:/ " E870: (NFA regexp) Error reading repetition limits + catch /E871:/ " E871: (NFA regexp) Can't have a multi follow a multi ! + catch /E874:/ " E874: (NFA) Could not pop the stack ! (with \&) + catch /E877:/ " E877: (NFA regexp) Invalid character class: 109 + catch /E888:/ " E888: (NFA regexp) cannot repeat (with /\ze*) + call s:hi.disable_all() + catch + echohl ErrorMsg | echom v:throwpoint . " " . v:exception | echohl None + endtry +endfunction + +function! s:on_char_pre(cmdline) abort + " NOTE: + " `:call a:cmdline.setchar('')` as soon as possible! + let [pattern, offset] = incsearch#cli_parse_pattern(a:cmdline) + + " Interactive :h last-pattern if pattern is empty + if ( a:cmdline.is_input("(incsearch-next)") + \ || a:cmdline.is_input("(incsearch-prev)") + \ ) && empty(pattern) + call a:cmdline.setchar('') + " Use history instead of @/ to work with magic option and converter + call a:cmdline.setline(histget('/', -1) . (empty(offset) ? '' : a:cmdline._base_key) . offset) + " Just insert last-pattern and do not count up, but the incsearch-prev + " should move the cursor to reversed directly, so do not return if the + " command is prev + if a:cmdline.is_input("(incsearch-next)") | return | endif + endif + + if a:cmdline.is_input("(incsearch-next)") + call a:cmdline.setchar('') + if a:cmdline._flag ==# 'n' " exit stay mode + let a:cmdline._flag = '' + else + let a:cmdline._vcount1 += 1 + endif + elseif a:cmdline.is_input("(incsearch-prev)") + call a:cmdline.setchar('') + if a:cmdline._flag ==# 'n' " exit stay mode + let a:cmdline._flag = '' + endif + let a:cmdline._vcount1 -= 1 + elseif (a:cmdline.is_input("(incsearch-scroll-f)") + \ && (a:cmdline._flag ==# '' || a:cmdline._flag ==# 'n')) + \ || (a:cmdline.is_input("(incsearch-scroll-b)") && a:cmdline._flag ==# 'b') + call a:cmdline.setchar('') + if a:cmdline._flag ==# 'n' | let a:cmdline._flag = '' | endif + let pos_expr = a:cmdline.is_input("(incsearch-scroll-f)") ? 'w$' : 'w0' + let to_col = a:cmdline.is_input("(incsearch-scroll-f)") + \ ? s:U.get_max_col(pos_expr) : 1 + let [from, to] = [getpos('.')[1:2], [line(pos_expr), to_col]] + let cnt = s:U.count_pattern(pattern, from, to) + let a:cmdline._vcount1 += cnt + elseif (a:cmdline.is_input("(incsearch-scroll-b)") + \ && (a:cmdline._flag ==# '' || a:cmdline._flag ==# 'n')) + \ || (a:cmdline.is_input("(incsearch-scroll-f)") && a:cmdline._flag ==# 'b') + call a:cmdline.setchar('') + if a:cmdline._flag ==# 'n' + let a:cmdline._flag = '' + let a:cmdline._vcount1 -= 1 + endif + let pos_expr = a:cmdline.is_input("(incsearch-scroll-f)") ? 'w$' : 'w0' + let to_col = a:cmdline.is_input("(incsearch-scroll-f)") + \ ? s:U.get_max_col(pos_expr) : 1 + let [from, to] = [getpos('.')[1:2], [line(pos_expr), to_col]] + let cnt = s:U.count_pattern(pattern, from, to) + let a:cmdline._vcount1 -= cnt + endif + + " Handle nowrapscan: + " if you `:set nowrapscan`, you can't move to the reversed direction + if !&wrapscan && ( + \ a:cmdline.is_input("(incsearch-next)") + \ || a:cmdline.is_input("(incsearch-prev)") + \ || a:cmdline.is_input("(incsearch-scroll-f)") + \ || a:cmdline.is_input("(incsearch-scroll-b)") + \ ) + if a:cmdline._vcount1 < 1 + let a:cmdline._vcount1 = 1 + else + call a:cmdline.setchar('') + let [from, to] = [[a:cmdline._w.lnum, a:cmdline._w.col + 1], + \ a:cmdline._flag !=# 'b' + \ ? [line('$'), s:U.get_max_col('$')] + \ : [1, 1] + \ ] + let max_cnt = s:U.count_pattern(pattern, from, to, s:TRUE) + let a:cmdline._vcount1 = min([max_cnt, a:cmdline._vcount1]) + endif + endif + if &wrapscan && a:cmdline._vcount1 < 1 + let a:cmdline._vcount1 += s:U.count_pattern(pattern) + endif +endfunction + +function! s:on_char(cmdline) abort + let [raw_pattern, offset] = incsearch#cli_parse_pattern(a:cmdline) + + if raw_pattern ==# '' + call s:hi.disable_all() + nohlsearch + return + endif + + " For InsertRegister + if a:cmdline.get_tap_key() ==# "\" + let p = a:cmdline.getpos() + " Remove `"` + let raw_pattern = raw_pattern[:p-1] . raw_pattern[p+1:] + let w = winsaveview() + call cursor(line('.'), col('.') + len(a:cmdline.backward_word())) + call a:cmdline.get_module('InsertRegister').reset() + call winrestview(w) + endif + + let pattern = incsearch#convert(raw_pattern) + + " Improved Incremental cursor move! + call s:move_cursor(a:cmdline, pattern, offset) + + " Improved Incremental highlighing! + " case: because matchadd() doesn't handle 'ignorecase' nor 'smartcase' + let case = incsearch#detect_case(raw_pattern) + let should_separate = g:incsearch#separate_highlight && a:cmdline._flag !=# 'n' + call incsearch#highlight#incremental_highlight( + \ pattern . case, + \ should_separate, + \ a:cmdline._direction, + \ [a:cmdline._w.lnum, a:cmdline._w.col]) + + " functional `normal! zz` after scroll for mappings + if ( a:cmdline.is_input("(incsearch-scroll-f)") + \ || a:cmdline.is_input("(incsearch-scroll-b)")) + call winrestview({'topline': max([1, line('.') - winheight(0) / 2])}) + endif +endfunction + +" Caveat: It handle :h last-pattern, so be careful if you want to pass empty +" string as a pattern +function! s:move_cursor(cli, pattern, ...) abort + let offset = get(a:, 1, '') + if a:cli._flag ==# 'n' " skip if stay mode + return + endif + call winrestview(a:cli._w) + " pseud-move cursor position: this is restored afterward if called by + " mappings + if a:cli._is_expr + for _ in range(a:cli._vcount1) + " NOTE: This cannot handle {offset} for cursor position + call search(a:pattern, a:cli._flag) + endfor + else + " More precise cursor position while searching + " Caveat: + " This block contains `normal`, please make sure mappings + " doesn't reach this block + let is_visual_mode = s:U.is_visual(mode(1)) + let cmd = incsearch#with_ignore_foldopen( + \ function('incsearch#build_search_cmd'), + \ a:cli, 'n', incsearch#combine_pattern(a:cli, a:pattern, offset)) + " NOTE: + " :silent! + " Shut up errors! because this is just for the cursor emulation + " while searching + silent! call incsearch#execute_search(cmd) + if is_visual_mode + let w = winsaveview() + normal! gv + call winrestview(w) + call incsearch#highlight#emulate_visual_highlight() + endif + endif +endfunction + +function! s:inc.on_char_pre(cmdline) abort + call s:on_searching(function('s:on_char_pre'), a:cmdline) +endfunction + +function! s:inc.on_char(cmdline) abort + call s:on_searching(function('s:on_char'), a:cmdline) +endfunction + +function! incsearch#over#modules#incsearch#make() abort + return deepcopy(s:inc) +endfunction + + +let &cpo = s:save_cpo +unlet s:save_cpo +" __END__ +" vim: expandtab softtabstop=2 shiftwidth=2 foldmethod=marker diff --git a/autoload/incsearch/over/modules/module_management.vim b/autoload/incsearch/over/modules/module_management.vim new file mode 100644 index 0000000..fb968a3 --- /dev/null +++ b/autoload/incsearch/over/modules/module_management.vim @@ -0,0 +1,50 @@ +"============================================================================= +" FILE: autoload/incsearch/over/modules/module_management.vim +" AUTHOR: haya14busa +" License: MIT license +"============================================================================= +scriptencoding utf-8 +let s:save_cpo = &cpo +set cpo&vim + +let s:module_management = { +\ 'name' : 'IncsearchModuleManagement', +\ 'modules' : [ ] +\} + +function! s:module_management.on_enter(cmdline) abort + if !exists('s:default_backward_word') + let s:default_backward_word = a:cmdline.backward_word + endif + for module in self.modules + if has_key(module, '_condition') && ! module._condition() + call a:cmdline.disconnect(module.name) + if module.name ==# 'IgnoreRegexpBackwardWord' + function! a:cmdline.backward_word(...) abort + return call(s:default_backward_word, a:000, self) + endfunction + endif + elseif empty(a:cmdline.get_module(module.name)) + call a:cmdline.connect(module) + if has_key(module, 'on_enter') + call module.on_enter(a:cmdline) + endif + endif + endfor +endfunction + +function! s:module_management.priority(event) abort + " NOTE: to overwrite backward_word() with default function + return a:event ==# 'on_enter' ? 5 : 0 +endfunction + +function! incsearch#over#modules#module_management#make(modules) abort + let m = deepcopy(s:module_management) + let m.modules = a:modules + return m +endfunction + +let &cpo = s:save_cpo +unlet s:save_cpo +" __END__ +" vim: expandtab softtabstop=2 shiftwidth=2 foldmethod=marker diff --git a/autoload/incsearch/over/modules/pattern_saver.vim b/autoload/incsearch/over/modules/pattern_saver.vim new file mode 100644 index 0000000..a5ba74f --- /dev/null +++ b/autoload/incsearch/over/modules/pattern_saver.vim @@ -0,0 +1,48 @@ +"============================================================================= +" FILE: autoload/incsearch/over/modules/pattern_saver.vim +" AUTHOR: haya14busa +" License: MIT license +" @vimlint(EVL103, 1, a:cmdline) +"============================================================================= +scriptencoding utf-8 +let s:save_cpo = &cpo +set cpo&vim + +let s:pattern_saver = { +\ 'name' : 'PatternSaver', +\ 'pattern' : '', +\ 'hlsearch' : &hlsearch +\} + +function! s:pattern_saver.on_enter(cmdline) abort + if ! g:incsearch#no_inc_hlsearch + let self.pattern = @/ + let self.hlsearch = &hlsearch + if exists('v:hlsearch') + let self.vhlsearch = v:hlsearch + endif + set hlsearch | nohlsearch + endif +endfunction + +function! s:pattern_saver.on_leave(cmdline) abort + if ! g:incsearch#no_inc_hlsearch + let is_cancel = a:cmdline.exit_code() + if is_cancel + let @/ = self.pattern + endif + let &hlsearch = self.hlsearch + if exists('v:hlsearch') + let v:hlsearch = self.vhlsearch + endif + endif +endfunction + +function! incsearch#over#modules#pattern_saver#make() abort + return deepcopy(s:pattern_saver) +endfunction + +let &cpo = s:save_cpo +unlet s:save_cpo +" __END__ +" vim: expandtab softtabstop=2 shiftwidth=2 foldmethod=marker diff --git a/autoload/incsearch/util.vim b/autoload/incsearch/util.vim index 37e1f98..b35e712 100644 --- a/autoload/incsearch/util.vim +++ b/autoload/incsearch/util.vim @@ -106,18 +106,22 @@ endfunction " parameter: pattern, from, to function! s:count_pattern(pattern, ...) abort let w = winsaveview() - let [from, to] = s:sort_pos([ + 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, 'cW') - while (pos != [0, 0] && s:is_pos_less_equal(pos, to)) + 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, 'W') + let pos = searchpos(a:pattern, base_flag) endwhile finally call winrestview(w) @@ -126,6 +130,7 @@ function! s:count_pattern(pattern, ...) abort 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 diff --git a/autoload/vital/_incsearch.vim b/autoload/vital/_incsearch.vim index 2723144..7f81595 100644 --- a/autoload/vital/_incsearch.vim +++ b/autoload/vital/_incsearch.vim @@ -5,6 +5,12 @@ let s:self_file = expand('') let s:globpath_third_arg = v:version > 702 || v:version == 702 && has('patch51') let s:loaded = {} +let s:cache_module_path = {} +let s:cache_sid = {} + +let s:_vital_files_cache_runtimepath = '' +let s:_vital_files_cache = [] +let s:_unify_path_cache = {} function! s:import(name, ...) abort let target = {} @@ -35,12 +41,13 @@ function! s:load(...) dict abort let [name; as] = type(arg) == type([]) ? arg[: 1] : [arg, arg] let target = split(join(as, ''), '\W\+') let dict = self - while 1 <= len(target) + let dict_type = type({}) + while !empty(target) let ns = remove(target, 0) if !has_key(dict, ns) let dict[ns] = {} endif - if type(dict[ns]) == type({}) + if type(dict[ns]) == dict_type let dict = dict[ns] else unlet dict @@ -58,6 +65,8 @@ endfunction function! s:unload() abort let s:loaded = {} + let s:cache_sid = {} + let s:cache_module_path = {} endfunction function! s:exists(name) abort @@ -118,6 +127,10 @@ function! s:_import(name) abort endfunction function! s:_get_module_path(name) abort + let key = a:name . '_' + if has_key(s:cache_module_path, key) + return s:cache_module_path[key] + endif if s:_is_absolute_path(a:name) && filereadable(a:name) return a:name endif @@ -131,16 +144,22 @@ function! s:_get_module_path(name) abort call filter(paths, 'filereadable(expand(v:val, 1))') let path = get(paths, 0, '') - return path !=# '' ? path : '' + let s:cache_module_path[key] = path + return path endfunction function! s:_get_sid_by_script(path) abort + if has_key(s:cache_sid, a:path) + return s:cache_sid[a:path] + endif + let path = s:_unify_path(a:path) for line in filter(split(s:_redir('scriptnames'), "\n"), \ 'stridx(v:val, s:self_version) > 0') let list = matchlist(line, '^\s*\(\d\+\):\s\+\(.\+\)\s*$') if !empty(list) && s:_unify_path(list[2]) ==# path - return list[1] - 0 + let s:cache_sid[a:path] = list[1] - 0 + return s:cache_sid[a:path] endif endfor return 0 @@ -154,7 +173,6 @@ endfunction if filereadable(expand(':r') . '.VIM') " resolve() is slow, so we cache results. - let s:_unify_path_cache = {} " Note: On windows, vim can't expand path names from 8.3 formats. " So if getting full path via and $HOME was set as 8.3 format, " vital load duplicated scripts. Below's :~ avoid this issue. @@ -183,8 +201,6 @@ else endfunction endif -let s:_vital_files_cache_runtimepath = '' -let s:_vital_files_cache = [] function! s:_vital_files(pattern) abort if s:_vital_files_cache_runtimepath !=# &runtimepath let path = printf('autoload/vital/%s/**/*.vim', s:self_version) diff --git a/autoload/vital/_incsearch/Gift.vim b/autoload/vital/_incsearch/Gift.vim index 50999f2..2219cfe 100644 --- a/autoload/vital/_incsearch/Gift.vim +++ b/autoload/vital/_incsearch/Gift.vim @@ -87,8 +87,12 @@ function! s:find(expr) endfunction -function! s:find_by(func) - return filter(s:tabpagewinnr_list(), "a:func(s:bufnr([v:val[0], v:val[1]), v:val[0], v:val[1])") +function! s:find_by(expr) + if type(a:expr) == type(function("tr")) + return filter(s:tabpagewinnr_list(), "a:expr(s:bufnr([v:val[0], v:val[1]]), v:val[0], v:val[1])") + else + return s:find(a:expr) + endif endfunction diff --git a/autoload/vital/_incsearch/Gift/Tabpage.vim b/autoload/vital/_incsearch/Gift/Tabpage.vim index 35888d4..d27414c 100644 --- a/autoload/vital/_incsearch/Gift/Tabpage.vim +++ b/autoload/vital/_incsearch/Gift/Tabpage.vim @@ -4,6 +4,9 @@ set cpo&vim let s:prefix = expand(":p:h:h:t") +function! s:set_prefix(prefix) + let s:prefix = a:prefix +endfunction let s:uniq_counter = 0 diff --git a/autoload/vital/_incsearch/Gift/Window.vim b/autoload/vital/_incsearch/Gift/Window.vim index 6f3aa0b..600f835 100644 --- a/autoload/vital/_incsearch/Gift/Window.vim +++ b/autoload/vital/_incsearch/Gift/Window.vim @@ -21,7 +21,6 @@ function! s:set_prefix(prefix) let s:prefix = a:prefix endfunction - function! s:flatten(list) return eval(join(a:list, "+")) endfunction @@ -32,7 +31,9 @@ function! s:tabpagewinnr_list() endfunction -let s:uniq_counter = 0 +if !exists("s:uniq_counter") + let s:uniq_counter = 0 +endif function! s:make_uniq_nr() let s:uniq_counter += 1 return s:uniq_counter diff --git a/autoload/vital/incsearch.vital b/autoload/vital/incsearch.vital index 78030c7..16994b7 100644 --- a/autoload/vital/incsearch.vital +++ b/autoload/vital/incsearch.vital @@ -1,5 +1,5 @@ incsearch -1c73bcc +386a15e Over.Commandline Coaster.Highlight diff --git a/doc/incsearch.txt b/doc/incsearch.txt index f192f77..d518aa6 100644 --- a/doc/incsearch.txt +++ b/doc/incsearch.txt @@ -1,7 +1,7 @@ *incsearch.txt* Incrementally highlight all pattern matches Author : haya14busa -Version : 1.1.0 +Version : 1.2.0 License : MIT license {{{ Copyright (c) 2014-2015 haya14busa @@ -454,6 +454,13 @@ pattern *incsearch-config-pattern* Example: > noremap / incsearch#go({'pattern': histget('/', -1)}) +prompt *incsearch-config-prompt* + Prompt string. + Type: |string| + Default: same as |incsearch-config-command| ('/' or '?') + Example: > + noremap / incsearch#go({'prompt': '>>>'}) + modules *incsearch-config-modules* Additional |Vital.Over.Commandline-modules| to connect. Type: |list| of |Vital.Over.Commandline-modules| @@ -490,6 +497,16 @@ Version 2.0(?) Roadmap~ 3. More configurable options - e.g. option for prompt format including right prompt feature +1.2.0 2015-06-06 + 1. Now, fix unexpected key input with |feedkeys()| by auto-nohlsearch + feature (|incsearch-additional-usage|) #82 + 2. Avoid Unneeded loading for mappings like |(incsearch-nohl)| + related with #81 + 3. Fix |nowrapscan| handling #83 + 4. Add |incsearch-config-prompt| option + 5. auto-nohlsearch on |CursorMoved| after |InsertLeave| related with #80 + 6. Some refactoring + 1.1.0 2015-05-03 1. Add experimental API |incsearch-api|. - Now you can pass a default pattern |incsearch-config-pattern| @@ -499,7 +516,6 @@ Version 2.0(?) Roadmap~ - You can pass additional vital-over module to connect |incsearch-config-modules| - 1.0.3 2015-05-03 1. Fix 'hlsearch' handling #74 @@ -513,7 +529,6 @@ Version 2.0(?) Roadmap~ 5. Fix handling for mappings #51 6. Fix flicker at entering incsearch's commands #73 - 1.0.1 2015-01-04 1. Fix keymapping escape 2. Fix |last-pattern| with |g:incsearch#magic| diff --git a/plugin/incsearch.vim b/plugin/incsearch.vim index a59f504..81ed75f 100644 --- a/plugin/incsearch.vim +++ b/plugin/incsearch.vim @@ -38,6 +38,7 @@ let s:save_cpo = &cpo set cpo&vim " }}} +" : do not show command in command line noremap (incsearch-forward) incsearch#go({'command': '/'}) noremap (incsearch-backward) incsearch#go({'command': '?'}) noremap (incsearch-stay) incsearch#go({'command': '/', 'is_stay': 1}) @@ -49,9 +50,9 @@ noremap (incsearch-stay) incsearch#go({'command': '/', " - Make sure calling this mapping __before__ moving commands " e.g. `(incsearch-nohl)n` works but `n(incsearch-nohl)` doesn't " work -noremap (incsearch-nohl) incsearch#auto_nohlsearch(1) -noremap (incsearch-nohl0) incsearch#auto_nohlsearch(0) -noremap (incsearch-nohl2) incsearch#auto_nohlsearch(2) +noremap (incsearch-nohl) incsearch#autocmd#auto_nohlsearch(1) +noremap (incsearch-nohl0) incsearch#autocmd#auto_nohlsearch(0) +noremap (incsearch-nohl2) incsearch#autocmd#auto_nohlsearch(2) map (incsearch-nohl-n) (incsearch-nohl)(_incsearch-n) diff --git a/test/autonohlsearch.vim b/test/autonohlsearch.vim index 5f3b325..0114cd2 100644 --- a/test/autonohlsearch.vim +++ b/test/autonohlsearch.vim @@ -67,17 +67,17 @@ endfunction function! s:suite.function_works() let g:incsearch#auto_nohlsearch = 0 call s:assert.equals(exists('#incsearch-auto-nohlsearch#CursorMoved'), 0) - call incsearch#auto_nohlsearch(1) + call incsearch#autocmd#auto_nohlsearch(1) call s:assert.equals(exists('#incsearch-auto-nohlsearch#CursorMoved'), 0) let g:incsearch#auto_nohlsearch = 1 call s:assert.equals(exists('#incsearch-auto-nohlsearch#CursorMoved'), 0) - call incsearch#auto_nohlsearch(1) + call incsearch#autocmd#auto_nohlsearch(1) call s:assert.equals(exists('#incsearch-auto-nohlsearch#CursorMoved'), 1) endfunction function! s:suite.nolsearch_with_cursormove_0() call s:assert.equals(exists('#incsearch-auto-nohlsearch#CursorMoved'), 0) - call incsearch#auto_nohlsearch(0) + call incsearch#autocmd#auto_nohlsearch(0) call s:assert.equals(exists('#incsearch-auto-nohlsearch#CursorMoved'), 1) doautocmd CursorMoved call s:assert.equals(exists('#incsearch-auto-nohlsearch#CursorMoved'), 0) @@ -85,7 +85,7 @@ endfunction function! s:suite.nolsearch_with_cursormove_1() call s:assert.equals(exists('#incsearch-auto-nohlsearch#CursorMoved'), 0) - call incsearch#auto_nohlsearch(1) + call incsearch#autocmd#auto_nohlsearch(1) call s:assert.equals(exists('#incsearch-auto-nohlsearch#CursorMoved'), 1) doautocmd CursorMoved call s:assert.equals(exists('#incsearch-auto-nohlsearch#CursorMoved'), 1) @@ -95,7 +95,7 @@ endfunction function! s:suite.nolsearch_with_cursormove_2() call s:assert.equals(exists('#incsearch-auto-nohlsearch#CursorMoved'), 0) - call incsearch#auto_nohlsearch(2) + call incsearch#autocmd#auto_nohlsearch(2) call s:assert.equals(exists('#incsearch-auto-nohlsearch#CursorMoved'), 1) doautocmd CursorMoved call s:assert.equals(exists('#incsearch-auto-nohlsearch#CursorMoved'), 1) @@ -105,14 +105,24 @@ function! s:suite.nolsearch_with_cursormove_2() call s:assert.equals(exists('#incsearch-auto-nohlsearch#CursorMoved'), 0) endfunction -" This breaks dot repeat >< -" function! s:suite.nolsearch_with_insert_enter() -" call s:assert.equals(exists('#incsearch-auto-nohlsearch#CursorMoved'), 0) -" call incsearch#auto_nohlsearch(10) -" call s:assert.equals(exists('#incsearch-auto-nohlsearch#CursorMoved'), 1) -" doautocmd InsertEnter -" call s:assert.equals(exists('#incsearch-auto-nohlsearch#CursorMoved'), 0) -" endfunction +function! s:suite.nolsearch_with_insert_enter() + call s:assert.equals(exists('#incsearch-auto-nohlsearch#CursorMoved'), 0) + call incsearch#autocmd#auto_nohlsearch(10) + call s:assert.equals(exists('#incsearch-auto-nohlsearch#CursorMoved'), 1) + call s:assert.equals(exists('#incsearch-auto-nohlsearch#InsertEnter'), 1) + call s:assert.equals(exists('#incsearch-auto-nohlsearch#InsertLeave'), 0, 'do not set InsertLeave until InsertEnter') + doautocmd InsertEnter + call s:assert.equals(exists('#incsearch-auto-nohlsearch#CursorMoved'), 0) + call s:assert.equals(exists('#incsearch-auto-nohlsearch-on-insert-leave#InsertLeave'), 1) + doautocmd InsertLeave + call s:assert.equals(exists('#incsearch-auto-nohlsearch#CursorMoved'), 1, 'trigger auto nohlsearch again') + call s:assert.equals(exists('#incsearch-auto-nohlsearch-on-insert-leave#InsertLeave'), 0, 'remove insert leave') + doautocmd CursorMoved + call s:assert.equals(exists('#incsearch-auto-nohlsearch#CursorMoved'), 1) + doautocmd CursorMoved + call s:assert.equals(exists('#incsearch-auto-nohlsearch#CursorMoved'), 0) + call s:assert.equals(exists('#incsearch-auto-nohlsearch-on-insert-leave#InsertLeave'), 0) +endfunction function! s:suite.work_with_search() for key in ['/', '?', 'g/'] diff --git a/test/default_settings.vim b/test/default_settings.vim index d289201..c28ba18 100644 --- a/test/default_settings.vim +++ b/test/default_settings.vim @@ -26,8 +26,8 @@ function! s:suite.mappings() call s:assert.equals(maparg('(incsearch-backward)', 'nvo'), "incsearch#go({'command': '?'})") call s:assert.equals(maparg('(incsearch-stay)' , 'nvo'), "incsearch#go({'command': '/', 'is_stay': 1})") " Additional: - call s:assert.equals(maparg('(incsearch-nohl)', 'nvo'), 'incsearch#auto_nohlsearch(1)') - call s:assert.equals(maparg('(incsearch-nohl0)', 'nvo'), 'incsearch#auto_nohlsearch(0)') + call s:assert.equals(maparg('(incsearch-nohl)', 'nvo'), 'incsearch#autocmd#auto_nohlsearch(1)') + call s:assert.equals(maparg('(incsearch-nohl0)', 'nvo'), 'incsearch#autocmd#auto_nohlsearch(0)') call s:assert.equals(maparg('(incsearch-nohl-n)' , 'nvo'), '(incsearch-nohl)(_incsearch-n)') call s:assert.equals(maparg('(incsearch-nohl-N)' , 'nvo'), '(incsearch-nohl)(_incsearch-N)') call s:assert.equals(maparg('(incsearch-nohl-*)' , 'nvo'), '(incsearch-nohl)(_incsearch-*)') diff --git a/test/incremental_next_prev.vim b/test/incremental_next_prev.vim index f642a3a..4615226 100644 --- a/test/incremental_next_prev.vim +++ b/test/incremental_next_prev.vim @@ -38,6 +38,7 @@ endfunction function! s:suite.before_each() :1 + set wrapscan& endfunction function! s:suite.after() @@ -123,3 +124,24 @@ function! s:suite.inc_last_pattern_reset() call s:assert.equals(getline('.'), s:line_texts[0]) endfunction +function! s:suite.match_at_cursor_pos_with_nowrapscan() abort + set nowrapscan + :3 + exec "normal" "/pattern\\\\" + call s:assert.equals(getline('.'), s:line_texts[5]) + set wrapscan + :3 + exec "normal" "/pattern\\\\" + call s:assert.equals(getline('.'), s:line_texts[1]) +endfunction + +function! s:suite.match_at_cursor_pos_with_nowrapscan_backward() abort + set nowrapscan + :5 + exec "normal" "?pattern\\\\" + call s:assert.equals(getline('.'), s:line_texts[1]) + set wrapscan + :5 + exec "normal" "?pattern\\\\" + call s:assert.equals(getline('.'), s:line_texts[5]) +endfunction diff --git a/test/scroll.vim b/test/scroll.vim index 7052b56..069ce50 100644 --- a/test/scroll.vim +++ b/test/scroll.vim @@ -82,10 +82,10 @@ function! s:suite.nowrapscan_scroll_reverse_move_cursor_to_the_last_pattern() call s:assert.equals(s:get_pos_char(), 'p') exec "normal /pattern\\zs\\d\\" call s:assert.not_equals(s:get_pos_char(), '3') - call s:assert.equals(s:get_pos_char(), '6') + call s:assert.equals(s:get_pos_char(), '4') normal! gg$ call s:assert.equals(s:get_pos_char(), '3') exec "normal ?pattern\\zs\\d\\" call s:assert.not_equals(s:get_pos_char(), '4') - call s:assert.equals(s:get_pos_char(), '1') + call s:assert.equals(s:get_pos_char(), '2') endfunction diff --git a/test/visual_behavior.vim b/test/visual_behavior.vim index 5a8bbce..49c24ee 100644 --- a/test/visual_behavior.vim +++ b/test/visual_behavior.vim @@ -45,13 +45,13 @@ endfunction function! s:suite.forward() call s:assert.equals(s:get_pos_char(), '1') - exec "normal" "v/2pattern\y" + exec "normal" "v/2pattern\" | normal! y call s:assert.equals(getreg(), "1pattern 2") normal! gg0 - exec "normal" "V/2pattern\y" + exec "normal" "V/2pattern\" | normal! y call s:assert.equals(getreg(), "1pattern 2pattern\n") normal! gg0 - exec "normal" "\/4pattern\y" + exec "normal" "\/4pattern\" | normal! y call s:assert.equals(getreg(), "1pattern 2\n3pattern 4") normal! gg0 exec "normal" "v/2pattern/e\" | normal! y @@ -66,7 +66,7 @@ function! s:suite.backward() exec "normal" "V?3pattern?e\" | normal! y call s:assert.equals(getreg(), "3pattern 4pattern\n") normal! G$ - exec "normal" "\?2pattern\y" + exec "normal" "\?2pattern\" | normal! y call s:assert.equals(getreg(), "2pattern\n4pattern") endfunction @@ -76,7 +76,7 @@ function! s:suite.stay() exec "normal" "vg/2pattern\" | normal! y call s:assert.equals(getreg(), '1') normal! gg0 - exec "normal" "vg/2pattern\\y" + exec "normal" "vg/2pattern\\" | normal! y call s:assert.equals(getreg(), '1pattern 2') normal! gg0 exec "normal" "veg/3pattern\" | normal! y