othree / eregex.vim

Perl/Ruby style regexp notation for Vim
http://www.vim.org/scripts/script.php?script_id=3282
214 stars 18 forks source link

support incsearch #29

Open ZSaberLv0 opened 1 year ago

ZSaberLv0 commented 1 year ago

add incsearch support, for :M/ :S/ :G/ :V/ command, and :M? backward search series

limitations:

huyz commented 1 year ago

This is awesome. Thanks @ZSaberLv0 !

huyz commented 1 year ago

And thanks @othree as well of course :)

othree commented 1 year ago

@ZSaberLv0 is this ready for review? I see you kept pushing new commits when you created the PR. So I am waiting.

ZSaberLv0 commented 1 year ago

it's ready for review, I have tested for these days, no further issue found for my case

huyz commented 1 year ago

Question: I always prefer case-insensitive matching for / but the safer case-sensitive matching for :s (and :S). So what I've been doing for years is to add \c by default:

nnoremap / /\c
nnoremap ? ?\c

In the case of eregex, I now do the same with \c:

nnoremap <expr> /  ":<C-U>".v:count1."M/\\c"
nnoremap <expr> ? ":<C-U>".v:count1."M?\\c"

The problem is that incsearch will highlight the entire file because the pattern already has \c and vim doesn't realize that this is equivalent to just empty string and thus shouldn't be highlighted. Is there a workaround to avoid the momentary whole-file highlighting?

I guess another solution for me would be if eregex could support separate eregex_force_case_in_search and eregex_force_case_in_substitute. Then I wouldn't need the \c. But I don't know if this would cause problems (e.g., if I reuse the search history)

Not a big deal, either way.

ZSaberLv0 commented 1 year ago

maybe we can add user defined pattern filter around here: incsearch.vim#L53

let Fn_filter = get(b:, 'Fn_eregex_incsearch_filter', get(g:, 'Fn_eregex_incsearch_filter', ''))
if !empty(Fn_filter) && Fn_filter(cmd['pattern'])
    if exists('s:patternSaved')
        let @/ = s:patternSaved
    endif
    if exists('s:stateSaved')
        call winrestview(s:stateSaved)
    endif
    redraw!
    return
endif
huyz commented 1 year ago

@ZSaberLv0 A user-defined filter would be great for me.

huyz commented 1 year ago

Great, it works fine for me now if I do /. But for some reason I can't get incsearch when I do ?.

This is my .vimrc right now:

" We want `:S` substitutions to be case-sensitive.
let g:eregex_force_case = 1

" Disable the default mappings because we have to do the mapping ourselves
let g:eregex_default_enable = 0
let g:eregex_forward_delim = '/'
let g:eregex_backward_delim = '?'

" Avoid total-file highlight because of the additional `\c`
let g:Fn_eregex_incsearch_filter = {x -> x == '\c'}

" Code from eregex.vim
let s:enable = 0
function! EregexToggle(...)
    let silent = 0
    if exists('a:1') && a:1
        let silent = 1
    endif
    if s:enable == 0
        exec 'nnoremap <expr> '.g:eregex_forward_delim.' ":<C-U>".v:count1."M/\\c"'
        exec 'nnoremap <expr> '.g:eregex_backward_delim.' ":<C-U>".v:count1."M?\\c"'
        if silent != 1
            echo "eregex.vim key mapping enabled"
        endif
    else
        exec 'nunmap '.g:eregex_forward_delim
        exec 'nunmap '.g:eregex_backward_delim
        if silent != 1
            echo "eregex.vim key mapping disabled"
        endif
    endif
    let s:enable = 1 - s:enable
endfun
call MapKey('<M-t>/', '<Cmd>call EregexToggle()<CR>')

" Start out enabled
call EregexToggle(v:true)
huyz commented 1 year ago

There might be another issue. I have:

set ignorecase smartcase
let g:eregex_force_case = 1

So if I type :%S/case then CASE should not be highlighted by incsearch, but it is highlighted.

huyz commented 1 year ago

Thanks a lot for the quick turnaround! Looks good so far.

huyz commented 1 year ago

I think it's a good time to review and merge :)

ZSaberLv0 commented 1 year ago

add support for custom cmdparser, a typical config for dkprice/vim-easygrep

function! Easygrep_incsearch(cmdline)
    let cmd = substitute(a:cmdline, ' .*', '', '')
    if cmd == 'Grep'
        return {
                    \   'method' : cmd,
                    \   'delim' : '/',
                    \   'modifiers' : '',
                    \   'pattern' : substitute(a:cmdline, '^ *[^ ]\+ \+', '', ''),
                    \ }
    elseif cmd == 'Replace'
        let slashToken = nr2char(127)
        let cmdline = substitute(a:cmdline, '^ *[^ ]\+ \+', '', '')
        let cmdline = substitute(cmdline, '\\/', slashToken, 'g')
        let items = split(cmdline, '/')
        if empty(items)
            return {}
        endif
        return {
                    \   'method' : cmd,
                    \   'delim' : '/',
                    \   'modifiers' : '',
                    \   'pattern' : substitute(items[0], slashToken, '\\/', 'g'),
                    \ }
    else
        return {}
    endif
endfunction
let g:eregex_incsearch_custom_cmdparser = {
           \   'easygrep' : function('Easygrep_incsearch'),
           \ }