├── autoload └── singleton.vim └── doc └── singleton.txt /autoload/singleton.vim: -------------------------------------------------------------------------------- 1 | " Uses Vim with singleton. 2 | " Version: 1.1 3 | " Author : thinca 4 | " License: zlib License 5 | 6 | let s:save_cpo = &cpo 7 | set cpo&vim 8 | 9 | function! s:def(var, val) 10 | if !exists(a:var) 11 | let {a:var} = a:val 12 | endif 13 | endfunction 14 | 15 | call s:def('g:singleton#ignore_pattern', {}) 16 | call s:def('g:singleton#entrust_pattern', { 17 | \ 'svn': [ 18 | \ '/svn-\%(commit\|prop\)\%(\.\d\+\)\?\.tmp$', 19 | \ '/\.svn/tmp/.*\.tmp$', 20 | \ ], 21 | \ 'git': [ 22 | \ '/\.git/modules/', 23 | \ '/\.git/\%(modules/.\+/\)\?\%(COMMIT_EDIT\|TAG_EDIT\|MERGE_\|SQUASH_\)MSG$', 24 | \ '/\.git/.*git-rebase-todo$', 25 | \ '/\.git/rebase-\%(merge\|apply\)/', 26 | \ '/\.git/.*\.diff$', 27 | \ ], 28 | \ 'hub': '/\.git/\%(RELEASE\|PULLREQ\|ISSUE\)_EDITMSG$', 29 | \ 'hg': '/hg-editor-.\{6}\.txt$', 30 | \ 'bzr': '/bzr_log\..\{6}$', 31 | \ 'yaourt': '^/tmp/yaourt-tmp-[^/]\+/', 32 | \ }) 33 | call s:def('g:singleton#group', $USER . $USERNAME) 34 | call s:def('g:singleton#opener', 'tab drop') 35 | call s:def('g:singleton#treat_stdin', 1) 36 | call s:def('g:singleton#disable', 0) 37 | 38 | let s:data_count = get(s:, 'data_count', 1) 39 | let s:master = get(s:, 'master', 0) 40 | 41 | let s:entrust_clients = {} 42 | 43 | function! singleton#enable(...) 44 | if !has('vim_starting') || g:singleton#disable 45 | return 46 | endif 47 | if !has('clientserver') 48 | throw 'singleton: This plugin requires +clientserver feature.' 49 | endif 50 | 51 | " Avoid starting with multiple Vim instances. 52 | call s:set_leave() 53 | if singleton#get_master() ==# '' 54 | let s:master = 1 55 | return 56 | endif 57 | 58 | " Stdin(:help --) support. 59 | let c = argc() 60 | if g:singleton#treat_stdin && c == 0 61 | augroup plugin-singleton-stdin 62 | autocmd! StdinReadPost * 63 | \ call singleton#send('stdin', ['[stdin]', getline(1, '$')]) 64 | augroup END 65 | return 66 | endif 67 | 68 | " FIXME: A path that doesn't exist can not expand to fullpath. 69 | let files = map(argv(), 'fnamemodify(v:val, ":p")') 70 | 71 | " Diff mode support. 72 | if &diff && c <= 2 73 | call singleton#send('diff', [files]) 74 | return 75 | endif 76 | 77 | " Remote edit support. 78 | let pattern = s:to_pattern(g:singleton#entrust_pattern) 79 | if c == 1 && s:path(files[0]) =~? pattern 80 | call singleton#send('entrust', [files[0]]) 81 | return 82 | endif 83 | 84 | let pattern = s:to_pattern(g:singleton#ignore_pattern) 85 | if pattern !=# '' 86 | call filter(files, 's:path(v:val) !~? pattern') 87 | endif 88 | if !empty(files) 89 | call singleton#send('file', [files]) 90 | endif 91 | endfunction 92 | 93 | function! singleton#is_master() 94 | return 0 < s:master 95 | endfunction 96 | 97 | function! singleton#get_master() 98 | let master = get(filter(s:serverlist(), 99 | \ 's:remote_expr(v:val, "singleton#is_master()", "")'), 0, '') 100 | return master 101 | endfunction 102 | 103 | function! singleton#set_master(...) 104 | let server = '' 105 | let val = 1 106 | for arg in a:000 107 | if type(arg) == type('') 108 | let server = arg 109 | elseif type(arg) == type(0) 110 | let val = arg 111 | endif 112 | unlet arg 113 | endfor 114 | 115 | if server !=# '' 116 | return s:remote_expr(server, printf('singleton#set_master(%d)', val)) 117 | endif 118 | 119 | if 0 < val 120 | if v:servername !=# '' && s:master == 0 121 | let master = singleton#get_master() 122 | if master ==# '' || s:remote_expr(master, 'singleton#set_master(0)') 123 | let s:master = 1 124 | return 1 125 | endif 126 | endif 127 | else 128 | let s:master = val 129 | return 1 130 | endif 131 | return 0 132 | endfunction 133 | 134 | function! singleton#send(action, args) 135 | let server = singleton#get_master() 136 | if server ==# '' 137 | return 138 | endif 139 | 140 | augroup plugin-singleton-wait 141 | autocmd! RemoteReply * call s:replied(expand('')) 142 | augroup END 143 | set viminfo= " Don't save viminfo. 144 | 145 | try 146 | call remote_foreground(server) 147 | catch 148 | endtry 149 | 150 | let expr = printf('singleton#receive(%s, %s)', 151 | \ string(a:action), string(a:args)) 152 | let ret = s:remote_expr(server, expr, 'error') 153 | 154 | if ret ==# 'error' 155 | return 156 | endif 157 | 158 | if ret ==# 'ok' 159 | quitall! 160 | endif 161 | 162 | if !has('gui_running') 163 | echo 'Opening by remote Vim...' 164 | echo 'cancel to ' 165 | endif 166 | call s:wait() 167 | echo 'Cancelled. Starting up Vim...' 168 | call s:remote_expr(server, 'singleton#receive("cancel", [])') 169 | endfunction 170 | 171 | function! s:replied(serverid) 172 | autocmd! plugin-singleton-wait 173 | let result = remote_read(a:serverid) 174 | if !has('gui_running') 175 | " echo result 176 | endif 177 | quitall! 178 | endfunction 179 | 180 | function! singleton#receive(cmd, args) 181 | stopinsert 182 | let ret = call('s:action_' . a:cmd, a:args) 183 | call foreground() 184 | return ret 185 | endfunction 186 | 187 | function! s:action_file(files) 188 | for f in type(a:files) == type([]) ? a:files : [a:files] 189 | call s:open(f, 'file') 190 | endfor 191 | redraw 192 | return 'ok' 193 | endfunction 194 | 195 | function! s:action_entrust(file) 196 | call s:open(a:file, 'entrust') 197 | setlocal bufhidden=wipe 198 | let s:entrust_clients[bufnr('%')] = expand('') 199 | augroup plugin-singleton-reply 200 | autocmd! BufWipeout call s:finish_edit(expand('')) 201 | autocmd! VimLeave * call s:finish_edit_all() 202 | augroup END 203 | redraw 204 | return 'delay' 205 | endfunction 206 | 207 | function! s:finish_edit(bufnr) 208 | if has_key(s:entrust_clients, a:bufnr) 209 | let client_id = remove(s:entrust_clients, a:bufnr) 210 | call s:server2client(client_id, 'ok') 211 | endif 212 | endfunction 213 | 214 | function! s:finish_edit_all() 215 | for bufnr in keys(s:entrust_clients) 216 | call s:finish_edit(bufnr) 217 | endfor 218 | endfunction 219 | 220 | function! s:action_diff(files) 221 | if type(a:files) != type([]) || len(a:files) < 2 || 3 < len(a:files) 222 | throw 'singleton: Invalid argument for diff(): ' . string(a:files) 223 | endif 224 | 225 | let files = map(copy(a:files), 'fnamemodify(v:val, ":p")') 226 | call s:open(files[0], 'diff') 227 | diffthis 228 | for f in files[1 :] 229 | rightbelow vsplit `=f` 230 | diffthis 231 | endfor 232 | " windo diffthis 233 | 1 wincmd w 234 | return 'ok' 235 | endfunction 236 | 237 | function! s:action_stdin(name, data) 238 | let name = a:name . '@' . s:data_count 239 | let s:data_count += 1 240 | call s:open(name, 'stdin') 241 | silent put =a:data 242 | silent 1 delete _ 243 | setlocal readonly nomodified buftype=nofile 244 | filetype detect 245 | redraw 246 | return 'ok' 247 | endfunction 248 | 249 | function! s:action_cancel() 250 | " XXX: Don't support multi client. 251 | autocmd! plugin-singleton-reply 252 | echohl WarningMsg 253 | echomsg 'singleton: Operation cancelled from client.' 254 | echohl None 255 | endfunction 256 | 257 | function! s:wait() 258 | let c = '' 259 | try 260 | while c !=# "\" 261 | let c = getchar() 262 | if type(c) == type(0) 263 | let c = nr2char(c) 264 | endif 265 | endwhile 266 | catch '^Vim:Interrupt' 267 | endtry 268 | endfunction 269 | 270 | function! s:to_pattern(pat) 271 | if type(a:pat) == type('') 272 | return a:pat 273 | elseif type(a:pat) == type([]) 274 | return join(map(a:pat, 's:to_pattern(v:val)'), '\m\|') 275 | elseif type(a:pat) == type({}) 276 | return s:to_pattern(values(a:pat)) 277 | endif 278 | return '' 279 | endfunction 280 | 281 | function! s:bufopened(file) 282 | let f = fnamemodify(a:file, ':p') 283 | for tabnr in range(1, tabpagenr('$')) 284 | for nbuf in tabpagebuflist(tabnr) 285 | if f ==# fnamemodify(bufname(nbuf), ':p') 286 | return 1 287 | endif 288 | endfor 289 | endfor 290 | return 0 291 | endfunction 292 | 293 | function! s:open(file, type) 294 | if exists('g:singleton#opener_' . a:type) 295 | let opener = g:singleton#opener_{a:type} 296 | else 297 | let opener = g:singleton#opener 298 | endif 299 | let openfile = fnamemodify(a:file, ':p') 300 | execute opener '`=openfile`' 301 | endfunction 302 | 303 | function! s:path(path) 304 | return simplify(substitute(a:path, '\\', '/', 'g')) 305 | endfunction 306 | 307 | function! s:server2client(clientid, string) 308 | try 309 | return server2client(a:clientid, a:string) 310 | catch 311 | echohl ErrorMsg 312 | echomsg matchstr(v:exception, '^Vim(.\{-}):\zs.*') 313 | echohl None 314 | endtry 315 | endfunction 316 | 317 | function! s:serverlist() 318 | return filter(split(serverlist(), "\n"), 's:check_id(v:val)') 319 | endfunction 320 | 321 | function! s:check_id(server) 322 | return s:remote_expr(a:server, 'g:singleton#group') ==# g:singleton#group 323 | endfunction 324 | 325 | function! s:remote_expr(server, expr, ...) 326 | let default = a:0 ? a:1 : 0 327 | try 328 | return remote_expr(a:server, a:expr) 329 | catch 330 | endtry 331 | return default 332 | endfunction 333 | 334 | function! s:set_leave() 335 | augroup plugin-singleton-leave 336 | autocmd! VimLeave * call s:on_leave() 337 | augroup END 338 | endfunction 339 | 340 | function! s:on_leave() 341 | if singleton#is_master() 342 | for s in s:serverlist() 343 | if s:remote_expr(s, 'singleton#set_master(1)') 344 | return 345 | endif 346 | endfor 347 | endif 348 | endfunction 349 | 350 | let &cpo = s:save_cpo 351 | unlet s:save_cpo 352 | -------------------------------------------------------------------------------- /doc/singleton.txt: -------------------------------------------------------------------------------- 1 | *singleton.txt* Uses Vim with singleton. 2 | 3 | Version: 1.1 4 | Author : thinca 5 | License: zlib License 6 | 7 | ============================================================================== 8 | CONTENTS *singleton-contents* 9 | 10 | INTRODUCTION |singleton-introduction| 11 | FEATURES |singleton-features| 12 | INTERFACE |singleton-interface| 13 | FUNCTIONS |singleton-functions| 14 | CUSTOMIZING |singleton-customizing| 15 | PATTERN |singleton-pattern| 16 | MERGE DEFAULT DICT |singleton-merge-default-dict| 17 | LIMITATION |singleton-limitation| 18 | CHANGELOG |singleton-changelog| 19 | 20 | 21 | 22 | ============================================================================== 23 | INTRODUCTION *singleton-introduction* 24 | 25 | *singleton* is a Vim plugin to use Vim with singleton. 26 | 27 | This plugin provides the following features. 28 | 29 | 1. Avoid starting with multiplex. 30 | 2. Diff mode support. 31 | 3. Stdin(|--|) support. 32 | 4. Remote edit support. 33 | 34 | See |singleton-features| for details. 35 | 36 | You just call a function in vimrc for use. 37 | > 38 | call singleton#enable() 39 | < 40 | And Vim exits if given files by arguments 41 | were successfully passed to remote Vim. 42 | 43 | Requirements: 44 | - Vim 7.3 or later 45 | - |+clientserver| 46 | 47 | Latest version: 48 | https://github.com/thinca/vim-singleton 49 | 50 | 51 | 52 | ============================================================================== 53 | FEATURES *singleton-features* 54 | 55 | This plugin provides the following features. 56 | 57 | 1. Avoid starting with multiple Vim instances. 58 | When you start Vim with one or more files, and remote Vim has already started, 59 | this plugin opens the files by the existing Vim, and exits immediately. 60 | 61 | 2. Diff mode support. 62 | If you start Vim by |vimdiff| or with |-d| option, this plugin opens the files 63 | by diff mode by an existing Vim. 64 | 65 | 3. Stdin(|--|) support. 66 | When stdin is supplied to Vim, the data is sent to an existing Vim. 67 | 68 | 4. Remote edit support. 69 | When a file that matches to specified pattern(|g:singleton#entrust_pattern|) 70 | was passed, this plugin opens the file by an existing Vim, and wait. The edit 71 | has finished and the buffer is closed, the first Vim is terminate. 72 | 73 | 74 | 75 | ============================================================================== 76 | INTERFACE *singleton-interface* 77 | 78 | ------------------------------------------------------------------------------ 79 | FUNCTIONS *singleton-functions* 80 | 81 | singleton#enable() *singleton#enable()* 82 | Enables this plugin. 83 | See |singleton-features| for the details. 84 | This works only at Vim startup. 85 | 86 | singleton#is_master() *singleton#is_master()* 87 | Returns true if this Vim instance is master. 88 | 89 | singleton#get_master() *singleton#get_master()* 90 | Returns the server name of master. 91 | Returns an empty string if master does not exist. 92 | 93 | singleton#set_master([{server-name}][, {val}]) *singleton#set_master()* 94 | Sets a Vim as master. The master is only one always. 95 | If the argument is a string, it is {server-name}. If omitted, set to 96 | current Vim. 97 | If the argument is a number, it is {val}. Default is 1. 98 | val mean ~ 99 | 1 Makes a master. 100 | 0 Stops a master. 101 | -1 Never sets to master. 102 | 103 | 104 | 105 | ============================================================================== 106 | CUSTOMIZING *singleton-customizing* 107 | 108 | g:singleton#ignore_pattern *g:singleton#ignore_pattern* 109 | A pattern(|singleton-pattern|) for specifying the file which should 110 | be ignored. 111 | If you want to add key/value pair, see |singleton-merge-default-dict|. 112 | Default: empty 113 | 114 | g:singleton#entrust_pattern *g:singleton#entrust_pattern* 115 | A pattern(|singleton-pattern|) for specifying the file which should 116 | be entrusted to remote Vim. 117 | It means, Vim waits until remote file is closed. 118 | This is useful to get along with version control systems. 119 | If you want to add key/value pair, see |singleton-merge-default-dict|. 120 | Default: Files for Subversion, Git, Hg, and Bazaar. 121 | This is large. See the variable directly for details. 122 | 123 | g:singleton#treat_stdin *g:singleton#treat_stdin* 124 | Is whether to treat stdin. 125 | Default: 1 126 | 127 | g:singleton#opener *g:singleton#opener* 128 | A command to open a buffer. 129 | Default: "tab drop" 130 | 131 | g:singleton#opener_file *g:singleton#opener_file* 132 | g:singleton#opener_entrust *g:singleton#opener_entrust* 133 | g:singleton#opener_diff *g:singleton#opener_diff* 134 | g:singleton#opener_stdin *g:singleton#opener_stdin* 135 | Specialized version of opener for each situation. 136 | If these are not defined, |g:singleton#opener| is used. 137 | 138 | g:singleton#group *g:singleton#group* 139 | Group name. This plugin connects to same groups only. 140 | Default: $USER . $USERNAME 141 | 142 | g:singleton#disable *g:singleton#disable* 143 | Disables this plugin. 144 | Usually, you do not need to set this variable. 145 | You can disable this plugin from command-line argument. > 146 | $ vim --cmd "let g:singleton#disable=1" 147 | < Default: 0 148 | 149 | ------------------------------------------------------------------------------ 150 | PATTERN *singleton-pattern* 151 | 152 | A pattern is one of following. 153 | 154 | 1. A string which is |regexp|. 155 | 2. A list of patterns. 156 | All patterns are made |regexp| and joined as |regexp|. 157 | 3. A dictionary which has pattern as value. 158 | The values as list are treated like 2. 159 | 160 | 161 | ------------------------------------------------------------------------------ 162 | MERGE DEFAULT DICT *singleton-merge-default-dict* 163 | 164 | If you want to add key/value pair while keeping default value, you can write: > 165 | let g:singleton#entrust_pattern = extend({ 166 | \ 'foo' : ['/path/to/foo-file', '/more/path/to/foo-file'], 167 | \ 'bar' : '/tmp/.*bar.*', 168 | \}, g:singleton#entrust_pattern, 'keep') 169 | < 170 | If you want to overwrite default value, you can write: > 171 | let g:singleton#entrust_pattern = { 172 | \ 'foo' : ['/path/to/foo-file', '/more/path/to/foo-file'], 173 | \ 'bar' : '/tmp/.*bar.*', 174 | \} 175 | < 176 | 177 | ============================================================================== 178 | LIMITATION *singleton-limitation* 179 | 180 | - Vim script can not know command line arguments of vim. So, this plugin can 181 | not change the behavior by these. 182 | - ex: |-o| |-O| |-p| |-f| |-+| |-+/| ... 183 | 184 | 185 | 186 | ============================================================================== 187 | CHANGELOG *singleton-changelog* 188 | 189 | 1.1 2012-12-26 190 | - Fix the timing of |remote_foreground()|. 191 | - Improve default value for svn. 192 | 193 | 1.0 2012-02-01 194 | - Initial version. 195 | 196 | 197 | ============================================================================== 198 | vim:tw=78:fo=tcq2mM:ts=8:ft=help:norl 199 | --------------------------------------------------------------------------------