Skip to content

Instantly share code, notes, and snippets.

@jldiaz
Last active April 11, 2024 07:45
Show Gist options
  • Save jldiaz/74b911ccab3db9f4a267f03d91f32ef0 to your computer and use it in GitHub Desktop.
Save jldiaz/74b911ccab3db9f4a267f03d91f32ef0 to your computer and use it in GitHub Desktop.
Copy/paste into vim registers via ssh tunnel

These scripts allows you to use vim in a remote machine via ssh, and being capable of sharing the local clipboard with vim's 0 register (which is the one used by default by yank/paste).

To make it work:

  1. In your local machine, run clipboard-server.py, which uses flask to create a simple REST API exposing only the / endpoint, listening on localhost:11223

    GET / will retrieve the system clipboard of the machine in which the server is run, in form of a JSON with the field "content".

    POST / expects a JSON with a field named "content" containing one string, which is placed in the system clipborad of the machine running the server.

  2. Copy the content of the vimrc file to the end of your ~/.vimrc in the remote machine. Follow the commments for customization. This script makes use of vim's python extension, which has to be enabled (you can check if it is enabled with vim --version | grep python)

  3. Connect via ssh to your remote machine, while setting up a remote tunnel:

    ssh -R11223:localhost:11223 user@remote.ip
    

    This leaves a socket listening in port 11223 in the remote machine, and forwards all the traffic to that port to your local Flask server. I.e.: in your remote machine you can make HTTP GET and POST requests to localhost:11223 and your local server will answer. As bonus, the connection is encrypted by the tunnel.

  4. Open vim in the remote machine.

    Select some text in vim (eg: using V) and then press RY. The contents of the selection will be sent to your local flask sever, which will put them in the system clipboard, from which you can paste it into any other app in your local host.

    In your local machine select some text in any app and copy it to your system clipboard. Then, in the remote vim use KP to retrieve this text and paste it. The text is also placed in 0 register, so you can paste it again pressing p or P.

# This is the local server
# To run it you need to have installed flask and xerox modules
# In windows, module pywin32 is also required
from flask import Flask, jsonify, request
import xerox
app = Flask(__name__)
@app.route("/")
def get_clipboard():
# Remove `\r` in Windows
clipboard = xerox.paste().replace("\r", "")
# print(clipboard) # For debugging
return jsonify({"content": clipboard})
@app.route("/", methods=["POST"])
def post_clipboard():
xerox.copy(request.json["content"])
return ""
if __name__ == "__main__":
app.run("localhost", 11223)
" By default, register 0 is used as source and destination for the data
" You can change it to register + or * if you want to use the remote
" system clipboard, but that will not work without a remote X session
function! SendRegister()
python << END
# Send the content of register 0 to REST API
import vim, urllib2, json
TIMEOUT = 3
URL = "http://localhost:11223"
local_clipboard = vim.eval("@0")
try:
req = urllib2.Request(URL)
req.add_header('Content-Type', 'application/json')
data = {"content": local_clipboard}
r = urllib2.urlopen(req, data=json.dumps(data), timeout=TIMEOUT)
print(r.read())
except Exception as e:
print e
print "Some error happened. Is your server running?"
END
endfunction
function! GetRegister()
python << END
# Get text from REST API and place it in register 0
import vim, urllib2, json
TIMEOUT=3
URL="http://localhost:11223"
try:
req = urllib2.Request(URL)
r = urllib2.urlopen(req, timeout=TIMEOUT)
data = json.loads(r.read())
vim.command("let @0 = '%s'" % data["content"])
except Exception as e:
print e
print "Some error happened. Is your server running?"
END
endfunction
" Define some keybindings.
" RY will yank the current selection and then call SendRegister()
" RP will call GetRegister() and then paste the retrieved text
"
" You may prefer to simply call SendRegister() and GetRegister(), ie:
"
" map RY :call SendRegister()<cr>
" map RP :call GetRegister()<cr>
"
" This way you have more freedom about how to "yank", and how to "paste"
" using other vim commands. For example, you use "yy" to yank current line
" and then "RY" to send it to the REST server
map RY y:call SendRegister()<cr>
map RP :call GetRegister()<cr>P
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment