···11-local utils = require "lib.utils"
11+local utils = require("lib.utils")
22local defaults = require("lib.config").defaults
3344---@class multinput.Input
···1414---@param opts any
1515---@param on_confirm fun(input?: string)
1616function Input:new(config, opts, on_confirm)
1717- local i = {}
1818- i.config = vim.tbl_deep_extend(
1919- "force",
2020- defaults,
2121- config,
2222- { win = { title = opts.prompt or "Input: " } }
2323- )
2424- i.default = opts.default or ""
2525- i.on_confirm = on_confirm or function() end
2626- setmetatable(i, self)
2727- self.__index = self
2828- return i
1717+ local i = {}
1818+ i.config = vim.tbl_deep_extend("force", defaults, config, { win = { title = opts.prompt or "Input: " } })
1919+ i.default = opts.default or ""
2020+ i.on_confirm = on_confirm or function() end
2121+ setmetatable(i, self)
2222+ self.__index = self
2323+ return i
2924end
30253126---@param default string
3227function Input:open(default)
3333- -- Position window relative to the cursor, such that it doesn't overlap with the cursor's line.
3434- local curr_win = vim.api.nvim_get_current_win()
3535- local cursor_row = vim.api.nvim_win_get_cursor(curr_win)[1]
3636- local win_config = (cursor_row <= 3) and { anchor = "NW", row = 1 }
3737- or { anchor = "SW", row = 0 }
3838- self.config = vim.tbl_deep_extend("keep", self.config, { win = win_config })
2828+ -- Position window relative to the cursor, such that it doesn't overlap with the cursor's line.
2929+ local curr_win = vim.api.nvim_get_current_win()
3030+ local cursor_row = vim.api.nvim_win_get_cursor(curr_win)[1]
3131+ local win_config = (cursor_row <= 3) and { anchor = "NW", row = 1 } or { anchor = "SW", row = 0 }
3232+ self.config = vim.tbl_deep_extend("keep", self.config, { win = win_config })
39334040- -- Create buffer and floating window.
4141- self.bufnr = vim.api.nvim_create_buf(false, true)
4242- utils.set_options({ buftype = "prompt", bufhidden = "wipe" }, { buf = self.bufnr })
4343- vim.fn.prompt_setprompt(self.bufnr, "")
3434+ -- Create buffer and floating window.
3535+ self.bufnr = vim.api.nvim_create_buf(false, true)
3636+ utils.set_options({ buftype = "prompt", bufhidden = "wipe" }, { buf = self.bufnr })
3737+ vim.fn.prompt_setprompt(self.bufnr, "")
44384545- self.winnr = vim.api.nvim_open_win(self.bufnr, true, self.config.win)
4646- utils.set_options(
4747- { wrap = true, linebreak = true, winhighlight = "Search:None" },
4848- { win = self.winnr }
4949- )
3939+ self.winnr = vim.api.nvim_open_win(self.bufnr, true, self.config.win)
4040+ utils.set_options({ wrap = true, linebreak = true, winhighlight = "Search:None" }, { win = self.winnr })
50415151- -- Write default value and put cursor at the end
5252- vim.api.nvim_buf_set_lines(self.bufnr, 0, -1, true, { default })
5353- vim.cmd "startinsert"
5454- vim.api.nvim_win_set_cursor(
5555- self.winnr,
5656- { 1, vim.str_utfindex(default, "utf-8") + 1 }
5757- )
4242+ -- Write default value and put cursor at the end
4343+ vim.api.nvim_buf_set_lines(self.bufnr, 0, -1, true, { default })
4444+ vim.cmd("startinsert")
4545+ vim.api.nvim_win_set_cursor(self.winnr, { 1, vim.str_utfindex(default, "utf-8") + 1 })
58465959- self:resize()
6060- self:autocmds()
6161- self:mappings()
4747+ self:resize()
4848+ self:autocmds()
4949+ self:mappings()
6250end
63516452---@param result string?
6553function Input:close(result)
6666- vim.cmd "stopinsert"
6767- vim.api.nvim_win_close(self.winnr, true)
6868- self.on_confirm(result)
5454+ vim.cmd("stopinsert")
5555+ vim.api.nvim_win_close(self.winnr, true)
5656+ self.on_confirm(result)
6957end
70587159---@param height integer
7260---@return boolean
7361function Input:set_numbers(height)
7474- if
7575- self.config.opts.numbers == "always"
7676- or (self.config.opts.numbers == "multiline" and height > 1)
7777- then
7878- utils.set_option_if_globally_enabled("number", self.winnr)
7979- utils.set_option_if_globally_enabled("relativenumber", self.winnr)
8080- end
6262+ if self.config.opts.numbers == "always" or (self.config.opts.numbers == "multiline" and height > 1) then
6363+ utils.set_option_if_globally_enabled("number", self.winnr)
6464+ utils.set_option_if_globally_enabled("relativenumber", self.winnr)
6565+ end
81668282- return vim.api.nvim_get_option_value("number", { win = self.winnr })
8383- or vim.api.nvim_get_option_value("relativenumber", { win = self.winnr })
6767+ return vim.api.nvim_get_option_value("number", { win = self.winnr })
6868+ or vim.api.nvim_get_option_value("relativenumber", { win = self.winnr })
8469end
85708671---@param width integer
8772---@param height integer
8873function Input:set_size(width, height)
8989- local h = utils.clamp(height, self.config.height.min, self.config.height.max)
9090- vim.api.nvim_win_set_height(self.winnr, h)
7474+ local h = utils.clamp(height, self.config.height.min, self.config.height.max)
7575+ vim.api.nvim_win_set_height(self.winnr, h)
91769292- local w = utils.clamp(
9393- width + self.config.padding,
9494- self.config.width.min,
9595- self.config.width.max
9696- )
7777+ local w = utils.clamp(width + self.config.padding, self.config.width.min, self.config.width.max)
97789898- local has_numbers = self:set_numbers(h)
9999- w = has_numbers and w + utils.get_linenr_width(self.winnr, self.bufnr) or w
100100- w = w + 2 -- HACK: add padding to avoid glitches
101101- vim.api.nvim_win_set_width(self.winnr, w)
7979+ local has_numbers = self:set_numbers(h)
8080+ w = has_numbers and w + utils.get_linenr_width(self.winnr, self.bufnr) or w
8181+ w = w + 2 -- HACK: add padding to avoid glitches
8282+ vim.api.nvim_win_set_width(self.winnr, w)
10283end
1038410485function Input:resize()
105105- local text = vim.api.nvim_buf_get_lines(self.bufnr, 0, -1, false)
106106- local line = table.concat(text, "")
8686+ local text = vim.api.nvim_buf_get_lines(self.bufnr, 0, -1, false)
8787+ local line = table.concat(text, "")
10788108108- if line == "" then
109109- self:set_size(0, 1)
110110- return
111111- end
8989+ if line == "" then
9090+ self:set_size(0, 1)
9191+ return
9292+ end
11293113113- local lines = utils.split_wrapped_lines(line, self.config.width.max)
9494+ local lines = utils.split_wrapped_lines(line, self.config.width.max)
11495115115- local lens = vim.tbl_map(function(l)
116116- return vim.fn.strdisplaywidth(l)
117117- end, lines)
9696+ local lens = vim.tbl_map(function(l)
9797+ return vim.fn.strdisplaywidth(l)
9898+ end, lines)
11899119119- self:set_size(math.max(unpack(lens)), #lines)
100100+ self:set_size(math.max(unpack(lens)), #lines)
120101end
121102122103function Input:autocmds()
123123- vim.api.nvim_create_autocmd({ "TextChanged", "TextChangedI" }, {
124124- group = group,
125125- buffer = self.bufnr,
126126- callback = function()
127127- self:resize()
128128- end,
129129- })
104104+ vim.api.nvim_create_autocmd({ "TextChanged", "TextChangedI" }, {
105105+ group = group,
106106+ buffer = self.bufnr,
107107+ callback = function()
108108+ self:resize()
109109+ end,
110110+ })
130111end
131112132113function Input:mappings()
133133- ---@param mode string|string[]
134134- ---@param lhs string
135135- ---@param rhs string|function
136136- local function map(mode, lhs, rhs)
137137- vim.keymap.set(mode, lhs, rhs, { buffer = self.bufnr })
138138- end
114114+ ---@param mode string|string[]
115115+ ---@param lhs string
116116+ ---@param rhs string|function
117117+ local function map(mode, lhs, rhs)
118118+ vim.keymap.set(mode, lhs, rhs, { buffer = self.bufnr })
119119+ end
139120140140- map({ "n", "i", "v" }, "<cr>", function()
141141- self:close(vim.api.nvim_buf_get_lines(self.bufnr, 0, 1, false)[1])
142142- end)
143143- map({ "i" }, "<a-cr>", function()
144144- vim.api.nvim_feedkeys(
145145- vim.api.nvim_replace_termcodes("<cr>", true, false, true),
146146- "n",
147147- true
148148- )
149149- end)
121121+ map({ "n", "i", "v" }, "<cr>", function()
122122+ self:close(vim.api.nvim_buf_get_lines(self.bufnr, 0, 1, false)[1])
123123+ end)
124124+ map({ "i" }, "<a-cr>", function()
125125+ vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes("<cr>", true, false, true), "n", true)
126126+ end)
150127151151- map("n", "<esc>", function()
152152- self:close()
153153- end)
128128+ map("n", "<esc>", function()
129129+ self:close()
130130+ end)
154131155155- map("n", "q", function()
156156- self:close()
157157- end)
132132+ map("n", "q", function()
133133+ self:close()
134134+ end)
158135end
159136160137return Input
+25-25
lua/lib/utils.lua
···33---@param options table<string, any>
44---@param opts vim.api.keyset.option
55function M.set_options(options, opts)
66- for k, v in pairs(options) do
77- vim.api.nvim_set_option_value(k, v, opts)
88- end
66+ for k, v in pairs(options) do
77+ vim.api.nvim_set_option_value(k, v, opts)
88+ end
99end
10101111---@param option string
1212---@param winnr integer
1313function M.set_option_if_globally_enabled(option, winnr)
1414- if vim.api.nvim_get_option_value(option, { scope = "global" }) then
1515- vim.api.nvim_set_option_value(option, true, { win = winnr })
1616- end
1414+ if vim.api.nvim_get_option_value(option, { scope = "global" }) then
1515+ vim.api.nvim_set_option_value(option, true, { win = winnr })
1616+ end
1717end
18181919---@param value number
···2121---@param max number
2222---@return number
2323function M.clamp(value, min, max)
2424- return math.min(math.max(value, min), max)
2424+ return math.min(math.max(value, min), max)
2525end
26262727---@param text string
2828---@param width integer
2929---@return string[]
3030function M.split_wrapped_lines(text, width)
3131- if text == "" then
3232- return {}
3333- end
3131+ if text == "" then
3232+ return {}
3333+ end
34343535- ---@type string[]
3636- local lines = {}
3737- local textlen = vim.fn.strchars(text, true)
3535+ ---@type string[]
3636+ local lines = {}
3737+ local textlen = vim.fn.strchars(text, true)
38383939- local i = 0
4040- while i < textlen do
4141- local len = i + width <= textlen and width or textlen - i
4242- local new_line = vim.fn.strcharpart(text, i, len)
3939+ local i = 0
4040+ while i < textlen do
4141+ local len = i + width <= textlen and width or textlen - i
4242+ local new_line = vim.fn.strcharpart(text, i, len)
43434444- table.insert(lines, new_line)
4545- i = i + len
4646- end
4444+ table.insert(lines, new_line)
4545+ i = i + len
4646+ end
47474848- return lines
4848+ return lines
4949end
50505151---@param winnr integer
5252---@param bufnr integer
5353---@return integer
5454function M.get_linenr_width(winnr, bufnr)
5555- local line_count = vim.api.nvim_buf_line_count(bufnr)
5656- local line_digits = math.floor(math.log10(math.max(1, line_count))) + 1
5757- local numberwidth = vim.api.nvim_get_option_value("numberwidth", { win = winnr })
5858- return math.max(line_digits, numberwidth)
5555+ local line_count = vim.api.nvim_buf_line_count(bufnr)
5656+ local line_digits = math.floor(math.log10(math.max(1, line_count))) + 1
5757+ local numberwidth = vim.api.nvim_get_option_value("numberwidth", { win = winnr })
5858+ return math.max(line_digits, numberwidth)
5959end
60606161return M
+5-5
lua/multinput.lua
···11-local Input = require "lib.input"
11+local Input = require("lib.input")
2233local M = {}
4455---@param config? multinput.Config
66function M.setup(config)
77- vim.ui.input = function(opts, on_confirm)
88- local input = Input:new(config or {}, opts or {}, on_confirm)
99- input:open(opts.default or "")
1010- end
77+ vim.ui.input = function(opts, on_confirm)
88+ local input = Input:new(config or {}, opts or {}, on_confirm)
99+ input:open(opts.default or "")
1010+ end
1111end
12121313return M