├── .gitignore
├── .netrwhist
├── LICENSE
├── README.md
├── autoload
└── plug.vim
├── deploy.sh
├── img
├── QR.png
└── screenshots.png
├── template.sh
├── version
├── vimrc
└── vimrc_agiclass
/.gitignore:
--------------------------------------------------------------------------------
1 | /plug/
2 |
--------------------------------------------------------------------------------
/.netrwhist:
--------------------------------------------------------------------------------
1 | let g:netrw_dirhistmax =10
2 | let g:netrw_dirhist_cnt =1
3 | let g:netrw_dirhist_1='/home/tjx/html'
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction, and
10 | distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by the copyright
13 | owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all other entities
16 | that control, are controlled by, or are under common control with that entity.
17 | For the purposes of this definition, "control" means (i) the power, direct or
18 | indirect, to cause the direction or management of such entity, whether by
19 | contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
20 | outstanding shares, or (iii) beneficial ownership of such entity.
21 |
22 | "You" (or "Your") shall mean an individual or Legal Entity exercising
23 | permissions granted by this License.
24 |
25 | "Source" form shall mean the preferred form for making modifications, including
26 | but not limited to software source code, documentation source, and configuration
27 | files.
28 |
29 | "Object" form shall mean any form resulting from mechanical transformation or
30 | translation of a Source form, including but not limited to compiled object code,
31 | generated documentation, and conversions to other media types.
32 |
33 | "Work" shall mean the work of authorship, whether in Source or Object form, made
34 | available under the License, as indicated by a copyright notice that is included
35 | in or attached to the work (an example is provided in the Appendix below).
36 |
37 | "Derivative Works" shall mean any work, whether in Source or Object form, that
38 | is based on (or derived from) the Work and for which the editorial revisions,
39 | annotations, elaborations, or other modifications represent, as a whole, an
40 | original work of authorship. For the purposes of this License, Derivative Works
41 | shall not include works that remain separable from, or merely link (or bind by
42 | name) to the interfaces of, the Work and Derivative Works thereof.
43 |
44 | "Contribution" shall mean any work of authorship, including the original version
45 | of the Work and any modifications or additions to that Work or Derivative Works
46 | thereof, that is intentionally submitted to Licensor for inclusion in the Work
47 | by the copyright owner or by an individual or Legal Entity authorized to submit
48 | on behalf of the copyright owner. For the purposes of this definition,
49 | "submitted" means any form of electronic, verbal, or written communication sent
50 | to the Licensor or its representatives, including but not limited to
51 | communication on electronic mailing lists, source code control systems, and
52 | issue tracking systems that are managed by, or on behalf of, the Licensor for
53 | the purpose of discussing and improving the Work, but excluding communication
54 | that is conspicuously marked or otherwise designated in writing by the copyright
55 | owner as "Not a Contribution."
56 |
57 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf
58 | of whom a Contribution has been received by Licensor and subsequently
59 | incorporated within the Work.
60 |
61 | 2. Grant of Copyright License.
62 |
63 | Subject to the terms and conditions of this License, each Contributor hereby
64 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
65 | irrevocable copyright license to reproduce, prepare Derivative Works of,
66 | publicly display, publicly perform, sublicense, and distribute the Work and such
67 | Derivative Works in Source or Object form.
68 |
69 | 3. Grant of Patent License.
70 |
71 | Subject to the terms and conditions of this License, each Contributor hereby
72 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
73 | irrevocable (except as stated in this section) patent license to make, have
74 | made, use, offer to sell, sell, import, and otherwise transfer the Work, where
75 | such license applies only to those patent claims licensable by such Contributor
76 | that are necessarily infringed by their Contribution(s) alone or by combination
77 | of their Contribution(s) with the Work to which such Contribution(s) was
78 | submitted. If You institute patent litigation against any entity (including a
79 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a
80 | Contribution incorporated within the Work constitutes direct or contributory
81 | patent infringement, then any patent licenses granted to You under this License
82 | for that Work shall terminate as of the date such litigation is filed.
83 |
84 | 4. Redistribution.
85 |
86 | You may reproduce and distribute copies of the Work or Derivative Works thereof
87 | in any medium, with or without modifications, and in Source or Object form,
88 | provided that You meet the following conditions:
89 |
90 | You must give any other recipients of the Work or Derivative Works a copy of
91 | this License; and
92 | You must cause any modified files to carry prominent notices stating that You
93 | changed the files; and
94 | You must retain, in the Source form of any Derivative Works that You distribute,
95 | all copyright, patent, trademark, and attribution notices from the Source form
96 | of the Work, excluding those notices that do not pertain to any part of the
97 | Derivative Works; and
98 | If the Work includes a "NOTICE" text file as part of its distribution, then any
99 | Derivative Works that You distribute must include a readable copy of the
100 | attribution notices contained within such NOTICE file, excluding those notices
101 | that do not pertain to any part of the Derivative Works, in at least one of the
102 | following places: within a NOTICE text file distributed as part of the
103 | Derivative Works; within the Source form or documentation, if provided along
104 | with the Derivative Works; or, within a display generated by the Derivative
105 | Works, if and wherever such third-party notices normally appear. The contents of
106 | the NOTICE file are for informational purposes only and do not modify the
107 | License. You may add Your own attribution notices within Derivative Works that
108 | You distribute, alongside or as an addendum to the NOTICE text from the Work,
109 | provided that such additional attribution notices cannot be construed as
110 | modifying the License.
111 | You may add Your own copyright statement to Your modifications and may provide
112 | additional or different license terms and conditions for use, reproduction, or
113 | distribution of Your modifications, or for any such Derivative Works as a whole,
114 | provided Your use, reproduction, and distribution of the Work otherwise complies
115 | with the conditions stated in this License.
116 |
117 | 5. Submission of Contributions.
118 |
119 | Unless You explicitly state otherwise, any Contribution intentionally submitted
120 | for inclusion in the Work by You to the Licensor shall be under the terms and
121 | conditions of this License, without any additional terms or conditions.
122 | Notwithstanding the above, nothing herein shall supersede or modify the terms of
123 | any separate license agreement you may have executed with Licensor regarding
124 | such Contributions.
125 |
126 | 6. Trademarks.
127 |
128 | This License does not grant permission to use the trade names, trademarks,
129 | service marks, or product names of the Licensor, except as required for
130 | reasonable and customary use in describing the origin of the Work and
131 | reproducing the content of the NOTICE file.
132 |
133 | 7. Disclaimer of Warranty.
134 |
135 | Unless required by applicable law or agreed to in writing, Licensor provides the
136 | Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
137 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
138 | including, without limitation, any warranties or conditions of TITLE,
139 | NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
140 | solely responsible for determining the appropriateness of using or
141 | redistributing the Work and assume any risks associated with Your exercise of
142 | permissions under this License.
143 |
144 | 8. Limitation of Liability.
145 |
146 | In no event and under no legal theory, whether in tort (including negligence),
147 | contract, or otherwise, unless required by applicable law (such as deliberate
148 | and grossly negligent acts) or agreed to in writing, shall any Contributor be
149 | liable to You for damages, including any direct, indirect, special, incidental,
150 | or consequential damages of any character arising as a result of this License or
151 | out of the use or inability to use the Work (including but not limited to
152 | damages for loss of goodwill, work stoppage, computer failure or malfunction, or
153 | any and all other commercial damages or losses), even if such Contributor has
154 | been advised of the possibility of such damages.
155 |
156 | 9. Accepting Warranty or Additional Liability.
157 |
158 | While redistributing the Work or Derivative Works thereof, You may choose to
159 | offer, and charge a fee for, acceptance of support, warranty, indemnity, or
160 | other liability obligations and/or rights consistent with this License. However,
161 | in accepting such obligations, You may act only on Your own behalf and on Your
162 | sole responsibility, not on behalf of any other Contributor, and only if You
163 | agree to indemnify, defend, and hold each Contributor harmless for any liability
164 | incurred by, or claims asserted against, such Contributor by reason of your
165 | accepting any such warranty or additional liability.
166 |
167 | END OF TERMS AND CONDITIONS
168 |
169 | APPENDIX: How to apply the Apache License to your work
170 |
171 | To apply the Apache License to your work, attach the following boilerplate
172 | notice, with the fields enclosed by brackets "{}" replaced with your own
173 | identifying information. (Don't include the brackets!) The text should be
174 | enclosed in the appropriate comment syntax for the file format. We also
175 | recommend that a file or class name and description of purpose be included on
176 | the same "printed page" as the copyright notice for easier identification within
177 | third-party archives.
178 |
179 | Copyright 2015 mrytsr
180 |
181 | Licensed under the Apache License, Version 2.0 (the "License");
182 | you may not use this file except in compliance with the License.
183 | You may obtain a copy of the License at
184 |
185 | http://www.apache.org/licenses/LICENSE-2.0
186 |
187 | Unless required by applicable law or agreed to in writing, software
188 | distributed under the License is distributed on an "AS IS" BASIS,
189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
190 | See the License for the specific language governing permissions and
191 | limitations under the License.
192 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | 目录
4 | ===
5 |
6 |
7 | 点击展开目录菜单
8 |
9 |
10 |
11 | * [交流群](#交流群)
12 | * [QQ交流群](#qq交流群)
13 | * [Install (安装)](#install-安装)
14 | * [Uninstall (卸载)](#uninstall-卸载)
15 | * [Upgrade Plugins (升级插件)](#upgrade-plugins-升级插件)
16 | * [Features (特色功能)](#features-特色功能)
17 | * [vim-plug](#vim-plug)
18 | * [Statusline (状态栏)](#statusline-状态栏)
19 | * [Backup (备份路径)](#backup-备份路径)
20 | * [Template (模板)](#template-模板)
21 | * [什么是Vim,为什么要使用Vim](#什么是vim为什么要使用vim)
22 | * [历史](#历史)
23 | * [帮助文档](#帮助文档)
24 | * [Vim的五种模式](#vim的五种模式)
25 | * [Vim的哲学](#vim的哲学)
26 | * [Vim基本操作](#vim基本操作)
27 | * [打开/切换文件](#打开切换文件)
28 | * [退出/保存](#退出保存)
29 | * [编辑](#编辑)
30 | * [删除](#删除)
31 | * [移动](#移动)
32 | * [复制/粘贴](#复制粘贴)
33 | * [搜索](#搜索)
34 | * [Vim进阶](#vim进阶)
35 | * [代码补全](#代码补全)
36 | * [拖动功能](#拖动功能)
37 | * [设置编码和格式](#设置编码和格式)
38 | * [代码折叠](#代码折叠)
39 | * [分割窗口](#分割窗口)
40 | * [宏](#宏)
41 | * [Vim插件](#vim插件)
42 | * [必装插件](#必装插件)
43 | * [NERDTree操作命令](#nerdtree操作命令)
44 | * [vim-commentary操作命令](#vim-commentary操作命令)
45 | * [资源](#资源)
46 | * [Vim资源](#vim资源)
47 | * [其他](#其他)
48 |
49 |
50 |
51 |
52 | ## 交流群
53 |
54 | ### QQ交流群
55 |
56 | 群号:1019096841
57 | 扫码进群:
58 | 
59 |
60 | ## Install (安装)
61 |
62 | 如果您之前有定制自己的`Vim`配置,**请先备份`~/.vim`目录**,以下操作会覆盖`~/.vim`目录
63 | ```bash
64 | wget http://upos-sz-staticcos.bilivideo.com/appstaticboss/vim-vide-20200812.tgz && tar xvf ./vim-vide-20200812.tgz -C ~
65 | ```
66 |
67 | ## Uninstall (卸载)
68 |
69 | 如需恢复您原来的自定义配置,执行以下命令后,再将您原来的`.vim`目录还原至根目录即可
70 | ```bash
71 | rm -rf ~/.vim
72 | rm -rf ~/.vimtmp
73 | ```
74 |
75 | ## Upgrade Plugins (升级插件)
76 |
77 | ```bash
78 | vim +PlugClean[!] +PlugUpdate +qa
79 | ```
80 |
81 | ## Features (特色功能)
82 |
83 | ### vim-plug
84 |
85 | Base on vim-plug, A minimalist Vim plugin manager.
86 | 新一代的Vim配置管理组件,体积最小,plugit用了并行下载,速度较顺序下载的vundle快很多!!!
87 |
88 | https://github.com/junegunn/vim-plug
89 |
90 | ### Statusline (状态栏)
91 |
92 | Origin Vim config statusline, much faster than airline or powerline!
93 | (Vim原生配置实现的状态栏,信息很全,比airline或者powerline快数倍!!!)
94 |
95 | ```bash
96 | (Vide) README.md[+] /home/tjx/vide [unix:utf-8:MARKDOWN] 18,5 50%
97 | +----+ +----------+ +------------+ +-------------------+ +--+ +-+
98 | 1 2 3 4 5 6
99 | ```
100 |
101 | 1. Logo
102 | 1. Relative path (相对路径)
103 | 1. Absolute path of directory (绝对路径)
104 | 1. Format : file-encoding : filetype (文件类型)
105 | 1. Row-position, col position (行号,列号)
106 | 1. Percent of rows (当前行在总行数中的百分比)
107 |
108 | ### Backup (备份路径)
109 |
110 | - 撤销文件夹 ~/.vimtmp/undodir
111 | - 备份文件夹 ~/.vimtmp/backupdir
112 | - 交换文件夹 ~/.vimtmp/directory .swp files
113 |
114 | ### Template (模板)
115 |
116 | - call system('bash ~/.vim/lua.template > /tmp/lua.template')
117 | - call system('bash ~/.vim/php.template > /tmp/php.template')
118 |
119 | ## 什么是Vim,为什么要使用Vim
120 |
121 | ### 历史
122 |
123 | ```bash
124 | ed -> ex(sed) -> Vi -> Vim -> gVim / macvim
125 | ```
126 |
127 | ### 帮助文档
128 |
129 | - `vimtutor`
130 |
131 | 命令行直接运行,非Vim编辑器内命令
132 |
133 | - `:h`
134 |
135 | Vim内部命令
136 |
137 | ### Vim的五种模式
138 |
139 | 1. 插入模式(和普通编辑器的模式差不多)
140 |
141 | 2. 普通模式
142 |
143 | 3. 可视模式
144 |
145 | 4. 命令行模式(扩展Vim的功能,Vim的精髓所在)
146 |
147 | 5. 选择模式(不常用)
148 |
149 | ### Vim的哲学
150 |
151 | ```bash
152 | 执行次数 + 操作 + 范围
153 | 次数 1,2,3
154 | 操作 v d c
155 | 范围 a i w p $ " ' { ( [ t
156 |
157 | ni hao tang jun xin
158 | $('nihao')
159 | kjkjk
160 | ```
161 |
162 | ## Vim基本操作
163 |
164 | ### 打开/切换文件
165 |
166 | | 命令 | 功能 |
167 | | ------------------------------- | ------------------------------------------------------------ |
168 | | `:e file_name` 、`:o file_name` | 打开文件 |
169 | | `:args file_name` | 如果使用`vim file1 file2 [filen]`命令打开多个文件,就可以使用`:args file_name`命令在打开的文件之间切换 |
170 | | `:buffers`、`:ls`、`files` | 查看缓冲区中的文件列表 |
171 | | `:bnext` | 切换到下一个缓冲区文件 |
172 | | `:bprevious`、`bpre` | 切换到上一个缓冲区文件 |
173 | | `:bfirst` | 切换到第一个缓冲区文件 |
174 | | `:blast` | 切换到最后一个缓冲区文件 |
175 | | `:bdelete file_name` | 删除缓冲区文件 |
176 | | `:badd file_name` | 添加文件到缓冲区 |
177 |
178 | ### 退出/保存
179 |
180 | | 命令 | 功能 |
181 | | ----------- | ---------------------------------------------- |
182 | | `:w` | 保存 |
183 | | `:w!` | 强制保存,不退出Vim |
184 | | `:q` | 退出 |
185 | | `:q!` | 强制退出不保存 |
186 | | `:wq`、`ZZ` | 保存并退出 |
187 | | `:wq!` | 强制保存,并退出 |
188 | | `:w file` | 将修改另存到file中,不退出Vim |
189 | | `:e!` | 放弃所有修改,从上次保存文件开始再编辑命令历史 |
190 |
191 | ### 编辑
192 |
193 | | 命令 | 功能 |
194 | | --------------- | ---------------------------------------------------------- |
195 | | `i`/`I` | 编辑 |
196 | | `esc` | 退出编辑模式 |
197 | | `u` | 撤销操作 |
198 | | ` + r ` | 重做(恢复被撤销的动作) |
199 | | `cc` 、`C`、`S` | 清空当前行并进入编辑模式 |
200 | | `s` | 删除当前字符并进入编辑模式 |
201 | | `r` | 替换当前字符(替换后不进入编辑模式) |
202 | | `R` | 持续替换字符(不进入编辑模式),替换一个光标自动移到下一个 |
203 | | `=-` | 格式化当前行代码 |
204 | | `gg=G` | 格式化所有代码 |
205 | | ` + v` | 可视化多选 |
206 | | `:set mouse=a` | 设置鼠标可区域选择,跟普通的编辑器一样可以进行拖选 |
207 |
208 | ### 删除
209 |
210 | | 命令 | 功能 |
211 | | ----- | ------------------------------------------- |
212 | | `dd` | 删除当前行 |
213 | | `ndd` | 删除包含当前行的n行数据(从当前行往下删除) |
214 | | `dG` | 删除包含当前行及之后的全部行 |
215 |
216 | ### 移动
217 |
218 | | 命令 | 功能 |
219 | | ---------- | -------------------- |
220 | | `gg` | 跳转到首行 |
221 | | `G` | 跳转到尾行 |
222 | | `:n`、`nG` | 跳转到第n行 |
223 | | `o` | 在下一行插入 |
224 | | `O` | 在上一行插入 |
225 | | `w`、`W` | 移动到下一个单词开头 |
226 | | `b`、`B` | 移动到上一个单词开头 |
227 | | `e`、`E` | 移动到下一个单词结尾 |
228 | | `{` | 下一段落 |
229 | | `}` | 上一段落 |
230 | | `M` | 跳转到文件内容的中部 |
231 | | `H` | 跳转到文件内容的顶部 |
232 | | `L` | 跳转到文件内容的底部 |
233 |
234 | ### 复制/粘贴
235 |
236 | | 命令 | 功能 |
237 | | -------- | ---------- |
238 | | `y` | 复制 |
239 | | `p` | 粘贴到下部 |
240 | | `P` | 粘贴到上部 |
241 | | `x`、`X` | 剪切 |
242 |
243 | ### 搜索
244 |
245 | | 命令 | 功能 |
246 | | ------------------------ | --------------------------------- |
247 | | `f` / `F` | 当前行搜索,til,正向 / 反向 |
248 | | `t` / `T` | 当前行搜索,until,正向 / 反向 |
249 | | `;`、`,` | 重复当前行搜索 |
250 | | `/`、`?` | 当前文件搜索,向上搜索 / 向下搜索 |
251 | | `:grep -r` / `:!grep -r` | 跨文件搜索 |
252 | | `n` | 下一个匹配内容 |
253 | | `N` | 上一个匹配内容 |
254 | | `ctrl-L` | 清除搜索高亮 |
255 |
256 | ## Vim进阶
257 |
258 | ### 代码补全
259 |
260 | | 命令 | 代码 |
261 | | ------------- | ------------ |
262 | | ` + p ` | 往前搜索补全 |
263 | | ` + n ` | 往后搜索补全 |
264 | | ` + e ` | 取消补全 |
265 | | ` + y ` | 确定补全 |
266 |
267 | ### 拖动功能
268 |
269 | | 命令 | 功能 |
270 | | ---- | ---------------------- |
271 | | `zz` | 将当前行定位到屏幕中间 |
272 | | `zb` | 将当前行定位到屏幕底部 |
273 | | `zt` | 将当前行定位到屏幕顶部 |
274 |
275 | ### 设置编码和格式
276 |
277 | | 命令 | 功能 |
278 | | ------------------------------ | ---------------------------------------------- |
279 | | `:set fileformat unix dos mas` | 让换行符自由切换 |
280 | | `:set fileencodings` | 检测打开文档编码的顺序,一般设置为utf-8、cp936 |
281 | | `:set fileencoding` | 保存文档的编码,一般为utf-8 |
282 | | `:set encoding` | Vim本身界面的编码,一般和文档无关 |
283 | | `\3` | `NERDTree-Find` |
284 | | `\a` | `:set filetype=awk` |
285 | | `\c` | `:set filetype=css` |
286 | | `\h` | `:set filetype=html` |
287 | | `\j` | `:set filetype=javascript` |
288 | | `\l` | `:set filetype=lua` |
289 | | `\m` | `:set filetype=markdown` |
290 | | `\p` | `:set filetype=php` |
291 | | `\s` | `:set filetype=sh` |
292 | | `\v` | `:set filetype=vim` |
293 | | `\y` | `:set filetype=python` |
294 |
295 | ### 代码折叠
296 |
297 | | 命令 | 功能 |
298 | | ----------------------- | -------------- |
299 | | `zf` | 创建折叠 |
300 | | `zo` | 打开折叠 |
301 | | `zc` | 关闭折叠 |
302 | | `:mkview` / `:loadview` | 保存,载入绘画 |
303 |
304 | ### 分割窗口
305 |
306 | | 命令 | 功能 |
307 | | ---------------------------------------- | -------------------- |
308 | | `:split [file_name]` 、`:sp [file_name]` | 水平分割 |
309 | | `:vsplit [file_name]`、`:vs [file_name]` | 垂直分割 |
310 | | ` + w + h` | 将焦点移动到左边窗口 |
311 | | ` + w + j` | 将焦点移动到下方窗口 |
312 | | ` + w + k` | 将焦点移动到上方窗口 |
313 | | ` + w + l` | 将焦点移动到右边窗口 |
314 |
315 | ### 宏
316 |
317 | | 命令 | 功能 |
318 | | ---- | ------- |
319 | | `qa` | 录制到a |
320 | | `@a` | 播放a |
321 |
322 | ## Vim插件
323 |
324 | ### 必装插件
325 |
326 | | 名称 | 功能 |
327 | | -------------- | -------------------- |
328 | | vim-phpmanual | php文档,``查询 |
329 | | syntastic | 语法检查 |
330 | | ctrlp.vim | 文件跳转 |
331 | | nerdtree | 浏览文件 |
332 | | vim-gitgutter | 观察git状态 |
333 | | vim-commentary | 强大的注释插件 |
334 |
335 | ### NERDTree操作命令
336 |
337 | | 命令 | 功能 |
338 | | ---- | ---------------------------------------- |
339 | | `o` | 打开/关闭文件或目录 |
340 | | `t` | 在新标签页中打开 |
341 | | `T` | 在后台标签页打开 |
342 | | `!` | 执行此文件 |
343 | | `p` | 到上层目录 |
344 | | `P` | 到根目录 |
345 | | `K` | 到第一个节点 |
346 | | `J` | 到最后一个节点 |
347 | | `u` | 打开上层目录 |
348 | | `m` | 显示文件系统菜单(添加、删除、移动操作) |
349 | | `?` | 帮助,再按一下关闭 |
350 | | `q` | 关闭 |
351 | | `\3` | `NERDTree-Find` |
352 |
353 | ### vim-commentary操作命令
354 |
355 | | 快捷键 | 功能 |
356 | | ------ | ---------------------------------- |
357 | | `gcc` | 注释当前行(普通模式下) |
358 | | `gc` | 注释当前选中内容(可视多选模式下) |
359 | | `gcap` | 注释当前所在段落 |
360 | | `gcu` | 注释上一次注释的部分 |
361 | | `gcgc` | 取消一组相邻的注释 |
362 |
363 | ## 资源
364 |
365 | ### Vim资源
366 |
367 | - [Vimbits](http://www.vimbits.com/)
368 | - [简明 Vim 练级攻略 | 酷 壳 - CoolShell.cn](http://coolshell.cn/articles/5426.html)
369 | - [[翻译]130+vim基本命令](http://wklken.me/posts/2013/08/17/130-essential-vim-commands.html#stq=&stp=0)
370 | - [Vimer的程序世界 | 一个vim使用者的程序世界](http://www.vimer.cn/)
371 | - [Vim实用技巧 (豆瓣)](https://book.douban.com/subject/25869486/)
372 | - [welcome home : vim online](http://www.vim.org/)
373 | - [Vim | 易水博客](http://easwy.com/blog/archives/tag/vim/)
374 | - [Vimcasts - Free screencasts about the text editor Vim](http://vimcasts.org/)
375 | - [VimGolf - real Vim ninjas count every keystroke!](http://vimgolf.com/)
376 | - [Vim Awesome](http://vimawesome.com/)
377 |
378 | ### 其他
379 |
380 | - [用DBGPavim在Vim中调试PHP/Python程序](https://brookhong.github.io/2014/09/27/dbgpavim-cn.html)
381 | - [Cscope的使用(领略Vim + Cscope的强大魅力) - 面码的个人空间 - 开源中国社区](http://my.oschina.net/u/572632/blog/267471)
382 | - [VundleVim/Vundle.vim](https://github.com/VundleVim/Vundle.vim)
383 | - [Using tab pages - Vim Tips Wiki - Wikia](http://vim.wikia.com/wiki/Using_tab_pages)
384 | - powerline / airline
385 |
--------------------------------------------------------------------------------
/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') || has('win64')
100 | let s:nvim = has('nvim-0.2') || (has('nvim') && exists('*jobwait') && !s:is_win)
101 | let s:vim8 = has('patch-8.0.0039') && exists('*job_start')
102 | let s:me = resolve(expand(':p'))
103 | let s:base_spec = { 'branch': 'master', 'frozen': 0 }
104 | let s:TYPE = {
105 | \ 'string': type(''),
106 | \ 'list': type([]),
107 | \ 'dict': type({}),
108 | \ 'funcref': type(function('call'))
109 | \ }
110 | let s:loaded = get(s:, 'loaded', {})
111 | let s:triggers = get(s:, 'triggers', {})
112 |
113 | function! plug#begin(...)
114 | if a:0 > 0
115 | let s:plug_home_org = a:1
116 | let home = s:path(fnamemodify(expand(a:1), ':p'))
117 | elseif exists('g:plug_home')
118 | let home = s:path(g:plug_home)
119 | elseif !empty(&rtp)
120 | let home = s:path(split(&rtp, ',')[0]) . '/plugged'
121 | else
122 | return s:err('Unable to determine plug home. Try calling plug#begin() with a path argument.')
123 | endif
124 | if fnamemodify(home, ':t') ==# 'plugin' && fnamemodify(home, ':h') ==# s:first_rtp
125 | return s:err('Invalid plug home. '.home.' is a standard Vim runtime path and is not allowed.')
126 | endif
127 |
128 | let g:plug_home = home
129 | let g:plugs = {}
130 | let g:plugs_order = []
131 | let s:triggers = {}
132 |
133 | call s:define_commands()
134 | return 1
135 | endfunction
136 |
137 | function! s:define_commands()
138 | command! -nargs=+ -bar Plug call plug#()
139 | if !executable('git')
140 | return s:err('`git` executable not found. Most commands will not be available. To suppress this message, prepend `silent!` to `call plug#begin(...)`.')
141 | endif
142 | command! -nargs=* -bar -bang -complete=customlist,s:names PlugInstall call s:install(0, [])
143 | command! -nargs=* -bar -bang -complete=customlist,s:names PlugUpdate call s:update(0, [])
144 | command! -nargs=0 -bar -bang PlugClean call s:clean(0)
145 | command! -nargs=0 -bar PlugUpgrade if s:upgrade() | execute 'source' s:esc(s:me) | endif
146 | command! -nargs=0 -bar PlugStatus call s:status()
147 | command! -nargs=0 -bar PlugDiff call s:diff()
148 | command! -nargs=? -bar -bang -complete=file PlugSnapshot call s:snapshot(0, )
149 | endfunction
150 |
151 | function! s:to_a(v)
152 | return type(a:v) == s:TYPE.list ? a:v : [a:v]
153 | endfunction
154 |
155 | function! s:to_s(v)
156 | return type(a:v) == s:TYPE.string ? a:v : join(a:v, "\n") . "\n"
157 | endfunction
158 |
159 | function! s:glob(from, pattern)
160 | return s:lines(globpath(a:from, a:pattern))
161 | endfunction
162 |
163 | function! s:source(from, ...)
164 | let found = 0
165 | for pattern in a:000
166 | for vim in s:glob(a:from, pattern)
167 | execute 'source' s:esc(vim)
168 | let found = 1
169 | endfor
170 | endfor
171 | return found
172 | endfunction
173 |
174 | function! s:assoc(dict, key, val)
175 | let a:dict[a:key] = add(get(a:dict, a:key, []), a:val)
176 | endfunction
177 |
178 | function! s:ask(message, ...)
179 | call inputsave()
180 | echohl WarningMsg
181 | let answer = input(a:message.(a:0 ? ' (y/N/a) ' : ' (y/N) '))
182 | echohl None
183 | call inputrestore()
184 | echo "\r"
185 | return (a:0 && answer =~? '^a') ? 2 : (answer =~? '^y') ? 1 : 0
186 | endfunction
187 |
188 | function! s:ask_no_interrupt(...)
189 | try
190 | return call('s:ask', a:000)
191 | catch
192 | return 0
193 | endtry
194 | endfunction
195 |
196 | function! plug#end()
197 | if !exists('g:plugs')
198 | return s:err('Call plug#begin() first')
199 | endif
200 |
201 | if exists('#PlugLOD')
202 | augroup PlugLOD
203 | autocmd!
204 | augroup END
205 | augroup! PlugLOD
206 | endif
207 | let lod = { 'ft': {}, 'map': {}, 'cmd': {} }
208 |
209 | if exists('g:did_load_filetypes')
210 | filetype off
211 | endif
212 | for name in g:plugs_order
213 | if !has_key(g:plugs, name)
214 | continue
215 | endif
216 | let plug = g:plugs[name]
217 | if get(s:loaded, name, 0) || !has_key(plug, 'on') && !has_key(plug, 'for')
218 | let s:loaded[name] = 1
219 | continue
220 | endif
221 |
222 | if has_key(plug, 'on')
223 | let s:triggers[name] = { 'map': [], 'cmd': [] }
224 | for cmd in s:to_a(plug.on)
225 | if cmd =~? '^.\+'
226 | if empty(mapcheck(cmd)) && empty(mapcheck(cmd, 'i'))
227 | call s:assoc(lod.map, cmd, name)
228 | endif
229 | call add(s:triggers[name].map, cmd)
230 | elseif cmd =~# '^[A-Z]'
231 | let cmd = substitute(cmd, '!*$', '', '')
232 | if exists(':'.cmd) != 2
233 | call s:assoc(lod.cmd, cmd, name)
234 | endif
235 | call add(s:triggers[name].cmd, cmd)
236 | else
237 | call s:err('Invalid `on` option: '.cmd.
238 | \ '. Should start with an uppercase letter or ``.')
239 | endif
240 | endfor
241 | endif
242 |
243 | if has_key(plug, 'for')
244 | let types = s:to_a(plug.for)
245 | if !empty(types)
246 | augroup filetypedetect
247 | call s:source(s:rtp(plug), 'ftdetect/**/*.vim', 'after/ftdetect/**/*.vim')
248 | augroup END
249 | endif
250 | for type in types
251 | call s:assoc(lod.ft, type, name)
252 | endfor
253 | endif
254 | endfor
255 |
256 | for [cmd, names] in items(lod.cmd)
257 | execute printf(
258 | \ 'command! -nargs=* -range -bang -complete=file %s call s:lod_cmd(%s, "", , , , %s)',
259 | \ cmd, string(cmd), string(names))
260 | endfor
261 |
262 | for [map, names] in items(lod.map)
263 | for [mode, map_prefix, key_prefix] in
264 | \ [['i', '', ''], ['n', '', ''], ['v', '', 'gv'], ['o', '', '']]
265 | execute printf(
266 | \ '%snoremap %s %s:call lod_map(%s, %s, %s, "%s")',
267 | \ mode, map, map_prefix, string(map), string(names), mode != 'i', key_prefix)
268 | endfor
269 | endfor
270 |
271 | for [ft, names] in items(lod.ft)
272 | augroup PlugLOD
273 | execute printf('autocmd FileType %s call lod_ft(%s, %s)',
274 | \ ft, string(ft), string(names))
275 | augroup END
276 | endfor
277 |
278 | call s:reorg_rtp()
279 | filetype plugin indent on
280 | if has('vim_starting')
281 | if has('syntax') && !exists('g:syntax_on')
282 | syntax enable
283 | end
284 | else
285 | call s:reload_plugins()
286 | endif
287 | endfunction
288 |
289 | function! s:loaded_names()
290 | return filter(copy(g:plugs_order), 'get(s:loaded, v:val, 0)')
291 | endfunction
292 |
293 | function! s:load_plugin(spec)
294 | call s:source(s:rtp(a:spec), 'plugin/**/*.vim', 'after/plugin/**/*.vim')
295 | endfunction
296 |
297 | function! s:reload_plugins()
298 | for name in s:loaded_names()
299 | call s:load_plugin(g:plugs[name])
300 | endfor
301 | endfunction
302 |
303 | function! s:trim(str)
304 | return substitute(a:str, '[\/]\+$', '', '')
305 | endfunction
306 |
307 | function! s:version_requirement(val, min)
308 | for idx in range(0, len(a:min) - 1)
309 | let v = get(a:val, idx, 0)
310 | if v < a:min[idx] | return 0
311 | elseif v > a:min[idx] | return 1
312 | endif
313 | endfor
314 | return 1
315 | endfunction
316 |
317 | function! s:git_version_requirement(...)
318 | if !exists('s:git_version')
319 | let s:git_version = map(split(split(s:system('git --version'))[2], '\.'), 'str2nr(v:val)')
320 | endif
321 | return s:version_requirement(s:git_version, a:000)
322 | endfunction
323 |
324 | function! s:progress_opt(base)
325 | return a:base && !s:is_win &&
326 | \ s:git_version_requirement(1, 7, 1) ? '--progress' : ''
327 | endfunction
328 |
329 | if s:is_win
330 | function! s:rtp(spec)
331 | return s:path(a:spec.dir . get(a:spec, 'rtp', ''))
332 | endfunction
333 |
334 | function! s:path(path)
335 | return s:trim(substitute(a:path, '/', '\', 'g'))
336 | endfunction
337 |
338 | function! s:dirpath(path)
339 | return s:path(a:path) . '\'
340 | endfunction
341 |
342 | function! s:is_local_plug(repo)
343 | return a:repo =~? '^[a-z]:\|^[%~]'
344 | endfunction
345 | else
346 | function! s:rtp(spec)
347 | return s:dirpath(a:spec.dir . get(a:spec, 'rtp', ''))
348 | endfunction
349 |
350 | function! s:path(path)
351 | return s:trim(a:path)
352 | endfunction
353 |
354 | function! s:dirpath(path)
355 | return substitute(a:path, '[/\\]*$', '/', '')
356 | endfunction
357 |
358 | function! s:is_local_plug(repo)
359 | return a:repo[0] =~ '[/$~]'
360 | endfunction
361 | endif
362 |
363 | function! s:err(msg)
364 | echohl ErrorMsg
365 | echom '[vim-plug] '.a:msg
366 | echohl None
367 | endfunction
368 |
369 | function! s:warn(cmd, msg)
370 | echohl WarningMsg
371 | execute a:cmd 'a:msg'
372 | echohl None
373 | endfunction
374 |
375 | function! s:esc(path)
376 | return escape(a:path, ' ')
377 | endfunction
378 |
379 | function! s:escrtp(path)
380 | return escape(a:path, ' ,')
381 | endfunction
382 |
383 | function! s:remove_rtp()
384 | for name in s:loaded_names()
385 | let rtp = s:rtp(g:plugs[name])
386 | execute 'set rtp-='.s:escrtp(rtp)
387 | let after = globpath(rtp, 'after')
388 | if isdirectory(after)
389 | execute 'set rtp-='.s:escrtp(after)
390 | endif
391 | endfor
392 | endfunction
393 |
394 | function! s:reorg_rtp()
395 | if !empty(s:first_rtp)
396 | execute 'set rtp-='.s:first_rtp
397 | execute 'set rtp-='.s:last_rtp
398 | endif
399 |
400 | " &rtp is modified from outside
401 | if exists('s:prtp') && s:prtp !=# &rtp
402 | call s:remove_rtp()
403 | unlet! s:middle
404 | endif
405 |
406 | let s:middle = get(s:, 'middle', &rtp)
407 | let rtps = map(s:loaded_names(), 's:rtp(g:plugs[v:val])')
408 | let afters = filter(map(copy(rtps), 'globpath(v:val, "after")'), '!empty(v:val)')
409 | let rtp = join(map(rtps, 'escape(v:val, ",")'), ',')
410 | \ . ','.s:middle.','
411 | \ . join(map(afters, 'escape(v:val, ",")'), ',')
412 | let &rtp = substitute(substitute(rtp, ',,*', ',', 'g'), '^,\|,$', '', 'g')
413 | let s:prtp = &rtp
414 |
415 | if !empty(s:first_rtp)
416 | execute 'set rtp^='.s:first_rtp
417 | execute 'set rtp+='.s:last_rtp
418 | endif
419 | endfunction
420 |
421 | function! s:doautocmd(...)
422 | if exists('#'.join(a:000, '#'))
423 | execute 'doautocmd' ((v:version > 703 || has('patch442')) ? '' : '') join(a:000)
424 | endif
425 | endfunction
426 |
427 | function! s:dobufread(names)
428 | for name in a:names
429 | let path = s:rtp(g:plugs[name]).'/**'
430 | for dir in ['ftdetect', 'ftplugin']
431 | if len(finddir(dir, path))
432 | if exists('#BufRead')
433 | doautocmd BufRead
434 | endif
435 | return
436 | endif
437 | endfor
438 | endfor
439 | endfunction
440 |
441 | function! plug#load(...)
442 | if a:0 == 0
443 | return s:err('Argument missing: plugin name(s) required')
444 | endif
445 | if !exists('g:plugs')
446 | return s:err('plug#begin was not called')
447 | endif
448 | let names = a:0 == 1 && type(a:1) == s:TYPE.list ? a:1 : a:000
449 | let unknowns = filter(copy(names), '!has_key(g:plugs, v:val)')
450 | if !empty(unknowns)
451 | let s = len(unknowns) > 1 ? 's' : ''
452 | return s:err(printf('Unknown plugin%s: %s', s, join(unknowns, ', ')))
453 | end
454 | let unloaded = filter(copy(names), '!get(s:loaded, v:val, 0)')
455 | if !empty(unloaded)
456 | for name in unloaded
457 | call s:lod([name], ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin'])
458 | endfor
459 | call s:dobufread(unloaded)
460 | return 1
461 | end
462 | return 0
463 | endfunction
464 |
465 | function! s:remove_triggers(name)
466 | if !has_key(s:triggers, a:name)
467 | return
468 | endif
469 | for cmd in s:triggers[a:name].cmd
470 | execute 'silent! delc' cmd
471 | endfor
472 | for map in s:triggers[a:name].map
473 | execute 'silent! unmap' map
474 | execute 'silent! iunmap' map
475 | endfor
476 | call remove(s:triggers, a:name)
477 | endfunction
478 |
479 | function! s:lod(names, types, ...)
480 | for name in a:names
481 | call s:remove_triggers(name)
482 | let s:loaded[name] = 1
483 | endfor
484 | call s:reorg_rtp()
485 |
486 | for name in a:names
487 | let rtp = s:rtp(g:plugs[name])
488 | for dir in a:types
489 | call s:source(rtp, dir.'/**/*.vim')
490 | endfor
491 | if a:0
492 | if !s:source(rtp, a:1) && !empty(s:glob(rtp, a:2))
493 | execute 'runtime' a:1
494 | endif
495 | call s:source(rtp, a:2)
496 | endif
497 | call s:doautocmd('User', name)
498 | endfor
499 | endfunction
500 |
501 | function! s:lod_ft(pat, names)
502 | let syn = 'syntax/'.a:pat.'.vim'
503 | call s:lod(a:names, ['plugin', 'after/plugin'], syn, 'after/'.syn)
504 | execute 'autocmd! PlugLOD FileType' a:pat
505 | call s:doautocmd('filetypeplugin', 'FileType')
506 | call s:doautocmd('filetypeindent', 'FileType')
507 | endfunction
508 |
509 | function! s:lod_cmd(cmd, bang, l1, l2, args, names)
510 | call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin'])
511 | call s:dobufread(a:names)
512 | execute printf('%s%s%s %s', (a:l1 == a:l2 ? '' : (a:l1.','.a:l2)), a:cmd, a:bang, a:args)
513 | endfunction
514 |
515 | function! s:lod_map(map, names, with_prefix, prefix)
516 | call s:lod(a:names, ['ftdetect', 'after/ftdetect', 'plugin', 'after/plugin'])
517 | call s:dobufread(a:names)
518 | let extra = ''
519 | while 1
520 | let c = getchar(0)
521 | if c == 0
522 | break
523 | endif
524 | let extra .= nr2char(c)
525 | endwhile
526 |
527 | if a:with_prefix
528 | let prefix = v:count ? v:count : ''
529 | let prefix .= '"'.v:register.a:prefix
530 | if mode(1) == 'no'
531 | if v:operator == 'c'
532 | let prefix = "\" . prefix
533 | endif
534 | let prefix .= v:operator
535 | endif
536 | call feedkeys(prefix, 'n')
537 | endif
538 | call feedkeys(substitute(a:map, '^', "\", '') . extra)
539 | endfunction
540 |
541 | function! plug#(repo, ...)
542 | if a:0 > 1
543 | return s:err('Invalid number of arguments (1..2)')
544 | endif
545 |
546 | try
547 | let repo = s:trim(a:repo)
548 | let opts = a:0 == 1 ? s:parse_options(a:1) : s:base_spec
549 | let name = get(opts, 'as', fnamemodify(repo, ':t:s?\.git$??'))
550 | let spec = extend(s:infer_properties(name, repo), opts)
551 | if !has_key(g:plugs, name)
552 | call add(g:plugs_order, name)
553 | endif
554 | let g:plugs[name] = spec
555 | let s:loaded[name] = get(s:loaded, name, 0)
556 | catch
557 | return s:err(v:exception)
558 | endtry
559 | endfunction
560 |
561 | function! s:parse_options(arg)
562 | let opts = copy(s:base_spec)
563 | let type = type(a:arg)
564 | if type == s:TYPE.string
565 | let opts.tag = a:arg
566 | elseif type == s:TYPE.dict
567 | call extend(opts, a:arg)
568 | if has_key(opts, 'dir')
569 | let opts.dir = s:dirpath(expand(opts.dir))
570 | endif
571 | else
572 | throw 'Invalid argument type (expected: string or dictionary)'
573 | endif
574 | return opts
575 | endfunction
576 |
577 | function! s:infer_properties(name, repo)
578 | let repo = a:repo
579 | if s:is_local_plug(repo)
580 | return { 'dir': s:dirpath(expand(repo)) }
581 | else
582 | if repo =~ ':'
583 | let uri = repo
584 | else
585 | if repo !~ '/'
586 | throw printf('Invalid argument: %s (implicit `vim-scripts'' expansion is deprecated)', repo)
587 | endif
588 | let fmt = get(g:, 'plug_url_format', 'https://git::@github.com/%s.git')
589 | let uri = printf(fmt, repo)
590 | endif
591 | return { 'dir': s:dirpath(g:plug_home.'/'.a:name), 'uri': uri }
592 | endif
593 | endfunction
594 |
595 | function! s:install(force, names)
596 | call s:update_impl(0, a:force, a:names)
597 | endfunction
598 |
599 | function! s:update(force, names)
600 | call s:update_impl(1, a:force, a:names)
601 | endfunction
602 |
603 | function! plug#helptags()
604 | if !exists('g:plugs')
605 | return s:err('plug#begin was not called')
606 | endif
607 | for spec in values(g:plugs)
608 | let docd = join([s:rtp(spec), 'doc'], '/')
609 | if isdirectory(docd)
610 | silent! execute 'helptags' s:esc(docd)
611 | endif
612 | endfor
613 | return 1
614 | endfunction
615 |
616 | function! s:syntax()
617 | syntax clear
618 | syntax region plug1 start=/\%1l/ end=/\%2l/ contains=plugNumber
619 | syntax region plug2 start=/\%2l/ end=/\%3l/ contains=plugBracket,plugX
620 | syn match plugNumber /[0-9]\+[0-9.]*/ contained
621 | syn match plugBracket /[[\]]/ contained
622 | syn match plugX /x/ contained
623 | syn match plugDash /^-/
624 | syn match plugPlus /^+/
625 | syn match plugStar /^*/
626 | syn match plugMessage /\(^- \)\@<=.*/
627 | syn match plugName /\(^- \)\@<=[^ ]*:/
628 | syn match plugSha /\%(: \)\@<=[0-9a-f]\{4,}$/
629 | syn match plugTag /(tag: [^)]\+)/
630 | syn match plugInstall /\(^+ \)\@<=[^:]*/
631 | syn match plugUpdate /\(^* \)\@<=[^:]*/
632 | syn match plugCommit /^ \X*[0-9a-f]\{7,9} .*/ contains=plugRelDate,plugEdge,plugTag
633 | syn match plugEdge /^ \X\+$/
634 | syn match plugEdge /^ \X*/ contained nextgroup=plugSha
635 | syn match plugSha /[0-9a-f]\{7,9}/ contained
636 | syn match plugRelDate /([^)]*)$/ contained
637 | syn match plugNotLoaded /(not loaded)$/
638 | syn match plugError /^x.*/
639 | syn region plugDeleted start=/^\~ .*/ end=/^\ze\S/
640 | syn match plugH2 /^.*:\n-\+$/
641 | syn keyword Function PlugInstall PlugStatus PlugUpdate PlugClean
642 | hi def link plug1 Title
643 | hi def link plug2 Repeat
644 | hi def link plugH2 Type
645 | hi def link plugX Exception
646 | hi def link plugBracket Structure
647 | hi def link plugNumber Number
648 |
649 | hi def link plugDash Special
650 | hi def link plugPlus Constant
651 | hi def link plugStar Boolean
652 |
653 | hi def link plugMessage Function
654 | hi def link plugName Label
655 | hi def link plugInstall Function
656 | hi def link plugUpdate Type
657 |
658 | hi def link plugError Error
659 | hi def link plugDeleted Ignore
660 | hi def link plugRelDate Comment
661 | hi def link plugEdge PreProc
662 | hi def link plugSha Identifier
663 | hi def link plugTag Constant
664 |
665 | hi def link plugNotLoaded Comment
666 | endfunction
667 |
668 | function! s:lpad(str, len)
669 | return a:str . repeat(' ', a:len - len(a:str))
670 | endfunction
671 |
672 | function! s:lines(msg)
673 | return split(a:msg, "[\r\n]")
674 | endfunction
675 |
676 | function! s:lastline(msg)
677 | return get(s:lines(a:msg), -1, '')
678 | endfunction
679 |
680 | function! s:new_window()
681 | execute get(g:, 'plug_window', 'vertical topleft new')
682 | endfunction
683 |
684 | function! s:plug_window_exists()
685 | let buflist = tabpagebuflist(s:plug_tab)
686 | return !empty(buflist) && index(buflist, s:plug_buf) >= 0
687 | endfunction
688 |
689 | function! s:switch_in()
690 | if !s:plug_window_exists()
691 | return 0
692 | endif
693 |
694 | if winbufnr(0) != s:plug_buf
695 | let s:pos = [tabpagenr(), winnr(), winsaveview()]
696 | execute 'normal!' s:plug_tab.'gt'
697 | let winnr = bufwinnr(s:plug_buf)
698 | execute winnr.'wincmd w'
699 | call add(s:pos, winsaveview())
700 | else
701 | let s:pos = [winsaveview()]
702 | endif
703 |
704 | setlocal modifiable
705 | return 1
706 | endfunction
707 |
708 | function! s:switch_out(...)
709 | call winrestview(s:pos[-1])
710 | setlocal nomodifiable
711 | if a:0 > 0
712 | execute a:1
713 | endif
714 |
715 | if len(s:pos) > 1
716 | execute 'normal!' s:pos[0].'gt'
717 | execute s:pos[1] 'wincmd w'
718 | call winrestview(s:pos[2])
719 | endif
720 | endfunction
721 |
722 | function! s:finish_bindings()
723 | nnoremap R :call retry()
724 | nnoremap D :PlugDiff
725 | nnoremap S :PlugStatus
726 | nnoremap U :call status_update()
727 | xnoremap U :call status_update()
728 | nnoremap ]] :silent! call section('')
729 | nnoremap [[ :silent! call section('b')
730 | endfunction
731 |
732 | function! s:prepare(...)
733 | if empty(getcwd())
734 | throw 'Invalid current working directory. Cannot proceed.'
735 | endif
736 |
737 | for evar in ['$GIT_DIR', '$GIT_WORK_TREE']
738 | if exists(evar)
739 | throw evar.' detected. Cannot proceed.'
740 | endif
741 | endfor
742 |
743 | call s:job_abort()
744 | if s:switch_in()
745 | if b:plug_preview == 1
746 | pc
747 | endif
748 | enew
749 | else
750 | call s:new_window()
751 | endif
752 |
753 | nnoremap q :if b:plug_preview==1pcendifbd
754 | if a:0 == 0
755 | call s:finish_bindings()
756 | endif
757 | let b:plug_preview = -1
758 | let s:plug_tab = tabpagenr()
759 | let s:plug_buf = winbufnr(0)
760 | call s:assign_name()
761 |
762 | for k in ['', 'L', 'o', 'X', 'd', 'dd']
763 | execute 'silent! unmap ' k
764 | endfor
765 | setlocal buftype=nofile bufhidden=wipe nobuflisted nolist noswapfile nowrap cursorline modifiable nospell
766 | setf vim-plug
767 | if exists('g:syntax_on')
768 | call s:syntax()
769 | endif
770 | endfunction
771 |
772 | function! s:assign_name()
773 | " Assign buffer name
774 | let prefix = '[Plugins]'
775 | let name = prefix
776 | let idx = 2
777 | while bufexists(name)
778 | let name = printf('%s (%s)', prefix, idx)
779 | let idx = idx + 1
780 | endwhile
781 | silent! execute 'f' fnameescape(name)
782 | endfunction
783 |
784 | function! s:chsh(swap)
785 | let prev = [&shell, &shellcmdflag, &shellredir]
786 | if s:is_win
787 | set shell=cmd.exe shellcmdflag=/c shellredir=>%s\ 2>&1
788 | elseif a:swap
789 | set shell=sh shellredir=>%s\ 2>&1
790 | endif
791 | return prev
792 | endfunction
793 |
794 | function! s:bang(cmd, ...)
795 | try
796 | let [sh, shellcmdflag, shrd] = s:chsh(a:0)
797 | " FIXME: Escaping is incomplete. We could use shellescape with eval,
798 | " but it won't work on Windows.
799 | let cmd = a:0 ? s:with_cd(a:cmd, a:1) : a:cmd
800 | if s:is_win
801 | let batchfile = tempname().'.bat'
802 | call writefile(['@echo off', cmd], batchfile)
803 | let cmd = batchfile
804 | endif
805 | let g:_plug_bang = (s:is_win && has('gui_running') ? 'silent ' : '').'!'.escape(cmd, '#!%')
806 | execute "normal! :execute g:_plug_bang\\"
807 | finally
808 | unlet g:_plug_bang
809 | let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd]
810 | if s:is_win
811 | call delete(batchfile)
812 | endif
813 | endtry
814 | return v:shell_error ? 'Exit status: ' . v:shell_error : ''
815 | endfunction
816 |
817 | function! s:regress_bar()
818 | let bar = substitute(getline(2)[1:-2], '.*\zs=', 'x', '')
819 | call s:progress_bar(2, bar, len(bar))
820 | endfunction
821 |
822 | function! s:is_updated(dir)
823 | return !empty(s:system_chomp('git log --pretty=format:"%h" "HEAD...HEAD@{1}"', a:dir))
824 | endfunction
825 |
826 | function! s:do(pull, force, todo)
827 | for [name, spec] in items(a:todo)
828 | if !isdirectory(spec.dir)
829 | continue
830 | endif
831 | let installed = has_key(s:update.new, name)
832 | let updated = installed ? 0 :
833 | \ (a:pull && index(s:update.errors, name) < 0 && s:is_updated(spec.dir))
834 | if a:force || installed || updated
835 | execute 'cd' s:esc(spec.dir)
836 | call append(3, '- Post-update hook for '. name .' ... ')
837 | let error = ''
838 | let type = type(spec.do)
839 | if type == s:TYPE.string
840 | if spec.do[0] == ':'
841 | if !get(s:loaded, name, 0)
842 | let s:loaded[name] = 1
843 | call s:reorg_rtp()
844 | endif
845 | call s:load_plugin(spec)
846 | try
847 | execute spec.do[1:]
848 | catch
849 | let error = v:exception
850 | endtry
851 | if !s:plug_window_exists()
852 | cd -
853 | throw 'Warning: vim-plug was terminated by the post-update hook of '.name
854 | endif
855 | else
856 | let error = s:bang(spec.do)
857 | endif
858 | elseif type == s:TYPE.funcref
859 | try
860 | let status = installed ? 'installed' : (updated ? 'updated' : 'unchanged')
861 | call spec.do({ 'name': name, 'status': status, 'force': a:force })
862 | catch
863 | let error = v:exception
864 | endtry
865 | else
866 | let error = 'Invalid hook type'
867 | endif
868 | call s:switch_in()
869 | call setline(4, empty(error) ? (getline(4) . 'OK')
870 | \ : ('x' . getline(4)[1:] . error))
871 | if !empty(error)
872 | call add(s:update.errors, name)
873 | call s:regress_bar()
874 | endif
875 | cd -
876 | endif
877 | endfor
878 | endfunction
879 |
880 | function! s:hash_match(a, b)
881 | return stridx(a:a, a:b) == 0 || stridx(a:b, a:a) == 0
882 | endfunction
883 |
884 | function! s:checkout(spec)
885 | let sha = a:spec.commit
886 | let output = s:system('git rev-parse HEAD', a:spec.dir)
887 | if !v:shell_error && !s:hash_match(sha, s:lines(output)[0])
888 | let output = s:system(
889 | \ 'git fetch --depth 999999 && git checkout '.s:esc(sha).' --', a:spec.dir)
890 | endif
891 | return output
892 | endfunction
893 |
894 | function! s:finish(pull)
895 | let new_frozen = len(filter(keys(s:update.new), 'g:plugs[v:val].frozen'))
896 | if new_frozen
897 | let s = new_frozen > 1 ? 's' : ''
898 | call append(3, printf('- Installed %d frozen plugin%s', new_frozen, s))
899 | endif
900 | call append(3, '- Finishing ... ') | 4
901 | redraw
902 | call plug#helptags()
903 | call plug#end()
904 | call setline(4, getline(4) . 'Done!')
905 | redraw
906 | let msgs = []
907 | if !empty(s:update.errors)
908 | call add(msgs, "Press 'R' to retry.")
909 | endif
910 | if a:pull && len(s:update.new) < len(filter(getline(5, '$'),
911 | \ "v:val =~ '^- ' && stridx(v:val, 'Already up-to-date') < 0"))
912 | call add(msgs, "Press 'D' to see the updated changes.")
913 | endif
914 | echo join(msgs, ' ')
915 | call s:finish_bindings()
916 | endfunction
917 |
918 | function! s:retry()
919 | if empty(s:update.errors)
920 | return
921 | endif
922 | echo
923 | call s:update_impl(s:update.pull, s:update.force,
924 | \ extend(copy(s:update.errors), [s:update.threads]))
925 | endfunction
926 |
927 | function! s:is_managed(name)
928 | return has_key(g:plugs[a:name], 'uri')
929 | endfunction
930 |
931 | function! s:names(...)
932 | return sort(filter(keys(g:plugs), 'stridx(v:val, a:1) == 0 && s:is_managed(v:val)'))
933 | endfunction
934 |
935 | function! s:check_ruby()
936 | silent! ruby require 'thread'; VIM::command("let g:plug_ruby = '#{RUBY_VERSION}'")
937 | if !exists('g:plug_ruby')
938 | redraw!
939 | return s:warn('echom', 'Warning: Ruby interface is broken')
940 | endif
941 | let ruby_version = split(g:plug_ruby, '\.')
942 | unlet g:plug_ruby
943 | return s:version_requirement(ruby_version, [1, 8, 7])
944 | endfunction
945 |
946 | function! s:update_impl(pull, force, args) abort
947 | let sync = index(a:args, '--sync') >= 0 || has('vim_starting')
948 | let args = filter(copy(a:args), 'v:val != "--sync"')
949 | let threads = (len(args) > 0 && args[-1] =~ '^[1-9][0-9]*$') ?
950 | \ remove(args, -1) : get(g:, 'plug_threads', 16)
951 |
952 | let managed = filter(copy(g:plugs), 's:is_managed(v:key)')
953 | let todo = empty(args) ? filter(managed, '!v:val.frozen || !isdirectory(v:val.dir)') :
954 | \ filter(managed, 'index(args, v:key) >= 0')
955 |
956 | if empty(todo)
957 | return s:warn('echo', 'No plugin to '. (a:pull ? 'update' : 'install'))
958 | endif
959 |
960 | if !s:is_win && s:git_version_requirement(2, 3)
961 | let s:git_terminal_prompt = exists('$GIT_TERMINAL_PROMPT') ? $GIT_TERMINAL_PROMPT : ''
962 | let $GIT_TERMINAL_PROMPT = 0
963 | for plug in values(todo)
964 | let plug.uri = substitute(plug.uri,
965 | \ '^https://git::@github\.com', 'https://github.com', '')
966 | endfor
967 | endif
968 |
969 | if !isdirectory(g:plug_home)
970 | try
971 | call mkdir(g:plug_home, 'p')
972 | catch
973 | return s:err(printf('Invalid plug directory: %s. '.
974 | \ 'Try to call plug#begin with a valid directory', g:plug_home))
975 | endtry
976 | endif
977 |
978 | if has('nvim') && !exists('*jobwait') && threads > 1
979 | call s:warn('echom', '[vim-plug] Update Neovim for parallel installer')
980 | endif
981 |
982 | let use_job = s:nvim || s:vim8
983 | let python = (has('python') || has('python3')) && !use_job
984 | 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()
985 |
986 | let s:update = {
987 | \ 'start': reltime(),
988 | \ 'all': todo,
989 | \ 'todo': copy(todo),
990 | \ 'errors': [],
991 | \ 'pull': a:pull,
992 | \ 'force': a:force,
993 | \ 'new': {},
994 | \ 'threads': (python || ruby || use_job) ? min([len(todo), threads]) : 1,
995 | \ 'bar': '',
996 | \ 'fin': 0
997 | \ }
998 |
999 | call s:prepare(1)
1000 | call append(0, ['', ''])
1001 | normal! 2G
1002 | silent! redraw
1003 |
1004 | let s:clone_opt = get(g:, 'plug_shallow', 1) ?
1005 | \ '--depth 1' . (s:git_version_requirement(1, 7, 10) ? ' --no-single-branch' : '') : ''
1006 |
1007 | if has('win32unix')
1008 | let s:clone_opt .= ' -c core.eol=lf -c core.autocrlf=input'
1009 | endif
1010 |
1011 | " Python version requirement (>= 2.7)
1012 | if python && !has('python3') && !ruby && !use_job && s:update.threads > 1
1013 | redir => pyv
1014 | silent python import platform; print platform.python_version()
1015 | redir END
1016 | let python = s:version_requirement(
1017 | \ map(split(split(pyv)[0], '\.'), 'str2nr(v:val)'), [2, 6])
1018 | endif
1019 |
1020 | if (python || ruby) && s:update.threads > 1
1021 | try
1022 | let imd = &imd
1023 | if s:mac_gui
1024 | set noimd
1025 | endif
1026 | if ruby
1027 | call s:update_ruby()
1028 | else
1029 | call s:update_python()
1030 | endif
1031 | catch
1032 | let lines = getline(4, '$')
1033 | let printed = {}
1034 | silent! 4,$d _
1035 | for line in lines
1036 | let name = s:extract_name(line, '.', '')
1037 | if empty(name) || !has_key(printed, name)
1038 | call append('$', line)
1039 | if !empty(name)
1040 | let printed[name] = 1
1041 | if line[0] == 'x' && index(s:update.errors, name) < 0
1042 | call add(s:update.errors, name)
1043 | end
1044 | endif
1045 | endif
1046 | endfor
1047 | finally
1048 | let &imd = imd
1049 | call s:update_finish()
1050 | endtry
1051 | else
1052 | call s:update_vim()
1053 | while use_job && sync
1054 | sleep 100m
1055 | if s:update.fin
1056 | break
1057 | endif
1058 | endwhile
1059 | endif
1060 | endfunction
1061 |
1062 | function! s:log4(name, msg)
1063 | call setline(4, printf('- %s (%s)', a:msg, a:name))
1064 | redraw
1065 | endfunction
1066 |
1067 | function! s:update_finish()
1068 | if exists('s:git_terminal_prompt')
1069 | let $GIT_TERMINAL_PROMPT = s:git_terminal_prompt
1070 | endif
1071 | if s:switch_in()
1072 | call append(3, '- Updating ...') | 4
1073 | 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))'))
1074 | let [pos, _] = s:logpos(name)
1075 | if !pos
1076 | continue
1077 | endif
1078 | if has_key(spec, 'commit')
1079 | call s:log4(name, 'Checking out '.spec.commit)
1080 | let out = s:checkout(spec)
1081 | elseif has_key(spec, 'tag')
1082 | let tag = spec.tag
1083 | if tag =~ '\*'
1084 | let tags = s:lines(s:system('git tag --list '.s:shellesc(tag).' --sort -version:refname 2>&1', spec.dir))
1085 | if !v:shell_error && !empty(tags)
1086 | let tag = tags[0]
1087 | call s:log4(name, printf('Latest tag for %s -> %s', spec.tag, tag))
1088 | call append(3, '')
1089 | endif
1090 | endif
1091 | call s:log4(name, 'Checking out '.tag)
1092 | let out = s:system('git checkout -q '.s:esc(tag).' -- 2>&1', spec.dir)
1093 | else
1094 | let branch = s:esc(get(spec, 'branch', 'master'))
1095 | call s:log4(name, 'Merging origin/'.branch)
1096 | let out = s:system('git checkout -q '.branch.' -- 2>&1'
1097 | \. (has_key(s:update.new, name) ? '' : ('&& git merge --ff-only origin/'.branch.' 2>&1')), spec.dir)
1098 | endif
1099 | if !v:shell_error && filereadable(spec.dir.'/.gitmodules') &&
1100 | \ (s:update.force || has_key(s:update.new, name) || s:is_updated(spec.dir))
1101 | call s:log4(name, 'Updating submodules. This may take a while.')
1102 | let out .= s:bang('git submodule update --init --recursive 2>&1', spec.dir)
1103 | endif
1104 | let msg = s:format_message(v:shell_error ? 'x': '-', name, out)
1105 | if v:shell_error
1106 | call add(s:update.errors, name)
1107 | call s:regress_bar()
1108 | silent execute pos 'd _'
1109 | call append(4, msg) | 4
1110 | elseif !empty(out)
1111 | call setline(pos, msg[0])
1112 | endif
1113 | redraw
1114 | endfor
1115 | silent 4 d _
1116 | try
1117 | 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")'))
1118 | catch
1119 | call s:warn('echom', v:exception)
1120 | call s:warn('echo', '')
1121 | return
1122 | endtry
1123 | call s:finish(s:update.pull)
1124 | call setline(1, 'Updated. Elapsed time: ' . split(reltimestr(reltime(s:update.start)))[0] . ' sec.')
1125 | call s:switch_out('normal! gg')
1126 | endif
1127 | endfunction
1128 |
1129 | function! s:job_abort()
1130 | if (!s:nvim && !s:vim8) || !exists('s:jobs')
1131 | return
1132 | endif
1133 |
1134 | for [name, j] in items(s:jobs)
1135 | if s:nvim
1136 | silent! call jobstop(j.jobid)
1137 | elseif s:vim8
1138 | silent! call job_stop(j.jobid)
1139 | endif
1140 | if j.new
1141 | call s:system('rm -rf ' . s:shellesc(g:plugs[name].dir))
1142 | endif
1143 | endfor
1144 | let s:jobs = {}
1145 | endfunction
1146 |
1147 | function! s:last_non_empty_line(lines)
1148 | let len = len(a:lines)
1149 | for idx in range(len)
1150 | let line = a:lines[len-idx-1]
1151 | if !empty(line)
1152 | return line
1153 | endif
1154 | endfor
1155 | return ''
1156 | endfunction
1157 |
1158 | function! s:job_out_cb(self, data) abort
1159 | let self = a:self
1160 | let data = remove(self.lines, -1) . a:data
1161 | let lines = map(split(data, "\n", 1), 'split(v:val, "\r", 1)[-1]')
1162 | call extend(self.lines, lines)
1163 | " To reduce the number of buffer updates
1164 | let self.tick = get(self, 'tick', -1) + 1
1165 | if !self.running || self.tick % len(s:jobs) == 0
1166 | let bullet = self.running ? (self.new ? '+' : '*') : (self.error ? 'x' : '-')
1167 | let result = self.error ? join(self.lines, "\n") : s:last_non_empty_line(self.lines)
1168 | call s:log(bullet, self.name, result)
1169 | endif
1170 | endfunction
1171 |
1172 | function! s:job_exit_cb(self, data) abort
1173 | let a:self.running = 0
1174 | let a:self.error = a:data != 0
1175 | call s:reap(a:self.name)
1176 | call s:tick()
1177 | endfunction
1178 |
1179 | function! s:job_cb(fn, job, ch, data)
1180 | if !s:plug_window_exists() " plug window closed
1181 | return s:job_abort()
1182 | endif
1183 | call call(a:fn, [a:job, a:data])
1184 | endfunction
1185 |
1186 | function! s:nvim_cb(job_id, data, event) dict abort
1187 | return a:event == 'stdout' ?
1188 | \ s:job_cb('s:job_out_cb', self, 0, join(a:data, "\n")) :
1189 | \ s:job_cb('s:job_exit_cb', self, 0, a:data)
1190 | endfunction
1191 |
1192 | function! s:spawn(name, cmd, opts)
1193 | let job = { 'name': a:name, 'running': 1, 'error': 0, 'lines': [''],
1194 | \ 'batchfile': (s:is_win && (s:nvim || s:vim8)) ? tempname().'.bat' : '',
1195 | \ 'new': get(a:opts, 'new', 0) }
1196 | let s:jobs[a:name] = job
1197 | let cmd = has_key(a:opts, 'dir') ? s:with_cd(a:cmd, a:opts.dir) : a:cmd
1198 | if !empty(job.batchfile)
1199 | call writefile(['@echo off', cmd], job.batchfile)
1200 | let cmd = job.batchfile
1201 | endif
1202 | let argv = add(s:is_win ? ['cmd', '/c'] : ['sh', '-c'], cmd)
1203 |
1204 | if s:nvim
1205 | call extend(job, {
1206 | \ 'on_stdout': function('s:nvim_cb'),
1207 | \ 'on_exit': function('s:nvim_cb'),
1208 | \ })
1209 | let jid = jobstart(argv, job)
1210 | if jid > 0
1211 | let job.jobid = jid
1212 | else
1213 | let job.running = 0
1214 | let job.error = 1
1215 | let job.lines = [jid < 0 ? argv[0].' is not executable' :
1216 | \ 'Invalid arguments (or job table is full)']
1217 | endif
1218 | elseif s:vim8
1219 | let jid = job_start(s:is_win ? join(argv, ' ') : argv, {
1220 | \ 'out_cb': function('s:job_cb', ['s:job_out_cb', job]),
1221 | \ 'exit_cb': function('s:job_cb', ['s:job_exit_cb', job]),
1222 | \ 'out_mode': 'raw'
1223 | \})
1224 | if job_status(jid) == 'run'
1225 | let job.jobid = jid
1226 | else
1227 | let job.running = 0
1228 | let job.error = 1
1229 | let job.lines = ['Failed to start job']
1230 | endif
1231 | else
1232 | let job.lines = s:lines(call('s:system', [cmd]))
1233 | let job.error = v:shell_error != 0
1234 | let job.running = 0
1235 | endif
1236 | endfunction
1237 |
1238 | function! s:reap(name)
1239 | let job = s:jobs[a:name]
1240 | if job.error
1241 | call add(s:update.errors, a:name)
1242 | elseif get(job, 'new', 0)
1243 | let s:update.new[a:name] = 1
1244 | endif
1245 | let s:update.bar .= job.error ? 'x' : '='
1246 |
1247 | let bullet = job.error ? 'x' : '-'
1248 | let result = job.error ? join(job.lines, "\n") : s:last_non_empty_line(job.lines)
1249 | call s:log(bullet, a:name, empty(result) ? 'OK' : result)
1250 | call s:bar()
1251 |
1252 | if has_key(job, 'batchfile') && !empty(job.batchfile)
1253 | call delete(job.batchfile)
1254 | endif
1255 | call remove(s:jobs, a:name)
1256 | endfunction
1257 |
1258 | function! s:bar()
1259 | if s:switch_in()
1260 | let total = len(s:update.all)
1261 | call setline(1, (s:update.pull ? 'Updating' : 'Installing').
1262 | \ ' plugins ('.len(s:update.bar).'/'.total.')')
1263 | call s:progress_bar(2, s:update.bar, total)
1264 | call s:switch_out()
1265 | endif
1266 | endfunction
1267 |
1268 | function! s:logpos(name)
1269 | for i in range(4, line('$'))
1270 | if getline(i) =~# '^[-+x*] '.a:name.':'
1271 | for j in range(i + 1, line('$'))
1272 | if getline(j) !~ '^ '
1273 | return [i, j - 1]
1274 | endif
1275 | endfor
1276 | return [i, i]
1277 | endif
1278 | endfor
1279 | return [0, 0]
1280 | endfunction
1281 |
1282 | function! s:log(bullet, name, lines)
1283 | if s:switch_in()
1284 | let [b, e] = s:logpos(a:name)
1285 | if b > 0
1286 | silent execute printf('%d,%d d _', b, e)
1287 | if b > winheight('.')
1288 | let b = 4
1289 | endif
1290 | else
1291 | let b = 4
1292 | endif
1293 | " FIXME For some reason, nomodifiable is set after :d in vim8
1294 | setlocal modifiable
1295 | call append(b - 1, s:format_message(a:bullet, a:name, a:lines))
1296 | call s:switch_out()
1297 | endif
1298 | endfunction
1299 |
1300 | function! s:update_vim()
1301 | let s:jobs = {}
1302 |
1303 | call s:bar()
1304 | call s:tick()
1305 | endfunction
1306 |
1307 | function! s:tick()
1308 | let pull = s:update.pull
1309 | let prog = s:progress_opt(s:nvim || s:vim8)
1310 | while 1 " Without TCO, Vim stack is bound to explode
1311 | if empty(s:update.todo)
1312 | if empty(s:jobs) && !s:update.fin
1313 | call s:update_finish()
1314 | let s:update.fin = 1
1315 | endif
1316 | return
1317 | endif
1318 |
1319 | let name = keys(s:update.todo)[0]
1320 | let spec = remove(s:update.todo, name)
1321 | let new = !isdirectory(spec.dir)
1322 |
1323 | call s:log(new ? '+' : '*', name, pull ? 'Updating ...' : 'Installing ...')
1324 | redraw
1325 |
1326 | let has_tag = has_key(spec, 'tag')
1327 | if !new
1328 | let [error, _] = s:git_validate(spec, 0)
1329 | if empty(error)
1330 | if pull
1331 | let fetch_opt = (has_tag && !empty(globpath(spec.dir, '.git/shallow'))) ? '--depth 99999999' : ''
1332 | call s:spawn(name, printf('git fetch %s %s 2>&1', fetch_opt, prog), { 'dir': spec.dir })
1333 | else
1334 | let s:jobs[name] = { 'running': 0, 'lines': ['Already installed'], 'error': 0 }
1335 | endif
1336 | else
1337 | let s:jobs[name] = { 'running': 0, 'lines': s:lines(error), 'error': 1 }
1338 | endif
1339 | else
1340 | call s:spawn(name,
1341 | \ printf('git clone %s %s %s %s 2>&1',
1342 | \ has_tag ? '' : s:clone_opt,
1343 | \ prog,
1344 | \ s:shellesc(spec.uri),
1345 | \ s:shellesc(s:trim(spec.dir))), { 'new': 1 })
1346 | endif
1347 |
1348 | if !s:jobs[name].running
1349 | call s:reap(name)
1350 | endif
1351 | if len(s:jobs) >= s:update.threads
1352 | break
1353 | endif
1354 | endwhile
1355 | endfunction
1356 |
1357 | function! s:update_python()
1358 | let py_exe = has('python') ? 'python' : 'python3'
1359 | execute py_exe "<< EOF"
1360 | import datetime
1361 | import functools
1362 | import os
1363 | try:
1364 | import queue
1365 | except ImportError:
1366 | import Queue as queue
1367 | import random
1368 | import re
1369 | import shutil
1370 | import signal
1371 | import subprocess
1372 | import tempfile
1373 | import threading as thr
1374 | import time
1375 | import traceback
1376 | import vim
1377 |
1378 | G_NVIM = vim.eval("has('nvim')") == '1'
1379 | G_PULL = vim.eval('s:update.pull') == '1'
1380 | G_RETRIES = int(vim.eval('get(g:, "plug_retries", 2)')) + 1
1381 | G_TIMEOUT = int(vim.eval('get(g:, "plug_timeout", 60)'))
1382 | G_CLONE_OPT = vim.eval('s:clone_opt')
1383 | G_PROGRESS = vim.eval('s:progress_opt(1)')
1384 | G_LOG_PROB = 1.0 / int(vim.eval('s:update.threads'))
1385 | G_STOP = thr.Event()
1386 | G_IS_WIN = vim.eval('s:is_win') == '1'
1387 |
1388 | class PlugError(Exception):
1389 | def __init__(self, msg):
1390 | self.msg = msg
1391 | class CmdTimedOut(PlugError):
1392 | pass
1393 | class CmdFailed(PlugError):
1394 | pass
1395 | class InvalidURI(PlugError):
1396 | pass
1397 | class Action(object):
1398 | INSTALL, UPDATE, ERROR, DONE = ['+', '*', 'x', '-']
1399 |
1400 | class Buffer(object):
1401 | def __init__(self, lock, num_plugs, is_pull):
1402 | self.bar = ''
1403 | self.event = 'Updating' if is_pull else 'Installing'
1404 | self.lock = lock
1405 | self.maxy = int(vim.eval('winheight(".")'))
1406 | self.num_plugs = num_plugs
1407 |
1408 | def __where(self, name):
1409 | """ Find first line with name in current buffer. Return line num. """
1410 | found, lnum = False, 0
1411 | matcher = re.compile('^[-+x*] {0}:'.format(name))
1412 | for line in vim.current.buffer:
1413 | if matcher.search(line) is not None:
1414 | found = True
1415 | break
1416 | lnum += 1
1417 |
1418 | if not found:
1419 | lnum = -1
1420 | return lnum
1421 |
1422 | def header(self):
1423 | curbuf = vim.current.buffer
1424 | curbuf[0] = self.event + ' plugins ({0}/{1})'.format(len(self.bar), self.num_plugs)
1425 |
1426 | num_spaces = self.num_plugs - len(self.bar)
1427 | curbuf[1] = '[{0}{1}]'.format(self.bar, num_spaces * ' ')
1428 |
1429 | with self.lock:
1430 | vim.command('normal! 2G')
1431 | vim.command('redraw')
1432 |
1433 | def write(self, action, name, lines):
1434 | first, rest = lines[0], lines[1:]
1435 | msg = ['{0} {1}{2}{3}'.format(action, name, ': ' if first else '', first)]
1436 | msg.extend([' ' + line for line in rest])
1437 |
1438 | try:
1439 | if action == Action.ERROR:
1440 | self.bar += 'x'
1441 | vim.command("call add(s:update.errors, '{0}')".format(name))
1442 | elif action == Action.DONE:
1443 | self.bar += '='
1444 |
1445 | curbuf = vim.current.buffer
1446 | lnum = self.__where(name)
1447 | if lnum != -1: # Found matching line num
1448 | del curbuf[lnum]
1449 | if lnum > self.maxy and action in set([Action.INSTALL, Action.UPDATE]):
1450 | lnum = 3
1451 | else:
1452 | lnum = 3
1453 | curbuf.append(msg, lnum)
1454 |
1455 | self.header()
1456 | except vim.error:
1457 | pass
1458 |
1459 | class Command(object):
1460 | CD = 'cd /d' if G_IS_WIN else 'cd'
1461 |
1462 | def __init__(self, cmd, cmd_dir=None, timeout=60, cb=None, clean=None):
1463 | self.cmd = cmd
1464 | if cmd_dir:
1465 | self.cmd = '{0} {1} && {2}'.format(Command.CD, cmd_dir, self.cmd)
1466 | self.timeout = timeout
1467 | self.callback = cb if cb else (lambda msg: None)
1468 | self.clean = clean if clean else (lambda: None)
1469 | self.proc = None
1470 |
1471 | @property
1472 | def alive(self):
1473 | """ Returns true only if command still running. """
1474 | return self.proc and self.proc.poll() is None
1475 |
1476 | def execute(self, ntries=3):
1477 | """ Execute the command with ntries if CmdTimedOut.
1478 | Returns the output of the command if no Exception.
1479 | """
1480 | attempt, finished, limit = 0, False, self.timeout
1481 |
1482 | while not finished:
1483 | try:
1484 | attempt += 1
1485 | result = self.try_command()
1486 | finished = True
1487 | return result
1488 | except CmdTimedOut:
1489 | if attempt != ntries:
1490 | self.notify_retry()
1491 | self.timeout += limit
1492 | else:
1493 | raise
1494 |
1495 | def notify_retry(self):
1496 | """ Retry required for command, notify user. """
1497 | for count in range(3, 0, -1):
1498 | if G_STOP.is_set():
1499 | raise KeyboardInterrupt
1500 | msg = 'Timeout. Will retry in {0} second{1} ...'.format(
1501 | count, 's' if count != 1 else '')
1502 | self.callback([msg])
1503 | time.sleep(1)
1504 | self.callback(['Retrying ...'])
1505 |
1506 | def try_command(self):
1507 | """ Execute a cmd & poll for callback. Returns list of output.
1508 | Raises CmdFailed -> return code for Popen isn't 0
1509 | Raises CmdTimedOut -> command exceeded timeout without new output
1510 | """
1511 | first_line = True
1512 |
1513 | try:
1514 | tfile = tempfile.NamedTemporaryFile(mode='w+b')
1515 | preexec_fn = not G_IS_WIN and os.setsid or None
1516 | self.proc = subprocess.Popen(self.cmd, stdout=tfile,
1517 | stderr=subprocess.STDOUT,
1518 | stdin=subprocess.PIPE, shell=True,
1519 | preexec_fn=preexec_fn)
1520 | thrd = thr.Thread(target=(lambda proc: proc.wait()), args=(self.proc,))
1521 | thrd.start()
1522 |
1523 | thread_not_started = True
1524 | while thread_not_started:
1525 | try:
1526 | thrd.join(0.1)
1527 | thread_not_started = False
1528 | except RuntimeError:
1529 | pass
1530 |
1531 | while self.alive:
1532 | if G_STOP.is_set():
1533 | raise KeyboardInterrupt
1534 |
1535 | if first_line or random.random() < G_LOG_PROB:
1536 | first_line = False
1537 | line = '' if G_IS_WIN else nonblock_read(tfile.name)
1538 | if line:
1539 | self.callback([line])
1540 |
1541 | time_diff = time.time() - os.path.getmtime(tfile.name)
1542 | if time_diff > self.timeout:
1543 | raise CmdTimedOut(['Timeout!'])
1544 |
1545 | thrd.join(0.5)
1546 |
1547 | tfile.seek(0)
1548 | result = [line.decode('utf-8', 'replace').rstrip() for line in tfile]
1549 |
1550 | if self.proc.returncode != 0:
1551 | raise CmdFailed([''] + result)
1552 |
1553 | return result
1554 | except:
1555 | self.terminate()
1556 | raise
1557 |
1558 | def terminate(self):
1559 | """ Terminate process and cleanup. """
1560 | if self.alive:
1561 | if G_IS_WIN:
1562 | os.kill(self.proc.pid, signal.SIGINT)
1563 | else:
1564 | os.killpg(self.proc.pid, signal.SIGTERM)
1565 | self.clean()
1566 |
1567 | class Plugin(object):
1568 | def __init__(self, name, args, buf_q, lock):
1569 | self.name = name
1570 | self.args = args
1571 | self.buf_q = buf_q
1572 | self.lock = lock
1573 | self.tag = args.get('tag', 0)
1574 |
1575 | def manage(self):
1576 | try:
1577 | if os.path.exists(self.args['dir']):
1578 | self.update()
1579 | else:
1580 | self.install()
1581 | with self.lock:
1582 | thread_vim_command("let s:update.new['{0}'] = 1".format(self.name))
1583 | except PlugError as exc:
1584 | self.write(Action.ERROR, self.name, exc.msg)
1585 | except KeyboardInterrupt:
1586 | G_STOP.set()
1587 | self.write(Action.ERROR, self.name, ['Interrupted!'])
1588 | except:
1589 | # Any exception except those above print stack trace
1590 | msg = 'Trace:\n{0}'.format(traceback.format_exc().rstrip())
1591 | self.write(Action.ERROR, self.name, msg.split('\n'))
1592 | raise
1593 |
1594 | def install(self):
1595 | target = self.args['dir']
1596 | if target[-1] == '\\':
1597 | target = target[0:-1]
1598 |
1599 | def clean(target):
1600 | def _clean():
1601 | try:
1602 | shutil.rmtree(target)
1603 | except OSError:
1604 | pass
1605 | return _clean
1606 |
1607 | self.write(Action.INSTALL, self.name, ['Installing ...'])
1608 | callback = functools.partial(self.write, Action.INSTALL, self.name)
1609 | cmd = 'git clone {0} {1} {2} {3} 2>&1'.format(
1610 | '' if self.tag else G_CLONE_OPT, G_PROGRESS, self.args['uri'],
1611 | esc(target))
1612 | com = Command(cmd, None, G_TIMEOUT, callback, clean(target))
1613 | result = com.execute(G_RETRIES)
1614 | self.write(Action.DONE, self.name, result[-1:])
1615 |
1616 | def repo_uri(self):
1617 | cmd = 'git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url'
1618 | command = Command(cmd, self.args['dir'], G_TIMEOUT,)
1619 | result = command.execute(G_RETRIES)
1620 | return result[-1]
1621 |
1622 | def update(self):
1623 | actual_uri = self.repo_uri()
1624 | expect_uri = self.args['uri']
1625 | regex = re.compile(r'^(?:\w+://)?(?:[^@/]*@)?([^:/]*(?::[0-9]*)?)[:/](.*?)(?:\.git)?/?$')
1626 | ma = regex.match(actual_uri)
1627 | mb = regex.match(expect_uri)
1628 | if ma is None or mb is None or ma.groups() != mb.groups():
1629 | msg = ['',
1630 | 'Invalid URI: {0}'.format(actual_uri),
1631 | 'Expected {0}'.format(expect_uri),
1632 | 'PlugClean required.']
1633 | raise InvalidURI(msg)
1634 |
1635 | if G_PULL:
1636 | self.write(Action.UPDATE, self.name, ['Updating ...'])
1637 | callback = functools.partial(self.write, Action.UPDATE, self.name)
1638 | fetch_opt = '--depth 99999999' if self.tag and os.path.isfile(os.path.join(self.args['dir'], '.git/shallow')) else ''
1639 | cmd = 'git fetch {0} {1} 2>&1'.format(fetch_opt, G_PROGRESS)
1640 | com = Command(cmd, self.args['dir'], G_TIMEOUT, callback)
1641 | result = com.execute(G_RETRIES)
1642 | self.write(Action.DONE, self.name, result[-1:])
1643 | else:
1644 | self.write(Action.DONE, self.name, ['Already installed'])
1645 |
1646 | def write(self, action, name, msg):
1647 | self.buf_q.put((action, name, msg))
1648 |
1649 | class PlugThread(thr.Thread):
1650 | def __init__(self, tname, args):
1651 | super(PlugThread, self).__init__()
1652 | self.tname = tname
1653 | self.args = args
1654 |
1655 | def run(self):
1656 | thr.current_thread().name = self.tname
1657 | buf_q, work_q, lock = self.args
1658 |
1659 | try:
1660 | while not G_STOP.is_set():
1661 | name, args = work_q.get_nowait()
1662 | plug = Plugin(name, args, buf_q, lock)
1663 | plug.manage()
1664 | work_q.task_done()
1665 | except queue.Empty:
1666 | pass
1667 |
1668 | class RefreshThread(thr.Thread):
1669 | def __init__(self, lock):
1670 | super(RefreshThread, self).__init__()
1671 | self.lock = lock
1672 | self.running = True
1673 |
1674 | def run(self):
1675 | while self.running:
1676 | with self.lock:
1677 | thread_vim_command('noautocmd normal! a')
1678 | time.sleep(0.33)
1679 |
1680 | def stop(self):
1681 | self.running = False
1682 |
1683 | if G_NVIM:
1684 | def thread_vim_command(cmd):
1685 | vim.session.threadsafe_call(lambda: vim.command(cmd))
1686 | else:
1687 | def thread_vim_command(cmd):
1688 | vim.command(cmd)
1689 |
1690 | def esc(name):
1691 | return '"' + name.replace('"', '\"') + '"'
1692 |
1693 | def nonblock_read(fname):
1694 | """ Read a file with nonblock flag. Return the last line. """
1695 | fread = os.open(fname, os.O_RDONLY | os.O_NONBLOCK)
1696 | buf = os.read(fread, 100000).decode('utf-8', 'replace')
1697 | os.close(fread)
1698 |
1699 | line = buf.rstrip('\r\n')
1700 | left = max(line.rfind('\r'), line.rfind('\n'))
1701 | if left != -1:
1702 | left += 1
1703 | line = line[left:]
1704 |
1705 | return line
1706 |
1707 | def main():
1708 | thr.current_thread().name = 'main'
1709 | nthreads = int(vim.eval('s:update.threads'))
1710 | plugs = vim.eval('s:update.todo')
1711 | mac_gui = vim.eval('s:mac_gui') == '1'
1712 |
1713 | lock = thr.Lock()
1714 | buf = Buffer(lock, len(plugs), G_PULL)
1715 | buf_q, work_q = queue.Queue(), queue.Queue()
1716 | for work in plugs.items():
1717 | work_q.put(work)
1718 |
1719 | start_cnt = thr.active_count()
1720 | for num in range(nthreads):
1721 | tname = 'PlugT-{0:02}'.format(num)
1722 | thread = PlugThread(tname, (buf_q, work_q, lock))
1723 | thread.start()
1724 | if mac_gui:
1725 | rthread = RefreshThread(lock)
1726 | rthread.start()
1727 |
1728 | while not buf_q.empty() or thr.active_count() != start_cnt:
1729 | try:
1730 | action, name, msg = buf_q.get(True, 0.25)
1731 | buf.write(action, name, ['OK'] if not msg else msg)
1732 | buf_q.task_done()
1733 | except queue.Empty:
1734 | pass
1735 | except KeyboardInterrupt:
1736 | G_STOP.set()
1737 |
1738 | if mac_gui:
1739 | rthread.stop()
1740 | rthread.join()
1741 |
1742 | main()
1743 | EOF
1744 | endfunction
1745 |
1746 | function! s:update_ruby()
1747 | ruby << EOF
1748 | module PlugStream
1749 | SEP = ["\r", "\n", nil]
1750 | def get_line
1751 | buffer = ''
1752 | loop do
1753 | char = readchar rescue return
1754 | if SEP.include? char.chr
1755 | buffer << $/
1756 | break
1757 | else
1758 | buffer << char
1759 | end
1760 | end
1761 | buffer
1762 | end
1763 | end unless defined?(PlugStream)
1764 |
1765 | def esc arg
1766 | %["#{arg.gsub('"', '\"')}"]
1767 | end
1768 |
1769 | def killall pid
1770 | pids = [pid]
1771 | if /mswin|mingw|bccwin/ =~ RUBY_PLATFORM
1772 | pids.each { |pid| Process.kill 'INT', pid.to_i rescue nil }
1773 | else
1774 | unless `which pgrep 2> /dev/null`.empty?
1775 | children = pids
1776 | until children.empty?
1777 | children = children.map { |pid|
1778 | `pgrep -P #{pid}`.lines.map { |l| l.chomp }
1779 | }.flatten
1780 | pids += children
1781 | end
1782 | end
1783 | pids.each { |pid| Process.kill 'TERM', pid.to_i rescue nil }
1784 | end
1785 | end
1786 |
1787 | def compare_git_uri a, b
1788 | regex = %r{^(?:\w+://)?(?:[^@/]*@)?([^:/]*(?::[0-9]*)?)[:/](.*?)(?:\.git)?/?$}
1789 | regex.match(a).to_a.drop(1) == regex.match(b).to_a.drop(1)
1790 | end
1791 |
1792 | require 'thread'
1793 | require 'fileutils'
1794 | require 'timeout'
1795 | running = true
1796 | iswin = VIM::evaluate('s:is_win').to_i == 1
1797 | pull = VIM::evaluate('s:update.pull').to_i == 1
1798 | base = VIM::evaluate('g:plug_home')
1799 | all = VIM::evaluate('s:update.todo')
1800 | limit = VIM::evaluate('get(g:, "plug_timeout", 60)')
1801 | tries = VIM::evaluate('get(g:, "plug_retries", 2)') + 1
1802 | nthr = VIM::evaluate('s:update.threads').to_i
1803 | maxy = VIM::evaluate('winheight(".")').to_i
1804 | vim7 = VIM::evaluate('v:version').to_i <= 703 && RUBY_PLATFORM =~ /darwin/
1805 | cd = iswin ? 'cd /d' : 'cd'
1806 | tot = VIM::evaluate('len(s:update.todo)') || 0
1807 | bar = ''
1808 | skip = 'Already installed'
1809 | mtx = Mutex.new
1810 | take1 = proc { mtx.synchronize { running && all.shift } }
1811 | logh = proc {
1812 | cnt = bar.length
1813 | $curbuf[1] = "#{pull ? 'Updating' : 'Installing'} plugins (#{cnt}/#{tot})"
1814 | $curbuf[2] = '[' + bar.ljust(tot) + ']'
1815 | VIM::command('normal! 2G')
1816 | VIM::command('redraw')
1817 | }
1818 | where = proc { |name| (1..($curbuf.length)).find { |l| $curbuf[l] =~ /^[-+x*] #{name}:/ } }
1819 | log = proc { |name, result, type|
1820 | mtx.synchronize do
1821 | ing = ![true, false].include?(type)
1822 | bar += type ? '=' : 'x' unless ing
1823 | b = case type
1824 | when :install then '+' when :update then '*'
1825 | when true, nil then '-' else
1826 | VIM::command("call add(s:update.errors, '#{name}')")
1827 | 'x'
1828 | end
1829 | result =
1830 | if type || type.nil?
1831 | ["#{b} #{name}: #{result.lines.to_a.last || 'OK'}"]
1832 | elsif result =~ /^Interrupted|^Timeout/
1833 | ["#{b} #{name}: #{result}"]
1834 | else
1835 | ["#{b} #{name}"] + result.lines.map { |l| " " << l }
1836 | end
1837 | if lnum = where.call(name)
1838 | $curbuf.delete lnum
1839 | lnum = 4 if ing && lnum > maxy
1840 | end
1841 | result.each_with_index do |line, offset|
1842 | $curbuf.append((lnum || 4) - 1 + offset, line.gsub(/\e\[./, '').chomp)
1843 | end
1844 | logh.call
1845 | end
1846 | }
1847 | bt = proc { |cmd, name, type, cleanup|
1848 | tried = timeout = 0
1849 | begin
1850 | tried += 1
1851 | timeout += limit
1852 | fd = nil
1853 | data = ''
1854 | if iswin
1855 | Timeout::timeout(timeout) do
1856 | tmp = VIM::evaluate('tempname()')
1857 | system("(#{cmd}) > #{tmp}")
1858 | data = File.read(tmp).chomp
1859 | File.unlink tmp rescue nil
1860 | end
1861 | else
1862 | fd = IO.popen(cmd).extend(PlugStream)
1863 | first_line = true
1864 | log_prob = 1.0 / nthr
1865 | while line = Timeout::timeout(timeout) { fd.get_line }
1866 | data << line
1867 | log.call name, line.chomp, type if name && (first_line || rand < log_prob)
1868 | first_line = false
1869 | end
1870 | fd.close
1871 | end
1872 | [$? == 0, data.chomp]
1873 | rescue Timeout::Error, Interrupt => e
1874 | if fd && !fd.closed?
1875 | killall fd.pid
1876 | fd.close
1877 | end
1878 | cleanup.call if cleanup
1879 | if e.is_a?(Timeout::Error) && tried < tries
1880 | 3.downto(1) do |countdown|
1881 | s = countdown > 1 ? 's' : ''
1882 | log.call name, "Timeout. Will retry in #{countdown} second#{s} ...", type
1883 | sleep 1
1884 | end
1885 | log.call name, 'Retrying ...', type
1886 | retry
1887 | end
1888 | [false, e.is_a?(Interrupt) ? "Interrupted!" : "Timeout!"]
1889 | end
1890 | }
1891 | main = Thread.current
1892 | threads = []
1893 | watcher = Thread.new {
1894 | if vim7
1895 | while VIM::evaluate('getchar(1)')
1896 | sleep 0.1
1897 | end
1898 | else
1899 | require 'io/console' # >= Ruby 1.9
1900 | nil until IO.console.getch == 3.chr
1901 | end
1902 | mtx.synchronize do
1903 | running = false
1904 | threads.each { |t| t.raise Interrupt } unless vim7
1905 | end
1906 | threads.each { |t| t.join rescue nil }
1907 | main.kill
1908 | }
1909 | refresh = Thread.new {
1910 | while true
1911 | mtx.synchronize do
1912 | break unless running
1913 | VIM::command('noautocmd normal! a')
1914 | end
1915 | sleep 0.2
1916 | end
1917 | } if VIM::evaluate('s:mac_gui') == 1
1918 |
1919 | clone_opt = VIM::evaluate('s:clone_opt')
1920 | progress = VIM::evaluate('s:progress_opt(1)')
1921 | nthr.times do
1922 | mtx.synchronize do
1923 | threads << Thread.new {
1924 | while pair = take1.call
1925 | name = pair.first
1926 | dir, uri, tag = pair.last.values_at *%w[dir uri tag]
1927 | exists = File.directory? dir
1928 | ok, result =
1929 | if exists
1930 | chdir = "#{cd} #{iswin ? dir : esc(dir)}"
1931 | ret, data = bt.call "#{chdir} && git rev-parse --abbrev-ref HEAD 2>&1 && git config -f .git/config remote.origin.url", nil, nil, nil
1932 | current_uri = data.lines.to_a.last
1933 | if !ret
1934 | if data =~ /^Interrupted|^Timeout/
1935 | [false, data]
1936 | else
1937 | [false, [data.chomp, "PlugClean required."].join($/)]
1938 | end
1939 | elsif !compare_git_uri(current_uri, uri)
1940 | [false, ["Invalid URI: #{current_uri}",
1941 | "Expected: #{uri}",
1942 | "PlugClean required."].join($/)]
1943 | else
1944 | if pull
1945 | log.call name, 'Updating ...', :update
1946 | fetch_opt = (tag && File.exist?(File.join(dir, '.git/shallow'))) ? '--depth 99999999' : ''
1947 | bt.call "#{chdir} && git fetch #{fetch_opt} #{progress} 2>&1", name, :update, nil
1948 | else
1949 | [true, skip]
1950 | end
1951 | end
1952 | else
1953 | d = esc dir.sub(%r{[\\/]+$}, '')
1954 | log.call name, 'Installing ...', :install
1955 | bt.call "git clone #{clone_opt unless tag} #{progress} #{uri} #{d} 2>&1", name, :install, proc {
1956 | FileUtils.rm_rf dir
1957 | }
1958 | end
1959 | mtx.synchronize { VIM::command("let s:update.new['#{name}'] = 1") } if !exists && ok
1960 | log.call name, result, ok
1961 | end
1962 | } if running
1963 | end
1964 | end
1965 | threads.each { |t| t.join rescue nil }
1966 | logh.call
1967 | refresh.kill if refresh
1968 | watcher.kill
1969 | EOF
1970 | endfunction
1971 |
1972 | function! s:shellesc_cmd(arg)
1973 | let escaped = substitute(a:arg, '[&|<>()@^]', '^&', 'g')
1974 | let escaped = substitute(escaped, '%', '%%', 'g')
1975 | let escaped = substitute(escaped, '"', '\\^&', 'g')
1976 | let escaped = substitute(escaped, '\(\\\+\)\(\\^\)', '\1\1\2', 'g')
1977 | return '^"'.substitute(escaped, '\(\\\+\)$', '\1\1', '').'^"'
1978 | endfunction
1979 |
1980 | function! s:shellesc(arg)
1981 | if &shell =~# 'cmd.exe$'
1982 | return s:shellesc_cmd(a:arg)
1983 | endif
1984 | return shellescape(a:arg)
1985 | endfunction
1986 |
1987 | function! s:glob_dir(path)
1988 | return map(filter(s:glob(a:path, '**'), 'isdirectory(v:val)'), 's:dirpath(v:val)')
1989 | endfunction
1990 |
1991 | function! s:progress_bar(line, bar, total)
1992 | call setline(a:line, '[' . s:lpad(a:bar, a:total) . ']')
1993 | endfunction
1994 |
1995 | function! s:compare_git_uri(a, b)
1996 | " See `git help clone'
1997 | " https:// [user@] github.com[:port] / junegunn/vim-plug [.git]
1998 | " [git@] github.com[:port] : junegunn/vim-plug [.git]
1999 | " file:// / junegunn/vim-plug [/]
2000 | " / junegunn/vim-plug [/]
2001 | let pat = '^\%(\w\+://\)\='.'\%([^@/]*@\)\='.'\([^:/]*\%(:[0-9]*\)\=\)'.'[:/]'.'\(.\{-}\)'.'\%(\.git\)\=/\?$'
2002 | let ma = matchlist(a:a, pat)
2003 | let mb = matchlist(a:b, pat)
2004 | return ma[1:2] ==# mb[1:2]
2005 | endfunction
2006 |
2007 | function! s:format_message(bullet, name, message)
2008 | if a:bullet != 'x'
2009 | return [printf('%s %s: %s', a:bullet, a:name, s:lastline(a:message))]
2010 | else
2011 | let lines = map(s:lines(a:message), '" ".v:val')
2012 | return extend([printf('x %s:', a:name)], lines)
2013 | endif
2014 | endfunction
2015 |
2016 | function! s:with_cd(cmd, dir)
2017 | return printf('cd%s %s && %s', s:is_win ? ' /d' : '', s:shellesc(a:dir), a:cmd)
2018 | endfunction
2019 |
2020 | function! s:system(cmd, ...)
2021 | try
2022 | let [sh, shellcmdflag, shrd] = s:chsh(1)
2023 | let cmd = a:0 > 0 ? s:with_cd(a:cmd, a:1) : a:cmd
2024 | if s:is_win
2025 | let batchfile = tempname().'.bat'
2026 | call writefile(['@echo off', cmd], batchfile)
2027 | let cmd = batchfile
2028 | endif
2029 | return system(s:is_win ? '('.cmd.')' : cmd)
2030 | finally
2031 | let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd]
2032 | if s:is_win
2033 | call delete(batchfile)
2034 | endif
2035 | endtry
2036 | endfunction
2037 |
2038 | function! s:system_chomp(...)
2039 | let ret = call('s:system', a:000)
2040 | return v:shell_error ? '' : substitute(ret, '\n$', '', '')
2041 | endfunction
2042 |
2043 | function! s:git_validate(spec, check_branch)
2044 | let err = ''
2045 | if isdirectory(a:spec.dir)
2046 | 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))
2047 | let remote = result[-1]
2048 | if v:shell_error
2049 | let err = join([remote, 'PlugClean required.'], "\n")
2050 | elseif !s:compare_git_uri(remote, a:spec.uri)
2051 | let err = join(['Invalid URI: '.remote,
2052 | \ 'Expected: '.a:spec.uri,
2053 | \ 'PlugClean required.'], "\n")
2054 | elseif a:check_branch && has_key(a:spec, 'commit')
2055 | let result = s:lines(s:system('git rev-parse HEAD 2>&1', a:spec.dir))
2056 | let sha = result[-1]
2057 | if v:shell_error
2058 | let err = join(add(result, 'PlugClean required.'), "\n")
2059 | elseif !s:hash_match(sha, a:spec.commit)
2060 | let err = join([printf('Invalid HEAD (expected: %s, actual: %s)',
2061 | \ a:spec.commit[:6], sha[:6]),
2062 | \ 'PlugUpdate required.'], "\n")
2063 | endif
2064 | elseif a:check_branch
2065 | let branch = result[0]
2066 | " Check tag
2067 | if has_key(a:spec, 'tag')
2068 | let tag = s:system_chomp('git describe --exact-match --tags HEAD 2>&1', a:spec.dir)
2069 | if a:spec.tag !=# tag && a:spec.tag !~ '\*'
2070 | let err = printf('Invalid tag: %s (expected: %s). Try PlugUpdate.',
2071 | \ (empty(tag) ? 'N/A' : tag), a:spec.tag)
2072 | endif
2073 | " Check branch
2074 | elseif a:spec.branch !=# branch
2075 | let err = printf('Invalid branch: %s (expected: %s). Try PlugUpdate.',
2076 | \ branch, a:spec.branch)
2077 | endif
2078 | if empty(err)
2079 | let [ahead, behind] = split(s:lastline(s:system(printf(
2080 | \ 'git rev-list --count --left-right HEAD...origin/%s',
2081 | \ a:spec.branch), a:spec.dir)), '\t')
2082 | if !v:shell_error && ahead
2083 | if behind
2084 | " Only mention PlugClean if diverged, otherwise it's likely to be
2085 | " pushable (and probably not that messed up).
2086 | let err = printf(
2087 | \ "Diverged from origin/%s (%d commit(s) ahead and %d commit(s) behind!\n"
2088 | \ .'Backup local changes and run PlugClean and PlugUpdate to reinstall it.', a:spec.branch, ahead, behind)
2089 | else
2090 | let err = printf("Ahead of origin/%s by %d commit(s).\n"
2091 | \ .'Cannot update until local changes are pushed.',
2092 | \ a:spec.branch, ahead)
2093 | endif
2094 | endif
2095 | endif
2096 | endif
2097 | else
2098 | let err = 'Not found'
2099 | endif
2100 | return [err, err =~# 'PlugClean']
2101 | endfunction
2102 |
2103 | function! s:rm_rf(dir)
2104 | if isdirectory(a:dir)
2105 | call s:system((s:is_win ? 'rmdir /S /Q ' : 'rm -rf ') . s:shellesc(a:dir))
2106 | endif
2107 | endfunction
2108 |
2109 | function! s:clean(force)
2110 | call s:prepare()
2111 | call append(0, 'Searching for invalid plugins in '.g:plug_home)
2112 | call append(1, '')
2113 |
2114 | " List of valid directories
2115 | let dirs = []
2116 | let errs = {}
2117 | let [cnt, total] = [0, len(g:plugs)]
2118 | for [name, spec] in items(g:plugs)
2119 | if !s:is_managed(name)
2120 | call add(dirs, spec.dir)
2121 | else
2122 | let [err, clean] = s:git_validate(spec, 1)
2123 | if clean
2124 | let errs[spec.dir] = s:lines(err)[0]
2125 | else
2126 | call add(dirs, spec.dir)
2127 | endif
2128 | endif
2129 | let cnt += 1
2130 | call s:progress_bar(2, repeat('=', cnt), total)
2131 | normal! 2G
2132 | redraw
2133 | endfor
2134 |
2135 | let allowed = {}
2136 | for dir in dirs
2137 | let allowed[s:dirpath(fnamemodify(dir, ':h:h'))] = 1
2138 | let allowed[dir] = 1
2139 | for child in s:glob_dir(dir)
2140 | let allowed[child] = 1
2141 | endfor
2142 | endfor
2143 |
2144 | let todo = []
2145 | let found = sort(s:glob_dir(g:plug_home))
2146 | while !empty(found)
2147 | let f = remove(found, 0)
2148 | if !has_key(allowed, f) && isdirectory(f)
2149 | call add(todo, f)
2150 | call append(line('$'), '- ' . f)
2151 | if has_key(errs, f)
2152 | call append(line('$'), ' ' . errs[f])
2153 | endif
2154 | let found = filter(found, 'stridx(v:val, f) != 0')
2155 | end
2156 | endwhile
2157 |
2158 | 4
2159 | redraw
2160 | if empty(todo)
2161 | call append(line('$'), 'Already clean.')
2162 | else
2163 | let s:clean_count = 0
2164 | call append(3, ['Directories to delete:', ''])
2165 | redraw!
2166 | if a:force || s:ask_no_interrupt('Delete all directories?')
2167 | call s:delete([6, line('$')], 1)
2168 | else
2169 | call setline(4, 'Cancelled.')
2170 | nnoremap d :set opfunc=delete_opg@
2171 | nmap dd d_
2172 | xnoremap d :call delete_op(visualmode(), 1)
2173 | echo 'Delete the lines (d{motion}) to delete the corresponding directories'
2174 | endif
2175 | endif
2176 | 4
2177 | setlocal nomodifiable
2178 | endfunction
2179 |
2180 | function! s:delete_op(type, ...)
2181 | call s:delete(a:0 ? [line("'<"), line("'>")] : [line("'["), line("']")], 0)
2182 | endfunction
2183 |
2184 | function! s:delete(range, force)
2185 | let [l1, l2] = a:range
2186 | let force = a:force
2187 | while l1 <= l2
2188 | let line = getline(l1)
2189 | if line =~ '^- ' && isdirectory(line[2:])
2190 | execute l1
2191 | redraw!
2192 | let answer = force ? 1 : s:ask('Delete '.line[2:].'?', 1)
2193 | let force = force || answer > 1
2194 | if answer
2195 | call s:rm_rf(line[2:])
2196 | setlocal modifiable
2197 | call setline(l1, '~'.line[1:])
2198 | let s:clean_count += 1
2199 | call setline(4, printf('Removed %d directories.', s:clean_count))
2200 | setlocal nomodifiable
2201 | endif
2202 | endif
2203 | let l1 += 1
2204 | endwhile
2205 | endfunction
2206 |
2207 | function! s:upgrade()
2208 | echo 'Downloading the latest version of vim-plug'
2209 | redraw
2210 | let tmp = tempname()
2211 | let new = tmp . '/plug.vim'
2212 |
2213 | try
2214 | let out = s:system(printf('git clone --depth 1 %s %s', s:plug_src, tmp))
2215 | if v:shell_error
2216 | return s:err('Error upgrading vim-plug: '. out)
2217 | endif
2218 |
2219 | if readfile(s:me) ==# readfile(new)
2220 | echo 'vim-plug is already up-to-date'
2221 | return 0
2222 | else
2223 | call rename(s:me, s:me . '.old')
2224 | call rename(new, s:me)
2225 | unlet g:loaded_plug
2226 | echo 'vim-plug has been upgraded'
2227 | return 1
2228 | endif
2229 | finally
2230 | silent! call s:rm_rf(tmp)
2231 | endtry
2232 | endfunction
2233 |
2234 | function! s:upgrade_specs()
2235 | for spec in values(g:plugs)
2236 | let spec.frozen = get(spec, 'frozen', 0)
2237 | endfor
2238 | endfunction
2239 |
2240 | function! s:status()
2241 | call s:prepare()
2242 | call append(0, 'Checking plugins')
2243 | call append(1, '')
2244 |
2245 | let ecnt = 0
2246 | let unloaded = 0
2247 | let [cnt, total] = [0, len(g:plugs)]
2248 | for [name, spec] in items(g:plugs)
2249 | let is_dir = isdirectory(spec.dir)
2250 | if has_key(spec, 'uri')
2251 | if is_dir
2252 | let [err, _] = s:git_validate(spec, 1)
2253 | let [valid, msg] = [empty(err), empty(err) ? 'OK' : err]
2254 | else
2255 | let [valid, msg] = [0, 'Not found. Try PlugInstall.']
2256 | endif
2257 | else
2258 | if is_dir
2259 | let [valid, msg] = [1, 'OK']
2260 | else
2261 | let [valid, msg] = [0, 'Not found.']
2262 | endif
2263 | endif
2264 | let cnt += 1
2265 | let ecnt += !valid
2266 | " `s:loaded` entry can be missing if PlugUpgraded
2267 | if is_dir && get(s:loaded, name, -1) == 0
2268 | let unloaded = 1
2269 | let msg .= ' (not loaded)'
2270 | endif
2271 | call s:progress_bar(2, repeat('=', cnt), total)
2272 | call append(3, s:format_message(valid ? '-' : 'x', name, msg))
2273 | normal! 2G
2274 | redraw
2275 | endfor
2276 | call setline(1, 'Finished. '.ecnt.' error(s).')
2277 | normal! gg
2278 | setlocal nomodifiable
2279 | if unloaded
2280 | echo "Press 'L' on each line to load plugin, or 'U' to update"
2281 | nnoremap L :call status_load(line('.'))
2282 | xnoremap L :call status_load(line('.'))
2283 | end
2284 | endfunction
2285 |
2286 | function! s:extract_name(str, prefix, suffix)
2287 | return matchstr(a:str, '^'.a:prefix.' \zs[^:]\+\ze:.*'.a:suffix.'$')
2288 | endfunction
2289 |
2290 | function! s:status_load(lnum)
2291 | let line = getline(a:lnum)
2292 | let name = s:extract_name(line, '-', '(not loaded)')
2293 | if !empty(name)
2294 | call plug#load(name)
2295 | setlocal modifiable
2296 | call setline(a:lnum, substitute(line, ' (not loaded)$', '', ''))
2297 | setlocal nomodifiable
2298 | endif
2299 | endfunction
2300 |
2301 | function! s:status_update() range
2302 | let lines = getline(a:firstline, a:lastline)
2303 | let names = filter(map(lines, 's:extract_name(v:val, "[x-]", "")'), '!empty(v:val)')
2304 | if !empty(names)
2305 | echo
2306 | execute 'PlugUpdate' join(names)
2307 | endif
2308 | endfunction
2309 |
2310 | function! s:is_preview_window_open()
2311 | silent! wincmd P
2312 | if &previewwindow
2313 | wincmd p
2314 | return 1
2315 | endif
2316 | endfunction
2317 |
2318 | function! s:find_name(lnum)
2319 | for lnum in reverse(range(1, a:lnum))
2320 | let line = getline(lnum)
2321 | if empty(line)
2322 | return ''
2323 | endif
2324 | let name = s:extract_name(line, '-', '')
2325 | if !empty(name)
2326 | return name
2327 | endif
2328 | endfor
2329 | return ''
2330 | endfunction
2331 |
2332 | function! s:preview_commit()
2333 | if b:plug_preview < 0
2334 | let b:plug_preview = !s:is_preview_window_open()
2335 | endif
2336 |
2337 | let sha = matchstr(getline('.'), '^ \X*\zs[0-9a-f]\{7,9}')
2338 | if empty(sha)
2339 | return
2340 | endif
2341 |
2342 | let name = s:find_name(line('.'))
2343 | if empty(name) || !has_key(g:plugs, name) || !isdirectory(g:plugs[name].dir)
2344 | return
2345 | endif
2346 |
2347 | if exists('g:plug_pwindow') && !s:is_preview_window_open()
2348 | execute g:plug_pwindow
2349 | execute 'e' sha
2350 | else
2351 | execute 'pedit' sha
2352 | wincmd P
2353 | endif
2354 | setlocal previewwindow filetype=git buftype=nofile nobuflisted modifiable
2355 | try
2356 | let [sh, shellcmdflag, shrd] = s:chsh(1)
2357 | let cmd = 'cd '.s:shellesc(g:plugs[name].dir).' && git show --no-color --pretty=medium '.sha
2358 | if s:is_win
2359 | let batchfile = tempname().'.bat'
2360 | call writefile(['@echo off', cmd], batchfile)
2361 | let cmd = batchfile
2362 | endif
2363 | execute 'silent %!' cmd
2364 | finally
2365 | let [&shell, &shellcmdflag, &shellredir] = [sh, shellcmdflag, shrd]
2366 | if s:is_win
2367 | call delete(batchfile)
2368 | endif
2369 | endtry
2370 | setlocal nomodifiable
2371 | nnoremap q :q
2372 | wincmd p
2373 | endfunction
2374 |
2375 | function! s:section(flags)
2376 | call search('\(^[x-] \)\@<=[^:]\+:', a:flags)
2377 | endfunction
2378 |
2379 | function! s:format_git_log(line)
2380 | let indent = ' '
2381 | let tokens = split(a:line, nr2char(1))
2382 | if len(tokens) != 5
2383 | return indent.substitute(a:line, '\s*$', '', '')
2384 | endif
2385 | let [graph, sha, refs, subject, date] = tokens
2386 | let tag = matchstr(refs, 'tag: [^,)]\+')
2387 | let tag = empty(tag) ? ' ' : ' ('.tag.') '
2388 | return printf('%s%s%s%s%s (%s)', indent, graph, sha, tag, subject, date)
2389 | endfunction
2390 |
2391 | function! s:append_ul(lnum, text)
2392 | call append(a:lnum, ['', a:text, repeat('-', len(a:text))])
2393 | endfunction
2394 |
2395 | function! s:diff()
2396 | call s:prepare()
2397 | call append(0, ['Collecting changes ...', ''])
2398 | let cnts = [0, 0]
2399 | let bar = ''
2400 | let total = filter(copy(g:plugs), 's:is_managed(v:key) && isdirectory(v:val.dir)')
2401 | call s:progress_bar(2, bar, len(total))
2402 | for origin in [1, 0]
2403 | let plugs = reverse(sort(items(filter(copy(total), (origin ? '' : '!').'(has_key(v:val, "commit") || has_key(v:val, "tag"))'))))
2404 | if empty(plugs)
2405 | continue
2406 | endif
2407 | call s:append_ul(2, origin ? 'Pending updates:' : 'Last update:')
2408 | for [k, v] in plugs
2409 | let range = origin ? '..origin/'.v.branch : 'HEAD@{1}..'
2410 | let diff = s:system_chomp('git log --graph --color=never '.join(map(['--pretty=format:%x01%h%x01%d%x01%s%x01%cr', range], 's:shellesc(v:val)')), v.dir)
2411 | if !empty(diff)
2412 | let ref = has_key(v, 'tag') ? (' (tag: '.v.tag.')') : has_key(v, 'commit') ? (' '.v.commit) : ''
2413 | call append(5, extend(['', '- '.k.':'.ref], map(s:lines(diff), 's:format_git_log(v:val)')))
2414 | let cnts[origin] += 1
2415 | endif
2416 | let bar .= '='
2417 | call s:progress_bar(2, bar, len(total))
2418 | normal! 2G
2419 | redraw
2420 | endfor
2421 | if !cnts[origin]
2422 | call append(5, ['', 'N/A'])
2423 | endif
2424 | endfor
2425 | call setline(1, printf('%d plugin(s) updated.', cnts[0])
2426 | \ . (cnts[1] ? printf(' %d plugin(s) have pending updates.', cnts[1]) : ''))
2427 |
2428 | if cnts[0] || cnts[1]
2429 | nnoremap :silent! call preview_commit()
2430 | nnoremap o :silent! call preview_commit()
2431 | endif
2432 | if cnts[0]
2433 | nnoremap X :call revert()
2434 | echo "Press 'X' on each block to revert the update"
2435 | endif
2436 | normal! gg
2437 | setlocal nomodifiable
2438 | endfunction
2439 |
2440 | function! s:revert()
2441 | if search('^Pending updates', 'bnW')
2442 | return
2443 | endif
2444 |
2445 | let name = s:find_name(line('.'))
2446 | if empty(name) || !has_key(g:plugs, name) ||
2447 | \ input(printf('Revert the update of %s? (y/N) ', name)) !~? '^y'
2448 | return
2449 | endif
2450 |
2451 | call s:system('git reset --hard HEAD@{1} && git checkout '.s:esc(g:plugs[name].branch).' --', g:plugs[name].dir)
2452 | setlocal modifiable
2453 | normal! "_dap
2454 | setlocal nomodifiable
2455 | echo 'Reverted'
2456 | endfunction
2457 |
2458 | function! s:snapshot(force, ...) abort
2459 | call s:prepare()
2460 | setf vim
2461 | call append(0, ['" Generated by vim-plug',
2462 | \ '" '.strftime("%c"),
2463 | \ '" :source this file in vim to restore the snapshot',
2464 | \ '" or execute: vim -S snapshot.vim',
2465 | \ '', '', 'PlugUpdate!'])
2466 | 1
2467 | let anchor = line('$') - 3
2468 | let names = sort(keys(filter(copy(g:plugs),
2469 | \'has_key(v:val, "uri") && !has_key(v:val, "commit") && isdirectory(v:val.dir)')))
2470 | for name in reverse(names)
2471 | let sha = s:system_chomp('git rev-parse --short HEAD', g:plugs[name].dir)
2472 | if !empty(sha)
2473 | call append(anchor, printf("silent! let g:plugs['%s'].commit = '%s'", name, sha))
2474 | redraw
2475 | endif
2476 | endfor
2477 |
2478 | if a:0 > 0
2479 | let fn = expand(a:1)
2480 | if filereadable(fn) && !(a:force || s:ask(a:1.' already exists. Overwrite?'))
2481 | return
2482 | endif
2483 | call writefile(getline(1, '$'), fn)
2484 | echo 'Saved as '.a:1
2485 | silent execute 'e' s:esc(fn)
2486 | setf vim
2487 | endif
2488 | endfunction
2489 |
2490 | function! s:split_rtp()
2491 | return split(&rtp, '\\\@ :noh
27 | nmap 3 :NERDTreeFind
28 | nmap a :set filetype=awk
29 | nmap c :set filetype=css
30 | nmap d :set filetype=htmldjango
31 | nmap e :set filetype=sed
32 | nmap g :set filetype=go
33 | nmap h :set filetype=html
34 | nmap j :set filetype=javascript
35 | nmap l :set filetype=lua
36 | nmap m :set filetype=markdown
37 | nmap p :set filetype=php
38 | nmap s :set filetype=sh
39 | nmap t :set filetype=txt
40 | nmap v :set filetype=vim
41 | nmap y :set filetype=python
42 | set ambiwidth=double
43 | set autoread
44 | set autowriteall
45 | set backup
46 | set bs+=start
47 | set smartindent cindent autoindent
48 | set shiftwidth=4 tabstop=4 smarttab
49 | set clipboard+=unnamed
50 | set complete-=i
51 | set cursorline
52 | set expandtab
53 | set encoding=utf-8 fileencodings=ucs-bom,utf-8,cp936 fileencoding=utf-8
54 | set foldmethod=manual
55 | set hidden hlsearch
56 | set ignorecase smartcase
57 | set noautochdir
58 | set noshowmatch
59 | set nowrapscan
60 | set number
61 | set pastetoggle=
62 | set path+=./model/,./ctrl/,./lib/,*/templates/,*/static/,..,*/src/main/java/
63 | set printoptions=formfeed:y,header:0,paper:A4,duplex:off,syntax:n
64 | set scrolloff=1
65 | set shell=/bin/bash
66 | set nocompatible
67 | set showcmd " Show cmd in vim-cmdline.
68 | set t_Co=256 " Make vim look better in putty.
69 | set textwidth=0
70 | set undodir=~/.vimtmp/undodir
71 | \ directory=~/.vimtmp/directory
72 | \ backupdir=~/.vimtmp/backupdir
73 | \ viewdir=~/.vimtmp/view
74 | \ undofile
75 | set vb t_vb= " Turn off bi-sound of vim.
76 | set wildignore+=*.git\\*,*.tgz,*.zip,*.url,*.pyc,*.class
77 | syntax on
78 |
79 | "
80 | "" statusline
81 | "
82 | " function Version ()
83 | " return system("grep -o '^v[0-9]*' ~/.vim/version|tr -d '\n'")
84 | " endfunction
85 | " set statusline=(Vide.%{Version()})\ \ %<%f
86 | " set statusline+=%w%h%m%r
87 | " set statusline+=\ %{getcwd()}
88 | " set statusline+=\ [%{&ff}:%{&fenc}:%Y]
89 | " set statusline+=%=%-14.(%l,%c%V%)\ %p%%
90 | set laststatus=2
91 | let g:lightline = {
92 | \ 'active': {
93 | \ 'left': [ [ 'mode', 'paste' ],
94 | \ [ 'readonly', 'filename', 'modified', 'method' ] ]
95 | \ },
96 | \ 'component': {
97 | \ 'lineinfo': "%{line('.') . '/' . line('$')}",
98 | \ }
99 | \ }
100 |
101 |
102 | "
103 | " vim-plug
104 | "
105 | call plug#begin('~/.vim/plug')
106 | " Plug 'vim-scripts/taglist.vim'
107 | " Plug 'fatih/vim-go', { 'do': ':GoUpdateBinaries' }
108 | Plug 'github/copilot'
109 | Plug 'itchyny/lightline.vim'
110 | Plug 'NLKNguyen/papercolor-theme'
111 | Plug 'wgwoods/vim-systemd-syntax'
112 | Plug 'leafgarland/typescript-vim'
113 | Plug 'xolox/vim-misc'
114 | Plug 'xolox/vim-lua-ftplugin'
115 | Plug 'pangloss/vim-javascript'
116 | Plug 'airblade/vim-gitgutter'
117 | Plug 'posva/vim-vue'
118 | Plug 'alvan/vim-php-manual'
119 | Plug 'cespare/vim-toml'
120 | Plug 'godlygeek/tabular'
121 | Plug 'kien/ctrlp.vim'
122 | Plug 'mzlogin/vim-markdown-toc'
123 | Plug 'plasticboy/vim-markdown'
124 | Plug 'roxma/vim-paste-easy'
125 | Plug 'scrooloose/nerdtree'
126 | Plug 'tmhedberg/matchit'
127 | Plug 'tpope/vim-commentary'
128 | " Plug 'vim-syntastic/syntastic'
129 | call plug#end()
130 |
131 | set background=dark
132 | colorscheme PaperColor
133 |
134 | let g:vim_markdown_folding_disabled = 1
135 | let g:gitgutter_max_signs=10000
136 |
137 | "
138 | " syntastic
139 | "
140 | let g:syntastic_always_populate_loc_list = 1
141 | let g:syntastic_quiet_messages = { "level": "errors" }
142 | let g:syntastic_go_checkers = ['golint', 'govet', 'errcheck']
143 | let g:syntastic_mode_map = { 'mode': 'active', 'passive_filetypes': ['go'] }
144 |
145 | "
146 | " go-vim
147 | "
148 | let g:go_version_warning = 0
149 | let g:go_fmt_autosave = 1
150 | let g:go_fmt_command = "goimports"
151 |
152 | "
153 | " NERDTree
154 | "
155 | let g:NERDTreeDirArrowExpandable = '@'
156 | " let g:NERDTreeNodeDelimiter = "\u00a0"
157 | let g:NERDTreeDirArrowCollapsible = '-'
158 | let g:NERDTreeShowHidden = 0
159 | let g:NERDTreeBookmarksFile = $HOME.'/.vimtmp/NerdBookmarks.txt'
160 | let g:NERDTreeShowBookmarks = 1
161 | let g:NERDTreeShowFiles = 1
162 | let g:NERDTreeShowLineNumbers = 0
163 | let g:NERDTreeWinSize = 29
164 | let g:NERDTreeMinimalUI = 1
165 | let g:NERDTreeDirArrows = 1
166 | let g:NERDTreeIgnore = [
167 | \ '.*\.class',
168 | \ '.*\.pyc',
169 | \ '.*\.chm',
170 | \ '.*\.ttf',
171 | \ '.*\.lnk',
172 | \ '.*\.cproj',
173 | \ '.*\.exe',
174 | \ '.*\.dll',
175 | \ '.*\.out',
176 | \ '.*\.files',
177 | \ '.*\.zip',
178 | \ '.*\.rar',
179 | \ '.*\.gif'
180 | \ ]
181 | let g:NERDTreeIndicatorMapCustom = {
182 | \ "Modified" : "✹",
183 | \ "Staged" : "✚",
184 | \ "Untracked" : "✭",
185 | \ "Renamed" : "➜",
186 | \ "Unmerged" : "═",
187 | \ "Deleted" : "✖",
188 | \ "Dirty" : "✗",
189 | \ "Clean" : "✔︎",
190 | \ "Unknown" : "?"
191 | \ }
192 |
193 | "
194 | " ctrlp
195 | "
196 | " Making CtrlP.vim load 100x faster — A Tiny Piece of Vim — Medm
197 | " https://medium.com/a-tiny-piece-of-vim/making-ctrlp-vim-load-100x-faster-7a722fae7df6#.emcvo89nx
198 | let g:ctrlp_user_command = [
199 | \ '.git/',
200 | \ 'git --git-dir=%s/.git ls-files -oc --exclude-standard'
201 | \ ]
202 | let g:ctrlp_match_window = 'bottom,order:btt,min:5,max:5,results:10'
203 | let g:ctrlp_cmd = 'CtrlPMixed'
204 | let g:ctrlp_mruf_default_order = 1
205 |
206 | "
207 | " utime.vim
208 | "
209 | let g:timeStampFormat = '170101'
210 | let g:timeStampString = '%y%m%d'
211 | let g:timeStampLeader = 'version'
212 |
--------------------------------------------------------------------------------
/vimrc_agiclass:
--------------------------------------------------------------------------------
1 | "检查plug vim管理器是否存在
2 | if empty(glob('$HOME/.vim/autoload/plug.vim'))
3 | let command = 'curl -fLo ~/.vim/autoload/plug.vim --create-dirs https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim'
4 | echo 'plug vim not exitst'
5 | execute '!'.command
6 | endif
7 | "使用plug包管理器管理插件包
8 | call plug#begin('~/.vim/plugged')
9 | "开始的导航界面
10 | Plug 'mhinz/vim-startify'
11 | "轻量彩色工具栏
12 | Plug 'itchyny/lightline.vim'
13 | "添加文件icon支持
14 | Plug 'ryanoasis/vim-devicons'
15 | "树状的文件浏览窗口
16 | Plug 'preservim/nerdtree'
17 | Plug 'majutsushi/tagbar'
18 | "格式代码,对齐符号或空格
19 | Plug 'godlygeek/tabular'
20 | "编辑括号、引号和标签等包围结构
21 | Plug 'tpope/vim-surround'
22 | "整行或整段注释代码
23 | Plug 'tpope/vim-commentary'
24 | "增强.命令的重复
25 | Plug 'tpope/vim-repeat'
26 | "用括号做常用命名的mapping
27 | Plug 'tpope/vim-unimpaired'
28 | "基于搜字符来快速跳转
29 | Plug 'easymotion/vim-easymotion'
30 | "主题
31 | Plug 'arcticicestudio/nord-vim'
32 | Plug 'NLKNguyen/papercolor-theme'
33 | "高亮多个选中的关键字
34 | Plug 'lfv89/vim-interestingwords'
35 | "自动补全的括号
36 | Plug 'jiangmiao/auto-pairs'
37 | "常用的snippets
38 | Plug 'honza/vim-snippets'
39 | "C++的语法高亮
40 | Plug 'octol/vim-cpp-enhanced-highlight', {'for': 'cpp'}
41 | call plug#end()
42 |
43 | "开启文件插件
44 | filetype plugin on
45 | "设置高亮主题和风格
46 | set background=dark
47 | set termguicolors
48 | " colorscheme nord
49 | colorscheme PaperColor
50 | hi Normal ctermbg=NONE guibg=NONE
51 | hi LineNr ctermbg=NONE guibg=NONE
52 | hi SignColumn ctermbg=NONE guibg=NONE
53 | "打开 matchit.vim, 增强%匹配跳转的功能
54 | runtime macros/matchit.vim
55 | set clipboard=unnamed
56 | "语法高亮
57 | syntax on
58 | set re=0
59 | "显示行号
60 | set number
61 | set relativenumber
62 | "显示光标所在位置的行号和列号
63 | set ruler
64 | "高亮光标所在行
65 | set cursorline
66 | "隐藏鼠标指针
67 | set mousehide
68 | "设置帮助语言为中文
69 | set helplang=ch
70 | "设置鼠标可用
71 | set mouse=a
72 | "设置显示正在输入的命令
73 | set showcmd
74 | "不使用缓存文件
75 | set nobackup
76 | set noswapfile
77 | "快速的响应时间
78 | set updatetime=300
79 | "打开命令菜单的补全
80 | set wildmenu
81 | set wildmode=longest:list,full
82 | "关闭高亮搜索
83 | set nohlsearch
84 | "设置 leader key
85 | let mapleader=";"
86 | "设置字符编码
87 | set encoding=utf-8
88 | "倒数第二行显示状态行
89 | set laststatus=2
90 | "设置tab为2个空格
91 | set tabstop=2
92 | set expandtab
93 | "设置缩进为2个空格
94 | set shiftwidth=2
95 | "强制写入文件
96 | cmap w!! w !sudo tee % > /dev/null
97 | "设置跳转窗口的快捷键
98 | nmap j
99 | nmap k
100 | nmap l
101 | nmap h
102 | "设置折叠代码的快捷键
103 | nnoremap za
104 | "设置保存和退出的快捷键
105 | nnoremap q :q
106 | nnoremap w :w
107 | nnoremap x :x
108 | "设置关闭tab的快捷键
109 | nnoremap :tabclose
110 | nnoremap t :TagbarToggle
111 | let g:tagbar_width=25
112 | "设置快捷键启动NERDTree
113 | nnoremap ' :NERDTreeToggle
114 | let g:NERDTreeWinSize=25
115 | let g:NERDTreeDirArrowExpandable = ''
116 | let g:NERDTreeDirArrowCollapsible = ''
117 | let g:webdevicons_conceal_nerdtree_brackets = 1
118 | if exists('g:loaded_webdevicons')
119 | call webdevicons#refresh()
120 | endif
121 |
122 | let g:lightline = {
123 | \ 'active': {
124 | \ 'left': [ [ 'mode', 'paste' ],
125 | \ [ 'readonly', 'filename', 'modified', 'method' ] ]
126 | \ },
127 | \ 'component': {
128 | \ 'lineinfo': "%{line('.') . '/' . line('$')}",
129 | \ }
130 | \ }
131 | let g:EasyMotion_leader_key = 'f'
132 | "设置Interesting Word快捷键
133 | nnoremap h :call InterestingWords('n')
134 | nnoremap c :call UncolorAllWords()
135 | nnoremap n :call WordNavigation('forward')
136 | nnoremap N :call WordNavigation('backward')
137 | let g:interestingWordsTermColors = ['154', '121', '211', '137', '214', '222']
138 | let g:interestingWordsGUIColors = ['#8CCBEA', '#A4E57E', '#FFDB72', '#FF7272', '#FFB3FF', '#9999FF']
139 |
140 | autocmd Filetype python setlocal ts=4 sw=4 expandtab
141 | autocmd FileType python,shell set commentstring=#\ %s
142 | autocmd FileType c,cpp set commentstring=//\ %s
143 |
144 |
--------------------------------------------------------------------------------