From 009b3aa8c092e201243e73e48dc5a5c3a94902de Mon Sep 17 00:00:00 2001 From: psychhim Date: Thu, 9 Oct 2025 03:26:44 +0530 Subject: [PATCH] yank_notification: show line numbers. Clipboard copy notification to have line counts. Fix: Paste clipboard over selection --- init.lua | 3 ++ lua/copy_to_clipboard_fix.lua | 9 +++++- lua/keymaps.lua | 52 ++++++++++++++++++++++++----------- lua/yank_notification.lua | 23 ++++++++++++++++ 4 files changed, 70 insertions(+), 17 deletions(-) create mode 100644 lua/yank_notification.lua diff --git a/init.lua b/init.lua index 6cbcd0c..afc1f4a 100644 --- a/init.lua +++ b/init.lua @@ -1,6 +1,9 @@ vim.g.mapleader = ' ' vim.g.maplocalleader = ' ' +-- Load yank notification with line counts +require 'yank_notification' + -- [[ Install `lazy.nvim` plugin manager ]] -- https://github.com/folke/lazy.nvim -- `:help lazy.nvim.txt` for more info diff --git a/lua/copy_to_clipboard_fix.lua b/lua/copy_to_clipboard_fix.lua index 4bcfa87..92d3668 100644 --- a/lua/copy_to_clipboard_fix.lua +++ b/lua/copy_to_clipboard_fix.lua @@ -1,10 +1,17 @@ local M = {} function M.trim_clipboard() + -- Get the content of the '+' register local content = vim.fn.getreg '+' -- Remove single trailing newline if present content = content:gsub('\n$', '') + -- Update '+' and '*' registers vim.fn.setreg('+', content) vim.fn.setreg('*', content) - vim.notify('Copied selection to clipboard', vim.log.levels.INFO) + -- Count the number of lines copied + local line_count = select(2, content:gsub('\n', '')) + 1 -- Number of newlines + 1 + -- Notification with line count + local plural = line_count == 1 and '' or 's' + vim.notify(string.format('Copied %d line%s to clipboard', line_count, plural), vim.log.levels.INFO) end + return M diff --git a/lua/keymaps.lua b/lua/keymaps.lua index 3da563a..92ee51d 100644 --- a/lua/keymaps.lua +++ b/lua/keymaps.lua @@ -165,39 +165,59 @@ vim.keymap.set('x', 'Y', [["+y:lua require("copy_to_clipboard_fix").trim_cl -- [[ Paste from clipboard with line count ]] local function paste_from_clipboard() + -- Get clipboard lines local lines = vim.fn.getreg('+', 1, true) - local line_count = #lines if vim.tbl_isempty(lines) then return end + local line_count = #lines local mode = vim.fn.mode() if mode == 'v' or mode == 'V' or mode == '\22' then - local selection_linewise = (mode == 'V') - -- Force register type - if selection_linewise and #lines == 1 then - -- Make clipboard linewise to force newline after paste - vim.fn.setreg('+', table.concat(lines, '\n'), 'l') - else - -- Otherwise characterwise is fine - vim.fn.setreg('+', table.concat(lines, '\n'), 'c') + -- Visual mode: replace selection with clipboard + local start_line = vim.fn.line 'v' + local start_col = vim.fn.col 'v' + local end_line = vim.fn.line '.' + local end_col = vim.fn.col '.' + if start_line > end_line or (start_line == end_line and start_col > end_col) then + start_line, end_line = end_line, start_line + start_col, end_col = end_col, start_col end - -- Delete selection without affecting clipboard + -- Determine register type + local reg_type + if #lines > 1 or (end_line - start_line + 1) > 1 then + reg_type = 'l' -- linewise + else + reg_type = 'c' -- characterwise + end + vim.fn.setreg('"', vim.fn.getreg '+', reg_type) + -- Delete selection into blackhole and paste from unnamed (clipboard) vim.cmd 'normal! "_dP' -- Move cursor to end of pasted text - vim.cmd 'normal! `]' + local buf = vim.api.nvim_get_current_buf() + local last_line = start_line + #lines - 1 + local last_col + if reg_type == 'l' then + local line_content = vim.api.nvim_buf_get_lines(buf, last_line - 1, last_line, false)[1] + last_col = #line_content + else + local first_line = vim.api.nvim_buf_get_lines(buf, start_line - 1, start_line, false)[1] + last_col = start_col - 1 + #lines[1] + end + vim.api.nvim_win_set_cursor(0, { last_line, last_col }) -- Exit visual mode vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes('', true, false, true), 'n', true) else -- Normal mode: paste before cursor vim.api.nvim_put(lines, 'c', false, true) end - -- Notify with line count - vim.schedule(function() - vim.notify(line_count .. ' line' .. (line_count > 1 and 's' or '') .. ' pasted from clipboard', vim.log.levels.INFO) - end) + -- Notify line count + vim.defer_fn(function() + local plural = line_count > 1 and 's' or '' + vim.notify(line_count .. ' line' .. plural .. ' pasted from clipboard', vim.log.levels.INFO) + end, 0) end vim.keymap.set('n', 'P', paste_from_clipboard, { desc = 'Paste from clipboard before cursor' }) -vim.keymap.set('x', 'P', paste_from_clipboard, { desc = 'Paste from clipboard over selection' }) +vim.keymap.set('x', 'P', paste_from_clipboard, { desc = 'Paste clipboard over selection' }) -- [[ Paste Neovim yanks ]] -- In Normal Mode before cursor inline diff --git a/lua/yank_notification.lua b/lua/yank_notification.lua new file mode 100644 index 0000000..08ce1ee --- /dev/null +++ b/lua/yank_notification.lua @@ -0,0 +1,23 @@ +local M = {} +vim.api.nvim_create_autocmd('TextYankPost', { + pattern = '*', + callback = function() + local content = vim.fn.getreg '"' + if not content or content == '' then + return + end + -- Split by newline + local lines = vim.split(content, '\n', true) + -- Remove trailing empty line if present + if lines[#lines] == '' then + table.remove(lines, #lines) + end + local line_count = #lines + vim.schedule(function() + local plural = line_count == 1 and '' or 's' + vim.notify(string.format('Yanked %d line%s', line_count, plural), vim.log.levels.INFO) + end) + end, +}) + +return M