init.lua 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. local M = {}
  2. -- Helper function to detect clipboard command
  3. local function detect_clipboard_cmd()
  4. -- Check for Wayland
  5. if vim.fn.executable("wl-copy") == 1 then
  6. return "wl-copy --type image/png"
  7. end
  8. -- Check for X11
  9. if vim.fn.executable("xclip") == 1 then
  10. return "xclip -selection clipboard -t image/png"
  11. end
  12. -- Check for macOS
  13. if vim.fn.executable("pbcopy") == 1 then
  14. return "pbcopy"
  15. end
  16. -- Default fallback (Wayland)
  17. return "wl-copy --type image/png"
  18. end
  19. -- Configuration
  20. local config = {
  21. freeze_cmd = "freeze",
  22. clipboard_cmd = detect_clipboard_cmd(),
  23. }
  24. -- Helper function to detect visual selection
  25. local function get_visual_selection()
  26. local mode = vim.fn.mode()
  27. -- If we're in visual mode, capture directly from visual marks
  28. if mode:match("[vV]") then
  29. local v_pos = vim.fn.getpos("v") -- Start of visual selection
  30. local dot_pos = vim.fn.getpos(".") -- Current cursor position (end of selection)
  31. if v_pos[2] > 0 and dot_pos[2] > 0 then
  32. return math.min(v_pos[2], dot_pos[2]), math.max(v_pos[2], dot_pos[2])
  33. end
  34. end
  35. return nil, nil
  36. end
  37. -- Helper function to get language from filetype
  38. local function get_language_from_filetype()
  39. local filetype = vim.bo.filetype
  40. if filetype == "" then
  41. return nil
  42. end
  43. return filetype
  44. end
  45. -- Helper function to validate yes/no input
  46. local function parse_yes_no(input, default)
  47. if not input or input == "" then
  48. return default
  49. end
  50. local lower = string.lower(input)
  51. if lower == "y" or lower == "yes" then
  52. return true
  53. elseif lower == "n" or lower == "no" then
  54. return false
  55. end
  56. return default
  57. end
  58. -- Helper function to build freeze command
  59. local function build_freeze_command(filepath, output, opts)
  60. local cmd = config.freeze_cmd .. " " .. vim.fn.shellescape(filepath)
  61. if opts.language then
  62. cmd = cmd .. " --language " .. vim.fn.shellescape(opts.language)
  63. end
  64. if opts.show_line_numbers then
  65. cmd = cmd .. " --show-line-numbers"
  66. end
  67. if opts.window then
  68. cmd = cmd .. " --window"
  69. end
  70. if opts.start_line and opts.end_line then
  71. cmd = cmd .. " --lines " .. opts.start_line .. "," .. opts.end_line
  72. end
  73. cmd = cmd .. " -o " .. vim.fn.shellescape(output)
  74. return cmd
  75. end
  76. -- Helper function to execute freeze and copy to clipboard
  77. local function execute_freeze(cmd, output)
  78. vim.fn.system(cmd)
  79. local clipboard_cmd = config.clipboard_cmd .. " < " .. vim.fn.shellescape(output)
  80. vim.fn.system(clipboard_cmd)
  81. vim.fn.delete(output)
  82. end
  83. local function freeze_screenshot(start_line, end_line)
  84. local filepath = vim.fn.expand("%:p")
  85. if filepath == "" then
  86. vim.notify("No file to screenshot", vim.log.levels.ERROR)
  87. return
  88. end
  89. -- Auto-detect visual selection if not provided
  90. if not start_line or not end_line then
  91. start_line, end_line = get_visual_selection()
  92. end
  93. local output = vim.fn.tempname() .. ".png"
  94. local cmd = config.freeze_cmd .. " " .. vim.fn.shellescape(filepath) .. " --show-line-numbers -o " .. vim.fn.shellescape(output)
  95. if start_line and end_line then
  96. cmd = cmd .. " --lines " .. start_line .. "," .. end_line
  97. end
  98. execute_freeze(cmd, output)
  99. local msg = start_line and string.format("Screenshot of lines %d–%d copied", start_line, end_line)
  100. or "Screenshot of entire file copied"
  101. vim.notify(msg, vim.log.levels.INFO)
  102. end
  103. local function freeze_interactive(start_line, end_line)
  104. local filepath = vim.fn.expand("%:p")
  105. if filepath == "" then
  106. vim.notify("No file to screenshot", vim.log.levels.ERROR)
  107. return
  108. end
  109. -- Check if snacks.nvim is available
  110. local ok, snacks = pcall(require, "snacks")
  111. if not ok or not snacks or not snacks.input then
  112. vim.notify("snacks.nvim is required for interactive mode", vim.log.levels.ERROR)
  113. return
  114. end
  115. -- Auto-detect visual selection if not provided
  116. if not start_line or not end_line then
  117. start_line, end_line = get_visual_selection()
  118. end
  119. local opts = {
  120. show_line_numbers = true, -- default: yes
  121. window = false, -- default: no
  122. language = get_language_from_filetype(),
  123. start_line = start_line,
  124. end_line = end_line,
  125. }
  126. -- First prompt: show line numbers
  127. snacks.input.input({
  128. prompt = "Mostrar números de línea? (y/n)",
  129. default = "y",
  130. }, function(value)
  131. if value then
  132. opts.show_line_numbers = parse_yes_no(value, true)
  133. end
  134. -- Second prompt: show window controls
  135. snacks.input.input({
  136. prompt = "Mostrar controles de ventana? (y/n)",
  137. default = "n",
  138. }, function(value2)
  139. if value2 then
  140. opts.window = parse_yes_no(value2, false)
  141. end
  142. -- Execute freeze with selected options
  143. local output = vim.fn.tempname() .. ".png"
  144. local cmd = build_freeze_command(filepath, output, opts)
  145. execute_freeze(cmd, output)
  146. local msg = start_line and string.format("Screenshot of lines %d–%d copied", start_line, end_line)
  147. or "Screenshot copied to clipboard"
  148. vim.notify(msg, vim.log.levels.INFO)
  149. end)
  150. end)
  151. end
  152. function M.setup(opts)
  153. opts = opts or {}
  154. -- Configure freeze command path
  155. if opts.freeze_cmd then
  156. config.freeze_cmd = opts.freeze_cmd
  157. end
  158. -- Configure clipboard command
  159. if opts.clipboard_cmd then
  160. config.clipboard_cmd = opts.clipboard_cmd
  161. end
  162. end
  163. -- Wrapper functions that properly handle visual mode
  164. -- These should be used in keymaps to ensure selection is captured correctly
  165. local function freeze_screenshot_wrapper()
  166. -- Try to get visual selection - this will work if called from visual mode keymap
  167. local start_line, end_line = get_visual_selection()
  168. freeze_screenshot(start_line, end_line)
  169. end
  170. local function freeze_interactive_wrapper()
  171. -- Try to get visual selection - this will work if called from visual mode keymap
  172. local start_line, end_line = get_visual_selection()
  173. freeze_interactive(start_line, end_line)
  174. end
  175. M.freeze_screenshot = freeze_screenshot
  176. M.freeze_interactive = freeze_interactive
  177. M.freeze_screenshot_wrapper = freeze_screenshot_wrapper
  178. M.freeze_interactive_wrapper = freeze_interactive_wrapper
  179. return M