Diffview auto-refresh, yank file path key bindings
This commit is contained in:
@@ -20,3 +20,19 @@ vim.opt.titlestring = "%{substitute(getcwd(),$HOME,'~','')} - Neovim"
|
||||
vim.opt.fillchars:append({ diff = "╱" })
|
||||
|
||||
vim.opt.mousescroll = "ver:6,hor:6" -- better mouse scroll in hidpi mode
|
||||
|
||||
-- WSL2 clipboard support via win32yank
|
||||
if vim.fn.has("wsl") == 1 then
|
||||
vim.g.clipboard = {
|
||||
name = "win32yank-wsl",
|
||||
copy = {
|
||||
["+"] = "win32yank.exe -i --crlf",
|
||||
["*"] = "win32yank.exe -i --crlf",
|
||||
},
|
||||
paste = {
|
||||
["+"] = "win32yank.exe -o --lf",
|
||||
["*"] = "win32yank.exe -o --lf",
|
||||
},
|
||||
cache_enabled = true,
|
||||
}
|
||||
end
|
||||
|
||||
96
lua/custom/directory-watcher.lua
Normal file
96
lua/custom/directory-watcher.lua
Normal file
@@ -0,0 +1,96 @@
|
||||
-- Custom directory watcher using Neovim's built-in libuv bindings
|
||||
-- Source: https://github.com/richardgill/nix/blob/ebdd8260f770d242db7b6163158cfe9ad9784c41/modules/home-manager/dot-files/nvim/lua/custom/directory-watcher.lua
|
||||
|
||||
local M = {}
|
||||
|
||||
local uv = vim.uv
|
||||
local watcher = nil
|
||||
local debounce_timer = nil
|
||||
local on_change_handlers = {}
|
||||
|
||||
-- Debounce helper to prevent callback storms
|
||||
local debounce = function(fn, delay)
|
||||
return function(...)
|
||||
local args = { ... }
|
||||
if debounce_timer then
|
||||
debounce_timer:close()
|
||||
end
|
||||
debounce_timer = vim.defer_fn(function()
|
||||
debounce_timer = nil
|
||||
fn(unpack(args))
|
||||
end, delay)
|
||||
end
|
||||
end
|
||||
|
||||
-- Register a named handler to be called when files change
|
||||
-- If a handler with the same name exists, it will be replaced
|
||||
-- Note: Named handlers are required to support Lua hotreload - when a file is reloaded,
|
||||
-- it re-registers its handler with the same name, replacing the old one instead of
|
||||
-- creating duplicates
|
||||
M.registerOnChangeHandler = function(name, handler)
|
||||
on_change_handlers[name] = handler
|
||||
end
|
||||
|
||||
-- Start watching a directory for file changes
|
||||
M.setup = function(opts)
|
||||
opts = opts or {}
|
||||
local path = opts.path
|
||||
local debounce_delay = opts.debounce or 100 -- ms
|
||||
|
||||
if not path then
|
||||
return false
|
||||
end
|
||||
|
||||
-- Stop existing watcher if any
|
||||
if watcher then
|
||||
M.stop()
|
||||
end
|
||||
|
||||
-- Create fs_event handle
|
||||
local fs_event = uv.new_fs_event()
|
||||
if not fs_event then
|
||||
return false
|
||||
end
|
||||
|
||||
-- Debounced callback for file changes
|
||||
local on_change = debounce(function(err, filename, events)
|
||||
if err then
|
||||
M.stop()
|
||||
return
|
||||
end
|
||||
|
||||
if filename then
|
||||
local full_path = path .. "/" .. filename
|
||||
|
||||
-- Call all registered handlers
|
||||
for _, handler in pairs(on_change_handlers) do
|
||||
pcall(handler, full_path, events)
|
||||
end
|
||||
end
|
||||
end, debounce_delay)
|
||||
|
||||
-- Start watching (wrapped for thread safety)
|
||||
local ok, err = fs_event:start(path, { recursive = false }, vim.schedule_wrap(on_change))
|
||||
|
||||
if ok ~= 0 then
|
||||
return false
|
||||
end
|
||||
|
||||
watcher = fs_event
|
||||
return true
|
||||
end
|
||||
|
||||
-- Stop the watcher and clean up resources
|
||||
M.stop = function()
|
||||
if watcher then
|
||||
watcher:stop()
|
||||
watcher = nil
|
||||
end
|
||||
|
||||
if debounce_timer then
|
||||
debounce_timer:close()
|
||||
debounce_timer = nil
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
92
lua/custom/yank.lua
Normal file
92
lua/custom/yank.lua
Normal file
@@ -0,0 +1,92 @@
|
||||
-- Custom yank functions for copying file paths and visual selections with file paths.
|
||||
-- From: https://richardgill.org/blog/configuring-neovim-coding-agents
|
||||
-- Source: https://github.com/richardgill/nix/blob/ebdd8260f770d242db7b6163158cfe9ad9784c41/modules/home-manager/dot-files/nvim/lua/custom/yank.lua
|
||||
|
||||
local M = {}
|
||||
|
||||
M.get_buffer_absolute = function()
|
||||
return vim.fn.expand("%:p")
|
||||
end
|
||||
|
||||
M.get_buffer_cwd_relative = function()
|
||||
return vim.fn.expand("%:.")
|
||||
end
|
||||
|
||||
M.get_visual_bounds = function()
|
||||
local mode = vim.fn.mode()
|
||||
if mode ~= "v" and mode ~= "V" then
|
||||
error("get_visual_bounds must be called in visual or visual-line mode (current mode: " .. vim.inspect(mode) .. ")")
|
||||
end
|
||||
local is_visual_line_mode = mode == "V"
|
||||
local start_pos = vim.fn.getpos("v")
|
||||
local end_pos = vim.fn.getpos(".")
|
||||
|
||||
return {
|
||||
start_line = math.min(start_pos[2], end_pos[2]),
|
||||
end_line = math.max(start_pos[2], end_pos[2]),
|
||||
start_col = is_visual_line_mode and 0 or math.min(start_pos[3], end_pos[3]) - 1,
|
||||
end_col = is_visual_line_mode and -1 or math.max(start_pos[3], end_pos[3]),
|
||||
mode = mode,
|
||||
start_pos = start_pos,
|
||||
end_pos = end_pos,
|
||||
}
|
||||
end
|
||||
|
||||
M.format_line_range = function(start_line, end_line)
|
||||
return start_line == end_line and tostring(start_line) or start_line .. "-" .. end_line
|
||||
end
|
||||
|
||||
M.simulate_yank_highlight = function()
|
||||
local bounds = M.get_visual_bounds()
|
||||
|
||||
local ns = vim.api.nvim_create_namespace("simulate_yank_highlight")
|
||||
vim.highlight.range(
|
||||
0,
|
||||
ns,
|
||||
"IncSearch",
|
||||
{ bounds.start_line - 1, bounds.start_col },
|
||||
{ bounds.end_line - 1, bounds.end_col },
|
||||
{ priority = 200 }
|
||||
)
|
||||
vim.defer_fn(function()
|
||||
vim.api.nvim_buf_clear_namespace(0, ns, 0, -1)
|
||||
end, 150)
|
||||
end
|
||||
|
||||
M.exit_visual_mode = function()
|
||||
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes("<Esc>", true, false, true), "n", false)
|
||||
end
|
||||
|
||||
M.yank_path = function(path, label)
|
||||
vim.fn.setreg("+", path)
|
||||
print("Yanked " .. label .. " path: " .. path)
|
||||
end
|
||||
|
||||
M.yank_visual_with_path = function(path, label)
|
||||
local bounds = M.get_visual_bounds()
|
||||
|
||||
local selected_lines = vim.fn.getregion(bounds.start_pos, bounds.end_pos, { type = bounds.mode })
|
||||
local selected_text = table.concat(selected_lines, "\n")
|
||||
|
||||
local line_range = M.format_line_range(bounds.start_line, bounds.end_line)
|
||||
local path_with_lines = path .. ":" .. line_range
|
||||
|
||||
local result = path_with_lines .. "\n\n" .. selected_text
|
||||
vim.fn.setreg("+", result)
|
||||
|
||||
M.simulate_yank_highlight()
|
||||
|
||||
M.exit_visual_mode()
|
||||
|
||||
print("Yanked " .. label .. " with lines " .. line_range)
|
||||
end
|
||||
|
||||
vim.keymap.set("v", "<leader>ya", function()
|
||||
M.yank_visual_with_path(M.get_buffer_absolute(), "absolute")
|
||||
end, { desc = "[Y]ank selection with [A]bsolute path" })
|
||||
|
||||
vim.keymap.set("v", "<leader>yr", function()
|
||||
M.yank_visual_with_path(M.get_buffer_cwd_relative(), "relative")
|
||||
end, { desc = "[Y]ank selection with [R]elative path" })
|
||||
|
||||
return M
|
||||
@@ -1,3 +1,6 @@
|
||||
-- Custom Diffview auto-refresh config from https://richardgill.org/blog/configuring-neovim-coding-agents
|
||||
-- Source: https://github.com/richardgill/nix/blob/ebdd8260f770d242db7b6163158cfe9ad9784c41/modules/home-manager/dot-files/nvim/lua/plugins/git-diff_diffview.lua
|
||||
|
||||
return {
|
||||
"sindrets/diffview.nvim",
|
||||
cmd = { "DiffviewOpen", "DiffviewClose", "DiffviewToggleFiles", "DiffviewFocusFiles" },
|
||||
@@ -5,13 +8,13 @@ return {
|
||||
enhanced_diff_hl = true,
|
||||
keymaps = {
|
||||
view = {
|
||||
["gq"] = "<Cmd>DiffviewClose<CR>",
|
||||
{ "n", "gq", "<cmd>DiffviewClose<cr>", { desc = "Close diffview" } },
|
||||
},
|
||||
file_panel = {
|
||||
["gq"] = "<Cmd>DiffviewClose<CR>",
|
||||
{ "n", "gq", "<cmd>DiffviewClose<cr>", { desc = "Close diffview" } },
|
||||
},
|
||||
file_history_panel = {
|
||||
["gq"] = "<Cmd>DiffviewClose<CR>",
|
||||
{ "n", "gq", "<cmd>DiffviewClose<cr>", { desc = "Close diffview" } },
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -19,6 +22,51 @@ return {
|
||||
{ "<leader>gl", ":'<,'>DiffviewFileHistory<cr>", mode = "v", desc = "Toggle git log of selected lines" },
|
||||
{ "<leader>gL", "<cmd>DiffviewFileHistory<cr>", desc = "Toggle git log of current branch" },
|
||||
{ "<leader>gl", "<cmd>DiffviewFileHistory %<cr>", desc = "Toggle git log of current file history" },
|
||||
{ "<leader>gd", "<cmd>DiffviewOpen<cr>", desc = "Git diff current file against the index" },
|
||||
{ "<leader>gv", "<cmd>DiffviewOpen<cr>", desc = "Open [g]it Diff[v]iew tab" },
|
||||
},
|
||||
config = function(_, opts)
|
||||
require("diffview").setup(opts)
|
||||
|
||||
local watcher = require("custom.directory-watcher")
|
||||
|
||||
local is_git_ignored = function(filepath)
|
||||
vim.fn.system("git check-ignore -q " .. vim.fn.shellescape(filepath))
|
||||
return vim.v.shell_error == 0
|
||||
end
|
||||
|
||||
local update_left_pane = function()
|
||||
pcall(function()
|
||||
local lib = require("diffview.lib")
|
||||
local view = lib.get_current_view()
|
||||
if view then
|
||||
view:update_files()
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- Register handler for file changes in watched directory
|
||||
watcher.registerOnChangeHandler("diffview", function(filepath, events)
|
||||
local is_in_dot_git_dir = filepath:match("/%.git/") or filepath:match("^%.git/")
|
||||
|
||||
if is_in_dot_git_dir or not is_git_ignored(filepath) then
|
||||
vim.notify("[diffview] File changed: " .. vim.fn.fnamemodify(filepath, ":t"), vim.log.levels.INFO)
|
||||
update_left_pane()
|
||||
end
|
||||
end)
|
||||
|
||||
-- Start watching the current working directory
|
||||
watcher.setup({ path = vim.fn.getcwd() })
|
||||
|
||||
vim.api.nvim_create_autocmd("FocusGained", {
|
||||
callback = update_left_pane,
|
||||
})
|
||||
|
||||
vim.api.nvim_create_autocmd("User", {
|
||||
pattern = "DiffviewViewLeave",
|
||||
callback = function()
|
||||
watcher.stop()
|
||||
vim.cmd(":DiffviewClose")
|
||||
end,
|
||||
})
|
||||
end,
|
||||
}
|
||||
|
||||
6
lua/plugins/yank-path.lua
Normal file
6
lua/plugins/yank-path.lua
Normal file
@@ -0,0 +1,6 @@
|
||||
return {
|
||||
"ywpkwon/yank-path.nvim",
|
||||
config = function()
|
||||
require("yank-path").setup()
|
||||
end,
|
||||
}
|
||||
9
lua/plugins/yank.lua
Normal file
9
lua/plugins/yank.lua
Normal file
@@ -0,0 +1,9 @@
|
||||
-- Load custom yank keymaps for copying file paths + visual selections
|
||||
-- See lua/custom/yank.lua for implementation
|
||||
return {
|
||||
dir = vim.fn.stdpath("config"),
|
||||
name = "custom-yank",
|
||||
config = function()
|
||||
require("custom.yank")
|
||||
end,
|
||||
}
|
||||
Reference in New Issue
Block a user