Skip to content

matarina/pyrola.nvim

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

70 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

logo

Pyrola

If you are seeking an alternative to Jupyter, Spyder, or RStudio in Neovim, Pyrola is the solution.

Pyrola delivers a multi-language REPL (Read-Eval-Print Loop) experience inside Neovim. It is designed for interactive programming, especially for data scientists, and supports:

  • Real-time code execution
  • Variable inspection
  • Image visualization

Since Pyrola is built on Jupyter kernels, any language with a Jupyter kernel can be integrated.


Demo

Features

  • Multi-language support: Any language with a Jupyter kernel works in Pyrola (Python, R, C++, Julia, etc.). recording_2026-01-04_07-36-08 - frame at 0m57s

  • Real-time REPL: Execute code dynamically within Neovim with immediate feedback.

  • Multiple code-sending methods: Send code via Tree-sitter semantic blocks, visual selection, or entire buffer.

  • Variable inspector: Inspect variables (class, type, shape, content) directly from the REPL (Python and R). recording_2026-01-04_07-36-08 - frame at 0m52s

  • Global variable browser: View all user globals in a floating window. Press <CR> on any variable to inspect it.

  • Image viewer: Preview image outputs in a floating window via Kitty or iTerm2 terminal protocols. recording_2026-01-04_07-36-08 - frame at 0m40s

  • Image history: Browse previously plotted images in a floating window.

  • Reliable interrupts: Interrupt long-running cells and recover cleanly.

  • One-command setup: :Pyrola setup installs dependencies and prepares a managed pyrola_<language> kernel automatically.

  • Auto kernel registration: Pyrola auto-manages pyrola_python, pyrola_r, pyrola_cpp, and pyrola_julia unless you explicitly override the kernel name.


Installation

1. Plugin setup

Add Pyrola to your plugin manager. Example using lazy.nvim:

{
  "matarina/pyrola.nvim",
  dependencies = { "nvim-treesitter/nvim-treesitter" },
  config = function()
    local pyrola = require("pyrola")

    pyrola.setup({
      -- Optional: point to a specific Python (conda/venv).
      -- If omitted, Pyrola uses the active python3 from PATH.
      -- python_path = "~/miniconda3/envs/ds/bin/python",

      -- Optional overrides. When omitted, Pyrola auto-creates and uses
      -- managed kernels named pyrola_<language>.
      kernel_map = {
        -- python = "custom-python-kernel",
        -- r = "custom-r-kernel",
      },
      split_horizontal = false,
      split_ratio = 0.65,
      image = {
        cell_width = 10,
        cell_height = 20,
        max_width_ratio = 0.5,
        max_height_ratio = 0.5,
        offset_row = 0,
        offset_col = 0,
        protocol = "auto", -- auto | kitty | iterm2 | none
      },
    })

    -- Keybindings
    vim.keymap.set("n", "<CR>", pyrola.send_statement_definition, { noremap = true })
    vim.keymap.set("v", "<leader>vs", pyrola.send_visual_to_repl, { noremap = true })
    vim.keymap.set("n", "<leader>vb", pyrola.send_buffer_to_repl, { noremap = true })
    vim.keymap.set("n", "<leader>is", pyrola.inspect, { noremap = true })
    vim.keymap.set("n", "<leader>ig", pyrola.show_globals, { noremap = true })
    vim.keymap.set("n", "<leader>ik", pyrola.interrupt_kernel, { noremap = true })
    vim.keymap.set("n", "<leader>im", pyrola.open_history_manager, { noremap = true })
  end,
},

-- Tree-sitter is required. Install parsers for languages in kernel_map.
{
  "nvim-treesitter/nvim-treesitter",
  build = ":TSUpdate",
  config = function()
    local ts = require("nvim-treesitter")
    ts.setup({ install_dir = vim.fn.stdpath("data") .. "/site" })
    ts.install({ "python", "r", "lua" })
  end,
}

2. Python environment setup

Pyrola needs a few Python packages. There are three ways to set them up:

Option A: One-command setup (recommended)

Open a file and run:

:Pyrola setup

This installs all dependencies via pip and prepares the managed kernel for the current filetype. By default the managed name is pyrola_<language>. When finished, run :Pyrola init to start.

Option B: Manual install

pip install -r /path/to/pyrola.nvim/rplugin/python3/requirements.txt
pip install ipykernel
python3 -m ipykernel install --user --name py3

If you manually set kernel_map.python = "py3", the kernel name must match that explicit config.

Option C: Auto-prompted install

When you run :Pyrola init with missing packages, Pyrola prompts you to install them. If the kernel is missing for a Python file, it offers to install and register it.

3. Conda / venv users

Usually you do not need extra config. Pyrola follows the active environment that launched Neovim and creates managed kernels named pyrola_<language>.

Set python_path only if you want to override the active Python interpreter:

pyrola.setup({
    python_path = "~/miniconda3/envs/ds/bin/python",
})

This takes precedence over the active python3 on PATH.

4. Non-Python kernels

Pyrola auto-creates pyrola_r, pyrola_cpp, and pyrola_julia by reusing the language kernel available in the current environment. If the underlying language kernel is missing, install it first:

Language Underlying kernel requirement Managed name
R IRkernel installed in the active R environment pyrola_r
C++ xeus-cling installed pyrola_cpp
Julia IJulia installed pyrola_julia

5. Image preview (optional)

Pyrola renders images in floating windows via Kitty or iTerm2 terminal protocols (image.protocol = "auto" detects automatically).

For embedded pixel images in the REPL console output, install timg:

# Debian/Ubuntu
apt install timg

# macOS
brew install timg

tmux users: Add to ~/.tmux.conf:

set -g focus-events on
set -g allow-passthrough all

Usage

Commands

Command Description
:Pyrola setup Install dependencies + prepare the managed kernel for the current filetype
:Pyrola init Start kernel and open REPL terminal

Both commands support tab completion.

Sending code

Function Description
pyrola.send_statement_definition() Send the Tree-sitter semantic block under cursor. When REPL is not running, falls back to normal <CR>.
pyrola.send_visual_to_repl() Send visual selection to REPL. Exits visual mode and moves cursor to next code line.
pyrola.send_buffer_to_repl() Send the entire buffer to REPL.

Inspecting variables

Function Description
pyrola.inspect() Inspect the symbol under cursor in a floating window. Uses Tree-sitter to identify the symbol, falls back to <cword>. Shows type, shape, content, methods, etc. Supports Python and R.
pyrola.show_globals() Show all user variables in a floating window. Press <CR> on any entry to inspect it. Press q or <Esc> to close.

Kernel control

Function Description
pyrola.interrupt_kernel() Send SIGINT to interrupt the running kernel execution.

Image history

Function Description
pyrola.open_history_manager() Open image history browser. Use h/l to navigate, q to close.
pyrola.show_last_image() Show the most recent image in a floating window.
pyrola.show_previous_image() Navigate to the previous image in history.
pyrola.show_next_image() Navigate to the next image in history.

Configuration reference

pyrola.setup({
    -- Python interpreter path. Supports ~ expansion.
    -- Priority: python_path > exepath("python3") > g:python3_host_prog > "python3"
    python_path = nil,

    -- Optional explicit overrides. If a filetype is omitted, Pyrola uses
    -- its managed kernel name: pyrola_<language>.
    kernel_map = {
        -- python = "python3",
        -- r = "ir",
        -- cpp = "xcpp17",
    },

    -- REPL terminal split direction and size.
    split_horizontal = false,  -- false = vertical split (right), true = horizontal (bottom)
    split_ratio = 0.65,        -- fraction of editor width/height for the split

    -- Image display settings.
    image = {
        cell_width = 10,        -- terminal cell width in pixels (for size calculations)
        cell_height = 20,       -- terminal cell height in pixels
        max_width_ratio = 0.5,  -- max image width as fraction of editor columns
        max_height_ratio = 0.5, -- max image height as fraction of editor lines
        offset_row = 0,         -- adjust image row position (cells)
        offset_col = 0,         -- adjust image col position (cells)
        protocol = "auto",      -- "auto" | "kitty" | "iterm2" | "none"
    },
})

Highlight groups

Floating windows use theme-aware defaults (linked to FloatBorder, FloatTitle, NormalFloat). Highlight groups are automatically refreshed when you change colorscheme. Override them for custom colors:

-- Inspector window
vim.api.nvim_set_hl(0, "PyrolaInspectorBorder", { link = "FloatBorder" })
vim.api.nvim_set_hl(0, "PyrolaInspectorTitle",  { link = "FloatTitle" })
vim.api.nvim_set_hl(0, "PyrolaInspectorNormal", { link = "NormalFloat" })

-- Image window
vim.api.nvim_set_hl(0, "PyrolaImageBorder", { link = "FloatBorder" })
vim.api.nvim_set_hl(0, "PyrolaImageTitle",  { link = "FloatTitle" })
vim.api.nvim_set_hl(0, "PyrolaImageNormal", { link = "NormalFloat" })

-- Globals window
vim.api.nvim_set_hl(0, "PyrolaGlobalsBorder", { link = "FloatBorder" })
vim.api.nvim_set_hl(0, "PyrolaGlobalsTitle",  { link = "FloatTitle" })
vim.api.nvim_set_hl(0, "PyrolaGlobalsNormal", { link = "NormalFloat" })

Troubleshooting

:Pyrola init says "No such kernel"

If you set an explicit kernel_map override, that name must exist:

jupyter kernelspec list

For Python, register a matching kernel:

python3 -m ipykernel install --user --name py3

If you do not want to manage kernels manually, remove the explicit override and run :Pyrola setup.

:Pyrola init says "python3 executable not found"

Set python_path in your config or ensure python3 is on PATH:

pyrola.setup({ python_path = "/usr/bin/python3" })

Non-Python managed kernel setup fails

Pyrola can create pyrola_r, pyrola_cpp, and pyrola_julia, but the underlying language kernel must already be installed in the active environment:

  • R: install IRkernel
  • C++: install xeus-cling
  • Julia: install IJulia

Images don't display

  1. Check your terminal supports Kitty or iTerm2 image protocols.
  2. For inline REPL images, install timg: apt install timg or brew install timg.
  3. In tmux, ensure allow-passthrough all and focus-events on are set.

REPL is unresponsive

Use pyrola.interrupt_kernel() to send SIGINT. If the kernel is fully hung, restart with :Pyrola init.

:Pyrola init says "request timed out"

Kernel startup can take longer on the first launch, especially in slower or remote environments. Pyrola now waits up to 30 seconds for :Pyrola init.

If it still times out, verify the configured Python can import the required packages and start the target kernel:

python3 -c "import jupyter_client, prompt_toolkit, PIL, pygments"
jupyter kernelspec list

TODO

  • Multi-language variable inspector: Extend inspection beyond Python and R.

Credits

Contributing

Contributions are welcome! Issues and pull requests will receive prompt attention.

Note: Terminal graphic protocols such as Sixel are not yet supported inside Neovim terminal buffers due to upstream limitations (see Neovim Issue #30889).

About

Nvim Read–Eval–Print Loop (REPL) plugin for data science workflow

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors