diff --git a/autoload/dressing.vim b/autoload/dressing.vim index e229e85..ef89d13 100644 --- a/autoload/dressing.vim +++ b/autoload/dressing.vim @@ -1,9 +1,9 @@ func! dressing#prompt_confirm(text) abort - call v:lua.dressing_prompt_confirm(a:text) + call luaeval("require('dressing.input').confirm(_A)", a:text) endfunc func! dressing#prompt_cancel() abort - lua dressing_prompt_confirm() + lua require('dressing.input').confirm() endfunc function! dressing#fzf_run(labels, options, window) abort diff --git a/lua/dressing/input.lua b/lua/dressing/input.lua index 5f653d0..810d82a 100644 --- a/lua/dressing/input.lua +++ b/lua/dressing/input.lua @@ -1,97 +1,121 @@ local global_config = require("dressing.config") local util = require("dressing.util") +local M = {} -local function clear_callbacks() - _G.dressing_prompt_confirm = function() end - _G.dressing_prompt_hl = function() end +local context = { + opts = nil, + on_confirm = nil, + winid = nil, +} + +M.confirm = function(text) + if not context.on_confirm then + return + end + local ctx = context + context = {} + vim.api.nvim_win_close(ctx.winid, true) + vim.cmd("stopinsert") + -- stopinsert will move the cursor back 1. We need to move it forward 1 to + -- put it in the place you were when you opened the modal. + local cursor = vim.api.nvim_win_get_cursor(0) + cursor[2] = cursor[2] + 1 + vim.api.nvim_win_set_cursor(0, cursor) + vim.schedule_wrap(ctx.on_confirm)(text) end -clear_callbacks() - -return function(opts, on_confirm) - vim.validate({ - on_confirm = { on_confirm, "function", false }, - }) - - opts = opts or {} - if type(opts) ~= "table" then - opts = { prompt = tostring(opts) } +M.highlight = function() + if not context.opts or not context.opts.highlight then + return end - local config = global_config.get_mod_config("input", opts) + local bufnr = vim.api.nvim_win_get_buf(context.winid) + local opts = context.opts + local text = vim.api.nvim_buf_get_lines(bufnr, 0, 1, true)[1] + local ns = vim.api.nvim_create_namespace("DressingHl") + local highlights + if type(opts.highlight) == "function" then + highlights = opts.highlight(text) + else + highlights = vim.fn[opts.highlight](text) + end + vim.api.nvim_buf_clear_namespace(bufnr, ns, 0, -1) + for _, highlight in ipairs(highlights) do + local start = highlight[1] + local stop = highlight[2] + local group = highlight[3] + vim.api.nvim_buf_add_highlight(bufnr, ns, group, 0, start, stop) + end +end - local start_in_insert = vim.api.nvim_get_mode().mode == "i" - local bufnr = vim.api.nvim_create_buf(false, true) - local prompt = opts.prompt or config.default_prompt - local width = util.calculate_width(config.prefer_width + vim.api.nvim_strwidth(prompt), config) - local winopt = { - relative = config.relative, - anchor = config.anchor, - row = config.row, - col = config.col, - border = config.border, - width = width, - height = 1, - zindex = 150, - style = "minimal", - } - local winnr = vim.api.nvim_open_win(bufnr, true, winopt) - vim.api.nvim_buf_set_option(bufnr, "buftype", "prompt") - vim.api.nvim_buf_set_option(bufnr, "swapfile", false) - vim.api.nvim_buf_set_option(bufnr, "bufhidden", "wipe") - local keyopts = { silent = true, noremap = true } - vim.api.nvim_buf_set_keymap( - bufnr, - "i", - "", - "lua dressing_prompt_confirm()", - keyopts - ) - vim.fn.prompt_setprompt(bufnr, prompt) - -- Would prefer to use v:lua directly here, but it doesn't work :( - vim.fn.prompt_setcallback(bufnr, "dressing#prompt_confirm") - vim.fn.prompt_setinterrupt(bufnr, "dressing#prompt_cancel") - _G.dressing_prompt_confirm = function(text) - clear_callbacks() - vim.api.nvim_win_close(winnr, true) - if not start_in_insert then - vim.cmd("stopinsert") - -- stopinsert will move the cursor back 1. We need to move it forward 1 to - -- put it in the place you were when you opened the modal. - local cursor = vim.api.nvim_win_get_cursor(0) - cursor[2] = cursor[2] + 1 - vim.api.nvim_win_set_cursor(0, cursor) +setmetatable(M, { + __call = function(_, opts, on_confirm) + vim.validate({ + on_confirm = { on_confirm, "function", false }, + }) + opts = opts or {} + if type(opts) ~= "table" then + opts = { prompt = tostring(opts) } end - vim.schedule_wrap(on_confirm)(text) - end - if opts.highlight then - _G.dressing_prompt_hl = function() - local text = vim.api.nvim_buf_get_lines(bufnr, 0, 1, true)[1] - local ns = vim.api.nvim_create_namespace("DressingHl") - local highlights - if type(opts.highlight) == "function" then - highlights = opts.highlight(text) - else - highlights = vim.fn[opts.highlight](text) - end - vim.api.nvim_buf_clear_namespace(bufnr, ns, 0, -1) - for _, highlight in ipairs(highlights) do - local start = highlight[1] - local stop = highlight[2] - local group = highlight[3] - vim.api.nvim_buf_add_highlight(bufnr, ns, group, 0, start, stop) - end + local config = global_config.get_mod_config("input", opts) + + local bufnr = vim.api.nvim_create_buf(false, true) + local prompt = opts.prompt or config.default_prompt + local width = util.calculate_width(config.prefer_width + vim.api.nvim_strwidth(prompt), config) + local winopt = { + relative = config.relative, + anchor = config.anchor, + row = config.row, + col = config.col, + border = config.border, + width = width, + height = 1, + zindex = 150, + style = "minimal", + } + local winnr + if context.winid and vim.api.nvim_win_is_valid(context.winid) then + winnr = context.winid + vim.schedule(context.on_confirm) + vim.api.nvim_win_set_width(winnr, width) + bufnr = vim.api.nvim_win_get_buf(winnr) + else + winnr = vim.api.nvim_open_win(bufnr, true, winopt) + end + context = { + winid = winnr, + on_confirm = on_confirm, + opts = opts, + } + vim.api.nvim_buf_set_option(bufnr, "buftype", "prompt") + vim.api.nvim_buf_set_option(bufnr, "swapfile", false) + vim.api.nvim_buf_set_option(bufnr, "bufhidden", "wipe") + local keyopts = { silent = true, noremap = true } + vim.api.nvim_buf_set_keymap( + bufnr, + "i", + "", + "lua require('dressing.input').confirm()", + keyopts + ) + vim.fn.prompt_setprompt(bufnr, prompt) + -- Would prefer to use v:lua directly here, but it doesn't work :( + vim.fn.prompt_setcallback(bufnr, "dressing#prompt_confirm") + vim.fn.prompt_setinterrupt(bufnr, "dressing#prompt_cancel") + if opts.highlight then + vim.cmd([[ + autocmd TextChanged lua require('dressing.input').highlight() + autocmd TextChangedI lua require('dressing.input').highlight() + ]]) end vim.cmd([[ - autocmd TextChanged lua dressing_prompt_hl() - autocmd TextChangedI lua dressing_prompt_hl() + autocmd BufLeave ++nested ++once lua require('dressing.input').confirm() ]]) - end - vim.cmd([[ - autocmd BufLeave ++nested ++once lua dressing_prompt_confirm() - ]]) - vim.cmd("startinsert!") - if opts.default then - vim.api.nvim_feedkeys(opts.default, "n", false) - end - _G.dressing_prompt_hl() -end + vim.cmd("startinsert!") + if opts.default then + vim.api.nvim_feedkeys(opts.default, "n", false) + end + M.highlight() + end, +}) + +return M