Skip to content

Commit 5b84866

Browse files
BlueDrink9bluedrink9
and
bluedrink9
authored
feat: allow operator mapping to send to terminal (#507)
* Fix: Send correct visual range when using lua mapping Fixes #458, I think * Docs: Document using lua function directly in a mapping Especially useful for python users * feat: Add support for opfunc motions * docs: document use of motion opfunc --------- Co-authored-by: bluedrink9 <[email protected]>
1 parent c80844f commit 5b84866

File tree

3 files changed

+67
-11
lines changed

3 files changed

+67
-11
lines changed

README.md

+38
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,44 @@ You can "send lines" to the toggled terminals with the following commands:
297297
(`<T_ID` is an optional terminal ID parameter, which defines where should we send the lines.
298298
If the parameter is not provided, then the default is the `first terminal`)
299299

300+
Alternatively, for more fine-grained control and use in mappings, in lua:
301+
302+
```lua
303+
local trim_spaces = true
304+
vim.keymap.set("v", "<space>s", function()
305+
require("toggleterm").send_lines_to_terminal("single_line", trim_spaces, { args = vim.v.count })
306+
end
307+
-- Replace with these for the other two options
308+
-- require("toggleterm").send_lines_to_terminal("visual_line", trim_spaces, { args = vim.v.count })
309+
-- require("toggleterm").send_lines_to_terminal("visual_selection", trim_spaces, { args = vim.v.count })
310+
311+
-- For use as an operator map:
312+
-- Send motion to terminal
313+
vim.keymap.set("n", [[<leader><c-\>]], function()
314+
set_opfunc(function(motion_type)
315+
require("toggleterm").send_lines_to_terminal(motion_type, false, { args = vim.v.count })
316+
end)
317+
vim.api.nvim_feedkeys("g@", "n", false)
318+
end)
319+
-- Double the command to send line to terminal
320+
vim.keymap.set("n", [[<leader><c-\><c-\>]], function()
321+
set_opfunc(function(motion_type)
322+
require("toggleterm").send_lines_to_terminal(motion_type, false, { args = vim.v.count })
323+
end)
324+
vim.api.nvim_feedkeys("g@_", "n", false)
325+
end)
326+
-- Send whole file
327+
vim.keymap.set("n", [[<leader><leader><c-\>]], function()
328+
set_opfunc(function(motion_type)
329+
require("toggleterm").send_lines_to_terminal(motion_type, false, { args = vim.v.count })
330+
end)
331+
vim.api.nvim_feedkeys("ggg@G''", "n", false)
332+
end)
333+
```
334+
335+
Set `trim_spaces=false` for sending to REPLs for whitespace-sensitive languages like python.
336+
(For python, you probably want to start ipython with `ipython --no-autoindent`.)
337+
300338
<!-- panvimdoc-ignore-start -->
301339

302340
Example:

lua/toggleterm.lua

+20-10
Original file line numberDiff line numberDiff line change
@@ -221,21 +221,31 @@ function M.send_lines_to_terminal(selection_type, trim_spaces, cmd_data)
221221
if selection_type == "single_line" then
222222
start_line, start_col = unpack(api.nvim_win_get_cursor(0))
223223
table.insert(lines, fn.getline(start_line))
224-
elseif selection_type == "visual_lines" then
225-
local res = utils.get_line_selection("visual")
226-
start_line, start_col = unpack(res.start_pos)
227-
lines = res.selected_lines
228-
elseif selection_type == "visual_selection" then
229-
local res = utils.get_line_selection("visual")
224+
else
225+
local res = nil
226+
if string.match(selection_type, "visual") then
227+
res = utils.get_line_selection("visual")
228+
else
229+
res = utils.get_line_selection("motion")
230+
end
230231
start_line, start_col = unpack(res.start_pos)
231-
lines = utils.get_visual_selection(res)
232+
-- char, line and block are used for motion/operatorfunc. 'block' is ignored
233+
if selection_type == "visual_lines" or selection_type == "line" then
234+
lines = res.selected_lines
235+
elseif selection_type == "visual_selection" or selection_type == "char" then
236+
lines = utils.get_visual_selection(res, true)
237+
end
232238
end
233239

234240
if not lines or not next(lines) then return end
235241

236-
for _, line in ipairs(lines) do
237-
local l = trim_spaces and line:gsub("^%s+", ""):gsub("%s+$", "") or line
238-
M.exec(l, id)
242+
if not trim_spaces then
243+
M.exec(table.concat(lines, "\n"))
244+
else
245+
for _, line in ipairs(lines) do
246+
local l = trim_spaces and line:gsub("^%s+", ""):gsub("%s+$", "") or line
247+
M.exec(l, id)
248+
end
239249
end
240250

241251
-- Jump back with the cursor where we were at the beginning of the selection

lua/toggleterm/utils.lua

+9-1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ function M.get_line_selection(mode)
5555
visual = { "'<", "'>" },
5656
motion = { "'[", "']" },
5757
})[mode])
58+
-- '< marks are only updated when one leaves visual mode.
59+
-- When calling lua functions directly from a mapping, need to
60+
-- explicitly exit visual with the escape key to ensure those marks are
61+
-- accurate.
62+
vim.cmd("normal! ")
5863

5964
-- Get the start and the end of the selection
6065
local start_line, start_col = unpack(fn.getpos(start_char), 2, 3)
@@ -67,8 +72,11 @@ function M.get_line_selection(mode)
6772
}
6873
end
6974

70-
function M.get_visual_selection(res)
75+
function M.get_visual_selection(res, motion)
76+
motion = motion or false
7177
local mode = fn.visualmode()
78+
if motion then mode = "v" end
79+
7280
-- line-visual
7381
-- return lines encompassed by the selection; already in res object
7482
if mode == "V" then return res.selected_lines end

0 commit comments

Comments
 (0)