Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(molten-nvim): enhance molten config and add custom init function and statusline #1242

Merged
merged 7 commits into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions lua/astrocommunity/code-runner/molten-nvim/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,12 @@ A neovim plugin for interactively running code with the jupyter kernel. Fork of
> For up to date docs, visit the repository.

**Repository:** <https://github.com/benlubas/molten-nvim>

## Customizations

This plugin config adds a custom init function for Python venvs. When calling `<Leader>mmp`, we check if a venv is activated. If it is, we check if a jupyter kernel spec for this venv already exists and `:MoltenInit` this kernel spec. If no kernel spec is found, we prompt for a unique name, create the kernel spec and `:MoltenInit` it.

It also adds a status line indication if molten is activated and what kernel is initialized.

> [!TIP]
> When using a global molten venv as described in the repository, we need to set the variable `python3_host_prog` to the Python executable there, which in AstroNVim is done in `AstroCore` in the `options.g` table, for example: `python3_host_prog = vim.fn.expand "~/.virtualenvs/neovim/bin/python3"`.
134 changes: 123 additions & 11 deletions lua/astrocommunity/code-runner/molten-nvim/init.lua
Original file line number Diff line number Diff line change
@@ -1,20 +1,132 @@
local function ensure_kernel_for_venv()
local venv_path = os.getenv "VIRTUAL_ENV" or os.getenv "CONDA_PREFIX"
if not venv_path then
vim.notify("No virtual environment found.", vim.log.levels.WARN)
return
end

-- Canonicalize the venv_path to ensure consistency
venv_path = vim.fn.fnamemodify(venv_path, ":p")

-- Check if the kernel spec already exists
local handle = io.popen "jupyter kernelspec list --json"
local existing_kernels = {}
if handle then
local result = handle:read "*a"
handle:close()
local json = vim.fn.json_decode(result)
-- Iterate over available kernel specs to find the one for this virtual environment
for kernel_name, data in pairs(json.kernelspecs) do
existing_kernels[kernel_name] = true -- Store existing kernel names for validation
local kernel_path = vim.fn.fnamemodify(data.spec.argv[1], ":p") -- Canonicalize the kernel path
if kernel_path:find(venv_path, 1, true) then
vim.notify("Kernel spec for this virtual environment already exists.", vim.log.levels.INFO)
return kernel_name
end
end
end

-- Prompt the user for a custom kernel name, ensuring it is unique
local new_kernel_name
repeat
new_kernel_name = vim.fn.input "Enter a unique name for the new kernel spec: "
if new_kernel_name == "" then
vim.notify("Please provide a valid kernel name.", vim.log.levels.ERROR)
return
elseif existing_kernels[new_kernel_name] then
vim.notify(
"Kernel name '" .. new_kernel_name .. "' already exists. Please choose another name.",
vim.log.levels.WARN
)
new_kernel_name = nil
end
until new_kernel_name

-- Create the kernel spec with the unique name
print "Creating a new kernel spec for this virtual environment..."
local cmd = string.format(
'%s -m ipykernel install --user --name="%s"',
vim.fn.shellescape(venv_path .. "/bin/python"),
new_kernel_name
)

os.execute(cmd)
vim.notify("Kernel spec '" .. new_kernel_name .. "' created successfully.", vim.log.levels.INFO)
return new_kernel_name
end

---@type LazySpec
return {
"benlubas/molten-nvim",
lazy = false,
version = "^1", -- use version <2.0.0 to avoid breaking changes
build = ":UpdateRemotePlugins",
dependencies = {
"AstroNvim/astrocore",
opts = function(_, opts)
if not opts.mappings then opts.mappings = {} end
local prefix = "<leader>m"

opts.mappings.n[prefix .. "mi"] = { "<Cmd>MoltenInit<CR>", desc = "Initialize the plugin" }
opts.mappings.n[prefix .. "e"] = { "<Cmd>MoltenEvaluateOperator<CR>", desc = "Run operator selection" }
opts.mappings.n[prefix .. "rl"] = { "<Cmd>MoltenEvaluateLine<CR>", desc = "Evaluate line" }
opts.mappings.n[prefix .. "rr"] = { "<Cmd>MoltenReevaluateCell<CR>", desc = "Re-evaluate cell" }
opts.mappings.v[prefix .. "r"] = { ":<C-u>MoltenEvaluateVisual<CR>gv", desc = "Evaluate visual selection" }
end,
{ "AstroNvim/astroui", opts = { icons = { Molten = "󱓞" } } },
{
"AstroNvim/astrocore",
opts = function(_, opts)
if not opts.mappings then opts.mappings = {} end
local prefix = "<leader>m"

opts.mappings.n[prefix] = { desc = require("astroui").get_icon("Molten", 1, true) .. "Molten" }
opts.mappings.n[prefix .. "e"] = { "<Cmd>MoltenEvaluateOperator<CR>", desc = "Run operator selection" }
opts.mappings.n[prefix .. "l"] = { "<Cmd>MoltenEvaluateLine<CR>", desc = "Evaluate line" }
opts.mappings.n[prefix .. "c"] = { "<Cmd>MoltenReevaluateCell<CR>", desc = "Re-evaluate cell" }

opts.mappings.n[prefix .. "m"] = { desc = "Commands" }
opts.mappings.n[prefix .. "mi"] = { "<Cmd>MoltenInit<CR>", desc = "Initialize the plugin" }
opts.mappings.n[prefix .. "mh"] = { "<Cmd>MoltenHideOutput<CR>", desc = "Hide Output" }
opts.mappings.n[prefix .. "mI"] = { "<Cmd>MoltenInterrupt<CR>", desc = "Interrupt Kernel" }
opts.mappings.n[prefix .. "mR"] = { "<Cmd>MoltenRestart<CR>", desc = "Restart Kernel" }
-- Dynamic Kernel Initialization based on Python Virtual Environment
opts.mappings.n[prefix .. "mp"] = {
function()
local kernel_name = ensure_kernel_for_venv()
if kernel_name then
vim.cmd(("MoltenInit %s"):format(kernel_name))
else
vim.notify("No kernel to initialize.", vim.log.levels.WARN)
end
end,
desc = "Initialize for Python venv",
silent = true,
}

opts.mappings.v[prefix] = { desc = require("astroui").get_icon("Molten", 1, true) .. "Molten" }
opts.mappings.v[prefix .. "r"] = { ":<C-u>MoltenEvaluateVisual<CR>gv", desc = "Evaluate visual selection" }

opts.mappings.n["]c"] = { "<Cmd>MoltenNext<CR>", desc = "Next Molten Cel" }
opts.mappings.n["[c"] = { "<Cmd>MoltenPrev<CR>", desc = "Previous Molten Cell" }
end,
},
{
-- Configure heirline.nvim to add the Molten status component
"rebelot/heirline.nvim",
opts = function(_, opts)
local utils = require "heirline.utils"
local molten_component = {
provider = function()
-- Display the status of Molten and attached kernels
local init_status = require("molten.status").initialized()
local kernel_status = require("molten.status").kernels()
local info = (init_status ~= "" and init_status .. ": ") .. kernel_status
return info ~= "" and info or ""
end,
condition = function() return require("molten.status").initialized() ~= "" end,
padding = { left = 1, right = 1 },
hl = function()
local theme_hl = utils.get_highlight "@comment.note"
return {
bg = theme_hl.bg,
fg = theme_hl.fg,
}
end,
}
local spacer = { provider = " " }
table.insert(opts.statusline, #opts.statusline, spacer)
table.insert(opts.statusline, #opts.statusline, molten_component)
end,
},
},
}
Loading