From b4e9935951c9913e37316175a0698685b8918b0e Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Wed, 15 Jul 2026 10:35:53 -0700 Subject: [PATCH] Simplify remote clipboard and share Neovim yanks over OSC 52 inside tmux Sessions inside tmux used to skip the clipboard provider entirely, so yanks only reached the machine's own Wayland clipboard and never traveled over OSC 52 to clients attached via SSH. Now every copy also emits OSC 52, which tmux turns into a buffer and rebroadcasts to all attached clients. Paste prefers wl-paste when a display is available. This also drops the private-file fallback clipboard and its regtype sidecar: Neovim's built-in clipboard layer already restores the register type when pasted content matches its cache, and OSC 52 queries answered by tmux cover the display-less paste case. Co-Authored-By: Claude Fable 5 --- pkgbuilds/omarchy-nvim/PKGBUILD | 2 +- .../lua/config/remote_clipboard.lua | 264 +++--------------- 2 files changed, 45 insertions(+), 221 deletions(-) diff --git a/pkgbuilds/omarchy-nvim/PKGBUILD b/pkgbuilds/omarchy-nvim/PKGBUILD index c05930d..d1d763e 100644 --- a/pkgbuilds/omarchy-nvim/PKGBUILD +++ b/pkgbuilds/omarchy-nvim/PKGBUILD @@ -1,6 +1,6 @@ # Maintainer: Ryan Hughes pkgname=omarchy-nvim -pkgver=2026.6.17 +pkgver=2026.7.15 pkgrel=1 pkgdesc="Pre-built LazyVim configuration with cached plugins" arch=('any') diff --git a/pkgbuilds/omarchy-nvim/lua/config/remote_clipboard.lua b/pkgbuilds/omarchy-nvim/lua/config/remote_clipboard.lua index c43aa23..b54c54e 100644 --- a/pkgbuilds/omarchy-nvim/lua/config/remote_clipboard.lua +++ b/pkgbuilds/omarchy-nvim/lua/config/remote_clipboard.lua @@ -1,69 +1,10 @@ +-- Clipboard for sessions whose yanks may need to reach another machine: +-- every copy is emitted as OSC 52 (inside tmux this becomes a tmux buffer, +-- rebroadcast to every attached client, local or SSH). Paste prefers the +-- local Wayland clipboard when one is available, so content copied in other +-- apps remains pasteable; without a display, paste is an OSC 52 query that +-- tmux (or the terminal) answers. local M = {} -local uv = vim.uv or vim.loop - -local function secure_runtime_dir() - local uid = uv.getuid() - local candidates = {} - - local function add(path) - if path and path ~= "" then - candidates[#candidates + 1] = path - end - end - - add(vim.env.XDG_RUNTIME_DIR) - if uid then - add("/run/user/" .. uid) - add("/dev/shm/nvim-remote-clipboard-" .. uid) - end - - for _, dir in ipairs(candidates) do - if dir:sub(1, 9) == "/dev/shm/" then - pcall(vim.fn.mkdir, dir, "p", 448) - pcall(vim.fn.setfperm, dir, "rwx------") - end - - local stat = uv.fs_stat(dir) - if stat and stat.type == "directory" and stat.uid == uid and stat.mode % 512 == 448 then - return dir - end - end -end - -local function wayland_connection(runtime_dir) - if vim.fn.executable("wl-copy") ~= 1 or vim.fn.executable("wl-paste") ~= 1 then - return nil - end - - local function valid_socket(path) - local stat = uv.fs_stat(path) - return stat and stat.type == "socket" - end - - if vim.env.WAYLAND_DISPLAY and vim.env.WAYLAND_DISPLAY ~= "" then - local display = vim.env.WAYLAND_DISPLAY - local socket_path = display:sub(1, 1) == "/" and display or (runtime_dir .. "/" .. display) - if valid_socket(socket_path) then - return display - end - end - - for _, path in ipairs(vim.fn.glob(runtime_dir .. "/wayland-*", false, true)) do - local display = vim.fn.fnamemodify(path, ":t") - if not display:match("%.lock$") and valid_socket(path) then - return display - end - end -end - -local function write_private_file(path, payload) - local tmp_path = path .. "." .. vim.fn.getpid() .. ".tmp" - - vim.fn.writefile({ payload }, tmp_path, "b") - vim.fn.setfperm(tmp_path, "rw-------") - vim.fn.rename(tmp_path, path) - vim.fn.setfperm(path, "rw-------") -end local function proc_lines(pid, file) local ok, lines = pcall(vim.fn.readfile, "/proc/" .. pid .. "/" .. file) @@ -99,177 +40,60 @@ local function ancestor_process_named(name) return false end -local function copy_to_client_clipboard(register, lines) - if vim.g.omarchy_remote_clipboard_osc52 == false then - return - end - - local ok, osc52 = pcall(require, "vim.ui.clipboard.osc52") - if ok then - pcall(osc52.copy(register), lines) - end -end - function M.setup() local in_tmux = vim.env.TMUX ~= nil - local has_display = vim.env.WAYLAND_DISPLAY or vim.env.DISPLAY - local in_remote_session = vim.env.SSH_TTY or vim.env.SSH_CONNECTION + local in_ssh = vim.env.SSH_TTY ~= nil or vim.env.SSH_CONNECTION ~= nil local in_herdr = vim.env.HERDR_PANE_ID ~= nil or ancestor_process_named("herdr") - local needs_remote_clipboard = not in_tmux and (in_herdr or (in_remote_session and not has_display)) - if not needs_remote_clipboard then + if not (in_tmux or in_ssh or in_herdr) then return end - local runtime_dir = secure_runtime_dir() - if not runtime_dir then - return - end + local osc52 = require("vim.ui.clipboard.osc52") + local has_wayland = vim.env.WAYLAND_DISPLAY ~= nil + and vim.fn.executable("wl-copy") == 1 + and vim.fn.executable("wl-paste") == 1 - local wayland_display = wayland_connection(runtime_dir) - local ttl_seconds = 10 * 60 + local function copy(register) + local emit = osc52.copy(register) - local function empty_clipboard() - return { {}, "v" } - end - - local function read_regtype(path) - local stat = uv.fs_stat(path) - if not stat or stat.uid ~= uv.getuid() or stat.mode % 512 ~= 384 then - return "v" - end - - local ok, file_lines = pcall(vim.fn.readfile, path) - if not ok or #file_lines == 0 then - return "v" - end - - local ok_decode, data = pcall(vim.fn.json_decode, table.concat(file_lines, "\n")) - if not ok_decode or type(data) ~= "table" then - return "v" - end - - if type(data.created_at) == "number" and os.time() - data.created_at > ttl_seconds then - vim.fn.delete(path) - return "v" - end - - return type(data.regtype) == "string" and data.regtype or "v" - end - - if wayland_display then - local regtype_path = runtime_dir .. "/nvim-remote-clipboard-regtype.json" - local env = { - "env", - "XDG_RUNTIME_DIR=" .. runtime_dir, - "WAYLAND_DISPLAY=" .. wayland_display, - } - - local function write_regtype(regtype) - write_private_file(regtype_path, vim.fn.json_encode({ - regtype = regtype, - created_at = os.time(), - })) - end - - local function copy(register) - return function(lines, regtype) - write_regtype(regtype) - - local cmd = vim.list_extend(vim.deepcopy(env), { - "wl-copy", - "--sensitive", - "--type", - "text/plain", - }) + return function(lines) + if has_wayland then + local cmd = { "wl-copy", "--sensitive", "--type", "text/plain" } if register == "*" then cmd[#cmd + 1] = "--primary" end - vim.fn.system(cmd, lines) - copy_to_client_clipboard(register, lines) + end + + if vim.g.omarchy_remote_clipboard_osc52 ~= false then + emit(lines) end end - - local function paste(register) - return function() - local cmd = vim.list_extend(vim.deepcopy(env), { "wl-paste", "--no-newline" }) - if register == "*" then - cmd[#cmd + 1] = "--primary" - end - - local lines = vim.fn.systemlist(cmd, "", 1) - if vim.v.shell_error ~= 0 then - return empty_clipboard() - end - - return { lines, read_regtype(regtype_path) } - end - end - - vim.g.clipboard = { - name = "OmarchyWaylandClipboard", - copy = { - ["+"] = copy("+"), - ["*"] = copy("*"), - }, - paste = { - ["+"] = paste("+"), - ["*"] = paste("*"), - }, - cache_enabled = 0, - } - else - local clipboard_path = runtime_dir .. "/nvim-remote-clipboard.json" - - local function copy(register) - return function(lines, regtype) - write_private_file(clipboard_path, vim.fn.json_encode({ - lines = lines, - regtype = regtype, - created_at = os.time(), - })) - copy_to_client_clipboard(register, lines) - end - end - - local function read_clipboard() - local stat = uv.fs_stat(clipboard_path) - if not stat or stat.uid ~= uv.getuid() or stat.mode % 512 ~= 384 then - return empty_clipboard() - end - - local ok, file_lines = pcall(vim.fn.readfile, clipboard_path) - if not ok or #file_lines == 0 then - return empty_clipboard() - end - - local ok_decode, data = pcall(vim.fn.json_decode, table.concat(file_lines, "\n")) - if not ok_decode or type(data) ~= "table" or type(data.lines) ~= "table" then - return empty_clipboard() - end - - if type(data.created_at) == "number" and os.time() - data.created_at > ttl_seconds then - vim.fn.delete(clipboard_path) - return empty_clipboard() - end - - return { data.lines, data.regtype or "v" } - end - - vim.g.clipboard = { - name = "RemoteRuntimeClipboard", - copy = { - ["+"] = copy("+"), - ["*"] = copy("*"), - }, - paste = { - ["+"] = read_clipboard, - ["*"] = read_clipboard, - }, - cache_enabled = 0, - } end + + local function paste(register) + if not has_wayland then + return osc52.paste(register) + end + + return function() + local cmd = { "wl-paste", "--no-newline" } + if register == "*" then + cmd[#cmd + 1] = "--primary" + end + + local lines = vim.fn.systemlist(cmd, "", 1) + return vim.v.shell_error == 0 and lines or {} + end + end + + vim.g.clipboard = { + name = "OmarchyRemoteClipboard", + copy = { ["+"] = copy("+"), ["*"] = copy("*") }, + paste = { ["+"] = paste("+"), ["*"] = paste("*") }, + cache_enabled = 0, + } end return M