The problems with .ipynb
The fact that Jupyter notebooks are not just simple text files creates some inconveniences. The main ones being:
- You can’t edit them with a simple text editor;
- Git diffs are useless.
Even though Jupyter Notebooks can be opened with a text editor, they can’t really be edited that way since they are full of text that is just JSON jumble. So most of the text you end up seeing is not the Python code you want to edit. Consequently, in git diffs most of the “changes” that show up are actually just metadata.
The alternative
The solution I found and that I’ve been using for the past year is a Neovim plugin called iron.nvim.
This plugin basically allows you to turn a simple .py files into notebooks simply by adding delimiters #%% into the Python code which serve as separators between cells.
So everything between two #%% is considered a single cell and works just as you would expect in a Jupyter notebook.
Configuration
The official repository outlines the basic configuration needed to have iron running. However, there are some tweaks I made that are worth sharing.
REPL Window
I like to have the REPL window to be vertical and take 40% of the space.
repl_open_cmd = view.split.vertical.botright(vim.api.nvim_win_get_width(0) * 0.4)
Cell Operations
These keymaps allow you to navigate, add, and delete cells quickly.
-- Go to cell below
vim.keymap.set("n", "<space>j" , "/#%%\\|# %%<Enter><CR>:nohlsearch<CR>", {silent = true})
-- Go to cell above
vim.keymap.set("n", "<space>k" , "?#%%\\|# %%<CR>n:nohlsearch<CR><Enter>", {silent = true})
-- Add cell below
vim.keymap.set("n", "<space>aj", "/#%%\\|# %%<CR>:nohlsearch<CR>O#%%<Enter><Esc>O", {silent = true})
-- Add cell above
vim.keymap.set("n", "<space>ak", "?#%%\\|# %%<CR>:nohlsearch<CR>O#%%<Enter><Esc>O", {silent = true})
-- Delete cell
vim.keymap.set("n", "<space>dd", "?#%%\\|# %%<enter>v/#%%\\|# %%<enter>kd<CR>:nohlsearch<CR>", {silent = true})
-- Add separator symbol
vim.keymap.set("n", "<space>ah", "o<Esc>i#%%<enter>")
Autodetect Virtual Environment
This allows Iron to detect the virtual environment.
-- Helper to find the current project's venv
local function get_venv_python()
local venv = vim.fn.getcwd() .. "/.venv/bin/python3"
if vim.fn.executable(venv) == 1 then
return venv
else
return "python3" -- fallback to system python3
end
end
If you prefer to use ipython you can simply replace it like this:
-- Helper to find the current project's venv
local function get_venv_python()
local venv = vim.fn.getcwd() .. "/.venv/bin/ipython3"
if vim.fn.executable(venv) == 1 then
return venv
else
return "ipython3" -- fallback to system ipython3
end
end