Search code examples
neovim

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


Solution

  • 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