├── .gitignore ├── README.md ├── autoload ├── lv.vim └── plug.vim ├── ftplugin ├── json.vim ├── php.vim └── vim.vim └── init.vim /.gitignore: -------------------------------------------------------------------------------- 1 | plugged/* 2 | *~ 3 | autoload/plug.vim.old 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | __!!Archived. Use [lvht/vim](https://github.com/lvht/vim) instead!!__ 2 | 3 | Lv's NeoVim conf 4 | ================ 5 | 6 | Some useful configuration and plugins. **NeoVim ONLY**!! 7 | 8 | Enjoy it :) 9 | -------------------------------------------------------------------------------- /autoload/lv.vim: -------------------------------------------------------------------------------- 1 | func! lv#ExpandTab(len) " {{{ 2 | if a:len 3 | setlocal expandtab 4 | execute 'setlocal shiftwidth='.a:len 5 | execute 'setlocal softtabstop='.a:len 6 | execute 'setlocal tabstop='.a:len 7 | else 8 | setlocal noexpandtab 9 | execute 'setlocal shiftwidth&vim' 10 | execute 'setlocal softtabstop&vim' 11 | execute 'setlocal tabstop&vim' 12 | endif 13 | endfunc " }}} 14 | 15 | func! s:savePos() " {{{ 16 | let cur_pos = getcurpos() 17 | let cur_pos[0] = bufnr('%') 18 | let s:lv_viml_jump_stack = get(s:, 'lv_viml_jump_stack', []) 19 | call add(s:lv_viml_jump_stack, cur_pos) 20 | endfunc " }}} 21 | 22 | func! lv#VimGoTo() "{{{ 23 | let cw = expand('') 24 | 25 | let parts = split(cw, '#') 26 | if len(parts) == 1 27 | call s:savePos() 28 | call search('func.*'.cw) 29 | normal zv 30 | return 31 | endif 32 | 33 | let paths = split(&runtimepath, ',') 34 | for p in paths 35 | let f = p.'/autoload/'.join(parts[0:-2],'/').'.vim' 36 | if filereadable(f) 37 | call s:savePos() 38 | execute 'e '.f 39 | call search('func.*'.cw) 40 | normal zv 41 | return 42 | endif 43 | endfor 44 | endfunc "}}} 45 | 46 | func! lv#VimBack() "{{{ 47 | let s:lv_viml_jump_stack = get(s:, 'lv_viml_jump_stack', []) 48 | 49 | if len(s:lv_viml_jump_stack) == 0 50 | return 51 | endif 52 | 53 | let prev_pos = s:lv_viml_jump_stack[-1] 54 | let prev_buf = prev_pos[0] 55 | let prev_pos[0] = 0 56 | unlet s:lv_viml_jump_stack[-1] 57 | exec 'buffer '.prev_buf 58 | call setpos('.', prev_pos) 59 | normal zv 60 | endfunc "}}} 61 | 62 | function! lv#FindRoot() " {{{ 63 | let result = system('git rev-parse --show-toplevel') 64 | if v:shell_error == 0 65 | return substitute(result, '\n*$', '', 'g') 66 | endif 67 | 68 | return "." 69 | endfunction " }}} 70 | -------------------------------------------------------------------------------- /autoload/plug.vim: -------------------------------------------------------------------------------- 1 | " vim-plug: Vim plugin manager 2 | " ============================ 3 | " 4 | " Download plug.vim and put it in ~/.vim/autoload 5 | " 6 | " curl -fLo ~/.vim/autoload/plug.vim --create-dirs \ 7 | " https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim 8 | " 9 | " Edit your .vimrc 10 | " 11 | " call plug#begin('~/.vim/plugged') 12 | " 13 | " " Make sure you use single quotes 14 | " 15 | " " Shorthand notation; fetches https://github.com/junegunn/vim-easy-align 16 | " Plug 'junegunn/vim-easy-align' 17 | " 18 | " " Any valid git URL is allowed 19 | " Plug 'https://github.com/junegunn/vim-github-dashboard.git' 20 | " 21 | " " Multiple Plug commands can be written in a single line using | separators 22 | " Plug 'SirVer/ultisnips' | Plug 'honza/vim-snippets' 23 | " 24 | " " On-demand loading 25 | " Plug 'scrooloose/nerdtree', { 'on': 'NERDTreeToggle' } 26 | " Plug 'tpope/vim-fireplace', { 'for': 'clojure' } 27 | " 28 | " " Using a non-master branch 29 | " Plug 'rdnetto/YCM-Generator', { 'branch': 'stable' } 30 | " 31 | " " Using a tagged release; wildcard allowed (requires git 1.9.2 or above) 32 | " Plug 'fatih/vim-go', { 'tag': '*' } 33 | " 34 | " " Plugin options 35 | " Plug 'nsf/gocode', { 'tag': 'v.20150303', 'rtp': 'vim' } 36 | " 37 | " " Plugin outside ~/.vim/plugged with post-update hook 38 | " Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': './install --all' } 39 | " 40 | " " Unmanaged plugin (manually installed and updated) 41 | " Plug '~/my-prototype-plugin' 42 | " 43 | " " Initialize plugin system 44 | " call plug#end() 45 | " 46 | " Then reload .vimrc and :PlugInstall to install plugins. 47 | " 48 | " Plug options: 49 | " 50 | "| Option | Description | 51 | "| ----------------------- | ------------------------------------------------ | 52 | "| `branch`/`tag`/`commit` | Branch/tag/commit of the repository to use | 53 | "| `rtp` | Subdirectory that contains Vim plugin | 54 | "| `dir` | Custom directory for the plugin | 55 | "| `as` | Use different name for the plugin | 56 | "| `do` | Post-update hook (string or funcref) | 57 | "| `on` | On-demand loading: Commands or ``-mappings | 58 | "| `for` | On-demand loading: File types | 59 | "| `frozen` | Do not update unless explicitly specified | 60 | " 61 | " More information: https://github.com/junegunn/vim-plug 62 | " 63 | " 64 | " Copyright (c) 2017 Junegunn Choi 65 | " 66 | " MIT License 67 | " 68 | " Permission is hereby granted, free of charge, to any person obtaining 69 | " a copy of this software and associated documentation files (the 70 | " "Software"), to deal in the Software without restriction, including 71 | " without limitation the rights to use, copy, modify, merge, publish, 72 | " distribute, sublicense, and/or sell copies of the Software, and to 73 | " permit persons to whom the Software is furnished to do so, subject to 74 | " the following conditions: 75 | " 76 | " The above copyright notice and this permission notice shall be 77 | " included in all copies or substantial portions of the Software. 78 | " 79 | " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 80 | " EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 81 | " MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 82 | " NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 83 | " LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 84 | " OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 85 | " WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 86 | 87 | if exists('g:loaded_plug') 88 | finish 89 | endif 90 | let g:loaded_plug = 1 91 | 92 | let s:cpo_save = &cpo 93 | set cpo&vim 94 | 95 | let s:plug_src = 'https://github.com/junegunn/vim-plug.git' 96 | let s:plug_tab = get(s:, 'plug_tab', -1) 97 | let s:plug_buf = get(s:, 'plug_buf', -1) 98 | let s:mac_gui = has('gui_macvim') && has('gui_running') 99 | let s:is_win = has('win32') 100 | let s:nvim = has('nvim-0.2') || (has('nvim') && exists('*jobwait') && !s:is_win) 101 | let s:vim8 = has('patch-8.0.0039') && exists('*job_start') 102 | let s:me = resolve(expand(':p')) 103 | let s:base_spec = { 'branch': 'master', 'frozen': 0 } 104 | let s:TYPE = { 105 | \ 'string': type(''), 106 | \ 'list': type([]), 107 | \ 'dict': type({}), 108 | \ 'funcref': type(function('call')) 109 | \ } 110 | let s:loaded = get(s:, 'loaded', {}) 111 | let s:triggers = get(s:, 'triggers', {}) 112 | 113 | function! plug#begin(...) 114 | if a:0 > 0 115 | let s:plug_home_org = a:1 116 | let home = s:path(fnamemodify(expand(a:1), ':p')) 117 | elseif exists('g:plug_home') 118 | let home = s:path(g:plug_home) 119 | elseif !empty(&rtp) 120 | let home = s:path(split(&rtp, ',')[0]) . '/plugged' 121 | else 122 | return s:err('Unable to determine plug home. Try calling plug#begin() with a path argument.') 123 | endif 124 | if fnamemodify(home, ':t') ==# 'plugin' && fnamemodify(home, ':h') ==# s:first_rtp 125 | return s:err('Invalid plug home. '.home.' is a standard Vim runtime path and is not allowed.') 126 | endif 127 | 128 | let g:plug_home = home 129 | let g:plugs = {} 130 | let g:plugs_order = [] 131 | let s:triggers = {} 132 | 133 | call s:define_commands() 134 | return 1 135 | endfunction 136 | 137 | function! s:define_commands() 138 | command! -nargs=+ -bar Plug call plug#() 139 | if !executable('git') 140 | return s:err('`git` executable not found. Most commands will not be available. To suppress this message, prepend `silent!` to `call plug#begin(...)`.') 141 | endif 142 | command! -nargs=* -bar -bang -complete=customlist,s:names PlugInstall call s:install(0, []) 143 | command! -nargs=* -bar -bang -complete=customlist,s:names PlugUpdate call s:update(0, []) 144 | command! -nargs=0 -bar -bang PlugClean call s:clean(0) 145 | command! -nargs=0 -bar PlugUpgrade if s:upgrade() | execute 'source' s:esc(s:me) | endif 146 | command! -nargs=0 -bar PlugStatus call s:status() 147 | command! -nargs=0 -bar PlugDiff call s:diff() 148 | command! -nargs=? -bar -bang -complete=file PlugSnapshot call s:snapshot(0, ) 149 | endfunction 150 | 151 | function! s:to_a(v) 152 | return type(a:v) == s:TYPE.list ? a:v : [a:v] 153 | endfunction 154 | 155 | function! s:to_s(v) 156 | return type(a:v) == s:TYPE.string ? a:v : join(a:v, "\n") . "\n" 157 | endfunction 158 | 159 | function! s:glob(from, pattern) 160 | return s:lines(globpath(a:from, a:pattern)) 161 | endfunction 162 | 163 | function! s:source(from, ...) 164 | let found = 0 165 | for pattern in a:000 166 | for vim in s:glob(a:from, pattern) 167 | execute 'source' s:esc(vim) 168 | let found = 1 169 | endfor 170 | endfor 171 | return found 172 | endfunction 173 | 174 | function! s:assoc(dict, key, val) 175 | let a:dict[a:key] = add(get(a:dict, a:key, []), a:val) 176 | endfunction 177 | 178 | function! s:ask(message, ...) 179 | call inputsave() 180 | echohl WarningMsg 181 | let answer = input(a:message.(a:0 ? ' (y/N/a) ' : ' (y/N) ')) 182 | echohl None 183 | call inputrestore() 184 | echo "\r" 185 | return (a:0 && answer =~? '^a') ? 2 : (answer =~? '^y') ? 1 : 0 186 | endfunction 187 | 188 | function! s:ask_no_interrupt(...) 189 | try 190 | return call('s:ask', a:000) 191 | catch 192 | return 0 193 | endtry 194 | endfunction 195 | 196 | function! plug#end() 197 | if !exists('g:plugs') 198 | return s:err('Call plug#begin() first') 199 | endif 200 | 201 | if exists('#PlugLOD') 202 | augroup PlugLOD 203 | autocmd! 204 | augroup END 205 | augroup! PlugLOD 206 | endif 207 | let lod = { 'ft': {}, 'map': {}, 'cmd': {} } 208 | 209 | if exists('g:did_load_filetypes') 210 | filetype off 211 | endif 212 | for name in g:plugs_order 213 | if !has_key(g:plugs, name) 214 | continue 215 | endif 216 | let plug = g:plugs[name] 217 | if get(s:loaded, name, 0) || !has_key(plug, 'on') && !has_key(plug, 'for') 218 | let s:loaded[name] = 1 219 | continue 220 | endif 221 | 222 | if has_key(plug, 'on') 223 | let s:triggers[name] = { 'map': [], 'cmd': [] } 224 | for cmd in s:to_a(plug.on) 225 | if cmd =~? '^.\+' 226 | if empty(mapcheck(cmd)) && empty(mapcheck(cmd, 'i')) 227 | call s:assoc(lod.map, cmd, name) 228 | endif 229 | call add(s:triggers[name].map, cmd) 230 | elseif cmd =~# '^[A-Z]' 231 | let cmd = substitute(cmd, '!*$', '', '') 232 | if exists(':'.cmd) != 2 233 | call s:assoc(lod.cmd, cmd, name) 234 | endif 235 | call add(s:triggers[name].cmd, cmd) 236 | else 237 | call s:err('Invalid `on` option: '.cmd. 238 | \ '. Should start with an uppercase letter or ``.') 239 | endif 240 | endfor 241 | endif 242 | 243 | if has_key(plug, 'for') 244 | let types = s:to_a(plug.for) 245 | if !empty(types) 246 | augroup filetypedetect 247 | call s:source(s:rtp(plug), 'ftdetect/**/*.vim', 'after/ftdetect/**/*.vim') 248 | augroup END 249 | endif 250 | for type in types 251 | call s:assoc(lod.ft, type, name) 252 | endfor 253 | endif 254 | endfor 255 | 256 | for [cmd, names] in items(lod.cmd) 257 | execute printf( 258 | \ 'command! -nargs=* -range -bang -complete=file %s call s:lod_cmd(%s, "", , , , %s)', 259 | \ cmd, string(cmd), string(names)) 260 | endfor 261 | 262 | for [map, names] in items(lod.map) 263 | for [mode, map_prefix, key_prefix] in 264 | \ [['i', '', ''], ['n', '', ''], ['v', '', 'gv'], ['o', '', '']] 265 | execute printf( 266 | \ '%snoremap %s %s:call lod_map(%s, %s, %s, "%s")', 267 | \ mode, map, map_prefix, string(map), string(names), mode != 'i', key_prefix) 268 | endfor 269 | endfor 270 | 271 | for [ft, names] in items(lod.ft) 272 | augroup PlugLOD 273 | execute printf('autocmd FileType %s call lod_ft(%s, %s)', 274 | \ ft, string(ft), string(names)) 275 | augroup END 276 | endfor 277 | 278 | call s:reorg_rtp() 279 | filetype plugin indent on 280 | if has('vim_starting') 281 | if has('syntax') && !exists('g:syntax_on') 282 | syntax enable 283 | end 284 | else 285 | call s:reload_plugins() 286 | endif 287 | endfunction 288 | 289 | function! s:loaded_names() 290 | return filter(copy(g:plugs_order), 'get(s:loaded, v:val, 0)') 291 | endfunction 292 | 293 | function! s:load_plugin(spec) 294 | call s:source(s:rtp(a:spec), 'plugin/**/*.vim', 'after/plugin/**/*.vim') 295 | endfunction 296 | 297 | function! s:reload_plugins() 298 | for name in s:loaded_names() 299 | call s:load_plugin(g:plugs[name]) 300 | endfor 301 | endfunction 302 | 303 | function! s:trim(str) 304 | return substitute(a:str, '[\/]\+$', '', '') 305 | endfunction 306 | 307 | function! s:version_requirement(val, min) 308 | for idx in range(0, len(a:min) - 1) 309 | let v = get(a:val, idx, 0) 310 | if v < a:min[idx] | return 0 311 | elseif v > a:min[idx] | return 1 312 | endif 313 | endfor 314 | return 1 315 | endfunction 316 | 317 | function! s:git_version_requirement(...) 318 | if !exists('s:git_version') 319 | let s:git_version = map(split(split(s:system('git --version'))[2], '\.'), 'str2nr(v:val)') 320 | endif 321 | return s:version_requirement(s:git_version, a:000) 322 | endfunction 323 | 324 | function! s:progress_opt(base) 325 | return a:base && !s:is_win && 326 | \ s:git_version_requirement(1, 7, 1) ? '--progress' : '' 327 | endfunction 328 | 329 | if s:is_win 330 | function! s:rtp(spec) 331 | return s:path(a:spec.dir . get(a:spec, 'rtp', '')) 332 | endfunction 333 | 334 | function! s:path(path) 335 | return s:trim(substitute(a:path, '/', '\', 'g')) 336 | endfunction 337 | 338 | function! s:dirpath(path) 339 | return s:path(a:path) . '\' 340 | endfunction 341 | 342 | function! s:is_local_plug(repo) 343 | return a:repo =~? '^[a-z]:\|^[%~]' 344 | endfunction 345 | else 346 | function! s:rtp(spec) 347 | return s:dirpath(a:spec.dir . get(a:spec, 'rtp', '')) 348 | endfunction 349 | 350 | function! s:path(path) 351 | return s:trim(a:path) 352 | endfunction 353 | 354 | function! s:dirpath(path) 355 | return substitute(a:path, '[/\\]*$', '/', '') 356 | endfunction 357 | 358 | function! s:is_local_plug(repo) 359 | return a:repo[0] =~ '[/$~]' 360 | endfunction 361 | endif 362 | 363 | function! s:err(msg) 364 | echohl ErrorMsg 365 | echom '[vim-plug] '.a:msg 366 | echohl None 367 | endfunction 368 | 369 | function! s:warn(cmd, msg) 370 | echohl WarningMsg 371 | execute a:cmd 'a:msg' 372 | echohl None 373 | endfunction 374 | 375 | function! s:esc(path) 376 | return escape(a:path, ' ') 377 | endfunction 378 | 379 | function! s:escrtp(path) 380 | return escape(a:path, ' ,') 381 | endfunction 382 | 383 | function! s:remove_rtp() 384 | for name in s:loaded_names() 385 | let rtp = s:rtp(g:plugs[name]) 386 | execute 'set rtp-='.s:escrtp(rtp) 387 | let after = globpath(rtp, 'after') 388 | if isdirectory(after) 389 | execute 'set rtp-='.s:escrtp(after) 390 | endif 391 | endfor 392 | endfunction 393 | 394 | function! s:reorg_rtp() 395 | if !empty(s:first_rtp) 396 | execute 'set rtp-='.s:first_rtp 397 | execute 'set rtp-='.s:last_rtp 398 | endif 399 | 400 | " &rtp is modified from outside 401 | if exists('s:prtp') && s:prtp !=# &rtp 402 | call s:remove_rtp() 403 | unlet! s:middle 404 | endif 405 | 406 | let s:middle = get(s:, 'middle', &rtp) 407 | let rtps = map(s:loaded_names(), 's:rtp(g:plugs[v:val])') 408 | let afters = filter(map(copy(rtps), 'globpath(v:val, "after")'), '!empty(v:val)') 409 | let rtp = join(map(rtps, 'escape(v:val, ",")'), ',') 410 | \ . ','.s:middle.',' 411 | \ . join(map(afters, 'escape(v:val, ",")'), ',') 412 | let &rtp = substitute(substitute(rtp, ',,*', ',', 'g'), '^,\|,$', '', 'g') 413 | let s:prtp = &rtp 414 | 415 | if !empty(s:first_rtp) 416 | execute 'set rtp^='.s:first_rtp 417 | execute 'set rtp+='.s:last_rtp 418 | endif 419 | endfunction 420 | 421 | function! s:doautocmd(...) 422 | if exists('#'.join(a:000, '#')) 423 | execute 'doautocmd' ((v:version > 703 || has('patch442')) ? '' : '') join(a:000) 424 | endif 425 | endfunction 426 | 427 | function! s:dobufread(names) 428 | for name in a:names 429 | let path = s:rtp(g:plugs[name]).'/**' 430 | for dir in ['ftdetect', 'ftplugin'] 431 | if len(finddir(dir, path)) 432 | if exists('#BufRead') 433 | doautocmd BufRead 434 | endif 435 | return 436 | endif 437 | endfor 438 | endfor 439 | endfunction 440 | 441 | function! plug#load(...) 442 | if a:0 == 0 443 | return s:err('Argument missing: plugin name(s) required') 444 | endif 445 | if !exists('g:plugs') 446 | return s:err('plug#begin was not called') 447 | endif 448 | let names = a:0 == 1 && type(a:1) == s:TYPE.list ? a:1 : a:000 449 | let unknowns = filter(copy(names), '!has_key(g:plugs, v:val)') 450 | if !empty(unknowns) 451 | let s = len(unknowns) > 1 ? 's' : '' 452 | return s:err(printf('Unknown plugin%s: %s', s, join(unknowns, ', '))) 453 | end 454 | let unloaded = filter(copy(names), '!get(s:loaded, v:val, 0)') 455 | if !empty(unloaded) 456 | for name in unloaded 457 | call s:lod([name], ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin']) 458 | endfor 459 | call s:dobufread(unloaded) 460 | return 1 461 | end 462 | return 0 463 | endfunction 464 | 465 | function! s:remove_triggers(name) 466 | if !has_key(s:triggers, a:name) 467 | return 468 | endif 469 | for cmd in s:triggers[a:name].cmd 470 | execute 'silent! delc' cmd 471 | endfor 472 | for map in s:triggers[a:name].map 473 | execute 'silent! unmap' map 474 | execute 'silent! iunmap' map 475 | endfor 476 | call remove(s:triggers, a:name) 477 | endfunction 478 | 479 | function! s:lod(names, types, ...) 480 | for name in a:names 481 | call s:remove_triggers(name) 482 | let s:loaded[name] = 1 483 | endfor 484 | call s:reorg_rtp() 485 | 486 | for name in a:names 487 | let rtp = s:rtp(g:plugs[name]) 488 | for dir in a:types 489 | call s:source(rtp, dir.'/**/*.vim') 490 | endfor 491 | if a:0 492 | if !s:source(rtp, a:1) && !empty(s:glob(rtp, a:2)) 493 | execute 'runtime' a:1 494 | endif 495 | call s:source(rtp, a:2) 496 | endif 497 | call s:doautocmd('User', name) 498 | endfor 499 | endfunction 500 | 501 | function! s:lod_ft(pat, names) 502 | let syn = 'syntax/'.a:pat.'.vim' 503 | call s:lod(a:names, ['plugin', 'after/plugin'], syn, 'after/'.syn) 504 | execute 'autocmd! PlugLOD FileType' a:pat 505 | call s:doautocmd('filetypeplugin', 'FileType') 506 | call s:doautocmd('filetypeindent', 'FileType') 507 | endfunction 508 | 509 | function! s:lod_cmd(cmd, bang, l1, l2, args, names) 510 | call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin']) 511 | call s:dobufread(a:names) 512 | execute printf('%s%s%s %s', (a:l1 == a:l2 ? '' : (a:l1.','.a:l2)), a:cmd, a:bang, a:args) 513 | endfunction 514 | 515 | function! s:lod_map(map, names, with_prefix, prefix) 516 | call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin']) 517 | call s:dobufread(a:names) 518 | let extra = '' 519 | while 1 520 | let c = getchar(0) 521 | if c == 0 522 | break 523 | endif 524 | let extra .= nr2char(c) 525 | endwhile 526 | 527 | if a:with_prefix 528 | let prefix = v:count ? v:count : '' 529 | let prefix .= '"'.v:register.a:prefix 530 | if mode(1) == 'no' 531 | if v:operator == 'c' 532 | let prefix = "\" . prefix 533 | endif 534 | let prefix .= v:operator 535 | endif 536 | call feedkeys(prefix, 'n') 537 | endif 538 | call feedkeys(substitute(a:map, '^', "\", '') . extra) 539 | endfunction 540 | 541 | function! plug#(repo, ...) 542 | if a:0 > 1 543 | return s:err('Invalid number of arguments (1..2)') 544 | endif 545 | 546 | try 547 | let repo = s:trim(a:repo) 548 | let opts = a:0 == 1 ? s:parse_options(a:1) : s:base_spec 549 | let name = get(opts, 'as', fnamemodify(repo, ':t:s?\.git$??')) 550 | let spec = extend(s:infer_properties(name, repo), opts) 551 | if !has_key(g:plugs, name) 552 | call add(g:plugs_order, name) 553 | endif 554 | let g:plugs[name] = spec 555 | let s:loaded[name] = get(s:loaded, name, 0) 556 | catch 557 | return s:err(v:exception) 558 | endtry 559 | endfunction 560 | 561 | function! s:parse_options(arg) 562 | let opts = copy(s:base_spec) 563 | let type = type(a:arg) 564 | if type == s:TYPE.string 565 | let opts.tag = a:arg 566 | elseif type == s:TYPE.dict 567 | call extend(opts, a:arg) 568 | if has_key(opts, 'dir') 569 | let opts.dir = s:dirpath(expand(opts.dir)) 570 | endif 571 | else 572 | throw 'Invalid argument type (expected: string or dictionary)' 573 | endif 574 | return opts 575 | endfunction 576 | 577 | function! s:infer_properties(name, repo) 578 | let repo = a:repo 579 | if s:is_local_plug(repo) 580 | return { 'dir': s:dirpath(expand(repo)) } 581 | else 582 | if repo =~ ':' 583 | let uri = repo 584 | else 585 | if repo !~ '/' 586 | throw printf('Invalid argument: %s (implicit `vim-scripts'' expansion is deprecated)', repo) 587 | endif 588 | let fmt = get(g:, 'plug_url_format', 'https://git::@github.com/%s.git') 589 | let uri = printf(fmt, repo) 590 | endif 591 | return { 'dir': s:dirpath(g:plug_home.'/'.a:name), 'uri': uri } 592 | endif 593 | endfunction 594 | 595 | function! s:install(force, names) 596 | call s:update_impl(0, a:force, a:names) 597 | endfunction 598 | 599 | function! s:update(force, names) 600 | call s:update_impl(1, a:force, a:names) 601 | endfunction 602 | 603 | function! plug#helptags() 604 | if !exists('g:plugs') 605 | return s:err('plug#begin was not called') 606 | endif 607 | for spec in values(g:plugs) 608 | let docd = join([s:rtp(spec), 'doc'], '/') 609 | if isdirectory(docd) 610 | silent! execute 'helptags' s:esc(docd) 611 | endif 612 | endfor 613 | return 1 614 | endfunction 615 | 616 | function! s:syntax() 617 | syntax clear 618 | syntax region plug1 start=/\%1l/ end=/\%2l/ contains=plugNumber 619 | syntax region plug2 start=/\%2l/ end=/\%3l/ contains=plugBracket,plugX 620 | syn match plugNumber /[0-9]\+[0-9.]*/ contained 621 | syn match plugBracket /[[\]]/ contained 622 | syn match plugX /x/ contained 623 | syn match plugDash /^-/ 624 | syn match plugPlus /^+/ 625 | syn match plugStar /^*/ 626 | syn match plugMessage /\(^- \)\@<=.*/ 627 | syn match plugName /\(^- \)\@<=[^ ]*:/ 628 | syn match plugSha /\%(: \)\@<=[0-9a-f]\{4,}$/ 629 | syn match plugTag /(tag: [^)]\+)/ 630 | syn match plugInstall /\(^+ \)\@<=[^:]*/ 631 | syn match plugUpdate /\(^* \)\@<=[^:]*/ 632 | syn match plugCommit /^ \X*[0-9a-f]\{7,9} .*/ contains=plugRelDate,plugEdge,plugTag 633 | syn match plugEdge /^ \X\+$/ 634 | syn match plugEdge /^ \X*/ contained nextgroup=plugSha 635 | syn match plugSha /[0-9a-f]\{7,9}/ contained 636 | syn match plugRelDate /([^)]*)$/ contained 637 | syn match plugNotLoaded /(not loaded)$/ 638 | syn match plugError /^x.*/ 639 | syn region plugDeleted start=/^\~ .*/ end=/^\ze\S/ 640 | syn match plugH2 /^.*:\n-\+$/ 641 | syn keyword Function PlugInstall PlugStatus PlugUpdate PlugClean 642 | hi def link plug1 Title 643 | hi def link plug2 Repeat 644 | hi def link plugH2 Type 645 | hi def link plugX Exception 646 | hi def link plugBracket Structure 647 | hi def link plugNumber Number 648 | 649 | hi def link plugDash Special 650 | hi def link plugPlus Constant 651 | hi def link plugStar Boolean 652 | 653 | hi def link plugMessage Function 654 | hi def link plugName Label 655 | hi def link plugInstall Function 656 | hi def link plugUpdate Type 657 | 658 | hi def link plugError Error 659 | hi def link plugDeleted Ignore 660 | hi def link plugRelDate Comment 661 | hi def link plugEdge PreProc 662 | hi def link plugSha Identifier 663 | hi def link plugTag Constant 664 | 665 | hi def link plugNotLoaded Comment 666 | endfunction 667 | 668 | function! s:lpad(str, len) 669 | return a:str . repeat(' ', a:len - len(a:str)) 670 | endfunction 671 | 672 | function! s:lines(msg) 673 | return split(a:msg, "[\r\n]") 674 | endfunction 675 | 676 | function! s:lastline(msg) 677 | return get(s:lines(a:msg), -1, '') 678 | endfunction 679 | 680 | function! s:new_window() 681 | execute get(g:, 'plug_window', 'vertical topleft new') 682 | endfunction 683 | 684 | function! s:plug_window_exists() 685 | let buflist = tabpagebuflist(s:plug_tab) 686 | return !empty(buflist) && index(buflist, s:plug_buf) >= 0 687 | endfunction 688 | 689 | function! s:switch_in() 690 | if !s:plug_window_exists() 691 | return 0 692 | endif 693 | 694 | if winbufnr(0) != s:plug_buf 695 | let s:pos = [tabpagenr(), winnr(), winsaveview()] 696 | execute 'normal!' s:plug_tab.'gt' 697 | let winnr = bufwinnr(s:plug_buf) 698 | execute winnr.'wincmd w' 699 | call add(s:pos, winsaveview()) 700 | else 701 | let s:pos = [winsaveview()] 702 | endif 703 | 704 | setlocal modifiable 705 | return 1 706 | endfunction 707 | 708 | function! s:switch_out(...) 709 | call winrestview(s:pos[-1]) 710 | setlocal nomodifiable 711 | if a:0 > 0 712 | execute a:1 713 | endif 714 | 715 | if len(s:pos) > 1 716 | execute 'normal!' s:pos[0].'gt' 717 | execute s:pos[1] 'wincmd w' 718 | call winrestview(s:pos[2]) 719 | endif 720 | endfunction 721 | 722 | function! s:finish_bindings() 723 | nnoremap R :call retry() 724 | nnoremap D :PlugDiff 725 | nnoremap S :PlugStatus 726 | nnoremap U :call status_update() 727 | xnoremap U :call status_update() 728 | nnoremap ]] :silent! call section('') 729 | nnoremap [[ :silent! call section('b') 730 | endfunction 731 | 732 | function! s:prepare(...) 733 | if empty(getcwd()) 734 | throw 'Invalid current working directory. Cannot proceed.' 735 | endif 736 | 737 | for evar in ['$GIT_DIR', '$GIT_WORK_TREE'] 738 | if exists(evar) 739 | throw evar.' detected. Cannot proceed.' 740 | endif 741 | endfor 742 | 743 | call s:job_abort() 744 | if s:switch_in() 745 | if b:plug_preview == 1 746 | pc 747 | endif 748 | enew 749 | else 750 | call s:new_window() 751 | endif 752 | 753 | nnoremap q :if b:plug_preview==1pcendifbd 754 | if a:0 == 0 755 | call s:finish_bindings() 756 | endif 757 | let b:plug_preview = -1 758 | let s:plug_tab = tabpagenr() 759 | let s:plug_buf = winbufnr(0) 760 | call s:assign_name() 761 | 762 | for k in ['', 'L', 'o', 'X', 'd', 'dd'] 763 | execute 'silent! unmap ' k 764 | endfor 765 | setlocal buftype=nofile bufhidden=wipe nobuflisted nolist noswapfile nowrap cursorline modifiable nospell 766 | if exists('+colorcolumn') 767 | setlocal colorcolumn= 768 | endif 769 | setf vim-plug 770 | if exists('g:syntax_on') 771 | call s:syntax() 772 | endif 773 | endfunction 774 | 775 | function! s:assign_name() 776 | " Assign buffer name 777 | let prefix = '[Plugins]' 778 | let name = prefix 779 | let idx = 2 780 | while bufexists(name) 781 | let name = printf('%s (%s)', prefix, idx) 782 | let idx = idx + 1 783 | endwhile 784 | silent! execute 'f' fnameescape(name) 785 | endfunction 786 | 787 | function! s:chsh(swap) 788 | let prev = [&shell, &shellcmdflag, &shellredir] 789 | if s:is_win 790 | set shell=cmd.exe shellcmdflag=/c shellredir=>%s\ 2>&1 791 | elseif a:swap 792 | set shell=sh shellredir=>%s\ 2>&1 793 | endif 794 | return prev 795 | endfunction 796 | 797 | function! s:bang(cmd, ...) 798 | try 799 | let [sh, shellcmdflag, shrd] = s:chsh(a:0) 800 | " FIXME: Escaping is incomplete. We could use shellescape with eval, 801 | " but it won't work on Windows. 802 | let cmd = a:0 ? s:with_cd(a:cmd, a:1) : a:cmd 803 | if s:is_win 804 | let batchfile = tempname().'.bat' 805 | call writefile(["@echo off\r", cmd . "\r"], batchfile) 806 | let cmd = batchfile 807 | endif 808 | let g:_plug_bang = (s:is_win && has('gui_running') ? 'silent ' : '').'!'.escape(cmd, '#!%') 809 | execute "normal! :execute g:_plug_bang\\" 810 | finally 811 | unlet g:_plug_bang 812 | let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd] 813 | if s:is_win 814 | call delete(batchfile) 815 | endif 816 | endtry 817 | return v:shell_error ? 'Exit status: ' . v:shell_error : '' 818 | endfunction 819 | 820 | function! s:regress_bar() 821 | let bar = substitute(getline(2)[1:-2], '.*\zs=', 'x', '') 822 | call s:progress_bar(2, bar, len(bar)) 823 | endfunction 824 | 825 | function! s:is_updated(dir) 826 | return !empty(s:system_chomp('git log --pretty=format:"%h" "HEAD...HEAD@{1}"', a:dir)) 827 | endfunction 828 | 829 | function! s:do(pull, force, todo) 830 | for [name, spec] in items(a:todo) 831 | if !isdirectory(spec.dir) 832 | continue 833 | endif 834 | let installed = has_key(s:update.new, name) 835 | let updated = installed ? 0 : 836 | \ (a:pull && index(s:update.errors, name) < 0 && s:is_updated(spec.dir)) 837 | if a:force || installed || updated 838 | execute 'cd' s:esc(spec.dir) 839 | call append(3, '- Post-update hook for '. name .' ... ') 840 | let error = '' 841 | let type = type(spec.do) 842 | if type == s:TYPE.string 843 | if spec.do[0] == ':' 844 | if !get(s:loaded, name, 0) 845 | let s:loaded[name] = 1 846 | call s:reorg_rtp() 847 | endif 848 | call s:load_plugin(spec) 849 | try 850 | execute spec.do[1:] 851 | catch 852 | let error = v:exception 853 | endtry 854 | if !s:plug_window_exists() 855 | cd - 856 | throw 'Warning: vim-plug was terminated by the post-update hook of '.name 857 | endif 858 | else 859 | let error = s:bang(spec.do) 860 | endif 861 | elseif type == s:TYPE.funcref 862 | try 863 | let status = installed ? 'installed' : (updated ? 'updated' : 'unchanged') 864 | call spec.do({ 'name': name, 'status': status, 'force': a:force }) 865 | catch 866 | let error = v:exception 867 | endtry 868 | else 869 | let error = 'Invalid hook type' 870 | endif 871 | call s:switch_in() 872 | call setline(4, empty(error) ? (getline(4) . 'OK') 873 | \ : ('x' . getline(4)[1:] . error)) 874 | if !empty(error) 875 | call add(s:update.errors, name) 876 | call s:regress_bar() 877 | endif 878 | cd - 879 | endif 880 | endfor 881 | endfunction 882 | 883 | function! s:hash_match(a, b) 884 | return stridx(a:a, a:b) == 0 || stridx(a:b, a:a) == 0 885 | endfunction 886 | 887 | function! s:checkout(spec) 888 | let sha = a:spec.commit 889 | let output = s:system('git rev-parse HEAD', a:spec.dir) 890 | if !v:shell_error && !s:hash_match(sha, s:lines(output)[0]) 891 | let output = s:system( 892 | \ 'git fetch --depth 999999 && git checkout '.s:esc(sha).' --', a:spec.dir) 893 | endif 894 | return output 895 | endfunction 896 | 897 | function! s:finish(pull) 898 | let new_frozen = len(filter(keys(s:update.new), 'g:plugs[v:val].frozen')) 899 | if new_frozen 900 | let s = new_frozen > 1 ? 's' : '' 901 | call append(3, printf('- Installed %d frozen plugin%s', new_frozen, s)) 902 | endif 903 | call append(3, '- Finishing ... ') | 4 904 | redraw 905 | call plug#helptags() 906 | call plug#end() 907 | call setline(4, getline(4) . 'Done!') 908 | redraw 909 | let msgs = [] 910 | if !empty(s:update.errors) 911 | call add(msgs, "Press 'R' to retry.") 912 | endif 913 | if a:pull && len(s:update.new) < len(filter(getline(5, '$'), 914 | \ "v:val =~ '^- ' && v:val !~# 'Already up.to.date'")) 915 | call add(msgs, "Press 'D' to see the updated changes.") 916 | endif 917 | echo join(msgs, ' ') 918 | call s:finish_bindings() 919 | endfunction 920 | 921 | function! s:retry() 922 | if empty(s:update.errors) 923 | return 924 | endif 925 | echo 926 | call s:update_impl(s:update.pull, s:update.force, 927 | \ extend(copy(s:update.errors), [s:update.threads])) 928 | endfunction 929 | 930 | function! s:is_managed(name) 931 | return has_key(g:plugs[a:name], 'uri') 932 | endfunction 933 | 934 | function! s:names(...) 935 | return sort(filter(keys(g:plugs), 'stridx(v:val, a:1) == 0 && s:is_managed(v:val)')) 936 | endfunction 937 | 938 | function! s:check_ruby() 939 | silent! ruby require 'thread'; VIM::command("let g:plug_ruby = '#{RUBY_VERSION}'") 940 | if !exists('g:plug_ruby') 941 | redraw! 942 | return s:warn('echom', 'Warning: Ruby interface is broken') 943 | endif 944 | let ruby_version = split(g:plug_ruby, '\.') 945 | unlet g:plug_ruby 946 | return s:version_requirement(ruby_version, [1, 8, 7]) 947 | endfunction 948 | 949 | function! s:update_impl(pull, force, args) abort 950 | let sync = index(a:args, '--sync') >= 0 || has('vim_starting') 951 | let args = filter(copy(a:args), 'v:val != "--sync"') 952 | let threads = (len(args) > 0 && args[-1] =~ '^[1-9][0-9]*$') ? 953 | \ remove(args, -1) : get(g:, 'plug_threads', 16) 954 | 955 | let managed = filter(copy(g:plugs), 's:is_managed(v:key)') 956 | let todo = empty(args) ? filter(managed, '!v:val.frozen || !isdirectory(v:val.dir)') : 957 | \ filter(managed, 'index(args, v:key) >= 0') 958 | 959 | if empty(todo) 960 | return s:warn('echo', 'No plugin to '. (a:pull ? 'update' : 'install')) 961 | endif 962 | 963 | if !s:is_win && s:git_version_requirement(2, 3) 964 | let s:git_terminal_prompt = exists('$GIT_TERMINAL_PROMPT') ? $GIT_TERMINAL_PROMPT : '' 965 | let $GIT_TERMINAL_PROMPT = 0 966 | for plug in values(todo) 967 | let plug.uri = substitute(plug.uri, 968 | \ '^https://git::@github\.com', 'https://github.com', '') 969 | endfor 970 | endif 971 | 972 | if !isdirectory(g:plug_home) 973 | try 974 | call mkdir(g:plug_home, 'p') 975 | catch 976 | return s:err(printf('Invalid plug directory: %s. '. 977 | \ 'Try to call plug#begin with a valid directory', g:plug_home)) 978 | endtry 979 | endif 980 | 981 | if has('nvim') && !exists('*jobwait') && threads > 1 982 | call s:warn('echom', '[vim-plug] Update Neovim for parallel installer') 983 | endif 984 | 985 | let use_job = s:nvim || s:vim8 986 | let python = (has('python') || has('python3')) && !use_job 987 | let ruby = has('ruby') && !use_job && (v:version >= 703 || v:version == 702 && has('patch374')) && !(s:is_win && has('gui_running')) && threads > 1 && s:check_ruby() 988 | 989 | let s:update = { 990 | \ 'start': reltime(), 991 | \ 'all': todo, 992 | \ 'todo': copy(todo), 993 | \ 'errors': [], 994 | \ 'pull': a:pull, 995 | \ 'force': a:force, 996 | \ 'new': {}, 997 | \ 'threads': (python || ruby || use_job) ? min([len(todo), threads]) : 1, 998 | \ 'bar': '', 999 | \ 'fin': 0 1000 | \ } 1001 | 1002 | call s:prepare(1) 1003 | call append(0, ['', '']) 1004 | normal! 2G 1005 | silent! redraw 1006 | 1007 | let s:clone_opt = get(g:, 'plug_shallow', 1) ? 1008 | \ '--depth 1' . (s:git_version_requirement(1, 7, 10) ? ' --no-single-branch' : '') : '' 1009 | 1010 | if has('win32unix') 1011 | let s:clone_opt .= ' -c core.eol=lf -c core.autocrlf=input' 1012 | endif 1013 | 1014 | let s:submodule_opt = s:git_version_requirement(2, 8) ? ' --jobs='.threads : '' 1015 | 1016 | " Python version requirement (>= 2.7) 1017 | if python && !has('python3') && !ruby && !use_job && s:update.threads > 1 1018 | redir => pyv 1019 | silent python import platform; print platform.python_version() 1020 | redir END 1021 | let python = s:version_requirement( 1022 | \ map(split(split(pyv)[0], '\.'), 'str2nr(v:val)'), [2, 6]) 1023 | endif 1024 | 1025 | if (python || ruby) && s:update.threads > 1 1026 | try 1027 | let imd = &imd 1028 | if s:mac_gui 1029 | set noimd 1030 | endif 1031 | if ruby 1032 | call s:update_ruby() 1033 | else 1034 | call s:update_python() 1035 | endif 1036 | catch 1037 | let lines = getline(4, '$') 1038 | let printed = {} 1039 | silent! 4,$d _ 1040 | for line in lines 1041 | let name = s:extract_name(line, '.', '') 1042 | if empty(name) || !has_key(printed, name) 1043 | call append('$', line) 1044 | if !empty(name) 1045 | let printed[name] = 1 1046 | if line[0] == 'x' && index(s:update.errors, name) < 0 1047 | call add(s:update.errors, name) 1048 | end 1049 | endif 1050 | endif 1051 | endfor 1052 | finally 1053 | let &imd = imd 1054 | call s:update_finish() 1055 | endtry 1056 | else 1057 | call s:update_vim() 1058 | while use_job && sync 1059 | sleep 100m 1060 | if s:update.fin 1061 | break 1062 | endif 1063 | endwhile 1064 | endif 1065 | endfunction 1066 | 1067 | function! s:log4(name, msg) 1068 | call setline(4, printf('- %s (%s)', a:msg, a:name)) 1069 | redraw 1070 | endfunction 1071 | 1072 | function! s:update_finish() 1073 | if exists('s:git_terminal_prompt') 1074 | let $GIT_TERMINAL_PROMPT = s:git_terminal_prompt 1075 | endif 1076 | if s:switch_in() 1077 | call append(3, '- Updating ...') | 4 1078 | for [name, spec] in items(filter(copy(s:update.all), 'index(s:update.errors, v:key) < 0 && (s:update.force || s:update.pull || has_key(s:update.new, v:key))')) 1079 | let [pos, _] = s:logpos(name) 1080 | if !pos 1081 | continue 1082 | endif 1083 | if has_key(spec, 'commit') 1084 | call s:log4(name, 'Checking out '.spec.commit) 1085 | let out = s:checkout(spec) 1086 | elseif has_key(spec, 'tag') 1087 | let tag = spec.tag 1088 | if tag =~ '\*' 1089 | let tags = s:lines(s:system('git tag --list '.s:shellesc(tag).' --sort -version:refname 2>&1', spec.dir)) 1090 | if !v:shell_error && !empty(tags) 1091 | let tag = tags[0] 1092 | call s:log4(name, printf('Latest tag for %s -> %s', spec.tag, tag)) 1093 | call append(3, '') 1094 | endif 1095 | endif 1096 | call s:log4(name, 'Checking out '.tag) 1097 | let out = s:system('git checkout -q '.s:esc(tag).' -- 2>&1', spec.dir) 1098 | else 1099 | let branch = s:esc(get(spec, 'branch', 'master')) 1100 | call s:log4(name, 'Merging origin/'.branch) 1101 | let out = s:system('git checkout -q '.branch.' -- 2>&1' 1102 | \. (has_key(s:update.new, name) ? '' : ('&& git merge --ff-only origin/'.branch.' 2>&1')), spec.dir) 1103 | endif 1104 | if !v:shell_error && filereadable(spec.dir.'/.gitmodules') && 1105 | \ (s:update.force || has_key(s:update.new, name) || s:is_updated(spec.dir)) 1106 | call s:log4(name, 'Updating submodules. This may take a while.') 1107 | let out .= s:bang('git submodule update --init --recursive'.s:submodule_opt.' 2>&1', spec.dir) 1108 | endif 1109 | let msg = s:format_message(v:shell_error ? 'x': '-', name, out) 1110 | if v:shell_error 1111 | call add(s:update.errors, name) 1112 | call s:regress_bar() 1113 | silent execute pos 'd _' 1114 | call append(4, msg) | 4 1115 | elseif !empty(out) 1116 | call setline(pos, msg[0]) 1117 | endif 1118 | redraw 1119 | endfor 1120 | silent 4 d _ 1121 | try 1122 | call s:do(s:update.pull, s:update.force, filter(copy(s:update.all), 'index(s:update.errors, v:key) < 0 && has_key(v:val, "do")')) 1123 | catch 1124 | call s:warn('echom', v:exception) 1125 | call s:warn('echo', '') 1126 | return 1127 | endtry 1128 | call s:finish(s:update.pull) 1129 | call setline(1, 'Updated. Elapsed time: ' . split(reltimestr(reltime(s:update.start)))[0] . ' sec.') 1130 | call s:switch_out('normal! gg') 1131 | endif 1132 | endfunction 1133 | 1134 | function! s:job_abort() 1135 | if (!s:nvim && !s:vim8) || !exists('s:jobs') 1136 | return 1137 | endif 1138 | 1139 | for [name, j] in items(s:jobs) 1140 | if s:nvim 1141 | silent! call jobstop(j.jobid) 1142 | elseif s:vim8 1143 | silent! call job_stop(j.jobid) 1144 | endif 1145 | if j.new 1146 | call s:system('rm -rf ' . s:shellesc(g:plugs[name].dir)) 1147 | endif 1148 | endfor 1149 | let s:jobs = {} 1150 | endfunction 1151 | 1152 | function! s:last_non_empty_line(lines) 1153 | let len = len(a:lines) 1154 | for idx in range(len) 1155 | let line = a:lines[len-idx-1] 1156 | if !empty(line) 1157 | return line 1158 | endif 1159 | endfor 1160 | return '' 1161 | endfunction 1162 | 1163 | function! s:job_out_cb(self, data) abort 1164 | let self = a:self 1165 | let data = remove(self.lines, -1) . a:data 1166 | let lines = map(split(data, "\n", 1), 'split(v:val, "\r", 1)[-1]') 1167 | call extend(self.lines, lines) 1168 | " To reduce the number of buffer updates 1169 | let self.tick = get(self, 'tick', -1) + 1 1170 | if !self.running || self.tick % len(s:jobs) == 0 1171 | let bullet = self.running ? (self.new ? '+' : '*') : (self.error ? 'x' : '-') 1172 | let result = self.error ? join(self.lines, "\n") : s:last_non_empty_line(self.lines) 1173 | call s:log(bullet, self.name, result) 1174 | endif 1175 | endfunction 1176 | 1177 | function! s:job_exit_cb(self, data) abort 1178 | let a:self.running = 0 1179 | let a:self.error = a:data != 0 1180 | call s:reap(a:self.name) 1181 | call s:tick() 1182 | endfunction 1183 | 1184 | function! s:job_cb(fn, job, ch, data) 1185 | if !s:plug_window_exists() " plug window closed 1186 | return s:job_abort() 1187 | endif 1188 | call call(a:fn, [a:job, a:data]) 1189 | endfunction 1190 | 1191 | function! s:nvim_cb(job_id, data, event) dict abort 1192 | return a:event == 'stdout' ? 1193 | \ s:job_cb('s:job_out_cb', self, 0, join(a:data, "\n")) : 1194 | \ s:job_cb('s:job_exit_cb', self, 0, a:data) 1195 | endfunction 1196 | 1197 | function! s:spawn(name, cmd, opts) 1198 | let job = { 'name': a:name, 'running': 1, 'error': 0, 'lines': [''], 1199 | \ 'batchfile': (s:is_win && (s:nvim || s:vim8)) ? tempname().'.bat' : '', 1200 | \ 'new': get(a:opts, 'new', 0) } 1201 | let s:jobs[a:name] = job 1202 | let cmd = has_key(a:opts, 'dir') ? s:with_cd(a:cmd, a:opts.dir) : a:cmd 1203 | if !empty(job.batchfile) 1204 | call writefile(["@echo off\r", cmd . "\r"], job.batchfile) 1205 | let cmd = job.batchfile 1206 | endif 1207 | let argv = add(s:is_win ? ['cmd', '/c'] : ['sh', '-c'], cmd) 1208 | 1209 | if s:nvim 1210 | call extend(job, { 1211 | \ 'on_stdout': function('s:nvim_cb'), 1212 | \ 'on_exit': function('s:nvim_cb'), 1213 | \ }) 1214 | let jid = jobstart(argv, job) 1215 | if jid > 0 1216 | let job.jobid = jid 1217 | else 1218 | let job.running = 0 1219 | let job.error = 1 1220 | let job.lines = [jid < 0 ? argv[0].' is not executable' : 1221 | \ 'Invalid arguments (or job table is full)'] 1222 | endif 1223 | elseif s:vim8 1224 | let jid = job_start(s:is_win ? join(argv, ' ') : argv, { 1225 | \ 'out_cb': function('s:job_cb', ['s:job_out_cb', job]), 1226 | \ 'exit_cb': function('s:job_cb', ['s:job_exit_cb', job]), 1227 | \ 'out_mode': 'raw' 1228 | \}) 1229 | if job_status(jid) == 'run' 1230 | let job.jobid = jid 1231 | else 1232 | let job.running = 0 1233 | let job.error = 1 1234 | let job.lines = ['Failed to start job'] 1235 | endif 1236 | else 1237 | let job.lines = s:lines(call('s:system', [cmd])) 1238 | let job.error = v:shell_error != 0 1239 | let job.running = 0 1240 | endif 1241 | endfunction 1242 | 1243 | function! s:reap(name) 1244 | let job = s:jobs[a:name] 1245 | if job.error 1246 | call add(s:update.errors, a:name) 1247 | elseif get(job, 'new', 0) 1248 | let s:update.new[a:name] = 1 1249 | endif 1250 | let s:update.bar .= job.error ? 'x' : '=' 1251 | 1252 | let bullet = job.error ? 'x' : '-' 1253 | let result = job.error ? join(job.lines, "\n") : s:last_non_empty_line(job.lines) 1254 | call s:log(bullet, a:name, empty(result) ? 'OK' : result) 1255 | call s:bar() 1256 | 1257 | if has_key(job, 'batchfile') && !empty(job.batchfile) 1258 | call delete(job.batchfile) 1259 | endif 1260 | call remove(s:jobs, a:name) 1261 | endfunction 1262 | 1263 | function! s:bar() 1264 | if s:switch_in() 1265 | let total = len(s:update.all) 1266 | call setline(1, (s:update.pull ? 'Updating' : 'Installing'). 1267 | \ ' plugins ('.len(s:update.bar).'/'.total.')') 1268 | call s:progress_bar(2, s:update.bar, total) 1269 | call s:switch_out() 1270 | endif 1271 | endfunction 1272 | 1273 | function! s:logpos(name) 1274 | for i in range(4, line('$')) 1275 | if getline(i) =~# '^[-+x*] '.a:name.':' 1276 | for j in range(i + 1, line('$')) 1277 | if getline(j) !~ '^ ' 1278 | return [i, j - 1] 1279 | endif 1280 | endfor 1281 | return [i, i] 1282 | endif 1283 | endfor 1284 | return [0, 0] 1285 | endfunction 1286 | 1287 | function! s:log(bullet, name, lines) 1288 | if s:switch_in() 1289 | let [b, e] = s:logpos(a:name) 1290 | if b > 0 1291 | silent execute printf('%d,%d d _', b, e) 1292 | if b > winheight('.') 1293 | let b = 4 1294 | endif 1295 | else 1296 | let b = 4 1297 | endif 1298 | " FIXME For some reason, nomodifiable is set after :d in vim8 1299 | setlocal modifiable 1300 | call append(b - 1, s:format_message(a:bullet, a:name, a:lines)) 1301 | call s:switch_out() 1302 | endif 1303 | endfunction 1304 | 1305 | function! s:update_vim() 1306 | let s:jobs = {} 1307 | 1308 | call s:bar() 1309 | call s:tick() 1310 | endfunction 1311 | 1312 | function! s:tick() 1313 | let pull = s:update.pull 1314 | let prog = s:progress_opt(s:nvim || s:vim8) 1315 | while 1 " Without TCO, Vim stack is bound to explode 1316 | if empty(s:update.todo) 1317 | if empty(s:jobs) && !s:update.fin 1318 | call s:update_finish() 1319 | let s:update.fin = 1 1320 | endif 1321 | return 1322 | endif 1323 | 1324 | let name = keys(s:update.todo)[0] 1325 | let spec = remove(s:update.todo, name) 1326 | let new = !isdirectory(spec.dir) 1327 | 1328 | call s:log(new ? '+' : '*', name, pull ? 'Updating ...' : 'Installing ...') 1329 | redraw 1330 | 1331 | let has_tag = has_key(spec, 'tag') 1332 | if !new 1333 | let [error, _] = s:git_validate(spec, 0) 1334 | if empty(error) 1335 | if pull 1336 | let fetch_opt = (has_tag && !empty(globpath(spec.dir, '.git/shallow'))) ? '--depth 99999999' : '' 1337 | call s:spawn(name, printf('git fetch %s %s 2>&1', fetch_opt, prog), { 'dir': spec.dir }) 1338 | else 1339 | let s:jobs[name] = { 'running': 0, 'lines': ['Already installed'], 'error': 0 } 1340 | endif 1341 | else 1342 | let s:jobs[name] = { 'running': 0, 'lines': s:lines(error), 'error': 1 } 1343 | endif 1344 | else 1345 | call s:spawn(name, 1346 | \ printf('git clone %s %s %s %s 2>&1', 1347 | \ has_tag ? '' : s:clone_opt, 1348 | \ prog, 1349 | \ s:shellesc(spec.uri), 1350 | \ s:shellesc(s:trim(spec.dir))), { 'new': 1 }) 1351 | endif 1352 | 1353 | if !s:jobs[name].running 1354 | call s:reap(name) 1355 | endif 1356 | if len(s:jobs) >= s:update.threads 1357 | break 1358 | endif 1359 | endwhile 1360 | endfunction 1361 | 1362 | function! s:update_python() 1363 | let py_exe = has('python') ? 'python' : 'python3' 1364 | execute py_exe "<< EOF" 1365 | import datetime 1366 | import functools 1367 | import os 1368 | try: 1369 | import queue 1370 | except ImportError: 1371 | import Queue as queue 1372 | import random 1373 | import re 1374 | import shutil 1375 | import signal 1376 | import subprocess 1377 | import tempfile 1378 | import threading as thr 1379 | import time 1380 | import traceback 1381 | import vim 1382 | 1383 | G_NVIM = vim.eval("has('nvim')") == '1' 1384 | G_PULL = vim.eval('s:update.pull') == '1' 1385 | G_RETRIES = int(vim.eval('get(g:, "plug_retries", 2)')) + 1 1386 | G_TIMEOUT = int(vim.eval('get(g:, "plug_timeout", 60)')) 1387 | G_CLONE_OPT = vim.eval('s:clone_opt') 1388 | G_PROGRESS = vim.eval('s:progress_opt(1)') 1389 | G_LOG_PROB = 1.0 / int(vim.eval('s:update.threads')) 1390 | G_STOP = thr.Event() 1391 | G_IS_WIN = vim.eval('s:is_win') == '1' 1392 | 1393 | class PlugError(Exception): 1394 | def __init__(self, msg): 1395 | self.msg = msg 1396 | class CmdTimedOut(PlugError): 1397 | pass 1398 | class CmdFailed(PlugError): 1399 | pass 1400 | class InvalidURI(PlugError): 1401 | pass 1402 | class Action(object): 1403 | INSTALL, UPDATE, ERROR, DONE = ['+', '*', 'x', '-'] 1404 | 1405 | class Buffer(object): 1406 | def __init__(self, lock, num_plugs, is_pull): 1407 | self.bar = '' 1408 | self.event = 'Updating' if is_pull else 'Installing' 1409 | self.lock = lock 1410 | self.maxy = int(vim.eval('winheight(".")')) 1411 | self.num_plugs = num_plugs 1412 | 1413 | def __where(self, name): 1414 | """ Find first line with name in current buffer. Return line num. """ 1415 | found, lnum = False, 0 1416 | matcher = re.compile('^[-+x*] {0}:'.format(name)) 1417 | for line in vim.current.buffer: 1418 | if matcher.search(line) is not None: 1419 | found = True 1420 | break 1421 | lnum += 1 1422 | 1423 | if not found: 1424 | lnum = -1 1425 | return lnum 1426 | 1427 | def header(self): 1428 | curbuf = vim.current.buffer 1429 | curbuf[0] = self.event + ' plugins ({0}/{1})'.format(len(self.bar), self.num_plugs) 1430 | 1431 | num_spaces = self.num_plugs - len(self.bar) 1432 | curbuf[1] = '[{0}{1}]'.format(self.bar, num_spaces * ' ') 1433 | 1434 | with self.lock: 1435 | vim.command('normal! 2G') 1436 | vim.command('redraw') 1437 | 1438 | def write(self, action, name, lines): 1439 | first, rest = lines[0], lines[1:] 1440 | msg = ['{0} {1}{2}{3}'.format(action, name, ': ' if first else '', first)] 1441 | msg.extend([' ' + line for line in rest]) 1442 | 1443 | try: 1444 | if action == Action.ERROR: 1445 | self.bar += 'x' 1446 | vim.command("call add(s:update.errors, '{0}')".format(name)) 1447 | elif action == Action.DONE: 1448 | self.bar += '=' 1449 | 1450 | curbuf = vim.current.buffer 1451 | lnum = self.__where(name) 1452 | if lnum != -1: # Found matching line num 1453 | del curbuf[lnum] 1454 | if lnum > self.maxy and action in set([Action.INSTALL, Action.UPDATE]): 1455 | lnum = 3 1456 | else: 1457 | lnum = 3 1458 | curbuf.append(msg, lnum) 1459 | 1460 | self.header() 1461 | except vim.error: 1462 | pass 1463 | 1464 | class Command(object): 1465 | CD = 'cd /d' if G_IS_WIN else 'cd' 1466 | 1467 | def __init__(self, cmd, cmd_dir=None, timeout=60, cb=None, clean=None): 1468 | self.cmd = cmd 1469 | if cmd_dir: 1470 | self.cmd = '{0} {1} && {2}'.format(Command.CD, cmd_dir, self.cmd) 1471 | self.timeout = timeout 1472 | self.callback = cb if cb else (lambda msg: None) 1473 | self.clean = clean if clean else (lambda: None) 1474 | self.proc = None 1475 | 1476 | @property 1477 | def alive(self): 1478 | """ Returns true only if command still running. """ 1479 | return self.proc and self.proc.poll() is None 1480 | 1481 | def execute(self, ntries=3): 1482 | """ Execute the command with ntries if CmdTimedOut. 1483 | Returns the output of the command if no Exception. 1484 | """ 1485 | attempt, finished, limit = 0, False, self.timeout 1486 | 1487 | while not finished: 1488 | try: 1489 | attempt += 1 1490 | result = self.try_command() 1491 | finished = True 1492 | return result 1493 | except CmdTimedOut: 1494 | if attempt != ntries: 1495 | self.notify_retry() 1496 | self.timeout += limit 1497 | else: 1498 | raise 1499 | 1500 | def notify_retry(self): 1501 | """ Retry required for command, notify user. """ 1502 | for count in range(3, 0, -1): 1503 | if G_STOP.is_set(): 1504 | raise KeyboardInterrupt 1505 | msg = 'Timeout. Will retry in {0} second{1} ...'.format( 1506 | count, 's' if count != 1 else '') 1507 | self.callback([msg]) 1508 | time.sleep(1) 1509 | self.callback(['Retrying ...']) 1510 | 1511 | def try_command(self): 1512 | """ Execute a cmd & poll for callback. Returns list of output. 1513 | Raises CmdFailed -> return code for Popen isn't 0 1514 | Raises CmdTimedOut -> command exceeded timeout without new output 1515 | """ 1516 | first_line = True 1517 | 1518 | try: 1519 | tfile = tempfile.NamedTemporaryFile(mode='w+b') 1520 | preexec_fn = not G_IS_WIN and os.setsid or None 1521 | self.proc = subprocess.Popen(self.cmd, stdout=tfile, 1522 | stderr=subprocess.STDOUT, 1523 | stdin=subprocess.PIPE, shell=True, 1524 | preexec_fn=preexec_fn) 1525 | thrd = thr.Thread(target=(lambda proc: proc.wait()), args=(self.proc,)) 1526 | thrd.start() 1527 | 1528 | thread_not_started = True 1529 | while thread_not_started: 1530 | try: 1531 | thrd.join(0.1) 1532 | thread_not_started = False 1533 | except RuntimeError: 1534 | pass 1535 | 1536 | while self.alive: 1537 | if G_STOP.is_set(): 1538 | raise KeyboardInterrupt 1539 | 1540 | if first_line or random.random() < G_LOG_PROB: 1541 | first_line = False 1542 | line = '' if G_IS_WIN else nonblock_read(tfile.name) 1543 | if line: 1544 | self.callback([line]) 1545 | 1546 | time_diff = time.time() - os.path.getmtime(tfile.name) 1547 | if time_diff > self.timeout: 1548 | raise CmdTimedOut(['Timeout!']) 1549 | 1550 | thrd.join(0.5) 1551 | 1552 | tfile.seek(0) 1553 | result = [line.decode('utf-8', 'replace').rstrip() for line in tfile] 1554 | 1555 | if self.proc.returncode != 0: 1556 | raise CmdFailed([''] + result) 1557 | 1558 | return result 1559 | except: 1560 | self.terminate() 1561 | raise 1562 | 1563 | def terminate(self): 1564 | """ Terminate process and cleanup. """ 1565 | if self.alive: 1566 | if G_IS_WIN: 1567 | os.kill(self.proc.pid, signal.SIGINT) 1568 | else: 1569 | os.killpg(self.proc.pid, signal.SIGTERM) 1570 | self.clean() 1571 | 1572 | class Plugin(object): 1573 | def __init__(self, name, args, buf_q, lock): 1574 | self.name = name 1575 | self.args = args 1576 | self.buf_q = buf_q 1577 | self.lock = lock 1578 | self.tag = args.get('tag', 0) 1579 | 1580 | def manage(self): 1581 | try: 1582 | if os.path.exists(self.args['dir']): 1583 | self.update() 1584 | else: 1585 | self.install() 1586 | with self.lock: 1587 | thread_vim_command("let s:update.new['{0}'] = 1".format(self.name)) 1588 | except PlugError as exc: 1589 | self.write(Action.ERROR, self.name, exc.msg) 1590 | except KeyboardInterrupt: 1591 | G_STOP.set() 1592 | self.write(Action.ERROR, self.name, ['Interrupted!']) 1593 | except: 1594 | # Any exception except those above print stack trace 1595 | msg = 'Trace:\n{0}'.format(traceback.format_exc().rstrip()) 1596 | self.write(Action.ERROR, self.name, msg.split('\n')) 1597 | raise 1598 | 1599 | def install(self): 1600 | target = self.args['dir'] 1601 | if target[-1] == '\\': 1602 | target = target[0:-1] 1603 | 1604 | def clean(target): 1605 | def _clean(): 1606 | try: 1607 | shutil.rmtree(target) 1608 | except OSError: 1609 | pass 1610 | return _clean 1611 | 1612 | self.write(Action.INSTALL, self.name, ['Installing ...']) 1613 | callback = functools.partial(self.write, Action.INSTALL, self.name) 1614 | cmd = 'git clone {0} {1} {2} {3} 2>&1'.format( 1615 | '' if self.tag else G_CLONE_OPT, G_PROGRESS, self.args['uri'], 1616 | esc(target)) 1617 | com = Command(cmd, None, G_TIMEOUT, callback, clean(target)) 1618 | result = com.execute(G_RETRIES) 1619 | self.write(Action.DONE, self.name, result[-1:]) 1620 | 1621 | def repo_uri(self): 1622 | cmd = 'git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url' 1623 | command = Command(cmd, self.args['dir'], G_TIMEOUT,) 1624 | result = command.execute(G_RETRIES) 1625 | return result[-1] 1626 | 1627 | def update(self): 1628 | actual_uri = self.repo_uri() 1629 | expect_uri = self.args['uri'] 1630 | regex = re.compile(r'^(?:\w+://)?(?:[^@/]*@)?([^:/]*(?::[0-9]*)?)[:/](.*?)(?:\.git)?/?$') 1631 | ma = regex.match(actual_uri) 1632 | mb = regex.match(expect_uri) 1633 | if ma is None or mb is None or ma.groups() != mb.groups(): 1634 | msg = ['', 1635 | 'Invalid URI: {0}'.format(actual_uri), 1636 | 'Expected {0}'.format(expect_uri), 1637 | 'PlugClean required.'] 1638 | raise InvalidURI(msg) 1639 | 1640 | if G_PULL: 1641 | self.write(Action.UPDATE, self.name, ['Updating ...']) 1642 | callback = functools.partial(self.write, Action.UPDATE, self.name) 1643 | fetch_opt = '--depth 99999999' if self.tag and os.path.isfile(os.path.join(self.args['dir'], '.git/shallow')) else '' 1644 | cmd = 'git fetch {0} {1} 2>&1'.format(fetch_opt, G_PROGRESS) 1645 | com = Command(cmd, self.args['dir'], G_TIMEOUT, callback) 1646 | result = com.execute(G_RETRIES) 1647 | self.write(Action.DONE, self.name, result[-1:]) 1648 | else: 1649 | self.write(Action.DONE, self.name, ['Already installed']) 1650 | 1651 | def write(self, action, name, msg): 1652 | self.buf_q.put((action, name, msg)) 1653 | 1654 | class PlugThread(thr.Thread): 1655 | def __init__(self, tname, args): 1656 | super(PlugThread, self).__init__() 1657 | self.tname = tname 1658 | self.args = args 1659 | 1660 | def run(self): 1661 | thr.current_thread().name = self.tname 1662 | buf_q, work_q, lock = self.args 1663 | 1664 | try: 1665 | while not G_STOP.is_set(): 1666 | name, args = work_q.get_nowait() 1667 | plug = Plugin(name, args, buf_q, lock) 1668 | plug.manage() 1669 | work_q.task_done() 1670 | except queue.Empty: 1671 | pass 1672 | 1673 | class RefreshThread(thr.Thread): 1674 | def __init__(self, lock): 1675 | super(RefreshThread, self).__init__() 1676 | self.lock = lock 1677 | self.running = True 1678 | 1679 | def run(self): 1680 | while self.running: 1681 | with self.lock: 1682 | thread_vim_command('noautocmd normal! a') 1683 | time.sleep(0.33) 1684 | 1685 | def stop(self): 1686 | self.running = False 1687 | 1688 | if G_NVIM: 1689 | def thread_vim_command(cmd): 1690 | vim.session.threadsafe_call(lambda: vim.command(cmd)) 1691 | else: 1692 | def thread_vim_command(cmd): 1693 | vim.command(cmd) 1694 | 1695 | def esc(name): 1696 | return '"' + name.replace('"', '\"') + '"' 1697 | 1698 | def nonblock_read(fname): 1699 | """ Read a file with nonblock flag. Return the last line. """ 1700 | fread = os.open(fname, os.O_RDONLY | os.O_NONBLOCK) 1701 | buf = os.read(fread, 100000).decode('utf-8', 'replace') 1702 | os.close(fread) 1703 | 1704 | line = buf.rstrip('\r\n') 1705 | left = max(line.rfind('\r'), line.rfind('\n')) 1706 | if left != -1: 1707 | left += 1 1708 | line = line[left:] 1709 | 1710 | return line 1711 | 1712 | def main(): 1713 | thr.current_thread().name = 'main' 1714 | nthreads = int(vim.eval('s:update.threads')) 1715 | plugs = vim.eval('s:update.todo') 1716 | mac_gui = vim.eval('s:mac_gui') == '1' 1717 | 1718 | lock = thr.Lock() 1719 | buf = Buffer(lock, len(plugs), G_PULL) 1720 | buf_q, work_q = queue.Queue(), queue.Queue() 1721 | for work in plugs.items(): 1722 | work_q.put(work) 1723 | 1724 | start_cnt = thr.active_count() 1725 | for num in range(nthreads): 1726 | tname = 'PlugT-{0:02}'.format(num) 1727 | thread = PlugThread(tname, (buf_q, work_q, lock)) 1728 | thread.start() 1729 | if mac_gui: 1730 | rthread = RefreshThread(lock) 1731 | rthread.start() 1732 | 1733 | while not buf_q.empty() or thr.active_count() != start_cnt: 1734 | try: 1735 | action, name, msg = buf_q.get(True, 0.25) 1736 | buf.write(action, name, ['OK'] if not msg else msg) 1737 | buf_q.task_done() 1738 | except queue.Empty: 1739 | pass 1740 | except KeyboardInterrupt: 1741 | G_STOP.set() 1742 | 1743 | if mac_gui: 1744 | rthread.stop() 1745 | rthread.join() 1746 | 1747 | main() 1748 | EOF 1749 | endfunction 1750 | 1751 | function! s:update_ruby() 1752 | ruby << EOF 1753 | module PlugStream 1754 | SEP = ["\r", "\n", nil] 1755 | def get_line 1756 | buffer = '' 1757 | loop do 1758 | char = readchar rescue return 1759 | if SEP.include? char.chr 1760 | buffer << $/ 1761 | break 1762 | else 1763 | buffer << char 1764 | end 1765 | end 1766 | buffer 1767 | end 1768 | end unless defined?(PlugStream) 1769 | 1770 | def esc arg 1771 | %["#{arg.gsub('"', '\"')}"] 1772 | end 1773 | 1774 | def killall pid 1775 | pids = [pid] 1776 | if /mswin|mingw|bccwin/ =~ RUBY_PLATFORM 1777 | pids.each { |pid| Process.kill 'INT', pid.to_i rescue nil } 1778 | else 1779 | unless `which pgrep 2> /dev/null`.empty? 1780 | children = pids 1781 | until children.empty? 1782 | children = children.map { |pid| 1783 | `pgrep -P #{pid}`.lines.map { |l| l.chomp } 1784 | }.flatten 1785 | pids += children 1786 | end 1787 | end 1788 | pids.each { |pid| Process.kill 'TERM', pid.to_i rescue nil } 1789 | end 1790 | end 1791 | 1792 | def compare_git_uri a, b 1793 | regex = %r{^(?:\w+://)?(?:[^@/]*@)?([^:/]*(?::[0-9]*)?)[:/](.*?)(?:\.git)?/?$} 1794 | regex.match(a).to_a.drop(1) == regex.match(b).to_a.drop(1) 1795 | end 1796 | 1797 | require 'thread' 1798 | require 'fileutils' 1799 | require 'timeout' 1800 | running = true 1801 | iswin = VIM::evaluate('s:is_win').to_i == 1 1802 | pull = VIM::evaluate('s:update.pull').to_i == 1 1803 | base = VIM::evaluate('g:plug_home') 1804 | all = VIM::evaluate('s:update.todo') 1805 | limit = VIM::evaluate('get(g:, "plug_timeout", 60)') 1806 | tries = VIM::evaluate('get(g:, "plug_retries", 2)') + 1 1807 | nthr = VIM::evaluate('s:update.threads').to_i 1808 | maxy = VIM::evaluate('winheight(".")').to_i 1809 | vim7 = VIM::evaluate('v:version').to_i <= 703 && RUBY_PLATFORM =~ /darwin/ 1810 | cd = iswin ? 'cd /d' : 'cd' 1811 | tot = VIM::evaluate('len(s:update.todo)') || 0 1812 | bar = '' 1813 | skip = 'Already installed' 1814 | mtx = Mutex.new 1815 | take1 = proc { mtx.synchronize { running && all.shift } } 1816 | logh = proc { 1817 | cnt = bar.length 1818 | $curbuf[1] = "#{pull ? 'Updating' : 'Installing'} plugins (#{cnt}/#{tot})" 1819 | $curbuf[2] = '[' + bar.ljust(tot) + ']' 1820 | VIM::command('normal! 2G') 1821 | VIM::command('redraw') 1822 | } 1823 | where = proc { |name| (1..($curbuf.length)).find { |l| $curbuf[l] =~ /^[-+x*] #{name}:/ } } 1824 | log = proc { |name, result, type| 1825 | mtx.synchronize do 1826 | ing = ![true, false].include?(type) 1827 | bar += type ? '=' : 'x' unless ing 1828 | b = case type 1829 | when :install then '+' when :update then '*' 1830 | when true, nil then '-' else 1831 | VIM::command("call add(s:update.errors, '#{name}')") 1832 | 'x' 1833 | end 1834 | result = 1835 | if type || type.nil? 1836 | ["#{b} #{name}: #{result.lines.to_a.last || 'OK'}"] 1837 | elsif result =~ /^Interrupted|^Timeout/ 1838 | ["#{b} #{name}: #{result}"] 1839 | else 1840 | ["#{b} #{name}"] + result.lines.map { |l| " " << l } 1841 | end 1842 | if lnum = where.call(name) 1843 | $curbuf.delete lnum 1844 | lnum = 4 if ing && lnum > maxy 1845 | end 1846 | result.each_with_index do |line, offset| 1847 | $curbuf.append((lnum || 4) - 1 + offset, line.gsub(/\e\[./, '').chomp) 1848 | end 1849 | logh.call 1850 | end 1851 | } 1852 | bt = proc { |cmd, name, type, cleanup| 1853 | tried = timeout = 0 1854 | begin 1855 | tried += 1 1856 | timeout += limit 1857 | fd = nil 1858 | data = '' 1859 | if iswin 1860 | Timeout::timeout(timeout) do 1861 | tmp = VIM::evaluate('tempname()') 1862 | system("(#{cmd}) > #{tmp}") 1863 | data = File.read(tmp).chomp 1864 | File.unlink tmp rescue nil 1865 | end 1866 | else 1867 | fd = IO.popen(cmd).extend(PlugStream) 1868 | first_line = true 1869 | log_prob = 1.0 / nthr 1870 | while line = Timeout::timeout(timeout) { fd.get_line } 1871 | data << line 1872 | log.call name, line.chomp, type if name && (first_line || rand < log_prob) 1873 | first_line = false 1874 | end 1875 | fd.close 1876 | end 1877 | [$? == 0, data.chomp] 1878 | rescue Timeout::Error, Interrupt => e 1879 | if fd && !fd.closed? 1880 | killall fd.pid 1881 | fd.close 1882 | end 1883 | cleanup.call if cleanup 1884 | if e.is_a?(Timeout::Error) && tried < tries 1885 | 3.downto(1) do |countdown| 1886 | s = countdown > 1 ? 's' : '' 1887 | log.call name, "Timeout. Will retry in #{countdown} second#{s} ...", type 1888 | sleep 1 1889 | end 1890 | log.call name, 'Retrying ...', type 1891 | retry 1892 | end 1893 | [false, e.is_a?(Interrupt) ? "Interrupted!" : "Timeout!"] 1894 | end 1895 | } 1896 | main = Thread.current 1897 | threads = [] 1898 | watcher = Thread.new { 1899 | if vim7 1900 | while VIM::evaluate('getchar(1)') 1901 | sleep 0.1 1902 | end 1903 | else 1904 | require 'io/console' # >= Ruby 1.9 1905 | nil until IO.console.getch == 3.chr 1906 | end 1907 | mtx.synchronize do 1908 | running = false 1909 | threads.each { |t| t.raise Interrupt } unless vim7 1910 | end 1911 | threads.each { |t| t.join rescue nil } 1912 | main.kill 1913 | } 1914 | refresh = Thread.new { 1915 | while true 1916 | mtx.synchronize do 1917 | break unless running 1918 | VIM::command('noautocmd normal! a') 1919 | end 1920 | sleep 0.2 1921 | end 1922 | } if VIM::evaluate('s:mac_gui') == 1 1923 | 1924 | clone_opt = VIM::evaluate('s:clone_opt') 1925 | progress = VIM::evaluate('s:progress_opt(1)') 1926 | nthr.times do 1927 | mtx.synchronize do 1928 | threads << Thread.new { 1929 | while pair = take1.call 1930 | name = pair.first 1931 | dir, uri, tag = pair.last.values_at *%w[dir uri tag] 1932 | exists = File.directory? dir 1933 | ok, result = 1934 | if exists 1935 | chdir = "#{cd} #{iswin ? dir : esc(dir)}" 1936 | ret, data = bt.call "#{chdir} && git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url", nil, nil, nil 1937 | current_uri = data.lines.to_a.last 1938 | if !ret 1939 | if data =~ /^Interrupted|^Timeout/ 1940 | [false, data] 1941 | else 1942 | [false, [data.chomp, "PlugClean required."].join($/)] 1943 | end 1944 | elsif !compare_git_uri(current_uri, uri) 1945 | [false, ["Invalid URI: #{current_uri}", 1946 | "Expected: #{uri}", 1947 | "PlugClean required."].join($/)] 1948 | else 1949 | if pull 1950 | log.call name, 'Updating ...', :update 1951 | fetch_opt = (tag && File.exist?(File.join(dir, '.git/shallow'))) ? '--depth 99999999' : '' 1952 | bt.call "#{chdir} && git fetch #{fetch_opt} #{progress} 2>&1", name, :update, nil 1953 | else 1954 | [true, skip] 1955 | end 1956 | end 1957 | else 1958 | d = esc dir.sub(%r{[\\/]+$}, '') 1959 | log.call name, 'Installing ...', :install 1960 | bt.call "git clone #{clone_opt unless tag} #{progress} #{uri} #{d} 2>&1", name, :install, proc { 1961 | FileUtils.rm_rf dir 1962 | } 1963 | end 1964 | mtx.synchronize { VIM::command("let s:update.new['#{name}'] = 1") } if !exists && ok 1965 | log.call name, result, ok 1966 | end 1967 | } if running 1968 | end 1969 | end 1970 | threads.each { |t| t.join rescue nil } 1971 | logh.call 1972 | refresh.kill if refresh 1973 | watcher.kill 1974 | EOF 1975 | endfunction 1976 | 1977 | function! s:shellesc_cmd(arg) 1978 | let escaped = substitute(a:arg, '[&|<>()@^]', '^&', 'g') 1979 | let escaped = substitute(escaped, '%', '%%', 'g') 1980 | let escaped = substitute(escaped, '"', '\\^&', 'g') 1981 | let escaped = substitute(escaped, '\(\\\+\)\(\\^\)', '\1\1\2', 'g') 1982 | return '^"'.substitute(escaped, '\(\\\+\)$', '\1\1', '').'^"' 1983 | endfunction 1984 | 1985 | function! s:shellesc(arg) 1986 | if &shell =~# 'cmd.exe$' 1987 | return s:shellesc_cmd(a:arg) 1988 | endif 1989 | return shellescape(a:arg) 1990 | endfunction 1991 | 1992 | function! s:glob_dir(path) 1993 | return map(filter(s:glob(a:path, '**'), 'isdirectory(v:val)'), 's:dirpath(v:val)') 1994 | endfunction 1995 | 1996 | function! s:progress_bar(line, bar, total) 1997 | call setline(a:line, '[' . s:lpad(a:bar, a:total) . ']') 1998 | endfunction 1999 | 2000 | function! s:compare_git_uri(a, b) 2001 | " See `git help clone' 2002 | " https:// [user@] github.com[:port] / junegunn/vim-plug [.git] 2003 | " [git@] github.com[:port] : junegunn/vim-plug [.git] 2004 | " file:// / junegunn/vim-plug [/] 2005 | " / junegunn/vim-plug [/] 2006 | let pat = '^\%(\w\+://\)\='.'\%([^@/]*@\)\='.'\([^:/]*\%(:[0-9]*\)\=\)'.'[:/]'.'\(.\{-}\)'.'\%(\.git\)\=/\?$' 2007 | let ma = matchlist(a:a, pat) 2008 | let mb = matchlist(a:b, pat) 2009 | return ma[1:2] ==# mb[1:2] 2010 | endfunction 2011 | 2012 | function! s:format_message(bullet, name, message) 2013 | if a:bullet != 'x' 2014 | return [printf('%s %s: %s', a:bullet, a:name, s:lastline(a:message))] 2015 | else 2016 | let lines = map(s:lines(a:message), '" ".v:val') 2017 | return extend([printf('x %s:', a:name)], lines) 2018 | endif 2019 | endfunction 2020 | 2021 | function! s:with_cd(cmd, dir) 2022 | return printf('cd%s %s && %s', s:is_win ? ' /d' : '', s:shellesc(a:dir), a:cmd) 2023 | endfunction 2024 | 2025 | function! s:system(cmd, ...) 2026 | try 2027 | let [sh, shellcmdflag, shrd] = s:chsh(1) 2028 | let cmd = a:0 > 0 ? s:with_cd(a:cmd, a:1) : a:cmd 2029 | if s:is_win 2030 | let batchfile = tempname().'.bat' 2031 | call writefile(["@echo off\r", cmd . "\r"], batchfile) 2032 | let cmd = batchfile 2033 | endif 2034 | return system(s:is_win ? '('.cmd.')' : cmd) 2035 | finally 2036 | let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd] 2037 | if s:is_win 2038 | call delete(batchfile) 2039 | endif 2040 | endtry 2041 | endfunction 2042 | 2043 | function! s:system_chomp(...) 2044 | let ret = call('s:system', a:000) 2045 | return v:shell_error ? '' : substitute(ret, '\n$', '', '') 2046 | endfunction 2047 | 2048 | function! s:git_validate(spec, check_branch) 2049 | let err = '' 2050 | if isdirectory(a:spec.dir) 2051 | let result = s:lines(s:system('git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url', a:spec.dir)) 2052 | let remote = result[-1] 2053 | if v:shell_error 2054 | let err = join([remote, 'PlugClean required.'], "\n") 2055 | elseif !s:compare_git_uri(remote, a:spec.uri) 2056 | let err = join(['Invalid URI: '.remote, 2057 | \ 'Expected: '.a:spec.uri, 2058 | \ 'PlugClean required.'], "\n") 2059 | elseif a:check_branch && has_key(a:spec, 'commit') 2060 | let result = s:lines(s:system('git rev-parse HEAD 2>&1', a:spec.dir)) 2061 | let sha = result[-1] 2062 | if v:shell_error 2063 | let err = join(add(result, 'PlugClean required.'), "\n") 2064 | elseif !s:hash_match(sha, a:spec.commit) 2065 | let err = join([printf('Invalid HEAD (expected: %s, actual: %s)', 2066 | \ a:spec.commit[:6], sha[:6]), 2067 | \ 'PlugUpdate required.'], "\n") 2068 | endif 2069 | elseif a:check_branch 2070 | let branch = result[0] 2071 | " Check tag 2072 | if has_key(a:spec, 'tag') 2073 | let tag = s:system_chomp('git describe --exact-match --tags HEAD 2>&1', a:spec.dir) 2074 | if a:spec.tag !=# tag && a:spec.tag !~ '\*' 2075 | let err = printf('Invalid tag: %s (expected: %s). Try PlugUpdate.', 2076 | \ (empty(tag) ? 'N/A' : tag), a:spec.tag) 2077 | endif 2078 | " Check branch 2079 | elseif a:spec.branch !=# branch 2080 | let err = printf('Invalid branch: %s (expected: %s). Try PlugUpdate.', 2081 | \ branch, a:spec.branch) 2082 | endif 2083 | if empty(err) 2084 | let [ahead, behind] = split(s:lastline(s:system(printf( 2085 | \ 'git rev-list --count --left-right HEAD...origin/%s', 2086 | \ a:spec.branch), a:spec.dir)), '\t') 2087 | if !v:shell_error && ahead 2088 | if behind 2089 | " Only mention PlugClean if diverged, otherwise it's likely to be 2090 | " pushable (and probably not that messed up). 2091 | let err = printf( 2092 | \ "Diverged from origin/%s (%d commit(s) ahead and %d commit(s) behind!\n" 2093 | \ .'Backup local changes and run PlugClean and PlugUpdate to reinstall it.', a:spec.branch, ahead, behind) 2094 | else 2095 | let err = printf("Ahead of origin/%s by %d commit(s).\n" 2096 | \ .'Cannot update until local changes are pushed.', 2097 | \ a:spec.branch, ahead) 2098 | endif 2099 | endif 2100 | endif 2101 | endif 2102 | else 2103 | let err = 'Not found' 2104 | endif 2105 | return [err, err =~# 'PlugClean'] 2106 | endfunction 2107 | 2108 | function! s:rm_rf(dir) 2109 | if isdirectory(a:dir) 2110 | call s:system((s:is_win ? 'rmdir /S /Q ' : 'rm -rf ') . s:shellesc(a:dir)) 2111 | endif 2112 | endfunction 2113 | 2114 | function! s:clean(force) 2115 | call s:prepare() 2116 | call append(0, 'Searching for invalid plugins in '.g:plug_home) 2117 | call append(1, '') 2118 | 2119 | " List of valid directories 2120 | let dirs = [] 2121 | let errs = {} 2122 | let [cnt, total] = [0, len(g:plugs)] 2123 | for [name, spec] in items(g:plugs) 2124 | if !s:is_managed(name) 2125 | call add(dirs, spec.dir) 2126 | else 2127 | let [err, clean] = s:git_validate(spec, 1) 2128 | if clean 2129 | let errs[spec.dir] = s:lines(err)[0] 2130 | else 2131 | call add(dirs, spec.dir) 2132 | endif 2133 | endif 2134 | let cnt += 1 2135 | call s:progress_bar(2, repeat('=', cnt), total) 2136 | normal! 2G 2137 | redraw 2138 | endfor 2139 | 2140 | let allowed = {} 2141 | for dir in dirs 2142 | let allowed[s:dirpath(fnamemodify(dir, ':h:h'))] = 1 2143 | let allowed[dir] = 1 2144 | for child in s:glob_dir(dir) 2145 | let allowed[child] = 1 2146 | endfor 2147 | endfor 2148 | 2149 | let todo = [] 2150 | let found = sort(s:glob_dir(g:plug_home)) 2151 | while !empty(found) 2152 | let f = remove(found, 0) 2153 | if !has_key(allowed, f) && isdirectory(f) 2154 | call add(todo, f) 2155 | call append(line('$'), '- ' . f) 2156 | if has_key(errs, f) 2157 | call append(line('$'), ' ' . errs[f]) 2158 | endif 2159 | let found = filter(found, 'stridx(v:val, f) != 0') 2160 | end 2161 | endwhile 2162 | 2163 | 4 2164 | redraw 2165 | if empty(todo) 2166 | call append(line('$'), 'Already clean.') 2167 | else 2168 | let s:clean_count = 0 2169 | call append(3, ['Directories to delete:', '']) 2170 | redraw! 2171 | if a:force || s:ask_no_interrupt('Delete all directories?') 2172 | call s:delete([6, line('$')], 1) 2173 | else 2174 | call setline(4, 'Cancelled.') 2175 | nnoremap d :set opfunc=delete_opg@ 2176 | nmap dd d_ 2177 | xnoremap d :call delete_op(visualmode(), 1) 2178 | echo 'Delete the lines (d{motion}) to delete the corresponding directories' 2179 | endif 2180 | endif 2181 | 4 2182 | setlocal nomodifiable 2183 | endfunction 2184 | 2185 | function! s:delete_op(type, ...) 2186 | call s:delete(a:0 ? [line("'<"), line("'>")] : [line("'["), line("']")], 0) 2187 | endfunction 2188 | 2189 | function! s:delete(range, force) 2190 | let [l1, l2] = a:range 2191 | let force = a:force 2192 | while l1 <= l2 2193 | let line = getline(l1) 2194 | if line =~ '^- ' && isdirectory(line[2:]) 2195 | execute l1 2196 | redraw! 2197 | let answer = force ? 1 : s:ask('Delete '.line[2:].'?', 1) 2198 | let force = force || answer > 1 2199 | if answer 2200 | call s:rm_rf(line[2:]) 2201 | setlocal modifiable 2202 | call setline(l1, '~'.line[1:]) 2203 | let s:clean_count += 1 2204 | call setline(4, printf('Removed %d directories.', s:clean_count)) 2205 | setlocal nomodifiable 2206 | endif 2207 | endif 2208 | let l1 += 1 2209 | endwhile 2210 | endfunction 2211 | 2212 | function! s:upgrade() 2213 | echo 'Downloading the latest version of vim-plug' 2214 | redraw 2215 | let tmp = tempname() 2216 | let new = tmp . '/plug.vim' 2217 | 2218 | try 2219 | let out = s:system(printf('git clone --depth 1 %s %s', s:plug_src, tmp)) 2220 | if v:shell_error 2221 | return s:err('Error upgrading vim-plug: '. out) 2222 | endif 2223 | 2224 | if readfile(s:me) ==# readfile(new) 2225 | echo 'vim-plug is already up-to-date' 2226 | return 0 2227 | else 2228 | call rename(s:me, s:me . '.old') 2229 | call rename(new, s:me) 2230 | unlet g:loaded_plug 2231 | echo 'vim-plug has been upgraded' 2232 | return 1 2233 | endif 2234 | finally 2235 | silent! call s:rm_rf(tmp) 2236 | endtry 2237 | endfunction 2238 | 2239 | function! s:upgrade_specs() 2240 | for spec in values(g:plugs) 2241 | let spec.frozen = get(spec, 'frozen', 0) 2242 | endfor 2243 | endfunction 2244 | 2245 | function! s:status() 2246 | call s:prepare() 2247 | call append(0, 'Checking plugins') 2248 | call append(1, '') 2249 | 2250 | let ecnt = 0 2251 | let unloaded = 0 2252 | let [cnt, total] = [0, len(g:plugs)] 2253 | for [name, spec] in items(g:plugs) 2254 | let is_dir = isdirectory(spec.dir) 2255 | if has_key(spec, 'uri') 2256 | if is_dir 2257 | let [err, _] = s:git_validate(spec, 1) 2258 | let [valid, msg] = [empty(err), empty(err) ? 'OK' : err] 2259 | else 2260 | let [valid, msg] = [0, 'Not found. Try PlugInstall.'] 2261 | endif 2262 | else 2263 | if is_dir 2264 | let [valid, msg] = [1, 'OK'] 2265 | else 2266 | let [valid, msg] = [0, 'Not found.'] 2267 | endif 2268 | endif 2269 | let cnt += 1 2270 | let ecnt += !valid 2271 | " `s:loaded` entry can be missing if PlugUpgraded 2272 | if is_dir && get(s:loaded, name, -1) == 0 2273 | let unloaded = 1 2274 | let msg .= ' (not loaded)' 2275 | endif 2276 | call s:progress_bar(2, repeat('=', cnt), total) 2277 | call append(3, s:format_message(valid ? '-' : 'x', name, msg)) 2278 | normal! 2G 2279 | redraw 2280 | endfor 2281 | call setline(1, 'Finished. '.ecnt.' error(s).') 2282 | normal! gg 2283 | setlocal nomodifiable 2284 | if unloaded 2285 | echo "Press 'L' on each line to load plugin, or 'U' to update" 2286 | nnoremap L :call status_load(line('.')) 2287 | xnoremap L :call status_load(line('.')) 2288 | end 2289 | endfunction 2290 | 2291 | function! s:extract_name(str, prefix, suffix) 2292 | return matchstr(a:str, '^'.a:prefix.' \zs[^:]\+\ze:.*'.a:suffix.'$') 2293 | endfunction 2294 | 2295 | function! s:status_load(lnum) 2296 | let line = getline(a:lnum) 2297 | let name = s:extract_name(line, '-', '(not loaded)') 2298 | if !empty(name) 2299 | call plug#load(name) 2300 | setlocal modifiable 2301 | call setline(a:lnum, substitute(line, ' (not loaded)$', '', '')) 2302 | setlocal nomodifiable 2303 | endif 2304 | endfunction 2305 | 2306 | function! s:status_update() range 2307 | let lines = getline(a:firstline, a:lastline) 2308 | let names = filter(map(lines, 's:extract_name(v:val, "[x-]", "")'), '!empty(v:val)') 2309 | if !empty(names) 2310 | echo 2311 | execute 'PlugUpdate' join(names) 2312 | endif 2313 | endfunction 2314 | 2315 | function! s:is_preview_window_open() 2316 | silent! wincmd P 2317 | if &previewwindow 2318 | wincmd p 2319 | return 1 2320 | endif 2321 | endfunction 2322 | 2323 | function! s:find_name(lnum) 2324 | for lnum in reverse(range(1, a:lnum)) 2325 | let line = getline(lnum) 2326 | if empty(line) 2327 | return '' 2328 | endif 2329 | let name = s:extract_name(line, '-', '') 2330 | if !empty(name) 2331 | return name 2332 | endif 2333 | endfor 2334 | return '' 2335 | endfunction 2336 | 2337 | function! s:preview_commit() 2338 | if b:plug_preview < 0 2339 | let b:plug_preview = !s:is_preview_window_open() 2340 | endif 2341 | 2342 | let sha = matchstr(getline('.'), '^ \X*\zs[0-9a-f]\{7,9}') 2343 | if empty(sha) 2344 | return 2345 | endif 2346 | 2347 | let name = s:find_name(line('.')) 2348 | if empty(name) || !has_key(g:plugs, name) || !isdirectory(g:plugs[name].dir) 2349 | return 2350 | endif 2351 | 2352 | if exists('g:plug_pwindow') && !s:is_preview_window_open() 2353 | execute g:plug_pwindow 2354 | execute 'e' sha 2355 | else 2356 | execute 'pedit' sha 2357 | wincmd P 2358 | endif 2359 | setlocal previewwindow filetype=git buftype=nofile nobuflisted modifiable 2360 | try 2361 | let [sh, shellcmdflag, shrd] = s:chsh(1) 2362 | let cmd = 'cd '.s:shellesc(g:plugs[name].dir).' && git show --no-color --pretty=medium '.sha 2363 | if s:is_win 2364 | let batchfile = tempname().'.bat' 2365 | call writefile(["@echo off\r", cmd . "\r"], batchfile) 2366 | let cmd = batchfile 2367 | endif 2368 | execute 'silent %!' cmd 2369 | finally 2370 | let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd] 2371 | if s:is_win 2372 | call delete(batchfile) 2373 | endif 2374 | endtry 2375 | setlocal nomodifiable 2376 | nnoremap q :q 2377 | wincmd p 2378 | endfunction 2379 | 2380 | function! s:section(flags) 2381 | call search('\(^[x-] \)\@<=[^:]\+:', a:flags) 2382 | endfunction 2383 | 2384 | function! s:format_git_log(line) 2385 | let indent = ' ' 2386 | let tokens = split(a:line, nr2char(1)) 2387 | if len(tokens) != 5 2388 | return indent.substitute(a:line, '\s*$', '', '') 2389 | endif 2390 | let [graph, sha, refs, subject, date] = tokens 2391 | let tag = matchstr(refs, 'tag: [^,)]\+') 2392 | let tag = empty(tag) ? ' ' : ' ('.tag.') ' 2393 | return printf('%s%s%s%s%s (%s)', indent, graph, sha, tag, subject, date) 2394 | endfunction 2395 | 2396 | function! s:append_ul(lnum, text) 2397 | call append(a:lnum, ['', a:text, repeat('-', len(a:text))]) 2398 | endfunction 2399 | 2400 | function! s:diff() 2401 | call s:prepare() 2402 | call append(0, ['Collecting changes ...', '']) 2403 | let cnts = [0, 0] 2404 | let bar = '' 2405 | let total = filter(copy(g:plugs), 's:is_managed(v:key) && isdirectory(v:val.dir)') 2406 | call s:progress_bar(2, bar, len(total)) 2407 | for origin in [1, 0] 2408 | let plugs = reverse(sort(items(filter(copy(total), (origin ? '' : '!').'(has_key(v:val, "commit") || has_key(v:val, "tag"))')))) 2409 | if empty(plugs) 2410 | continue 2411 | endif 2412 | call s:append_ul(2, origin ? 'Pending updates:' : 'Last update:') 2413 | for [k, v] in plugs 2414 | let range = origin ? '..origin/'.v.branch : 'HEAD@{1}..' 2415 | let diff = s:system_chomp('git log --graph --color=never '.join(map(['--pretty=format:%x01%h%x01%d%x01%s%x01%cr', range], 's:shellesc(v:val)')), v.dir) 2416 | if !empty(diff) 2417 | let ref = has_key(v, 'tag') ? (' (tag: '.v.tag.')') : has_key(v, 'commit') ? (' '.v.commit) : '' 2418 | call append(5, extend(['', '- '.k.':'.ref], map(s:lines(diff), 's:format_git_log(v:val)'))) 2419 | let cnts[origin] += 1 2420 | endif 2421 | let bar .= '=' 2422 | call s:progress_bar(2, bar, len(total)) 2423 | normal! 2G 2424 | redraw 2425 | endfor 2426 | if !cnts[origin] 2427 | call append(5, ['', 'N/A']) 2428 | endif 2429 | endfor 2430 | call setline(1, printf('%d plugin(s) updated.', cnts[0]) 2431 | \ . (cnts[1] ? printf(' %d plugin(s) have pending updates.', cnts[1]) : '')) 2432 | 2433 | if cnts[0] || cnts[1] 2434 | nnoremap (plug-preview) :silent! call preview_commit() 2435 | if empty(maparg("\", 'n')) 2436 | nmap (plug-preview) 2437 | endif 2438 | if empty(maparg('o', 'n')) 2439 | nmap o (plug-preview) 2440 | endif 2441 | endif 2442 | if cnts[0] 2443 | nnoremap X :call revert() 2444 | echo "Press 'X' on each block to revert the update" 2445 | endif 2446 | normal! gg 2447 | setlocal nomodifiable 2448 | endfunction 2449 | 2450 | function! s:revert() 2451 | if search('^Pending updates', 'bnW') 2452 | return 2453 | endif 2454 | 2455 | let name = s:find_name(line('.')) 2456 | if empty(name) || !has_key(g:plugs, name) || 2457 | \ input(printf('Revert the update of %s? (y/N) ', name)) !~? '^y' 2458 | return 2459 | endif 2460 | 2461 | call s:system('git reset --hard HEAD@{1} && git checkout '.s:esc(g:plugs[name].branch).' --', g:plugs[name].dir) 2462 | setlocal modifiable 2463 | normal! "_dap 2464 | setlocal nomodifiable 2465 | echo 'Reverted' 2466 | endfunction 2467 | 2468 | function! s:snapshot(force, ...) abort 2469 | call s:prepare() 2470 | setf vim 2471 | call append(0, ['" Generated by vim-plug', 2472 | \ '" '.strftime("%c"), 2473 | \ '" :source this file in vim to restore the snapshot', 2474 | \ '" or execute: vim -S snapshot.vim', 2475 | \ '', '', 'PlugUpdate!']) 2476 | 1 2477 | let anchor = line('$') - 3 2478 | let names = sort(keys(filter(copy(g:plugs), 2479 | \'has_key(v:val, "uri") && !has_key(v:val, "commit") && isdirectory(v:val.dir)'))) 2480 | for name in reverse(names) 2481 | let sha = s:system_chomp('git rev-parse --short HEAD', g:plugs[name].dir) 2482 | if !empty(sha) 2483 | call append(anchor, printf("silent! let g:plugs['%s'].commit = '%s'", name, sha)) 2484 | redraw 2485 | endif 2486 | endfor 2487 | 2488 | if a:0 > 0 2489 | let fn = expand(a:1) 2490 | if filereadable(fn) && !(a:force || s:ask(a:1.' already exists. Overwrite?')) 2491 | return 2492 | endif 2493 | call writefile(getline(1, '$'), fn) 2494 | echo 'Saved as '.a:1 2495 | silent execute 'e' s:esc(fn) 2496 | setf vim 2497 | endif 2498 | endfunction 2499 | 2500 | function! s:split_rtp() 2501 | return split(&rtp, '\\\@ :call lv#VimGoTo() 2 | nnoremap :call lv#VimBack() 3 | -------------------------------------------------------------------------------- /init.vim: -------------------------------------------------------------------------------- 1 | call plug#begin() " {{{ 2 | Plug 'majutsushi/tagbar' 3 | Plug 'scrooloose/nerdtree' 4 | Plug 'jacoborus/tender.vim' 5 | Plug 'Shougo/deoplete.nvim', { 'do': ':UpdateRemotePlugins' } 6 | Plug 'junegunn/fzf' 7 | Plug 'lvht/fzf-mru' 8 | Plug 'mileszs/ack.vim' 9 | Plug 'Xuyuanp/nerdtree-git-plugin' 10 | Plug 'airblade/vim-gitgutter' 11 | Plug 'tpope/vim-fugitive' 12 | Plug 'Townk/vim-autoclose' 13 | Plug 'godlygeek/tabular' 14 | Plug 'tomtom/tcomment_vim' 15 | Plug '2072/PHP-Indenting-for-VIm' 16 | Plug 'StanAngeloff/php.vim' 17 | Plug 'lvht/phpcd.vim' 18 | Plug 'lvht/phpfold.vim' 19 | Plug 'w0rp/ale' 20 | Plug 'hynek/vim-python-pep8-indent' 21 | Plug 'justinmk/vim-syntax-extra' 22 | Plug 'wavded/vim-stylus' 23 | Plug 'cakebaker/scss-syntax.vim' 24 | Plug 'groenewege/vim-less' 25 | Plug 'hail2u/vim-css3-syntax' 26 | Plug 'othree/html5-syntax.vim' 27 | Plug 'othree/html5.vim' 28 | Plug 'pangloss/vim-javascript' 29 | Plug 'gavocanov/vim-js-indent' 30 | Plug 'othree/yajs.vim' 31 | Plug 'plasticboy/vim-markdown' 32 | Plug 'lvht/tagbar-markdown' 33 | Plug 'ironhouzi/vim-stim' 34 | Plug 'jreybert/vimagit' 35 | Plug 'fatih/vim-go' 36 | Plug 'zchee/deoplete-go', { 'do': 'make', 'for': 'go' } 37 | Plug 'cespare/vim-toml' 38 | Plug 'Lenovsky/nuake' 39 | call plug#end() " }}} 40 | 41 | color tender " {{{ 42 | highlight Normal guibg=#000001 43 | highlight Visual guibg=#323232 44 | highlight Todo guifg=yellow guibg=bg gui=bold 45 | highlight StatusLine guifg=NONE guibg=#323232 gui=bold 46 | " }}} 47 | 48 | set colorcolumn=80 " {{{ 49 | set linebreak 50 | set list 51 | set fileformat=unix 52 | set fileencodings=ucs-bom,utf-8,cp936,gb18030,big5,euc-jp,euc-kr,latin1 53 | set termguicolors 54 | set noswapfile 55 | set ignorecase 56 | set smartcase 57 | set smartindent 58 | set pastetoggle=v 59 | set maxmempattern=2000000 60 | " }}} 61 | 62 | nnoremap :noh 63 | 64 | autocmd FileType vim setlocal foldmethod=marker " {{{ 65 | autocmd FileType html,css,scss,javascript,tex call lv#ExpandTab(2) 66 | autocmd FileType php,python,json,nginx,proto call lv#ExpandTab(4) 67 | " 将光标跳转到上次打开当前文件的位置 68 | autocmd BufReadPost * if line("'\"") > 0 && line("'\"") <= line("$") | 69 | \ execute "normal! g`\"" | 70 | \ endif 71 | " }}} 72 | 73 | " Tagbar {{{ 74 | nnoremap t :TagbarToggle 75 | let g:tagbar_compact = 1 76 | let g:tagbar_sort = 0 77 | let g:tagbar_iconchars = ['▸', '▾'] 78 | " }}} 79 | 80 | " NERD Tree {{{ 81 | nnoremap e :NERDTreeToggle 82 | nnoremap f :NERDTreeFind 83 | let NERDTreeMinimalUI = 1 84 | let NERDTreeChDirMode = 2 85 | " }}} 86 | 87 | " vim-markdown {{{ 88 | let g:vim_markdown_folding_disabled=1 89 | let g:vim_markdown_frontmatter=1 90 | " }}} 91 | 92 | " deoplete {{{ 93 | let g:deoplete#enable_at_startup = 1 94 | let g:deoplete#file#enable_buffer_path = 1 95 | " }}} 96 | 97 | " ack {{{ 98 | let g:ackprg = 'ag --vimgrep --ignore tags' 99 | " }}} 100 | 101 | " fzf {{{ 102 | let g:fzf_root = lv#FindRoot() 103 | let g:fzf_mru_file_list_size = 100 104 | execute "nnoremap :FZF ".g:fzf_root."" 105 | nnoremap :FZFMru 106 | " }}} 107 | 108 | " ale {{{ 109 | let g:ale_lint_on_save = 1 110 | let g:ale_lint_on_enter = 0 111 | let g:ale_lint_on_text_changed = 0 112 | let g:ale_open_list = 1 113 | " }}} 114 | 115 | " go {{{ 116 | autocmd InsertLeave,CompleteDone *.go if pumvisible() == 0 | pclose | endif 117 | let g:go_fmt_command = "goimports" 118 | let g:go_term_enabled = 0 119 | " }}} 120 | --------------------------------------------------------------------------------