A Neovim plugin for sending code directly to an interactive terminal REPL. Designed for rapid experimentation, data analysis, and iterative script development without needing Tmux or external multiplexers.
- Send Any Code Chunk: Send the current line, word, paragraph, or visual selection with a single keypress.
- Run Notebook Cells: Execute self-contained code cells delimited by
# %%,##,// %%, or-- %%. - Use Vim Motions: Use native operator motions (e.g.
gxcipfor inner paragraph,gxcaffor a function) to send arbitrary text objects, the entire buffer or custom line ranges. - Control the REPL: Toggle window focus, restart the session, send interrupts (
Ctrl-C), or clear the screen (Ctrl-L). - Flexible Terminal Layouts: Open REPLs in vertical splits, horizontal splits, or tabs with customizable split sizes.
- Ready for Multiple Languages: Built-in defaults for Python (
uv+ IPython with autoreload), R, Julia, Lua, Bash, Zsh, Node.js, and TypeScript.
- Neovim >= 0.10.0 (0.11+ recommended)
- If using Python defaults: uv (or configure standard
python3/ipython)
{
"toreerdmann/send-to-repl.nvim",
cmd = {
"SendToReplWith",
"SendToReplStart",
"SendToReplToggle",
"SendToReplRestart",
},
keys = {
{ "<leader>l", function() require("send-to-repl").send_line() end, desc = "Send line to REPL" },
{ "<leader>p", function() require("send-to-repl").send_word() end, desc = "Send word to REPL" },
{ "<leader>c", function() require("send-to-repl").send_cell() end, desc = "Send cell to REPL" },
{ "<leader><CR>", function() require("send-to-repl").send_paragraph() end, desc = "Send paragraph to REPL" },
{ "<leader><CR>", function() require("send-to-repl").send_visual() end, mode = "v", desc = "Send selection to REPL" },
{ "<leader>rf", function() require("send-to-repl").send_file() end, desc = "Send file to REPL" },
{ "<leader>rt", function() require("send-to-repl").toggle_repl() end, desc = "Toggle REPL window" },
{ "<leader>rr", function() require("send-to-repl").restart_repl() end, desc = "Restart REPL" },
{ "<leader>rw", function() require("send-to-repl").start_repl_with() end, desc = "Start REPL with packages" },
{ "gxc", function() require("send-to-repl").send_operator() end, desc = "Send motion to REPL" },
},
opts = {
layout = {
split = "vertical", -- "vertical" | "horizontal" | "tab"
size = 0.4, -- 40% split width/height
},
},
}Default options:
require("send-to-repl").setup({
layout = {
split = "vertical", -- "vertical" | "horizontal" | "tab"
size = nil, -- nil for default split, or number (e.g. 0.4 for 40% or 80 for cols)
open = nil, -- optional custom function: fun(cmd: string): number (term_buf)
},
cell_marker = nil, -- string or table<filetype, pattern> (defaults to `# %%`, `// %%`, etc.)
bracketed_paste = true,
wait_for_prompt = true,
repls = {
python = {
cmd = "uv",
args = { "run", "--with", "ipython", "--", "ipython", "--profile", "nvim" },
ensure_ipython_profile = true,
-- Fallback packages when working in a directory without a venv/project:
no_venv_packages = { "pandas" }, -- e.g. automatically runs: uv run --with ipython --with pandas ...
detect_venv = true, -- whether to auto-detect virtual environments
},
r = { cmd = "R", args = { "--no-save", "--quiet" } },
julia = { cmd = "julia", args = {} },
lua = { cmd = "lua", args = {} },
sh = { cmd = "bash", args = {} },
bash = { cmd = "bash", args = {} },
zsh = { cmd = "zsh", args = {} },
javascript = { cmd = "node", args = {} },
typescript = { cmd = "ts-node", args = {} },
},
})When working in a folder without a virtual environment (no .venv, venv, active $VIRTUAL_ENV, or pyproject.toml), send-to-repl.nvim can automatically include fallback packages using no_venv_packages = { "pandas" }.
You can also start or restart a REPL on-demand with specific packages or interactive prompt:
:SendToReplWith pandas polars— Starts or restarts REPL withpandasandpolars(includes autocompletion for common libraries).:SendToReplWith(or<leader>rw) — Prompts interactively for packages to run with.:SendToReplStart <command>— Starts or restarts REPL with an exact custom command.
Dynamic commands and arguments can also be functions:
opts = {
repls = {
python = {
cmd = function()
if vim.fn.filereadable(".venv/bin/python") == 1 then
return ".venv/bin/python"
end
return "python3"
end,
args = { "-i" },
ensure_ipython_profile = false,
},
},
}| Lua API | User Command | Description |
|---|---|---|
require("send-to-repl").send_line() |
:SendToReplLine |
Send line under cursor |
require("send-to-repl").send_word() |
:SendToReplWord |
Send word under cursor |
require("send-to-repl").send_paragraph() |
:SendToReplParagraph |
Send paragraph under cursor |
require("send-to-repl").send_visual() |
:SendToReplVisual |
Send visually selected text |
require("send-to-repl").send_cell() |
:SendToReplCell |
Send code cell (# %%, ##, etc.) |
require("send-to-repl").send_file() |
:SendToReplFile / :SendToReplBuffer |
Send entire buffer |
require("send-to-repl").send_range(l1, l2) |
— | Send line range [l1, l2] |
require("send-to-repl").send_operator() |
— | Operator motion function |
require("send-to-repl").send(text) |
:SendToReplSend <text> |
Send arbitrary text |
require("send-to-repl").start_repl_with([pkgs]) |
:SendToReplWith [pkgs] |
Start/restart REPL with packages (prompts if empty) |
require("send-to-repl").start_repl(opts) |
:SendToReplStart [cmd] |
Start/restart REPL with custom packages or command |
require("send-to-repl").toggle_repl() |
:SendToReplToggle |
Toggle/Focus REPL window |
require("send-to-repl").restart_repl() |
:SendToReplRestart |
Restart the REPL process |
require("send-to-repl").clear() |
:SendToReplClear |
Send <C-l> / clear screen |
require("send-to-repl").interrupt() |
:SendToReplInterrupt |
Send <C-c> / SIGINT |
require("send-to-repl").has_venv([dir]) |
— | Check if virtual environment / project is detected |
You can launch a completely isolated, clean Neovim session with pre-configured keymaps and an interactive demo buffer without touching your personal config:
./scripts/test_drive.shOr open a specific file:
./scripts/test_drive.sh path/to/script.py./scripts/run_tests.shOr run headless directly:
XDG_CONFIG_HOME=/tmp XDG_DATA_HOME=/tmp nvim --headless --clean -u tests/minimal_init.lua -c "PlenaryBustedFile tests/tests.lua"