├── .gitignore ├── .vim ├── bundle │ └── THIS IS THE DIR FOR PLUGIN MANAGER TO INSTALL PLUGINS ├── vim-notes.vim ├── vim-latex.vim ├── deoplete.vim ├── clang-format.vim ├── airline.vim ├── gtags.vim ├── racer.vim ├── vim-wiki.vim ├── ale.vim ├── base.vim └── autoload │ └── plug.vim ├── .stow-local-ignore ├── Makefile ├── README.md └── .vimrc /.gitignore: -------------------------------------------------------------------------------- 1 | .vim/autoload/*.old 2 | -------------------------------------------------------------------------------- /.vim/bundle/THIS IS THE DIR FOR PLUGIN MANAGER TO INSTALL PLUGINS: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.vim/vim-notes.vim: -------------------------------------------------------------------------------- 1 | let g:notes_directories = ['~/Documents/Notes/'] 2 | -------------------------------------------------------------------------------- /.vim/vim-latex.vim: -------------------------------------------------------------------------------- 1 | set grepprg="rg\ -nH \ $*" 2 | let g:tex_flavor = "latex" 3 | -------------------------------------------------------------------------------- /.stow-local-ignore: -------------------------------------------------------------------------------- 1 | # Ignore the project build files 2 | Makefile 3 | README.md 4 | # VCS files 5 | .git 6 | # Vim plugin files 7 | bundle/* 8 | .vim/autoload/plug.vim 9 | .gitignore 10 | -------------------------------------------------------------------------------- /.vim/deoplete.vim: -------------------------------------------------------------------------------- 1 | " Enable deoplete 2 | let g:deoplete#enable_at_startup = 1 3 | call deoplete#custom#option('sources', { 4 | \ '_': ['ale'], 5 | \}) 6 | call deoplete#enable_logging('INFO', '/tmp/'.$USER.'_deoplete.log') 7 | -------------------------------------------------------------------------------- /.vim/clang-format.vim: -------------------------------------------------------------------------------- 1 | function! FormatOnSave() 2 | let l:formatdiff = 1 3 | pyf /usr/share/clang/clang-format.py 4 | endfunction 5 | "autocmd BufWritePre *.h,*.cc,*.cpp call FormatOnSave() 6 | 7 | map :pyf /usr/share/clang/clang-format.py 8 | -------------------------------------------------------------------------------- /.vim/airline.vim: -------------------------------------------------------------------------------- 1 | " Airline Configurations 2 | set t_Co=256 " Explicitly tell Vim that the terminal supports 256 colors 3 | set laststatus=2 4 | let g:airline_powerline_fonts = 1 5 | let g:airline_theme = 'cool' 6 | let g:airline#extensions#tabline#enabled=1 7 | let g:airline#extensions#whitespace#enabled=1 8 | -------------------------------------------------------------------------------- /.vim/gtags.vim: -------------------------------------------------------------------------------- 1 | " This is the configuration file for GNU GLOBAL vim plugin 2 | 3 | nmap :copen 4 | nmap :cclose 5 | nmap :Gtags 6 | nmap :Gtags -f % 7 | nmap :GtagsCursor 8 | nmap :Gozilla 9 | nmap :cn 10 | nmap :cp 11 | nmap ] :GtagsCursor 12 | let g:Gtags_OpenQuickfixWindow = 1 13 | 14 | -------------------------------------------------------------------------------- /.vim/racer.vim: -------------------------------------------------------------------------------- 1 | let g:racer_cmd = "~/.cargo/bin/racer" 2 | let g:racer_experimental_completer = 1 3 | 4 | autocmd FileType * :call RustChangeTabMapping() 5 | 6 | function RustChangeTabMapping() 7 | if (&filetype == "rust") 8 | inoremap pumvisible() ? "\" : SmartTab() 9 | endif 10 | endfunction 11 | 12 | function! SmartTab() 13 | let col = col('.') - 1 14 | if !col || getline('.')[col - 1] == ' ' 15 | return "\" 16 | else 17 | echo "Completing" 18 | return "\\" 19 | endif 20 | endfunction 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PWD = $(shell pwd) 2 | 3 | # Use the Makefile string subsitution to retrieve the basedir name 4 | PACKAGE = $(lastword $(subst /, , $(PWD))) 5 | 6 | .PHONY: install clean uninstall 7 | 8 | simulation: 9 | - @stow --dir=$(shell pwd)/../ $(PACKAGE) --target=$(HOME) -n -v 10 | 11 | install: ~/.vim/autoload/plug.vim 12 | - @echo "WARNING! This operation will install the configuration files into your filesystem, really proceeding ? (Ctrl-C to abort, Other keys to proceed)" 13 | - @read 14 | - @stow --dir=$(shell pwd)/../ $(PACKAGE) --target=$(HOME) 15 | - @vim +"PlugInstall | echohl StatusLineTermNC | echom \"Plug install finished, enjoy your new vim configuration! (:qa! to quit)\"" 16 | 17 | uninstall: 18 | - @stow -D --dir=$(shell pwd)/../ $(PACKAGE) --target=$(HOME) 19 | 20 | ~/.vim/autoload/plug.vim: 21 | - mkdir -p ~/.vim/autoload 22 | - @curl -fLo ~/.vim/autoload/plug.vim --create-dirs \ 23 | https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim 24 | -------------------------------------------------------------------------------- /.vim/vim-wiki.vim: -------------------------------------------------------------------------------- 1 | let g:vimwiki_user_root = "/home/void001/Documents/Notes" 2 | let g:vimwiki_list = [{ 3 | \ 'path': '~/Documents/Notes/', 'index': 'index', 'ext': '.mw', 4 | \ 'auto_tags': 1, 5 | \ 'nested_syntaxes': {'py': 'python', 'cpp': 'cpp', 'c':'c'} 6 | \ }] 7 | let g:vimwiki_folding = 'expr' 8 | nmap tt VimwikiToggleListItem 9 | 10 | function! SaveAndUpdate() 11 | let cwd = getcwd() 12 | let fname = expand('%:t') 13 | silent exe 'cd ' . g:vimwiki_user_root 14 | echohl StatusLineTermNC | echom "Updating your Notes repository ..." 15 | silent exe '!git add .' 16 | silent exe '!git commit -s -m "' 'Update file(s): '. fname . '">/dev/null' 17 | silent exe '!git push >/dev/null 2>/dev/null' 18 | echohl StatusLineTermNC | echom "Done!" 19 | echohl None 20 | silent exe 'cd' cwd 21 | endfunction 22 | 23 | " TODO: Maybe we need finer granularity e.g /home/void001/Documents/Notes/*.mw 24 | " But currently I don't know how to pass the g:vimwiki_user_root argument to autocmd 25 | autocmd BufWritePost *.mw call SaveAndUpdate() 26 | -------------------------------------------------------------------------------- /.vim/ale.vim: -------------------------------------------------------------------------------- 1 | let g:go_code_completion_enabled = 0 " Disable the completion for vim-go, we have ale 2 | let g:ale_linters = { 3 | \ 'cpp': ['ccls'], 4 | \ 'rust': ['analyzer', 'cargo'], 5 | \ 'go': ['gopls'], 6 | \ 'sh': ['shellcheck'] 7 | \ } 8 | let g:ale_cpp_ccls_init_options = { 9 | \ 'cache': { 10 | \ 'directory': '/tmp/ccls/cache', 11 | \ }, 12 | \ } 13 | " let g:ale_completion_enabled = 1 14 | 15 | autocmd FileType * :call CheckIfToggleALEShortCut() 16 | 17 | function CheckIfToggleALEShortCut() 18 | if (&filetype == "cpp" || &filetype == "rust") 19 | nnoremap ] :ALEGoToDefinition 20 | endif 21 | inoremap pumvisible() ? "\" : SmartTab() 22 | endfunction 23 | 24 | function! SmartTab() 25 | let col = col('.') - 1 26 | if !col || getline('.')[col - 1] == ' ' 27 | return "\" 28 | else 29 | return deoplete#complete() 30 | endif 31 | endfunction 32 | 33 | " NOTE: You need to have the following dependencies for language completion 34 | " support: 35 | " c/cpp: ccls 36 | " rust: rust-analyzer 37 | " go: gopls 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | VOID-vimrc 2 | ==== 3 | 4 | * **NOTE**: Due to compatibility issues on OS X, the config is not supported on OS X yet. 5 | 6 | ## Supported Features: 7 | * Easy multi-tab support + navigation 8 | * C/C++, Rust, Golang, Linter support + Navigation + Completion. Shellscript Linter Support 9 | * Nice and informative airline 10 | * Cloud sync notebook based on vim-wiki (Sync to any git server you want) 11 | * NERDTree Support 12 | 13 | 14 | ## Requirement 15 | 16 | Basic requirement 17 | * Python3 (Needed by various plugins) 18 | * Vim >=8.0 with +python3 support (Of course! This is a vim repo) 19 | * curl (Used to download plug.vim) 20 | * GNU Makefile 21 | * GNU Stow 22 | * python pynvim (install from pip or your favorite package manager) 23 | 24 | For completion support, you may need the following dependencies: 25 | 26 | * C/cpp: ccls 27 | * Rust: rust-analyzer 28 | * Golang: gopls 29 | 30 | ## Installation 31 | 32 | ```shell 33 | mkdir -p $HOME/stow-root 34 | cd $HOME/stow-root/ 35 | git clone https://github.com/VOID001/VOID-vimrc 36 | cd VOID-vimrc 37 | ``` 38 | * Run `make simulation` to see what will happen if you run the installation 39 | * Run `make install` to install the config files into your home directory 40 | 41 | ## Usage 42 | 43 | * For installation just need to follow the **installation** section 44 | * NOTE: in order to make vim-wiki works correctly, you need to change the variable `g:viwiki_user_root` in .vim/vim-wiki.vim 45 | * \\\\ for open the tree view 46 | * \\ww for open the vim-wiki index page 47 | * C-n for a new tab 48 | * C-l for moving to the right tab 49 | * C-h for moving to the left tab 50 | * `` in insert mode for completion (powered by ALE + Deoplete) 51 | -------------------------------------------------------------------------------- /.vimrc: -------------------------------------------------------------------------------- 1 | " VOID001's Custom Vim Configuration File 2 | " = BEGIN GPLv3 = 3 | " Copyright (C) 2017-2020 Jianqiu Zhang 4 | " 5 | " This program is free software: you can redistribute it and/or modify 6 | " it under the terms of the GNU General Public License as published by 7 | " the Free Software Foundation, either version 3 of the License, or 8 | " (at your option) any later version. 9 | " 10 | " This program is distributed in the hope that it will be useful, 11 | " but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | " GNU General Public License for more details. 14 | " 15 | " You should have received a copy of the GNU General Public License 16 | " along with this program. If not, see . 17 | " = END GPLv3 = 18 | 19 | syntax on 20 | filetype plugin indent on 21 | 22 | call plug#begin('~/.vim/bundle') 23 | 24 | Plug 'WolfgangMehner/bash-support' 25 | Plug 'scrooloose/nerdtree' " File Tree Support 26 | Plug 'vim-airline/vim-airline' 27 | Plug 'vim-airline/vim-airline-themes' 28 | Plug 'Yggdroot/indentLine' 29 | 30 | Plug 'jiangmiao/auto-pairs' " Insert or delete brackets, parens, quotes in pair 31 | Plug 'majutsushi/tagbar' " Tagbar 32 | Plug 'fatih/vim-go' , {'for': ['go']} 33 | 34 | Plug 'Firef0x/PKGBUILD.vim' 35 | Plug 'vim-scripts/gtags.vim' , {'for': ['c', 'cpp']} 36 | Plug 'vimwiki/vimwiki', { 'branch': 'dev' } 37 | 38 | Plug 'w0rp/ale', {'for': ['sh', 'c', 'cpp', 'rust', 'python', 'go']} " Asynchornous Lint Engine 39 | 40 | if has('nvim') 41 | Plug 'Shougo/deoplete.nvim', { 'do': ':UpdateRemotePlugins' } 42 | else 43 | Plug 'Shougo/deoplete.nvim' 44 | Plug 'roxma/nvim-yarp' 45 | Plug 'roxma/vim-hug-neovim-rpc' 46 | endif 47 | 48 | Plug 'rust-lang/rust.vim' , {'for': ['rust']} 49 | 50 | Plug 'python-mode/python-mode', {'for': 'python'} 51 | Plug 'rhysd/vim-clang-format', {'for': 'cpp'} 52 | Plug 'davidhalter/jedi-vim', {'for': 'python'} 53 | Plug 'VOID001/graph-easy-vim' 54 | Plug 'godlygeek/tabular' " Align your data as you wish 55 | 56 | " Plug 'vim/killersheep' " Vim game for demoing the new feature > < 57 | call plug#end() 58 | 59 | syntax on 60 | set nocompatible " Remove all vi compatible features to have a better experience 61 | set wildmenu " Use enhanced menu when completing the commands in vim command buffer 62 | set rnu 63 | autocmd InsertEnter * :set nu 64 | autocmd InsertLeave * :set rnu 65 | 66 | set incsearch " Use increment search 67 | set hlsearch " Use highlight search 68 | set autoread " Autoread file when modified 69 | " Set the indentation styles editor-wide 70 | set tabstop=4 71 | set softtabstop=4 72 | set shiftwidth=4 73 | set autoindent 74 | set smartindent 75 | set backspace=2 76 | 77 | "Set encoding 78 | set fencs=utf-8,ucs-bom,shift-jis,gb18030,gbk,gb2312,cp936 79 | set termencoding=utf-8 80 | set encoding=utf-8 81 | set fileencodings=ucs-bom,utf-8,cp936 82 | set fileencoding=utf-8 83 | set nobackup " Do not create .swp file for each file opened 84 | set directory = "~/.cache/vim/swap" " Create swap file in a separate directory 85 | set expandtab 86 | set noshowmode 87 | set encoding=utf8 88 | 89 | " auto-pairs Configurations 90 | let g:AutoPairs = { '{':'}' } 91 | let g:AutoPairsMultilineClose = 0 92 | " taglist Configuration 93 | let Tlist_Sort_Type = "name" " 按照名称排序 94 | let Tlist_Use_Right_Window = 1 " 在右侧显示窗口 95 | let Tlist_Compart_Format = 1 " 压缩方式 96 | let Tlist_Exist_OnlyWindow = 1 " 如果只有一个buffer,kill窗口也kill掉buffer 97 | let Tlist_File_Fold_Auto_Close = 0 " 不要关闭其他文件的tags 98 | let Tlist_Enable_Fold_Column = 0 " 不要显示折叠树 99 | 100 | " Vim-go Configuration 101 | let g:go_bin_path = "/home/" . $USER . "/GOPATH/bin" 102 | au FileType go nmap gd (go-doc) 103 | au FileType go nmap s (go-implements) 104 | au FileType go nmap gb (go-doc-browser) 105 | let g:go_highlight_functions = 1 106 | let g:go_highlight_fields = 1 107 | let g:go_highlight_methods = 1 108 | let g:go_highlight_structs = 1 109 | let g:go_highlight_interfaces = 1 110 | let g:go_highlight_operators = 1 111 | let g:go_highlight_build_constraints = 1 112 | 113 | " My general keymap configuration 114 | noremap :NERDTreeToggle 115 | map :tabnew 116 | map :tabclose 117 | map gt 118 | map gT 119 | map :TlistToggle 120 | "" 带有如下符号的单词不要被换行分割 121 | set iskeyword+=_,$,@,%,#,- 122 | set tags=tags;/ 123 | 124 | " Load user config files only if the file exist, supress warning 125 | function! LoadCustomConfig(config) 126 | if filereadable(expand(a:config)) 127 | exec 'source' a:config 128 | endif 129 | endfunction 130 | command! -nargs=1 LoadConfig call LoadCustomConfig() 131 | 132 | 133 | " LoadConfig ~/.vim/denite.vim 134 | LoadConfig ~/.vim/deoplete.vim 135 | LoadConfig ~/.vim/airline.vim 136 | LoadConfig ~/.vim/vim-notes.vim 137 | LoadConfig ~/.vim/vim-latex.vim 138 | LoadConfig ~/.vim/gtags.vim 139 | LoadConfig ~/.vim/vim-wiki.vim 140 | LoadConfig ~/.vim/ale.vim 141 | LoadConfig ~/.vim/clang-format.vim 142 | " The following config file doesn't exist now 143 | LoadConfig ~/.vim/vim-go.vim 144 | -------------------------------------------------------------------------------- /.vim/base.vim: -------------------------------------------------------------------------------- 1 | "======================================PLUGIN CONFIGURATION================================================ 2 | " 3 | " Set Up Vundle 4 | runtime! .vim 5 | set nocompatible 6 | filetype off 7 | set rtp+=~/.vim/bundle/Vundle.vim 8 | filetype plugin indent on 9 | 10 | call plug#begin('~/.vim/bundle') 11 | Plug 'vim/killersheep' 12 | Plug 'scrooloose/nerdtree' "File Tree Support 13 | " Plug 'Shougo/vimproc' " Vimshell dependencies 14 | " Plug 'rdnetto/YCM-Generator' "Using for generate .ycm_extra_config.py 15 | Plug 'jiangmiao/auto-pairs' "Insert or delete brackets, parens, quotes in pair 16 | Plug 'vim-scripts/taglist.vim' "tag viewer 17 | " Plug 'shawncplus/phpcomplete.vim', {'for': 'php'} "php omnicompletion 18 | Plug 'fatih/vim-go' 19 | ", {'for': ['golang', 'go']} 20 | " Plug 'wikitopian/hardmode' 21 | Plug 'Shougo/denite.nvim' 22 | " Plug 'jceb/vim-orgmode' 23 | Plug 'Firef0x/PKGBUILD.vim' 24 | " Plug 'vim-latex/vim-latex' 25 | Plug 'vim-scripts/gtags.vim' , {'for': ['c', 'cpp']} 26 | Plug 'vimwiki/vimwiki', { 'branch': 'dev' } 27 | " Plug 'vim-scripts/bad-apple' 28 | Plug 'w0rp/ale', {'for': ['c', 'cpp', 'rust', 'python', 'go']} 29 | Plug 'rust-lang/rust.vim' , {'for': ['rust']} 30 | " Plug 'racer-rust/vim-racer', {'for': ['rust']} 31 | 32 | " Autocomplete deoplete 33 | " if has('nvim') 34 | " Plug 'Shougo/deoplete.nvim', { 'do': ':UpdateRemotePlugins' } 35 | " else 36 | " Plug 'Shougo/deoplete.nvim', {'for': ['cpp'] } 37 | " Plug 'roxma/nvim-yarp', {'for': ['cpp'] } 38 | " Plug 'roxma/vim-hug-neovim-rpc', {'for': ['cpp'] } 39 | " endif 40 | 41 | Plug 'python-mode/python-mode', {'for': 'python'} 42 | Plug 'davidhalter/jedi-vim', {'for': 'python'} 43 | Plug 'VOID001/graph-easy-vim' 44 | Plug 'rhysd/vim-clang-format', {'for': 'cpp'} 45 | 46 | call plug#end() 47 | 48 | "auto-pairs Configurations 49 | let g:AutoPairs = { '{':'}' } 50 | let g:AutoPairsMultilineClose = 0 51 | 52 | "Airline Configurations 53 | set noshowmode 54 | set encoding=utf8 55 | 56 | " Deoplete Enable 57 | " let g:deoplete#enable_at_startup = 1 58 | 59 | "set fillchars+=stl:\ ,stlnc:\ 60 | set t_Co=256 " Explicitly tell Vim that the terminal supports 256 colors 61 | set laststatus=2 62 | let g:airline_powerline_fonts = 1 63 | let g:airline_theme = 'cool' 64 | let g:airline#extensions#tabline#enabled=1 65 | let g:airline#extensions#whitespace#enabled=1 66 | let g:ycm_show_diagnostics_ui = 1 67 | syntax on 68 | set showcmd 69 | 70 | "YouCompleteme Configurations 71 | let g:ycm_global_ycm_extra_conf = '~/.ycm_extra_conf.py' 72 | let g:ycm_collect_identifiers_from_tag_files = 1 73 | let g:ycm_confirm_extra_conf = 0 74 | 75 | " let g:ycm_gocode_binary_path="$GOPATH/bin/gocode" 76 | " let g:ycm_godef_binary_path="$GOPATH/bin/godef" 77 | let g:ycm_gocode_binary_path="/home/void001/GOPATH/bin/gocode" 78 | let g:ycm_godef_binary_path="/home/void001/GOPATH/bin/godef" 79 | 80 | "taglist Configuration 81 | let Tlist_Sort_Type = "name" " 按照名称排序 82 | let Tlist_Use_Right_Window = 1 " 在右侧显示窗口 83 | let Tlist_Compart_Format = 1 " 压缩方式 84 | let Tlist_Exist_OnlyWindow = 1 " 如果只有一个buffer,kill窗口也kill掉buffer 85 | let Tlist_File_Fold_Auto_Close = 0 " 不要关闭其他文件的tags 86 | let Tlist_Enable_Fold_Column = 0 " 不要显示折叠树 87 | 88 | "Vim-go Configuration 89 | let g:go_bin_path = "/home/void001/GOPATH/bin" 90 | au FileType go nmap gd (go-doc) 91 | au FileType go nmap s (go-implements) 92 | au FileType go nmap gb (go-doc-browser) 93 | let g:go_highlight_functions = 1 94 | let g:go_highlight_methods = 1 95 | let g:go_highlight_fields = 1 96 | let g:go_highlight_structs = 1 97 | let g:go_highlight_interfaces = 1 98 | let g:go_highlight_operators = 1 99 | let g:go_highlight_build_constraints = 1 100 | 101 | "Syntastic Configuration 102 | " set statusline+=%#warningmsg# 103 | " set statusline+=%{SyntasticStatuslineFlag()} 104 | " set statusline+=%* 105 | 106 | let g:syntastic_always_populate_loc_list = 0 107 | let g:syntastic_auto_loc_list = 0 108 | let g:syntastic_check_on_open = 0 109 | let g:syntastic_check_on_wq = 0 110 | let g:syntastic_go_checkers = ['go', 'golint', 'gofmt'] 111 | let g:syntastic_quiet_messages = {"level": "warnings" } 112 | 113 | " 114 | "======================================BASIC CONFIGURATION================================================ 115 | set rnu 116 | autocmd InsertEnter * :set nu 117 | autocmd InsertLeave * :set rnu 118 | syntax on 119 | set incsearch "Use increment search 120 | set hlsearch "Use highlight search 121 | set autoread "Autoread file when modified 122 | set tabstop=4 123 | set softtabstop=4 124 | set shiftwidth=4 125 | set autoindent 126 | set smartindent 127 | set backspace=2 128 | set mouse=a "Enable Mouse 129 | 130 | "Set encoding 131 | set fencs=utf-8,ucs-bom,shift-jis,gb18030,gbk,gb2312,cp936 132 | set termencoding=utf-8 133 | set encoding=utf-8 134 | set fileencodings=ucs-bom,utf-8,cp936 135 | set fileencoding=utf-8 136 | set nobackup "Do not create .swp file for each file opened 137 | set noswapfile "Do not create swp file 138 | set expandtab 139 | 140 | "======================================PLUGIN KEYMAP CONFIGURATION================================================ 141 | "map n :NERDTreeToggle 142 | noremap :NERDTreeToggle 143 | noremap ] :YcmCompleter GoToDefinitionElseDeclaration 144 | map :tabnew 145 | map :tabclose 146 | map gt 147 | map gT 148 | map :TlistToggle 149 | 150 | "" All system-wide defaults are set in $VIMRUNTIME/debian.vim and sourced by 151 | "" the call to :runtime you can find below. If you wish to change any of those 152 | "" settings, you should do it in this file (/etc/vim/vimrc), since debian.vim 153 | "" will be overwritten everytime an upgrade of the vim packages is performed. 154 | "" It is recommended to make changes after sourcing debian.vim since it alters 155 | "" the value of the 'compatible' option. 156 | " 157 | "" This line should not be removed as it ensures that various options are 158 | "" properly set to work with the Vim-related packages available in Debian. 159 | "runtime! debian.vim 160 | " 161 | "" Uncomment the next line to make Vim more Vi-compatible 162 | "" NOTE: debian.vim sets 'nocompatible'. Setting 'compatible' changes numerous 163 | "" options, so any other options should be set AFTER setting 'compatible'. 164 | ""set compatible 165 | " 166 | "" Vim5 and later versions support syntax highlighting. Uncommenting the next 167 | "" line enables syntax highlighting by default. 168 | "if has("syntax") 169 | " syntax on 170 | "endif 171 | " 172 | "" If using a dark background within the editing area and syntax highlighting 173 | "" turn on this option as well 174 | ""set background=dark 175 | " 176 | "" Uncomment the following to have Vim jump to the last position when 177 | "" reopening a file 178 | ""if has("autocmd") 179 | "" au BufReadPost * if line("'\"") > 1 && line("'\"") <= line("$") | exe "normal! g'\"" | endif 180 | ""endif 181 | " 182 | "" Uncomment the following to have Vim load indentation rules and plugins 183 | "" according to the detected filetype. 184 | ""if has("autocmd") 185 | "" filetype plugin indent on 186 | ""endif 187 | " 188 | "" The following are commented out as they cause vim to behave a lot 189 | "" differently from regular Vi. They are highly recommended though. 190 | ""set showcmd " Show (partial) command in status line. 191 | ""set showmatch " Show matching brackets. 192 | ""set ignorecase " Do case insensitive matching 193 | ""set smartcase " Do smart case matching 194 | ""set incsearch " Incremental search 195 | ""set autowrite " Automatically save before commands like :next and :make 196 | ""set hidden " Hide buffers when they are abandoned 197 | ""set mouse=a " Enable mouse usage (all modes) 198 | " 199 | "" Source a global configuration file if available 200 | "if filereadable("/etc/vim/vimrc.local") 201 | " source /etc/vim/vimrc.local 202 | "endif 203 | " 204 | "colorscheme desert 205 | "set tabstop=4 206 | "set softtabstop=4 207 | "set shiftwidth =4 208 | ""set guifont=Consolas:h15 209 | "set nu! 210 | "set ai 211 | "set guifont=Consolas:h15:cANSI " 设置字体 212 | " 213 | ""==========================================BELOW IS NOT MY OWN CONFIG +============================================ 214 | " 215 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 216 | "" 显示相关 217 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 218 | ""set shortmess=atI " 启动的时候不显示那个援助乌干达儿童的提示 219 | ""winpos 5 5 " 设定窗口位置 220 | ""set lines=40 columns=155 " 设定窗口大小 221 | ""set nu " 显示行号 222 | "set go= " 不要图形按钮 223 | ""color asmanian2 " 设置背景主题 224 | ""syntax on " 语法高亮 225 | "autocmd InsertLeave * se nocul " 用浅色高亮当前行 226 | "autocmd InsertEnter * se cul " 用浅色高亮当前行 227 | ""set ruler " 显示标尺 228 | "set showcmd " 输入的命令显示出来,看的清楚些 229 | ""set cmdheight=1 " 命令行(在状态行下)的高度,设置为1 230 | "set whichwrap+=<,>,h,l " 允许backspace和光标键跨越行边界(不建议) 231 | ""set scrolloff=3 " 光标移动到buffer的顶部和底部时保持3行距离 232 | ""set novisualbell " 不要闪烁(不明白) 233 | "set statusline=%F%m%r%h%w\ [FORMAT=%{&ff}]\ [TYPE=%Y]\ [POS=%l,%v][%p%%]\ %{strftime(\"%d/%m/%y\ -\ %H:%M\")} "状态行显示的内容 234 | "set laststatus=1 " 启动显示状态行(1),总是显示状态行(2) 235 | "set foldenable " 允许折叠 236 | "set foldmethod=manual " 手动折叠 237 | "set background=dark "背景使用黑色 238 | "set nocompatible "去掉讨厌的有关vi一致性模式,避免以前版本的一些bug和局限 239 | "" 显示中文帮助 240 | "if version >= 603 241 | " set helplang=cn 242 | " set encoding=utf-8 243 | "endif 244 | "" 设置配色方案 245 | ""colorscheme murphy 246 | ""字体 247 | ""if (has("gui_running")) 248 | "" set guifont=Bitstream\ Vera\ Sans\ Mono\ 10 249 | ""endif 250 | " 251 | " 252 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 253 | """"""新文件标题"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 254 | autocmd BufNewFile *.cpp,*.[ch],*.sh, exec ":call SetTitle()" 255 | """定义函数SetTitle,自动插入文件头 256 | func! SetTitle() 257 | "如果文件类型为.sh文件 258 | if &filetype == 'sh' 259 | call setline(1,"\#########################################################################") 260 | call append(line("."), "\# File Name: ".expand("%")) 261 | call append(line(".")+1, "\# Author: VOID_133") 262 | call append(line(".")+2,"\# QQ: #########") 263 | call append(line(".")+3, "\# mail: ####@gmail.com") 264 | call append(line(".")+4, "\# Created Time: ".strftime("%c")) 265 | call append(line(".")+5, "\#########################################################################") 266 | call append(line(".")+6, "\#!/bin/bash") 267 | call append(line(".")+7, "") 268 | else 269 | call setline(1, "/*************************************************************************") 270 | call append(line("."), " > File Name: ".expand("%")) 271 | call append(line(".")+1, " > Author: VOID_133") 272 | call append(line(".")+2, " > ################### ") 273 | call append(line(".")+3, " > Mail: ################### ") 274 | call append(line(".")+4, " > Created Time: ".strftime("%c")) 275 | call append(line(".")+5, " ************************************************************************/") 276 | call append(line(".")+6, "") 277 | endif 278 | if &filetype == 'cpp' 279 | " call append(line(".")+6, "#include") 280 | " call append(line(".")+7, "#include") 281 | " call append(line(".")+8, "#include") 282 | " call append(line(".")+9, "#include") 283 | " call append(line(".")+10, "#include") 284 | " call append(line(".")+11, "#include") 285 | " call append(line(".")+12, "#include") 286 | " call append(line(".")+13, "#include") 287 | " call append(line(".")+14, "#include") 288 | " call append(line(".")+15, "#include") 289 | " call append(line(".")+16, "#include") 290 | " call append(line(".")+17, "#include") 291 | " call append(line(".")+18, "using namespace std;") 292 | " call append(line(".")+19, " /*MACRO*/") 293 | " call append(line(".")+20, "#define FORi(l,r) for(int i=(l);i<(r);i++)") 294 | " call append(line(".")+21, "#define FORj(l,r) for(int j=(l);j<(r);j++)") 295 | " call append(line(".")+22, "#define FORk(l,r) for(int k=(l);k<(r);k++)") 296 | " call append(line(".")+23, "#define MEMSET0(i) memset((i),0,sizeof((i)))") 297 | " call append(line(".")+24, "#define MEMSET1(i) memset((i),-1,sizeof((i)))") 298 | " call append(line(".")+25, "") 299 | endif 300 | if &filetype == 'c' 301 | call append(line(".")+6, "#include") 302 | call append(line(".")+7, "#include") 303 | call append(line(".")+8, "#include") 304 | call append(line(".")+9, "#include") 305 | call append(line(".")+10, "#include") 306 | call append(line(".")+11, "#include") 307 | call append(line(".")+12, "#include") 308 | call append(line(".")+13, "#include") 309 | call append(line(".")+14, "#include") 310 | call append(line(".")+15, "") 311 | endif 312 | endfunc 313 | "新建文件后,自动定位到文件末尾 314 | autocmd BufNewFile * normal G 315 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 316 | ""键盘命令 317 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 318 | " 319 | "nmap w :w! 320 | "nmap f :find 321 | " 322 | "" 映射全选+复制 ctrl+a 323 | ""map ggVGY 324 | ""map! ggVGY 325 | ""map gg=G 326 | "" 选中状态下 Ctrl+c 复制 327 | "vmap "+y 328 | ""去空行 329 | "nnoremap :g/^\s*$/d 330 | ""比较文件 331 | "nnoremap :vert diffsplit 332 | ""新建标签 333 | "map :tabnew 334 | "map :tabNext 335 | ""map :tabnew 336 | ""列出当前目录文件 337 | "map :tabnew . 338 | ""打开树状文件目录 339 | "map \be 340 | ""C,C++ 按F5编译运行 341 | "map :call CompileRunGcc() 342 | "func! CompileRunGcc() 343 | " exec "w" 344 | " if &filetype == 'c' 345 | " exec "!g++ % -o %<" 346 | " exec "! ./%<" 347 | " elseif &filetype == 'cpp' 348 | " exec "!g++ % -o %<" 349 | " exec "! ./%<" 350 | " elseif &filetype == 'java' 351 | " exec "!javac %" 352 | " exec "!java %<" 353 | " elseif &filetype == 'sh' 354 | " :!./% 355 | " endif 356 | "endfunc 357 | ""C,C++的调试 358 | "map :call Rungdb() 359 | "func! Rungdb() 360 | " exec "w" 361 | " exec "!g++ % -g -o %<" 362 | " exec "!gdb ./%<" 363 | "endfunc 364 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 365 | """实用设置 366 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 367 | "" 设置当文件被改动时自动载入 368 | "set autoread 369 | "" quickfix模式 370 | "autocmd FileType c,cpp map :w:make 371 | ""代码补全 372 | "set completeopt=preview,menu 373 | ""允许插件 374 | ""filetype plugin on 375 | ""共享剪贴板 376 | "set clipboard+=unnamed 377 | ""从不备份 378 | "set nobackup 379 | ""make 运行 380 | ":set makeprg=g++\ -Wall\ \ % 381 | ""自动保存 382 | "set autowrite 383 | "set ruler " 打开状态栏标尺 384 | "set cursorline " 突出显示当前行 385 | "set magic " 设置魔术 386 | "set guioptions-=T " 隐藏工具栏 387 | "set guioptions-=m " 隐藏菜单栏 388 | ""set statusline=\ %<%F[%1*%M%*%n%R%H]%=\ %y\ %0(%{&fileformat}\ %{&encoding}\ %c:%l/%L%)\ 389 | "" 设置在状态行显示的信息 390 | "set foldcolumn=0 391 | "set foldmethod=indent 392 | "set foldlevel=3 393 | "set foldenable " 开始折叠 394 | "" 不要使用vi的键盘模式,而是vim自己的 395 | "set nocompatible 396 | "" 语法高亮 397 | "set syntax=on 398 | "" 去掉输入错误的提示声音 399 | "set noeb 400 | "" 在处理未保存或只读文件的时候,弹出确认 401 | "set confirm 402 | "" 自动缩进 403 | "set autoindent 404 | "set smartindent 405 | "set cindent 406 | "" Tab键的宽度 407 | "set tabstop=4 408 | "" 统一缩进为4 409 | "set softtabstop=4 410 | "set shiftwidth=4 411 | "" 不要用空格代替制表符 412 | "set noexpandtab 413 | "" 在行和段开始处使用制表符 414 | "set smarttab 415 | "" 显示行号 416 | "set number 417 | "" 历史记录数 418 | "set history=1000 419 | ""禁止生成临时文件 420 | "set nobackup 421 | "set noswapfile 422 | ""搜索忽略大小写 423 | ""set ignorecase 424 | ""搜索逐字符高亮 425 | "set hlsearch 426 | "set incsearch 427 | ""行内替换 428 | "set gdefault 429 | ""编码设置 430 | "set enc=utf-8 431 | "set fencs=utf-8,ucs-bom,shift-jis,gb18030,gbk,gb2312,cp936 432 | ""语言设置 433 | "set langmenu=zh_CN.UTF-8 434 | "set helplang=cn 435 | "" 我的状态行显示的内容(包括文件类型和解码) 436 | ""set statusline=%F%m%r%h%w\ [FORMAT=%{&ff}]\ [TYPE=%Y]\ [POS=%l,%v][%p%%]\ %{strftime(\"%d/%m/%y\ -\ %H:%M\")} 437 | ""set statusline=[%F]%y%r%m%*%=[Line:%l/%L,Column:%c][%p%%] 438 | "" 总是显示状态行 439 | "set laststatus=2 440 | "" 命令行(在状态行下)的高度,默认为1,这里是2 441 | "set cmdheight=2 442 | "" 侦测文件类型 443 | ""filetype on 444 | "" 载入文件类型插件 445 | ""filetype plugin on 446 | "" 为特定文件类型载入相关缩进文件 447 | ""filetype indent on 448 | "" 保存全局变量 449 | "set viminfo+=! 450 | "" 带有如下符号的单词不要被换行分割 451 | set iskeyword+=_,$,@,%,#,- 452 | "" 字符间插入的像素行数目 453 | "set linespace=0 454 | "" 增强模式中的命令行自动完成操作 455 | "set wildmenu 456 | "" 使回格键(backspace)正常处理indent, eol, start等 457 | "set backspace=2 458 | "" 允许backspace和光标键跨越行边界 459 | "set whichwrap+=<,>,h,l 460 | "" 可以在buffer的任何地方使用鼠标(类似office中在工作区双击鼠标定位) 461 | "set mouse=a 462 | "set selection=exclusive 463 | "set selectmode=mouse,key 464 | "" 通过使用: commands命令,告诉我们文件的哪一行被改变过 465 | "set report=0 466 | "" 在被分割的窗口间显示空白,便于阅读 467 | "set fillchars=vert:\ ,stl:\ ,stlnc:\ 468 | "" 高亮显示匹配的括号 469 | "set showmatch 470 | "" 匹配括号高亮的时间(单位是十分之一秒) 471 | "set matchtime=1 472 | "" 光标移动到buffer的顶部和底部时保持3行距离 473 | "set scrolloff=3 474 | "" 为C程序提供自动缩进 475 | ""set smartindent 476 | "" 高亮显示普通txt文件(需要txt.vim脚本) 477 | "au BufRead,BufNewFile * setfiletype txt 478 | """"自动补全 479 | """:inoremap ( ()i 480 | """:inoremap ) =ClosePair(')') 481 | """:inoremap { {}O 482 | """:inoremap } =ClosePair('}') 483 | """:inoremap [ []i 484 | """:inoremap ] =ClosePair(']') 485 | """:inoremap " ""i 486 | """:inoremap ' ''i 487 | """function! ClosePair(char) 488 | """ if getline('.')[col('.') - 1] == a:char 489 | """ return "\" 490 | """ else 491 | """ return a:char 492 | """ endif 493 | """endfunction 494 | """filetype plugin indent on 495 | ""打开文件类型检测, 加了这句才可以用智能补全 496 | "set completeopt=longest,menu 497 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 498 | "" CTags的设定 499 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 500 | "let Tlist_Sort_Type = "name" " 按照名称排序 501 | "let Tlist_Use_Right_Window = 1 " 在右侧显示窗口 502 | "let Tlist_Compart_Format = 1 " 压缩方式 503 | "let Tlist_Exist_OnlyWindow = 1 " 如果只有一个buffer,kill窗口也kill掉buffer 504 | "let Tlist_File_Fold_Auto_Close = 0 " 不要关闭其他文件的tags 505 | "let Tlist_Enable_Fold_Column = 0 " 不要显示折叠树 506 | ""autocmd FileType java set tags+=D:\tools\java\tags 507 | ""autocmd FileType h,cpp,cc,c set tags+=D:\tools\cpp\tags 508 | ""let Tlist_Show_One_File=1 "不同时显示多个文件的tag,只显示当前文件的 509 | ""设置tags 510 | set tags=tags;/ 511 | ""set autochdir 512 | " 513 | """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 514 | ""其他东东 515 | """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" 516 | ""默认打开Taglist 517 | "let Tlist_Auto_Open=1 518 | """"""""""""""""""""""""""""""" 519 | "" Tag list (ctags) 520 | """"""""""""""""""""""""""""""""" 521 | "let Tlist_Ctags_Cmd = '/usr/bin/ctags' 522 | "let Tlist_Show_One_File = 1 "不同时显示多个文件的tag,只显示当前文件的 523 | "let Tlist_Exit_OnlyWindow = 1 "如果taglist窗口是最后一个窗口,则退出vim 524 | "let Tlist_Use_Right_Window = 1 "在右侧窗口中显示taglist窗口 525 | "" minibufexpl插件的一般设置 526 | "let g:miniBufExplMapWindowNavVim = 1 527 | "let g:miniBufExplMapWindowNavArrows = 1 528 | "let g:miniBufExplMapCTabSwitchBufs = 1 529 | " 530 | "let g:miniBufExplModSelTarget = 1 531 | " 532 | " 533 | " 534 | ""VOID's Own Conf''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 535 | ""call pathogen#infect() 536 | " 537 | " 538 | """powerline{ 539 | ""set guifont=PowerlineSymbols\ for\ Powerline 540 | ""set nocompatible 541 | ""set t_Co=256 542 | ""let g:Powerline_symbols = 'fancy' 543 | """} 544 | " 545 | " 546 | """taglist{ 547 | ""let Tlist_Show_One_File = 1 548 | """只显示当前文件的taglist,默认是显示多个 549 | ""let Tlist_Exit_OnlyWindow = 1 550 | """如果taglist是最后一个窗口,则退出vim 551 | ""let Tlist_Use_Right_Window = 1 "在右侧窗口中显示taglist 552 | ""let Tlist_GainFocus_On_ToggleOpen = 1 553 | """打开taglist时,光标保留在taglist窗口 554 | ""let Tlist_Ctags_Cmd='/usr/bin/ctags' 555 | """设置ctags命令的位置 556 | ""nnoremap tl : Tlist 557 | """设置关闭和打开taglist窗口的快捷键 558 | """} 559 | "set sessionoptions-=options 560 | " 561 | """"""""""""""""""""""""""""""""""""""""""""""""""""" 562 | "High Light Tabs 563 | let g:HighLightTabsInCodeStatus=0 564 | function! HighLightTabsInCode() 565 | if g:HighLightTabsInCodeStatus == 0 566 | highlight default link TabInCode ErrorMsg 567 | match TabInCode /\t/ 568 | let g:HighLightTabsInCodeStatus=1 569 | else 570 | highlight default link TabInCode ErrorMsg 571 | match TabInCode /THIS_WILL_NEVer_bE_sHoWN/ 572 | let g:HighLightTabsInCodeStatus=0 573 | endif 574 | endfunction 575 | nmap ta : call HighLightTabsInCode() 576 | """"""""""""""""""""""""""""""""""""""""""""""""""""" 577 | let g:WineRelayModeStat=0 578 | function! WineRelayMode() 579 | if g:WineRelayModeStat == 0 580 | match IncSearch /.*Call.*/ 581 | 2match ErrorMsg /.*Ret.*/ 582 | 3match StatusLine /.*trace.*/ 583 | let g:WineRelayModeStat=1 584 | else 585 | match NONE 586 | 2match NONE 587 | 3match NONE 588 | let g:WineRelayModeStat=0 589 | endif 590 | endfunction 591 | 592 | "set clipboard+=unnamed 593 | 594 | "Goyo configuration 595 | nmap gy :Goyo 596 | 597 | source ~/.vim/denite.vim 598 | source ~/.vim/vim-notes.vim 599 | source ~/.vim/vim-latex.vim 600 | source ~/.vim/gtags.vim 601 | source ~/.vim/vim-wiki.vim 602 | source ~/.vim/ale.vim 603 | source ~/.vim/clang-format.vim 604 | " source ~/.vim/racer.vim 605 | " autocmd BufNewFile *.cc,*.h exec ':source ~/.vim/indent.vim' 606 | " source ~/.vim/indent.vim 607 | -------------------------------------------------------------------------------- /.vim/autoload/plug.vim: -------------------------------------------------------------------------------- 1 | " vim-plug: Vim plugin manager 2 | " ============================ 3 | " 4 | " Download plug.vim and put it in ~/.vim/autoload 5 | " 6 | " curl -fLo ~/.vim/autoload/plug.vim --create-dirs \ 7 | " https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim 8 | " 9 | " Edit your .vimrc 10 | " 11 | " call plug#begin('~/.vim/plugged') 12 | " 13 | " " Make sure you use single quotes 14 | " 15 | " " Shorthand notation; fetches https://github.com/junegunn/vim-easy-align 16 | " Plug 'junegunn/vim-easy-align' 17 | " 18 | " " Any valid git URL is allowed 19 | " Plug 'https://github.com/junegunn/vim-github-dashboard.git' 20 | " 21 | " " Multiple Plug commands can be written in a single line using | separators 22 | " Plug 'SirVer/ultisnips' | Plug 'honza/vim-snippets' 23 | " 24 | " " On-demand loading 25 | " Plug 'scrooloose/nerdtree', { 'on': 'NERDTreeToggle' } 26 | " Plug 'tpope/vim-fireplace', { 'for': 'clojure' } 27 | " 28 | " " Using a non-master branch 29 | " Plug 'rdnetto/YCM-Generator', { 'branch': 'stable' } 30 | " 31 | " " Using a tagged release; wildcard allowed (requires git 1.9.2 or above) 32 | " Plug 'fatih/vim-go', { 'tag': '*' } 33 | " 34 | " " Plugin options 35 | " Plug 'nsf/gocode', { 'tag': 'v.20150303', 'rtp': 'vim' } 36 | " 37 | " " Plugin outside ~/.vim/plugged with post-update hook 38 | " Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': './install --all' } 39 | " 40 | " " Unmanaged plugin (manually installed and updated) 41 | " Plug '~/my-prototype-plugin' 42 | " 43 | " " Initialize plugin system 44 | " call plug#end() 45 | " 46 | " Then reload .vimrc and :PlugInstall to install plugins. 47 | " 48 | " Plug options: 49 | " 50 | "| Option | Description | 51 | "| ----------------------- | ------------------------------------------------ | 52 | "| `branch`/`tag`/`commit` | Branch/tag/commit of the repository to use | 53 | "| `rtp` | Subdirectory that contains Vim plugin | 54 | "| `dir` | Custom directory for the plugin | 55 | "| `as` | Use different name for the plugin | 56 | "| `do` | Post-update hook (string or funcref) | 57 | "| `on` | On-demand loading: Commands or ``-mappings | 58 | "| `for` | On-demand loading: File types | 59 | "| `frozen` | Do not update unless explicitly specified | 60 | " 61 | " More information: https://github.com/junegunn/vim-plug 62 | " 63 | " 64 | " Copyright (c) 2017 Junegunn Choi 65 | " 66 | " MIT License 67 | " 68 | " Permission is hereby granted, free of charge, to any person obtaining 69 | " a copy of this software and associated documentation files (the 70 | " "Software"), to deal in the Software without restriction, including 71 | " without limitation the rights to use, copy, modify, merge, publish, 72 | " distribute, sublicense, and/or sell copies of the Software, and to 73 | " permit persons to whom the Software is furnished to do so, subject to 74 | " the following conditions: 75 | " 76 | " The above copyright notice and this permission notice shall be 77 | " included in all copies or substantial portions of the Software. 78 | " 79 | " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 80 | " EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 81 | " MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 82 | " NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 83 | " LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 84 | " OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 85 | " WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 86 | 87 | if exists('g:loaded_plug') 88 | finish 89 | endif 90 | let g:loaded_plug = 1 91 | 92 | let s:cpo_save = &cpo 93 | set cpo&vim 94 | 95 | let s:plug_src = 'https://github.com/junegunn/vim-plug.git' 96 | let s:plug_tab = get(s:, 'plug_tab', -1) 97 | let s:plug_buf = get(s:, 'plug_buf', -1) 98 | let s:mac_gui = has('gui_macvim') && has('gui_running') 99 | let s:is_win = has('win32') 100 | let s:nvim = has('nvim-0.2') || (has('nvim') && exists('*jobwait') && !s:is_win) 101 | let s:vim8 = has('patch-8.0.0039') && exists('*job_start') 102 | if s:is_win && &shellslash 103 | set noshellslash 104 | let s:me = resolve(expand(':p')) 105 | set shellslash 106 | else 107 | let s:me = resolve(expand(':p')) 108 | endif 109 | let s:base_spec = { 'branch': 'master', 'frozen': 0 } 110 | let s:TYPE = { 111 | \ 'string': type(''), 112 | \ 'list': type([]), 113 | \ 'dict': type({}), 114 | \ 'funcref': type(function('call')) 115 | \ } 116 | let s:loaded = get(s:, 'loaded', {}) 117 | let s:triggers = get(s:, 'triggers', {}) 118 | 119 | if s:is_win 120 | function! s:plug_call(fn, ...) 121 | let shellslash = &shellslash 122 | try 123 | set noshellslash 124 | return call(a:fn, a:000) 125 | finally 126 | let &shellslash = shellslash 127 | endtry 128 | endfunction 129 | else 130 | function! s:plug_call(fn, ...) 131 | return call(a:fn, a:000) 132 | endfunction 133 | endif 134 | 135 | function! s:plug_getcwd() 136 | return s:plug_call('getcwd') 137 | endfunction 138 | 139 | function! s:plug_fnamemodify(fname, mods) 140 | return s:plug_call('fnamemodify', a:fname, a:mods) 141 | endfunction 142 | 143 | function! s:plug_expand(fmt) 144 | return s:plug_call('expand', a:fmt, 1) 145 | endfunction 146 | 147 | function! s:plug_tempname() 148 | return s:plug_call('tempname') 149 | endfunction 150 | 151 | function! plug#begin(...) 152 | if a:0 > 0 153 | let s:plug_home_org = a:1 154 | let home = s:path(s:plug_fnamemodify(s:plug_expand(a:1), ':p')) 155 | elseif exists('g:plug_home') 156 | let home = s:path(g:plug_home) 157 | elseif !empty(&rtp) 158 | let home = s:path(split(&rtp, ',')[0]) . '/plugged' 159 | else 160 | return s:err('Unable to determine plug home. Try calling plug#begin() with a path argument.') 161 | endif 162 | if s:plug_fnamemodify(home, ':t') ==# 'plugin' && s:plug_fnamemodify(home, ':h') ==# s:first_rtp 163 | return s:err('Invalid plug home. '.home.' is a standard Vim runtime path and is not allowed.') 164 | endif 165 | 166 | let g:plug_home = home 167 | let g:plugs = {} 168 | let g:plugs_order = [] 169 | let s:triggers = {} 170 | 171 | call s:define_commands() 172 | return 1 173 | endfunction 174 | 175 | function! s:define_commands() 176 | command! -nargs=+ -bar Plug call plug#() 177 | if !executable('git') 178 | return s:err('`git` executable not found. Most commands will not be available. To suppress this message, prepend `silent!` to `call plug#begin(...)`.') 179 | endif 180 | if has('win32') 181 | \ && &shellslash 182 | \ && (&shell =~# 'cmd\.exe' || &shell =~# 'powershell\.exe') 183 | return s:err('vim-plug does not support shell, ' . &shell . ', when shellslash is set.') 184 | endif 185 | if !has('nvim') 186 | \ && (has('win32') || has('win32unix')) 187 | \ && !has('multi_byte') 188 | return s:err('Vim needs +multi_byte feature on Windows to run shell commands. Enable +iconv for best results.') 189 | endif 190 | command! -nargs=* -bar -bang -complete=customlist,s:names PlugInstall call s:install(0, []) 191 | command! -nargs=* -bar -bang -complete=customlist,s:names PlugUpdate call s:update(0, []) 192 | command! -nargs=0 -bar -bang PlugClean call s:clean(0) 193 | command! -nargs=0 -bar PlugUpgrade if s:upgrade() | execute 'source' s:esc(s:me) | endif 194 | command! -nargs=0 -bar PlugStatus call s:status() 195 | command! -nargs=0 -bar PlugDiff call s:diff() 196 | command! -nargs=? -bar -bang -complete=file PlugSnapshot call s:snapshot(0, ) 197 | endfunction 198 | 199 | function! s:to_a(v) 200 | return type(a:v) == s:TYPE.list ? a:v : [a:v] 201 | endfunction 202 | 203 | function! s:to_s(v) 204 | return type(a:v) == s:TYPE.string ? a:v : join(a:v, "\n") . "\n" 205 | endfunction 206 | 207 | function! s:glob(from, pattern) 208 | return s:lines(globpath(a:from, a:pattern)) 209 | endfunction 210 | 211 | function! s:source(from, ...) 212 | let found = 0 213 | for pattern in a:000 214 | for vim in s:glob(a:from, pattern) 215 | execute 'source' s:esc(vim) 216 | let found = 1 217 | endfor 218 | endfor 219 | return found 220 | endfunction 221 | 222 | function! s:assoc(dict, key, val) 223 | let a:dict[a:key] = add(get(a:dict, a:key, []), a:val) 224 | endfunction 225 | 226 | function! s:ask(message, ...) 227 | call inputsave() 228 | echohl WarningMsg 229 | let answer = input(a:message.(a:0 ? ' (y/N/a) ' : ' (y/N) ')) 230 | echohl None 231 | call inputrestore() 232 | echo "\r" 233 | return (a:0 && answer =~? '^a') ? 2 : (answer =~? '^y') ? 1 : 0 234 | endfunction 235 | 236 | function! s:ask_no_interrupt(...) 237 | try 238 | return call('s:ask', a:000) 239 | catch 240 | return 0 241 | endtry 242 | endfunction 243 | 244 | function! s:lazy(plug, opt) 245 | return has_key(a:plug, a:opt) && 246 | \ (empty(s:to_a(a:plug[a:opt])) || 247 | \ !isdirectory(a:plug.dir) || 248 | \ len(s:glob(s:rtp(a:plug), 'plugin')) || 249 | \ len(s:glob(s:rtp(a:plug), 'after/plugin'))) 250 | endfunction 251 | 252 | function! plug#end() 253 | if !exists('g:plugs') 254 | return s:err('plug#end() called without calling plug#begin() first') 255 | endif 256 | 257 | if exists('#PlugLOD') 258 | augroup PlugLOD 259 | autocmd! 260 | augroup END 261 | augroup! PlugLOD 262 | endif 263 | let lod = { 'ft': {}, 'map': {}, 'cmd': {} } 264 | 265 | if exists('g:did_load_filetypes') 266 | filetype off 267 | endif 268 | for name in g:plugs_order 269 | if !has_key(g:plugs, name) 270 | continue 271 | endif 272 | let plug = g:plugs[name] 273 | if get(s:loaded, name, 0) || !s:lazy(plug, 'on') && !s:lazy(plug, 'for') 274 | let s:loaded[name] = 1 275 | continue 276 | endif 277 | 278 | if has_key(plug, 'on') 279 | let s:triggers[name] = { 'map': [], 'cmd': [] } 280 | for cmd in s:to_a(plug.on) 281 | if cmd =~? '^.\+' 282 | if empty(mapcheck(cmd)) && empty(mapcheck(cmd, 'i')) 283 | call s:assoc(lod.map, cmd, name) 284 | endif 285 | call add(s:triggers[name].map, cmd) 286 | elseif cmd =~# '^[A-Z]' 287 | let cmd = substitute(cmd, '!*$', '', '') 288 | if exists(':'.cmd) != 2 289 | call s:assoc(lod.cmd, cmd, name) 290 | endif 291 | call add(s:triggers[name].cmd, cmd) 292 | else 293 | call s:err('Invalid `on` option: '.cmd. 294 | \ '. Should start with an uppercase letter or ``.') 295 | endif 296 | endfor 297 | endif 298 | 299 | if has_key(plug, 'for') 300 | let types = s:to_a(plug.for) 301 | if !empty(types) 302 | augroup filetypedetect 303 | call s:source(s:rtp(plug), 'ftdetect/**/*.vim', 'after/ftdetect/**/*.vim') 304 | augroup END 305 | endif 306 | for type in types 307 | call s:assoc(lod.ft, type, name) 308 | endfor 309 | endif 310 | endfor 311 | 312 | for [cmd, names] in items(lod.cmd) 313 | execute printf( 314 | \ 'command! -nargs=* -range -bang -complete=file %s call s:lod_cmd(%s, "", , , , %s)', 315 | \ cmd, string(cmd), string(names)) 316 | endfor 317 | 318 | for [map, names] in items(lod.map) 319 | for [mode, map_prefix, key_prefix] in 320 | \ [['i', '', ''], ['n', '', ''], ['v', '', 'gv'], ['o', '', '']] 321 | execute printf( 322 | \ '%snoremap %s %s:call lod_map(%s, %s, %s, "%s")', 323 | \ mode, map, map_prefix, string(map), string(names), mode != 'i', key_prefix) 324 | endfor 325 | endfor 326 | 327 | for [ft, names] in items(lod.ft) 328 | augroup PlugLOD 329 | execute printf('autocmd FileType %s call lod_ft(%s, %s)', 330 | \ ft, string(ft), string(names)) 331 | augroup END 332 | endfor 333 | 334 | call s:reorg_rtp() 335 | filetype plugin indent on 336 | if has('vim_starting') 337 | if has('syntax') && !exists('g:syntax_on') 338 | syntax enable 339 | end 340 | else 341 | call s:reload_plugins() 342 | endif 343 | endfunction 344 | 345 | function! s:loaded_names() 346 | return filter(copy(g:plugs_order), 'get(s:loaded, v:val, 0)') 347 | endfunction 348 | 349 | function! s:load_plugin(spec) 350 | call s:source(s:rtp(a:spec), 'plugin/**/*.vim', 'after/plugin/**/*.vim') 351 | endfunction 352 | 353 | function! s:reload_plugins() 354 | for name in s:loaded_names() 355 | call s:load_plugin(g:plugs[name]) 356 | endfor 357 | endfunction 358 | 359 | function! s:trim(str) 360 | return substitute(a:str, '[\/]\+$', '', '') 361 | endfunction 362 | 363 | function! s:version_requirement(val, min) 364 | for idx in range(0, len(a:min) - 1) 365 | let v = get(a:val, idx, 0) 366 | if v < a:min[idx] | return 0 367 | elseif v > a:min[idx] | return 1 368 | endif 369 | endfor 370 | return 1 371 | endfunction 372 | 373 | function! s:git_version_requirement(...) 374 | if !exists('s:git_version') 375 | let s:git_version = map(split(split(s:system(['git', '--version']))[2], '\.'), 'str2nr(v:val)') 376 | endif 377 | return s:version_requirement(s:git_version, a:000) 378 | endfunction 379 | 380 | function! s:progress_opt(base) 381 | return a:base && !s:is_win && 382 | \ s:git_version_requirement(1, 7, 1) ? '--progress' : '' 383 | endfunction 384 | 385 | function! s:rtp(spec) 386 | return s:path(a:spec.dir . get(a:spec, 'rtp', '')) 387 | endfunction 388 | 389 | if s:is_win 390 | function! s:path(path) 391 | return s:trim(substitute(a:path, '/', '\', 'g')) 392 | endfunction 393 | 394 | function! s:dirpath(path) 395 | return s:path(a:path) . '\' 396 | endfunction 397 | 398 | function! s:is_local_plug(repo) 399 | return a:repo =~? '^[a-z]:\|^[%~]' 400 | endfunction 401 | 402 | " Copied from fzf 403 | function! s:wrap_cmds(cmds) 404 | let cmds = [ 405 | \ '@echo off', 406 | \ 'setlocal enabledelayedexpansion'] 407 | \ + (type(a:cmds) == type([]) ? a:cmds : [a:cmds]) 408 | \ + ['endlocal'] 409 | if has('iconv') 410 | if !exists('s:codepage') 411 | let s:codepage = libcallnr('kernel32.dll', 'GetACP', 0) 412 | endif 413 | return map(cmds, printf('iconv(v:val."\r", "%s", "cp%d")', &encoding, s:codepage)) 414 | endif 415 | return map(cmds, 'v:val."\r"') 416 | endfunction 417 | 418 | function! s:batchfile(cmd) 419 | let batchfile = s:plug_tempname().'.bat' 420 | call writefile(s:wrap_cmds(a:cmd), batchfile) 421 | let cmd = plug#shellescape(batchfile, {'shell': &shell, 'script': 0}) 422 | if &shell =~# 'powershell\.exe' 423 | let cmd = '& ' . cmd 424 | endif 425 | return [batchfile, cmd] 426 | endfunction 427 | else 428 | function! s:path(path) 429 | return s:trim(a:path) 430 | endfunction 431 | 432 | function! s:dirpath(path) 433 | return substitute(a:path, '[/\\]*$', '/', '') 434 | endfunction 435 | 436 | function! s:is_local_plug(repo) 437 | return a:repo[0] =~ '[/$~]' 438 | endfunction 439 | endif 440 | 441 | function! s:err(msg) 442 | echohl ErrorMsg 443 | echom '[vim-plug] '.a:msg 444 | echohl None 445 | endfunction 446 | 447 | function! s:warn(cmd, msg) 448 | echohl WarningMsg 449 | execute a:cmd 'a:msg' 450 | echohl None 451 | endfunction 452 | 453 | function! s:esc(path) 454 | return escape(a:path, ' ') 455 | endfunction 456 | 457 | function! s:escrtp(path) 458 | return escape(a:path, ' ,') 459 | endfunction 460 | 461 | function! s:remove_rtp() 462 | for name in s:loaded_names() 463 | let rtp = s:rtp(g:plugs[name]) 464 | execute 'set rtp-='.s:escrtp(rtp) 465 | let after = globpath(rtp, 'after') 466 | if isdirectory(after) 467 | execute 'set rtp-='.s:escrtp(after) 468 | endif 469 | endfor 470 | endfunction 471 | 472 | function! s:reorg_rtp() 473 | if !empty(s:first_rtp) 474 | execute 'set rtp-='.s:first_rtp 475 | execute 'set rtp-='.s:last_rtp 476 | endif 477 | 478 | " &rtp is modified from outside 479 | if exists('s:prtp') && s:prtp !=# &rtp 480 | call s:remove_rtp() 481 | unlet! s:middle 482 | endif 483 | 484 | let s:middle = get(s:, 'middle', &rtp) 485 | let rtps = map(s:loaded_names(), 's:rtp(g:plugs[v:val])') 486 | let afters = filter(map(copy(rtps), 'globpath(v:val, "after")'), '!empty(v:val)') 487 | let rtp = join(map(rtps, 'escape(v:val, ",")'), ',') 488 | \ . ','.s:middle.',' 489 | \ . join(map(afters, 'escape(v:val, ",")'), ',') 490 | let &rtp = substitute(substitute(rtp, ',,*', ',', 'g'), '^,\|,$', '', 'g') 491 | let s:prtp = &rtp 492 | 493 | if !empty(s:first_rtp) 494 | execute 'set rtp^='.s:first_rtp 495 | execute 'set rtp+='.s:last_rtp 496 | endif 497 | endfunction 498 | 499 | function! s:doautocmd(...) 500 | if exists('#'.join(a:000, '#')) 501 | execute 'doautocmd' ((v:version > 703 || has('patch442')) ? '' : '') join(a:000) 502 | endif 503 | endfunction 504 | 505 | function! s:dobufread(names) 506 | for name in a:names 507 | let path = s:rtp(g:plugs[name]) 508 | for dir in ['ftdetect', 'ftplugin', 'after/ftdetect', 'after/ftplugin'] 509 | if len(finddir(dir, path)) 510 | if exists('#BufRead') 511 | doautocmd BufRead 512 | endif 513 | return 514 | endif 515 | endfor 516 | endfor 517 | endfunction 518 | 519 | function! plug#load(...) 520 | if a:0 == 0 521 | return s:err('Argument missing: plugin name(s) required') 522 | endif 523 | if !exists('g:plugs') 524 | return s:err('plug#begin was not called') 525 | endif 526 | let names = a:0 == 1 && type(a:1) == s:TYPE.list ? a:1 : a:000 527 | let unknowns = filter(copy(names), '!has_key(g:plugs, v:val)') 528 | if !empty(unknowns) 529 | let s = len(unknowns) > 1 ? 's' : '' 530 | return s:err(printf('Unknown plugin%s: %s', s, join(unknowns, ', '))) 531 | end 532 | let unloaded = filter(copy(names), '!get(s:loaded, v:val, 0)') 533 | if !empty(unloaded) 534 | for name in unloaded 535 | call s:lod([name], ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin']) 536 | endfor 537 | call s:dobufread(unloaded) 538 | return 1 539 | end 540 | return 0 541 | endfunction 542 | 543 | function! s:remove_triggers(name) 544 | if !has_key(s:triggers, a:name) 545 | return 546 | endif 547 | for cmd in s:triggers[a:name].cmd 548 | execute 'silent! delc' cmd 549 | endfor 550 | for map in s:triggers[a:name].map 551 | execute 'silent! unmap' map 552 | execute 'silent! iunmap' map 553 | endfor 554 | call remove(s:triggers, a:name) 555 | endfunction 556 | 557 | function! s:lod(names, types, ...) 558 | for name in a:names 559 | call s:remove_triggers(name) 560 | let s:loaded[name] = 1 561 | endfor 562 | call s:reorg_rtp() 563 | 564 | for name in a:names 565 | let rtp = s:rtp(g:plugs[name]) 566 | for dir in a:types 567 | call s:source(rtp, dir.'/**/*.vim') 568 | endfor 569 | if a:0 570 | if !s:source(rtp, a:1) && !empty(s:glob(rtp, a:2)) 571 | execute 'runtime' a:1 572 | endif 573 | call s:source(rtp, a:2) 574 | endif 575 | call s:doautocmd('User', name) 576 | endfor 577 | endfunction 578 | 579 | function! s:lod_ft(pat, names) 580 | let syn = 'syntax/'.a:pat.'.vim' 581 | call s:lod(a:names, ['plugin', 'after/plugin'], syn, 'after/'.syn) 582 | execute 'autocmd! PlugLOD FileType' a:pat 583 | call s:doautocmd('filetypeplugin', 'FileType') 584 | call s:doautocmd('filetypeindent', 'FileType') 585 | endfunction 586 | 587 | function! s:lod_cmd(cmd, bang, l1, l2, args, names) 588 | call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin']) 589 | call s:dobufread(a:names) 590 | execute printf('%s%s%s %s', (a:l1 == a:l2 ? '' : (a:l1.','.a:l2)), a:cmd, a:bang, a:args) 591 | endfunction 592 | 593 | function! s:lod_map(map, names, with_prefix, prefix) 594 | call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin']) 595 | call s:dobufread(a:names) 596 | let extra = '' 597 | while 1 598 | let c = getchar(0) 599 | if c == 0 600 | break 601 | endif 602 | let extra .= nr2char(c) 603 | endwhile 604 | 605 | if a:with_prefix 606 | let prefix = v:count ? v:count : '' 607 | let prefix .= '"'.v:register.a:prefix 608 | if mode(1) == 'no' 609 | if v:operator == 'c' 610 | let prefix = "\" . prefix 611 | endif 612 | let prefix .= v:operator 613 | endif 614 | call feedkeys(prefix, 'n') 615 | endif 616 | call feedkeys(substitute(a:map, '^', "\", '') . extra) 617 | endfunction 618 | 619 | function! plug#(repo, ...) 620 | if a:0 > 1 621 | return s:err('Invalid number of arguments (1..2)') 622 | endif 623 | 624 | try 625 | let repo = s:trim(a:repo) 626 | let opts = a:0 == 1 ? s:parse_options(a:1) : s:base_spec 627 | let name = get(opts, 'as', s:plug_fnamemodify(repo, ':t:s?\.git$??')) 628 | let spec = extend(s:infer_properties(name, repo), opts) 629 | if !has_key(g:plugs, name) 630 | call add(g:plugs_order, name) 631 | endif 632 | let g:plugs[name] = spec 633 | let s:loaded[name] = get(s:loaded, name, 0) 634 | catch 635 | return s:err(repo . ' ' . v:exception) 636 | endtry 637 | endfunction 638 | 639 | function! s:parse_options(arg) 640 | let opts = copy(s:base_spec) 641 | let type = type(a:arg) 642 | let opt_errfmt = 'Invalid argument for "%s" option of :Plug (expected: %s)' 643 | if type == s:TYPE.string 644 | if empty(a:arg) 645 | throw printf(opt_errfmt, 'tag', 'string') 646 | endif 647 | let opts.tag = a:arg 648 | elseif type == s:TYPE.dict 649 | call extend(opts, a:arg) 650 | for opt in ['branch', 'tag', 'commit', 'rtp', 'dir', 'as'] 651 | if has_key(opts, opt) 652 | \ && (type(opts[opt]) != s:TYPE.string || empty(opts[opt])) 653 | throw printf(opt_errfmt, opt, 'string') 654 | endif 655 | endfor 656 | for opt in ['on', 'for'] 657 | if has_key(opts, opt) 658 | \ && type(opts[opt]) != s:TYPE.list 659 | \ && (type(opts[opt]) != s:TYPE.string || empty(opts[opt])) 660 | throw printf(opt_errfmt, opt, 'string or list') 661 | endif 662 | endfor 663 | if has_key(opts, 'do') 664 | \ && type(opts.do) != s:TYPE.funcref 665 | \ && (type(opts.do) != s:TYPE.string || empty(opts.do)) 666 | throw printf(opt_errfmt, 'do', 'string or funcref') 667 | endif 668 | if has_key(opts, 'dir') 669 | let opts.dir = s:dirpath(s:plug_expand(opts.dir)) 670 | endif 671 | else 672 | throw 'Invalid argument type (expected: string or dictionary)' 673 | endif 674 | return opts 675 | endfunction 676 | 677 | function! s:infer_properties(name, repo) 678 | let repo = a:repo 679 | if s:is_local_plug(repo) 680 | return { 'dir': s:dirpath(s:plug_expand(repo)) } 681 | else 682 | if repo =~ ':' 683 | let uri = repo 684 | else 685 | if repo !~ '/' 686 | throw printf('Invalid argument: %s (implicit `vim-scripts'' expansion is deprecated)', repo) 687 | endif 688 | let fmt = get(g:, 'plug_url_format', 'https://git::@github.com/%s.git') 689 | let uri = printf(fmt, repo) 690 | endif 691 | return { 'dir': s:dirpath(g:plug_home.'/'.a:name), 'uri': uri } 692 | endif 693 | endfunction 694 | 695 | function! s:install(force, names) 696 | call s:update_impl(0, a:force, a:names) 697 | endfunction 698 | 699 | function! s:update(force, names) 700 | call s:update_impl(1, a:force, a:names) 701 | endfunction 702 | 703 | function! plug#helptags() 704 | if !exists('g:plugs') 705 | return s:err('plug#begin was not called') 706 | endif 707 | for spec in values(g:plugs) 708 | let docd = join([s:rtp(spec), 'doc'], '/') 709 | if isdirectory(docd) 710 | silent! execute 'helptags' s:esc(docd) 711 | endif 712 | endfor 713 | return 1 714 | endfunction 715 | 716 | function! s:syntax() 717 | syntax clear 718 | syntax region plug1 start=/\%1l/ end=/\%2l/ contains=plugNumber 719 | syntax region plug2 start=/\%2l/ end=/\%3l/ contains=plugBracket,plugX 720 | syn match plugNumber /[0-9]\+[0-9.]*/ contained 721 | syn match plugBracket /[[\]]/ contained 722 | syn match plugX /x/ contained 723 | syn match plugDash /^-/ 724 | syn match plugPlus /^+/ 725 | syn match plugStar /^*/ 726 | syn match plugMessage /\(^- \)\@<=.*/ 727 | syn match plugName /\(^- \)\@<=[^ ]*:/ 728 | syn match plugSha /\%(: \)\@<=[0-9a-f]\{4,}$/ 729 | syn match plugTag /(tag: [^)]\+)/ 730 | syn match plugInstall /\(^+ \)\@<=[^:]*/ 731 | syn match plugUpdate /\(^* \)\@<=[^:]*/ 732 | syn match plugCommit /^ \X*[0-9a-f]\{7,9} .*/ contains=plugRelDate,plugEdge,plugTag 733 | syn match plugEdge /^ \X\+$/ 734 | syn match plugEdge /^ \X*/ contained nextgroup=plugSha 735 | syn match plugSha /[0-9a-f]\{7,9}/ contained 736 | syn match plugRelDate /([^)]*)$/ contained 737 | syn match plugNotLoaded /(not loaded)$/ 738 | syn match plugError /^x.*/ 739 | syn region plugDeleted start=/^\~ .*/ end=/^\ze\S/ 740 | syn match plugH2 /^.*:\n-\+$/ 741 | syn keyword Function PlugInstall PlugStatus PlugUpdate PlugClean 742 | hi def link plug1 Title 743 | hi def link plug2 Repeat 744 | hi def link plugH2 Type 745 | hi def link plugX Exception 746 | hi def link plugBracket Structure 747 | hi def link plugNumber Number 748 | 749 | hi def link plugDash Special 750 | hi def link plugPlus Constant 751 | hi def link plugStar Boolean 752 | 753 | hi def link plugMessage Function 754 | hi def link plugName Label 755 | hi def link plugInstall Function 756 | hi def link plugUpdate Type 757 | 758 | hi def link plugError Error 759 | hi def link plugDeleted Ignore 760 | hi def link plugRelDate Comment 761 | hi def link plugEdge PreProc 762 | hi def link plugSha Identifier 763 | hi def link plugTag Constant 764 | 765 | hi def link plugNotLoaded Comment 766 | endfunction 767 | 768 | function! s:lpad(str, len) 769 | return a:str . repeat(' ', a:len - len(a:str)) 770 | endfunction 771 | 772 | function! s:lines(msg) 773 | return split(a:msg, "[\r\n]") 774 | endfunction 775 | 776 | function! s:lastline(msg) 777 | return get(s:lines(a:msg), -1, '') 778 | endfunction 779 | 780 | function! s:new_window() 781 | execute get(g:, 'plug_window', 'vertical topleft new') 782 | endfunction 783 | 784 | function! s:plug_window_exists() 785 | let buflist = tabpagebuflist(s:plug_tab) 786 | return !empty(buflist) && index(buflist, s:plug_buf) >= 0 787 | endfunction 788 | 789 | function! s:switch_in() 790 | if !s:plug_window_exists() 791 | return 0 792 | endif 793 | 794 | if winbufnr(0) != s:plug_buf 795 | let s:pos = [tabpagenr(), winnr(), winsaveview()] 796 | execute 'normal!' s:plug_tab.'gt' 797 | let winnr = bufwinnr(s:plug_buf) 798 | execute winnr.'wincmd w' 799 | call add(s:pos, winsaveview()) 800 | else 801 | let s:pos = [winsaveview()] 802 | endif 803 | 804 | setlocal modifiable 805 | return 1 806 | endfunction 807 | 808 | function! s:switch_out(...) 809 | call winrestview(s:pos[-1]) 810 | setlocal nomodifiable 811 | if a:0 > 0 812 | execute a:1 813 | endif 814 | 815 | if len(s:pos) > 1 816 | execute 'normal!' s:pos[0].'gt' 817 | execute s:pos[1] 'wincmd w' 818 | call winrestview(s:pos[2]) 819 | endif 820 | endfunction 821 | 822 | function! s:finish_bindings() 823 | nnoremap R :call retry() 824 | nnoremap D :PlugDiff 825 | nnoremap S :PlugStatus 826 | nnoremap U :call status_update() 827 | xnoremap U :call status_update() 828 | nnoremap ]] :silent! call section('') 829 | nnoremap [[ :silent! call section('b') 830 | endfunction 831 | 832 | function! s:prepare(...) 833 | if empty(s:plug_getcwd()) 834 | throw 'Invalid current working directory. Cannot proceed.' 835 | endif 836 | 837 | for evar in ['$GIT_DIR', '$GIT_WORK_TREE'] 838 | if exists(evar) 839 | throw evar.' detected. Cannot proceed.' 840 | endif 841 | endfor 842 | 843 | call s:job_abort() 844 | if s:switch_in() 845 | if b:plug_preview == 1 846 | pc 847 | endif 848 | enew 849 | else 850 | call s:new_window() 851 | endif 852 | 853 | nnoremap q :if b:plug_preview==1pcendifbd 854 | if a:0 == 0 855 | call s:finish_bindings() 856 | endif 857 | let b:plug_preview = -1 858 | let s:plug_tab = tabpagenr() 859 | let s:plug_buf = winbufnr(0) 860 | call s:assign_name() 861 | 862 | for k in ['', 'L', 'o', 'X', 'd', 'dd'] 863 | execute 'silent! unmap ' k 864 | endfor 865 | setlocal buftype=nofile bufhidden=wipe nobuflisted nolist noswapfile nowrap cursorline modifiable nospell 866 | if exists('+colorcolumn') 867 | setlocal colorcolumn= 868 | endif 869 | setf vim-plug 870 | if exists('g:syntax_on') 871 | call s:syntax() 872 | endif 873 | endfunction 874 | 875 | function! s:assign_name() 876 | " Assign buffer name 877 | let prefix = '[Plugins]' 878 | let name = prefix 879 | let idx = 2 880 | while bufexists(name) 881 | let name = printf('%s (%s)', prefix, idx) 882 | let idx = idx + 1 883 | endwhile 884 | silent! execute 'f' fnameescape(name) 885 | endfunction 886 | 887 | function! s:chsh(swap) 888 | let prev = [&shell, &shellcmdflag, &shellredir] 889 | if !s:is_win 890 | set shell=sh 891 | endif 892 | if a:swap 893 | if &shell =~# 'powershell\.exe' || &shell =~# 'pwsh$' 894 | let &shellredir = '2>&1 | Out-File -Encoding UTF8 %s' 895 | elseif &shell =~# 'sh' || &shell =~# 'cmd\.exe' 896 | set shellredir=>%s\ 2>&1 897 | endif 898 | endif 899 | return prev 900 | endfunction 901 | 902 | function! s:bang(cmd, ...) 903 | let batchfile = '' 904 | try 905 | let [sh, shellcmdflag, shrd] = s:chsh(a:0) 906 | " FIXME: Escaping is incomplete. We could use shellescape with eval, 907 | " but it won't work on Windows. 908 | let cmd = a:0 ? s:with_cd(a:cmd, a:1) : a:cmd 909 | if s:is_win 910 | let [batchfile, cmd] = s:batchfile(cmd) 911 | endif 912 | let g:_plug_bang = (s:is_win && has('gui_running') ? 'silent ' : '').'!'.escape(cmd, '#!%') 913 | execute "normal! :execute g:_plug_bang\\" 914 | finally 915 | unlet g:_plug_bang 916 | let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd] 917 | if s:is_win && filereadable(batchfile) 918 | call delete(batchfile) 919 | endif 920 | endtry 921 | return v:shell_error ? 'Exit status: ' . v:shell_error : '' 922 | endfunction 923 | 924 | function! s:regress_bar() 925 | let bar = substitute(getline(2)[1:-2], '.*\zs=', 'x', '') 926 | call s:progress_bar(2, bar, len(bar)) 927 | endfunction 928 | 929 | function! s:is_updated(dir) 930 | return !empty(s:system_chomp(['git', 'log', '--pretty=format:%h', 'HEAD...HEAD@{1}'], a:dir)) 931 | endfunction 932 | 933 | function! s:do(pull, force, todo) 934 | for [name, spec] in items(a:todo) 935 | if !isdirectory(spec.dir) 936 | continue 937 | endif 938 | let installed = has_key(s:update.new, name) 939 | let updated = installed ? 0 : 940 | \ (a:pull && index(s:update.errors, name) < 0 && s:is_updated(spec.dir)) 941 | if a:force || installed || updated 942 | execute 'cd' s:esc(spec.dir) 943 | call append(3, '- Post-update hook for '. name .' ... ') 944 | let error = '' 945 | let type = type(spec.do) 946 | if type == s:TYPE.string 947 | if spec.do[0] == ':' 948 | if !get(s:loaded, name, 0) 949 | let s:loaded[name] = 1 950 | call s:reorg_rtp() 951 | endif 952 | call s:load_plugin(spec) 953 | try 954 | execute spec.do[1:] 955 | catch 956 | let error = v:exception 957 | endtry 958 | if !s:plug_window_exists() 959 | cd - 960 | throw 'Warning: vim-plug was terminated by the post-update hook of '.name 961 | endif 962 | else 963 | let error = s:bang(spec.do) 964 | endif 965 | elseif type == s:TYPE.funcref 966 | try 967 | call s:load_plugin(spec) 968 | let status = installed ? 'installed' : (updated ? 'updated' : 'unchanged') 969 | call spec.do({ 'name': name, 'status': status, 'force': a:force }) 970 | catch 971 | let error = v:exception 972 | endtry 973 | else 974 | let error = 'Invalid hook type' 975 | endif 976 | call s:switch_in() 977 | call setline(4, empty(error) ? (getline(4) . 'OK') 978 | \ : ('x' . getline(4)[1:] . error)) 979 | if !empty(error) 980 | call add(s:update.errors, name) 981 | call s:regress_bar() 982 | endif 983 | cd - 984 | endif 985 | endfor 986 | endfunction 987 | 988 | function! s:hash_match(a, b) 989 | return stridx(a:a, a:b) == 0 || stridx(a:b, a:a) == 0 990 | endfunction 991 | 992 | function! s:checkout(spec) 993 | let sha = a:spec.commit 994 | let output = s:system(['git', 'rev-parse', 'HEAD'], a:spec.dir) 995 | if !v:shell_error && !s:hash_match(sha, s:lines(output)[0]) 996 | let output = s:system( 997 | \ 'git fetch --depth 999999 && git checkout '.plug#shellescape(sha).' --', a:spec.dir) 998 | endif 999 | return output 1000 | endfunction 1001 | 1002 | function! s:finish(pull) 1003 | let new_frozen = len(filter(keys(s:update.new), 'g:plugs[v:val].frozen')) 1004 | if new_frozen 1005 | let s = new_frozen > 1 ? 's' : '' 1006 | call append(3, printf('- Installed %d frozen plugin%s', new_frozen, s)) 1007 | endif 1008 | call append(3, '- Finishing ... ') | 4 1009 | redraw 1010 | call plug#helptags() 1011 | call plug#end() 1012 | call setline(4, getline(4) . 'Done!') 1013 | redraw 1014 | let msgs = [] 1015 | if !empty(s:update.errors) 1016 | call add(msgs, "Press 'R' to retry.") 1017 | endif 1018 | if a:pull && len(s:update.new) < len(filter(getline(5, '$'), 1019 | \ "v:val =~ '^- ' && v:val !~# 'Already up.to.date'")) 1020 | call add(msgs, "Press 'D' to see the updated changes.") 1021 | endif 1022 | echo join(msgs, ' ') 1023 | call s:finish_bindings() 1024 | endfunction 1025 | 1026 | function! s:retry() 1027 | if empty(s:update.errors) 1028 | return 1029 | endif 1030 | echo 1031 | call s:update_impl(s:update.pull, s:update.force, 1032 | \ extend(copy(s:update.errors), [s:update.threads])) 1033 | endfunction 1034 | 1035 | function! s:is_managed(name) 1036 | return has_key(g:plugs[a:name], 'uri') 1037 | endfunction 1038 | 1039 | function! s:names(...) 1040 | return sort(filter(keys(g:plugs), 'stridx(v:val, a:1) == 0 && s:is_managed(v:val)')) 1041 | endfunction 1042 | 1043 | function! s:check_ruby() 1044 | silent! ruby require 'thread'; VIM::command("let g:plug_ruby = '#{RUBY_VERSION}'") 1045 | if !exists('g:plug_ruby') 1046 | redraw! 1047 | return s:warn('echom', 'Warning: Ruby interface is broken') 1048 | endif 1049 | let ruby_version = split(g:plug_ruby, '\.') 1050 | unlet g:plug_ruby 1051 | return s:version_requirement(ruby_version, [1, 8, 7]) 1052 | endfunction 1053 | 1054 | function! s:update_impl(pull, force, args) abort 1055 | let sync = index(a:args, '--sync') >= 0 || has('vim_starting') 1056 | let args = filter(copy(a:args), 'v:val != "--sync"') 1057 | let threads = (len(args) > 0 && args[-1] =~ '^[1-9][0-9]*$') ? 1058 | \ remove(args, -1) : get(g:, 'plug_threads', 16) 1059 | 1060 | let managed = filter(copy(g:plugs), 's:is_managed(v:key)') 1061 | let todo = empty(args) ? filter(managed, '!v:val.frozen || !isdirectory(v:val.dir)') : 1062 | \ filter(managed, 'index(args, v:key) >= 0') 1063 | 1064 | if empty(todo) 1065 | return s:warn('echo', 'No plugin to '. (a:pull ? 'update' : 'install')) 1066 | endif 1067 | 1068 | if !s:is_win && s:git_version_requirement(2, 3) 1069 | let s:git_terminal_prompt = exists('$GIT_TERMINAL_PROMPT') ? $GIT_TERMINAL_PROMPT : '' 1070 | let $GIT_TERMINAL_PROMPT = 0 1071 | for plug in values(todo) 1072 | let plug.uri = substitute(plug.uri, 1073 | \ '^https://git::@github\.com', 'https://github.com', '') 1074 | endfor 1075 | endif 1076 | 1077 | if !isdirectory(g:plug_home) 1078 | try 1079 | call mkdir(g:plug_home, 'p') 1080 | catch 1081 | return s:err(printf('Invalid plug directory: %s. '. 1082 | \ 'Try to call plug#begin with a valid directory', g:plug_home)) 1083 | endtry 1084 | endif 1085 | 1086 | if has('nvim') && !exists('*jobwait') && threads > 1 1087 | call s:warn('echom', '[vim-plug] Update Neovim for parallel installer') 1088 | endif 1089 | 1090 | let use_job = s:nvim || s:vim8 1091 | let python = (has('python') || has('python3')) && !use_job 1092 | let ruby = has('ruby') && !use_job && (v:version >= 703 || v:version == 702 && has('patch374')) && !(s:is_win && has('gui_running')) && threads > 1 && s:check_ruby() 1093 | 1094 | let s:update = { 1095 | \ 'start': reltime(), 1096 | \ 'all': todo, 1097 | \ 'todo': copy(todo), 1098 | \ 'errors': [], 1099 | \ 'pull': a:pull, 1100 | \ 'force': a:force, 1101 | \ 'new': {}, 1102 | \ 'threads': (python || ruby || use_job) ? min([len(todo), threads]) : 1, 1103 | \ 'bar': '', 1104 | \ 'fin': 0 1105 | \ } 1106 | 1107 | call s:prepare(1) 1108 | call append(0, ['', '']) 1109 | normal! 2G 1110 | silent! redraw 1111 | 1112 | let s:clone_opt = [] 1113 | if get(g:, 'plug_shallow', 1) 1114 | call extend(s:clone_opt, ['--depth', '1']) 1115 | if s:git_version_requirement(1, 7, 10) 1116 | call add(s:clone_opt, '--no-single-branch') 1117 | endif 1118 | endif 1119 | 1120 | if has('win32unix') || has('wsl') 1121 | call extend(s:clone_opt, ['-c', 'core.eol=lf', '-c', 'core.autocrlf=input']) 1122 | endif 1123 | 1124 | let s:submodule_opt = s:git_version_requirement(2, 8) ? ' --jobs='.threads : '' 1125 | 1126 | " Python version requirement (>= 2.7) 1127 | if python && !has('python3') && !ruby && !use_job && s:update.threads > 1 1128 | redir => pyv 1129 | silent python import platform; print platform.python_version() 1130 | redir END 1131 | let python = s:version_requirement( 1132 | \ map(split(split(pyv)[0], '\.'), 'str2nr(v:val)'), [2, 6]) 1133 | endif 1134 | 1135 | if (python || ruby) && s:update.threads > 1 1136 | try 1137 | let imd = &imd 1138 | if s:mac_gui 1139 | set noimd 1140 | endif 1141 | if ruby 1142 | call s:update_ruby() 1143 | else 1144 | call s:update_python() 1145 | endif 1146 | catch 1147 | let lines = getline(4, '$') 1148 | let printed = {} 1149 | silent! 4,$d _ 1150 | for line in lines 1151 | let name = s:extract_name(line, '.', '') 1152 | if empty(name) || !has_key(printed, name) 1153 | call append('$', line) 1154 | if !empty(name) 1155 | let printed[name] = 1 1156 | if line[0] == 'x' && index(s:update.errors, name) < 0 1157 | call add(s:update.errors, name) 1158 | end 1159 | endif 1160 | endif 1161 | endfor 1162 | finally 1163 | let &imd = imd 1164 | call s:update_finish() 1165 | endtry 1166 | else 1167 | call s:update_vim() 1168 | while use_job && sync 1169 | sleep 100m 1170 | if s:update.fin 1171 | break 1172 | endif 1173 | endwhile 1174 | endif 1175 | endfunction 1176 | 1177 | function! s:log4(name, msg) 1178 | call setline(4, printf('- %s (%s)', a:msg, a:name)) 1179 | redraw 1180 | endfunction 1181 | 1182 | function! s:update_finish() 1183 | if exists('s:git_terminal_prompt') 1184 | let $GIT_TERMINAL_PROMPT = s:git_terminal_prompt 1185 | endif 1186 | if s:switch_in() 1187 | call append(3, '- Updating ...') | 4 1188 | 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))')) 1189 | let [pos, _] = s:logpos(name) 1190 | if !pos 1191 | continue 1192 | endif 1193 | if has_key(spec, 'commit') 1194 | call s:log4(name, 'Checking out '.spec.commit) 1195 | let out = s:checkout(spec) 1196 | elseif has_key(spec, 'tag') 1197 | let tag = spec.tag 1198 | if tag =~ '\*' 1199 | let tags = s:lines(s:system('git tag --list '.plug#shellescape(tag).' --sort -version:refname 2>&1', spec.dir)) 1200 | if !v:shell_error && !empty(tags) 1201 | let tag = tags[0] 1202 | call s:log4(name, printf('Latest tag for %s -> %s', spec.tag, tag)) 1203 | call append(3, '') 1204 | endif 1205 | endif 1206 | call s:log4(name, 'Checking out '.tag) 1207 | let out = s:system('git checkout -q '.plug#shellescape(tag).' -- 2>&1', spec.dir) 1208 | else 1209 | let branch = get(spec, 'branch', 'master') 1210 | call s:log4(name, 'Merging origin/'.s:esc(branch)) 1211 | let out = s:system('git checkout -q '.plug#shellescape(branch).' -- 2>&1' 1212 | \. (has_key(s:update.new, name) ? '' : ('&& git merge --ff-only '.plug#shellescape('origin/'.branch).' 2>&1')), spec.dir) 1213 | endif 1214 | if !v:shell_error && filereadable(spec.dir.'/.gitmodules') && 1215 | \ (s:update.force || has_key(s:update.new, name) || s:is_updated(spec.dir)) 1216 | call s:log4(name, 'Updating submodules. This may take a while.') 1217 | let out .= s:bang('git submodule update --init --recursive'.s:submodule_opt.' 2>&1', spec.dir) 1218 | endif 1219 | let msg = s:format_message(v:shell_error ? 'x': '-', name, out) 1220 | if v:shell_error 1221 | call add(s:update.errors, name) 1222 | call s:regress_bar() 1223 | silent execute pos 'd _' 1224 | call append(4, msg) | 4 1225 | elseif !empty(out) 1226 | call setline(pos, msg[0]) 1227 | endif 1228 | redraw 1229 | endfor 1230 | silent 4 d _ 1231 | try 1232 | 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")')) 1233 | catch 1234 | call s:warn('echom', v:exception) 1235 | call s:warn('echo', '') 1236 | return 1237 | endtry 1238 | call s:finish(s:update.pull) 1239 | call setline(1, 'Updated. Elapsed time: ' . split(reltimestr(reltime(s:update.start)))[0] . ' sec.') 1240 | call s:switch_out('normal! gg') 1241 | endif 1242 | endfunction 1243 | 1244 | function! s:job_abort() 1245 | if (!s:nvim && !s:vim8) || !exists('s:jobs') 1246 | return 1247 | endif 1248 | 1249 | for [name, j] in items(s:jobs) 1250 | if s:nvim 1251 | silent! call jobstop(j.jobid) 1252 | elseif s:vim8 1253 | silent! call job_stop(j.jobid) 1254 | endif 1255 | if j.new 1256 | call s:rm_rf(g:plugs[name].dir) 1257 | endif 1258 | endfor 1259 | let s:jobs = {} 1260 | endfunction 1261 | 1262 | function! s:last_non_empty_line(lines) 1263 | let len = len(a:lines) 1264 | for idx in range(len) 1265 | let line = a:lines[len-idx-1] 1266 | if !empty(line) 1267 | return line 1268 | endif 1269 | endfor 1270 | return '' 1271 | endfunction 1272 | 1273 | function! s:job_out_cb(self, data) abort 1274 | let self = a:self 1275 | let data = remove(self.lines, -1) . a:data 1276 | let lines = map(split(data, "\n", 1), 'split(v:val, "\r", 1)[-1]') 1277 | call extend(self.lines, lines) 1278 | " To reduce the number of buffer updates 1279 | let self.tick = get(self, 'tick', -1) + 1 1280 | if !self.running || self.tick % len(s:jobs) == 0 1281 | let bullet = self.running ? (self.new ? '+' : '*') : (self.error ? 'x' : '-') 1282 | let result = self.error ? join(self.lines, "\n") : s:last_non_empty_line(self.lines) 1283 | call s:log(bullet, self.name, result) 1284 | endif 1285 | endfunction 1286 | 1287 | function! s:job_exit_cb(self, data) abort 1288 | let a:self.running = 0 1289 | let a:self.error = a:data != 0 1290 | call s:reap(a:self.name) 1291 | call s:tick() 1292 | endfunction 1293 | 1294 | function! s:job_cb(fn, job, ch, data) 1295 | if !s:plug_window_exists() " plug window closed 1296 | return s:job_abort() 1297 | endif 1298 | call call(a:fn, [a:job, a:data]) 1299 | endfunction 1300 | 1301 | function! s:nvim_cb(job_id, data, event) dict abort 1302 | return (a:event == 'stdout' || a:event == 'stderr') ? 1303 | \ s:job_cb('s:job_out_cb', self, 0, join(a:data, "\n")) : 1304 | \ s:job_cb('s:job_exit_cb', self, 0, a:data) 1305 | endfunction 1306 | 1307 | function! s:spawn(name, cmd, opts) 1308 | let job = { 'name': a:name, 'running': 1, 'error': 0, 'lines': [''], 1309 | \ 'new': get(a:opts, 'new', 0) } 1310 | let s:jobs[a:name] = job 1311 | 1312 | if s:nvim 1313 | if has_key(a:opts, 'dir') 1314 | let job.cwd = a:opts.dir 1315 | endif 1316 | let argv = a:cmd 1317 | call extend(job, { 1318 | \ 'on_stdout': function('s:nvim_cb'), 1319 | \ 'on_stderr': function('s:nvim_cb'), 1320 | \ 'on_exit': function('s:nvim_cb'), 1321 | \ }) 1322 | let jid = s:plug_call('jobstart', argv, job) 1323 | if jid > 0 1324 | let job.jobid = jid 1325 | else 1326 | let job.running = 0 1327 | let job.error = 1 1328 | let job.lines = [jid < 0 ? argv[0].' is not executable' : 1329 | \ 'Invalid arguments (or job table is full)'] 1330 | endif 1331 | elseif s:vim8 1332 | let cmd = join(map(copy(a:cmd), 'plug#shellescape(v:val, {"script": 0})')) 1333 | if has_key(a:opts, 'dir') 1334 | let cmd = s:with_cd(cmd, a:opts.dir, 0) 1335 | endif 1336 | let argv = s:is_win ? ['cmd', '/s', '/c', '"'.cmd.'"'] : ['sh', '-c', cmd] 1337 | let jid = job_start(s:is_win ? join(argv, ' ') : argv, { 1338 | \ 'out_cb': function('s:job_cb', ['s:job_out_cb', job]), 1339 | \ 'err_cb': function('s:job_cb', ['s:job_out_cb', job]), 1340 | \ 'exit_cb': function('s:job_cb', ['s:job_exit_cb', job]), 1341 | \ 'err_mode': 'raw', 1342 | \ 'out_mode': 'raw' 1343 | \}) 1344 | if job_status(jid) == 'run' 1345 | let job.jobid = jid 1346 | else 1347 | let job.running = 0 1348 | let job.error = 1 1349 | let job.lines = ['Failed to start job'] 1350 | endif 1351 | else 1352 | let job.lines = s:lines(call('s:system', has_key(a:opts, 'dir') ? [a:cmd, a:opts.dir] : [a:cmd])) 1353 | let job.error = v:shell_error != 0 1354 | let job.running = 0 1355 | endif 1356 | endfunction 1357 | 1358 | function! s:reap(name) 1359 | let job = s:jobs[a:name] 1360 | if job.error 1361 | call add(s:update.errors, a:name) 1362 | elseif get(job, 'new', 0) 1363 | let s:update.new[a:name] = 1 1364 | endif 1365 | let s:update.bar .= job.error ? 'x' : '=' 1366 | 1367 | let bullet = job.error ? 'x' : '-' 1368 | let result = job.error ? join(job.lines, "\n") : s:last_non_empty_line(job.lines) 1369 | call s:log(bullet, a:name, empty(result) ? 'OK' : result) 1370 | call s:bar() 1371 | 1372 | call remove(s:jobs, a:name) 1373 | endfunction 1374 | 1375 | function! s:bar() 1376 | if s:switch_in() 1377 | let total = len(s:update.all) 1378 | call setline(1, (s:update.pull ? 'Updating' : 'Installing'). 1379 | \ ' plugins ('.len(s:update.bar).'/'.total.')') 1380 | call s:progress_bar(2, s:update.bar, total) 1381 | call s:switch_out() 1382 | endif 1383 | endfunction 1384 | 1385 | function! s:logpos(name) 1386 | let max = line('$') 1387 | for i in range(4, max > 4 ? max : 4) 1388 | if getline(i) =~# '^[-+x*] '.a:name.':' 1389 | for j in range(i + 1, max > 5 ? max : 5) 1390 | if getline(j) !~ '^ ' 1391 | return [i, j - 1] 1392 | endif 1393 | endfor 1394 | return [i, i] 1395 | endif 1396 | endfor 1397 | return [0, 0] 1398 | endfunction 1399 | 1400 | function! s:log(bullet, name, lines) 1401 | if s:switch_in() 1402 | let [b, e] = s:logpos(a:name) 1403 | if b > 0 1404 | silent execute printf('%d,%d d _', b, e) 1405 | if b > winheight('.') 1406 | let b = 4 1407 | endif 1408 | else 1409 | let b = 4 1410 | endif 1411 | " FIXME For some reason, nomodifiable is set after :d in vim8 1412 | setlocal modifiable 1413 | call append(b - 1, s:format_message(a:bullet, a:name, a:lines)) 1414 | call s:switch_out() 1415 | endif 1416 | endfunction 1417 | 1418 | function! s:update_vim() 1419 | let s:jobs = {} 1420 | 1421 | call s:bar() 1422 | call s:tick() 1423 | endfunction 1424 | 1425 | function! s:tick() 1426 | let pull = s:update.pull 1427 | let prog = s:progress_opt(s:nvim || s:vim8) 1428 | while 1 " Without TCO, Vim stack is bound to explode 1429 | if empty(s:update.todo) 1430 | if empty(s:jobs) && !s:update.fin 1431 | call s:update_finish() 1432 | let s:update.fin = 1 1433 | endif 1434 | return 1435 | endif 1436 | 1437 | let name = keys(s:update.todo)[0] 1438 | let spec = remove(s:update.todo, name) 1439 | let new = empty(globpath(spec.dir, '.git', 1)) 1440 | 1441 | call s:log(new ? '+' : '*', name, pull ? 'Updating ...' : 'Installing ...') 1442 | redraw 1443 | 1444 | let has_tag = has_key(spec, 'tag') 1445 | if !new 1446 | let [error, _] = s:git_validate(spec, 0) 1447 | if empty(error) 1448 | if pull 1449 | let cmd = ['git', 'fetch'] 1450 | if has_tag && !empty(globpath(spec.dir, '.git/shallow')) 1451 | call extend(cmd, ['--depth', '99999999']) 1452 | endif 1453 | if !empty(prog) 1454 | call add(cmd, prog) 1455 | endif 1456 | call s:spawn(name, cmd, { 'dir': spec.dir }) 1457 | else 1458 | let s:jobs[name] = { 'running': 0, 'lines': ['Already installed'], 'error': 0 } 1459 | endif 1460 | else 1461 | let s:jobs[name] = { 'running': 0, 'lines': s:lines(error), 'error': 1 } 1462 | endif 1463 | else 1464 | let cmd = ['git', 'clone'] 1465 | if !has_tag 1466 | call extend(cmd, s:clone_opt) 1467 | endif 1468 | if !empty(prog) 1469 | call add(cmd, prog) 1470 | endif 1471 | call s:spawn(name, extend(cmd, [spec.uri, s:trim(spec.dir)]), { 'new': 1 }) 1472 | endif 1473 | 1474 | if !s:jobs[name].running 1475 | call s:reap(name) 1476 | endif 1477 | if len(s:jobs) >= s:update.threads 1478 | break 1479 | endif 1480 | endwhile 1481 | endfunction 1482 | 1483 | function! s:update_python() 1484 | let py_exe = has('python') ? 'python' : 'python3' 1485 | execute py_exe "<< EOF" 1486 | import datetime 1487 | import functools 1488 | import os 1489 | try: 1490 | import queue 1491 | except ImportError: 1492 | import Queue as queue 1493 | import random 1494 | import re 1495 | import shutil 1496 | import signal 1497 | import subprocess 1498 | import tempfile 1499 | import threading as thr 1500 | import time 1501 | import traceback 1502 | import vim 1503 | 1504 | G_NVIM = vim.eval("has('nvim')") == '1' 1505 | G_PULL = vim.eval('s:update.pull') == '1' 1506 | G_RETRIES = int(vim.eval('get(g:, "plug_retries", 2)')) + 1 1507 | G_TIMEOUT = int(vim.eval('get(g:, "plug_timeout", 60)')) 1508 | G_CLONE_OPT = ' '.join(vim.eval('s:clone_opt')) 1509 | G_PROGRESS = vim.eval('s:progress_opt(1)') 1510 | G_LOG_PROB = 1.0 / int(vim.eval('s:update.threads')) 1511 | G_STOP = thr.Event() 1512 | G_IS_WIN = vim.eval('s:is_win') == '1' 1513 | 1514 | class PlugError(Exception): 1515 | def __init__(self, msg): 1516 | self.msg = msg 1517 | class CmdTimedOut(PlugError): 1518 | pass 1519 | class CmdFailed(PlugError): 1520 | pass 1521 | class InvalidURI(PlugError): 1522 | pass 1523 | class Action(object): 1524 | INSTALL, UPDATE, ERROR, DONE = ['+', '*', 'x', '-'] 1525 | 1526 | class Buffer(object): 1527 | def __init__(self, lock, num_plugs, is_pull): 1528 | self.bar = '' 1529 | self.event = 'Updating' if is_pull else 'Installing' 1530 | self.lock = lock 1531 | self.maxy = int(vim.eval('winheight(".")')) 1532 | self.num_plugs = num_plugs 1533 | 1534 | def __where(self, name): 1535 | """ Find first line with name in current buffer. Return line num. """ 1536 | found, lnum = False, 0 1537 | matcher = re.compile('^[-+x*] {0}:'.format(name)) 1538 | for line in vim.current.buffer: 1539 | if matcher.search(line) is not None: 1540 | found = True 1541 | break 1542 | lnum += 1 1543 | 1544 | if not found: 1545 | lnum = -1 1546 | return lnum 1547 | 1548 | def header(self): 1549 | curbuf = vim.current.buffer 1550 | curbuf[0] = self.event + ' plugins ({0}/{1})'.format(len(self.bar), self.num_plugs) 1551 | 1552 | num_spaces = self.num_plugs - len(self.bar) 1553 | curbuf[1] = '[{0}{1}]'.format(self.bar, num_spaces * ' ') 1554 | 1555 | with self.lock: 1556 | vim.command('normal! 2G') 1557 | vim.command('redraw') 1558 | 1559 | def write(self, action, name, lines): 1560 | first, rest = lines[0], lines[1:] 1561 | msg = ['{0} {1}{2}{3}'.format(action, name, ': ' if first else '', first)] 1562 | msg.extend([' ' + line for line in rest]) 1563 | 1564 | try: 1565 | if action == Action.ERROR: 1566 | self.bar += 'x' 1567 | vim.command("call add(s:update.errors, '{0}')".format(name)) 1568 | elif action == Action.DONE: 1569 | self.bar += '=' 1570 | 1571 | curbuf = vim.current.buffer 1572 | lnum = self.__where(name) 1573 | if lnum != -1: # Found matching line num 1574 | del curbuf[lnum] 1575 | if lnum > self.maxy and action in set([Action.INSTALL, Action.UPDATE]): 1576 | lnum = 3 1577 | else: 1578 | lnum = 3 1579 | curbuf.append(msg, lnum) 1580 | 1581 | self.header() 1582 | except vim.error: 1583 | pass 1584 | 1585 | class Command(object): 1586 | CD = 'cd /d' if G_IS_WIN else 'cd' 1587 | 1588 | def __init__(self, cmd, cmd_dir=None, timeout=60, cb=None, clean=None): 1589 | self.cmd = cmd 1590 | if cmd_dir: 1591 | self.cmd = '{0} {1} && {2}'.format(Command.CD, cmd_dir, self.cmd) 1592 | self.timeout = timeout 1593 | self.callback = cb if cb else (lambda msg: None) 1594 | self.clean = clean if clean else (lambda: None) 1595 | self.proc = None 1596 | 1597 | @property 1598 | def alive(self): 1599 | """ Returns true only if command still running. """ 1600 | return self.proc and self.proc.poll() is None 1601 | 1602 | def execute(self, ntries=3): 1603 | """ Execute the command with ntries if CmdTimedOut. 1604 | Returns the output of the command if no Exception. 1605 | """ 1606 | attempt, finished, limit = 0, False, self.timeout 1607 | 1608 | while not finished: 1609 | try: 1610 | attempt += 1 1611 | result = self.try_command() 1612 | finished = True 1613 | return result 1614 | except CmdTimedOut: 1615 | if attempt != ntries: 1616 | self.notify_retry() 1617 | self.timeout += limit 1618 | else: 1619 | raise 1620 | 1621 | def notify_retry(self): 1622 | """ Retry required for command, notify user. """ 1623 | for count in range(3, 0, -1): 1624 | if G_STOP.is_set(): 1625 | raise KeyboardInterrupt 1626 | msg = 'Timeout. Will retry in {0} second{1} ...'.format( 1627 | count, 's' if count != 1 else '') 1628 | self.callback([msg]) 1629 | time.sleep(1) 1630 | self.callback(['Retrying ...']) 1631 | 1632 | def try_command(self): 1633 | """ Execute a cmd & poll for callback. Returns list of output. 1634 | Raises CmdFailed -> return code for Popen isn't 0 1635 | Raises CmdTimedOut -> command exceeded timeout without new output 1636 | """ 1637 | first_line = True 1638 | 1639 | try: 1640 | tfile = tempfile.NamedTemporaryFile(mode='w+b') 1641 | preexec_fn = not G_IS_WIN and os.setsid or None 1642 | self.proc = subprocess.Popen(self.cmd, stdout=tfile, 1643 | stderr=subprocess.STDOUT, 1644 | stdin=subprocess.PIPE, shell=True, 1645 | preexec_fn=preexec_fn) 1646 | thrd = thr.Thread(target=(lambda proc: proc.wait()), args=(self.proc,)) 1647 | thrd.start() 1648 | 1649 | thread_not_started = True 1650 | while thread_not_started: 1651 | try: 1652 | thrd.join(0.1) 1653 | thread_not_started = False 1654 | except RuntimeError: 1655 | pass 1656 | 1657 | while self.alive: 1658 | if G_STOP.is_set(): 1659 | raise KeyboardInterrupt 1660 | 1661 | if first_line or random.random() < G_LOG_PROB: 1662 | first_line = False 1663 | line = '' if G_IS_WIN else nonblock_read(tfile.name) 1664 | if line: 1665 | self.callback([line]) 1666 | 1667 | time_diff = time.time() - os.path.getmtime(tfile.name) 1668 | if time_diff > self.timeout: 1669 | raise CmdTimedOut(['Timeout!']) 1670 | 1671 | thrd.join(0.5) 1672 | 1673 | tfile.seek(0) 1674 | result = [line.decode('utf-8', 'replace').rstrip() for line in tfile] 1675 | 1676 | if self.proc.returncode != 0: 1677 | raise CmdFailed([''] + result) 1678 | 1679 | return result 1680 | except: 1681 | self.terminate() 1682 | raise 1683 | 1684 | def terminate(self): 1685 | """ Terminate process and cleanup. """ 1686 | if self.alive: 1687 | if G_IS_WIN: 1688 | os.kill(self.proc.pid, signal.SIGINT) 1689 | else: 1690 | os.killpg(self.proc.pid, signal.SIGTERM) 1691 | self.clean() 1692 | 1693 | class Plugin(object): 1694 | def __init__(self, name, args, buf_q, lock): 1695 | self.name = name 1696 | self.args = args 1697 | self.buf_q = buf_q 1698 | self.lock = lock 1699 | self.tag = args.get('tag', 0) 1700 | 1701 | def manage(self): 1702 | try: 1703 | if os.path.exists(self.args['dir']): 1704 | self.update() 1705 | else: 1706 | self.install() 1707 | with self.lock: 1708 | thread_vim_command("let s:update.new['{0}'] = 1".format(self.name)) 1709 | except PlugError as exc: 1710 | self.write(Action.ERROR, self.name, exc.msg) 1711 | except KeyboardInterrupt: 1712 | G_STOP.set() 1713 | self.write(Action.ERROR, self.name, ['Interrupted!']) 1714 | except: 1715 | # Any exception except those above print stack trace 1716 | msg = 'Trace:\n{0}'.format(traceback.format_exc().rstrip()) 1717 | self.write(Action.ERROR, self.name, msg.split('\n')) 1718 | raise 1719 | 1720 | def install(self): 1721 | target = self.args['dir'] 1722 | if target[-1] == '\\': 1723 | target = target[0:-1] 1724 | 1725 | def clean(target): 1726 | def _clean(): 1727 | try: 1728 | shutil.rmtree(target) 1729 | except OSError: 1730 | pass 1731 | return _clean 1732 | 1733 | self.write(Action.INSTALL, self.name, ['Installing ...']) 1734 | callback = functools.partial(self.write, Action.INSTALL, self.name) 1735 | cmd = 'git clone {0} {1} {2} {3} 2>&1'.format( 1736 | '' if self.tag else G_CLONE_OPT, G_PROGRESS, self.args['uri'], 1737 | esc(target)) 1738 | com = Command(cmd, None, G_TIMEOUT, callback, clean(target)) 1739 | result = com.execute(G_RETRIES) 1740 | self.write(Action.DONE, self.name, result[-1:]) 1741 | 1742 | def repo_uri(self): 1743 | cmd = 'git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url' 1744 | command = Command(cmd, self.args['dir'], G_TIMEOUT,) 1745 | result = command.execute(G_RETRIES) 1746 | return result[-1] 1747 | 1748 | def update(self): 1749 | actual_uri = self.repo_uri() 1750 | expect_uri = self.args['uri'] 1751 | regex = re.compile(r'^(?:\w+://)?(?:[^@/]*@)?([^:/]*(?::[0-9]*)?)[:/](.*?)(?:\.git)?/?$') 1752 | ma = regex.match(actual_uri) 1753 | mb = regex.match(expect_uri) 1754 | if ma is None or mb is None or ma.groups() != mb.groups(): 1755 | msg = ['', 1756 | 'Invalid URI: {0}'.format(actual_uri), 1757 | 'Expected {0}'.format(expect_uri), 1758 | 'PlugClean required.'] 1759 | raise InvalidURI(msg) 1760 | 1761 | if G_PULL: 1762 | self.write(Action.UPDATE, self.name, ['Updating ...']) 1763 | callback = functools.partial(self.write, Action.UPDATE, self.name) 1764 | fetch_opt = '--depth 99999999' if self.tag and os.path.isfile(os.path.join(self.args['dir'], '.git/shallow')) else '' 1765 | cmd = 'git fetch {0} {1} 2>&1'.format(fetch_opt, G_PROGRESS) 1766 | com = Command(cmd, self.args['dir'], G_TIMEOUT, callback) 1767 | result = com.execute(G_RETRIES) 1768 | self.write(Action.DONE, self.name, result[-1:]) 1769 | else: 1770 | self.write(Action.DONE, self.name, ['Already installed']) 1771 | 1772 | def write(self, action, name, msg): 1773 | self.buf_q.put((action, name, msg)) 1774 | 1775 | class PlugThread(thr.Thread): 1776 | def __init__(self, tname, args): 1777 | super(PlugThread, self).__init__() 1778 | self.tname = tname 1779 | self.args = args 1780 | 1781 | def run(self): 1782 | thr.current_thread().name = self.tname 1783 | buf_q, work_q, lock = self.args 1784 | 1785 | try: 1786 | while not G_STOP.is_set(): 1787 | name, args = work_q.get_nowait() 1788 | plug = Plugin(name, args, buf_q, lock) 1789 | plug.manage() 1790 | work_q.task_done() 1791 | except queue.Empty: 1792 | pass 1793 | 1794 | class RefreshThread(thr.Thread): 1795 | def __init__(self, lock): 1796 | super(RefreshThread, self).__init__() 1797 | self.lock = lock 1798 | self.running = True 1799 | 1800 | def run(self): 1801 | while self.running: 1802 | with self.lock: 1803 | thread_vim_command('noautocmd normal! a') 1804 | time.sleep(0.33) 1805 | 1806 | def stop(self): 1807 | self.running = False 1808 | 1809 | if G_NVIM: 1810 | def thread_vim_command(cmd): 1811 | vim.session.threadsafe_call(lambda: vim.command(cmd)) 1812 | else: 1813 | def thread_vim_command(cmd): 1814 | vim.command(cmd) 1815 | 1816 | def esc(name): 1817 | return '"' + name.replace('"', '\"') + '"' 1818 | 1819 | def nonblock_read(fname): 1820 | """ Read a file with nonblock flag. Return the last line. """ 1821 | fread = os.open(fname, os.O_RDONLY | os.O_NONBLOCK) 1822 | buf = os.read(fread, 100000).decode('utf-8', 'replace') 1823 | os.close(fread) 1824 | 1825 | line = buf.rstrip('\r\n') 1826 | left = max(line.rfind('\r'), line.rfind('\n')) 1827 | if left != -1: 1828 | left += 1 1829 | line = line[left:] 1830 | 1831 | return line 1832 | 1833 | def main(): 1834 | thr.current_thread().name = 'main' 1835 | nthreads = int(vim.eval('s:update.threads')) 1836 | plugs = vim.eval('s:update.todo') 1837 | mac_gui = vim.eval('s:mac_gui') == '1' 1838 | 1839 | lock = thr.Lock() 1840 | buf = Buffer(lock, len(plugs), G_PULL) 1841 | buf_q, work_q = queue.Queue(), queue.Queue() 1842 | for work in plugs.items(): 1843 | work_q.put(work) 1844 | 1845 | start_cnt = thr.active_count() 1846 | for num in range(nthreads): 1847 | tname = 'PlugT-{0:02}'.format(num) 1848 | thread = PlugThread(tname, (buf_q, work_q, lock)) 1849 | thread.start() 1850 | if mac_gui: 1851 | rthread = RefreshThread(lock) 1852 | rthread.start() 1853 | 1854 | while not buf_q.empty() or thr.active_count() != start_cnt: 1855 | try: 1856 | action, name, msg = buf_q.get(True, 0.25) 1857 | buf.write(action, name, ['OK'] if not msg else msg) 1858 | buf_q.task_done() 1859 | except queue.Empty: 1860 | pass 1861 | except KeyboardInterrupt: 1862 | G_STOP.set() 1863 | 1864 | if mac_gui: 1865 | rthread.stop() 1866 | rthread.join() 1867 | 1868 | main() 1869 | EOF 1870 | endfunction 1871 | 1872 | function! s:update_ruby() 1873 | ruby << EOF 1874 | module PlugStream 1875 | SEP = ["\r", "\n", nil] 1876 | def get_line 1877 | buffer = '' 1878 | loop do 1879 | char = readchar rescue return 1880 | if SEP.include? char.chr 1881 | buffer << $/ 1882 | break 1883 | else 1884 | buffer << char 1885 | end 1886 | end 1887 | buffer 1888 | end 1889 | end unless defined?(PlugStream) 1890 | 1891 | def esc arg 1892 | %["#{arg.gsub('"', '\"')}"] 1893 | end 1894 | 1895 | def killall pid 1896 | pids = [pid] 1897 | if /mswin|mingw|bccwin/ =~ RUBY_PLATFORM 1898 | pids.each { |pid| Process.kill 'INT', pid.to_i rescue nil } 1899 | else 1900 | unless `which pgrep 2> /dev/null`.empty? 1901 | children = pids 1902 | until children.empty? 1903 | children = children.map { |pid| 1904 | `pgrep -P #{pid}`.lines.map { |l| l.chomp } 1905 | }.flatten 1906 | pids += children 1907 | end 1908 | end 1909 | pids.each { |pid| Process.kill 'TERM', pid.to_i rescue nil } 1910 | end 1911 | end 1912 | 1913 | def compare_git_uri a, b 1914 | regex = %r{^(?:\w+://)?(?:[^@/]*@)?([^:/]*(?::[0-9]*)?)[:/](.*?)(?:\.git)?/?$} 1915 | regex.match(a).to_a.drop(1) == regex.match(b).to_a.drop(1) 1916 | end 1917 | 1918 | require 'thread' 1919 | require 'fileutils' 1920 | require 'timeout' 1921 | running = true 1922 | iswin = VIM::evaluate('s:is_win').to_i == 1 1923 | pull = VIM::evaluate('s:update.pull').to_i == 1 1924 | base = VIM::evaluate('g:plug_home') 1925 | all = VIM::evaluate('s:update.todo') 1926 | limit = VIM::evaluate('get(g:, "plug_timeout", 60)') 1927 | tries = VIM::evaluate('get(g:, "plug_retries", 2)') + 1 1928 | nthr = VIM::evaluate('s:update.threads').to_i 1929 | maxy = VIM::evaluate('winheight(".")').to_i 1930 | vim7 = VIM::evaluate('v:version').to_i <= 703 && RUBY_PLATFORM =~ /darwin/ 1931 | cd = iswin ? 'cd /d' : 'cd' 1932 | tot = VIM::evaluate('len(s:update.todo)') || 0 1933 | bar = '' 1934 | skip = 'Already installed' 1935 | mtx = Mutex.new 1936 | take1 = proc { mtx.synchronize { running && all.shift } } 1937 | logh = proc { 1938 | cnt = bar.length 1939 | $curbuf[1] = "#{pull ? 'Updating' : 'Installing'} plugins (#{cnt}/#{tot})" 1940 | $curbuf[2] = '[' + bar.ljust(tot) + ']' 1941 | VIM::command('normal! 2G') 1942 | VIM::command('redraw') 1943 | } 1944 | where = proc { |name| (1..($curbuf.length)).find { |l| $curbuf[l] =~ /^[-+x*] #{name}:/ } } 1945 | log = proc { |name, result, type| 1946 | mtx.synchronize do 1947 | ing = ![true, false].include?(type) 1948 | bar += type ? '=' : 'x' unless ing 1949 | b = case type 1950 | when :install then '+' when :update then '*' 1951 | when true, nil then '-' else 1952 | VIM::command("call add(s:update.errors, '#{name}')") 1953 | 'x' 1954 | end 1955 | result = 1956 | if type || type.nil? 1957 | ["#{b} #{name}: #{result.lines.to_a.last || 'OK'}"] 1958 | elsif result =~ /^Interrupted|^Timeout/ 1959 | ["#{b} #{name}: #{result}"] 1960 | else 1961 | ["#{b} #{name}"] + result.lines.map { |l| " " << l } 1962 | end 1963 | if lnum = where.call(name) 1964 | $curbuf.delete lnum 1965 | lnum = 4 if ing && lnum > maxy 1966 | end 1967 | result.each_with_index do |line, offset| 1968 | $curbuf.append((lnum || 4) - 1 + offset, line.gsub(/\e\[./, '').chomp) 1969 | end 1970 | logh.call 1971 | end 1972 | } 1973 | bt = proc { |cmd, name, type, cleanup| 1974 | tried = timeout = 0 1975 | begin 1976 | tried += 1 1977 | timeout += limit 1978 | fd = nil 1979 | data = '' 1980 | if iswin 1981 | Timeout::timeout(timeout) do 1982 | tmp = VIM::evaluate('tempname()') 1983 | system("(#{cmd}) > #{tmp}") 1984 | data = File.read(tmp).chomp 1985 | File.unlink tmp rescue nil 1986 | end 1987 | else 1988 | fd = IO.popen(cmd).extend(PlugStream) 1989 | first_line = true 1990 | log_prob = 1.0 / nthr 1991 | while line = Timeout::timeout(timeout) { fd.get_line } 1992 | data << line 1993 | log.call name, line.chomp, type if name && (first_line || rand < log_prob) 1994 | first_line = false 1995 | end 1996 | fd.close 1997 | end 1998 | [$? == 0, data.chomp] 1999 | rescue Timeout::Error, Interrupt => e 2000 | if fd && !fd.closed? 2001 | killall fd.pid 2002 | fd.close 2003 | end 2004 | cleanup.call if cleanup 2005 | if e.is_a?(Timeout::Error) && tried < tries 2006 | 3.downto(1) do |countdown| 2007 | s = countdown > 1 ? 's' : '' 2008 | log.call name, "Timeout. Will retry in #{countdown} second#{s} ...", type 2009 | sleep 1 2010 | end 2011 | log.call name, 'Retrying ...', type 2012 | retry 2013 | end 2014 | [false, e.is_a?(Interrupt) ? "Interrupted!" : "Timeout!"] 2015 | end 2016 | } 2017 | main = Thread.current 2018 | threads = [] 2019 | watcher = Thread.new { 2020 | if vim7 2021 | while VIM::evaluate('getchar(1)') 2022 | sleep 0.1 2023 | end 2024 | else 2025 | require 'io/console' # >= Ruby 1.9 2026 | nil until IO.console.getch == 3.chr 2027 | end 2028 | mtx.synchronize do 2029 | running = false 2030 | threads.each { |t| t.raise Interrupt } unless vim7 2031 | end 2032 | threads.each { |t| t.join rescue nil } 2033 | main.kill 2034 | } 2035 | refresh = Thread.new { 2036 | while true 2037 | mtx.synchronize do 2038 | break unless running 2039 | VIM::command('noautocmd normal! a') 2040 | end 2041 | sleep 0.2 2042 | end 2043 | } if VIM::evaluate('s:mac_gui') == 1 2044 | 2045 | clone_opt = VIM::evaluate('s:clone_opt').join(' ') 2046 | progress = VIM::evaluate('s:progress_opt(1)') 2047 | nthr.times do 2048 | mtx.synchronize do 2049 | threads << Thread.new { 2050 | while pair = take1.call 2051 | name = pair.first 2052 | dir, uri, tag = pair.last.values_at *%w[dir uri tag] 2053 | exists = File.directory? dir 2054 | ok, result = 2055 | if exists 2056 | chdir = "#{cd} #{iswin ? dir : esc(dir)}" 2057 | ret, data = bt.call "#{chdir} && git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url", nil, nil, nil 2058 | current_uri = data.lines.to_a.last 2059 | if !ret 2060 | if data =~ /^Interrupted|^Timeout/ 2061 | [false, data] 2062 | else 2063 | [false, [data.chomp, "PlugClean required."].join($/)] 2064 | end 2065 | elsif !compare_git_uri(current_uri, uri) 2066 | [false, ["Invalid URI: #{current_uri}", 2067 | "Expected: #{uri}", 2068 | "PlugClean required."].join($/)] 2069 | else 2070 | if pull 2071 | log.call name, 'Updating ...', :update 2072 | fetch_opt = (tag && File.exist?(File.join(dir, '.git/shallow'))) ? '--depth 99999999' : '' 2073 | bt.call "#{chdir} && git fetch #{fetch_opt} #{progress} 2>&1", name, :update, nil 2074 | else 2075 | [true, skip] 2076 | end 2077 | end 2078 | else 2079 | d = esc dir.sub(%r{[\\/]+$}, '') 2080 | log.call name, 'Installing ...', :install 2081 | bt.call "git clone #{clone_opt unless tag} #{progress} #{uri} #{d} 2>&1", name, :install, proc { 2082 | FileUtils.rm_rf dir 2083 | } 2084 | end 2085 | mtx.synchronize { VIM::command("let s:update.new['#{name}'] = 1") } if !exists && ok 2086 | log.call name, result, ok 2087 | end 2088 | } if running 2089 | end 2090 | end 2091 | threads.each { |t| t.join rescue nil } 2092 | logh.call 2093 | refresh.kill if refresh 2094 | watcher.kill 2095 | EOF 2096 | endfunction 2097 | 2098 | function! s:shellesc_cmd(arg, script) 2099 | let escaped = substitute('"'.a:arg.'"', '[&|<>()@^!"]', '^&', 'g') 2100 | return substitute(escaped, '%', (a:script ? '%' : '^') . '&', 'g') 2101 | endfunction 2102 | 2103 | function! s:shellesc_ps1(arg) 2104 | return "'".substitute(escape(a:arg, '\"'), "'", "''", 'g')."'" 2105 | endfunction 2106 | 2107 | function! s:shellesc_sh(arg) 2108 | return "'".substitute(a:arg, "'", "'\\\\''", 'g')."'" 2109 | endfunction 2110 | 2111 | " Escape the shell argument based on the shell. 2112 | " Vim and Neovim's shellescape() are insufficient. 2113 | " 1. shellslash determines whether to use single/double quotes. 2114 | " Double-quote escaping is fragile for cmd.exe. 2115 | " 2. It does not work for powershell. 2116 | " 3. It does not work for *sh shells if the command is executed 2117 | " via cmd.exe (ie. cmd.exe /c sh -c command command_args) 2118 | " 4. It does not support batchfile syntax. 2119 | " 2120 | " Accepts an optional dictionary with the following keys: 2121 | " - shell: same as Vim/Neovim 'shell' option. 2122 | " If unset, fallback to 'cmd.exe' on Windows or 'sh'. 2123 | " - script: If truthy and shell is cmd.exe, escape for batchfile syntax. 2124 | function! plug#shellescape(arg, ...) 2125 | if a:arg =~# '^[A-Za-z0-9_/:.-]\+$' 2126 | return a:arg 2127 | endif 2128 | let opts = a:0 > 0 && type(a:1) == s:TYPE.dict ? a:1 : {} 2129 | let shell = get(opts, 'shell', s:is_win ? 'cmd.exe' : 'sh') 2130 | let script = get(opts, 'script', 1) 2131 | if shell =~# 'cmd\.exe' 2132 | return s:shellesc_cmd(a:arg, script) 2133 | elseif shell =~# 'powershell\.exe' || shell =~# 'pwsh$' 2134 | return s:shellesc_ps1(a:arg) 2135 | endif 2136 | return s:shellesc_sh(a:arg) 2137 | endfunction 2138 | 2139 | function! s:glob_dir(path) 2140 | return map(filter(s:glob(a:path, '**'), 'isdirectory(v:val)'), 's:dirpath(v:val)') 2141 | endfunction 2142 | 2143 | function! s:progress_bar(line, bar, total) 2144 | call setline(a:line, '[' . s:lpad(a:bar, a:total) . ']') 2145 | endfunction 2146 | 2147 | function! s:compare_git_uri(a, b) 2148 | " See `git help clone' 2149 | " https:// [user@] github.com[:port] / junegunn/vim-plug [.git] 2150 | " [git@] github.com[:port] : junegunn/vim-plug [.git] 2151 | " file:// / junegunn/vim-plug [/] 2152 | " / junegunn/vim-plug [/] 2153 | let pat = '^\%(\w\+://\)\='.'\%([^@/]*@\)\='.'\([^:/]*\%(:[0-9]*\)\=\)'.'[:/]'.'\(.\{-}\)'.'\%(\.git\)\=/\?$' 2154 | let ma = matchlist(a:a, pat) 2155 | let mb = matchlist(a:b, pat) 2156 | return ma[1:2] ==# mb[1:2] 2157 | endfunction 2158 | 2159 | function! s:format_message(bullet, name, message) 2160 | if a:bullet != 'x' 2161 | return [printf('%s %s: %s', a:bullet, a:name, s:lastline(a:message))] 2162 | else 2163 | let lines = map(s:lines(a:message), '" ".v:val') 2164 | return extend([printf('x %s:', a:name)], lines) 2165 | endif 2166 | endfunction 2167 | 2168 | function! s:with_cd(cmd, dir, ...) 2169 | let script = a:0 > 0 ? a:1 : 1 2170 | return printf('cd%s %s && %s', s:is_win ? ' /d' : '', plug#shellescape(a:dir, {'script': script}), a:cmd) 2171 | endfunction 2172 | 2173 | function! s:system(cmd, ...) 2174 | let batchfile = '' 2175 | try 2176 | let [sh, shellcmdflag, shrd] = s:chsh(1) 2177 | if type(a:cmd) == s:TYPE.list 2178 | " Neovim's system() supports list argument to bypass the shell 2179 | " but it cannot set the working directory for the command. 2180 | " Assume that the command does not rely on the shell. 2181 | if has('nvim') && a:0 == 0 2182 | return system(a:cmd) 2183 | endif 2184 | let cmd = join(map(copy(a:cmd), 'plug#shellescape(v:val, {"shell": &shell, "script": 0})')) 2185 | if &shell =~# 'powershell\.exe' 2186 | let cmd = '& ' . cmd 2187 | endif 2188 | else 2189 | let cmd = a:cmd 2190 | endif 2191 | if a:0 > 0 2192 | let cmd = s:with_cd(cmd, a:1, type(a:cmd) != s:TYPE.list) 2193 | endif 2194 | if s:is_win && type(a:cmd) != s:TYPE.list 2195 | let [batchfile, cmd] = s:batchfile(cmd) 2196 | endif 2197 | return system(cmd) 2198 | finally 2199 | let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd] 2200 | if s:is_win && filereadable(batchfile) 2201 | call delete(batchfile) 2202 | endif 2203 | endtry 2204 | endfunction 2205 | 2206 | function! s:system_chomp(...) 2207 | let ret = call('s:system', a:000) 2208 | return v:shell_error ? '' : substitute(ret, '\n$', '', '') 2209 | endfunction 2210 | 2211 | function! s:git_validate(spec, check_branch) 2212 | let err = '' 2213 | if isdirectory(a:spec.dir) 2214 | 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)) 2215 | let remote = result[-1] 2216 | if v:shell_error 2217 | let err = join([remote, 'PlugClean required.'], "\n") 2218 | elseif !s:compare_git_uri(remote, a:spec.uri) 2219 | let err = join(['Invalid URI: '.remote, 2220 | \ 'Expected: '.a:spec.uri, 2221 | \ 'PlugClean required.'], "\n") 2222 | elseif a:check_branch && has_key(a:spec, 'commit') 2223 | let result = s:lines(s:system('git rev-parse HEAD 2>&1', a:spec.dir)) 2224 | let sha = result[-1] 2225 | if v:shell_error 2226 | let err = join(add(result, 'PlugClean required.'), "\n") 2227 | elseif !s:hash_match(sha, a:spec.commit) 2228 | let err = join([printf('Invalid HEAD (expected: %s, actual: %s)', 2229 | \ a:spec.commit[:6], sha[:6]), 2230 | \ 'PlugUpdate required.'], "\n") 2231 | endif 2232 | elseif a:check_branch 2233 | let branch = result[0] 2234 | " Check tag 2235 | if has_key(a:spec, 'tag') 2236 | let tag = s:system_chomp('git describe --exact-match --tags HEAD 2>&1', a:spec.dir) 2237 | if a:spec.tag !=# tag && a:spec.tag !~ '\*' 2238 | let err = printf('Invalid tag: %s (expected: %s). Try PlugUpdate.', 2239 | \ (empty(tag) ? 'N/A' : tag), a:spec.tag) 2240 | endif 2241 | " Check branch 2242 | elseif a:spec.branch !=# branch 2243 | let err = printf('Invalid branch: %s (expected: %s). Try PlugUpdate.', 2244 | \ branch, a:spec.branch) 2245 | endif 2246 | if empty(err) 2247 | let [ahead, behind] = split(s:lastline(s:system([ 2248 | \ 'git', 'rev-list', '--count', '--left-right', 2249 | \ printf('HEAD...origin/%s', a:spec.branch) 2250 | \ ], a:spec.dir)), '\t') 2251 | if !v:shell_error && ahead 2252 | if behind 2253 | " Only mention PlugClean if diverged, otherwise it's likely to be 2254 | " pushable (and probably not that messed up). 2255 | let err = printf( 2256 | \ "Diverged from origin/%s (%d commit(s) ahead and %d commit(s) behind!\n" 2257 | \ .'Backup local changes and run PlugClean and PlugUpdate to reinstall it.', a:spec.branch, ahead, behind) 2258 | else 2259 | let err = printf("Ahead of origin/%s by %d commit(s).\n" 2260 | \ .'Cannot update until local changes are pushed.', 2261 | \ a:spec.branch, ahead) 2262 | endif 2263 | endif 2264 | endif 2265 | endif 2266 | else 2267 | let err = 'Not found' 2268 | endif 2269 | return [err, err =~# 'PlugClean'] 2270 | endfunction 2271 | 2272 | function! s:rm_rf(dir) 2273 | if isdirectory(a:dir) 2274 | call s:system(s:is_win 2275 | \ ? 'rmdir /S /Q '.plug#shellescape(a:dir) 2276 | \ : ['rm', '-rf', a:dir]) 2277 | endif 2278 | endfunction 2279 | 2280 | function! s:clean(force) 2281 | call s:prepare() 2282 | call append(0, 'Searching for invalid plugins in '.g:plug_home) 2283 | call append(1, '') 2284 | 2285 | " List of valid directories 2286 | let dirs = [] 2287 | let errs = {} 2288 | let [cnt, total] = [0, len(g:plugs)] 2289 | for [name, spec] in items(g:plugs) 2290 | if !s:is_managed(name) 2291 | call add(dirs, spec.dir) 2292 | else 2293 | let [err, clean] = s:git_validate(spec, 1) 2294 | if clean 2295 | let errs[spec.dir] = s:lines(err)[0] 2296 | else 2297 | call add(dirs, spec.dir) 2298 | endif 2299 | endif 2300 | let cnt += 1 2301 | call s:progress_bar(2, repeat('=', cnt), total) 2302 | normal! 2G 2303 | redraw 2304 | endfor 2305 | 2306 | let allowed = {} 2307 | for dir in dirs 2308 | let allowed[s:dirpath(s:plug_fnamemodify(dir, ':h:h'))] = 1 2309 | let allowed[dir] = 1 2310 | for child in s:glob_dir(dir) 2311 | let allowed[child] = 1 2312 | endfor 2313 | endfor 2314 | 2315 | let todo = [] 2316 | let found = sort(s:glob_dir(g:plug_home)) 2317 | while !empty(found) 2318 | let f = remove(found, 0) 2319 | if !has_key(allowed, f) && isdirectory(f) 2320 | call add(todo, f) 2321 | call append(line('$'), '- ' . f) 2322 | if has_key(errs, f) 2323 | call append(line('$'), ' ' . errs[f]) 2324 | endif 2325 | let found = filter(found, 'stridx(v:val, f) != 0') 2326 | end 2327 | endwhile 2328 | 2329 | 4 2330 | redraw 2331 | if empty(todo) 2332 | call append(line('$'), 'Already clean.') 2333 | else 2334 | let s:clean_count = 0 2335 | call append(3, ['Directories to delete:', '']) 2336 | redraw! 2337 | if a:force || s:ask_no_interrupt('Delete all directories?') 2338 | call s:delete([6, line('$')], 1) 2339 | else 2340 | call setline(4, 'Cancelled.') 2341 | nnoremap d :set opfunc=delete_opg@ 2342 | nmap dd d_ 2343 | xnoremap d :call delete_op(visualmode(), 1) 2344 | echo 'Delete the lines (d{motion}) to delete the corresponding directories' 2345 | endif 2346 | endif 2347 | 4 2348 | setlocal nomodifiable 2349 | endfunction 2350 | 2351 | function! s:delete_op(type, ...) 2352 | call s:delete(a:0 ? [line("'<"), line("'>")] : [line("'["), line("']")], 0) 2353 | endfunction 2354 | 2355 | function! s:delete(range, force) 2356 | let [l1, l2] = a:range 2357 | let force = a:force 2358 | while l1 <= l2 2359 | let line = getline(l1) 2360 | if line =~ '^- ' && isdirectory(line[2:]) 2361 | execute l1 2362 | redraw! 2363 | let answer = force ? 1 : s:ask('Delete '.line[2:].'?', 1) 2364 | let force = force || answer > 1 2365 | if answer 2366 | call s:rm_rf(line[2:]) 2367 | setlocal modifiable 2368 | call setline(l1, '~'.line[1:]) 2369 | let s:clean_count += 1 2370 | call setline(4, printf('Removed %d directories.', s:clean_count)) 2371 | setlocal nomodifiable 2372 | endif 2373 | endif 2374 | let l1 += 1 2375 | endwhile 2376 | endfunction 2377 | 2378 | function! s:upgrade() 2379 | echo 'Downloading the latest version of vim-plug' 2380 | redraw 2381 | let tmp = s:plug_tempname() 2382 | let new = tmp . '/plug.vim' 2383 | 2384 | try 2385 | let out = s:system(['git', 'clone', '--depth', '1', s:plug_src, tmp]) 2386 | if v:shell_error 2387 | return s:err('Error upgrading vim-plug: '. out) 2388 | endif 2389 | 2390 | if readfile(s:me) ==# readfile(new) 2391 | echo 'vim-plug is already up-to-date' 2392 | return 0 2393 | else 2394 | call rename(s:me, s:me . '.old') 2395 | call rename(new, s:me) 2396 | unlet g:loaded_plug 2397 | echo 'vim-plug has been upgraded' 2398 | return 1 2399 | endif 2400 | finally 2401 | silent! call s:rm_rf(tmp) 2402 | endtry 2403 | endfunction 2404 | 2405 | function! s:upgrade_specs() 2406 | for spec in values(g:plugs) 2407 | let spec.frozen = get(spec, 'frozen', 0) 2408 | endfor 2409 | endfunction 2410 | 2411 | function! s:status() 2412 | call s:prepare() 2413 | call append(0, 'Checking plugins') 2414 | call append(1, '') 2415 | 2416 | let ecnt = 0 2417 | let unloaded = 0 2418 | let [cnt, total] = [0, len(g:plugs)] 2419 | for [name, spec] in items(g:plugs) 2420 | let is_dir = isdirectory(spec.dir) 2421 | if has_key(spec, 'uri') 2422 | if is_dir 2423 | let [err, _] = s:git_validate(spec, 1) 2424 | let [valid, msg] = [empty(err), empty(err) ? 'OK' : err] 2425 | else 2426 | let [valid, msg] = [0, 'Not found. Try PlugInstall.'] 2427 | endif 2428 | else 2429 | if is_dir 2430 | let [valid, msg] = [1, 'OK'] 2431 | else 2432 | let [valid, msg] = [0, 'Not found.'] 2433 | endif 2434 | endif 2435 | let cnt += 1 2436 | let ecnt += !valid 2437 | " `s:loaded` entry can be missing if PlugUpgraded 2438 | if is_dir && get(s:loaded, name, -1) == 0 2439 | let unloaded = 1 2440 | let msg .= ' (not loaded)' 2441 | endif 2442 | call s:progress_bar(2, repeat('=', cnt), total) 2443 | call append(3, s:format_message(valid ? '-' : 'x', name, msg)) 2444 | normal! 2G 2445 | redraw 2446 | endfor 2447 | call setline(1, 'Finished. '.ecnt.' error(s).') 2448 | normal! gg 2449 | setlocal nomodifiable 2450 | if unloaded 2451 | echo "Press 'L' on each line to load plugin, or 'U' to update" 2452 | nnoremap L :call status_load(line('.')) 2453 | xnoremap L :call status_load(line('.')) 2454 | end 2455 | endfunction 2456 | 2457 | function! s:extract_name(str, prefix, suffix) 2458 | return matchstr(a:str, '^'.a:prefix.' \zs[^:]\+\ze:.*'.a:suffix.'$') 2459 | endfunction 2460 | 2461 | function! s:status_load(lnum) 2462 | let line = getline(a:lnum) 2463 | let name = s:extract_name(line, '-', '(not loaded)') 2464 | if !empty(name) 2465 | call plug#load(name) 2466 | setlocal modifiable 2467 | call setline(a:lnum, substitute(line, ' (not loaded)$', '', '')) 2468 | setlocal nomodifiable 2469 | endif 2470 | endfunction 2471 | 2472 | function! s:status_update() range 2473 | let lines = getline(a:firstline, a:lastline) 2474 | let names = filter(map(lines, 's:extract_name(v:val, "[x-]", "")'), '!empty(v:val)') 2475 | if !empty(names) 2476 | echo 2477 | execute 'PlugUpdate' join(names) 2478 | endif 2479 | endfunction 2480 | 2481 | function! s:is_preview_window_open() 2482 | silent! wincmd P 2483 | if &previewwindow 2484 | wincmd p 2485 | return 1 2486 | endif 2487 | endfunction 2488 | 2489 | function! s:find_name(lnum) 2490 | for lnum in reverse(range(1, a:lnum)) 2491 | let line = getline(lnum) 2492 | if empty(line) 2493 | return '' 2494 | endif 2495 | let name = s:extract_name(line, '-', '') 2496 | if !empty(name) 2497 | return name 2498 | endif 2499 | endfor 2500 | return '' 2501 | endfunction 2502 | 2503 | function! s:preview_commit() 2504 | if b:plug_preview < 0 2505 | let b:plug_preview = !s:is_preview_window_open() 2506 | endif 2507 | 2508 | let sha = matchstr(getline('.'), '^ \X*\zs[0-9a-f]\{7,9}') 2509 | if empty(sha) 2510 | return 2511 | endif 2512 | 2513 | let name = s:find_name(line('.')) 2514 | if empty(name) || !has_key(g:plugs, name) || !isdirectory(g:plugs[name].dir) 2515 | return 2516 | endif 2517 | 2518 | if exists('g:plug_pwindow') && !s:is_preview_window_open() 2519 | execute g:plug_pwindow 2520 | execute 'e' sha 2521 | else 2522 | execute 'pedit' sha 2523 | wincmd P 2524 | endif 2525 | setlocal previewwindow filetype=git buftype=nofile nobuflisted modifiable 2526 | let batchfile = '' 2527 | try 2528 | let [sh, shellcmdflag, shrd] = s:chsh(1) 2529 | let cmd = 'cd '.plug#shellescape(g:plugs[name].dir).' && git show --no-color --pretty=medium '.sha 2530 | if s:is_win 2531 | let [batchfile, cmd] = s:batchfile(cmd) 2532 | endif 2533 | execute 'silent %!' cmd 2534 | finally 2535 | let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd] 2536 | if s:is_win && filereadable(batchfile) 2537 | call delete(batchfile) 2538 | endif 2539 | endtry 2540 | setlocal nomodifiable 2541 | nnoremap q :q 2542 | wincmd p 2543 | endfunction 2544 | 2545 | function! s:section(flags) 2546 | call search('\(^[x-] \)\@<=[^:]\+:', a:flags) 2547 | endfunction 2548 | 2549 | function! s:format_git_log(line) 2550 | let indent = ' ' 2551 | let tokens = split(a:line, nr2char(1)) 2552 | if len(tokens) != 5 2553 | return indent.substitute(a:line, '\s*$', '', '') 2554 | endif 2555 | let [graph, sha, refs, subject, date] = tokens 2556 | let tag = matchstr(refs, 'tag: [^,)]\+') 2557 | let tag = empty(tag) ? ' ' : ' ('.tag.') ' 2558 | return printf('%s%s%s%s%s (%s)', indent, graph, sha, tag, subject, date) 2559 | endfunction 2560 | 2561 | function! s:append_ul(lnum, text) 2562 | call append(a:lnum, ['', a:text, repeat('-', len(a:text))]) 2563 | endfunction 2564 | 2565 | function! s:diff() 2566 | call s:prepare() 2567 | call append(0, ['Collecting changes ...', '']) 2568 | let cnts = [0, 0] 2569 | let bar = '' 2570 | let total = filter(copy(g:plugs), 's:is_managed(v:key) && isdirectory(v:val.dir)') 2571 | call s:progress_bar(2, bar, len(total)) 2572 | for origin in [1, 0] 2573 | let plugs = reverse(sort(items(filter(copy(total), (origin ? '' : '!').'(has_key(v:val, "commit") || has_key(v:val, "tag"))')))) 2574 | if empty(plugs) 2575 | continue 2576 | endif 2577 | call s:append_ul(2, origin ? 'Pending updates:' : 'Last update:') 2578 | for [k, v] in plugs 2579 | let range = origin ? '..origin/'.v.branch : 'HEAD@{1}..' 2580 | let cmd = ['git', 'log', '--graph', '--color=never'] 2581 | if s:git_version_requirement(2, 10, 0) 2582 | call add(cmd, '--no-show-signature') 2583 | endif 2584 | call extend(cmd, ['--pretty=format:%x01%h%x01%d%x01%s%x01%cr', range]) 2585 | if has_key(v, 'rtp') 2586 | call extend(cmd, ['--', v.rtp]) 2587 | endif 2588 | let diff = s:system_chomp(cmd, v.dir) 2589 | if !empty(diff) 2590 | let ref = has_key(v, 'tag') ? (' (tag: '.v.tag.')') : has_key(v, 'commit') ? (' '.v.commit) : '' 2591 | call append(5, extend(['', '- '.k.':'.ref], map(s:lines(diff), 's:format_git_log(v:val)'))) 2592 | let cnts[origin] += 1 2593 | endif 2594 | let bar .= '=' 2595 | call s:progress_bar(2, bar, len(total)) 2596 | normal! 2G 2597 | redraw 2598 | endfor 2599 | if !cnts[origin] 2600 | call append(5, ['', 'N/A']) 2601 | endif 2602 | endfor 2603 | call setline(1, printf('%d plugin(s) updated.', cnts[0]) 2604 | \ . (cnts[1] ? printf(' %d plugin(s) have pending updates.', cnts[1]) : '')) 2605 | 2606 | if cnts[0] || cnts[1] 2607 | nnoremap (plug-preview) :silent! call preview_commit() 2608 | if empty(maparg("\", 'n')) 2609 | nmap (plug-preview) 2610 | endif 2611 | if empty(maparg('o', 'n')) 2612 | nmap o (plug-preview) 2613 | endif 2614 | endif 2615 | if cnts[0] 2616 | nnoremap X :call revert() 2617 | echo "Press 'X' on each block to revert the update" 2618 | endif 2619 | normal! gg 2620 | setlocal nomodifiable 2621 | endfunction 2622 | 2623 | function! s:revert() 2624 | if search('^Pending updates', 'bnW') 2625 | return 2626 | endif 2627 | 2628 | let name = s:find_name(line('.')) 2629 | if empty(name) || !has_key(g:plugs, name) || 2630 | \ input(printf('Revert the update of %s? (y/N) ', name)) !~? '^y' 2631 | return 2632 | endif 2633 | 2634 | call s:system('git reset --hard HEAD@{1} && git checkout '.plug#shellescape(g:plugs[name].branch).' --', g:plugs[name].dir) 2635 | setlocal modifiable 2636 | normal! "_dap 2637 | setlocal nomodifiable 2638 | echo 'Reverted' 2639 | endfunction 2640 | 2641 | function! s:snapshot(force, ...) abort 2642 | call s:prepare() 2643 | setf vim 2644 | call append(0, ['" Generated by vim-plug', 2645 | \ '" '.strftime("%c"), 2646 | \ '" :source this file in vim to restore the snapshot', 2647 | \ '" or execute: vim -S snapshot.vim', 2648 | \ '', '', 'PlugUpdate!']) 2649 | 1 2650 | let anchor = line('$') - 3 2651 | let names = sort(keys(filter(copy(g:plugs), 2652 | \'has_key(v:val, "uri") && !has_key(v:val, "commit") && isdirectory(v:val.dir)'))) 2653 | for name in reverse(names) 2654 | let sha = s:system_chomp(['git', 'rev-parse', '--short', 'HEAD'], g:plugs[name].dir) 2655 | if !empty(sha) 2656 | call append(anchor, printf("silent! let g:plugs['%s'].commit = '%s'", name, sha)) 2657 | redraw 2658 | endif 2659 | endfor 2660 | 2661 | if a:0 > 0 2662 | let fn = s:plug_expand(a:1) 2663 | if filereadable(fn) && !(a:force || s:ask(a:1.' already exists. Overwrite?')) 2664 | return 2665 | endif 2666 | call writefile(getline(1, '$'), fn) 2667 | echo 'Saved as '.a:1 2668 | silent execute 'e' s:esc(fn) 2669 | setf vim 2670 | endif 2671 | endfunction 2672 | 2673 | function! s:split_rtp() 2674 | return split(&rtp, '\\\@