What is wrong with this function?
-- execute commands without changing cursor position
function preserve(cmd)
local cmd = 'keepjumps keeppaterns ' .. cmd
local original_cursor = vim.fn.winsaveview()
vim.api.nvim_command(cmd)
vim.fn.winrestview(original_cursor)
end
If I try
preserve('%s/\s\+$//e')
Neovim complains:
E5112: Error while creating lua chunk: test.lua:8: invalid escape sequence near ‘’%s/’
Could be due to the backslash escaping the character, it will try to assert what \s
means, which doesn’t make sense if it’s not evaluated in a regex context. Not really valid syntax, or at least that’s my guess.
You want to preserve the backslash as well so it’s passed onto nvim_command
properly. Try to escape those backslashes as well, or wrap the string in double square brakets ([[ ]]
) if you don’t want to do the double backslashes.
-- Escape the backslahes
preserve('%s/\\s\\+$//ge')
-- OR use double square brackets
preserve([[%s/\s\+$//ge]])
1 Like
I have tested both options with no avail.
Hmm looks like I am also getting this when I try it out, seems like it doesn’t like how the string is injected into the function. I tried it by binding to a command and got the same error as you did, so all I did was wrap the line where I was defining the function with the double square brackets.
However, there is one more error you might end up, which I got as a Trailing Character
error, turns out you can’t just add an expression after calling commands, it’s best to execute
the command you want after calling keepjumps
and keeppatterns
. Here is what I ended up doing:
function _G.preserve(cmd)
cmd = string.format('keepjumps keeppatterns execute %q', cmd)
local original_cursor = vim.fn.winsaveview()
vim.api.nvim_command(cmd)
vim.fn.winrestview(original_cursor)
end
vim.cmd([[command! Preserve lua preserve('%s/\\s\\+$//ge')]])
Only line I modified is the first line of the function, I’ve added execute %q
, the %q
in this case is used by string.format()
which just adds quotes to the variable. So in that case the full cmd
string should be: keepjumps keeppaterns execute "%s/\s\+$//ge"
Basically, you want to execute the last function, this can take any vim Ex command so it should be fine but idk your use case for this besides trying to execute the expression of %s
.
1 Like
The actual idea for this is to wrap a bunch of solutions like:
- Removing trailing spaces
- Reindenting the code
- Deleting blank lines
Each one of these functions would be wrapped up into the preserve function. Heres my solution in pure vim:
command! Cls :call Preserve(':%s/\v\s+$//e')
cnoreabbrev cls Cls
cnoreabbrev StripTrailingSpace Cls
" https://stackoverflow.com/questions/4545275/
command! BufOnly silent! call Preserve("exec '%bd|e#|bd#'")
cnoreab bo BufOnly
command! -nargs=0 Reindent :call Preserve('exec "normal! gg=G"')
" join lines keeping cursor position
nnoremap J :call Preserve(':join')<CR>
nnoremap <Leader>J :call Preserve(':join!')<CR>
if !exists('*ChangeHeader')
" updates the file timestamp
fun! ChangeHeader() abort
if line('$')>=7
keepj keepp call Preserve(':1,7s/\v(Last (Change|Modified)|date):\s+\zs.*/\=strftime("%b %d, %Y - %H:%M")/ei')
endif
endfun
endif
if !exists('*Dos2unixFunction')
fun! Dos2unixFunction() abort
"call Preserve('%s/ $//ge')
call Preserve(":%s/\\%x0D$//e")
set ff=unix
set bomb
set encoding=utf-8
set fileencoding=utf-8
endfun
endif
com! Dos2Unix :call Dos2unixFunction()
cnoreabbrev dos2unix Dos2unix
cnoreabbrev d2u Dos2Unix
The actual preserve uses a try catch
if !exists('*Preserve')
function! Preserve(command)
try
let l:win_view = winsaveview()
"silent! keepjumps keeppatterns execute a:command
silent! execute 'keeppatterns keepjumps ' . a:command
finally
call winrestview(l:win_view)
endtry
endfunction
endif
I think many people can copy some of these ideas or improve them as well! Thanks!
I have also added “silent!” to the preserve function.
That’s probably because lua uses %
for patterns, see https://www.lua.org/pil/20.2.html. I assume it should be enough to replace %
by %%
in your code, but I can’t test right now.