nvimConfig/plugin/03_terminal.lua
Daniel 6142f7d55e fix: harden REPL/terminal code-sending paths
- lua/keymap/repl.lua
  - Replace non-existent `:SlimeSendCurrentLine` with synchronous
    `vim.fn["slime#send"](line .. "\n")` fallback.
  - Support both modern `r.send` and older `r.run` R.nvim Lua APIs,
    falling back to `<Plug>` mappings if neither module is available.

- lua/nix_smart_send.lua
  - Refactor `send_repl()` to extract Tree-sitter node text and send it
    directly via `slime#send`, eliminating the `feedkeys(..., "x", true)`
    race between visual selection and cursor movement.
  - Replace internal `slime#send_op` call in `slime_send_region()` with the
    public `<Plug>SlimeRegionSend` mapping.
  - Improve `move_to_next_non_empty_line()` to walk up the AST when a
    node has no next sibling, so the cursor escapes nested blocks.

- plugin/03_terminal.lua
  - Set `vim.g.slime_target = "neovim"` at the top of the module so the
    target is guaranteed before any slime send.

- plugin/21_datascience.lua
  - Remove duplicate `vim.g.slime_target = "neovim"` now that the
    terminal module owns the setting.
2026-07-26 06:57:57 +00:00

107 lines
3.1 KiB
Lua

local Config = require('config')
-- vim-slime target: use Neovim's built-in terminal.
-- Must be set before any slime send happens.
vim.g.slime_target = "neovim"
local M = {}
-- Configuration
Config.opt_bracket = true
M.opt_term = nil
-- Default terminal commands
-- Users can override this via Config.terminal_commands in their setup
local defaults = {
clickhouse_client = "clickhouse client -m",
clickhouse_local = "clickhouse local -m",
duckdb = "duckdb",
julia = "julia",
python = "ipython",
shell = "echo 'Hello " .. vim.env.USER .. "!'",
}
-- Registry of terminal commands
M.commands = vim.tbl_deep_extend("force", defaults, Config.terminal_commands or {})
-- Bracket paste control
function M.toggle_bracket()
Config.opt_bracket = not Config.opt_bracket
vim.g.slime_bracketed_paste = Config.opt_bracket
return Config.opt_bracket
end
-- Terminal management
function M.split_and_open_terminal()
vim.cmd("below terminal")
vim.cmd("resize " .. math.floor(vim.fn.winheight(0) * 0.9))
local term_buf = vim.api.nvim_win_get_buf(vim.api.nvim_get_current_win())
M.opt_term = term_buf
-- Set buffer-local variables for vim-slime
local job_id = vim.b[term_buf].terminal_job_id
if not job_id then
vim.notify("Terminal job id not available", vim.log.levels.WARN)
return term_buf
end
vim.b[term_buf].slime_config = { jobid = job_id }
return M.opt_term
end
-- Public functions
function M.open_in_terminal(cmd)
local command = cmd or ""
local current_window = vim.api.nvim_get_current_win()
local code_buf = vim.api.nvim_get_current_buf()
if not vim.api.nvim_buf_is_valid(code_buf) then
vim.notify("Code buffer is not valid", vim.log.levels.ERROR)
return
end
-- Open terminal and get buffer
local term_buf = M.split_and_open_terminal()
if not term_buf or not vim.api.nvim_buf_is_valid(term_buf) then
vim.notify("Failed to open terminal buffer", vim.log.levels.ERROR)
return
end
-- Send command if provided
local job_id = vim.b[term_buf].terminal_job_id
if command ~= "" then
if not job_id then
vim.notify("Terminal job not ready, cannot send command", vim.log.levels.WARN)
else
local ok, err = pcall(vim.api.nvim_chan_send, job_id, command .. "\r")
if not ok then
vim.notify("Failed to send command to terminal: " .. tostring(err), vim.log.levels.ERROR)
end
end
end
-- Configure slime for the ORIGINAL code buffer to point to this new terminal
-- This makes "Send to Terminal" work immediately
if job_id then
local slime_config = { jobid = job_id }
-- Fix: Set the variable on the captured code buffer, not the current (terminal) buffer
local ok, err = pcall(vim.api.nvim_buf_set_var, code_buf, "slime_config", slime_config)
if not ok then
vim.notify("Failed to set slime_config on code buffer: " .. tostring(err), vim.log.levels.ERROR)
end
end
-- Switch back to code buffer
vim.api.nvim_set_current_win(current_window)
end
-- Predefined terminal commands
for name, command in pairs(M.commands) do
M["open_" .. name] = function()
M.open_in_terminal(command)
end
end
Config.terminal = M