-
Notifications
You must be signed in to change notification settings - Fork 33
/
osc52.vim
157 lines (136 loc) · 5.12 KB
/
osc52.vim
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
" Copyright 2012 The ChromiumOS Authors
" Use of this source code is governed by a BSD-style license that can be
" found in the LICENSE file.
"
" This script can be used to send an arbitrary string to the terminal clipboard
" using the OSC 52 escape sequence, as specified in
" http://invisible-island.net/xterm/ctlseqs/ctlseqs.html, section "Operating
" System Controls", Ps => 52.
"
" To add this script to vim...
" 1. Save it somewhere.
" 2. Edit ~/.vimrc to include...
" source ~/path/to/osc52.vim
" vmap <C-c> y:call SendViaOSC52(getreg('"'))<cr>
"
" This will map Ctrl+C to copy. You can now select text in vi using the visual
" mark mode or the mouse, and press Ctrl+C to copy it to the clipboard.
"
" Max length of the OSC 52 sequence. Sequences longer than this will not be
" sent to the terminal.
let g:max_osc52_sequence=100000
" Send a string to the terminal's clipboard using the OSC 52 sequence.
function! SendViaOSC52 (str)
" Since tmux defaults to setting TERM=screen (ugh), we need to detect it here
" specially.
if !empty($TMUX)
let osc52 = s:get_OSC52_tmux(a:str)
elseif match($TERM, 'screen') > -1
let osc52 = s:get_OSC52_DCS(a:str)
else
let osc52 = s:get_OSC52(a:str)
endif
let len = strlen(osc52)
if len < g:max_osc52_sequence
call s:rawecho(osc52)
else
echo "Selection too long to send to terminal: " . len
endif
endfunction
" This function base64's the entire string and wraps it in a single OSC52.
"
" It's appropriate when running in a raw terminal that supports OSC 52.
function! s:get_OSC52 (str)
let b64 = s:b64encode(a:str, 0)
let rv = "\e]52;c;" . b64 . "\x07"
return rv
endfunction
" This function base64's the entire string and wraps it in a single OSC52 for
" tmux.
"
" This is for `tmux` sessions which filters OSC 52 locally.
function! s:get_OSC52_tmux (str)
let b64 = s:b64encode(a:str, 0)
let rv = "\ePtmux;\e\e]52;c;" . b64 . "\x07\e\\"
return rv
endfunction
" This function base64's the entire source, wraps it in a single OSC52, and then
" breaks the result in small chunks which are each wrapped in a DCS sequence.
"
" This is appropriate when running on `screen`. Screen doesn't support OSC 52,
" but will pass the contents of a DCS sequence to the outer terminal unmolested.
" It imposes a small max length to DCS sequences, so we send in chunks.
function! s:get_OSC52_DCS (str)
let b64 = s:b64encode(a:str, 76)
" Remove the trailing newline.
let b64 = substitute(b64, '\n*$', '', '')
" Replace each newline with an <end-dcs><start-dcs> pair.
let b64 = substitute(b64, '\n', "\e/\eP", "g")
" (except end-of-dcs is "ESC \", begin is "ESC P", and I can't figure out
" how to express "ESC \ ESC P" in a single string. So, the first substitute
" uses "ESC / ESC P", and the second one swaps out the "/". It seems like
" there should be a better way.)
let b64 = substitute(b64, '/', '\', 'g')
" Now wrap the whole thing in <start-dcs><start-osc52>...<end-osc52><end-dcs>.
let b64 = "\eP\e]52;c;" . b64 . "\x07\e\x5c"
return b64
endfunction
" Echo a string to the terminal without munging the escape sequences.
function! s:rawecho (str)
" We have to use some way to send this message to stdout.
" Vim's built-in echo does not write to stdout and only displays on the
" command line in the vim interface.
if filewritable('/dev/stdout')
" Write directly to stdout. This will prevent a flicker from occurring
" since no redraw is required.
call writefile([a:str], '/dev/stdout', 'b')
else
" This will cause a flicker to occur due to a new shell actually
" appearing, requiring a redraw of vim, but we will use as fallback.
exec("silent! !echo " . shellescape(a:str))
redraw!
endif
endfunction
" Lookup table for s:b64encode.
let s:b64_table = [
\ "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","+","/"]
" Encode a string of bytes in base 64.
" Based on http://vim-soko.googlecode.com/svn-history/r405/trunk/vimfiles/
" autoload/base64.vim
" If size is > 0 the output will be line wrapped every `size` chars.
function! s:b64encode(str, size)
let bytes = s:str2bytes(a:str)
let b64 = []
for i in range(0, len(bytes) - 1, 3)
let n = bytes[i] * 0x10000
\ + get(bytes, i + 1, 0) * 0x100
\ + get(bytes, i + 2, 0)
call add(b64, s:b64_table[n / 0x40000])
call add(b64, s:b64_table[n / 0x1000 % 0x40])
call add(b64, s:b64_table[n / 0x40 % 0x40])
call add(b64, s:b64_table[n % 0x40])
endfor
if len(bytes) % 3 == 1
let b64[-1] = '='
let b64[-2] = '='
endif
if len(bytes) % 3 == 2
let b64[-1] = '='
endif
let b64 = join(b64, '')
if a:size <= 0
return b64
endif
let chunked = ''
while strlen(b64) > 0
let chunked .= strpart(b64, 0, a:size) . "\n"
let b64 = strpart(b64, a:size)
endwhile
return chunked
endfunction
function! s:str2bytes(str)
return map(range(len(a:str)), 'char2nr(a:str[v:val])')
endfunction