diff --git a/CHANGELOG.md b/CHANGELOG.md index 51024ae2..27d6da3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [2.12.0](https://github.com/akinsho/toggleterm.nvim/compare/... + +### Features + +* add bracketed paste support to `send_lines_to_terminal` and Terminal:send. + ## [2.11.0](https://github.com/akinsho/toggleterm.nvim/compare/v2.10.0...v2.11.0) (2024-04-22) diff --git a/lua/toggleterm.lua b/lua/toggleterm.lua index 09310dce..4acd7c06 100644 --- a/lua/toggleterm.lua +++ b/lua/toggleterm.lua @@ -175,7 +175,8 @@ end --- @param name string? --- @param go_back boolean? whether or not to return to original window --- @param open boolean? whether or not to open terminal window -function M.exec(cmd, num, size, dir, direction, name, go_back, open) +--- @param use_bracketed_paste boolean? whether or not to use bracketed paste mode for send +function M.exec(cmd, num, size, dir, direction, name, go_back, open, use_bracketed_paste) vim.validate({ cmd = { cmd, "string" }, num = { num, "number", true }, @@ -185,6 +186,7 @@ function M.exec(cmd, num, size, dir, direction, name, go_back, open) name = { name, "string", true }, go_back = { go_back, "boolean", true }, open = { open, "boolean", true }, + use_bracketed_paste = { use_bracketed_paste, "boolean", true }, }) num = (num and num >= 1) and num or terms.get_toggled_id() open = open == nil or open @@ -197,13 +199,14 @@ function M.exec(cmd, num, size, dir, direction, name, go_back, open) term:close() go_back = false end - term:send(cmd, go_back) + term:send(cmd, go_back, use_bracketed_paste) end --- @param selection_type string --- @param trim_spaces boolean --- @param cmd_data table -function M.send_lines_to_terminal(selection_type, trim_spaces, cmd_data) +--- @param use_bracketed_paste boolean? +function M.send_lines_to_terminal(selection_type, trim_spaces, cmd_data, use_bracketed_paste) local id = tonumber(cmd_data.args) or 1 trim_spaces = trim_spaces == nil or trim_spaces @@ -244,11 +247,11 @@ function M.send_lines_to_terminal(selection_type, trim_spaces, cmd_data) if not lines or not next(lines) then return end if not trim_spaces then - M.exec(table.concat(lines, "\n"), id) + M.exec(table.concat(lines, "\n"), id, nil, nil, nil, nil, nil, nil, use_bracketed_paste) else for _, line in ipairs(lines) do local l = trim_spaces and line:gsub("^%s+", ""):gsub("%s+$", "") or line - M.exec(l, id) + M.exec(l, id, nil, nil, nil, nil, nil, nil, use_bracketed_paste) end end diff --git a/lua/toggleterm/terminal.lua b/lua/toggleterm/terminal.lua index 904eb340..354e046f 100644 --- a/lua/toggleterm/terminal.lua +++ b/lua/toggleterm/terminal.lua @@ -322,10 +322,14 @@ end ---Send a command to a running terminal ---@param cmd string|string[] ---@param go_back boolean? whether or not to return to original window -function Terminal:send(cmd, go_back) +---@param use_bracketed_paste boolean? Whether or not to add bracketed paste characters to send sequence +function Terminal:send(cmd, go_back, use_bracketed_paste) + local start_seq = use_bracketed_paste and "\x1b[200~" or "" + local end_seq = use_bracketed_paste and "\x1b[201~" .. self.newline_chr or "" + cmd = type(cmd) == "table" and with_cr(self.newline_chr, unpack(cmd)) or with_cr(self.newline_chr, cmd --[[@as string]]) - fn.chansend(self.job_id, cmd) + fn.chansend(self.job_id, start_seq .. cmd .. end_seq) self:scroll_bottom() if go_back and self:is_focused() then ui.goto_previous() diff --git a/tests/terminal_spec.lua b/tests/terminal_spec.lua index 488c807d..aa3e4d53 100644 --- a/tests/terminal_spec.lua +++ b/tests/terminal_spec.lua @@ -305,9 +305,25 @@ describe("ToggleTerm tests:", function() local test1 = Terminal:new():toggle() local _ = match._ spy.on(test1, "send") + spy.on(vim.fn, "chansend") toggleterm.exec('echo "hello world"', 1) assert.spy(test1.send).was_called() - assert.spy(test1.send).was_called_with(_, 'echo "hello world"', true) + assert.spy(test1.send).was_called_with(_, 'echo "hello world"', true, match.is_nil()) + assert.spy(vim.fn.chansend).was_called_with(test1.job_id, 'echo "hello world"\n') + assert.is_true(vim.tbl_contains(api.nvim_list_wins(), test1.window)) + end) + + it("should send commands to a terminal with bracketed paste characters when specified", function() + local start_seq = "\x1b[200~" + local end_seq = "\x1b[201~" + local test1 = Terminal:new():toggle() + local _ = match._ + spy.on(test1, "send") + spy.on(vim.fn, "chansend") + toggleterm.exec("def hello():\n print('foo')", 1, nil, nil, nil, nil, nil, nil, true) + assert.spy(test1.send).was_called() + assert.spy(test1.send).was_called_with(_, "def hello():\n print('foo')", true, true) + assert.spy(vim.fn.chansend).was_called_with(test1.job_id, start_seq .. "def hello():\n print('foo')\n" .. end_seq .. "\n") assert.is_true(vim.tbl_contains(api.nvim_list_wins(), test1.window)) end) @@ -316,7 +332,7 @@ describe("ToggleTerm tests:", function() test1:close() spy.on(test1, "send") toggleterm.exec_command("cmd='echo \"hello world\"' open=0", 1) - assert.spy(test1.send).was_called_with(test1, 'echo "hello world"', false) + assert.spy(test1.send).was_called_with(test1, 'echo "hello world"', false, match.is_nil()) assert.is_false(vim.tbl_contains(api.nvim_list_wins(), test1.window)) end) @@ -327,7 +343,7 @@ describe("ToggleTerm tests:", function() spy.on(test1, "send") toggleterm.exec('echo "hello world"', 1) assert.spy(test1.send).was_called() - assert.spy(test1.send).was_called_with(_, 'echo "hello world"', true) + assert.spy(test1.send).was_called_with(_, 'echo "hello world"', true, match.is_nil()) assert.is_true(vim.tbl_contains(api.nvim_list_wins(), test1.window)) end) @@ -338,7 +354,7 @@ describe("ToggleTerm tests:", function() vim.cmd("wincmd w") spy.on(test1, "send") toggleterm.exec_command("cmd='echo %'", 1) - assert.spy(test1.send).was_called_with(test1, fmt("echo %s", file), true) + assert.spy(test1.send).was_called_with(test1, fmt("echo %s", file), true, match.is_nil()) end) it("should handle nested quotes in cmd args", function() @@ -348,7 +364,7 @@ describe("ToggleTerm tests:", function() vim.cmd("wincmd w") spy.on(test1, "send") toggleterm.exec_command("cmd='g++ -std=c++17 % -o run'", 1) - assert.spy(test1.send).was_called_with(test1, fmt("g++ -std=c++17 %s -o run", file), true) + assert.spy(test1.send).was_called_with(test1, fmt("g++ -std=c++17 %s -o run", file), true, match.is_nil()) end) end)