@@ -8,6 +8,7 @@ local send_dispatch_mod = require("codex.runtime.send_dispatch")
88local mention_mod = require (" codex.context.mention" )
99local prompt_submit = require (" codex.context.prompt_submit" )
1010
11+ local CTRL_V = string.char (22 )
1112local SAVED_PROMPT_NOTIFY_MSG = " Saved current prompt to unnamed register"
1213local COULD_NOT_SAVE_PROMPT_NOTIFY_MSG = " Could not save existing prompt before clearing"
1314
@@ -377,6 +378,104 @@ local function log_selection_failure(deps, subject, err)
377378 deps .logger .error (" failed to collect %s: %s" , target , err or " unknown error" )
378379end
379380
381+ --- Check whether a value is an integer >= 1.
382+ --- @param value any
383+ --- @return boolean
384+ local function is_positive_integer (value )
385+ return type (value ) == " number" and value >= 1 and math.floor (value ) == value
386+ end
387+
388+ --- Check whether a value is an integer >= 0.
389+ --- @param value any
390+ --- @return boolean
391+ local function is_non_negative_integer (value )
392+ return type (value ) == " number" and value >= 0 and math.floor (value ) == value
393+ end
394+
395+ --- Return a shallow copy of a possibly nil table.
396+ --- @param opts table | nil
397+ --- @return table
398+ local function copy_opts (opts )
399+ local copied = {}
400+ for key , value in pairs (opts or {}) do
401+ copied [key ] = value
402+ end
403+ return copied
404+ end
405+
406+ --- Resolve active visual selection metadata when explicit range opts are missing.
407+ --- This supports first-use lazy-key visual mappings where visual marks may be unset.
408+ --- @param deps table
409+ --- @param opts ? codex.SelectionOpts
410+ --- @return codex.SelectionOpts
411+ local function resolve_selection_opts (deps , opts )
412+ local resolved = copy_opts (opts )
413+ if is_positive_integer (resolved .line1 ) and is_positive_integer (resolved .line2 ) then
414+ return resolved
415+ end
416+
417+ local fn = deps .vim .fn or {}
418+ if type (fn .mode ) ~= " function" then
419+ return resolved
420+ end
421+
422+ local ok_mode , visual_mode = pcall (fn .mode , 1 )
423+ if not ok_mode then
424+ return resolved
425+ end
426+ if visual_mode ~= " v" and visual_mode ~= " V" and visual_mode ~= CTRL_V then
427+ return resolved
428+ end
429+
430+ if resolved .visual_mode == nil then
431+ resolved .visual_mode = visual_mode
432+ end
433+
434+ if type (fn .getpos ) ~= " function" then
435+ return resolved
436+ end
437+ local api = deps .vim .api or {}
438+ if type (api .nvim_win_get_cursor ) ~= " function" then
439+ return resolved
440+ end
441+
442+ local ok_anchor , anchor = pcall (fn .getpos , " v" )
443+ local ok_cursor , cursor = pcall (api .nvim_win_get_cursor , 0 )
444+ if not ok_anchor or type (anchor ) ~= " table" then
445+ return resolved
446+ end
447+ if not ok_cursor or type (cursor ) ~= " table" then
448+ return resolved
449+ end
450+
451+ local anchor_line = anchor [2 ]
452+ local anchor_col = anchor [3 ]
453+ local cursor_line = cursor [1 ]
454+ local cursor_col = cursor [2 ]
455+
456+ if not is_positive_integer (anchor_line ) or not is_positive_integer (cursor_line ) then
457+ return resolved
458+ end
459+ if not is_positive_integer (anchor_col ) or not is_non_negative_integer (cursor_col ) then
460+ return resolved
461+ end
462+
463+ if not is_positive_integer (resolved .line1 ) then
464+ resolved .line1 = anchor_line
465+ end
466+ if not is_positive_integer (resolved .line2 ) then
467+ resolved .line2 = cursor_line
468+ end
469+ if not is_non_negative_integer (resolved .start_col ) then
470+ resolved .start_col = anchor_col - 1
471+ end
472+ if not is_non_negative_integer (resolved .end_col ) then
473+ resolved .end_col = cursor_col
474+ end
475+
476+ return resolved
477+ end
478+
380479--- Formats current buffer reference and sends it as bracketed paste.
381480--- @param opts ? codex.SelectionOpts Buffer override via ` opts.bufnr` .
382481--- @return codex.SendResult ok True when buffer payload is sent.
404503function M .send_selection (opts )
405504 ensure_setup ()
406505 local deps = get_deps ()
407- local spec , err = deps .selection .get_visual_selection (deps .vim , opts )
506+ local spec , err =
507+ deps .selection .get_visual_selection (deps .vim , resolve_selection_opts (deps , opts ))
408508 if not spec then
409509 log_selection_failure (deps , " selection" , err )
410510 return false , err
0 commit comments