<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>vim on Reorx’s Forge</title><link>https://reorx.com/tags/vim/</link><description>Recent content in vim on Reorx’s Forge</description><image><url>https://reorx.com/images/forge-v2-compat.svg</url><link>https://reorx.com/images/forge-v2-compat.svg</link></image><generator>Hugo -- gohugo.io</generator><lastBuildDate>Sat, 07 May 2022 00:00:00 +0800</lastBuildDate><atom:link href="https://reorx.com/tags/vim/feed.xml" rel="self" type="application/rss+xml"/><item><title>我的 Vim 自动补全配置变迁史</title><link>https://reorx.com/blog/the-history-of-my-vim-completion-config/</link><pubDate>Sat, 07 May 2022 00:00:00 +0800</pubDate><guid>https://reorx.com/blog/the-history-of-my-vim-completion-config/</guid><description>记录了从 2017 年至今，我的 Vim 自动补全配置的每次变更，从中窥见 Vim 生态发展的一角，也纪念这些曾经给我带来过便利，最终在技术发展中被轮替的插件</description><content:encoded><![CDATA[<p>Vim 是我系统学习的第一个终端编辑器，从学生时代至今，我几乎每天都会使用到它（长时间写前端代码时除外）。</p>
<p>自动补全（auto completion）大概是每个 Vim 用户在掌握了基本用法后，第一个想要进阶配置的功能。这篇文章记录了从 2017 年至今，我的 Vim 自动补全配置的每次变更，从中窥见 Vim 生态发展的一角，也纪念这些曾经给我带来过便利，最终在技术发展中被轮替的插件。</p>
<h2 id="before-2017">Before 2017</h2>
<p>2017 年我从 Vim 切换到 <a href="https://neovim.io/">Neovim</a>（下文简称 nvim），除了增加 nvim 特殊的 <code>init.vim</code>，基本沿用了以往的配置和插件。</p>
<p>彼时我使用的语言以 Python 为主，自动补全插件为 <a href="https://github.com/davidhalter/jedi-vim">jedi-vim</a>。</p>
<p>我对 jedi-vim 的了解最早可以追溯到 2012 年，那时还没有 LSP 的概念。开发者们针对自己的需求，编写如语法增强、文档查看、自动补全等各类插件，非常零散。jedi-vim 对这些插件的功能进行了重构和集成，提供了开箱即用的统一解决方案，一经推出便广受好评，成为使用 Vim 进行 Python 开发的标配。在后来的十年里，它的初心始终不变，得到持续的维护并沿用至今。</p>
<p>jedi-vim 的流行和长寿或许可以说明一个观点，即易用和功能全面才是软件流行的第一因素，无论它的实现有多么不优雅、效率有多么低，只要是能用的、可接受的就行，用户在使用体验上得到满足后，对于小问题的容忍度是相当高的。</p>
<h2 id="2017">2017</h2>
<p>还是 2017 年，在切换到 nvim 后不久，我发现了 <a href="https://github.com/Shougo/deoplete.nvim">deoplete</a> 插件，经过一番尝试将 jedi-vim 替换成了 deoplete + deoplete-jedi。</p>
<p>Commit: <a href="https://github.com/reorx/dotfiles/commit/0760ba6f7d11526e38e15b36a0d1db8709834825">0760ba6f7d11526e38e15b36a0d1db8709834825</a></p>
<blockquote>
<p><strong>use deoplete, remove jedi-vim</strong></p>
<p><em>committed on Jun 28, 2017</em></p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-diff" data-lang="diff"><span class="line"><span class="cl"><span class="gd">-Plug &#39;davidhalter/jedi-vim&#39;, { &#39;for&#39;: &#39;python&#39; }
</span></span></span><span class="line"><span class="cl"><span class="gd"></span><span class="gi">+Plug &#39;Shougo/deoplete.nvim&#39;, { &#39;do&#39;: &#39;:UpdateRemotePlugins&#39; }
</span></span></span><span class="line"><span class="cl"><span class="gi">+Plug &#39;zchee/deoplete-jedi&#39;, { &#39;for&#39;: &#39;python&#39; }
</span></span></span></code></pre></div><p>deoplete 的目标是提供一个通用的异步自动补全框架，这在设计理念上是一个巨大的进步。jedi-vim 虽然开箱即用，但却是一堆粘合在一起的 spaghetti code，不仅随着项目功能的增加变得越发庞大和迟缓（这是我想要离开 jedi-vim 的主要原因，文件一大各种操作都变得肉眼可见的慢），代码的可读性也非常糟糕，难以维护和参与。而 deoplete 本身并不提供针对任何语言的分析能力，只专注于与 nvim 的整合和 completion source 的调度，并且利用 nvim 的异步功能（后来 vim 8 也推出了自己的 async 接口），大大提升了补全的流畅度。</p>
<p>但 deoplete 也有着自身的局限性。首先配置变得复杂且麻烦，用户得理解其架构和设计，学会如何通过 deoplete 对接编程语言的 completion source。为了使检查结果的提示贴合自己的使用习惯，还要再去学习 completion source 的配置，每个语言的实现不同，配置也不一样。</p>
<blockquote>
<p>当时我却没有料到，配置复杂的问题在 LSP 时代不仅没能得到解决，反而变本加厉，直到本文完成时也依旧是使用者的巨大痛点</p>
</blockquote>
<p>deoplete 的第二个问题是，它只专注在 completion，缺少对于 go to definition 和显示 function siguature 等功能的支持，这对于从 jedi-vim 的 all-in-one 体验切换过来的我，显然是个巨大的落差。好在我找到了其他插件来解决这些问题。</p>
<p>对于 “go to definition”，通过装回 jedi-vim 并打开无补全模式可以解决。这样既可以使用 jedi-vim 提供的 go to definition 等辅助功能，也不会与 deoplete 的补全产生冲突。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-vim" data-lang="vim"><span class="line"><span class="cl"><span class="nx">Plug</span> <span class="s1">&#39;davidhalter/jedi-vim&#39;</span><span class="p">,</span> { <span class="s1">&#39;for&#39;</span>: <span class="s1">&#39;python&#39;</span> }<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c">
</span></span></span><span class="line"><span class="cl"><span class="c">&#34; jedi (only for go to definition)</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">let</span> <span class="nx">g</span>:<span class="nx">jedi</span>#<span class="nx">completions_enabled</span> <span class="p">=</span> <span class="m">0</span><span class="err">
</span></span></span></code></pre></div><p>对于 “function signature”，我找到了 deoplete 作者的另一个插件 <a href="https://github.com/Shougo/echodoc.vim">echodoc</a> 来实现。它将函数的签名信息显示在 cmd 区域，规避了 deoplete 占用 <code>completeopt</code> 导致编辑界面无法显示补全菜单以外的其他信息的问题。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-vim" data-lang="vim"><span class="line"><span class="cl"><span class="nx">Plug</span> <span class="s1">&#39;Shougo/echodoc.vim&#39;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c">
</span></span></span><span class="line"><span class="cl"><span class="c">&#34; echodoc</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="nx">set</span> <span class="nx">noshowmode</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">let</span> <span class="nx">g</span>:<span class="nx">echodoc</span>#<span class="nx">enable_at_startup</span><span class="p">=</span><span class="m">1</span><span class="err">
</span></span></span></code></pre></div><h2 id="2018">2018</h2>
<p>2018 年是里程碑式的一年，<a href="https://microsoft.github.io/language-server-protocol/">Language Server Protocol</a> 的生态逐渐成熟，新的补全工具涌现。我对 LSP 感到相当兴奋和好奇，迫不及待地从 deoplete 更换到了对 LSP 有更好支持的 <a href="https://github.com/ncm2/ncm2">ncm2</a>。</p>
<p>Commit: <a href="https://github.com/reorx/dotfiles/commit/7a1442c2334673ac17162c101663e220ef43a3c8">7a1442c2334673ac17162c101663e220ef43a3c8</a></p>
<blockquote>
<p><strong>nvim: update completion plugins (a lot!)</strong></p>
<ul>
<li>move and reorg completion plugins definitions and configurations</li>
<li>use LSP completion instead of deoplete</li>
<li>remove eslint from ale_linters</li>
<li>enable virtualenv display for airline</li>
<li>still working on passing settings to pyls through LanguageClient-neovim</li>
</ul>
<p><em>committed on Dec 7, 2018</em></p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-diff" data-lang="diff"><span class="line"><span class="cl"><span class="gd">-Plug &#39;Shougo/deoplete.nvim&#39;, { &#39;do&#39;: &#39;:UpdateRemotePlugins&#39; }
</span></span></span><span class="line"><span class="cl"><span class="gd">-Plug &#39;zchee/deoplete-jedi&#39;, { &#39;for&#39;: &#39;python&#39; }
</span></span></span><span class="line"><span class="cl"><span class="gd"></span><span class="gi">+Plug &#39;ervandew/supertab&#39;
</span></span></span><span class="line"><span class="cl"><span class="gi">+Plug &#39;ncm2/ncm2&#39;
</span></span></span><span class="line"><span class="cl"><span class="gi">+Plug &#39;roxma/nvim-yarp&#39;
</span></span></span><span class="line"><span class="cl"><span class="gi">+Plug &#39;autozimu/LanguageClient-neovim&#39;, { &#39;branch&#39;: &#39;next&#39;, &#39;do&#39;: &#39;bash install.sh&#39;, }
</span></span></span></code></pre></div><p>从设计理念上看，ncm2 与 deoplete 并无差别，都是通用的异步自动补全框架，唯有与<a href="https://en.wikipedia.org/wiki/Static_program_analysis">静态分析</a>器的集成方式不同，deoplete 是自己的私有协议，ncm2 则拥抱了更加通用的业界标准 LSP。</p>
<p>我为 deoplete 的作者感到惋惜，他在 LSP 还不够成熟的时期，自己设计了与静态分析器的集成协议，构建了一个完整的补全插件生态<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>。还写了很多小巧实用的插件，代码也非常优美，让人感到赏心悦目。但因为 LSP 的发展和新一代更加 LSP native 的补全插件的涌现，它已不再是当下的第一选择，势必因为历史包袱而逐渐被淘汰。</p>
<p>说回 ncm2，其实它也有许多瑕疵，印象中配置过程比 deoplete 还要痛苦，但当时已经是让 nvim 用上 LSP 的最好插件了。之后我对 JetBrains 和 VSCode 的使用频率变高，疏于对 nvim 插件的持续跟进，ncm2 于是一直服役到 2021 年。</p>
<p>ncm2 出现后没过多久，<a href="https://github.com/neoclide/coc.nvim">coc</a> 也诞生了，在 2019 年成为最受人关注的 vim 补全插件，国内也看到很多文章（似乎作者就是国内开发者）。由于长期受 Webpack 和 Nodejs 技术栈的折磨，当我了解到 coc 是 Nodejs 实现的，就放弃了尝试的念头 <sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>。一想到 jedi-vim 的缓慢，我实在没办法对同样大而全的 coc 抱有足够的信心<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>。</p>
<p>P.S. 从当年的笔记中找到了所参考的项目和文章：</p>
<ul>
<li><a href="https://www.reddit.com/r/neovim/comments/8ejfg0/state_of_lsp_servers/">https://www.reddit.com/r/neovim/comments/8ejfg0/state_of_lsp_servers/</a></li>
<li><a href="https://github.com/autozimu/LanguageClient-neovim">https://github.com/autozimu/LanguageClient-neovim</a></li>
<li><a href="https://github.com/prabirshrestha/vim-lsp">https://github.com/prabirshrestha/vim-lsp</a></li>
<li><a href="https://github.com/ncm2/ncm2">https://github.com/ncm2/ncm2</a></li>
<li><a href="https://github.com/palantir/python-language-server">https://github.com/palantir/python-language-server</a></li>
</ul>
<h2 id="2021">2021</h2>
<p>2021 年的某一天，因为 ncm2 长期存在的一个小问题（现在已经忘了），我一气之下再次打开了 deoplete 的项目页面，惊喜地发现它已经完善了对 LSP 的支持，于是立刻就开始迁移，换回了我更欣赏且代码品质更胜一筹的 deoplete。</p>
<p>Commit: <a href="https://github.com/reorx/dotfiles/commit/cd044fcda603ad5b9ee16bd4d7d7873c9ade9a31">cd044fcda603ad5b9ee16bd4d7d7873c9ade9a31</a></p>
<blockquote>
<p><strong>nvim: rework on languageserver &amp; python completion</strong></p>
<p><em>committed on Mar 26, 2021</em></p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-diff" data-lang="diff"><span class="line"><span class="cl"><span class="gd">-Plug &#39;ervandew/supertab&#39;
</span></span></span><span class="line"><span class="cl"><span class="gd">-Plug &#39;ncm2/ncm2&#39;
</span></span></span><span class="line"><span class="cl"><span class="gd">-Plug &#39;roxma/nvim-yarp&#39;
</span></span></span><span class="line"><span class="cl"><span class="gd">-Plug &#39;autozimu/LanguageClient-neovim&#39;, { &#39;branch&#39;: &#39;next&#39;, &#39;do&#39;: &#39;bash install.sh&#39;,
</span></span></span><span class="line"><span class="cl"><span class="gd"></span><span class="gi">+Plug &#39;Shougo/deoplete.nvim&#39;, { &#39;do&#39;: &#39;:UpdateRemotePlugins&#39; }
</span></span></span><span class="line"><span class="cl"><span class="gi">+Plug &#39;prabirshrestha/vim-lsp&#39;
</span></span></span><span class="line"><span class="cl"><span class="gi">+Plug &#39;mattn/vim-lsp-settings&#39;
</span></span></span><span class="line"><span class="cl"><span class="gi">+Plug &#39;lighttiger2505/deoplete-vim-lsp&#39;
</span></span></span></code></pre></div><p>这次变更除了换回 deoplete ，还去掉了陪伴多年的 supertab，在抄了一段看不懂的配置后，实现了我更为习惯的 tab 键触发补全的方式。</p>
<h2 id="2022">2022</h2>
<p>在咖啡馆结束了一天的主要工作后，看着好友 <a href="https://github.com/xwjdsh">@iwendellsun</a> 流畅的 vim 操作，我问起了他的 nvim 自动补全配置，果然有许多我从未听过的东西。于是趁此机会赶紧向他请教，在他的指导下完成了 2022 年的配置升级。</p>
<p>Commit: <a href="https://github.com/reorx/dotfiles/commit/3de43d030ca40b498911c6752a7396af38202fe6">3de43d030ca40b498911c6752a7396af38202fe6</a></p>
<blockquote>
<p><strong>nvim: use nvim-cmp for completion</strong></p>
<p><em>committed on May 08, 2022</em></p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-diff" data-lang="diff"><span class="line"><span class="cl"><span class="gd">-Plug &#39;Shougo/deoplete.nvim&#39;, { &#39;do&#39;: &#39;:UpdateRemotePlugins&#39; }
</span></span></span><span class="line"><span class="cl"><span class="gd">-Plug &#39;prabirshrestha/vim-lsp&#39;
</span></span></span><span class="line"><span class="cl"><span class="gd">-Plug &#39;mattn/vim-lsp-settings&#39;
</span></span></span><span class="line"><span class="cl"><span class="gd">-Plug &#39;lighttiger2505/deoplete-vim-lsp&#39;
</span></span></span><span class="line"><span class="cl"><span class="gd">-Plug &#39;w0rp/ale&#39;
</span></span></span><span class="line"><span class="cl"><span class="gd">-Plug &#39;rhysd/vim-lsp-ale&#39;
</span></span></span><span class="line"><span class="cl"><span class="gd"></span><span class="gi">+Plug &#39;williamboman/nvim-lsp-installer&#39;
</span></span></span><span class="line"><span class="cl"><span class="gi">+Plug &#39;neovim/nvim-lspconfig&#39;
</span></span></span><span class="line"><span class="cl"><span class="gi">+Plug &#39;hrsh7th/cmp-nvim-lsp&#39;
</span></span></span><span class="line"><span class="cl"><span class="gi">+Plug &#39;hrsh7th/cmp-buffer&#39;
</span></span></span><span class="line"><span class="cl"><span class="gi">+Plug &#39;hrsh7th/cmp-path&#39;
</span></span></span><span class="line"><span class="cl"><span class="gi">+Plug &#39;hrsh7th/cmp-cmdline&#39;
</span></span></span><span class="line"><span class="cl"><span class="gi">+Plug &#39;hrsh7th/nvim-cmp&#39;
</span></span></span></code></pre></div><p>这次变更分以下几个方面：</p>
<ol>
<li>补全框架从 deoplete 变为 <a href="https://github.com/hrsh7th/nvim-cmp">nvim-cmp</a>，我还没细看，不过据说它就是现在的 meta &amp; state of the art.</li>
<li>LSP 集成从 vim-lsp 换成了 nvim-lspconfig。迟来的官方出品。</li>
<li>去掉了 ale 和 vim-lsp-ale。nvim-cmp 可以将 LSP client 返回的错误提示直接在行内显示，不需要再依赖 ALE 这个 linter 框架了。</li>
<li>Last but not least, 这些插件的配置语法几乎都是用 Lua 写的，这让用了 10 年 Vimscript 的我感到极度陌生和恐慌。</li>
</ol>
<p>相比之前的变更，这是唯一一次生搬硬套而非全部理解的，我想快速上车，免得被社区发展抛在了后面，现在实在没有太多精力可以悠闲地慢慢尝试。虽然有人指导免去了初次上手的痛苦，但可以预见的是，想要让这套插件和我的编程习惯完美契合，还有许多坑等着我去折腾呢。</p>
<p><strong>参考链接</strong>:</p>
<ul>
<li><a href="https://github.com/hrsh7th/nvim-cmp#setup">Setup | nvim-cmp</a>
<ul>
<li><a href="https://github.com/hrsh7th/nvim-cmp/wiki/List-of-sources">List of sources · hrsh7th/nvim-cmp Wiki</a></li>
<li><a href="https://github.com/hrsh7th/nvim-cmp/wiki/Example-mappings">Example mappings · hrsh7th/nvim-cmp Wiki</a></li>
</ul>
</li>
<li><a href="https://github.com/williamboman/nvim-lsp-installer#default-configuration">Default configuration | nvim-lsp-installer</a></li>
<li><a href="https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md">Server Configurations | nvim-lspconfig</a>
<ul>
<li><a href="https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#pylsp">pylsp</a></li>
<li><a href="https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#pyright">pyright</a></li>
<li><a href="https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#gopls">gopls</a></li>
</ul>
</li>
<li><a href="https://github.com/ray-x/lsp_signature.nvim">ray-x/lsp_signature.nvim: LSP signature hint as you type</a></li>
</ul>
<p><strong>参考配置</strong>:</p>
<ul>
<li><a href="https://github.com/xwjdsh/dotfiles/blob/master/nvim1/lua/plugins.lua">https://github.com/xwjdsh/dotfiles/blob/master/nvim1/lua/plugins.lua</a></li>
<li><a href="https://github.com/Avimitin/nvim/blob/master/lua/plugins/load.lua">https://github.com/Avimitin/nvim/blob/master/lua/plugins/load.lua</a></li>
</ul>
<h2 id="结语">结语</h2>
<p>Vim 的 LSP 插件生态还有许多有待优化的空间，开发者们对生产力的追求是永无止境的，下一个 5 年编辑器的体验会有着怎样激动人心的变化，我对此充满期待。</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://github.com/Shougo/deoplete.nvim/wiki/Completion-Sources">https://github.com/Shougo/deoplete.nvim/wiki/Completion-Sources</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>其实 Nodejs 在服务端的性能不差，但 Webpack 和 Electron 给我造成的印象已经根深蒂固，使我看到 Nodejs 就会立刻联想到 slow and bloated.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>2022-05-09: 本文发布后，许多朋友向我推荐了 coc，并告知它的速度很快，修正了我在没有使用过的情况下产生的偏见认知。&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item></channel></rss>