diff --git a/config/mini.nvim/plugin/copy_ref.lua b/config/mini.nvim/plugin/copy_ref.lua new file mode 100644 index 0000000..e63d593 --- /dev/null +++ b/config/mini.nvim/plugin/copy_ref.lua @@ -0,0 +1,131 @@ +-- from smnatale's github.com/smnatale/nvim-config +-- +-- Copy file path / selection reference for pasting into AI chats + +local scratch_buf = nil +local scratch_buf_name = "copy-refs" + +local function get_or_create_scratch() + -- if we already created a buffer and it's still valid, reuse it + if scratch_buf and vim.api.nvim_buf_is_valid(scratch_buf) then + return scratch_buf + end + + -- look for an existing buffer by name + for _, buf in ipairs(vim.api.nvim_list_bufs()) do + if vim.api.nvim_buf_is_valid(buf) then + local name = vim.api.nvim_buf_get_name(buf) + if name:match("/" .. scratch_buf_name .. "$") or name == scratch_buf_name then + scratch_buf = buf + return scratch_buf + end + end + end + + -- create a new unlisted scratch buffer + scratch_buf = vim.api.nvim_create_buf(false, true) + vim.api.nvim_buf_set_name(scratch_buf, scratch_buf_name) + vim.bo[scratch_buf].buftype = "nofile" + vim.bo[scratch_buf].bufhidden = "hide" + vim.bo[scratch_buf].swapfile = false + + return scratch_buf +end + +local function open_scratch() + local buf = get_or_create_scratch() + local width = math.floor(vim.o.columns * 0.8) + local height = math.floor(vim.o.lines * 0.8) + local row = math.floor((vim.o.lines - height) / 2) + local col = math.floor((vim.o.columns - width) / 2) + local _ = vim.api.nvim_open_win(buf, true, { + relative = "editor", + width = width, + height = height, + row = row, + col = col, + style = "minimal", + border = "rounded", + }) + vim.api.nvim_buf_set_keymap(buf, "n", "q", ":close", { noremap = true, silent = true }) +end + +local function copy_ref(opts) + -- "%" is the current buffer's file name; ":." makes it relative to the cwd + local path = vim.fn.expand("%:.") + -- ref is what ends up in the clipboard; start with just the path + local ref = path + + if opts.visual then + -- '< and '> are only set after leaving visual mode, so read the live selection: + -- "v" is the line where visual mode was started (the anchor) + local start_line = vim.fn.line("v") + -- "." is the line the cursor is on now (the moving end of the selection) + local end_line = vim.fn.line(".") + -- if the selection was made upward, swap so start is always the smaller line + if start_line > end_line then + start_line, end_line = end_line, start_line + end + -- append the range, e.g. "lua/config/keymaps.lua:1:23" + ref = path .. ":" .. start_line .. ":" .. end_line + end + + -- ask for an optional free-text note on the command line (Enter to skip) + local note = vim.fn.input("Prompt (optional): ") + if note ~= "" then + -- append the note after the ref, separated by a space + ref = ref .. " " .. note + end + + -- write ref into the "+" register, which is the system clipboard + vim.fn.setreg("+", ref) + + -- append to scratch buffer + local buf = get_or_create_scratch() + local lines = vim.api.nvim_buf_get_lines(buf, 0, -1, false) + local insert_pos = #lines + if insert_pos > 0 and lines[insert_pos] ~= "" then + vim.api.nvim_buf_set_lines(buf, insert_pos, insert_pos, false, { "", ref }) + else + vim.api.nvim_buf_set_lines(buf, insert_pos, insert_pos, false, { ref }) + end + + -- clear visual selection if we were in visual mode + if opts.visual then + vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes("", true, false, true), "n", false) + end + + -- show a confirmation message with what was copied + vim.notify("Copied: " .. ref) +end + +-- normal mode: copy just the file path +vim.keymap.set("n", "bc", function() + copy_ref({}) +end, { desc = "Copy file path" }) + +-- visual mode: copy the file path plus the selected line range +vim.keymap.set("v", "bc", function() + copy_ref({ visual = true }) +end, { desc = "Copy file path with line range" }) + +-- open the copy-ref scratch buffer +vim.keymap.set("n", "b.", function() + open_scratch() +end, { desc = "Open copy-ref scratch buffer" }) + +-- clear the copy-ref scratch buffer +vim.keymap.set("n", "bx", function() + local buf = get_or_create_scratch() + vim.api.nvim_buf_set_lines(buf, 0, -1, false, {}) + vim.notify("Cleared copy-refs buffer") +end, { desc = "Clear copy-ref scratch buffer" }) + +-- yank the entire copy-ref scratch buffer into the + register +vim.keymap.set("n", "by", function() + local buf = get_or_create_scratch() + local lines = vim.api.nvim_buf_get_lines(buf, 0, -1, false) + local content = table.concat(lines, "\n") + vim.fn.setreg("+", content) + vim.notify("Yanked copy-refs buffer (" .. #lines .. " lines)") +end, { desc = "Yank copy-ref scratch buffer" }) diff --git a/config/nvim/after/plugin/keymaps.lua b/config/nvim/after/plugin/keymaps.lua index 64088b6..633c2f1 100644 --- a/config/nvim/after/plugin/keymaps.lua +++ b/config/nvim/after/plugin/keymaps.lua @@ -42,7 +42,6 @@ vim.keymap.set("n", "bn", "enew", { desc = "New File" }) -- vim.keymap.set("n", "bd", "bdelete", { desc = "Unload Buffer" }) vim.keymap.set("n", "`", "e #", { desc = "Switch to Other Buffer" }) -vim.keymap.set("n", "bc", "tabclose", { desc = "Close Tab" }) -- Clear search with vim.keymap.set('n', '', 'nohlsearch', { desc = "clear hlsearch" }) diff --git a/config/nvim/plugin/copy_ref.lua b/config/nvim/plugin/copy_ref.lua new file mode 100644 index 0000000..e63d593 --- /dev/null +++ b/config/nvim/plugin/copy_ref.lua @@ -0,0 +1,131 @@ +-- from smnatale's github.com/smnatale/nvim-config +-- +-- Copy file path / selection reference for pasting into AI chats + +local scratch_buf = nil +local scratch_buf_name = "copy-refs" + +local function get_or_create_scratch() + -- if we already created a buffer and it's still valid, reuse it + if scratch_buf and vim.api.nvim_buf_is_valid(scratch_buf) then + return scratch_buf + end + + -- look for an existing buffer by name + for _, buf in ipairs(vim.api.nvim_list_bufs()) do + if vim.api.nvim_buf_is_valid(buf) then + local name = vim.api.nvim_buf_get_name(buf) + if name:match("/" .. scratch_buf_name .. "$") or name == scratch_buf_name then + scratch_buf = buf + return scratch_buf + end + end + end + + -- create a new unlisted scratch buffer + scratch_buf = vim.api.nvim_create_buf(false, true) + vim.api.nvim_buf_set_name(scratch_buf, scratch_buf_name) + vim.bo[scratch_buf].buftype = "nofile" + vim.bo[scratch_buf].bufhidden = "hide" + vim.bo[scratch_buf].swapfile = false + + return scratch_buf +end + +local function open_scratch() + local buf = get_or_create_scratch() + local width = math.floor(vim.o.columns * 0.8) + local height = math.floor(vim.o.lines * 0.8) + local row = math.floor((vim.o.lines - height) / 2) + local col = math.floor((vim.o.columns - width) / 2) + local _ = vim.api.nvim_open_win(buf, true, { + relative = "editor", + width = width, + height = height, + row = row, + col = col, + style = "minimal", + border = "rounded", + }) + vim.api.nvim_buf_set_keymap(buf, "n", "q", ":close", { noremap = true, silent = true }) +end + +local function copy_ref(opts) + -- "%" is the current buffer's file name; ":." makes it relative to the cwd + local path = vim.fn.expand("%:.") + -- ref is what ends up in the clipboard; start with just the path + local ref = path + + if opts.visual then + -- '< and '> are only set after leaving visual mode, so read the live selection: + -- "v" is the line where visual mode was started (the anchor) + local start_line = vim.fn.line("v") + -- "." is the line the cursor is on now (the moving end of the selection) + local end_line = vim.fn.line(".") + -- if the selection was made upward, swap so start is always the smaller line + if start_line > end_line then + start_line, end_line = end_line, start_line + end + -- append the range, e.g. "lua/config/keymaps.lua:1:23" + ref = path .. ":" .. start_line .. ":" .. end_line + end + + -- ask for an optional free-text note on the command line (Enter to skip) + local note = vim.fn.input("Prompt (optional): ") + if note ~= "" then + -- append the note after the ref, separated by a space + ref = ref .. " " .. note + end + + -- write ref into the "+" register, which is the system clipboard + vim.fn.setreg("+", ref) + + -- append to scratch buffer + local buf = get_or_create_scratch() + local lines = vim.api.nvim_buf_get_lines(buf, 0, -1, false) + local insert_pos = #lines + if insert_pos > 0 and lines[insert_pos] ~= "" then + vim.api.nvim_buf_set_lines(buf, insert_pos, insert_pos, false, { "", ref }) + else + vim.api.nvim_buf_set_lines(buf, insert_pos, insert_pos, false, { ref }) + end + + -- clear visual selection if we were in visual mode + if opts.visual then + vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes("", true, false, true), "n", false) + end + + -- show a confirmation message with what was copied + vim.notify("Copied: " .. ref) +end + +-- normal mode: copy just the file path +vim.keymap.set("n", "bc", function() + copy_ref({}) +end, { desc = "Copy file path" }) + +-- visual mode: copy the file path plus the selected line range +vim.keymap.set("v", "bc", function() + copy_ref({ visual = true }) +end, { desc = "Copy file path with line range" }) + +-- open the copy-ref scratch buffer +vim.keymap.set("n", "b.", function() + open_scratch() +end, { desc = "Open copy-ref scratch buffer" }) + +-- clear the copy-ref scratch buffer +vim.keymap.set("n", "bx", function() + local buf = get_or_create_scratch() + vim.api.nvim_buf_set_lines(buf, 0, -1, false, {}) + vim.notify("Cleared copy-refs buffer") +end, { desc = "Clear copy-ref scratch buffer" }) + +-- yank the entire copy-ref scratch buffer into the + register +vim.keymap.set("n", "by", function() + local buf = get_or_create_scratch() + local lines = vim.api.nvim_buf_get_lines(buf, 0, -1, false) + local content = table.concat(lines, "\n") + vim.fn.setreg("+", content) + vim.notify("Yanked copy-refs buffer (" .. #lines .. " lines)") +end, { desc = "Yank copy-ref scratch buffer" })