Mohamed Irfan
Mohamed Irfan

Reputation: 155

Restore cursor position after :r

I have made a custom command, and I tried to use mark to save the cursor position. But the mark is set in the position where the file is inserted on the 6th line(using the r command).

vim.cmd [[ command! -nargs=1 Include call feedkeys("mx") | 6r <args> | call feedkeys("`x")]]

I think 6r <args> is getting executed before feedkeys("mx").Is there any way we can fix this?? or if there are other ways to restore cursor position

Upvotes: 3

Views: 2997

Answers (3)

Anton Maminov
Anton Maminov

Reputation: 532

This is converted from Vim defaults - https://github.com/vim/vim/blob/master/runtime/defaults.vim#L105-L116

-- When editing a file, always jump to the last known cursor position.
-- Don't do it when the position is invalid, when inside an event handler,
-- for a commit or rebase message
-- (likely a different one than last time), and when using xxd(1) to filter
-- and edit binary files (it transforms input files back and forth, causing
-- them to have dual nature, so to speak)
function RestoreCursorPosition()
  local line = vim.fn.line("'\"")
  if line > 1 and line <= vim.fn.line("$") and vim.bo.filetype ~= 'commit' and vim.fn.index({'xxd', 'gitrebase'}, vim.bo.filetype) == -1 then
    vim.cmd('normal! g`"')
  end
end

if vim.fn.has("autocmd") then
  vim.cmd([[
    autocmd BufReadPost * lua RestoreCursorPosition()
  ]])
end

Upvotes: 1

run_the_race
run_the_race

Reputation: 2318

For some read if you call vim.fn.winsaveview() immediately, it seems like it happens before the do things and the position is restored, well that's my theory. To get winrestview to work, you have to delay it, either by using feed keys (yuck) or call it deferred with a small (e.g. 0) delay asynchronously:

vim.g.cursor_position = vim.fn.winsaveview()
-- do things
-- Now we restore the cursor position with a delay of 0ms
vim.defer_fn(function() vim.fn.winrestview(vim.g.cursor_position) end, 0)

Deferred keys version (not recommended, press <ctrl+v>then escape to insert a escape at the start of the string, and a CR at the end of the string (those sepcial chars were removed by stackoverflow):

vim.fn.feedkeys([[:lua vim.fn.winrestview(vim.g.cursor_position)]])

Upvotes: 0

SergioAraujo
SergioAraujo

Reputation: 11800

I have a "preserve cursor position" function in lua (neovim), it is in my utils.lua file, it goes like this:

M.preserve = function(arguments)
    local arguments = string.format("keepjumps keeppatterns execute %q", arguments)
    -- local original_cursor = vim.fn.winsaveview()
    local line, col = unpack(vim.api.nvim_win_get_cursor(0))
    vim.api.nvim_command(arguments)
    local lastline = vim.fn.line("$")
    -- vim.fn.winrestview(original_cursor)
    if line > lastline then
        line = lastline
    end
    vim.api.nvim_win_set_cursor({ 0 }, { line, col })
end

The above function encapsules any give command, for example, if I want to reindent the whole file I create a Reindent command:

vim.cmd([[command! Reindent lua require('utils').preserve("sil keepj normal! gg=G")]])

And run:

:Reindent

To remove blank spaces at the end of any line:

vim.cmd([[cnoreab cls Cls]])
vim.cmd([[command! Cls lua require("utils").preserve('%s/\\s\\+$//ge')]])

Vimscript version of it:

" preserve function
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

In my case I have another function to squeeze blank lines (if I have more than one consecutive blank like they become one), so, I have this function:

M.squeeze_blank_lines = function()
    -- references: https://vi.stackexchange.com/posts/26304/revisions
    if vim.bo.binary == false and vim.opt.filetype:get() ~= "diff" then
        local old_query = vim.fn.getreg("/") -- save search register
        M.preserve("sil! 1,.s/^\\n\\{2,}/\\r/gn") -- set current search count number
        local result = vim.fn.searchcount({ maxcount = 1000, timeout = 500 }).current
        local line, col = unpack(vim.api.nvim_win_get_cursor(0))
        M.preserve("sil! keepp keepj %s/^\\n\\{2,}/\\r/ge")
        M.preserve("sil! keepp keepj %s/\\v($\\n\\s*)+%$/\\r/e")
        if result > 0 then
            vim.api.nvim_win_set_cursor({ 0 }, { (line - result), col })
        end
        vim.fn.setreg("/", old_query) -- restore search register
    end
end

Then I have the consecutive blank lines remove but the cursor remains where it is:

:nnoremap <leader>d :lua require('utils').squeeze_blank_lines()<cr>

Or if you are, by any chance using init.lua

-- map helper
local function map(mode, lhs, rhs, opts)
    local options = { noremap = true }
    if opts then
        options = vim.tbl_extend("force", options, opts)
    end
    vim.api.nvim_set_keymap(mode, lhs, rhs, options)
end

map("n", "<leader>d", '<cmd>lua require("utils").squeeze_blank_lines()<cr>')

I hope these ideas can help you to figure out a solution to your problem

A final tip: If you are using the proposed utils.lua you have to insert at the beginning of it:

local M = {}

and at the end of it:

return M

Upvotes: 3

Related Questions