Skip to content

Commit 604389e

Browse files
Fix visual selection range handling for :'<,'>ClaudeCodeSend
- Add get_range_selection() function to handle range-based selections - Modify send_at_mention_for_visual_selection() to accept line1/line2 parameters - Update ClaudeCodeSend command to pass range information when available - Fixes issue where :'<,'>ClaudeCodeSend would show 'No visual selection' warning Fixes #25 Co-authored-by: ThomasK33 <[email protected]>
1 parent b822036 commit 604389e

File tree

2 files changed

+93
-11
lines changed

2 files changed

+93
-11
lines changed

lua/claudecode/init.lua

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,17 @@ function M._create_commands()
402402
vim.notify("Claude Code integration is not running", vim.log.levels.ERROR)
403403
return
404404
end
405+
logger.debug(
406+
"command",
407+
"ClaudeCodeSend invoked. Mode: "
408+
.. vim.fn.mode(true)
409+
.. ", Range: "
410+
.. tostring(opts and opts.range)
411+
.. ", Line1: "
412+
.. tostring(opts and opts.line1)
413+
.. ", Line2: "
414+
.. tostring(opts and opts.line2)
415+
)
405416

406417
local current_ft = vim.bo.filetype
407418
local current_bufname = vim.api.nvim_buf_get_name(0)
@@ -434,7 +445,12 @@ function M._create_commands()
434445

435446
local selection_module_ok, selection_module = pcall(require, "claudecode.selection")
436447
if selection_module_ok then
437-
local sent_successfully = selection_module.send_at_mention_for_visual_selection()
448+
-- Pass range information if available (for :'<,'> commands)
449+
local line1, line2 = nil, nil
450+
if opts and opts.range and opts.range > 0 then
451+
line1, line2 = opts.line1, opts.line2
452+
end
453+
local sent_successfully = selection_module.send_at_mention_for_visual_selection(line1, line2)
438454
if sent_successfully then
439455
local terminal_ok, terminal = pcall(require, "claudecode.terminal")
440456
if terminal_ok then

lua/claudecode/selection.lua

Lines changed: 76 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -573,25 +573,91 @@ function M.send_current_selection()
573573
vim.api.nvim_echo({ { "Selection sent to Claude", "Normal" } }, false, {})
574574
end
575575

576+
--- Gets selection from range marks (e.g., when using :'<,'> commands)
577+
-- @param line1 number The start line (1-indexed)
578+
-- @param line2 number The end line (1-indexed)
579+
-- @return table|nil A table containing selection text, file path, URL, and
580+
-- start/end positions, or nil if invalid range
581+
function M.get_range_selection(line1, line2)
582+
if not line1 or not line2 or line1 < 1 or line2 < 1 or line1 > line2 then
583+
return nil
584+
end
585+
586+
local current_buf = vim.api.nvim_get_current_buf()
587+
local file_path = vim.api.nvim_buf_get_name(current_buf)
588+
589+
-- Get the total number of lines in the buffer
590+
local total_lines = vim.api.nvim_buf_line_count(current_buf)
591+
592+
-- Ensure line2 doesn't exceed buffer bounds
593+
if line2 > total_lines then
594+
line2 = total_lines
595+
end
596+
597+
local lines_content = vim.api.nvim_buf_get_lines(
598+
current_buf,
599+
line1 - 1, -- Convert to 0-indexed
600+
line2, -- nvim_buf_get_lines end is exclusive
601+
false
602+
)
603+
604+
if #lines_content == 0 then
605+
return nil
606+
end
607+
608+
local final_text = table.concat(lines_content, "\n")
609+
610+
-- For range selections, we treat them as linewise
611+
local lsp_start_line = line1 - 1 -- Convert to 0-indexed
612+
local lsp_end_line = line2 - 1
613+
local lsp_start_char = 0
614+
local lsp_end_char = #lines_content[#lines_content]
615+
616+
return {
617+
text = final_text or "",
618+
filePath = file_path,
619+
fileUrl = "file://" .. file_path,
620+
selection = {
621+
start = { line = lsp_start_line, character = lsp_start_char },
622+
["end"] = { line = lsp_end_line, character = lsp_end_char },
623+
isEmpty = (not final_text or #final_text == 0),
624+
},
625+
}
626+
end
627+
576628
--- Sends an at_mentioned notification for the current visual selection.
577-
function M.send_at_mention_for_visual_selection()
629+
-- @param line1 number|nil Optional start line for range-based selection
630+
-- @param line2 number|nil Optional end line for range-based selection
631+
function M.send_at_mention_for_visual_selection(line1, line2)
578632
if not M.state.tracking_enabled or not M.server then
579633
logger.error("selection", "Claude Code is not running or server not available for send_at_mention.")
580634
return false
581635
end
582636

583-
local sel_to_send = M.state.latest_selection
637+
local sel_to_send
584638

585-
if not sel_to_send or sel_to_send.selection.isEmpty then
586-
-- Fallback: try to get current visual selection directly.
587-
-- This helps if latest_selection was demoted or command was too fast.
588-
local current_visual = M.get_visual_selection()
589-
if current_visual and not current_visual.selection.isEmpty then
590-
sel_to_send = current_visual
591-
else
592-
logger.warn("selection", "No visual selection to send as at-mention.")
639+
-- If range parameters are provided, use them (for :'<,'> commands)
640+
if line1 and line2 then
641+
sel_to_send = M.get_range_selection(line1, line2)
642+
if not sel_to_send or sel_to_send.selection.isEmpty then
643+
logger.warn("selection", "Invalid range selection to send as at-mention.")
593644
return false
594645
end
646+
else
647+
-- Use existing logic for visual mode or tracked selection
648+
sel_to_send = M.state.latest_selection
649+
650+
if not sel_to_send or sel_to_send.selection.isEmpty then
651+
-- Fallback: try to get current visual selection directly.
652+
-- This helps if latest_selection was demoted or command was too fast.
653+
local current_visual = M.get_visual_selection()
654+
if current_visual and not current_visual.selection.isEmpty then
655+
sel_to_send = current_visual
656+
else
657+
logger.warn("selection", "No visual selection to send as at-mention.")
658+
return false
659+
end
660+
end
595661
end
596662

597663
-- Sanity check: ensure the selection is for the current buffer

0 commit comments

Comments
 (0)