|
@@ -0,0 +1,219 @@
|
|
|
|
|
+local M = {}
|
|
|
|
|
+
|
|
|
|
|
+-- Helper function to detect clipboard command
|
|
|
|
|
+local function detect_clipboard_cmd()
|
|
|
|
|
+ -- Check for Wayland
|
|
|
|
|
+ if vim.fn.executable("wl-copy") == 1 then
|
|
|
|
|
+ return "wl-copy --type image/png"
|
|
|
|
|
+ end
|
|
|
|
|
+ -- Check for X11
|
|
|
|
|
+ if vim.fn.executable("xclip") == 1 then
|
|
|
|
|
+ return "xclip -selection clipboard -t image/png"
|
|
|
|
|
+ end
|
|
|
|
|
+ -- Check for macOS
|
|
|
|
|
+ if vim.fn.executable("pbcopy") == 1 then
|
|
|
|
|
+ return "pbcopy"
|
|
|
|
|
+ end
|
|
|
|
|
+ -- Default fallback (Wayland)
|
|
|
|
|
+ return "wl-copy --type image/png"
|
|
|
|
|
+end
|
|
|
|
|
+
|
|
|
|
|
+-- Configuration
|
|
|
|
|
+local config = {
|
|
|
|
|
+ freeze_cmd = "freeze",
|
|
|
|
|
+ clipboard_cmd = detect_clipboard_cmd(),
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+-- Helper function to detect visual selection
|
|
|
|
|
+local function get_visual_selection()
|
|
|
|
|
+ local mode = vim.fn.mode()
|
|
|
|
|
+
|
|
|
|
|
+ -- If we're in visual mode, capture directly from visual marks
|
|
|
|
|
+ if mode:match("[vV]") then
|
|
|
|
|
+ local v_pos = vim.fn.getpos("v") -- Start of visual selection
|
|
|
|
|
+ local dot_pos = vim.fn.getpos(".") -- Current cursor position (end of selection)
|
|
|
|
|
+
|
|
|
|
|
+ if v_pos[2] > 0 and dot_pos[2] > 0 then
|
|
|
|
|
+ return math.min(v_pos[2], dot_pos[2]), math.max(v_pos[2], dot_pos[2])
|
|
|
|
|
+ end
|
|
|
|
|
+ end
|
|
|
|
|
+
|
|
|
|
|
+ return nil, nil
|
|
|
|
|
+end
|
|
|
|
|
+
|
|
|
|
|
+-- Helper function to get language from filetype
|
|
|
|
|
+local function get_language_from_filetype()
|
|
|
|
|
+ local filetype = vim.bo.filetype
|
|
|
|
|
+ if filetype == "" then
|
|
|
|
|
+ return nil
|
|
|
|
|
+ end
|
|
|
|
|
+ return filetype
|
|
|
|
|
+end
|
|
|
|
|
+
|
|
|
|
|
+-- Helper function to validate yes/no input
|
|
|
|
|
+local function parse_yes_no(input, default)
|
|
|
|
|
+ if not input or input == "" then
|
|
|
|
|
+ return default
|
|
|
|
|
+ end
|
|
|
|
|
+
|
|
|
|
|
+ local lower = string.lower(input)
|
|
|
|
|
+ if lower == "y" or lower == "yes" then
|
|
|
|
|
+ return true
|
|
|
|
|
+ elseif lower == "n" or lower == "no" then
|
|
|
|
|
+ return false
|
|
|
|
|
+ end
|
|
|
|
|
+
|
|
|
|
|
+ return default
|
|
|
|
|
+end
|
|
|
|
|
+
|
|
|
|
|
+-- Helper function to build freeze command
|
|
|
|
|
+local function build_freeze_command(filepath, output, opts)
|
|
|
|
|
+ local cmd = config.freeze_cmd .. " " .. vim.fn.shellescape(filepath)
|
|
|
|
|
+
|
|
|
|
|
+ if opts.language then
|
|
|
|
|
+ cmd = cmd .. " --language " .. vim.fn.shellescape(opts.language)
|
|
|
|
|
+ end
|
|
|
|
|
+
|
|
|
|
|
+ if opts.show_line_numbers then
|
|
|
|
|
+ cmd = cmd .. " --show-line-numbers"
|
|
|
|
|
+ end
|
|
|
|
|
+
|
|
|
|
|
+ if opts.window then
|
|
|
|
|
+ cmd = cmd .. " --window"
|
|
|
|
|
+ end
|
|
|
|
|
+
|
|
|
|
|
+ if opts.start_line and opts.end_line then
|
|
|
|
|
+ cmd = cmd .. " --lines " .. opts.start_line .. "," .. opts.end_line
|
|
|
|
|
+ end
|
|
|
|
|
+
|
|
|
|
|
+ cmd = cmd .. " -o " .. vim.fn.shellescape(output)
|
|
|
|
|
+
|
|
|
|
|
+ return cmd
|
|
|
|
|
+end
|
|
|
|
|
+
|
|
|
|
|
+-- Helper function to execute freeze and copy to clipboard
|
|
|
|
|
+local function execute_freeze(cmd, output)
|
|
|
|
|
+ vim.fn.system(cmd)
|
|
|
|
|
+ local clipboard_cmd = config.clipboard_cmd .. " < " .. vim.fn.shellescape(output)
|
|
|
|
|
+ vim.fn.system(clipboard_cmd)
|
|
|
|
|
+ vim.fn.delete(output)
|
|
|
|
|
+end
|
|
|
|
|
+
|
|
|
|
|
+local function freeze_screenshot(start_line, end_line)
|
|
|
|
|
+ local filepath = vim.fn.expand("%:p")
|
|
|
|
|
+ if filepath == "" then
|
|
|
|
|
+ vim.notify("No file to screenshot", vim.log.levels.ERROR)
|
|
|
|
|
+ return
|
|
|
|
|
+ end
|
|
|
|
|
+
|
|
|
|
|
+ -- Auto-detect visual selection if not provided
|
|
|
|
|
+ if not start_line or not end_line then
|
|
|
|
|
+ start_line, end_line = get_visual_selection()
|
|
|
|
|
+ end
|
|
|
|
|
+
|
|
|
|
|
+ local output = vim.fn.tempname() .. ".png"
|
|
|
|
|
+ local cmd = config.freeze_cmd .. " " .. vim.fn.shellescape(filepath) .. " --show-line-numbers -o " .. vim.fn.shellescape(output)
|
|
|
|
|
+
|
|
|
|
|
+ if start_line and end_line then
|
|
|
|
|
+ cmd = cmd .. " --lines " .. start_line .. "," .. end_line
|
|
|
|
|
+ end
|
|
|
|
|
+
|
|
|
|
|
+ execute_freeze(cmd, output)
|
|
|
|
|
+
|
|
|
|
|
+ local msg = start_line and string.format("Screenshot of lines %d–%d copied", start_line, end_line)
|
|
|
|
|
+ or "Screenshot of entire file copied"
|
|
|
|
|
+ vim.notify(msg, vim.log.levels.INFO)
|
|
|
|
|
+end
|
|
|
|
|
+
|
|
|
|
|
+local function freeze_interactive(start_line, end_line)
|
|
|
|
|
+ local filepath = vim.fn.expand("%:p")
|
|
|
|
|
+ if filepath == "" then
|
|
|
|
|
+ vim.notify("No file to screenshot", vim.log.levels.ERROR)
|
|
|
|
|
+ return
|
|
|
|
|
+ end
|
|
|
|
|
+
|
|
|
|
|
+ -- Check if snacks.nvim is available
|
|
|
|
|
+ local ok, snacks = pcall(require, "snacks")
|
|
|
|
|
+ if not ok or not snacks or not snacks.input then
|
|
|
|
|
+ vim.notify("snacks.nvim is required for interactive mode", vim.log.levels.ERROR)
|
|
|
|
|
+ return
|
|
|
|
|
+ end
|
|
|
|
|
+
|
|
|
|
|
+ -- Auto-detect visual selection if not provided
|
|
|
|
|
+ if not start_line or not end_line then
|
|
|
|
|
+ start_line, end_line = get_visual_selection()
|
|
|
|
|
+ end
|
|
|
|
|
+
|
|
|
|
|
+ local opts = {
|
|
|
|
|
+ show_line_numbers = true, -- default: yes
|
|
|
|
|
+ window = false, -- default: no
|
|
|
|
|
+ language = get_language_from_filetype(),
|
|
|
|
|
+ start_line = start_line,
|
|
|
|
|
+ end_line = end_line,
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ -- First prompt: show line numbers
|
|
|
|
|
+ snacks.input.input({
|
|
|
|
|
+ prompt = "Mostrar números de línea? (y/n)",
|
|
|
|
|
+ default = "y",
|
|
|
|
|
+ }, function(value)
|
|
|
|
|
+ if value then
|
|
|
|
|
+ opts.show_line_numbers = parse_yes_no(value, true)
|
|
|
|
|
+ end
|
|
|
|
|
+
|
|
|
|
|
+ -- Second prompt: show window controls
|
|
|
|
|
+ snacks.input.input({
|
|
|
|
|
+ prompt = "Mostrar controles de ventana? (y/n)",
|
|
|
|
|
+ default = "n",
|
|
|
|
|
+ }, function(value2)
|
|
|
|
|
+ if value2 then
|
|
|
|
|
+ opts.window = parse_yes_no(value2, false)
|
|
|
|
|
+ end
|
|
|
|
|
+
|
|
|
|
|
+ -- Execute freeze with selected options
|
|
|
|
|
+ local output = vim.fn.tempname() .. ".png"
|
|
|
|
|
+ local cmd = build_freeze_command(filepath, output, opts)
|
|
|
|
|
+ execute_freeze(cmd, output)
|
|
|
|
|
+
|
|
|
|
|
+ local msg = start_line and string.format("Screenshot of lines %d–%d copied", start_line, end_line)
|
|
|
|
|
+ or "Screenshot copied to clipboard"
|
|
|
|
|
+ vim.notify(msg, vim.log.levels.INFO)
|
|
|
|
|
+ end)
|
|
|
|
|
+ end)
|
|
|
|
|
+end
|
|
|
|
|
+
|
|
|
|
|
+function M.setup(opts)
|
|
|
|
|
+ opts = opts or {}
|
|
|
|
|
+
|
|
|
|
|
+ -- Configure freeze command path
|
|
|
|
|
+ if opts.freeze_cmd then
|
|
|
|
|
+ config.freeze_cmd = opts.freeze_cmd
|
|
|
|
|
+ end
|
|
|
|
|
+
|
|
|
|
|
+ -- Configure clipboard command
|
|
|
|
|
+ if opts.clipboard_cmd then
|
|
|
|
|
+ config.clipboard_cmd = opts.clipboard_cmd
|
|
|
|
|
+ end
|
|
|
|
|
+end
|
|
|
|
|
+
|
|
|
|
|
+-- Wrapper functions that properly handle visual mode
|
|
|
|
|
+-- These should be used in keymaps to ensure selection is captured correctly
|
|
|
|
|
+local function freeze_screenshot_wrapper()
|
|
|
|
|
+ -- Try to get visual selection - this will work if called from visual mode keymap
|
|
|
|
|
+ local start_line, end_line = get_visual_selection()
|
|
|
|
|
+ freeze_screenshot(start_line, end_line)
|
|
|
|
|
+end
|
|
|
|
|
+
|
|
|
|
|
+local function freeze_interactive_wrapper()
|
|
|
|
|
+ -- Try to get visual selection - this will work if called from visual mode keymap
|
|
|
|
|
+ local start_line, end_line = get_visual_selection()
|
|
|
|
|
+ freeze_interactive(start_line, end_line)
|
|
|
|
|
+end
|
|
|
|
|
+
|
|
|
|
|
+M.freeze_screenshot = freeze_screenshot
|
|
|
|
|
+M.freeze_interactive = freeze_interactive
|
|
|
|
|
+M.freeze_screenshot_wrapper = freeze_screenshot_wrapper
|
|
|
|
|
+M.freeze_interactive_wrapper = freeze_interactive_wrapper
|
|
|
|
|
+
|
|
|
|
|
+return M
|
|
|
|
|
+
|