Skip to content

Latest commit

 

History

19 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Configs

Configs for my neovim and alactritty setup

Rsync command to copy to this dir

$ rsync -av --exclude=".git" ~/.config/nvim .

Source

  • neovim config is copied from this repo, and modified

OSC 52 clipboard relay for remote vim/neovim

scripts/osc52/ makes a yank inside vim or neovim on a remote SSH host land in the local machine's clipboard, using the OSC 52 terminal escape sequence. No daemon, no port forwarding, no X11 forwarding — the sequence rides the existing SSH pty and the local terminal writes the clipboard. Tested with kitty and Ghostty.

Install on a remote host

# current user only (default when not root)
curl -fsSL https://raw.githubusercontent.com/riadafridishibly/configs/main/scripts/osc52/osc52-standalone.sh | sh -s -- --user

# system-wide, all users
curl -fsSL https://raw.githubusercontent.com/riadafridishibly/configs/main/scripts/osc52/osc52-standalone.sh | sudo sh -s -- --system

# see what it would do, change nothing
curl -fsSL https://raw.githubusercontent.com/riadafridishibly/configs/main/scripts/osc52/osc52-standalone.sh | sh -s -- --user --dry-run

# report whether it is installed, sourced and armed
curl -fsSL https://raw.githubusercontent.com/riadafridishibly/configs/main/scripts/osc52/osc52-standalone.sh | sh -s -- --user --check

# remove it again
curl -fsSL https://raw.githubusercontent.com/riadafridishibly/configs/main/scripts/osc52/osc52-standalone.sh | sh -s -- --user --uninstall

Piping a URL into a shell runs whatever that URL serves at that moment, and sudo sh runs it as root. Prefer fetching and checking the digest first, especially for the system-wide install:

curl -fsSL https://raw.githubusercontent.com/riadafridishibly/configs/main/scripts/osc52/osc52-standalone.sh -o /tmp/osc52.sh
shasum -a 256 /tmp/osc52.sh   # compare against a known-good digest
sh /tmp/osc52.sh --user

Replacing main in the URL with a commit SHA pins the content, so a later push cannot change what a host installs.

Terminal setup (local machine, once)

kitty allows clipboard writes by default. Ghostty asks every time, so set:

clipboard-write = allow

Check the whole path before touching any editor config — run this on the remote host, then paste locally:

printf "\033]52;c;$(printf 'hello-from-remote' | base64 | tr -d '\n')\a"

Verify it is loaded

--check reports all three failure modes separately — file missing, file present but not in runtimepath, and sourced but disarmed by a guard. Pass the same scope you installed with:

sh /tmp/osc52.sh --system --check
scope: system, action: check
vim  file:    present  /usr/share/vim/vimfiles/plugin/zzz-osc52.vim
vim  status:  ACTIVE - sourced and the yank hook is armed
vim  sourced from:
           /usr/share/vim/vimfiles/plugin/zzz-osc52.vim

The sourced from: list matters when both scopes are installed — it names the file that actually loaded, so a stale user-scope copy shadowing the system one is visible.

By hand, from inside vim or neovim:

Command Meaning
:echo exists('g:loaded_zzz_osc52') 1 = the file was found and sourced
:echo exists(':OSC52Yank') 2 = guards passed, the yank hook is armed
:filter zzz-osc52 scriptnames which path it was sourced from
:verbose autocmd TextYankPost shows the zzz_osc52 group and its autocmd

In neovim the first two also work as :lua= vim.g.loaded_zzz_osc52 and :lua= vim.fn.exists(':OSC52Yank').

loaded set with no :OSC52Yank means a guard bailed out — most often the session is not SSH, which is expected when testing locally. Force it with :let g:osc52_force = 1 before the plugin loads, or just test over SSH.

To prove the escape sequence really reaches the terminal, capture the pty:

printf 'yank-me-please\n' > /tmp/probe.txt
script -q /tmp/cap.bin vim -c 'normal! yy' -c 'qa!' /tmp/probe.txt >/dev/null
grep -c $'\033]52;c;' /tmp/cap.bin    # 1 = the sequence was emitted

Using it as root (after sudo su)

Yanking works as your normal user but stops after sudo su or sudo -i. Two separate causes, usually both at once:

  1. The plugin is not installed for root. su sets HOME=/root, so a --user install under your own $HOME is invisible to root's vim. Fixed by installing with --system.
  2. sudo strips SSH_TTY and SSH_CONNECTION. With env_reset on — the default nearly everywhere — only the env_keep whitelist survives, and those two are not on it. The plugin then sees a non-SSH session and disarms itself.

--check run as root names the second cause explicitly:

vim  status:  sourced, but a guard disarmed it
vim  reason:  SSH_TTY/SSH_CONNECTION unset as root - sudo stripped them.
vim           Fix: rerun the installer with --sudoers, or export OSC52_FORCE=1

Confirm it yourself with env | grep SSH before and after sudo su.

Pick one fix:

# preferred: let the SSH variables through sudo, system-wide
sudo sh /tmp/osc52.sh --system --sudoers

That writes /etc/sudoers.d/10-osc52-env containing:

Defaults env_keep += "SSH_TTY SSH_CONNECTION SSH_CLIENT"

The file is validated with visudo -cf before being installed and the whole sudoers set is re-validated afterwards, with a rollback if it does not parse. A malformed sudoers file locks everyone out of root, so --sudoers refuses to run at all when visudo is missing. --uninstall --sudoers removes it again.

Or, without touching sudoers, force the plugin on for root:

echo 'export OSC52_FORCE=1' >> /root/.bashrc

$OSC52_FORCE (and the vimscript g:osc52_force) bypass the SSH check. Root can always write to /dev/tty, so nothing else stands in the way.

Install targets

Scope vim neovim
--user ~/.vim/plugin/zzz-osc52.vim ${XDG_DATA_HOME:-~/.local/share}/nvim/site/plugin/zzz-osc52.lua
--system $VIM/vimfiles/plugin/zzz-osc52.vim /etc/xdg/nvim/plugin/zzz-osc52.lua

The neovim user-scope target is the site directory rather than ~/.config/nvim, so a git-tracked neovim config stays clean.

Why it does not collide

The plugin never assigns vim.g.clipboard and never touches the clipboard option. It only adds a TextYankPost autocmd that emits one extra escape sequence, so an existing clipboard provider, vim-oscyank, or a distro config such as LazyVim keeps working — the worst case is the same text sent twice and the terminal keeping the last copy.

It also bails out early when: already loaded (g:loaded_zzz_osc52), vim-oscyank is present, the session is not SSH (override with g:osc52_force), there is no writable /dev/tty, or the user opted out with g:osc52_disable / $OSC52_DISABLE. Yanks into named registers and deletes are ignored, and payloads above g:osc52_max_bytes (74994, the tmux buffer limit) are refused instead of silently truncated. tmux and screen get their DCS passthrough wrapping automatically.

Editing the scripts

osc52-standalone.sh is generated — edit zzz-osc52.vim, zzz-osc52.lua, or install-osc52.sh, then regenerate and commit the result:

sh scripts/osc52/build-standalone.sh

About

neovim, alactritty and other configs

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages