diff options
author | Jinwei Zhao <[email protected]> | 2016-11-14 21:11:16 +0800 |
---|---|---|
committer | Jinwei Zhao <[email protected]> | 2016-11-14 21:11:16 +0800 |
commit | 5711e281729282f987ed4ef85b0dfe02b81bbf45 (patch) | |
tree | 3d79f43706384083e6f13d9b51467ce79125a146 | |
parent | d24c59b03dde30af2a302db2672fa034fa27baf5 (diff) | |
download | conf.d-5711e281729282f987ed4ef85b0dfe02b81bbf45.tar.gz |
add .vimrc
-rw-r--r-- | vim/.vim/autoload/plug.vim | 2267 | ||||
-rw-r--r-- | vim/.vim/config/airline.vim | 4 | ||||
-rw-r--r-- | vim/.vim/config/molokai.vim | 1 | ||||
-rw-r--r-- | vim/.vim/config/rainbow.vim | 19 | ||||
-rw-r--r-- | vim/.vim/config/syntastic.vim | 35 | ||||
-rw-r--r-- | vim/.vim/config/ycm.vim | 20 | ||||
-rw-r--r-- | vim/.vim/config/ycm_extra_conf.py | 76 | ||||
-rw-r--r-- | vim/.vim/plugged/.gitkeep | 0 | ||||
-rw-r--r-- | vim/.vimrc | 69 |
9 files changed, 2491 insertions, 0 deletions
diff --git a/vim/.vim/autoload/plug.vim b/vim/.vim/autoload/plug.vim new file mode 100644 index 0000000..0d0e409 --- /dev/null +++ b/vim/.vim/autoload/plug.vim | |||
@@ -0,0 +1,2267 @@ | |||
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 | " " Group dependencies, vim-snippets depends on ultisnips | ||
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 | " " Add plugins to &runtimepath | ||
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 `<Plug>`-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) 2016 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') || has('win64') | ||
100 | let s:nvim = has('nvim') && exists('*jobwait') && !s:is_win | ||
101 | let s:me = resolve(expand('<sfile>:p')) | ||
102 | let s:base_spec = { 'branch': 'master', 'frozen': 0 } | ||
103 | let s:TYPE = { | ||
104 | \ 'string': type(''), | ||
105 | \ 'list': type([]), | ||
106 | \ 'dict': type({}), | ||
107 | \ 'funcref': type(function('call')) | ||
108 | \ } | ||
109 | let s:loaded = get(s:, 'loaded', {}) | ||
110 | let s:triggers = get(s:, 'triggers', {}) | ||
111 | |||
112 | function! plug#begin(...) | ||
113 | if a:0 > 0 | ||
114 | let s:plug_home_org = a:1 | ||
115 | let home = s:path(fnamemodify(expand(a:1), ':p')) | ||
116 | elseif exists('g:plug_home') | ||
117 | let home = s:path(g:plug_home) | ||
118 | elseif !empty(&rtp) | ||
119 | let home = s:path(split(&rtp, ',')[0]) . '/plugged' | ||
120 | else | ||
121 | return s:err('Unable to determine plug home. Try calling plug#begin() with a path argument.') | ||
122 | endif | ||
123 | |||
124 | let g:plug_home = home | ||
125 | let g:plugs = {} | ||
126 | let g:plugs_order = [] | ||
127 | let s:triggers = {} | ||
128 | |||
129 | call s:define_commands() | ||
130 | return 1 | ||
131 | endfunction | ||
132 | |||
133 | function! s:define_commands() | ||
134 | command! -nargs=+ -bar Plug call plug#(<args>) | ||
135 | if !executable('git') | ||
136 | return s:err('`git` executable not found. Most commands will not be available. To suppress this message, prepend `silent!` to `call plug#begin(...)`.') | ||
137 | endif | ||
138 | command! -nargs=* -bar -bang -complete=customlist,s:names PlugInstall call s:install(<bang>0, [<f-args>]) | ||
139 | command! -nargs=* -bar -bang -complete=customlist,s:names PlugUpdate call s:update(<bang>0, [<f-args>]) | ||
140 | command! -nargs=0 -bar -bang PlugClean call s:clean(<bang>0) | ||
141 | command! -nargs=0 -bar PlugUpgrade if s:upgrade() | execute 'source' s:esc(s:me) | endif | ||
142 | command! -nargs=0 -bar PlugStatus call s:status() | ||
143 | command! -nargs=0 -bar PlugDiff call s:diff() | ||
144 | command! -nargs=? -bar -bang -complete=file PlugSnapshot call s:snapshot(<bang>0, <f-args>) | ||
145 | endfunction | ||
146 | |||
147 | function! s:to_a(v) | ||
148 | return type(a:v) == s:TYPE.list ? a:v : [a:v] | ||
149 | endfunction | ||
150 | |||
151 | function! s:to_s(v) | ||
152 | return type(a:v) == s:TYPE.string ? a:v : join(a:v, "\n") . "\n" | ||
153 | endfunction | ||
154 | |||
155 | function! s:glob(from, pattern) | ||
156 | return s:lines(globpath(a:from, a:pattern)) | ||
157 | endfunction | ||
158 | |||
159 | function! s:source(from, ...) | ||
160 | let found = 0 | ||
161 | for pattern in a:000 | ||
162 | for vim in s:glob(a:from, pattern) | ||
163 | execute 'source' s:esc(vim) | ||
164 | let found = 1 | ||
165 | endfor | ||
166 | endfor | ||
167 | return found | ||
168 | endfunction | ||
169 | |||
170 | function! s:assoc(dict, key, val) | ||
171 | let a:dict[a:key] = add(get(a:dict, a:key, []), a:val) | ||
172 | endfunction | ||
173 | |||
174 | function! s:ask(message) | ||
175 | call inputsave() | ||
176 | echohl WarningMsg | ||
177 | let proceed = input(a:message.' (y/N) ') =~? '^y' | ||
178 | echohl None | ||
179 | call inputrestore() | ||
180 | echo "\r" | ||
181 | return proceed | ||
182 | endfunction | ||
183 | |||
184 | function! plug#end() | ||
185 | if !exists('g:plugs') | ||
186 | return s:err('Call plug#begin() first') | ||
187 | endif | ||
188 | |||
189 | if exists('#PlugLOD') | ||
190 | augroup PlugLOD | ||
191 | autocmd! | ||
192 | augroup END | ||
193 | augroup! PlugLOD | ||
194 | endif | ||
195 | let lod = { 'ft': {}, 'map': {}, 'cmd': {} } | ||
196 | |||
197 | filetype off | ||
198 | for name in g:plugs_order | ||
199 | if !has_key(g:plugs, name) | ||
200 | continue | ||
201 | endif | ||
202 | let plug = g:plugs[name] | ||
203 | if get(s:loaded, name, 0) || !has_key(plug, 'on') && !has_key(plug, 'for') | ||
204 | let s:loaded[name] = 1 | ||
205 | continue | ||
206 | endif | ||
207 | |||
208 | if has_key(plug, 'on') | ||
209 | let s:triggers[name] = { 'map': [], 'cmd': [] } | ||
210 | for cmd in s:to_a(plug.on) | ||
211 | if cmd =~? '^<Plug>.\+' | ||
212 | if empty(mapcheck(cmd)) && empty(mapcheck(cmd, 'i')) | ||
213 | call s:assoc(lod.map, cmd, name) | ||
214 | endif | ||
215 | call add(s:triggers[name].map, cmd) | ||
216 | elseif cmd =~# '^[A-Z]' | ||
217 | if exists(':'.cmd) != 2 | ||
218 | call s:assoc(lod.cmd, cmd, name) | ||
219 | endif | ||
220 | call add(s:triggers[name].cmd, cmd) | ||
221 | else | ||
222 | call s:err('Invalid `on` option: '.cmd. | ||
223 | \ '. Should start with an uppercase letter or `<Plug>`.') | ||
224 | endif | ||
225 | endfor | ||
226 | endif | ||
227 | |||
228 | if has_key(plug, 'for') | ||
229 | let types = s:to_a(plug.for) | ||
230 | if !empty(types) | ||
231 | augroup filetypedetect | ||
232 | call s:source(s:rtp(plug), 'ftdetect/**/*.vim', 'after/ftdetect/**/*.vim') | ||
233 | augroup END | ||
234 | endif | ||
235 | for type in types | ||
236 | call s:assoc(lod.ft, type, name) | ||
237 | endfor | ||
238 | endif | ||
239 | endfor | ||
240 | |||
241 | for [cmd, names] in items(lod.cmd) | ||
242 | execute printf( | ||
243 | \ 'command! -nargs=* -range -bang %s call s:lod_cmd(%s, "<bang>", <line1>, <line2>, <q-args>, %s)', | ||
244 | \ cmd, string(cmd), string(names)) | ||
245 | endfor | ||
246 | |||
247 | for [map, names] in items(lod.map) | ||
248 | for [mode, map_prefix, key_prefix] in | ||
249 | \ [['i', '<C-O>', ''], ['n', '', ''], ['v', '', 'gv'], ['o', '', '']] | ||
250 | execute printf( | ||
251 | \ '%snoremap <silent> %s %s:<C-U>call <SID>lod_map(%s, %s, "%s")<CR>', | ||
252 | \ mode, map, map_prefix, string(map), string(names), key_prefix) | ||
253 | endfor | ||
254 | endfor | ||
255 | |||
256 | for [ft, names] in items(lod.ft) | ||
257 | augroup PlugLOD | ||
258 | execute printf('autocmd FileType %s call <SID>lod_ft(%s, %s)', | ||
259 | \ ft, string(ft), string(names)) | ||
260 | augroup END | ||
261 | endfor | ||
262 | |||
263 | call s:reorg_rtp() | ||
264 | filetype plugin indent on | ||
265 | if has('vim_starting') | ||
266 | if has('syntax') && !exists('g:syntax_on') | ||
267 | syntax enable | ||
268 | end | ||
269 | else | ||
270 | call s:reload() | ||
271 | endif | ||
272 | endfunction | ||
273 | |||
274 | function! s:loaded_names() | ||
275 | return filter(copy(g:plugs_order), 'get(s:loaded, v:val, 0)') | ||
276 | endfunction | ||
277 | |||
278 | function! s:reload() | ||
279 | for name in s:loaded_names() | ||
280 | call s:source(s:rtp(g:plugs[name]), 'plugin/**/*.vim', 'after/plugin/**/*.vim') | ||
281 | endfor | ||
282 | endfunction | ||
283 | |||
284 | function! s:trim(str) | ||
285 | return substitute(a:str, '[\/]\+$', '', '') | ||
286 | endfunction | ||
287 | |||
288 | function! s:version_requirement(val, min) | ||
289 | for idx in range(0, len(a:min) - 1) | ||
290 | let v = get(a:val, idx, 0) | ||
291 | if v < a:min[idx] | return 0 | ||
292 | elseif v > a:min[idx] | return 1 | ||
293 | endif | ||
294 | endfor | ||
295 | return 1 | ||
296 | endfunction | ||
297 | |||
298 | function! s:git_version_requirement(...) | ||
299 | if !exists('s:git_version') | ||
300 | let s:git_version = map(split(split(s:system('git --version'))[-1], '\.'), 'str2nr(v:val)') | ||
301 | endif | ||
302 | return s:version_requirement(s:git_version, a:000) | ||
303 | endfunction | ||
304 | |||
305 | function! s:progress_opt(base) | ||
306 | return a:base && !s:is_win && | ||
307 | \ s:git_version_requirement(1, 7, 1) ? '--progress' : '' | ||
308 | endfunction | ||
309 | |||
310 | if s:is_win | ||
311 | function! s:rtp(spec) | ||
312 | return s:path(a:spec.dir . get(a:spec, 'rtp', '')) | ||
313 | endfunction | ||
314 | |||
315 | function! s:path(path) | ||
316 | return s:trim(substitute(a:path, '/', '\', 'g')) | ||
317 | endfunction | ||
318 | |||
319 | function! s:dirpath(path) | ||
320 | return s:path(a:path) . '\' | ||
321 | endfunction | ||
322 | |||
323 | function! s:is_local_plug(repo) | ||
324 | return a:repo =~? '^[a-z]:\|^[%~]' | ||
325 | endfunction | ||
326 | else | ||
327 | function! s:rtp(spec) | ||
328 | return s:dirpath(a:spec.dir . get(a:spec, 'rtp', '')) | ||
329 | endfunction | ||
330 | |||
331 | function! s:path(path) | ||
332 | return s:trim(a:path) | ||
333 | endfunction | ||
334 | |||
335 | function! s:dirpath(path) | ||
336 | return substitute(a:path, '[/\\]*$', '/', '') | ||
337 | endfunction | ||
338 | |||
339 | function! s:is_local_plug(repo) | ||
340 | return a:repo[0] =~ '[/$~]' | ||
341 | endfunction | ||
342 | endif | ||
343 | |||
344 | function! s:err(msg) | ||
345 | echohl ErrorMsg | ||
346 | echom '[vim-plug] '.a:msg | ||
347 | echohl None | ||
348 | endfunction | ||
349 | |||
350 | function! s:warn(cmd, msg) | ||
351 | echohl WarningMsg | ||
352 | execute a:cmd 'a:msg' | ||
353 | echohl None | ||
354 | endfunction | ||
355 | |||
356 | function! s:esc(path) | ||
357 | return escape(a:path, ' ') | ||
358 | endfunction | ||
359 | |||
360 | function! s:escrtp(path) | ||
361 | return escape(a:path, ' ,') | ||
362 | endfunction | ||
363 | |||
364 | function! s:remove_rtp() | ||
365 | for name in s:loaded_names() | ||
366 | let rtp = s:rtp(g:plugs[name]) | ||
367 | execute 'set rtp-='.s:escrtp(rtp) | ||
368 | let after = globpath(rtp, 'after') | ||
369 | if isdirectory(after) | ||
370 | execute 'set rtp-='.s:escrtp(after) | ||
371 | endif | ||
372 | endfor | ||
373 | endfunction | ||
374 | |||
375 | function! s:reorg_rtp() | ||
376 | if !empty(s:first_rtp) | ||
377 | execute 'set rtp-='.s:first_rtp | ||
378 | execute 'set rtp-='.s:last_rtp | ||
379 | endif | ||
380 | |||
381 | " &rtp is modified from outside | ||
382 | if exists('s:prtp') && s:prtp !=# &rtp | ||
383 | call s:remove_rtp() | ||
384 | unlet! s:middle | ||
385 | endif | ||
386 | |||
387 | let s:middle = get(s:, 'middle', &rtp) | ||
388 | let rtps = map(s:loaded_names(), 's:rtp(g:plugs[v:val])') | ||
389 | let afters = filter(map(copy(rtps), 'globpath(v:val, "after")'), 'isdirectory(v:val)') | ||
390 | let rtp = join(map(rtps, 'escape(v:val, ",")'), ',') | ||
391 | \ . ','.s:middle.',' | ||
392 | \ . join(map(afters, 'escape(v:val, ",")'), ',') | ||
393 | let &rtp = substitute(substitute(rtp, ',,*', ',', 'g'), '^,\|,$', '', 'g') | ||
394 | let s:prtp = &rtp | ||
395 | |||
396 | if !empty(s:first_rtp) | ||
397 | execute 'set rtp^='.s:first_rtp | ||
398 | execute 'set rtp+='.s:last_rtp | ||
399 | endif | ||
400 | endfunction | ||
401 | |||
402 | function! s:doautocmd(...) | ||
403 | if exists('#'.join(a:000, '#')) | ||
404 | execute 'doautocmd' ((v:version > 703 || has('patch442')) ? '<nomodeline>' : '') join(a:000) | ||
405 | endif | ||
406 | endfunction | ||
407 | |||
408 | function! s:dobufread(names) | ||
409 | for name in a:names | ||
410 | let path = s:rtp(g:plugs[name]).'/**' | ||
411 | for dir in ['ftdetect', 'ftplugin'] | ||
412 | if len(finddir(dir, path)) | ||
413 | return s:doautocmd('BufRead') | ||
414 | endif | ||
415 | endfor | ||
416 | endfor | ||
417 | endfunction | ||
418 | |||
419 | function! plug#load(...) | ||
420 | if a:0 == 0 | ||
421 | return s:err('Argument missing: plugin name(s) required') | ||
422 | endif | ||
423 | if !exists('g:plugs') | ||
424 | return s:err('plug#begin was not called') | ||
425 | endif | ||
426 | let unknowns = filter(copy(a:000), '!has_key(g:plugs, v:val)') | ||
427 | if !empty(unknowns) | ||
428 | let s = len(unknowns) > 1 ? 's' : '' | ||
429 | return s:err(printf('Unknown plugin%s: %s', s, join(unknowns, ', '))) | ||
430 | end | ||
431 | for name in a:000 | ||
432 | call s:lod([name], ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin']) | ||
433 | endfor | ||
434 | call s:dobufread(a:000) | ||
435 | return 1 | ||
436 | endfunction | ||
437 | |||
438 | function! s:remove_triggers(name) | ||
439 | if !has_key(s:triggers, a:name) | ||
440 | return | ||
441 | endif | ||
442 | for cmd in s:triggers[a:name].cmd | ||
443 | execute 'silent! delc' cmd | ||
444 | endfor | ||
445 | for map in s:triggers[a:name].map | ||
446 | execute 'silent! unmap' map | ||
447 | execute 'silent! iunmap' map | ||
448 | endfor | ||
449 | call remove(s:triggers, a:name) | ||
450 | endfunction | ||
451 | |||
452 | function! s:lod(names, types, ...) | ||
453 | for name in a:names | ||
454 | call s:remove_triggers(name) | ||
455 | let s:loaded[name] = 1 | ||
456 | endfor | ||
457 | call s:reorg_rtp() | ||
458 | |||
459 | for name in a:names | ||
460 | let rtp = s:rtp(g:plugs[name]) | ||
461 | for dir in a:types | ||
462 | call s:source(rtp, dir.'/**/*.vim') | ||
463 | endfor | ||
464 | if a:0 | ||
465 | if !s:source(rtp, a:1) && !empty(s:glob(rtp, a:2)) | ||
466 | execute 'runtime' a:1 | ||
467 | endif | ||
468 | call s:source(rtp, a:2) | ||
469 | endif | ||
470 | call s:doautocmd('User', name) | ||
471 | endfor | ||
472 | endfunction | ||
473 | |||
474 | function! s:lod_ft(pat, names) | ||
475 | let syn = 'syntax/'.a:pat.'.vim' | ||
476 | call s:lod(a:names, ['plugin', 'after/plugin'], syn, 'after/'.syn) | ||
477 | execute 'autocmd! PlugLOD FileType' a:pat | ||
478 | call s:doautocmd('filetypeplugin', 'FileType') | ||
479 | call s:doautocmd('filetypeindent', 'FileType') | ||
480 | endfunction | ||
481 | |||
482 | function! s:lod_cmd(cmd, bang, l1, l2, args, names) | ||
483 | call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin']) | ||
484 | call s:dobufread(a:names) | ||
485 | execute printf('%s%s%s %s', (a:l1 == a:l2 ? '' : (a:l1.','.a:l2)), a:cmd, a:bang, a:args) | ||
486 | endfunction | ||
487 | |||
488 | function! s:lod_map(map, names, prefix) | ||
489 | call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin']) | ||
490 | call s:dobufread(a:names) | ||
491 | let extra = '' | ||
492 | while 1 | ||
493 | let c = getchar(0) | ||
494 | if c == 0 | ||
495 | break | ||
496 | endif | ||
497 | let extra .= nr2char(c) | ||
498 | endwhile | ||
499 | if v:count | ||
500 | call feedkeys(v:count, 'n') | ||
501 | endif | ||
502 | call feedkeys('"'.v:register, 'n') | ||
503 | if mode(1) == 'no' | ||
504 | call feedkeys(v:operator) | ||
505 | endif | ||
506 | call feedkeys(a:prefix . substitute(a:map, '^<Plug>', "\<Plug>", '') . extra) | ||
507 | endfunction | ||
508 | |||
509 | function! plug#(repo, ...) | ||
510 | if a:0 > 1 | ||
511 | return s:err('Invalid number of arguments (1..2)') | ||
512 | endif | ||
513 | |||
514 | try | ||
515 | let repo = s:trim(a:repo) | ||
516 | let opts = a:0 == 1 ? s:parse_options(a:1) : s:base_spec | ||
517 | let name = get(opts, 'as', fnamemodify(repo, ':t:s?\.git$??')) | ||
518 | let spec = extend(s:infer_properties(name, repo), opts) | ||
519 | if !has_key(g:plugs, name) | ||
520 | call add(g:plugs_order, name) | ||
521 | endif | ||
522 | let g:plugs[name] = spec | ||
523 | let s:loaded[name] = get(s:loaded, name, 0) | ||
524 | catch | ||
525 | return s:err(v:exception) | ||
526 | endtry | ||
527 | endfunction | ||
528 | |||
529 | function! s:parse_options(arg) | ||
530 | let opts = copy(s:base_spec) | ||
531 | let type = type(a:arg) | ||
532 | if type == s:TYPE.string | ||
533 | let opts.tag = a:arg | ||
534 | elseif type == s:TYPE.dict | ||
535 | call extend(opts, a:arg) | ||
536 | if has_key(opts, 'dir') | ||
537 | let opts.dir = s:dirpath(expand(opts.dir)) | ||
538 | endif | ||
539 | else | ||
540 | throw 'Invalid argument type (expected: string or dictionary)' | ||
541 | endif | ||
542 | return opts | ||
543 | endfunction | ||
544 | |||
545 | function! s:infer_properties(name, repo) | ||
546 | let repo = a:repo | ||
547 | if s:is_local_plug(repo) | ||
548 | return { 'dir': s:dirpath(expand(repo)) } | ||
549 | else | ||
550 | if repo =~ ':' | ||
551 | let uri = repo | ||
552 | else | ||
553 | if repo !~ '/' | ||
554 | let repo = 'vim-scripts/'. repo | ||
555 | endif | ||
556 | let fmt = get(g:, 'plug_url_format', 'https://git::@github.com/%s.git') | ||
557 | let uri = printf(fmt, repo) | ||
558 | endif | ||
559 | let dir = s:dirpath( fnamemodify(join([g:plug_home, a:name], '/'), ':p') ) | ||
560 | return { 'dir': dir, 'uri': uri } | ||
561 | endif | ||
562 | endfunction | ||
563 | |||
564 | function! s:install(force, names) | ||
565 | call s:update_impl(0, a:force, a:names) | ||
566 | endfunction | ||
567 | |||
568 | function! s:update(force, names) | ||
569 | call s:update_impl(1, a:force, a:names) | ||
570 | endfunction | ||
571 | |||
572 | function! plug#helptags() | ||
573 | if !exists('g:plugs') | ||
574 | return s:err('plug#begin was not called') | ||
575 | endif | ||
576 | for spec in values(g:plugs) | ||
577 | let docd = join([spec.dir, 'doc'], '/') | ||
578 | if isdirectory(docd) | ||
579 | silent! execute 'helptags' s:esc(docd) | ||
580 | endif | ||
581 | endfor | ||
582 | return 1 | ||
583 | endfunction | ||
584 | |||
585 | function! s:syntax() | ||
586 | syntax clear | ||
587 | syntax region plug1 start=/\%1l/ end=/\%2l/ contains=plugNumber | ||
588 | syntax region plug2 start=/\%2l/ end=/\%3l/ contains=plugBracket,plugX | ||
589 | syn match plugNumber /[0-9]\+[0-9.]*/ contained | ||
590 | syn match plugBracket /[[\]]/ contained | ||
591 | syn match plugX /x/ contained | ||
592 | syn match plugDash /^-/ | ||
593 | syn match plugPlus /^+/ | ||
594 | syn match plugStar /^*/ | ||
595 | syn match plugMessage /\(^- \)\@<=.*/ | ||
596 | syn match plugName /\(^- \)\@<=[^ ]*:/ | ||
597 | syn match plugSha /\%(: \)\@<=[0-9a-f]\{4,}$/ | ||
598 | syn match plugTag /(tag: [^)]\+)/ | ||
599 | syn match plugInstall /\(^+ \)\@<=[^:]*/ | ||
600 | syn match plugUpdate /\(^* \)\@<=[^:]*/ | ||
601 | syn match plugCommit /^ \X*[0-9a-f]\{7} .*/ contains=plugRelDate,plugEdge,plugTag | ||
602 | syn match plugEdge /^ \X\+$/ | ||
603 | syn match plugEdge /^ \X*/ contained nextgroup=plugSha | ||
604 | syn match plugSha /[0-9a-f]\{7}/ contained | ||
605 | syn match plugRelDate /([^)]*)$/ contained | ||
606 | syn match plugNotLoaded /(not loaded)$/ | ||
607 | syn match plugError /^x.*/ | ||
608 | syn match plugH2 /^.*:\n-\+$/ | ||
609 | syn keyword Function PlugInstall PlugStatus PlugUpdate PlugClean | ||
610 | hi def link plug1 Title | ||
611 | hi def link plug2 Repeat | ||
612 | hi def link plugH2 Type | ||
613 | hi def link plugX Exception | ||
614 | hi def link plugBracket Structure | ||
615 | hi def link plugNumber Number | ||
616 | |||
617 | hi def link plugDash Special | ||
618 | hi def link plugPlus Constant | ||
619 | hi def link plugStar Boolean | ||
620 | |||
621 | hi def link plugMessage Function | ||
622 | hi def link plugName Label | ||
623 | hi def link plugInstall Function | ||
624 | hi def link plugUpdate Type | ||
625 | |||
626 | hi def link plugError Error | ||
627 | hi def link plugRelDate Comment | ||
628 | hi def link plugEdge PreProc | ||
629 | hi def link plugSha Identifier | ||
630 | hi def link plugTag Constant | ||
631 | |||
632 | hi def link plugNotLoaded Comment | ||
633 | endfunction | ||
634 | |||
635 | function! s:lpad(str, len) | ||
636 | return a:str . repeat(' ', a:len - len(a:str)) | ||
637 | endfunction | ||
638 | |||
639 | function! s:lines(msg) | ||
640 | return split(a:msg, "[\r\n]") | ||
641 | endfunction | ||
642 | |||
643 | function! s:lastline(msg) | ||
644 | return get(s:lines(a:msg), -1, '') | ||
645 | endfunction | ||
646 | |||
647 | function! s:new_window() | ||
648 | execute get(g:, 'plug_window', 'vertical topleft new') | ||
649 | endfunction | ||
650 | |||
651 | function! s:plug_window_exists() | ||
652 | let buflist = tabpagebuflist(s:plug_tab) | ||
653 | return !empty(buflist) && index(buflist, s:plug_buf) >= 0 | ||
654 | endfunction | ||
655 | |||
656 | function! s:switch_in() | ||
657 | if !s:plug_window_exists() | ||
658 | return 0 | ||
659 | endif | ||
660 | |||
661 | if winbufnr(0) != s:plug_buf | ||
662 | let s:pos = [tabpagenr(), winnr(), winsaveview()] | ||
663 | execute 'normal!' s:plug_tab.'gt' | ||
664 | let winnr = bufwinnr(s:plug_buf) | ||
665 | execute winnr.'wincmd w' | ||
666 | call add(s:pos, winsaveview()) | ||
667 | else | ||
668 | let s:pos = [winsaveview()] | ||
669 | endif | ||
670 | |||
671 | setlocal modifiable | ||
672 | return 1 | ||
673 | endfunction | ||
674 | |||
675 | function! s:switch_out(...) | ||
676 | call winrestview(s:pos[-1]) | ||
677 | setlocal nomodifiable | ||
678 | if a:0 > 0 | ||
679 | execute a:1 | ||
680 | endif | ||
681 | |||
682 | if len(s:pos) > 1 | ||
683 | execute 'normal!' s:pos[0].'gt' | ||
684 | execute s:pos[1] 'wincmd w' | ||
685 | call winrestview(s:pos[2]) | ||
686 | endif | ||
687 | endfunction | ||
688 | |||
689 | function! s:finish_bindings() | ||
690 | nnoremap <silent> <buffer> R :call <SID>retry()<cr> | ||
691 | nnoremap <silent> <buffer> D :PlugDiff<cr> | ||
692 | nnoremap <silent> <buffer> S :PlugStatus<cr> | ||
693 | nnoremap <silent> <buffer> U :call <SID>status_update()<cr> | ||
694 | xnoremap <silent> <buffer> U :call <SID>status_update()<cr> | ||
695 | nnoremap <silent> <buffer> ]] :silent! call <SID>section('')<cr> | ||
696 | nnoremap <silent> <buffer> [[ :silent! call <SID>section('b')<cr> | ||
697 | endfunction | ||
698 | |||
699 | function! s:prepare(...) | ||
700 | if empty(getcwd()) | ||
701 | throw 'Invalid current working directory. Cannot proceed.' | ||
702 | endif | ||
703 | |||
704 | call s:job_abort() | ||
705 | if s:switch_in() | ||
706 | normal q | ||
707 | endif | ||
708 | |||
709 | call s:new_window() | ||
710 | nnoremap <silent> <buffer> q :if b:plug_preview==1<bar>pc<bar>endif<bar>bd<cr> | ||
711 | if a:0 == 0 | ||
712 | call s:finish_bindings() | ||
713 | endif | ||
714 | let b:plug_preview = -1 | ||
715 | let s:plug_tab = tabpagenr() | ||
716 | let s:plug_buf = winbufnr(0) | ||
717 | call s:assign_name() | ||
718 | |||
719 | silent! unmap <buffer> <cr> | ||
720 | silent! unmap <buffer> L | ||
721 | silent! unmap <buffer> o | ||
722 | silent! unmap <buffer> X | ||
723 | setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile nowrap cursorline modifiable | ||
724 | setf vim-plug | ||
725 | if exists('g:syntax_on') | ||
726 | call s:syntax() | ||
727 | endif | ||
728 | endfunction | ||
729 | |||
730 | function! s:assign_name() | ||
731 | " Assign buffer name | ||
732 | let prefix = '[Plugins]' | ||
733 | let name = prefix | ||
734 | let idx = 2 | ||
735 | while bufexists(name) | ||
736 | let name = printf('%s (%s)', prefix, idx) | ||
737 | let idx = idx + 1 | ||
738 | endwhile | ||
739 | silent! execute 'f' fnameescape(name) | ||
740 | endfunction | ||
741 | |||
742 | function! s:chsh(swap) | ||
743 | let prev = [&shell, &shellredir] | ||
744 | if !s:is_win && a:swap | ||
745 | set shell=sh shellredir=>%s\ 2>&1 | ||
746 | endif | ||
747 | return prev | ||
748 | endfunction | ||
749 | |||
750 | function! s:bang(cmd, ...) | ||
751 | try | ||
752 | let [sh, shrd] = s:chsh(a:0) | ||
753 | " FIXME: Escaping is incomplete. We could use shellescape with eval, | ||
754 | " but it won't work on Windows. | ||
755 | let cmd = a:0 ? s:with_cd(a:cmd, a:1) : a:cmd | ||
756 | let g:_plug_bang = '!'.escape(cmd, '#!%') | ||
757 | execute "normal! :execute g:_plug_bang\<cr>\<cr>" | ||
758 | finally | ||
759 | unlet g:_plug_bang | ||
760 | let [&shell, &shellredir] = [sh, shrd] | ||
761 | endtry | ||
762 | return v:shell_error ? 'Exit status: ' . v:shell_error : '' | ||
763 | endfunction | ||
764 | |||
765 | function! s:regress_bar() | ||
766 | let bar = substitute(getline(2)[1:-2], '.*\zs=', 'x', '') | ||
767 | call s:progress_bar(2, bar, len(bar)) | ||
768 | endfunction | ||
769 | |||
770 | function! s:is_updated(dir) | ||
771 | return !empty(s:system_chomp('git log --pretty=format:"%h" "HEAD...HEAD@{1}"', a:dir)) | ||
772 | endfunction | ||
773 | |||
774 | function! s:do(pull, force, todo) | ||
775 | for [name, spec] in items(a:todo) | ||
776 | if !isdirectory(spec.dir) | ||
777 | continue | ||
778 | endif | ||
779 | let installed = has_key(s:update.new, name) | ||
780 | let updated = installed ? 0 : | ||
781 | \ (a:pull && index(s:update.errors, name) < 0 && s:is_updated(spec.dir)) | ||
782 | if a:force || installed || updated | ||
783 | execute 'cd' s:esc(spec.dir) | ||
784 | call append(3, '- Post-update hook for '. name .' ... ') | ||
785 | let error = '' | ||
786 | let type = type(spec.do) | ||
787 | if type == s:TYPE.string | ||
788 | let error = s:bang(spec.do) | ||
789 | elseif type == s:TYPE.funcref | ||
790 | try | ||
791 | let status = installed ? 'installed' : (updated ? 'updated' : 'unchanged') | ||
792 | call spec.do({ 'name': name, 'status': status, 'force': a:force }) | ||
793 | catch | ||
794 | let error = v:exception | ||
795 | endtry | ||
796 | else | ||
797 | let error = 'Invalid hook type' | ||
798 | endif | ||
799 | call s:switch_in() | ||
800 | call setline(4, empty(error) ? (getline(4) . 'OK') | ||
801 | \ : ('x' . getline(4)[1:] . error)) | ||
802 | if !empty(error) | ||
803 | call add(s:update.errors, name) | ||
804 | call s:regress_bar() | ||
805 | endif | ||
806 | cd - | ||
807 | endif | ||
808 | endfor | ||
809 | endfunction | ||
810 | |||
811 | function! s:hash_match(a, b) | ||
812 | return stridx(a:a, a:b) == 0 || stridx(a:b, a:a) == 0 | ||
813 | endfunction | ||
814 | |||
815 | function! s:checkout(spec) | ||
816 | let sha = a:spec.commit | ||
817 | let output = s:system('git rev-parse HEAD', a:spec.dir) | ||
818 | if !v:shell_error && !s:hash_match(sha, s:lines(output)[0]) | ||
819 | let output = s:system( | ||
820 | \ 'git fetch --depth 999999 && git checkout '.s:esc(sha), a:spec.dir) | ||
821 | endif | ||
822 | return output | ||
823 | endfunction | ||
824 | |||
825 | function! s:finish(pull) | ||
826 | let new_frozen = len(filter(keys(s:update.new), 'g:plugs[v:val].frozen')) | ||
827 | if new_frozen | ||
828 | let s = new_frozen > 1 ? 's' : '' | ||
829 | call append(3, printf('- Installed %d frozen plugin%s', new_frozen, s)) | ||
830 | endif | ||
831 | call append(3, '- Finishing ... ') | 4 | ||
832 | redraw | ||
833 | call plug#helptags() | ||
834 | call plug#end() | ||
835 | call setline(4, getline(4) . 'Done!') | ||
836 | redraw | ||
837 | let msgs = [] | ||
838 | if !empty(s:update.errors) | ||
839 | call add(msgs, "Press 'R' to retry.") | ||
840 | endif | ||
841 | if a:pull && len(s:update.new) < len(filter(getline(5, '$'), | ||
842 | \ "v:val =~ '^- ' && stridx(v:val, 'Already up-to-date') < 0")) | ||
843 | call add(msgs, "Press 'D' to see the updated changes.") | ||
844 | endif | ||
845 | echo join(msgs, ' ') | ||
846 | call s:finish_bindings() | ||
847 | endfunction | ||
848 | |||
849 | function! s:retry() | ||
850 | if empty(s:update.errors) | ||
851 | return | ||
852 | endif | ||
853 | echo | ||
854 | call s:update_impl(s:update.pull, s:update.force, | ||
855 | \ extend(copy(s:update.errors), [s:update.threads])) | ||
856 | endfunction | ||
857 | |||
858 | function! s:is_managed(name) | ||
859 | return has_key(g:plugs[a:name], 'uri') | ||
860 | endfunction | ||
861 | |||
862 | function! s:names(...) | ||
863 | return sort(filter(keys(g:plugs), 'stridx(v:val, a:1) == 0 && s:is_managed(v:val)')) | ||
864 | endfunction | ||
865 | |||
866 | function! s:check_ruby() | ||
867 | silent! ruby require 'thread'; VIM::command("let g:plug_ruby = '#{RUBY_VERSION}'") | ||
868 | if !exists('g:plug_ruby') | ||
869 | redraw! | ||
870 | return s:warn('echom', 'Warning: Ruby interface is broken') | ||
871 | endif | ||
872 | let ruby_version = split(g:plug_ruby, '\.') | ||
873 | unlet g:plug_ruby | ||
874 | return s:version_requirement(ruby_version, [1, 8, 7]) | ||
875 | endfunction | ||
876 | |||
877 | function! s:update_impl(pull, force, args) abort | ||
878 | let args = copy(a:args) | ||
879 | let threads = (len(args) > 0 && args[-1] =~ '^[1-9][0-9]*$') ? | ||
880 | \ remove(args, -1) : get(g:, 'plug_threads', 16) | ||
881 | |||
882 | let managed = filter(copy(g:plugs), 's:is_managed(v:key)') | ||
883 | let todo = empty(args) ? filter(managed, '!v:val.frozen || !isdirectory(v:val.dir)') : | ||
884 | \ filter(managed, 'index(args, v:key) >= 0') | ||
885 | |||
886 | if empty(todo) | ||
887 | return s:warn('echo', 'No plugin to '. (a:pull ? 'update' : 'install')) | ||
888 | endif | ||
889 | |||
890 | if !s:is_win && s:git_version_requirement(2, 3) | ||
891 | let s:git_terminal_prompt = exists('$GIT_TERMINAL_PROMPT') ? $GIT_TERMINAL_PROMPT : '' | ||
892 | let $GIT_TERMINAL_PROMPT = 0 | ||
893 | for plug in values(todo) | ||
894 | let plug.uri = substitute(plug.uri, | ||
895 | \ '^https://git::@github\.com', 'https://github.com', '') | ||
896 | endfor | ||
897 | endif | ||
898 | |||
899 | if !isdirectory(g:plug_home) | ||
900 | try | ||
901 | call mkdir(g:plug_home, 'p') | ||
902 | catch | ||
903 | return s:err(printf('Invalid plug directory: %s. '. | ||
904 | \ 'Try to call plug#begin with a valid directory', g:plug_home)) | ||
905 | endtry | ||
906 | endif | ||
907 | |||
908 | if has('nvim') && !exists('*jobwait') && threads > 1 | ||
909 | call s:warn('echom', '[vim-plug] Update Neovim for parallel installer') | ||
910 | endif | ||
911 | |||
912 | let python = (has('python') || has('python3')) && (!s:nvim || has('vim_starting')) | ||
913 | let ruby = has('ruby') && !s:nvim && (v:version >= 703 || v:version == 702 && has('patch374')) && !(s:is_win && has('gui_running')) && s:check_ruby() | ||
914 | |||
915 | let s:update = { | ||
916 | \ 'start': reltime(), | ||
917 | \ 'all': todo, | ||
918 | \ 'todo': copy(todo), | ||
919 | \ 'errors': [], | ||
920 | \ 'pull': a:pull, | ||
921 | \ 'force': a:force, | ||
922 | \ 'new': {}, | ||
923 | \ 'threads': (python || ruby || s:nvim) ? min([len(todo), threads]) : 1, | ||
924 | \ 'bar': '', | ||
925 | \ 'fin': 0 | ||
926 | \ } | ||
927 | |||
928 | call s:prepare(1) | ||
929 | call append(0, ['', '']) | ||
930 | normal! 2G | ||
931 | silent! redraw | ||
932 | |||
933 | let s:clone_opt = get(g:, 'plug_shallow', 1) ? | ||
934 | \ '--depth 1' . (s:git_version_requirement(1, 7, 10) ? ' --no-single-branch' : '') : '' | ||
935 | |||
936 | " Python version requirement (>= 2.7) | ||
937 | if python && !has('python3') && !ruby && !s:nvim && s:update.threads > 1 | ||
938 | redir => pyv | ||
939 | silent python import platform; print platform.python_version() | ||
940 | redir END | ||
941 | let python = s:version_requirement( | ||
942 | \ map(split(split(pyv)[0], '\.'), 'str2nr(v:val)'), [2, 6]) | ||
943 | endif | ||
944 | |||
945 | if (python || ruby) && s:update.threads > 1 | ||
946 | try | ||
947 | let imd = &imd | ||
948 | if s:mac_gui | ||
949 | set noimd | ||
950 | endif | ||
951 | if ruby | ||
952 | call s:update_ruby() | ||
953 | else | ||
954 | call s:update_python() | ||
955 | endif | ||
956 | catch | ||
957 | let lines = getline(4, '$') | ||
958 | let printed = {} | ||
959 | silent! 4,$d _ | ||
960 | for line in lines | ||
961 | let name = s:extract_name(line, '.', '') | ||
962 | if empty(name) || !has_key(printed, name) | ||
963 | call append('$', line) | ||
964 | if !empty(name) | ||
965 | let printed[name] = 1 | ||
966 | if line[0] == 'x' && index(s:update.errors, name) < 0 | ||
967 | call add(s:update.errors, name) | ||
968 | end | ||
969 | endif | ||
970 | endif | ||
971 | endfor | ||
972 | finally | ||
973 | let &imd = imd | ||
974 | call s:update_finish() | ||
975 | endtry | ||
976 | else | ||
977 | call s:update_vim() | ||
978 | endif | ||
979 | endfunction | ||
980 | |||
981 | function! s:log4(name, msg) | ||
982 | call setline(4, printf('- %s (%s)', a:msg, a:name)) | ||
983 | redraw | ||
984 | endfunction | ||
985 | |||
986 | function! s:update_finish() | ||
987 | if exists('s:git_terminal_prompt') | ||
988 | let $GIT_TERMINAL_PROMPT = s:git_terminal_prompt | ||
989 | endif | ||
990 | if s:switch_in() | ||
991 | call append(3, '- Updating ...') | 4 | ||
992 | 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))')) | ||
993 | let pos = s:logpos(name) | ||
994 | if !pos | ||
995 | continue | ||
996 | endif | ||
997 | if has_key(spec, 'commit') | ||
998 | call s:log4(name, 'Checking out '.spec.commit) | ||
999 | let out = s:checkout(spec) | ||
1000 | elseif has_key(spec, 'tag') | ||
1001 | let tag = spec.tag | ||
1002 | if tag =~ '\*' | ||
1003 | let tags = s:lines(s:system('git tag --list '.string(tag).' --sort -version:refname 2>&1', spec.dir)) | ||
1004 | if !v:shell_error && !empty(tags) | ||
1005 | let tag = tags[0] | ||
1006 | call s:log4(name, printf('Latest tag for %s -> %s', spec.tag, tag)) | ||
1007 | call append(3, '') | ||
1008 | endif | ||
1009 | endif | ||
1010 | call s:log4(name, 'Checking out '.tag) | ||
1011 | let out = s:system('git checkout -q '.s:esc(tag).' 2>&1', spec.dir) | ||
1012 | else | ||
1013 | let branch = s:esc(get(spec, 'branch', 'master')) | ||
1014 | call s:log4(name, 'Merging origin/'.branch) | ||
1015 | let out = s:system('git checkout -q '.branch.' 2>&1' | ||
1016 | \. (has_key(s:update.new, name) ? '' : ('&& git merge --ff-only origin/'.branch.' 2>&1')), spec.dir) | ||
1017 | endif | ||
1018 | if !v:shell_error && filereadable(spec.dir.'/.gitmodules') && | ||
1019 | \ (s:update.force || has_key(s:update.new, name) || s:is_updated(spec.dir)) | ||
1020 | call s:log4(name, 'Updating submodules. This may take a while.') | ||
1021 | let out .= s:bang('git submodule update --init --recursive 2>&1', spec.dir) | ||
1022 | endif | ||
1023 | let msg = printf('%s %s: %s', v:shell_error ? 'x': '-', name, s:lastline(out)) | ||
1024 | if v:shell_error | ||
1025 | call add(s:update.errors, name) | ||
1026 | call s:regress_bar() | ||
1027 | execute pos 'd _' | ||
1028 | call append(4, msg) | 4 | ||
1029 | elseif !empty(out) | ||
1030 | call setline(pos, msg) | ||
1031 | endif | ||
1032 | redraw | ||
1033 | endfor | ||
1034 | 4 d _ | ||
1035 | 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")')) | ||
1036 | call s:finish(s:update.pull) | ||
1037 | call setline(1, 'Updated. Elapsed time: ' . split(reltimestr(reltime(s:update.start)))[0] . ' sec.') | ||
1038 | call s:switch_out('normal! gg') | ||
1039 | endif | ||
1040 | endfunction | ||
1041 | |||
1042 | function! s:job_abort() | ||
1043 | if !s:nvim || !exists('s:jobs') | ||
1044 | return | ||
1045 | endif | ||
1046 | |||
1047 | for [name, j] in items(s:jobs) | ||
1048 | silent! call jobstop(j.jobid) | ||
1049 | if j.new | ||
1050 | call s:system('rm -rf ' . s:shellesc(g:plugs[name].dir)) | ||
1051 | endif | ||
1052 | endfor | ||
1053 | let s:jobs = {} | ||
1054 | endfunction | ||
1055 | |||
1056 | " When a:event == 'stdout', data = list of strings | ||
1057 | " When a:event == 'exit', data = returncode | ||
1058 | function! s:job_handler(job_id, data, event) abort | ||
1059 | if !s:plug_window_exists() " plug window closed | ||
1060 | return s:job_abort() | ||
1061 | endif | ||
1062 | |||
1063 | if a:event == 'stdout' | ||
1064 | let complete = empty(a:data[-1]) | ||
1065 | let lines = map(filter(a:data, 'len(v:val) > 0'), 'split(v:val, "[\r\n]")[-1]') | ||
1066 | call extend(self.lines, lines) | ||
1067 | let self.result = join(self.lines, "\n") | ||
1068 | if !complete | ||
1069 | call remove(self.lines, -1) | ||
1070 | endif | ||
1071 | " To reduce the number of buffer updates | ||
1072 | let self.tick = get(self, 'tick', -1) + 1 | ||
1073 | if self.tick % len(s:jobs) == 0 | ||
1074 | call s:log(self.new ? '+' : '*', self.name, self.result) | ||
1075 | endif | ||
1076 | elseif a:event == 'exit' | ||
1077 | let self.running = 0 | ||
1078 | if a:data != 0 | ||
1079 | let self.error = 1 | ||
1080 | endif | ||
1081 | call s:reap(self.name) | ||
1082 | call s:tick() | ||
1083 | endif | ||
1084 | endfunction | ||
1085 | |||
1086 | function! s:spawn(name, cmd, opts) | ||
1087 | let job = { 'name': a:name, 'running': 1, 'error': 0, 'lines': [], 'result': '', | ||
1088 | \ 'new': get(a:opts, 'new', 0), | ||
1089 | \ 'on_stdout': function('s:job_handler'), | ||
1090 | \ 'on_exit' : function('s:job_handler'), | ||
1091 | \ } | ||
1092 | let s:jobs[a:name] = job | ||
1093 | |||
1094 | if s:nvim | ||
1095 | let argv = [ 'sh', '-c', | ||
1096 | \ (has_key(a:opts, 'dir') ? s:with_cd(a:cmd, a:opts.dir) : a:cmd) ] | ||
1097 | let jid = jobstart(argv, job) | ||
1098 | if jid > 0 | ||
1099 | let job.jobid = jid | ||
1100 | else | ||
1101 | let job.running = 0 | ||
1102 | let job.error = 1 | ||
1103 | let job.result = jid < 0 ? 'sh is not executable' : | ||
1104 | \ 'Invalid arguments (or job table is full)' | ||
1105 | endif | ||
1106 | else | ||
1107 | let params = has_key(a:opts, 'dir') ? [a:cmd, a:opts.dir] : [a:cmd] | ||
1108 | let job.result = call('s:system', params) | ||
1109 | let job.error = v:shell_error != 0 | ||
1110 | let job.running = 0 | ||
1111 | endif | ||
1112 | endfunction | ||
1113 | |||
1114 | function! s:reap(name) | ||
1115 | let job = s:jobs[a:name] | ||
1116 | if job.error | ||
1117 | call add(s:update.errors, a:name) | ||
1118 | elseif get(job, 'new', 0) | ||
1119 | let s:update.new[a:name] = 1 | ||
1120 | endif | ||
1121 | let s:update.bar .= job.error ? 'x' : '=' | ||
1122 | |||
1123 | call s:log(job.error ? 'x' : '-', a:name, empty(job.result) ? 'OK' : job.result) | ||
1124 | call s:bar() | ||
1125 | |||
1126 | call remove(s:jobs, a:name) | ||
1127 | endfunction | ||
1128 | |||
1129 | function! s:bar() | ||
1130 | if s:switch_in() | ||
1131 | let total = len(s:update.all) | ||
1132 | call setline(1, (s:update.pull ? 'Updating' : 'Installing'). | ||
1133 | \ ' plugins ('.len(s:update.bar).'/'.total.')') | ||
1134 | call s:progress_bar(2, s:update.bar, total) | ||
1135 | call s:switch_out() | ||
1136 | endif | ||
1137 | endfunction | ||
1138 | |||
1139 | function! s:logpos(name) | ||
1140 | for i in range(4, line('$')) | ||
1141 | if getline(i) =~# '^[-+x*] '.a:name.':' | ||
1142 | return i | ||
1143 | endif | ||
1144 | endfor | ||
1145 | endfunction | ||
1146 | |||
1147 | function! s:log(bullet, name, lines) | ||
1148 | if s:switch_in() | ||
1149 | let pos = s:logpos(a:name) | ||
1150 | if pos > 0 | ||
1151 | execute pos 'd _' | ||
1152 | if pos > winheight('.') | ||
1153 | let pos = 4 | ||
1154 | endif | ||
1155 | else | ||
1156 | let pos = 4 | ||
1157 | endif | ||
1158 | call append(pos - 1, s:format_message(a:bullet, a:name, a:lines)) | ||
1159 | call s:switch_out() | ||
1160 | endif | ||
1161 | endfunction | ||
1162 | |||
1163 | function! s:update_vim() | ||
1164 | let s:jobs = {} | ||
1165 | |||
1166 | call s:bar() | ||
1167 | call s:tick() | ||
1168 | endfunction | ||
1169 | |||
1170 | function! s:tick() | ||
1171 | let pull = s:update.pull | ||
1172 | let prog = s:progress_opt(s:nvim) | ||
1173 | while 1 " Without TCO, Vim stack is bound to explode | ||
1174 | if empty(s:update.todo) | ||
1175 | if empty(s:jobs) && !s:update.fin | ||
1176 | let s:update.fin = 1 | ||
1177 | call s:update_finish() | ||
1178 | endif | ||
1179 | return | ||
1180 | endif | ||
1181 | |||
1182 | let name = keys(s:update.todo)[0] | ||
1183 | let spec = remove(s:update.todo, name) | ||
1184 | let new = !isdirectory(spec.dir) | ||
1185 | |||
1186 | call s:log(new ? '+' : '*', name, pull ? 'Updating ...' : 'Installing ...') | ||
1187 | redraw | ||
1188 | |||
1189 | let has_tag = has_key(spec, 'tag') | ||
1190 | if !new | ||
1191 | let [error, _] = s:git_validate(spec, 0) | ||
1192 | if empty(error) | ||
1193 | if pull | ||
1194 | let fetch_opt = (has_tag && !empty(globpath(spec.dir, '.git/shallow'))) ? '--depth 99999999' : '' | ||
1195 | call s:spawn(name, printf('git fetch %s %s 2>&1', fetch_opt, prog), { 'dir': spec.dir }) | ||
1196 | else | ||
1197 | let s:jobs[name] = { 'running': 0, 'result': 'Already installed', 'error': 0 } | ||
1198 | endif | ||
1199 | else | ||
1200 | let s:jobs[name] = { 'running': 0, 'result': error, 'error': 1 } | ||
1201 | endif | ||
1202 | else | ||
1203 | call s:spawn(name, | ||
1204 | \ printf('git clone %s %s %s %s 2>&1', | ||
1205 | \ has_tag ? '' : s:clone_opt, | ||
1206 | \ prog, | ||
1207 | \ s:shellesc(spec.uri), | ||
1208 | \ s:shellesc(s:trim(spec.dir))), { 'new': 1 }) | ||
1209 | endif | ||
1210 | |||
1211 | if !s:jobs[name].running | ||
1212 | call s:reap(name) | ||
1213 | endif | ||
1214 | if len(s:jobs) >= s:update.threads | ||
1215 | break | ||
1216 | endif | ||
1217 | endwhile | ||
1218 | endfunction | ||
1219 | |||
1220 | function! s:update_python() | ||
1221 | let py_exe = has('python') ? 'python' : 'python3' | ||
1222 | execute py_exe "<< EOF" | ||
1223 | import datetime | ||
1224 | import functools | ||
1225 | import os | ||
1226 | try: | ||
1227 | import queue | ||
1228 | except ImportError: | ||
1229 | import Queue as queue | ||
1230 | import random | ||
1231 | import re | ||
1232 | import shutil | ||
1233 | import signal | ||
1234 | import subprocess | ||
1235 | import tempfile | ||
1236 | import threading as thr | ||
1237 | import time | ||
1238 | import traceback | ||
1239 | import vim | ||
1240 | |||
1241 | G_NVIM = vim.eval("has('nvim')") == '1' | ||
1242 | G_PULL = vim.eval('s:update.pull') == '1' | ||
1243 | G_RETRIES = int(vim.eval('get(g:, "plug_retries", 2)')) + 1 | ||
1244 | G_TIMEOUT = int(vim.eval('get(g:, "plug_timeout", 60)')) | ||
1245 | G_CLONE_OPT = vim.eval('s:clone_opt') | ||
1246 | G_PROGRESS = vim.eval('s:progress_opt(1)') | ||
1247 | G_LOG_PROB = 1.0 / int(vim.eval('s:update.threads')) | ||
1248 | G_STOP = thr.Event() | ||
1249 | G_IS_WIN = vim.eval('s:is_win') == '1' | ||
1250 | |||
1251 | class PlugError(Exception): | ||
1252 | def __init__(self, msg): | ||
1253 | self.msg = msg | ||
1254 | class CmdTimedOut(PlugError): | ||
1255 | pass | ||
1256 | class CmdFailed(PlugError): | ||
1257 | pass | ||
1258 | class InvalidURI(PlugError): | ||
1259 | pass | ||
1260 | class Action(object): | ||
1261 | INSTALL, UPDATE, ERROR, DONE = ['+', '*', 'x', '-'] | ||
1262 | |||
1263 | class Buffer(object): | ||
1264 | def __init__(self, lock, num_plugs, is_pull): | ||
1265 | self.bar = '' | ||
1266 | self.event = 'Updating' if is_pull else 'Installing' | ||
1267 | self.lock = lock | ||
1268 | self.maxy = int(vim.eval('winheight(".")')) | ||
1269 | self.num_plugs = num_plugs | ||
1270 | |||
1271 | def __where(self, name): | ||
1272 | """ Find first line with name in current buffer. Return line num. """ | ||
1273 | found, lnum = False, 0 | ||
1274 | matcher = re.compile('^[-+x*] {0}:'.format(name)) | ||
1275 | for line in vim.current.buffer: | ||
1276 | if matcher.search(line) is not None: | ||
1277 | found = True | ||
1278 | break | ||
1279 | lnum += 1 | ||
1280 | |||
1281 | if not found: | ||
1282 | lnum = -1 | ||
1283 | return lnum | ||
1284 | |||
1285 | def header(self): | ||
1286 | curbuf = vim.current.buffer | ||
1287 | curbuf[0] = self.event + ' plugins ({0}/{1})'.format(len(self.bar), self.num_plugs) | ||
1288 | |||
1289 | num_spaces = self.num_plugs - len(self.bar) | ||
1290 | curbuf[1] = '[{0}{1}]'.format(self.bar, num_spaces * ' ') | ||
1291 | |||
1292 | with self.lock: | ||
1293 | vim.command('normal! 2G') | ||
1294 | vim.command('redraw') | ||
1295 | |||
1296 | def write(self, action, name, lines): | ||
1297 | first, rest = lines[0], lines[1:] | ||
1298 | msg = ['{0} {1}{2}{3}'.format(action, name, ': ' if first else '', first)] | ||
1299 | msg.extend([' ' + line for line in rest]) | ||
1300 | |||
1301 | try: | ||
1302 | if action == Action.ERROR: | ||
1303 | self.bar += 'x' | ||
1304 | vim.command("call add(s:update.errors, '{0}')".format(name)) | ||
1305 | elif action == Action.DONE: | ||
1306 | self.bar += '=' | ||
1307 | |||
1308 | curbuf = vim.current.buffer | ||
1309 | lnum = self.__where(name) | ||
1310 | if lnum != -1: # Found matching line num | ||
1311 | del curbuf[lnum] | ||
1312 | if lnum > self.maxy and action in set([Action.INSTALL, Action.UPDATE]): | ||
1313 | lnum = 3 | ||
1314 | else: | ||
1315 | lnum = 3 | ||
1316 | curbuf.append(msg, lnum) | ||
1317 | |||
1318 | self.header() | ||
1319 | except vim.error: | ||
1320 | pass | ||
1321 | |||
1322 | class Command(object): | ||
1323 | CD = 'cd /d' if G_IS_WIN else 'cd' | ||
1324 | |||
1325 | def __init__(self, cmd, cmd_dir=None, timeout=60, cb=None, clean=None): | ||
1326 | self.cmd = cmd | ||
1327 | if cmd_dir: | ||
1328 | self.cmd = '{0} {1} && {2}'.format(Command.CD, cmd_dir, self.cmd) | ||
1329 | self.timeout = timeout | ||
1330 | self.callback = cb if cb else (lambda msg: None) | ||
1331 | self.clean = clean if clean else (lambda: None) | ||
1332 | self.proc = None | ||
1333 | |||
1334 | @property | ||
1335 | def alive(self): | ||
1336 | """ Returns true only if command still running. """ | ||
1337 | return self.proc and self.proc.poll() is None | ||
1338 | |||
1339 | def execute(self, ntries=3): | ||
1340 | """ Execute the command with ntries if CmdTimedOut. | ||
1341 | Returns the output of the command if no Exception. | ||
1342 | """ | ||
1343 | attempt, finished, limit = 0, False, self.timeout | ||
1344 | |||
1345 | while not finished: | ||
1346 | try: | ||
1347 | attempt += 1 | ||
1348 | result = self.try_command() | ||
1349 | finished = True | ||
1350 | return result | ||
1351 | except CmdTimedOut: | ||
1352 | if attempt != ntries: | ||
1353 | self.notify_retry() | ||
1354 | self.timeout += limit | ||
1355 | else: | ||
1356 | raise | ||
1357 | |||
1358 | def notify_retry(self): | ||
1359 | """ Retry required for command, notify user. """ | ||
1360 | for count in range(3, 0, -1): | ||
1361 | if G_STOP.is_set(): | ||
1362 | raise KeyboardInterrupt | ||
1363 | msg = 'Timeout. Will retry in {0} second{1} ...'.format( | ||
1364 | count, 's' if count != 1 else '') | ||
1365 | self.callback([msg]) | ||
1366 | time.sleep(1) | ||
1367 | self.callback(['Retrying ...']) | ||
1368 | |||
1369 | def try_command(self): | ||
1370 | """ Execute a cmd & poll for callback. Returns list of output. | ||
1371 | Raises CmdFailed -> return code for Popen isn't 0 | ||
1372 | Raises CmdTimedOut -> command exceeded timeout without new output | ||
1373 | """ | ||
1374 | first_line = True | ||
1375 | |||
1376 | try: | ||
1377 | tfile = tempfile.NamedTemporaryFile(mode='w+b') | ||
1378 | preexec_fn = not G_IS_WIN and os.setsid or None | ||
1379 | self.proc = subprocess.Popen(self.cmd, stdout=tfile, | ||
1380 | stderr=subprocess.STDOUT, | ||
1381 | stdin=subprocess.PIPE, shell=True, | ||
1382 | preexec_fn=preexec_fn) | ||
1383 | thrd = thr.Thread(target=(lambda proc: proc.wait()), args=(self.proc,)) | ||
1384 | thrd.start() | ||
1385 | |||
1386 | thread_not_started = True | ||
1387 | while thread_not_started: | ||
1388 | try: | ||
1389 | thrd.join(0.1) | ||
1390 | thread_not_started = False | ||
1391 | except RuntimeError: | ||
1392 | pass | ||
1393 | |||
1394 | while self.alive: | ||
1395 | if G_STOP.is_set(): | ||
1396 | raise KeyboardInterrupt | ||
1397 | |||
1398 | if first_line or random.random() < G_LOG_PROB: | ||
1399 | first_line = False | ||
1400 | line = '' if G_IS_WIN else nonblock_read(tfile.name) | ||
1401 | if line: | ||
1402 | self.callback([line]) | ||
1403 | |||
1404 | time_diff = time.time() - os.path.getmtime(tfile.name) | ||
1405 | if time_diff > self.timeout: | ||
1406 | raise CmdTimedOut(['Timeout!']) | ||
1407 | |||
1408 | thrd.join(0.5) | ||
1409 | |||
1410 | tfile.seek(0) | ||
1411 | result = [line.decode('utf-8', 'replace').rstrip() for line in tfile] | ||
1412 | |||
1413 | if self.proc.returncode != 0: | ||
1414 | raise CmdFailed([''] + result) | ||
1415 | |||
1416 | return result | ||
1417 | except: | ||
1418 | self.terminate() | ||
1419 | raise | ||
1420 | |||
1421 | def terminate(self): | ||
1422 | """ Terminate process and cleanup. """ | ||
1423 | if self.alive: | ||
1424 | if G_IS_WIN: | ||
1425 | os.kill(self.proc.pid, signal.SIGINT) | ||
1426 | else: | ||
1427 | os.killpg(self.proc.pid, signal.SIGTERM) | ||
1428 | self.clean() | ||
1429 | |||
1430 | class Plugin(object): | ||
1431 | def __init__(self, name, args, buf_q, lock): | ||
1432 | self.name = name | ||
1433 | self.args = args | ||
1434 | self.buf_q = buf_q | ||
1435 | self.lock = lock | ||
1436 | self.tag = args.get('tag', 0) | ||
1437 | |||
1438 | def manage(self): | ||
1439 | try: | ||
1440 | if os.path.exists(self.args['dir']): | ||
1441 | self.update() | ||
1442 | else: | ||
1443 | self.install() | ||
1444 | with self.lock: | ||
1445 | thread_vim_command("let s:update.new['{0}'] = 1".format(self.name)) | ||
1446 | except PlugError as exc: | ||
1447 | self.write(Action.ERROR, self.name, exc.msg) | ||
1448 | except KeyboardInterrupt: | ||
1449 | G_STOP.set() | ||
1450 | self.write(Action.ERROR, self.name, ['Interrupted!']) | ||
1451 | except: | ||
1452 | # Any exception except those above print stack trace | ||
1453 | msg = 'Trace:\n{0}'.format(traceback.format_exc().rstrip()) | ||
1454 | self.write(Action.ERROR, self.name, msg.split('\n')) | ||
1455 | raise | ||
1456 | |||
1457 | def install(self): | ||
1458 | target = self.args['dir'] | ||
1459 | if target[-1] == '\\': | ||
1460 | target = target[0:-1] | ||
1461 | |||
1462 | def clean(target): | ||
1463 | def _clean(): | ||
1464 | try: | ||
1465 | shutil.rmtree(target) | ||
1466 | except OSError: | ||
1467 | pass | ||
1468 | return _clean | ||
1469 | |||
1470 | self.write(Action.INSTALL, self.name, ['Installing ...']) | ||
1471 | callback = functools.partial(self.write, Action.INSTALL, self.name) | ||
1472 | cmd = 'git clone {0} {1} {2} {3} 2>&1'.format( | ||
1473 | '' if self.tag else G_CLONE_OPT, G_PROGRESS, self.args['uri'], | ||
1474 | esc(target)) | ||
1475 | com = Command(cmd, None, G_TIMEOUT, callback, clean(target)) | ||
1476 | result = com.execute(G_RETRIES) | ||
1477 | self.write(Action.DONE, self.name, result[-1:]) | ||
1478 | |||
1479 | def repo_uri(self): | ||
1480 | cmd = 'git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url' | ||
1481 | command = Command(cmd, self.args['dir'], G_TIMEOUT,) | ||
1482 | result = command.execute(G_RETRIES) | ||
1483 | return result[-1] | ||
1484 | |||
1485 | def update(self): | ||
1486 | match = re.compile(r'git::?@') | ||
1487 | actual_uri = re.sub(match, '', self.repo_uri()) | ||
1488 | expect_uri = re.sub(match, '', self.args['uri']) | ||
1489 | if actual_uri != expect_uri: | ||
1490 | msg = ['', | ||
1491 | 'Invalid URI: {0}'.format(actual_uri), | ||
1492 | 'Expected {0}'.format(expect_uri), | ||
1493 | 'PlugClean required.'] | ||
1494 | raise InvalidURI(msg) | ||
1495 | |||
1496 | if G_PULL: | ||
1497 | self.write(Action.UPDATE, self.name, ['Updating ...']) | ||
1498 | callback = functools.partial(self.write, Action.UPDATE, self.name) | ||
1499 | fetch_opt = '--depth 99999999' if self.tag and os.path.isfile(os.path.join(self.args['dir'], '.git/shallow')) else '' | ||
1500 | cmd = 'git fetch {0} {1} 2>&1'.format(fetch_opt, G_PROGRESS) | ||
1501 | com = Command(cmd, self.args['dir'], G_TIMEOUT, callback) | ||
1502 | result = com.execute(G_RETRIES) | ||
1503 | self.write(Action.DONE, self.name, result[-1:]) | ||
1504 | else: | ||
1505 | self.write(Action.DONE, self.name, ['Already installed']) | ||
1506 | |||
1507 | def write(self, action, name, msg): | ||
1508 | self.buf_q.put((action, name, msg)) | ||
1509 | |||
1510 | class PlugThread(thr.Thread): | ||
1511 | def __init__(self, tname, args): | ||
1512 | super(PlugThread, self).__init__() | ||
1513 | self.tname = tname | ||
1514 | self.args = args | ||
1515 | |||
1516 | def run(self): | ||
1517 | thr.current_thread().name = self.tname | ||
1518 | buf_q, work_q, lock = self.args | ||
1519 | |||
1520 | try: | ||
1521 | while not G_STOP.is_set(): | ||
1522 | name, args = work_q.get_nowait() | ||
1523 | plug = Plugin(name, args, buf_q, lock) | ||
1524 | plug.manage() | ||
1525 | work_q.task_done() | ||
1526 | except queue.Empty: | ||
1527 | pass | ||
1528 | |||
1529 | class RefreshThread(thr.Thread): | ||
1530 | def __init__(self, lock): | ||
1531 | super(RefreshThread, self).__init__() | ||
1532 | self.lock = lock | ||
1533 | self.running = True | ||
1534 | |||
1535 | def run(self): | ||
1536 | while self.running: | ||
1537 | with self.lock: | ||
1538 | thread_vim_command('noautocmd normal! a') | ||
1539 | time.sleep(0.33) | ||
1540 | |||
1541 | def stop(self): | ||
1542 | self.running = False | ||
1543 | |||
1544 | if G_NVIM: | ||
1545 | def thread_vim_command(cmd): | ||
1546 | vim.session.threadsafe_call(lambda: vim.command(cmd)) | ||
1547 | else: | ||
1548 | def thread_vim_command(cmd): | ||
1549 | vim.command(cmd) | ||
1550 | |||
1551 | def esc(name): | ||
1552 | return '"' + name.replace('"', '\"') + '"' | ||
1553 | |||
1554 | def nonblock_read(fname): | ||
1555 | """ Read a file with nonblock flag. Return the last line. """ | ||
1556 | fread = os.open(fname, os.O_RDONLY | os.O_NONBLOCK) | ||
1557 | buf = os.read(fread, 100000).decode('utf-8', 'replace') | ||
1558 | os.close(fread) | ||
1559 | |||
1560 | line = buf.rstrip('\r\n') | ||
1561 | left = max(line.rfind('\r'), line.rfind('\n')) | ||
1562 | if left != -1: | ||
1563 | left += 1 | ||
1564 | line = line[left:] | ||
1565 | |||
1566 | return line | ||
1567 | |||
1568 | def main(): | ||
1569 | thr.current_thread().name = 'main' | ||
1570 | nthreads = int(vim.eval('s:update.threads')) | ||
1571 | plugs = vim.eval('s:update.todo') | ||
1572 | mac_gui = vim.eval('s:mac_gui') == '1' | ||
1573 | |||
1574 | lock = thr.Lock() | ||
1575 | buf = Buffer(lock, len(plugs), G_PULL) | ||
1576 | buf_q, work_q = queue.Queue(), queue.Queue() | ||
1577 | for work in plugs.items(): | ||
1578 | work_q.put(work) | ||
1579 | |||
1580 | start_cnt = thr.active_count() | ||
1581 | for num in range(nthreads): | ||
1582 | tname = 'PlugT-{0:02}'.format(num) | ||
1583 | thread = PlugThread(tname, (buf_q, work_q, lock)) | ||
1584 | thread.start() | ||
1585 | if mac_gui: | ||
1586 | rthread = RefreshThread(lock) | ||
1587 | rthread.start() | ||
1588 | |||
1589 | while not buf_q.empty() or thr.active_count() != start_cnt: | ||
1590 | try: | ||
1591 | action, name, msg = buf_q.get(True, 0.25) | ||
1592 | buf.write(action, name, ['OK'] if not msg else msg) | ||
1593 | buf_q.task_done() | ||
1594 | except queue.Empty: | ||
1595 | pass | ||
1596 | except KeyboardInterrupt: | ||
1597 | G_STOP.set() | ||
1598 | |||
1599 | if mac_gui: | ||
1600 | rthread.stop() | ||
1601 | rthread.join() | ||
1602 | |||
1603 | main() | ||
1604 | EOF | ||
1605 | endfunction | ||
1606 | |||
1607 | function! s:update_ruby() | ||
1608 | ruby << EOF | ||
1609 | module PlugStream | ||
1610 | SEP = ["\r", "\n", nil] | ||
1611 | def get_line | ||
1612 | buffer = '' | ||
1613 | loop do | ||
1614 | char = readchar rescue return | ||
1615 | if SEP.include? char.chr | ||
1616 | buffer << $/ | ||
1617 | break | ||
1618 | else | ||
1619 | buffer << char | ||
1620 | end | ||
1621 | end | ||
1622 | buffer | ||
1623 | end | ||
1624 | end unless defined?(PlugStream) | ||
1625 | |||
1626 | def esc arg | ||
1627 | %["#{arg.gsub('"', '\"')}"] | ||
1628 | end | ||
1629 | |||
1630 | def killall pid | ||
1631 | pids = [pid] | ||
1632 | if /mswin|mingw|bccwin/ =~ RUBY_PLATFORM | ||
1633 | pids.each { |pid| Process.kill 'INT', pid.to_i rescue nil } | ||
1634 | else | ||
1635 | unless `which pgrep 2> /dev/null`.empty? | ||
1636 | children = pids | ||
1637 | until children.empty? | ||
1638 | children = children.map { |pid| | ||
1639 | `pgrep -P #{pid}`.lines.map { |l| l.chomp } | ||
1640 | }.flatten | ||
1641 | pids += children | ||
1642 | end | ||
1643 | end | ||
1644 | pids.each { |pid| Process.kill 'TERM', pid.to_i rescue nil } | ||
1645 | end | ||
1646 | end | ||
1647 | |||
1648 | require 'thread' | ||
1649 | require 'fileutils' | ||
1650 | require 'timeout' | ||
1651 | running = true | ||
1652 | iswin = VIM::evaluate('s:is_win').to_i == 1 | ||
1653 | pull = VIM::evaluate('s:update.pull').to_i == 1 | ||
1654 | base = VIM::evaluate('g:plug_home') | ||
1655 | all = VIM::evaluate('s:update.todo') | ||
1656 | limit = VIM::evaluate('get(g:, "plug_timeout", 60)') | ||
1657 | tries = VIM::evaluate('get(g:, "plug_retries", 2)') + 1 | ||
1658 | nthr = VIM::evaluate('s:update.threads').to_i | ||
1659 | maxy = VIM::evaluate('winheight(".")').to_i | ||
1660 | cd = iswin ? 'cd /d' : 'cd' | ||
1661 | tot = VIM::evaluate('len(s:update.todo)') || 0 | ||
1662 | bar = '' | ||
1663 | skip = 'Already installed' | ||
1664 | mtx = Mutex.new | ||
1665 | take1 = proc { mtx.synchronize { running && all.shift } } | ||
1666 | logh = proc { | ||
1667 | cnt = bar.length | ||
1668 | $curbuf[1] = "#{pull ? 'Updating' : 'Installing'} plugins (#{cnt}/#{tot})" | ||
1669 | $curbuf[2] = '[' + bar.ljust(tot) + ']' | ||
1670 | VIM::command('normal! 2G') | ||
1671 | VIM::command('redraw') | ||
1672 | } | ||
1673 | where = proc { |name| (1..($curbuf.length)).find { |l| $curbuf[l] =~ /^[-+x*] #{name}:/ } } | ||
1674 | log = proc { |name, result, type| | ||
1675 | mtx.synchronize do | ||
1676 | ing = ![true, false].include?(type) | ||
1677 | bar += type ? '=' : 'x' unless ing | ||
1678 | b = case type | ||
1679 | when :install then '+' when :update then '*' | ||
1680 | when true, nil then '-' else | ||
1681 | VIM::command("call add(s:update.errors, '#{name}')") | ||
1682 | 'x' | ||
1683 | end | ||
1684 | result = | ||
1685 | if type || type.nil? | ||
1686 | ["#{b} #{name}: #{result.lines.to_a.last || 'OK'}"] | ||
1687 | elsif result =~ /^Interrupted|^Timeout/ | ||
1688 | ["#{b} #{name}: #{result}"] | ||
1689 | else | ||
1690 | ["#{b} #{name}"] + result.lines.map { |l| " " << l } | ||
1691 | end | ||
1692 | if lnum = where.call(name) | ||
1693 | $curbuf.delete lnum | ||
1694 | lnum = 4 if ing && lnum > maxy | ||
1695 | end | ||
1696 | result.each_with_index do |line, offset| | ||
1697 | $curbuf.append((lnum || 4) - 1 + offset, line.gsub(/\e\[./, '').chomp) | ||
1698 | end | ||
1699 | logh.call | ||
1700 | end | ||
1701 | } | ||
1702 | bt = proc { |cmd, name, type, cleanup| | ||
1703 | tried = timeout = 0 | ||
1704 | begin | ||
1705 | tried += 1 | ||
1706 | timeout += limit | ||
1707 | fd = nil | ||
1708 | data = '' | ||
1709 | if iswin | ||
1710 | Timeout::timeout(timeout) do | ||
1711 | tmp = VIM::evaluate('tempname()') | ||
1712 | system("(#{cmd}) > #{tmp}") | ||
1713 | data = File.read(tmp).chomp | ||
1714 | File.unlink tmp rescue nil | ||
1715 | end | ||
1716 | else | ||
1717 | fd = IO.popen(cmd).extend(PlugStream) | ||
1718 | first_line = true | ||
1719 | log_prob = 1.0 / nthr | ||
1720 | while line = Timeout::timeout(timeout) { fd.get_line } | ||
1721 | data << line | ||
1722 | log.call name, line.chomp, type if name && (first_line || rand < log_prob) | ||
1723 | first_line = false | ||
1724 | end | ||
1725 | fd.close | ||
1726 | end | ||
1727 | [$? == 0, data.chomp] | ||
1728 | rescue Timeout::Error, Interrupt => e | ||
1729 | if fd && !fd.closed? | ||
1730 | killall fd.pid | ||
1731 | fd.close | ||
1732 | end | ||
1733 | cleanup.call if cleanup | ||
1734 | if e.is_a?(Timeout::Error) && tried < tries | ||
1735 | 3.downto(1) do |countdown| | ||
1736 | s = countdown > 1 ? 's' : '' | ||
1737 | log.call name, "Timeout. Will retry in #{countdown} second#{s} ...", type | ||
1738 | sleep 1 | ||
1739 | end | ||
1740 | log.call name, 'Retrying ...', type | ||
1741 | retry | ||
1742 | end | ||
1743 | [false, e.is_a?(Interrupt) ? "Interrupted!" : "Timeout!"] | ||
1744 | end | ||
1745 | } | ||
1746 | main = Thread.current | ||
1747 | threads = [] | ||
1748 | watcher = Thread.new { | ||
1749 | while VIM::evaluate('getchar(1)') | ||
1750 | sleep 0.1 | ||
1751 | end | ||
1752 | mtx.synchronize do | ||
1753 | running = false | ||
1754 | threads.each { |t| t.raise Interrupt } | ||
1755 | end | ||
1756 | threads.each { |t| t.join rescue nil } | ||
1757 | main.kill | ||
1758 | } | ||
1759 | refresh = Thread.new { | ||
1760 | while true | ||
1761 | mtx.synchronize do | ||
1762 | break unless running | ||
1763 | VIM::command('noautocmd normal! a') | ||
1764 | end | ||
1765 | sleep 0.2 | ||
1766 | end | ||
1767 | } if VIM::evaluate('s:mac_gui') == 1 | ||
1768 | |||
1769 | clone_opt = VIM::evaluate('s:clone_opt') | ||
1770 | progress = VIM::evaluate('s:progress_opt(1)') | ||
1771 | nthr.times do | ||
1772 | mtx.synchronize do | ||
1773 | threads << Thread.new { | ||
1774 | while pair = take1.call | ||
1775 | name = pair.first | ||
1776 | dir, uri, tag = pair.last.values_at *%w[dir uri tag] | ||
1777 | exists = File.directory? dir | ||
1778 | ok, result = | ||
1779 | if exists | ||
1780 | chdir = "#{cd} #{iswin ? dir : esc(dir)}" | ||
1781 | ret, data = bt.call "#{chdir} && git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url", nil, nil, nil | ||
1782 | current_uri = data.lines.to_a.last | ||
1783 | if !ret | ||
1784 | if data =~ /^Interrupted|^Timeout/ | ||
1785 | [false, data] | ||
1786 | else | ||
1787 | [false, [data.chomp, "PlugClean required."].join($/)] | ||
1788 | end | ||
1789 | elsif current_uri.sub(/git::?@/, '') != uri.sub(/git::?@/, '') | ||
1790 | [false, ["Invalid URI: #{current_uri}", | ||
1791 | "Expected: #{uri}", | ||
1792 | "PlugClean required."].join($/)] | ||
1793 | else | ||
1794 | if pull | ||
1795 | log.call name, 'Updating ...', :update | ||
1796 | fetch_opt = (tag && File.exist?(File.join(dir, '.git/shallow'))) ? '--depth 99999999' : '' | ||
1797 | bt.call "#{chdir} && git fetch #{fetch_opt} #{progress} 2>&1", name, :update, nil | ||
1798 | else | ||
1799 | [true, skip] | ||
1800 | end | ||
1801 | end | ||
1802 | else | ||
1803 | d = esc dir.sub(%r{[\\/]+$}, '') | ||
1804 | log.call name, 'Installing ...', :install | ||
1805 | bt.call "git clone #{clone_opt unless tag} #{progress} #{uri} #{d} 2>&1", name, :install, proc { | ||
1806 | FileUtils.rm_rf dir | ||
1807 | } | ||
1808 | end | ||
1809 | mtx.synchronize { VIM::command("let s:update.new['#{name}'] = 1") } if !exists && ok | ||
1810 | log.call name, result, ok | ||
1811 | end | ||
1812 | } if running | ||
1813 | end | ||
1814 | end | ||
1815 | threads.each { |t| t.join rescue nil } | ||
1816 | logh.call | ||
1817 | refresh.kill if refresh | ||
1818 | watcher.kill | ||
1819 | EOF | ||
1820 | endfunction | ||
1821 | |||
1822 | function! s:shellesc(arg) | ||
1823 | return '"'.escape(a:arg, '"').'"' | ||
1824 | endfunction | ||
1825 | |||
1826 | function! s:glob_dir(path) | ||
1827 | return map(filter(s:glob(a:path, '**'), 'isdirectory(v:val)'), 's:dirpath(v:val)') | ||
1828 | endfunction | ||
1829 | |||
1830 | function! s:progress_bar(line, bar, total) | ||
1831 | call setline(a:line, '[' . s:lpad(a:bar, a:total) . ']') | ||
1832 | endfunction | ||
1833 | |||
1834 | function! s:compare_git_uri(a, b) | ||
1835 | let a = substitute(a:a, 'git:\{1,2}@', '', '') | ||
1836 | let b = substitute(a:b, 'git:\{1,2}@', '', '') | ||
1837 | return a ==# b | ||
1838 | endfunction | ||
1839 | |||
1840 | function! s:format_message(bullet, name, message) | ||
1841 | if a:bullet != 'x' | ||
1842 | return [printf('%s %s: %s', a:bullet, a:name, s:lastline(a:message))] | ||
1843 | else | ||
1844 | let lines = map(s:lines(a:message), '" ".v:val') | ||
1845 | return extend([printf('x %s:', a:name)], lines) | ||
1846 | endif | ||
1847 | endfunction | ||
1848 | |||
1849 | function! s:with_cd(cmd, dir) | ||
1850 | return printf('cd%s %s && %s', s:is_win ? ' /d' : '', s:shellesc(a:dir), a:cmd) | ||
1851 | endfunction | ||
1852 | |||
1853 | function! s:system(cmd, ...) | ||
1854 | try | ||
1855 | let [sh, shrd] = s:chsh(1) | ||
1856 | let cmd = a:0 > 0 ? s:with_cd(a:cmd, a:1) : a:cmd | ||
1857 | return system(s:is_win ? '('.cmd.')' : cmd) | ||
1858 | finally | ||
1859 | let [&shell, &shellredir] = [sh, shrd] | ||
1860 | endtry | ||
1861 | endfunction | ||
1862 | |||
1863 | function! s:system_chomp(...) | ||
1864 | let ret = call('s:system', a:000) | ||
1865 | return v:shell_error ? '' : substitute(ret, '\n$', '', '') | ||
1866 | endfunction | ||
1867 | |||
1868 | function! s:git_validate(spec, check_branch) | ||
1869 | let err = '' | ||
1870 | if isdirectory(a:spec.dir) | ||
1871 | 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)) | ||
1872 | let remote = result[-1] | ||
1873 | if v:shell_error | ||
1874 | let err = join([remote, 'PlugClean required.'], "\n") | ||
1875 | elseif !s:compare_git_uri(remote, a:spec.uri) | ||
1876 | let err = join(['Invalid URI: '.remote, | ||
1877 | \ 'Expected: '.a:spec.uri, | ||
1878 | \ 'PlugClean required.'], "\n") | ||
1879 | elseif a:check_branch && has_key(a:spec, 'commit') | ||
1880 | let result = s:lines(s:system('git rev-parse HEAD 2>&1', a:spec.dir)) | ||
1881 | let sha = result[-1] | ||
1882 | if v:shell_error | ||
1883 | let err = join(add(result, 'PlugClean required.'), "\n") | ||
1884 | elseif !s:hash_match(sha, a:spec.commit) | ||
1885 | let err = join([printf('Invalid HEAD (expected: %s, actual: %s)', | ||
1886 | \ a:spec.commit[:6], sha[:6]), | ||
1887 | \ 'PlugUpdate required.'], "\n") | ||
1888 | endif | ||
1889 | elseif a:check_branch | ||
1890 | let branch = result[0] | ||
1891 | " Check tag | ||
1892 | if has_key(a:spec, 'tag') | ||
1893 | let tag = s:system_chomp('git describe --exact-match --tags HEAD 2>&1', a:spec.dir) | ||
1894 | if a:spec.tag !=# tag | ||
1895 | let err = printf('Invalid tag: %s (expected: %s). Try PlugUpdate.', | ||
1896 | \ (empty(tag) ? 'N/A' : tag), a:spec.tag) | ||
1897 | endif | ||
1898 | " Check branch | ||
1899 | elseif a:spec.branch !=# branch | ||
1900 | let err = printf('Invalid branch: %s (expected: %s). Try PlugUpdate.', | ||
1901 | \ branch, a:spec.branch) | ||
1902 | endif | ||
1903 | if empty(err) | ||
1904 | let commits = len(s:lines(s:system(printf('git rev-list origin/%s..HEAD', a:spec.branch), a:spec.dir))) | ||
1905 | if !v:shell_error && commits | ||
1906 | let err = join([printf('Diverged from origin/%s by %d commit(s).', a:spec.branch, commits), | ||
1907 | \ 'Reinstall after PlugClean.'], "\n") | ||
1908 | endif | ||
1909 | endif | ||
1910 | endif | ||
1911 | else | ||
1912 | let err = 'Not found' | ||
1913 | endif | ||
1914 | return [err, err =~# 'PlugClean'] | ||
1915 | endfunction | ||
1916 | |||
1917 | function! s:rm_rf(dir) | ||
1918 | if isdirectory(a:dir) | ||
1919 | call s:system((s:is_win ? 'rmdir /S /Q ' : 'rm -rf ') . s:shellesc(a:dir)) | ||
1920 | endif | ||
1921 | endfunction | ||
1922 | |||
1923 | function! s:clean(force) | ||
1924 | call s:prepare() | ||
1925 | call append(0, 'Searching for invalid plugins in '.g:plug_home) | ||
1926 | call append(1, '') | ||
1927 | |||
1928 | " List of valid directories | ||
1929 | let dirs = [] | ||
1930 | let errs = {} | ||
1931 | let [cnt, total] = [0, len(g:plugs)] | ||
1932 | for [name, spec] in items(g:plugs) | ||
1933 | if !s:is_managed(name) | ||
1934 | call add(dirs, spec.dir) | ||
1935 | else | ||
1936 | let [err, clean] = s:git_validate(spec, 1) | ||
1937 | if clean | ||
1938 | let errs[spec.dir] = s:lines(err)[0] | ||
1939 | else | ||
1940 | call add(dirs, spec.dir) | ||
1941 | endif | ||
1942 | endif | ||
1943 | let cnt += 1 | ||
1944 | call s:progress_bar(2, repeat('=', cnt), total) | ||
1945 | normal! 2G | ||
1946 | redraw | ||
1947 | endfor | ||
1948 | |||
1949 | let allowed = {} | ||
1950 | for dir in dirs | ||
1951 | let allowed[s:dirpath(fnamemodify(dir, ':h:h'))] = 1 | ||
1952 | let allowed[dir] = 1 | ||
1953 | for child in s:glob_dir(dir) | ||
1954 | let allowed[child] = 1 | ||
1955 | endfor | ||
1956 | endfor | ||
1957 | |||
1958 | let todo = [] | ||
1959 | let found = sort(s:glob_dir(g:plug_home)) | ||
1960 | while !empty(found) | ||
1961 | let f = remove(found, 0) | ||
1962 | if !has_key(allowed, f) && isdirectory(f) | ||
1963 | call add(todo, f) | ||
1964 | call append(line('$'), '- ' . f) | ||
1965 | if has_key(errs, f) | ||
1966 | call append(line('$'), ' ' . errs[f]) | ||
1967 | endif | ||
1968 | let found = filter(found, 'stridx(v:val, f) != 0') | ||
1969 | end | ||
1970 | endwhile | ||
1971 | |||
1972 | 4 | ||
1973 | redraw | ||
1974 | if empty(todo) | ||
1975 | call append(line('$'), 'Already clean.') | ||
1976 | else | ||
1977 | if a:force || s:ask('Proceed?') | ||
1978 | for dir in todo | ||
1979 | call s:rm_rf(dir) | ||
1980 | endfor | ||
1981 | call append(3, ['Removed.', '']) | ||
1982 | else | ||
1983 | call append(3, ['Cancelled.', '']) | ||
1984 | endif | ||
1985 | endif | ||
1986 | 4 | ||
1987 | endfunction | ||
1988 | |||
1989 | function! s:upgrade() | ||
1990 | echo 'Downloading the latest version of vim-plug' | ||
1991 | redraw | ||
1992 | let tmp = tempname() | ||
1993 | let new = tmp . '/plug.vim' | ||
1994 | |||
1995 | try | ||
1996 | let out = s:system(printf('git clone --depth 1 %s %s', s:plug_src, tmp)) | ||
1997 | if v:shell_error | ||
1998 | return s:err('Error upgrading vim-plug: '. out) | ||
1999 | endif | ||
2000 | |||
2001 | if readfile(s:me) ==# readfile(new) | ||
2002 | echo 'vim-plug is already up-to-date' | ||
2003 | return 0 | ||
2004 | else | ||
2005 | call rename(s:me, s:me . '.old') | ||
2006 | call rename(new, s:me) | ||
2007 | unlet g:loaded_plug | ||
2008 | echo 'vim-plug has been upgraded' | ||
2009 | return 1 | ||
2010 | endif | ||
2011 | finally | ||
2012 | silent! call s:rm_rf(tmp) | ||
2013 | endtry | ||
2014 | endfunction | ||
2015 | |||
2016 | function! s:upgrade_specs() | ||
2017 | for spec in values(g:plugs) | ||
2018 | let spec.frozen = get(spec, 'frozen', 0) | ||
2019 | endfor | ||
2020 | endfunction | ||
2021 | |||
2022 | function! s:status() | ||
2023 | call s:prepare() | ||
2024 | call append(0, 'Checking plugins') | ||
2025 | call append(1, '') | ||
2026 | |||
2027 | let ecnt = 0 | ||
2028 | let unloaded = 0 | ||
2029 | let [cnt, total] = [0, len(g:plugs)] | ||
2030 | for [name, spec] in items(g:plugs) | ||
2031 | if has_key(spec, 'uri') | ||
2032 | if isdirectory(spec.dir) | ||
2033 | let [err, _] = s:git_validate(spec, 1) | ||
2034 | let [valid, msg] = [empty(err), empty(err) ? 'OK' : err] | ||
2035 | else | ||
2036 | let [valid, msg] = [0, 'Not found. Try PlugInstall.'] | ||
2037 | endif | ||
2038 | else | ||
2039 | if isdirectory(spec.dir) | ||
2040 | let [valid, msg] = [1, 'OK'] | ||
2041 | else | ||
2042 | let [valid, msg] = [0, 'Not found.'] | ||
2043 | endif | ||
2044 | endif | ||
2045 | let cnt += 1 | ||
2046 | let ecnt += !valid | ||
2047 | " `s:loaded` entry can be missing if PlugUpgraded | ||
2048 | if valid && get(s:loaded, name, -1) == 0 | ||
2049 | let unloaded = 1 | ||
2050 | let msg .= ' (not loaded)' | ||
2051 | endif | ||
2052 | call s:progress_bar(2, repeat('=', cnt), total) | ||
2053 | call append(3, s:format_message(valid ? '-' : 'x', name, msg)) | ||
2054 | normal! 2G | ||
2055 | redraw | ||
2056 | endfor | ||
2057 | call setline(1, 'Finished. '.ecnt.' error(s).') | ||
2058 | normal! gg | ||
2059 | setlocal nomodifiable | ||
2060 | if unloaded | ||
2061 | echo "Press 'L' on each line to load plugin, or 'U' to update" | ||
2062 | nnoremap <silent> <buffer> L :call <SID>status_load(line('.'))<cr> | ||
2063 | xnoremap <silent> <buffer> L :call <SID>status_load(line('.'))<cr> | ||
2064 | end | ||
2065 | endfunction | ||
2066 | |||
2067 | function! s:extract_name(str, prefix, suffix) | ||
2068 | return matchstr(a:str, '^'.a:prefix.' \zs[^:]\+\ze:.*'.a:suffix.'$') | ||
2069 | endfunction | ||
2070 | |||
2071 | function! s:status_load(lnum) | ||
2072 | let line = getline(a:lnum) | ||
2073 | let name = s:extract_name(line, '-', '(not loaded)') | ||
2074 | if !empty(name) | ||
2075 | call plug#load(name) | ||
2076 | setlocal modifiable | ||
2077 | call setline(a:lnum, substitute(line, ' (not loaded)$', '', '')) | ||
2078 | setlocal nomodifiable | ||
2079 | endif | ||
2080 | endfunction | ||
2081 | |||
2082 | function! s:status_update() range | ||
2083 | let lines = getline(a:firstline, a:lastline) | ||
2084 | let names = filter(map(lines, 's:extract_name(v:val, "[x-]", "")'), '!empty(v:val)') | ||
2085 | if !empty(names) | ||
2086 | echo | ||
2087 | execute 'PlugUpdate' join(names) | ||
2088 | endif | ||
2089 | endfunction | ||
2090 | |||
2091 | function! s:is_preview_window_open() | ||
2092 | silent! wincmd P | ||
2093 | if &previewwindow | ||
2094 | wincmd p | ||
2095 | return 1 | ||
2096 | endif | ||
2097 | endfunction | ||
2098 | |||
2099 | function! s:find_name(lnum) | ||
2100 | for lnum in reverse(range(1, a:lnum)) | ||
2101 | let line = getline(lnum) | ||
2102 | if empty(line) | ||
2103 | return '' | ||
2104 | endif | ||
2105 | let name = s:extract_name(line, '-', '') | ||
2106 | if !empty(name) | ||
2107 | return name | ||
2108 | endif | ||
2109 | endfor | ||
2110 | return '' | ||
2111 | endfunction | ||
2112 | |||
2113 | function! s:preview_commit() | ||
2114 | if b:plug_preview < 0 | ||
2115 | let b:plug_preview = !s:is_preview_window_open() | ||
2116 | endif | ||
2117 | |||
2118 | let sha = matchstr(getline('.'), '^ \X*\zs[0-9a-f]\{7}') | ||
2119 | if empty(sha) | ||
2120 | return | ||
2121 | endif | ||
2122 | |||
2123 | let name = s:find_name(line('.')) | ||
2124 | if empty(name) || !has_key(g:plugs, name) || !isdirectory(g:plugs[name].dir) | ||
2125 | return | ||
2126 | endif | ||
2127 | |||
2128 | execute 'pedit' sha | ||
2129 | wincmd P | ||
2130 | setlocal filetype=git buftype=nofile nobuflisted modifiable | ||
2131 | execute 'silent read !cd' s:shellesc(g:plugs[name].dir) '&& git show --no-color --pretty=medium' sha | ||
2132 | normal! gg"_dd | ||
2133 | setlocal nomodifiable | ||
2134 | nnoremap <silent> <buffer> q :q<cr> | ||
2135 | wincmd p | ||
2136 | endfunction | ||
2137 | |||
2138 | function! s:section(flags) | ||
2139 | call search('\(^[x-] \)\@<=[^:]\+:', a:flags) | ||
2140 | endfunction | ||
2141 | |||
2142 | function! s:format_git_log(line) | ||
2143 | let indent = ' ' | ||
2144 | let tokens = split(a:line, nr2char(1)) | ||
2145 | if len(tokens) != 5 | ||
2146 | return indent.substitute(a:line, '\s*$', '', '') | ||
2147 | endif | ||
2148 | let [graph, sha, refs, subject, date] = tokens | ||
2149 | let tag = matchstr(refs, 'tag: [^,)]\+') | ||
2150 | let tag = empty(tag) ? ' ' : ' ('.tag.') ' | ||
2151 | return printf('%s%s%s%s%s (%s)', indent, graph, sha, tag, subject, date) | ||
2152 | endfunction | ||
2153 | |||
2154 | function! s:append_ul(lnum, text) | ||
2155 | call append(a:lnum, ['', a:text, repeat('-', len(a:text))]) | ||
2156 | endfunction | ||
2157 | |||
2158 | function! s:diff() | ||
2159 | call s:prepare() | ||
2160 | call append(0, ['Collecting changes ...', '']) | ||
2161 | let cnts = [0, 0] | ||
2162 | let bar = '' | ||
2163 | let total = filter(copy(g:plugs), 's:is_managed(v:key) && isdirectory(v:val.dir)') | ||
2164 | call s:progress_bar(2, bar, len(total)) | ||
2165 | for origin in [1, 0] | ||
2166 | let plugs = reverse(sort(items(filter(copy(total), (origin ? '' : '!').'(has_key(v:val, "commit") || has_key(v:val, "tag"))')))) | ||
2167 | if empty(plugs) | ||
2168 | continue | ||
2169 | endif | ||
2170 | call s:append_ul(2, origin ? 'Pending updates:' : 'Last update:') | ||
2171 | for [k, v] in plugs | ||
2172 | let range = origin ? '..origin/'.v.branch : 'HEAD@{1}..' | ||
2173 | let diff = s:system_chomp('git log --graph --color=never --pretty=format:"%x01%h%x01%d%x01%s%x01%cr" '.s:shellesc(range), v.dir) | ||
2174 | if !empty(diff) | ||
2175 | let ref = has_key(v, 'tag') ? (' (tag: '.v.tag.')') : has_key(v, 'commit') ? (' '.v.commit) : '' | ||
2176 | call append(5, extend(['', '- '.k.':'.ref], map(s:lines(diff), 's:format_git_log(v:val)'))) | ||
2177 | let cnts[origin] += 1 | ||
2178 | endif | ||
2179 | let bar .= '=' | ||
2180 | call s:progress_bar(2, bar, len(total)) | ||
2181 | normal! 2G | ||
2182 | redraw | ||
2183 | endfor | ||
2184 | if !cnts[origin] | ||
2185 | call append(5, ['', 'N/A']) | ||
2186 | endif | ||
2187 | endfor | ||
2188 | call setline(1, printf('%d plugin(s) updated.', cnts[0]) | ||
2189 | \ . (cnts[1] ? printf(' %d plugin(s) have pending updates.', cnts[1]) : '')) | ||
2190 | |||
2191 | if cnts[0] || cnts[1] | ||
2192 | nnoremap <silent> <buffer> <cr> :silent! call <SID>preview_commit()<cr> | ||
2193 | nnoremap <silent> <buffer> o :silent! call <SID>preview_commit()<cr> | ||
2194 | endif | ||
2195 | if cnts[0] | ||
2196 | nnoremap <silent> <buffer> X :call <SID>revert()<cr> | ||
2197 | echo "Press 'X' on each block to revert the update" | ||
2198 | endif | ||
2199 | normal! gg | ||
2200 | setlocal nomodifiable | ||
2201 | endfunction | ||
2202 | |||
2203 | function! s:revert() | ||
2204 | if search('^Pending updates', 'bnW') | ||
2205 | return | ||
2206 | endif | ||
2207 | |||
2208 | let name = s:find_name(line('.')) | ||
2209 | if empty(name) || !has_key(g:plugs, name) || | ||
2210 | \ input(printf('Revert the update of %s? (y/N) ', name)) !~? '^y' | ||
2211 | return | ||
2212 | endif | ||
2213 | |||
2214 | call s:system('git reset --hard HEAD@{1} && git checkout '.s:esc(g:plugs[name].branch), g:plugs[name].dir) | ||
2215 | setlocal modifiable | ||
2216 | normal! "_dap | ||
2217 | setlocal nomodifiable | ||
2218 | echo 'Reverted.' | ||
2219 | endfunction | ||
2220 | |||
2221 | function! s:snapshot(force, ...) abort | ||
2222 | call s:prepare() | ||
2223 | setf vim | ||
2224 | call append(0, ['" Generated by vim-plug', | ||
2225 | \ '" '.strftime("%c"), | ||
2226 | \ '" :source this file in vim to restore the snapshot', | ||
2227 | \ '" or execute: vim -S snapshot.vim', | ||
2228 | \ '', '', 'PlugUpdate!']) | ||
2229 | 1 | ||
2230 | let anchor = line('$') - 3 | ||
2231 | let names = sort(keys(filter(copy(g:plugs), | ||
2232 | \'has_key(v:val, "uri") && !has_key(v:val, "commit") && isdirectory(v:val.dir)'))) | ||
2233 | for name in reverse(names) | ||
2234 | let sha = s:system_chomp('git rev-parse --short HEAD', g:plugs[name].dir) | ||
2235 | if !empty(sha) | ||
2236 | call append(anchor, printf("silent! let g:plugs['%s'].commit = '%s'", name, sha)) | ||
2237 | redraw | ||
2238 | endif | ||
2239 | endfor | ||
2240 | |||
2241 | if a:0 > 0 | ||
2242 | let fn = expand(a:1) | ||
2243 | if filereadable(fn) && !(a:force || s:ask(a:1.' already exists. Overwrite?')) | ||
2244 | return | ||
2245 | endif | ||
2246 | call writefile(getline(1, '$'), fn) | ||
2247 | echo 'Saved as '.a:1 | ||
2248 | silent execute 'e' s:esc(fn) | ||
2249 | setf vim | ||
2250 | endif | ||
2251 | endfunction | ||
2252 | |||
2253 | function! s:split_rtp() | ||
2254 | return split(&rtp, '\\\@<!,') | ||
2255 | endfunction | ||
2256 | |||
2257 | let s:first_rtp = s:escrtp(get(s:split_rtp(), 0, '')) | ||
2258 | let s:last_rtp = s:escrtp(get(s:split_rtp(), -1, '')) | ||
2259 | |||
2260 | if exists('g:plugs') | ||
2261 | let g:plugs_order = get(g:, 'plugs_order', keys(g:plugs)) | ||
2262 | call s:upgrade_specs() | ||
2263 | call s:define_commands() | ||
2264 | endif | ||
2265 | |||
2266 | let &cpo = s:cpo_save | ||
2267 | unlet s:cpo_save | ||
diff --git a/vim/.vim/config/airline.vim b/vim/.vim/config/airline.vim new file mode 100644 index 0000000..99fc4f0 --- /dev/null +++ b/vim/.vim/config/airline.vim | |||
@@ -0,0 +1,4 @@ | |||
1 | set laststatus=2 | ||
2 | let g:airline#extensions#tabline#enabled = 1 | ||
3 | let g:airline_powerline_fonts = 1 | ||
4 | let g:airline_theme = "molokai" | ||
diff --git a/vim/.vim/config/molokai.vim b/vim/.vim/config/molokai.vim new file mode 100644 index 0000000..848845c --- /dev/null +++ b/vim/.vim/config/molokai.vim | |||
@@ -0,0 +1 @@ | |||
let g:rehash256 = 1 \ No newline at end of file | |||
diff --git a/vim/.vim/config/rainbow.vim b/vim/.vim/config/rainbow.vim new file mode 100644 index 0000000..7870030 --- /dev/null +++ b/vim/.vim/config/rainbow.vim | |||
@@ -0,0 +1,19 @@ | |||
1 | let g:rbpt_colorpairs = [ | ||
2 | \ [158, '#00ceb3'], | ||
3 | \ [081, '#00a3ff'], | ||
4 | \ [214, '#ff8d00'], | ||
5 | \ [123, '#3fffc9'], | ||
6 | \ [045, '#29b9ec'], | ||
7 | \ [190, '#bfec29'], | ||
8 | \ [208, '#ffad00'], | ||
9 | \ [117, '#48bde0'], | ||
10 | \ ] | ||
11 | |||
12 | let g:rbpt_max = 8 | ||
13 | let g:rbpt_loadcmd_toggle = 0 | ||
14 | |||
15 | au VimEnter * RainbowParenthesesToggle | ||
16 | au Syntax * RainbowParenthesesLoadRound | ||
17 | au Syntax * RainbowParenthesesLoadSquare | ||
18 | au Syntax * RainbowParenthesesLoadBraces | ||
19 | |||
diff --git a/vim/.vim/config/syntastic.vim b/vim/.vim/config/syntastic.vim new file mode 100644 index 0000000..04babc7 --- /dev/null +++ b/vim/.vim/config/syntastic.vim | |||
@@ -0,0 +1,35 @@ | |||
1 | if has('nvim') | ||
2 | autocmd BufReadPost * Neomake | ||
3 | autocmd BufWritePost * Neomake | ||
4 | map <leader>sc :Neomake!<CR> | ||
5 | let g:neomake_python_enabled_makers = ['flake8'] | ||
6 | |||
7 | else | ||
8 | set statusline+=%#warningmsg# | ||
9 | set statusline+=%{SyntasticStatuslineFlag()} | ||
10 | set statusline+=%* | ||
11 | |||
12 | let g:syntastic_always_populate_loc_list = 1 | ||
13 | let g:syntastic_auto_loc_list = 1 | ||
14 | |||
15 | let g:syntastic_quiet_messages = {'level': 'warnings'} | ||
16 | let g:syntastic_check_on_open=1 | ||
17 | let g:syntastic_check_on_wq = 1 | ||
18 | let g:syntastic_enable_signs=1 | ||
19 | |||
20 | let g:syntastic_error_symbol='✗' | ||
21 | let g:syntastic_warning_symbol='⚠' | ||
22 | |||
23 | let g:syntastic_html_tidy_exec = 'tidy' | ||
24 | |||
25 | let g:syntastic_python_python_exec = '/usr/bin/python3' | ||
26 | let g:syntastic_mode_map = { 'mode': 'passive', | ||
27 | \ 'active_filetypes': ['python', 'javascript'], | ||
28 | \ 'passive_filetypes': ['scala', 'tex', 'java', 'go'] } | ||
29 | |||
30 | let g:syntastic_python_checkers = ['pep8'] | ||
31 | let g:syntastic_python_flake8_post_args = "--ignore=E501,E226,E225,E227" | ||
32 | let g:syntastic_c_compiler = 'clang' | ||
33 | |||
34 | map <leader>sc :SyntasticCheck<CR> | ||
35 | endif | ||
diff --git a/vim/.vim/config/ycm.vim b/vim/.vim/config/ycm.vim new file mode 100644 index 0000000..ec11cbd --- /dev/null +++ b/vim/.vim/config/ycm.vim | |||
@@ -0,0 +1,20 @@ | |||
1 | let g:ycm_global_ycm_extra_conf = '/home/clarkzjw/.vim/config/ycm_extra_conf.py' | ||
2 | let g:ycm_collect_identifiers_from_tags_files = 1 | ||
3 | let g:ycm_seed_identifiers_with_syntax = 1 | ||
4 | let g:ycm_confirm_extra_conf = 0 | ||
5 | let g:ycm_min_num_of_chars_for_completion = 2 | ||
6 | let g:ycm_auto_trigger = 1 | ||
7 | let g:ycm_path_to_python_interpreter = '/usr/bin/python3' | ||
8 | let g:ycm_show_diagnostics_ui = 0 | ||
9 | let g:ycm_key_list_select_completion = ['<TAB>', '<C-n>', '<Down>'] | ||
10 | let g:ycm_key_list_previous_completion = ['<C-p>', '<Up>'] | ||
11 | "let g:ycm_filetype_whitelist = { | ||
12 | " \ 'c': 1 , | ||
13 | " \ 'cpp': 1, | ||
14 | " \ 'python': 1 | ||
15 | " \} | ||
16 | nnoremap <leader>jd :YcmCompleter GoTo<CR> | ||
17 | |||
18 | let g:ycm_add_preview_to_completeopt = 0 | ||
19 | let g:ycm_autoclose_preview_window_after_completion = 1 | ||
20 | let g:ycm_autoclose_preview_window_after_insertion = 0 | ||
diff --git a/vim/.vim/config/ycm_extra_conf.py b/vim/.vim/config/ycm_extra_conf.py new file mode 100644 index 0000000..3a827dd --- /dev/null +++ b/vim/.vim/config/ycm_extra_conf.py | |||
@@ -0,0 +1,76 @@ | |||
1 | import os | ||
2 | import ycm_core | ||
3 | from clang_helpers import PrepareClangFlags | ||
4 | |||
5 | compilation_database_folder = '' | ||
6 | |||
7 | if compilation_database_folder: | ||
8 | database = ycm_core.CompilationDatabase(compilation_database_folder) | ||
9 | else: | ||
10 | database = None | ||
11 | |||
12 | flags = [ | ||
13 | '-Wall', | ||
14 | '-std=c++11', | ||
15 | '-stdlib=libc++', | ||
16 | '-x', | ||
17 | 'c++', | ||
18 | '-I/usr/local/include', | ||
19 | '-I/usr/include', | ||
20 | '-I/home/clarkzjw/Code/clang3.9.0/include/c++/v1', | ||
21 | '.', | ||
22 | ] | ||
23 | |||
24 | |||
25 | def DirectoryOfThisScript(): | ||
26 | return os.path.dirname(os.path.abspath(__file__)) | ||
27 | |||
28 | |||
29 | def MakeRelativePathsInFlagsAbsolute(flags, working_directory): | ||
30 | if not working_directory: | ||
31 | return flags | ||
32 | new_flags = [] | ||
33 | make_next_absolute = False | ||
34 | path_flags = ['-isystem', '-I', '-iquote', '--sysroot='] | ||
35 | for flag in flags: | ||
36 | new_flag = flag | ||
37 | |||
38 | if make_next_absolute: | ||
39 | make_next_absolute = False | ||
40 | if not flag.startswith('/'): | ||
41 | new_flag = os.path.join(working_directory, flag) | ||
42 | |||
43 | for path_flag in path_flags: | ||
44 | if flag == path_flag: | ||
45 | make_next_absolute = True | ||
46 | break | ||
47 | |||
48 | if flag.startswith(path_flag): | ||
49 | path = flag[len(path_flag):] | ||
50 | new_flag = path_flag + os.path.join(working_directory, path) | ||
51 | break | ||
52 | |||
53 | if new_flag: | ||
54 | new_flags.append(new_flag) | ||
55 | return new_flags | ||
56 | |||
57 | |||
58 | def FlagsForFile(filename): | ||
59 | if database: | ||
60 | # Bear in mind that compilation_info.compiler_flags_ does NOT return a | ||
61 | # python list, but a "list-like" StringVec object | ||
62 | compilation_info = database.GetCompilationInfoForFile(filename) | ||
63 | final_flags = PrepareClangFlags( | ||
64 | MakeRelativePathsInFlagsAbsolute( | ||
65 | compilation_info.compiler_flags_, | ||
66 | compilation_info.compiler_working_dir_), | ||
67 | filename) | ||
68 | |||
69 | else: | ||
70 | relative_to = DirectoryOfThisScript() | ||
71 | final_flags = MakeRelativePathsInFlagsAbsolute(flags, relative_to) | ||
72 | |||
73 | return { | ||
74 | 'flags': final_flags, | ||
75 | 'do_cache': True | ||
76 | } | ||
diff --git a/vim/.vim/plugged/.gitkeep b/vim/.vim/plugged/.gitkeep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/vim/.vim/plugged/.gitkeep | |||
diff --git a/vim/.vimrc b/vim/.vimrc new file mode 100644 index 0000000..a55de80 --- /dev/null +++ b/vim/.vimrc | |||
@@ -0,0 +1,69 @@ | |||
1 | " Vim Configuration | ||
2 | " Author: clarkzjw | ||
3 | " 2016.11.14 | ||
4 | |||
5 | """"""""""""""""""""""""""""""""" | ||
6 | " Vim-Plug基础插件 | ||
7 | call plug#begin('~/.vim/plugged') | ||
8 | |||
9 | " Complete | ||
10 | Plug 'Valloric/YouCompleteMe' | ||
11 | |||
12 | " Syntax | ||
13 | Plug 'scrooloose/syntastic' | ||
14 | |||
15 | " Theme | ||
16 | Plug 'bling/vim-airline' | ||
17 | Plug 'vim-airline/vim-airline-themes' | ||
18 | Plug 'tomasr/molokai' | ||
19 | |||
20 | " Python | ||
21 | Plug 'hdima/python-syntax' | ||
22 | Plug 'hynek/vim-python-pep8-indent' | ||
23 | |||
24 | " Misc | ||
25 | Plug 'jiangmiao/auto-pairs' | ||
26 | Plug 'Yggdroot/indentLine' | ||
27 | Plug 'kien/rainbow_parentheses.vim' | ||
28 | Plug 'bronson/vim-trailing-whitespace' | ||
29 | |||
30 | call plug#end() | ||
31 | """"""""""""""""""""""""""""""""" | ||
32 | |||
33 | """"""""""""""""""""""""""""""""" | ||
34 | " 设置缩进宽度为 4 个空格 | ||
35 | set shiftwidth=4 | ||
36 | set tabstop=4 | ||
37 | set softtabstop=4 | ||
38 | set expandtab | ||
39 | |||
40 | " 打开时光标放在上次退出时的位置 | ||
41 | if has("autocmd") | ||
42 | autocmd BufReadPost * | ||
43 | \ if line("'\"") > 0 && line ("'\"") <= line("$") | | ||
44 | \ exe "normal g'\"" | | ||
45 | \ endif | ||
46 | endif | ||
47 | |||
48 | set so=10 " 光标移动到倒数第10行时开始滚屏 | ||
49 | set number " 显示行号 | ||
50 | syntax on " 打开语法高亮 | ||
51 | filetype on " 打开文件类型支持 | ||
52 | filetype plugin on " 打开文件类型插件支持 | ||
53 | filetype indent on " 打开文件类型缩进支持 | ||
54 | let mapleader = "," | ||
55 | |||
56 | |||
57 | " 模糊键 | ||
58 | :command W w | ||
59 | :command WQ wq | ||
60 | :command Wq wq | ||
61 | :command Q q | ||
62 | """"""""""""""""""""""""""""""""" | ||
63 | |||
64 | " 插件配置文件 | ||
65 | source ~/.vim/config/airline.vim | ||
66 | source ~/.vim/config/molokai.vim | ||
67 | source ~/.vim/config/ycm.vim | ||
68 | source ~/.vim/config/rainbow.vim | ||
69 | source ~/.vim/config/syntastic.vim | ||