vim: execute normal search with highlight from vimrc - search

I am using the following to map * to search current selection with highlight
set hlsearch
vnoremap * :call VisualSelection('f')<CR>
vnoremap # :call VisualSelection('b')<CR>
function! VisualSelection(direction) range
let l:saved_reg = #"
execute "normal! vgvy"
let l:pattern = escape(#", '\\/.*$^~[]')
let l:pattern = substitute(l:pattern, "\n$", "", "")
if a:direction == 'b'
execute "normal ?" . l:pattern . "\<CR>"
elseif a:direction == 'gv'
call CmdLine("vimgrep " . '/'. l:pattern . '/' . ' **/*.')
elseif a:direction == 'replace'
call CmdLine("%s" . '/'. l:pattern . '/')
elseif a:direction == 'f'
execute "normal /" . l:pattern . "\<CR>"
endif
let #/ = l:pattern
let #" = l:saved_reg
endfunction
However, this script doesn't highlight the search result. When manually input the command in ex command mode: execute "normal /" . l:pattern . "\<CR>", however, I do get highlight.
Please let me know how to enable highlight from the vimrc script as well.

Related

Comment & Uncomment using same command - No plugin

I'm using a modified version of https://stackoverflow.com/a/1676672/618584
autocmd FileType php,styl,javascript let b:comment_leader = '// '
noremap <C-\> :<C-B>silent <C-E>s/^/<C-R>=escape(b:comment_leader,'\/')<CR>/<CR>:nohlsearch<CR>
"noremap <C-\> :<C-B>silent <C-E>s/^\V<C-R>=escape(b:comment_leader,'\/')<CR>//e<CR>:nohlsearch<CR>
I want to remove comments using the same command (see commented out line above). What I need to do is check if the line begins with '//' and if so, map the to removing the comment.
Any idea how to do this?
I was previously using tpope's commentary plugin, and to achieve what I wnt with that, I'd do:
" comments toggle
autocmd FileType php setlocal commentstring=\/\/\ %s
nmap <C-\> gcc
xmap <C-\> gcugin I'd do:
But again, I do not want to use a plugin because I only code in JS and PHP.
First, I would build abstractions via a custom function. Adding a custom command would make sense, too:
function! CommentToggle() range
silent execute a:firstline . ',' . a:lastline . 's/^/' . escape(b:comment_leader, '\/') . '/e'
"silent execute a:firstline . ',' . a:lastline . 's/^\V' . escape(b:comment_leader, '\/') . '//e'
endfunction
command! -range CT <line1>,<line2>call CommentToggle()|nohlsearch
noremap <C-\> :CT<CR>
With this, toggling just means checking (let's say the first line determines the on/off for all lines, i.e. you only operate on all-comments or nothing-commented) the line:
function! CommentToggle() range
if getline(a:firstline) =~ '^\V' . escape(b:comment_leader, '\')
silent execute a:firstline . ',' . a:lastline . 's/^\V' . escape(b:comment_leader, '\/') . '//e'
else
silent execute a:firstline . ',' . a:lastline . 's/^/' . escape(b:comment_leader, '\/') . '/e'
endif
endfunction

Vim - pass register to operator function

I'm trying to implement ChangePaste operator. It should replace text with the one from register.
It's working fine with motions, so I can use cp<motion> and the text will be replaced from default register.
Now I would like to be able to use it with different registers. I'm looking for information how to pass selected register to operator function. So, if one type "acpiw I would like the script to replace an inner word with register a content. Is that possible at all?
Code so far:
nmap <silent> cp :set opfunc=ChangePaste<CR>g#
function! ChangePaste(type, ...)
if a:0 " Invoked from Visual mode, use '< and '> marks.
silent exe "normal! `<" . a:type . "`>\"_c" . #"
elseif a:type == 'line'
silent exe "normal! '[V']\"_c" . #"
elseif a:type == 'block'
silent exe "normal! `[\<C-V>`]\"_c" . #"
else
silent exe "normal! `[v`]\"_c" . #"
endif
endfunction
Edit:
Solution using v:register and buffer variable:
nmap <silent> cp :let b:changepaste_buffer = v:register<cr>:set opfunc=ChangePaste<CR>g#
function! ChangePaste(type, ...)
if a:0 " Invoked from Visual mode, use '< and '> marks.
silent exe "normal! `<" . a:type . "`>\"_c" . getreg(b:changepaste_register)
elseif a:type == 'line'
silent exe "normal! '[V']\"_c" . getreg(b:changepaste_register)
elseif a:type == 'block'
silent exe "normal! `[\<C-V>`]\"_c" . getreg(b:changepaste_register)
else
silent exe "normal! `[v`]\"_c" . getreg(b:changepaste_register)
endif
endfunction
As mentioned by #romainl in the comments, you can access the v:register variable in your function.
No need to even save it as a buffer variable.

How to create a directory search and replace with vimscript and fzf

I'm trying to create a directory search and replace function in vimscript using fzf. The place where I block is when trying to use Alt-a fzf binding to select the whole list. I'm not even sure if that is possible given fzf is an external process but I may be wrong.
Here's my current function.
function! CWDSearchAndReplace()
" Try to get word under cursor else prompt user for a word
let wordToReplace = expand("<cword>")
if wordToReplace == ""
call inputsave()
let wordToReplace = input("Replace: ")
call inputrestore()
endif
" Prompt for replacement
call inputsave()
let replacement = input("Replace \"" . wordToReplace . "\" with: ")
call inputrestore()
execute "Ag " . wordToReplace
" =====> Here I'd like to execute Alt-a followed by <CR>
execute "cdo %s//" . replacement . "/gc"
end function
Thanks for your help!
As pointed out by the creator of fzf.vim, there is no need to use fzf here, one can simply use ag.
function! s:ag_to_qf(line)
let parts = split(a:line, ':')
echo parts
return {'filename': parts[0], 'lnum': parts[1], 'col': parts[2],
\ 'text': join(parts[3:], ':')}
endfunction
function! AgToQF(query)
call setqflist(map(systemlist('ag --column '.a:query), 's:ag_to_qf(v:val)'))
endfunction
function! CWDSearchAndReplace()
let wordUnderCursor = expand("<cword>")
call inputsave()
let wordToReplace = input("Replace : ", wordUnderCursor)
call inputrestore()
call inputsave()
let replacement = input("Replace \"" . wordUnderCursor . "\" with: ")
call inputrestore()
call AgToQF(wordUnderCursor)
execute "cdo %s/" . wordToReplace . "/" . replacement ."/gc"
endfunction
nnoremap <leader>r :call FileSearchAndReplace()<CR>

Vim. set command line from a function

I'm trying to write a function that replaces text in all buffers. So I call Ack to search all the matches and next step I want to set into Quickfix command line this code
:QuickFixDoAll %s/foo/boo/gc
Seems like I can only call 'exec' function which runs this command immediately and there is no ablility to edit it or cancel at all
I also tried "input" function to read user input but got this error at runtime
not an editor command
Any ideas?
.vimrc:
function! ReplaceInFiles(o, n)
exec "Ack '" . a:o . "'"
exec "QuickFixDoAll %s/" . a:o . "/" . a:n . "/gc"
endfunction
" QuickFixDoAll
function! QuickFixDoAll(command)
if empty(getqflist())
return
endif
let s:prev_val = ""
for d in getqflist()
let s:curr_val = bufname(d.bufnr)
if (s:curr_val != s:prev_val)
exec "edit " . s:curr_val
exec a:command
endif
let s:prev_val = s:curr_val
endfor
exec "quit"
endfunction
command! -nargs=+ QuickFixDoAll call QuickFixDoAll(<f-args>)
Using input()
This queries both values interactively:
function! ReplaceInFiles()
let l:o = input('search? ')
let l:n = input('replace? ')
exec "Ack '" . l:o . "'"
exec "QuickFixDoAll %s/" . l:o . "/" . l:n . "/gc"
endfunction
nnoremap <Leader>r :call ReplaceInFiles()<CR>
Incomplete mapping
nnoremap <Leader>r :let o = ''<Bar>exec "Ack '" . o . "'"<Bar>exec "QuickFixDoAll %s/" . o . "//gc"<Home><Right><Right><Right><Right><Right><Right><Right><Right><Right>
This one puts the cursor on the right spot for the search. As this value is used twice (Ack and QuickFixDoAll), it is assigned to a variable. After that, move to the end of the command and fill in the replacement in between the //gc.
Custom parsing
The most comfortable option would be a custom command :AckAndSubstAll/search/replacement/. For that, you'd need to parse the two parts in the custom command (like :s does). You could do that with matchstr(), or use ingo#cmdargs#substitute#Parse() from my ingo-library plugin.
First use vim-qargs to copy all files from the quickfix window into Vim's arglist by calling :Qargs.
Then run your replace on all arguments in the arglist by doing :argdo %s/search/replace/gc

Can we create a new op-pending command in Vim?

Does anyone know if it's possible to create a new op-pending command?
e.g. I'd like to replace a sequence such as vf(r<space>w with ,cf(. Specifically here, the idea is to "clear" the text from the cursor position up to and including the next opening brace and then put the cursor at the beginning of the next word.
I may just be missing something in the help files (or my Google-fu is off today), so a pointer to the right place would be much appreciated.
You want to use :set opfunc and g#. The documentation is pretty good, :h g#.
nnoremap <silent> ,c :set opfunc=Clearing<cr>g#
vnoremap <silent> ,c :<c-u>set opfunc=Clearing<cr>g#
function! Clearing(type, ...)
let sel_save = &selection
let &selection = "inclusive"
let reg_save = ##
if a:0 " Invoked from Visual mode, use '< and '> marks.
silent exe "normal! `<" . a:type . "`>r "
elseif a:type == 'line'
silent exe "normal! '[V']r "
elseif a:type == 'block'
silent exe "normal! `[\<C-V>`]r "
else
silent exe "normal! `[v`]r "
endif
norm! `]w
let &selection = sel_save
let ## = reg_save
endfunction
I think :h map-operator is what you're looking for.

Resources