Jupyter notebooks, edited natively in Neovim. Jove — /dʒoʊv/, as in Jupiter.
[!warning] This plugin is in the experimental phase and have frequent breaking changes. Version pinning or reading of release notes before update is highly recommended.
terminal-browseric/ac cell text-objects, [c/]c cell motions,
cell borders, per-cell execution counts and elapsed time.jupytextjupyter_client and ipykernel installed.snacks.nvim (optional): Image rendering.Run :checkhealth jove to verify the requirements
{
"nghiant03/jove.nvim",
version = "v*",
lazy = false,
opts = {
auto_kernel = true,
},
}
[!important] Do not load
jupytext.nvimwith jove since both registerBufReadCmdon*.ipynb. Jove detects it and refuses to register its handlers with a warning.
The kernel needs a Python interpreter that can import jupyter_client and
ipykernel. Jove picks one automatically, in this order:
$CONDA_PREFIX/bin/python (active conda env)$VIRTUAL_ENV/bin/python (active virtualenv)g:python3_host_prog (Neovim's configured Python host)bridge_python value (default "python3")If you launch Neovim from outside the environment that has the Jupyter stack, set the fallback explicitly:
opts = {
bridge_python = "/home/you/envs/jupyter/bin/python",
}
Full option list with defaults:
require("jove").setup({
jupytext = "jupytext", -- path to the jupytext binary
bridge_python = "python3", -- fallback Python for the kernel helper
auto_kernel = true, -- start kernel automatically on open
auto_import_outputs = true, -- render persisted outputs on open/reload
auto_export_outputs = true, -- merge session outputs into the notebook on save
persist_exec_counts = true, -- persist kernel execution counts into the notebook
elapsed = true, -- show per-cell elapsed execution time
auto_reload = false, -- auto-reload when the notebook changes on disk
cell_motions = true, -- map cell motions
signs = {
queued = "…", -- gutter sign: queued for execution
running = "▶", -- gutter sign: currently running
ok = "✓", -- gutter sign: finished successfully
error = "✗", -- gutter sign: finished with an error
},
output = {
max_lines = 50, -- inline output truncation limit
images = true, -- render images via snacks.image when available
image_max_width = 80, -- cap rendered image width in terminal cells
image_max_height = 40, -- cap rendered image height in terminal cells
header = true, -- draw the Output top frame
guide = "▎ ", -- per-line inner output rail
inside_border = false, -- render output inside the cell border instead of its own bordered block below it
hl = nil, -- output background tint
},
variables = {
auto_refresh = true, -- refresh the variables inspector on idle
size = 0.25, -- sidebar size as a fraction of the screen
},
ui = {
conceal_headers = true, -- conceal cell headers
active_cell = true, -- highlight the active cell
exec_counts = true, -- show per-cell execution counts
elapsed = true, -- show per-cell elapsed time
borders = true, -- draw a closing line below each cell
border_hl = nil, -- cell border highlight
window_mode = "vsplit", -- all Jove viewers: "vsplit" (right), "hsplit" (below), or "float"
window_overrides = {}, -- optional per-view modes: sidebar, output, inspect, webview
},
lsp = {
auto_attach = false, -- start the servers in `servers` on notebook open
servers = {}, -- language id to lsp server names,
},
webview = {
enabled = true,
cmd = "terminal-browser", -- terminal-browser binary
width = 0.5, -- webview width in float/vsplit mode, as a fraction of the editor
height = 0.5, -- webview height in float/hsplit mode, as a fraction of the editor
},
})
The sidebar, output viewer, variable details, and webview all open in a
right-hand split by default. Set ui.window_mode to "float" or "hsplit"
to change the default for all of them. Override individual views when needed:
require("jove").setup({
ui = {
window_mode = "vsplit",
window_overrides = {
output = "hsplit",
inspect = "float",
webview = "float",
},
},
})
:Jove open-webview runs terminal-browser in a terminal buffer and enters
Terminal mode automatically.
The webview recognizes HTML, Plotly, Vega-Lite v4–v6, and Vega v5–v6 output.
[!note] Hover events require Neovim's
mousemoveeventoption to be enabled.
[!warning] Plotly and Vega renderers load JavaScript from CDNs, so they require network access. With Plotly, emit its rich MIME bundle explicitly if your kernel selects a different renderer:
fig.show(renderer="plotly_mimetype")
[!warning] Live
ipywidgets,FigureWidget, and Python callbacks require a widget manager and kernel comms, which are not yet supported.
Built-in languages:
| Language | Filetype | Cell marker | Known servers |
|---|---|---|---|
| Python | python |
# %% |
pyright, basedpyright, ruff |
| Julia | julia |
# %% |
julials |
| R | r |
# %% |
r_language_server |
| JavaScript | javascript |
// %% |
ts_ls |
| TypeScript | typescript |
// %% |
ts_ls |
Unknown languages fall back to Python conventions. Register more yourself:
require("jove.lang").register("scala", { fmt = "scala", comment = "//", servers = { "metals" } })
Because the buffer carries the language's real filetype, any LSP server you have configured attaches to notebook buffers automatically. If you prefer jove to start the servers for you:
opts = {
lsp = {
auto_attach = true,
servers = { python = { "pyright" }, javascript = { "ts_ls" } },
},
}
| Command | Action |
|---|---|
:Jove run-cell |
Run current notebook cell |
:Jove run-above |
Run all notebook cells above the cursor |
:Jove run-all |
Run all notebook cells |
:Jove run-selection |
Run the visual selection as one unit |
:Jove run-cell-and-advance |
Run the current cell and jump to the next |
:Jove next-cell |
Jump to next notebook cell |
:Jove prev-cell |
Jump to previous notebook cell |
:Jove goto-running-cell |
Jump to the currently executing cell |
:Jove toggle-follow-running |
Toggle following the currently executing cell with the cursor |
:Jove init-kernel |
Start a kernel for the current notebook |
:Jove select-kernel |
Pick a kernelspec for the current notebook (replaces running kernel) |
:Jove interrupt |
Interrupt the running execution |
:Jove restart-kernel |
Restart the current notebook kernel |
:Jove shutdown-kernel |
Shut down the current notebook kernel and bridge |
:Jove toggle-output |
Show/hide rendered outputs of the current cell |
:Jove open-output |
Open the current cell's outputs in a viewer |
:Jove open-webview |
Open the current cell's rich output in an interactive terminal-browser webview |
:Jove clear-output |
Clear outputs of the current cell |
:Jove clear-outputs |
Clear all rendered outputs in this buffer |
:Jove reload |
Reload the current notebook buffer from disk |
:Jove sidebar |
Toggle the sidebar (variables, kernel info, table of contents) |
On jove buffers:
ic / ac cell text-objects (operator-pending and visual modes): always
on, no extra plugin needed.[c / ]c cell motions (normal mode): on by default, disable with
cell_motions = false. <Plug> mappings to your custom keys:vim.keymap.set("n", "<leader>x", "<Plug>(JoveRunCell)", { desc = "Run cell" })
vim.keymap.set("n", "<leader>X", "<Plug>(JoveRunCellAndAdvance)", { desc = "Run cell, advance" })
vim.keymap.set("x", "<leader>xx", "<Plug>(JoveRunSelection)", { desc = "Run selection" })
vim.keymap.set("n", "<leader>j", "<Plug>(JoveGotoRunningCell)", { desc = "Go to running cell" })
vim.keymap.set("n", "<leader>J", "<Plug>(JoveToggleFollowRunning)", { desc = "Follow running cell" })
Available <Plug> mappings:
| Mapping | Action |
|---|---|
<Plug>(JoveRunCell) |
Run current notebook cell |
<Plug>(JoveRunAbove) |
Run all notebook cells above the cursor |
<Plug>(JoveRunAll) |
Run all notebook cells |
<Plug>(JoveRunSelection) |
Run the visual selection as one unit (visual mode) |
<Plug>(JoveRunCellAndAdvance) |
Run the current cell and jump to the next |
<Plug>(JoveNextCell) |
Jump to next notebook cell |
<Plug>(JovePrevCell) |
Jump to previous notebook cell |
<Plug>(JoveGotoRunningCell) |
Jump to the currently executing cell |
<Plug>(JoveToggleFollowRunning) |
Toggle following the currently executing cell |