diff --git a/lua/core/autocmd.lua b/lua/core/autocmd.lua index b27869f3..691deb31 100644 --- a/lua/core/autocmd.lua +++ b/lua/core/autocmd.lua @@ -33,11 +33,19 @@ if option.highlight_yanked or option.copy_yanked_to_clipboard then end local copy = function(str) - pcall(vim.fn.setreg, "+", str) + local ok, error = pcall(vim.fn.setreg, "+", str) + if not ok then + vim.notify("fail to copy to clipboard: " .. error, vim.log.levels.ERROR) + return + end end local present, yank_data = pcall(vim.fn.getreg, "0") - if not present or #yank_data < 1 then + if not present then + vim.notify("fail to get content from reg 0: " .. yank_data, vim.log.levels.ERROR) + return + end + if #yank_data < 1 then return end diff --git a/lua/core/options.lua b/lua/core/options.lua index 2a087340..7c6e7d6d 100644 --- a/lua/core/options.lua +++ b/lua/core/options.lua @@ -138,4 +138,16 @@ if vim.g.neovide then vim.g.neovide_cursor_vfx_mode = vim.cfg.neovide.vfx_mode end +vim.g.clipboard = { + name = "OSC 52", + copy = { + ["+"] = require("libs.osc52").copy, + ["*"] = require("libs.osc52").copy, + }, + paste = { + ["+"] = require("libs.osc52").paste, + ["*"] = require("libs.osc52").paste, + }, +} + vim.loader.enable() diff --git a/lua/libs/osc52.lua b/lua/libs/osc52.lua new file mode 100644 index 00000000..ba555bda --- /dev/null +++ b/lua/libs/osc52.lua @@ -0,0 +1,73 @@ +local M = {} + +local lshift = require("bit").lshift +local rshift = require("bit").rshift +local band = require("bit").band +local bor = require("bit").bor + +-- stylua: ignore +local base64 = { + [0] = 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', +} +local mask = 0x3f -- 0b00111111 + +local function encode(s) + local len = string.len(s) + local output = {} + + for i = 1, len, 3 do + local byte1, byte2, byte3 = string.byte(s, i, i + 2) + local bits = bor(lshift(byte1, 16), lshift(byte2 or 0, 8), byte3 or 0) + table.insert(output, base64[rshift(bits, 18)]) + table.insert(output, base64[band(rshift(bits, 12), mask)]) + table.insert(output, base64[band(rshift(bits, 6), mask)]) + table.insert(output, base64[band(bits, mask)]) + end + + for i = 0, 1 - ((len - 1) % 3) do + output[#output - i] = "=" + end + + return table.concat(output) +end + +function M.copy(lines) + local s = table.concat(lines, "\n") + io.stdout:write(string.format("\x1b]52;;%s\x1b\\", encode(s))) +end + +function M.paste() + local contents = nil + local id = vim.api.nvim_create_autocmd("TermResponse", { + callback = function(args) + local resp = args.data ---@type string + local encoded = resp:match("\x1b%]52;%w?;([A-Za-z0-9+/=]*)") + if encoded then + contents = vim.base64.decode(encoded) + return true + end + end, + }) + + io.stdout:write("\x1b]52;;?\x1b\\") + + vim.wait(1000, function() + return contents ~= nil + end) + + -- Delete the autocommand if it didn't already delete itself + pcall(vim.api.nvim_del_autocmd, id) + + if contents then + return vim.split(contents, "\n") + end + + vim.notify("Timed out waiting for a clipboard response from the terminal", vim.log.levels.WARN) + return 0 +end + +return M