From 16f3d7bfa9182b71b6c3f26efe6eaa84f34edbdb Mon Sep 17 00:00:00 2001 From: Barrett Ruth Date: Fri, 20 Feb 2026 16:28:26 -0500 Subject: [PATCH] fix: cancel visual/operator-pending mode instead of closing buffer Problem: when close() is triggered from visual or operator-pending mode (e.g. pressing q after d in an oil buffer), the buffer closes instead of canceling the pending operation. This happens because keymaps without an explicit mode default to "" which covers normal, visual, select, and operator-pending modes. Solution: check the current mode at the top of close() and send to cancel the mode instead of proceeding with the close. The check covers all visual modes (v, V, CTRL-V), select modes (s, S, CTRL-S), and operator-pending submodes (no, nov, noV, noCTRL-V). Based on: stevearc/oil.nvim#495 --- lua/oil/init.lua | 5 +++++ tests/close_spec.lua | 49 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 tests/close_spec.lua diff --git a/lua/oil/init.lua b/lua/oil/init.lua index 67d462f..ba64860 100644 --- a/lua/oil/init.lua +++ b/lua/oil/init.lua @@ -410,6 +410,11 @@ end ---@param opts? oil.CloseOpts M.close = function(opts) opts = opts or {} + local mode = vim.api.nvim_get_mode().mode + if mode:match("^[vVsS\22\19]") or mode:match("^no") then + vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes("", true, true, true), "n", false) + return + end -- If we're in a floating oil window, close it and try to restore focus to the original window if vim.w.is_oil_win then local original_winid = vim.w.oil_original_win diff --git a/tests/close_spec.lua b/tests/close_spec.lua new file mode 100644 index 0000000..05f1d1a --- /dev/null +++ b/tests/close_spec.lua @@ -0,0 +1,49 @@ +require("plenary.async").tests.add_to_env() +local oil = require("oil") +local test_util = require("tests.test_util") + +a.describe("close", function() + a.before_each(function() + test_util.reset_editor() + end) + a.after_each(function() + test_util.reset_editor() + end) + + a.it("does not close buffer from visual mode", function() + oil.open() + test_util.wait_for_autocmd({ "User", pattern = "OilEnter" }) + assert.equals("oil", vim.bo.filetype) + test_util.feedkeys({ "V" }, 10) + oil.close() + assert.equals("oil", vim.bo.filetype) + test_util.feedkeys({ "" }, 10) + end) + + a.it("does not close buffer from operator-pending mode", function() + oil.open() + test_util.wait_for_autocmd({ "User", pattern = "OilEnter" }) + assert.equals("oil", vim.bo.filetype) + vim.api.nvim_feedkeys("d", "n", false) + a.util.sleep(20) + local mode = vim.api.nvim_get_mode().mode + if mode:match("^no") then + oil.close() + assert.equals("oil", vim.bo.filetype) + end + vim.api.nvim_feedkeys( + vim.api.nvim_replace_termcodes("", true, true, true), + "n", + false + ) + a.util.sleep(20) + end) + + a.it("closes buffer from normal mode", function() + oil.open() + test_util.wait_for_autocmd({ "User", pattern = "OilEnter" }) + assert.equals("oil", vim.bo.filetype) + oil.close() + assert.not_equals("oil", vim.bo.filetype) + end) +end)