Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for start_mode #175

Merged
merged 11 commits into from
Nov 3, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,8 @@ require("dressing").setup({
-- Can be 'left', 'right', or 'center'
title_pos = "left",

-- When true, input will start in insert mode.
start_in_insert = true,
-- The initial mode when the window opens (insert|normal|visual|select).
start_mode = "insert",

-- These are passed to nvim_open_win
border = "rounded",
Expand Down
4 changes: 2 additions & 2 deletions doc/dressing.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ Configure dressing.nvim by calling the setup() function.
-- Can be 'left', 'right', or 'center'
title_pos = "left",

-- When true, input will start in insert mode.
start_in_insert = true,
-- The initial mode when the window opens (insert|normal|visual|select).
start_mode = "insert",

-- These are passed to nvim_open_win
border = "rounded",
Expand Down
4 changes: 2 additions & 2 deletions lua/dressing/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ local default_config = {
-- Can be 'left', 'right', or 'center'
title_pos = "left",

-- When true, input will start in insert mode.
start_in_insert = true,
-- The initial mode when the window opens (insert|normal|visual|select).
start_mode = "insert",

-- These are passed to nvim_open_win
border = "rounded",
Expand Down
64 changes: 48 additions & 16 deletions lua/dressing/input.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,26 @@ local patch = require("dressing.patch")
local util = require("dressing.util")
local M = {}

---@alias dressing.Mode "insert" | "visual" | "normal" | "select"

---@class (exact) dressing.InputContext
---@field opts? dressing.InputOptions
---@field on_confirm? fun(text?: string)
---@field winid? integer
---@field history_idx? integer
---@field history_tip? string
---@field restore_mode? dressing.Mode The mode to restore when the input window closes.

---@class (exact) dressing.InputConfig
---@field start_in_insert? boolean
MarcelRobitaille marked this conversation as resolved.
Show resolved Hide resolved
---@field start_mode? dressing.Mode
---@field enabled? boolean
---@field default_prompt? string
---@field win_options? table
---@field buf_options? table
---@field mappings? table
---@field trim_prompt? boolean
---@field prompt_align? string

---@class (exact) dressing.InputOptions
---@field prompt? string
Expand All @@ -26,7 +39,7 @@ local context = {
winid = nil,
history_idx = nil,
history_tip = nil,
start_in_insert = nil,
restore_mode = nil,
}

local keymaps = {
Expand Down Expand Up @@ -91,6 +104,19 @@ M.history_next = function()
end
end

---@param mode dressing.Mode
local function set_mode(mode)
if mode == "normal" then
vim.cmd("stopinsert")
elseif mode == "insert" then
vim.cmd("startinsert!")
elseif mode == "visual" then
vim.api.nvim_command("normal! vg_")
elseif mode == "select" then
-- TODO
end
end

local function close_completion_window()
if vim.fn.pumvisible() == 1 then
local escape_key = vim.api.nvim_replace_termcodes("<C-e>", true, false, true)
Expand All @@ -105,9 +131,8 @@ local function confirm(text)
close_completion_window()
local ctx = context
context = {}
if not ctx.start_in_insert then
vim.cmd("stopinsert")
end
set_mode(ctx.restore_mode)
MarcelRobitaille marked this conversation as resolved.
Show resolved Hide resolved

-- We have to wait briefly for the popup window to close (if present),
-- otherwise vim gets into a very weird and bad state. I was seeing text get
-- deleted from the buffer after the input window closes.
Expand Down Expand Up @@ -266,7 +291,7 @@ end
---@param prompt_lines string[]
---@param default? string
---@return integer
---@return boolean
---@return dressing.Mode?
local function create_or_update_win(config, prompt_lines, default)
local parent_win = 0
local winopt
Expand Down Expand Up @@ -326,18 +351,19 @@ local function create_or_update_win(config, prompt_lines, default)

winopt = config.override(winopt) or winopt

local winid, start_in_insert
local winid, restore_mode
-- If the floating win was already open
if win_conf then
-- Make sure the previous on_confirm callback is called with nil
vim.schedule(context.on_confirm)
vim.api.nvim_win_set_config(context.winid, winopt)
winid = context.winid
start_in_insert = context.start_in_insert
MarcelRobitaille marked this conversation as resolved.
Show resolved Hide resolved
restore_mode = context.restore_mode
else
start_in_insert = string.sub(vim.api.nvim_get_mode().mode, 1, 1) == "i"
local bufnr = vim.api.nvim_create_buf(false, true)
winid = vim.api.nvim_open_win(bufnr, true, winopt)
local mode_chr = string.sub(vim.api.nvim_get_mode().mode, 1, 1)
restore_mode = ({ i = "insert", n = "normal", v = "visual", s = "select" })[mode_chr]
MarcelRobitaille marked this conversation as resolved.
Show resolved Hide resolved
end

-- If the prompt is multiple lines, create another window for it
Expand Down Expand Up @@ -387,8 +413,7 @@ local function create_or_update_win(config, prompt_lines, default)
end

---@cast winid integer
---@cast start_in_insert boolean
return winid, start_in_insert
return winid, restore_mode
end

---@param opts string|dressing.InputOptions
Expand All @@ -401,22 +426,29 @@ local show_input = util.make_queued_async_fn(2, function(opts, on_confirm)
if type(opts) ~= "table" then
opts = { prompt = tostring(opts) }
end
local config = global_config.get_mod_config("input", opts)
local config = global_config.get_mod_config("input", opts) --[[@as dressing.InputConfig]]
if not config.enabled then
return patch.original_mods.input(opts, on_confirm)
end

local start_mode = config.start_mode

-- Support start_in_insert for backwards compatibility.
if config.start_in_insert ~= nil then
start_mode = config.start_in_insert and "insert" or "normal"
end

local prompt = opts.prompt or config.default_prompt
local prompt_lines = vim.split(prompt, "\n", { plain = true, trimempty = true })

-- Create or update the window
local winid, start_in_insert = create_or_update_win(config, prompt_lines, opts.default)
local winid, restore_mode = create_or_update_win(config, prompt_lines, opts.default)
context = {
winid = winid,
on_confirm = on_confirm,
opts = opts,
history_idx = nil,
start_in_insert = start_in_insert,
restore_mode = restore_mode,
}
for option, value in pairs(config.win_options) do
vim.api.nvim_set_option_value(option, value, { scope = "local", win = winid })
Expand Down Expand Up @@ -484,9 +516,9 @@ local show_input = util.make_queued_async_fn(2, function(opts, on_confirm)
callback = M.close,
})

if config.start_in_insert then
vim.cmd("startinsert!")
end
---@cast start_mode dressing.Mode
set_mode(start_mode)

close_completion_window()
apply_highlight()
end)
Expand Down
26 changes: 24 additions & 2 deletions tests/input_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ a.describe("input modal", function()
assert(ret == "", string.format("Got '%s' expected nil", ret))
end)

a.it("starts in normal mode when start_in_insert = false", function()
a.it("starts in normal mode when start_mode = 'normal'", function()
local orig_cmd = vim.cmd
local startinsert_called = false
vim.cmd = function(cmd)
Expand All @@ -91,7 +91,29 @@ a.describe("input modal", function()
orig_cmd(cmd)
end

require("dressing.config").input.start_in_insert = false
require("dressing.config").input.start_mode = "normal"
run_input({
"my text",
"<CR>",
}, {
after_fn = function()
vim.cmd = orig_cmd
end,
})
assert(not startinsert_called, "Got 'true' expected 'false'")
end)

a.it("is backwards compatible with start_in_insert = false", function()
local orig_cmd = vim.cmd
local startinsert_called = false
vim.cmd = function(cmd)
if cmd == "startinsert!" then
startinsert_called = true
end
orig_cmd(cmd)
end

require("dressing.config").input.start_mode = "normal"
run_input({
"my text",
"<CR>",
Expand Down
Loading