Run current python cell
Closed this issue · 4 comments
Is your feature request related to a problem? Please describe.
I would like to be able to run a python cell (delimited by delimiters such as # %%
)
Describe the solution you'd like
running :SnipRunCell
would (only for filetype python
) detect the beginning and end of the cell, and run this section, similarly to the visual mode '<,'>SnipRun
Describe alternatives you've considered
I currently have a shortcut to visually select the current cell, and then I run '<,'>SnipRun
manually (but my cursor moves after I exit the visual selection), using the following code:
-- Function to select the current Python cell delimited by # %%
function SelectPythonCell()
local current_line = vim.fn.line "."
local start_line = current_line
local end_line = current_line
-- Find the start of the section
while start_line > 1 do
local line_content = vim.fn.getline(start_line - 1)
if line_content:match "^# %%%%" then break end
start_line = start_line - 1
end
-- Find the end of the section
local total_lines = vim.fn.line "$"
while end_line < total_lines do
local line_content = vim.fn.getline(end_line + 1)
if line_content:match "^# %%%%" then break end
end_line = end_line + 1
end
-- Construct the command string with the given start and end lines
local cmd = string.format("normal! %dGV%dG", start_line, end_line)
-- Execute the command
vim.cmd(cmd)
end
Additional context
I'm not knowledgeable enough with the source code of SnipRun to propose a fully functioning solution, so I hope that at least the above piece of code can be useful in this implementation.
Thank you in advance if you decide to implement this :)
While clearly not impossible (such features exist for literate programming language such as markdown/neorg/orgmode), it's always somewhat a mess (in sniprun's code) to implement that properly to some prod-ready extent.
(For example, what if you don't have a closing delimiter and are working on a huge file? Sniprun might choke if it tries to read GBs in order to find the delimiter)
There is a more straightforward solution for you since you're already halfway, see:
https://michaelb.github.io/sniprun/sources/README.html#mappings-recommandations
The 'moving cursor after selection' is a Neovim 'feature' that plagued sniprun for a long time so a workaround was eventually found. Hopefully you can adapt that script-fu into your shortcut, good luck
Oh yeah I see! Well since it's something that I really want, I spent a bit more time on it and finalized my function:
function Run_sniprun_section()
-- Check if the file type is Python
if vim.bo.filetype ~= "python" then
vim.notify("Run current cell is only available in Python files.", vim.log.levels.WARN)
return
end
-- save the cursor position to move back to it later
local cursor_pos = vim.api.nvim_win_get_cursor(0)
-- variables to find the cell delimitor
local current_line = vim.fn.line "."
local start_line = current_line
local end_line = current_line
-- Find the start of the cell
while start_line > 1 do
local line_content = vim.fn.getline(start_line - 1)
if line_content:match "^# %%%%" then break end
start_line = start_line - 1
end
-- Find the end of the cell
local total_lines = vim.fn.line "$"
while end_line < total_lines do
local line_content = vim.fn.getline(end_line + 1)
if line_content:match "^# %%%%" then break end
end_line = end_line + 1
end
-- Set the visual selection marks
vim.fn.setpos("'<", { 0, start_line, 1, 0 })
vim.fn.setpos("'>", { 0, end_line, 1, 0 })
-- Execute SnipRun on the visual selection
vim.cmd "'<,'>SnipRun"
-- Restore the cursor position
vim.api.nvim_win_set_cursor(0, cursor_pos)
end
Calling it manually with :lua Run_sniprun_section()
or with a keybind
I did a similar workaround as you for the cursor moving (saving the position initially, and then moving back to it).
(btw for non-scripted usage of visual mode I agree with the neovim feature to move the cursor, it would just be nice to have in the api the ability to disable the feature for scripting purposes)
Anyway, now to address your concern about the huge files, we can add a confirmation dialog if the number of lines in the cell is let's say above 100 lines (poorly written because of a copy paste towards the end, but it works):
function Run_sniprun_section()
-- Check if the file type is Python
if vim.bo.filetype ~= "python" then
vim.notify("Run current cell is only available in Python files.", vim.log.levels.WARN)
return
end
-- save the cursor position to move back to it later
local cursor_pos = vim.api.nvim_win_get_cursor(0)
-- variables to find the cell delimitor
local current_line = vim.fn.line "."
local start_line = current_line
local end_line = current_line
-- Find the start of the cell
while start_line > 1 do
local line_content = vim.fn.getline(start_line - 1)
if line_content:match "^# %%%%" then break end
start_line = start_line - 1
end
-- Find the end of the cell
local total_lines = vim.fn.line "$"
while end_line < total_lines do
local line_content = vim.fn.getline(end_line + 1)
if line_content:match "^# %%%%" then break end
end_line = end_line + 1
end
-- Check if cell contains more than 100
if (end_line - start_line) > 100 then
-- Define the options for the confirmation dialog
local choice = { "Yes", "No" }
-- Show the confirmation dialog
vim.ui.select(
choice,
{ prompt = "Cell containing more than 100 lines, do you want to run SnipRun?" },
function(chosen)
if chosen ~= "Yes" then
vim.notify("SnipRun command cancelled.", vim.log.levels.INFO)
return
end
-- Set the visual selection marks
vim.fn.setpos("'<", { 0, start_line, 1, 0 })
vim.fn.setpos("'>", { 0, end_line, 1, 0 })
-- Execute SnipRun on the visual selection
vim.cmd "'<,'>SnipRun"
-- Restore the cursor position
vim.api.nvim_win_set_cursor(0, cursor_pos)
end
)
else
-- Set the visual selection marks
vim.fn.setpos("'<", { 0, start_line, 1, 0 })
vim.fn.setpos("'>", { 0, end_line, 1, 0 })
-- Execute SnipRun on the visual selection
vim.cmd "'<,'>SnipRun"
-- Restore the cursor position
vim.api.nvim_win_set_cursor(0, cursor_pos)
end
end
Maybe this is not too far from being prod-ready for other people to use too?
Anyway, now to address your concern about the huge files,
This was but an example of the additional requirements doing this in sniprun would create
it would just be nice to have in the api the ability to disable the feature for scripting purposes
While I can't comment on Neovim's API, sniprun does expose an API that would work well with 'scripting' approaches, and also completely workarounds the cursor-move-when-selecting 'issue'
https://michaelb.github.io/sniprun/sources/README.html#api
While I don't think I'll implement 'code-bloc' detection in Python, you look like you already have a satisfying solution ?
Thanks I missed this in the documentation, this package is really well done!
Yeah i have a satisfying solution, I'll mark the issue "closed"