Skip to content

bug: cursor is reset after keymap function call #639

Closed

Description

@DavidGamba

Did you check the docs and existing issues?

  • I have read the docs
  • I have searched the existing issues

Neovim version (nvim -v)

v0.12.0-dev-683+g2b4c1127ad

Operating system/version

MacOS 15.5

Describe the bug

I am trying to set a function to manually Git Add and Git Reset the file under the cursor with a keymap.
The function is working but the cursor gets reset after I call the function to the top possition.

Calling :lua vim.fn.cursor(9,0) will work after my function call, so I think it is Oil the one resetting the cursor.

These are the functions in question:

		["<leader>ga"] = function()
			local cwd = oil.get_current_dir()
			local entry = oil.get_cursor_entry()
			if cwd and entry then
				local pos = vim.fn.getcurpos()
				local line = pos[2]
				local col = pos[3]
				vim.fn.jobstart({ "git", "add", string.format("%s/%s", cwd, entry.name) })
				oil.open(cwd)
				print("Cursor Position: Line " .. line .. ", Column " .. col)
				vim.fn.cursor(line, 0)
			end
		end,
		["<leader>gr"] = function()
			local cwd = oil.get_current_dir()
			local entry = oil.get_cursor_entry()
			if cwd and entry then
				local pos = vim.fn.getcurpos()
				local line = pos[2]
				local col = pos[3]
				vim.fn.jobstart({ "git", "reset", string.format("%s/%s", cwd, entry.name) })
				oil.open(cwd)
				print("Cursor Position: Line " .. line .. ", Column " .. col)
				vim.fn.cursor(line, 0)
			end
		end,
My full Config, though it repros with the minimal config so this is just noise.
local add = MiniDeps.add

add({
	source = "stevearc/oil.nvim",
	depends = { "nvim-tree/nvim-web-devicons" },
})

add({
	source = "refractalize/oil-git-status.nvim",
	depends = { "stevearc/oil.nvim" },
})

local oil = require("oil")

oil.setup({
	win_options = {
		signcolumn = "yes:2",
	},
	columns = { "icon" },
	keymaps = {
		-- Don't use motions I use for window management for splits
		["<C-h>"] = false,
		["<C-l>"] = false,
		-- Splits
		["<C-s>"] = { "actions.select", opts = { horizontal = true } },
		["<C-v>"] = { "actions.select", opts = { vertical = true } },
		["<leader>ga"] = function()
			local cwd = oil.get_current_dir()
			local entry = oil.get_cursor_entry()
			if cwd and entry then
				local pos = vim.fn.getcurpos()
				local line = pos[2]
				local col = pos[3]
				vim.fn.jobstart({ "git", "add", string.format("%s/%s", cwd, entry.name) })
				oil.open(cwd)
				print("Cursor Position: Line " .. line .. ", Column " .. col)
				vim.fn.cursor(line, 0)
			end
		end,
		["<leader>gr"] = function()
			local cwd = oil.get_current_dir()
			local entry = oil.get_cursor_entry()
			if cwd and entry then
				local pos = vim.fn.getcurpos()
				local line = pos[2]
				local col = pos[3]
				vim.fn.jobstart({ "git", "reset", string.format("%s/%s", cwd, entry.name) })
				oil.open(cwd)
				print("Cursor Position: Line " .. line .. ", Column " .. col)
				vim.fn.cursor(line, 0)
			end
		end,
	},
	view_options = {
		show_hidden = true,
	},
	-- EXPERIMENTAL support for performing file operations with git
	git = {
		-- Return true to automatically git add/mv/rm files
		add = function(path)
			return false
		end,
		mv = function(src_path, dest_path)
			return true
		end,
		rm = function(path)
			return false
		end,
	},
})

require("oil-git-status").setup()

-- Open parent directory in current window
vim.keymap.set("n", "-", "<CMD>Oil<CR>", { desc = "Open parent directory" })

-- Open parent directory in floating window
vim.keymap.set("n", "<space>-", require("oil").toggle_float, { desc = "Floating directory viewer" })

What is the severity of this bug?

tolerable (can work around it)

Steps To Reproduce

dir=$(mktemp -d)
cd $dir
touch a
nvim -u ~/oil-keymap-cursor/repro.lua
:Oil
# Scroll over to the file "a"
<space>ga
# At this point the cursor will jump to the top
# in another window you can confirm that the file was added to git
# Scroll over to the file "a"
<space>gr
# At this point the cursor will jump to the top
# in another window you can confirm that the file was reset from git

Expected Behavior

Since I set vim.fn.cursor() from within my keymap I expect the cursor to be retained.

Directory structure

a

Repro

-- save as repro.lua
-- run with nvim -u repro.lua
-- DO NOT change the paths
local root = vim.fn.fnamemodify("./.repro", ":p")

-- set stdpaths to use .repro
for _, name in ipairs({ "config", "data", "state", "runtime", "cache" }) do
	vim.env[("XDG_%s_HOME"):format(name:upper())] = root .. "/" .. name
end

-- bootstrap lazy
local lazypath = root .. "/plugins/lazy.nvim"
if not vim.loop.fs_stat(lazypath) then
	vim.fn.system({
		"git",
		"clone",
		"--filter=blob:none",
		"--single-branch",
		"https://github.com/folke/lazy.nvim.git",
		lazypath,
	})
end
vim.opt.runtimepath:prepend(lazypath)

-- install plugins
local plugins = {
	"folke/tokyonight.nvim",
	{
		"stevearc/oil.nvim",
		config = function()
			require("oil").setup({
				-- add any needed settings here
			})
		end,
	},
	-- add any other plugins here
}
require("lazy").setup(plugins, {
	root = root .. "/plugins",
})

vim.cmd.colorscheme("tokyonight")
-- add anything else here

vim.g.mapleader = " "

local oil = require("oil")

oil.setup({
	win_options = {
		signcolumn = "yes:2",
	},
	columns = { "icon" },
	keymaps = {
		-- Don't use motions I use for window management for splits
		["<C-h>"] = false,
		["<C-l>"] = false,
		-- Splits
		["<C-s>"] = { "actions.select", opts = { horizontal = true } },
		["<C-v>"] = { "actions.select", opts = { vertical = true } },
		["<leader>ga"] = function()
			local cwd = oil.get_current_dir()
			local entry = oil.get_cursor_entry()
			if cwd and entry then
				local pos = vim.fn.getcurpos()
				local line = pos[2]
				local col = pos[3]
				vim.fn.jobstart({ "git", "add", string.format("%s/%s", cwd, entry.name) })
				oil.open(cwd)
				print("Cursor Position: Line " .. line .. ", Column " .. col)
				vim.fn.cursor(line, 0)
			end
		end,
		["<leader>gr"] = function()
			local cwd = oil.get_current_dir()
			local entry = oil.get_cursor_entry()
			if cwd and entry then
				local pos = vim.fn.getcurpos()
				local line = pos[2]
				local col = pos[3]
				vim.fn.jobstart({ "git", "reset", string.format("%s/%s", cwd, entry.name) })
				oil.open(cwd)
				print("Cursor Position: Line " .. line .. ", Column " .. col)
				vim.fn.cursor(line, 0)
			end
		end,
	},
	view_options = {
		show_hidden = true,
	},
	-- EXPERIMENTAL support for performing file operations with git
	git = {
		-- Return true to automatically git add/mv/rm files
		add = function(path)
			return false
		end,
		mv = function(src_path, dest_path)
			return true
		end,
		rm = function(path)
			return false
		end,
	},
})

Did you check the bug with a clean config?

  • I have confirmed that the bug reproduces with nvim -u repro.lua using the repro.lua file above.

Activity

  1. restacksyj commented on Jan 26, 2026

    @restacksyj

    Hey @DavidGamba , I don't know if you are still facing the issue but I went through the issue and seems like not calling oil.open(cwd) after vim.fn.jobstart fixes the cursor issue. Since you call, oil.open(cwd) after that, it resets the cursor to the top.

    Let me know if this helps! For your reference

    ["<leader>ga"] = function()
    			local cwd = oil.get_current_dir()
    			local entry = oil.get_cursor_entry()
    			if cwd and entry then
    				local pos = vim.fn.getcurpos()
    				local line = pos[2]
    				local col = pos[3]
    				vim.fn.jobstart({ "git", "add", string.format("%s/%s", cwd, entry.name) })
    				-- oil.open(cwd) (commenting this line, fixes the cursor issue)
    				-- print("Cursor Position: Line " .. line .. ", Column " .. col)
    				-- vim.fn.cursor(line, 0)
    			end
    		end,
    
  2. DavidGamba commented on Jan 26, 2026

    @DavidGamba
    Author

    @restacksyj the reason I am doing oil.open is because oil doesn't refresh the status view after I do git add. Even when adding watch_for_changes = true.

    Also, triggering the actions.refresh keybind also sends me back to the top of the file.

    I want to be able to get immediate visual confirmation that the file was added to the list of staged files or removed from it.

  3. restacksyj commented on Jan 27, 2026

    @restacksyj

    @DavidGamba This is kinda workaround by changing your config a little, you can use this instead of just oil.open(cwd). The thing is oil takes precedence when we call oil.open(cwd) and while rendering the buffer, resets the cursor position. Let me know if this helps!

    https://asciinema.org/a/zKABCwNmdD9ysSBU

    vim.api.nvim_create_autocmd("User", {
      pattern = "OilEnter",
      once = true,
      callback = function()
        vim.schedule(function()
          vim.fn.cursor(line, 0)
        end)
      end,
    })
    oil.open(cwd)
  4. DavidGamba commented on Jan 27, 2026

    @DavidGamba
    Author

    Oh wow, that really did it! Thanks so much for going out of your way to help me!

    For the record, in case someone else runs into this, this is my config after the suggestion:

    config
    local add = MiniDeps.add
    
    add({
    	source = "stevearc/oil.nvim",
    	depends = { "nvim-tree/nvim-web-devicons" },
    })
    
    add({
    	source = "refractalize/oil-git-status.nvim",
    	depends = { "stevearc/oil.nvim" },
    })
    
    local oil = require("oil")
    
    oil.setup({
    	win_options = {
    		signcolumn = "yes:2",
    	},
    	columns = { "icon" },
    	keymaps = {
    		-- Show help with g?
    		["<esc>"] = { "actions.refresh" },
    		-- Don't use motions I use for window management for splits
    		["<C-h>"] = false,
    		["<C-l>"] = false,
    		-- Splits
    		["<C-s>"] = { "actions.select", opts = { horizontal = true } },
    		["<C-v>"] = { "actions.select", opts = { vertical = true } },
    		["<leader>ga"] = function()
    			local cwd = oil.get_current_dir()
    			local entry = oil.get_cursor_entry()
    			if cwd and entry then
    				local pos = vim.fn.getcurpos()
    				local line = pos[2]
    				local col = pos[3]
    				vim.fn.jobstart({ "git", "add", string.format("%s/%s", cwd, entry.name) })
    				vim.api.nvim_create_autocmd("User", {
    					pattern = "OilEnter",
    					once = true,
    					callback = function()
    						vim.schedule(function()
    							vim.fn.cursor(line, 0)
    						end)
    					end,
    				})
    				oil.open(cwd)
    			end
    		end,
    		["<leader>gr"] = function()
    			local cwd = oil.get_current_dir()
    			local entry = oil.get_cursor_entry()
    			if cwd and entry then
    				local pos = vim.fn.getcurpos()
    				local line = pos[2]
    				local col = pos[3]
    				vim.fn.jobstart({ "git", "reset", string.format("%s/%s", cwd, entry.name) })
    				vim.api.nvim_create_autocmd("User", {
    					pattern = "OilEnter",
    					once = true,
    					callback = function()
    						vim.schedule(function()
    							vim.fn.cursor(line, 0)
    						end)
    					end,
    				})
    				oil.open(cwd)
    			end
    		end,
    	},
    	view_options = {
    		show_hidden = true,
    	},
    	-- EXPERIMENTAL support for performing file operations with git
    	git = {
    		-- Return true to automatically git add/mv/rm files
    		add = function(path)
    			return false
    		end,
    		mv = function(src_path, dest_path)
    			return true
    		end,
    		rm = function(path)
    			return false
    		end,
    	},
    	watch_for_changes = true,
    })
    
    require("oil-git-status").setup()
    
    -- Open parent directory in current window
    vim.keymap.set("n", "-", "<CMD>Oil<CR>", { desc = "Open parent directory" })
    
    -- Open parent directory in floating window
    vim.keymap.set("n", "<space>-", require("oil").toggle_float, { desc = "Floating directory viewer" })
    
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions