405 lines
12 KiB
Lua
405 lines
12 KiB
Lua
local utils = require("yanky.utils")
|
|
local highlight = require("yanky.highlight")
|
|
local system_clipboard = require("yanky.system_clipboard")
|
|
local preserve_cursor = require("yanky.preserve_cursor")
|
|
local picker = require("yanky.picker")
|
|
local textobj = require("yanky.textobj")
|
|
|
|
local yanky = {}
|
|
|
|
yanky.ring = {
|
|
state = nil,
|
|
is_cycling = false,
|
|
callback = nil,
|
|
}
|
|
|
|
yanky.direction = {
|
|
FORWARD = 1,
|
|
BACKWARD = -1,
|
|
}
|
|
|
|
yanky.type = {
|
|
PUT_BEFORE = "P",
|
|
PUT_AFTER = "p",
|
|
GPUT_BEFORE = "gP",
|
|
GPUT_AFTER = "gp",
|
|
PUT_INDENT_AFTER = "]p",
|
|
PUT_INDENT_BEFORE = "[p",
|
|
}
|
|
|
|
function yanky.setup(options)
|
|
yanky.config = require("yanky.config")
|
|
yanky.config.setup(options)
|
|
|
|
yanky.history = require("yanky.history")
|
|
yanky.history.setup()
|
|
|
|
system_clipboard.setup()
|
|
highlight.setup()
|
|
preserve_cursor.setup()
|
|
picker.setup()
|
|
|
|
local yanky_augroup = vim.api.nvim_create_augroup("Yanky", { clear = true })
|
|
vim.api.nvim_create_autocmd("TextYankPost", {
|
|
group = yanky_augroup,
|
|
pattern = "*",
|
|
callback = function(_)
|
|
yanky.on_yank()
|
|
end,
|
|
})
|
|
if vim.v.vim_did_enter == 1 then
|
|
yanky.init_history()
|
|
else
|
|
vim.api.nvim_create_autocmd("VimEnter", {
|
|
group = yanky_augroup,
|
|
pattern = "*",
|
|
callback = yanky.init_history,
|
|
})
|
|
end
|
|
|
|
vim.api.nvim_create_user_command("YankyClearHistory", yanky.clear_history, {})
|
|
end
|
|
|
|
function yanky.init_history()
|
|
yanky.history.push(utils.get_register_info(utils.get_default_register()))
|
|
yanky.history.sync_with_numbered_registers()
|
|
end
|
|
|
|
local function do_put(state, _)
|
|
if state.is_visual then
|
|
vim.cmd([[execute "normal! \<esc>"]])
|
|
end
|
|
|
|
local ok, val = pcall(
|
|
vim.cmd,
|
|
string.format(
|
|
'silent normal! %s"%s%s%s',
|
|
state.is_visual and "gv" or "",
|
|
state.register ~= "=" and state.register or "=" .. vim.api.nvim_replace_termcodes("<CR>", true, false, true),
|
|
state.count,
|
|
state.type
|
|
)
|
|
)
|
|
if not ok then
|
|
vim.notify(val, vim.log.levels.WARN)
|
|
return
|
|
end
|
|
|
|
highlight.highlight_put(state)
|
|
end
|
|
|
|
function yanky.put(type, is_visual, callback)
|
|
if not vim.tbl_contains(vim.tbl_values(yanky.type), type) then
|
|
vim.notify("Invalid type " .. type, vim.log.levels.ERROR)
|
|
return
|
|
end
|
|
|
|
yanky.ring.state = nil
|
|
yanky.ring.is_cycling = false
|
|
yanky.ring.callback = callback or do_put
|
|
|
|
-- On Yank event is not triggered when put from expression register,
|
|
-- To allows cycling, we must store value here
|
|
if vim.v.register == "=" then
|
|
local entry = utils.get_register_info("=")
|
|
entry.filetype = vim.bo.filetype
|
|
|
|
yanky.history.push(entry)
|
|
end
|
|
|
|
yanky.init_ring(type, vim.v.register, vim.v.count, is_visual, yanky.ring.callback)
|
|
end
|
|
|
|
function yanky.clear_ring()
|
|
if yanky.can_cycle() and nil ~= yanky.ring.state.augroup then
|
|
vim.api.nvim_clear_autocmds({ group = yanky.ring.state.augroup })
|
|
end
|
|
|
|
yanky.ring.state = nil
|
|
yanky.ring.is_cycling = false
|
|
end
|
|
|
|
function yanky.attach_cancel()
|
|
if yanky.config.options.ring.cancel_event == "move" then
|
|
yanky.ring.state.augroup = vim.api.nvim_create_augroup("YankyRingClear", { clear = true })
|
|
vim.schedule(function()
|
|
vim.api.nvim_create_autocmd("CursorMoved", {
|
|
group = yanky.ring.state.augroup,
|
|
buffer = 0,
|
|
callback = yanky.clear_ring,
|
|
})
|
|
end)
|
|
else
|
|
vim.api.nvim_buf_attach(0, false, {
|
|
on_lines = function(_)
|
|
yanky.clear_ring()
|
|
|
|
return true
|
|
end,
|
|
})
|
|
end
|
|
end
|
|
|
|
function yanky.init_ring(type, register, count, is_visual, callback)
|
|
register = (register ~= '"' and register ~= "_") and register or utils.get_default_register()
|
|
|
|
local reg_content = vim.fn.getreg(register)
|
|
if nil == reg_content or "" == reg_content then
|
|
vim.notify(string.format('Register "%s" is empty', register), vim.log.levels.WARN)
|
|
return
|
|
end
|
|
|
|
local new_state = {
|
|
type = type,
|
|
register = register,
|
|
count = count > 0 and count or 1,
|
|
is_visual = is_visual,
|
|
use_repeat = callback == nil,
|
|
}
|
|
|
|
if nil ~= callback then
|
|
callback(new_state, do_put)
|
|
end
|
|
|
|
yanky.ring.state = new_state
|
|
yanky.ring.is_cycling = false
|
|
|
|
yanky.attach_cancel()
|
|
if yanky.config.options.textobj.enabled then
|
|
textobj.save_put()
|
|
end
|
|
end
|
|
|
|
function yanky.can_cycle()
|
|
return nil ~= yanky.ring.state
|
|
end
|
|
|
|
function yanky.cycle(direction)
|
|
if not yanky.can_cycle() then
|
|
vim.notify("Your last action was not put, ignoring cycle", vim.log.levels.INFO)
|
|
return
|
|
end
|
|
|
|
direction = direction or yanky.direction.FORWARD
|
|
if not vim.tbl_contains(vim.tbl_values(yanky.direction), direction) then
|
|
vim.notify("Invalid direction for cycle", vim.log.levels.ERROR)
|
|
return
|
|
end
|
|
|
|
if nil ~= yanky.ring.state.augroup then
|
|
vim.api.nvim_clear_autocmds({ group = yanky.ring.state.augroup })
|
|
end
|
|
|
|
if not yanky.ring.is_cycling then
|
|
yanky.history.reset()
|
|
|
|
local reg = utils.get_register_info(yanky.ring.state.register)
|
|
local first = yanky.history.first()
|
|
if nil ~= first and reg.regcontents == first.regcontents and reg.regtype == first.regtype then
|
|
yanky.history.skip()
|
|
end
|
|
end
|
|
|
|
local new_state = yanky.ring.state
|
|
|
|
local next_content
|
|
if direction == yanky.direction.FORWARD then
|
|
next_content = yanky.history.next()
|
|
if nil == next_content then
|
|
vim.notify("Reached oldest item", vim.log.levels.INFO)
|
|
yanky.attach_cancel()
|
|
return
|
|
end
|
|
else
|
|
next_content = yanky.history.previous()
|
|
if nil == next_content then
|
|
vim.notify("Reached first item", vim.log.levels.INFO)
|
|
yanky.attach_cancel()
|
|
return
|
|
end
|
|
end
|
|
|
|
yanky.ring.state.register = yanky.ring.state.register ~= "=" and yanky.ring.state.register
|
|
or utils.get_default_register()
|
|
|
|
utils.use_temporary_register(yanky.ring.state.register, next_content, function()
|
|
if new_state.use_repeat then
|
|
local ok, val = pcall(vim.cmd, "silent normal! u.")
|
|
if not ok then
|
|
vim.notify(val, vim.log.levels.WARN)
|
|
yanky.attach_cancel()
|
|
return
|
|
end
|
|
highlight.highlight_put(new_state)
|
|
else
|
|
local ok, val = pcall(vim.cmd, "silent normal! u")
|
|
if not ok then
|
|
vim.notify(val, vim.log.levels.WARN)
|
|
yanky.attach_cancel()
|
|
return
|
|
end
|
|
yanky.ring.callback(new_state, do_put)
|
|
end
|
|
end)
|
|
|
|
if yanky.config.options.ring.update_register_on_cycle then
|
|
vim.fn.setreg(new_state.register, next_content.regcontents, next_content.regtype)
|
|
end
|
|
|
|
yanky.ring.is_cycling = true
|
|
yanky.ring.state = new_state
|
|
|
|
yanky.attach_cancel()
|
|
end
|
|
|
|
function yanky.on_yank()
|
|
if vim.tbl_contains(yanky.config.options.ring.ignore_registers, vim.v.register) then
|
|
return
|
|
end
|
|
|
|
-- Only historize first delete in visual mode
|
|
if vim.v.event.visual and vim.v.event.operator == "d" and yanky.ring.is_cycling then
|
|
return
|
|
end
|
|
local entry = utils.get_register_info(vim.v.event.regname)
|
|
entry.filetype = vim.bo.filetype
|
|
|
|
yanky.history.push(entry)
|
|
|
|
preserve_cursor.on_yank()
|
|
end
|
|
|
|
function yanky.yank(options)
|
|
options = options or {}
|
|
preserve_cursor.yank()
|
|
|
|
return string.format("%sy", options.register and '"' .. options.register or "")
|
|
end
|
|
|
|
function yanky.clear_history()
|
|
yanky.history.clear()
|
|
end
|
|
|
|
function yanky.register_plugs()
|
|
local yanky_wrappers = require("yanky.wrappers")
|
|
|
|
vim.keymap.set("n", "<Plug>(YankyCycleForward)", function()
|
|
yanky.cycle(1)
|
|
end, { silent = true })
|
|
|
|
vim.keymap.set("n", "<Plug>(YankyCycleBackward)", function()
|
|
yanky.cycle(-1)
|
|
end, { silent = true })
|
|
|
|
vim.keymap.set("n", "<Plug>(YankyPreviousEntry)", function()
|
|
yanky.cycle(1)
|
|
end, { silent = true })
|
|
|
|
vim.keymap.set("n", "<Plug>(YankyNextEntry)", function()
|
|
yanky.cycle(-1)
|
|
end, { silent = true })
|
|
|
|
vim.keymap.set({ "n", "x" }, "<Plug>(YankyYank)", yanky.yank, { silent = true, expr = true })
|
|
|
|
for type, type_text in pairs({
|
|
p = "PutAfter",
|
|
P = "PutBefore",
|
|
gp = "GPutAfter",
|
|
gP = "GPutBefore",
|
|
["]p"] = "PutIndentAfter",
|
|
["[p"] = "PutIndentBefore",
|
|
}) do
|
|
vim.keymap.set("n", string.format("<Plug>(Yanky%s)", type_text), function()
|
|
yanky.put(type, false)
|
|
end, { silent = true })
|
|
|
|
vim.keymap.set("x", string.format("<Plug>(Yanky%s)", type_text), function()
|
|
yanky.put(type, true)
|
|
end, { silent = true })
|
|
|
|
vim.keymap.set("n", string.format("<Plug>(Yanky%sJoined)", type_text), function()
|
|
yanky.put(type, false, yanky_wrappers.trim_and_join_lines())
|
|
end, { silent = true })
|
|
|
|
vim.keymap.set("x", string.format("<Plug>(Yanky%sJoined)", type_text), function()
|
|
yanky.put(type, true, yanky_wrappers.trim_and_join_lines())
|
|
end, { silent = true })
|
|
|
|
vim.keymap.set("n", string.format("<Plug>(Yanky%sLinewise)", type_text), function()
|
|
yanky.put(type, false, yanky_wrappers.linewise())
|
|
end, { silent = true })
|
|
|
|
vim.keymap.set("x", string.format("<Plug>(Yanky%sLinewise)", type_text), function()
|
|
yanky.put(type, true, yanky_wrappers.linewise())
|
|
end, { silent = true })
|
|
|
|
vim.keymap.set("n", string.format("<Plug>(Yanky%sLinewiseJoined)", type_text), function()
|
|
yanky.put(type, false, yanky_wrappers.linewise(yanky_wrappers.trim_and_join_lines()))
|
|
end, { silent = true })
|
|
|
|
vim.keymap.set("x", string.format("<Plug>(Yanky%sLinewiseJoined)", type_text), function()
|
|
yanky.put(type, true, yanky_wrappers.linewise(yanky_wrappers.trim_and_join_lines()))
|
|
end, { silent = true })
|
|
|
|
vim.keymap.set("n", string.format("<Plug>(Yanky%sCharwise)", type_text), function()
|
|
yanky.put(type, false, yanky_wrappers.charwise())
|
|
end, { silent = true })
|
|
|
|
vim.keymap.set("x", string.format("<Plug>(Yanky%sCharwise)", type_text), function()
|
|
yanky.put(type, true, yanky_wrappers.charwise())
|
|
end, { silent = true })
|
|
|
|
vim.keymap.set("n", string.format("<Plug>(Yanky%sCharwiseJoined)", type_text), function()
|
|
yanky.put(type, false, yanky_wrappers.charwise(yanky_wrappers.trim_and_join_lines()))
|
|
end, { silent = true })
|
|
|
|
vim.keymap.set("x", string.format("<Plug>(Yanky%sCharwiseJoined)", type_text), function()
|
|
yanky.put(type, true, yanky_wrappers.charwise(yanky_wrappers.trim_and_join_lines()))
|
|
end, { silent = true })
|
|
|
|
vim.keymap.set("n", string.format("<Plug>(Yanky%sBlockwise)", type_text), function()
|
|
yanky.put(type, false, yanky_wrappers.blockwise())
|
|
end, { silent = true })
|
|
|
|
vim.keymap.set("x", string.format("<Plug>(Yanky%sBlockwise)", type_text), function()
|
|
yanky.put(type, true, yanky_wrappers.blockwise())
|
|
end, { silent = true })
|
|
|
|
vim.keymap.set("n", string.format("<Plug>(Yanky%sBlockwiseJoined)", type_text), function()
|
|
yanky.put(type, false, yanky_wrappers.blockwise(yanky_wrappers.trim_and_join_lines()))
|
|
end, { silent = true })
|
|
|
|
vim.keymap.set("x", string.format("<Plug>(Yanky%sBlockwiseJoined)", type_text), function()
|
|
yanky.put(type, true, yanky_wrappers.blockwise(yanky_wrappers.trim_and_join_lines()))
|
|
end, { silent = true })
|
|
|
|
for change, change_text in pairs({ [">>"] = "ShiftRight", ["<<"] = "ShiftLeft", ["=="] = "Filter" }) do
|
|
vim.keymap.set("n", string.format("<Plug>(Yanky%s%s)", type_text, change_text), function()
|
|
yanky.put(type, false, yanky_wrappers.linewise(yanky_wrappers.change(change)))
|
|
end, { silent = true })
|
|
|
|
vim.keymap.set("x", string.format("<Plug>(Yanky%s%s)", type_text, change_text), function()
|
|
yanky.put(type, true, yanky_wrappers.linewise(yanky_wrappers.change(change)))
|
|
end, { silent = true })
|
|
|
|
vim.keymap.set("n", string.format("<Plug>(Yanky%s%sJoined)", type_text, change_text), function()
|
|
yanky.put(
|
|
type,
|
|
false,
|
|
yanky_wrappers.linewise(yanky_wrappers.trim_and_join_lines(yanky_wrappers.change(change)))
|
|
)
|
|
end, { silent = true })
|
|
|
|
vim.keymap.set("x", string.format("<Plug>(Yanky%s%sJoined)", type_text, change_text), function()
|
|
yanky.put(
|
|
type,
|
|
true,
|
|
yanky_wrappers.linewise(yanky_wrappers.trim_and_join_lines(yanky_wrappers.change(change)))
|
|
)
|
|
end, { silent = true })
|
|
end
|
|
end
|
|
end
|
|
|
|
return yanky
|