Skip to content
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
36 changes: 31 additions & 5 deletions lua/r/run.lua
Original file line number Diff line number Diff line change
Expand Up @@ -433,13 +433,39 @@ M.insert = function(cmd, type)
end

M.insert_commented = function()
local lin = vim.api.nvim_get_current_line()
local cleanl = lin:gsub('".-"', "")
if cleanl:find(";") then
local bufnr = require("r.buffer").create_r_buffer()
local code, end_row

if bufnr then
code, end_row = send.get_pipe_chain(bufnr, true)
end

if code then
-- Strip comments and collapse to single line
local lines = {}
for line in code:gmatch("[^\n]+") do
local stripped = line:gsub("%s*#.*", "")
if stripped:match("%S") then
table.insert(lines, stripped)
end
end
code = table.concat(lines, " ")
-- Move cursor to end of chain
if end_row then
vim.api.nvim_win_set_cursor(0, { end_row, 0 })
end
else
-- Fallback to single line
code = vim.api.nvim_get_current_line():gsub("%s*#.*", "")
end

if not code:match("%S") then return end

local check = code:gsub('".-"', "")
if check:find(";") then
warn("`print(line)` works only if `line` is a single command")
end
cleanl = string.gsub(lin, "%s*#.*", "")
M.insert("print(" .. cleanl .. ")", "comment")
M.insert("print(" .. code .. ")", "comment")
end

---Call R functions for the word under cursor
Expand Down
76 changes: 59 additions & 17 deletions lua/r/send.lua
Original file line number Diff line number Diff line change
Expand Up @@ -638,16 +638,17 @@ M.line = function(m)
end
end

--- Send the above chain of piped commands
M.chain = function()
local bufnr = create_r_buffer()
if not bufnr then return end

--- Get the pipe chain node at cursor position
--- @param bufnr integer Buffer number
--- @return table|nil pipe_node The pipe chain node, or nil
--- @return table|nil root The tree root
--- @return integer cursor_row The cursor row (0-indexed)
local function get_pipe_node(bufnr)
local parser = vim.treesitter.get_parser(bufnr, "r")
if not parser then return end
if not parser then return nil end

local tree = parser:parse()[1]
if not tree then return end
if not tree then return nil end

local root = tree:root()
local query = vim.treesitter.query.parse(
Expand Down Expand Up @@ -677,17 +678,50 @@ M.chain = function()
)

local cursor_row = vim.api.nvim_win_get_cursor(0)[1] - 1
local pipe_block_node

for _, node in query:iter_captures(root, bufnr, 0, -1) do
local start_row, _, end_row = node:range()
if cursor_row >= start_row and cursor_row <= end_row then
pipe_block_node = node
break
return node, root, cursor_row
end
end
return nil
end

--- Get the full pipe chain code at cursor (with optional assignment)
--- @param bufnr integer Buffer number
--- @param include_assignment boolean Include parent assignment if present
--- @return string|nil code The code, or nil if not in a pipe chain
--- @return integer|nil end_row Last row of the chain (1-indexed)
M.get_pipe_chain = function(bufnr, include_assignment)
local pipe_node, _, _ = get_pipe_node(bufnr)
if not pipe_node then return nil, nil end

local node = pipe_node
if include_assignment then
local parent = pipe_node:parent()
if parent and parent:type() == "binary_operator" then
local op = parent:field("operator")[1]
if op then
local op_text = vim.treesitter.get_node_text(op, bufnr)
if op_text == "<-" or op_text == "=" then
node = parent
end
end
end
end

if not pipe_block_node then
local _, _, end_row = node:range()
return vim.treesitter.get_node_text(node, bufnr), end_row + 1
end

--- Send the above chain of piped commands
M.chain = function()
local bufnr = create_r_buffer()
if not bufnr then return end

local pipe_node, root, cursor_row = get_pipe_node(bufnr)
if not pipe_node then
inform("The cursor is not inside a piped expression.")
return
end
Expand All @@ -703,21 +737,29 @@ M.chain = function()
)

local sibling = nil
local last_sibling = nil
local pipe_start_row, _, pipe_end_row = pipe_node:range()

local pipe_start_row, _, pipe_end_row = pipe_block_node:range()
-- Check if cursor is on a comment line
local cur_line = vim.api.nvim_buf_get_lines(bufnr, cursor_row, cursor_row + 1, false)[1] or ""
local on_comment = cur_line:match("^%s*#") ~= nil

for id, node, _ in call_query:iter_captures(root, bufnr, pipe_start_row, pipe_end_row) do
local capture_name = call_query.captures[id]
local _, _, end_row = node:range()

if capture_name == "operator" and cursor_row <= end_row then
sibling = node:prev_sibling()
break
if capture_name == "operator" then
if cursor_row <= end_row then
sibling = node:prev_sibling()
break
end
-- Track last operator before cursor (for comment line case)
last_sibling = node:prev_sibling()
end
end

local captured_node = sibling or pipe_block_node

-- If on comment and no match found, use last operator before comment
local captured_node = sibling or (on_comment and last_sibling) or pipe_node
M.source_lines({ vim.treesitter.get_node_text(captured_node, bufnr) }, nil)
end

Expand Down