Simple replace in motion ... [Discussion]

The problem:

-- Turn this..
M.nnoremap('gr',"<cmd>lua require('lspsaga.rename').rename()<CR>","rename var")
-- Too this effortlessly...                                        ^^^^^^^^^^^
M.nnoremap('gr',"<cmd>lua require('lspsaga.rename').rename()<CR>","rename_var")
--                                                                ^^^^^^^^^^^

Overview

Today I wanted to organize my init.lua and I got stuck on this.
I had a bunch of this remaps , and all i wanted to do was try out changing the space with an underscore.

  • At first i thought of visually selecting it and then just running s// on the selection. As i have previously seen that `<,`> is character wise selection ( :h `< ), but it seems i was wrong.
  • Then I thought of using regex to substitute it but that is way to difficult for just this little trial of mine.
  • I also remembered that Kakoune had a feature where you could pass text to GNU commands and return them. So i went ahead and tried searching for doing something similar in nvim. I found nothing. I knew that with read and ! i could get something similar but it was a bit hacky…
    :read !echo "rename var" | tr " " "_"
    I had to yi" then when writing the command Ctrl-R to paste it and echoed it to my command.
    It worked ok but it printed to new line
    I searched the manuals but found nothing to help me put it in current line or in a register
    Edit: I read manuals on filters and saw that you could apply it in a range or motion, i tried it but it seems that it does it line wise anw…
    Trying to !i" in
NVIM v0.6.0-dev+274-g6188926e0
Build type: RelWithDebInfo
LuaJIT 2.0.5
Compilation: /usr/bin/cc -march=x86-64 -mtune=generic -O2 -pipe -fno-plt -fexceptions     -Wp,-D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security     -fstack-clash-protection -fcf-protection -Wp,-U_FORTIFY_SOURCE -Wp,-D_FORTIFY_SOURCE=1 -DNVIM_TS_HAS_SET_MATCH_LIMIT -O2 -g -Og -g -Wall -Wextra -pedantic -Wno-unused-parameter -Wstrict-prototypes -std=gnu99 -Wshadow -Wconversion -Wmissing-prototypes -Wimplicit-fallthrough -Wvla -fstack-protector-strong -fno-common -fdiagnostics-color=auto -DINCLUDE_GENERATED_DECLARATIONS -D_GNU_SOURCE -DNVIM_MSGPACK_HAS_FLOAT32 -DNVIM_UNIBI_HAS_VAR_FROM -DMIN_LOG_LEVEL=3 -I/home/inferno/.cache/paru/clone/neovim-git/src/build/config -I/home/inferno/.cache/paru/clone/neovim-git/src/neovim-git/src -I/usr/include -I/home/inferno/.cache/paru/clone/neovim-git/src/build/src/nvim/auto -I/home/inferno/.cache/paru/clone/neovim-git/src/build/include
Compiled by inferno

Features: +acl +iconv +tui
See ":help feature-compile"

   system vimrc file: "$VIM/sysinit.vim"
  fall-back for $VIM: "/usr/share/nvim"

results in a line wise edit…

  • After all this i gave macros a try but with the number of spaces not being fixed eg. ...show line diagnostics (3 spaces) i could make it work without spamming the macro manually.

I’ve been using nvim for almost a year now , which means a still don’t know much . Maybe some of you are gonna say “What?? you only have to do this and this…” which i will be happy to hear cause i love to learn more stuff about nvim, and was mainly the purpose of this post.
Is there a plugin to help with these kind of tasks? …Maybe multiple cursors could solve my problem?
it would be nice to try implementing GNU commands more in to nvim through a plugin or natively through nvim APIs. I’ve never made an nvim plugin but i think expanding on these while learning to make a plugin would be fun!

There are many simple ways to achieve this, no need to reach for plugins.

s/me var/me_var on the selection would work totally fine ('<,'> specifies a range, but the substitution command will run on each line that is inside that range.)

Applying norm! $F r_ on the selection is a similarly easy alternative. (That is, go to the end of the line, find the first space backwards, and replace it with an underscore - and automate this on the given line range. The ! is not necessary, it ignores any remaps, ensuring that F or r will be interpreted as the native commands.) :norm can frequently be used as a simpler alternative to macros (:h :norm).

As i mentioned , this wont work , see below, these are the mapping ( assume now that i want to change the ‘_’ back to a space. The substitution wont work cause i want to do this on multiple lines. Also , unless there is a away to run a macro recursively ,a macro won’t work because the number of ‘_’ isn’t fixed.

M.nnoremap('ca',"<cmd>lua require('lspsaga.codeaction').code_action()<CR>","code_action")
M.vnoremap('ca',":<C-U>lua require('lspsaga.codeaction').range_code_action()<CR>","range_code_action")
M.nnoremap('K',"<cmd>lua require('lspsaga.hover').render_hover_doc()<CR>","hover_doc")
M.nnoremap('<C-f>',"<cmd>lua require('lspsaga.action').smart_scroll_with_saga(1)<CR>","scroll_docs_up")
M.nnoremap('<C-b>',"<cmd>lua require('lspsaga.action').smart_scroll_with_saga(-1)<CR>","scroll_docs_down")
M.nnoremap('gs',"<cmd>lua require('lspsaga.signaturehelp').signature_help()<CR>","signature_help")
M.nnoremap('gr',"<cmd>lua require('lspsaga.rename').rename()<CR>","rename")
M.nnoremap('gd', "<cmd>lua require('lspsaga.provider').preview_definition()<CR>","preview_definition")
M.nnoremap('gd',"<cmd>lua require('lspsaga.diagnostic').show_line_diagnostics()<CR>","show_line_diagnostics")
M.nnoremap('gd',"<cmd>lua require('lspsaga.diagnostic').show_cursor_diagnostics()<CR>","show_cursor_diagnostics")
M.nnoremap('[e',"<cmd>lua require('lspsaga.diagnostic').lsp_jump_diagnostic_prev()<CR>","lsp_jump_diagnostic_prev")
M.nnoremap(']e',"<cmd>lua require('lspsaga.diagnostic').lsp_jump_diagnostic_next()<CR>","lsp_jump_diagnostic_next")
M.nnoremap('<A-d>',"<cmd>lua require('lspsaga.floaterm').open_float_terminal()<CR>","open_float_terminal") -- or open_float_terminal('lazygit')<CR>
M.tnoremap('<A-d>',"<C-\\><C-n>:lua require('lspsaga.floaterm').close_float_terminal()<CR>","close_float_terminal")

This is the solution i found:

 :'<,'>s/\("\w\+")\).*$/\=substitute(submatch(0),'_',' ','g')/

Explanation:

  • :’<,’>
    Visualy select the area
  • s/ {regex find what you want}/
  • =substitute(submatch(0),‘OLD’,‘NEW’,‘g’)/

Right, sorry, I was reading superficially, the crucial detail of the number of spaces not being fixed escaped my attention. I don’t think there is a significantly simpler (native) way to do this than your proposed solution.

You can record a macro though (and then run that on the given line range), that might be more intuitive: $F"hvT":s/ /_/g<cr>.

  • Find and select the area inside the last pair of double quotes.
  • Run a substitute command on it.

With targets.vim, it’s even simpler: $vi":s/ /_/g<cr>. (At the end of the line it will just find and select inside the last "..." with vi".)

Macro solution :smiley:

# First record the following macro
# NOTE: the ^M is insterted by first pressing <C-v> and then <CR> which is enter
$F";vt":s/\%V /_/g^M
# Now to run in all the lines just find them with a pattern
# I recorded my macro in "w"
:%g/^M.nnor/normal @w