V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
pony279
V2EX  ›  Vim

ncm2 - 更加专注 & 可拓展的 vim/neovim 代码补全框架

  •  3
     
  •   pony279 · 2018-08-01 16:36:07 +08:00 · 9352 次点击
    这是一个创建于 2066 天前的主题,其中的信息可能已经有所发展或是发生改变。

    ncm2/ncm2 的前身是 roxma/nvim-completion-manager (ncm)

    为了更加可拓展,更加专注于代码补全,清理历史遗留代码,我重写了 ncm。部分变化列 举如下:

    • 原本内置的 sources,snippet 集成,还有 subscope 代码检测全部移出 ncm2 repo, 作为独立插件维护。ncm2 本身的代码更少,更加独立可拓展和可维护。
    • 引入 roxma/nvim-yarp,ncm2 不再管理异 步进程。source 插件可以自由选择异步模型,对 vimscript 配置更加灵活友好,对异 步操作的性能提升也更有帮助。
    • 清理代码过程中发现了一些更加干净,简单的内部实现
      • 比如移除了定时器逻辑,优化了缓存逻辑
      • 比如 completeopt 更加灵活可配置
      • 对 unicode 字符更加友好

    以上主要是实现上的变化,作为插件使用者应该感受不太清晰,以下列举一些更容易看得 见的变化:

    • Language Server Protocol 方面
      • 优化了对 autozimu/LanguageClient-neovim 集成的 snippet 支持,效果近乎完美(参数展开,auto import )。理论上可以 自由选择你喜欢的 vim snippet 引擎,比如已经支持的 Ultisnips,和 vim-snipmate。
      • 新增加对 vim-lsp 补全功能 的集成。snippet 支持同上。
    • 更加好用的 fuzzy 匹配,目前有 abbrfuzzy 和 substrfuzzy
    • 我日常写 C/C++ 比较多,所以基于 libclang 写了一个补全插件 ncm2-pyclang ,因为有了缓存,所以速 度表现会比基于 clang -cc1 实现的 [ncm-clang]( https://github.com/roxma/ncm-clang] 更好,并且支持 goto declaration 和 头文件不全。后续还有更多的优化计划。

    相关资源可以在 READMEwiki 页面 查找

    Gif demo:

    gif demo

    37 条回复    2018-12-14 00:01:18 +08:00
    BBCCBB
        1
    BBCCBB  
       2018-08-01 16:48:47 +08:00
    支持! 看楼主是一个 self-driver developer?
    pony279
        2
    pony279  
    OP
       2018-08-01 17:14:07 +08:00
    @BBCCBB

    self-driven 😁
    BBCCBB
        3
    BBCCBB  
       2018-08-01 17:45:46 +08:00
    尴尬 ==


    github 上面说没时间开发这些玩意儿了, 咋又开始开发了啊?
    pony279
        4
    pony279  
    OP
       2018-08-01 17:51:23 +08:00   ❤️ 1
    @BBCCBB

    - 工作项目忙
    - 我想写个编辑器,发现工作量太大,又滚回来了
    jsfaint
        5
    jsfaint  
       2018-08-01 17:52:44 +08:00
    原来 ncm2-pyclang 已经支持头文件补全了啊……
    那看来就不需要 neoinclude 了
    jsfaint
        6
    jsfaint  
       2018-08-01 18:37:31 +08:00
    @pony279 ncm2-match-highlight 需要什么特殊设置吗?我用 neovim 在 windows/linux/mac 下都会乱码
    在 windows 下用 gvim 是正常的
    xiaotianhu
        7
    xiaotianhu  
       2018-08-01 19:06:50 +08:00
    一直用 vim 没有换到 neo 去 是不是太 old fashion 了
    借问一下 lz, 这种补全插件 对于网络映射到本地的磁盘,补全的效率是不是都不怎么样?比较依赖 IO
    补全一开就卡的要死,网络瓶颈
    pony279
        8
    pony279  
    OP
       2018-08-01 20:15:05 +08:00
    @jsfaint

    不需要设置的

    特殊 unicode 字符的展示在有些终端确实可能会乱码

    我在 windows 上用 putty 连 linux,或者在 debian 上用 kfce-terminal 都是正常的

    用不了的话只能等 neovim 实现了 https://github.com/neovim/neovim/issues/8780
    pony279
        9
    pony279  
    OP
       2018-08-01 20:19:53 +08:00
    @xiaotianhu

    网络映射做什么事情都很麻烦吧

    可以分情况看,如果对应的 Source 需要读大量文件才能得到补全结果的,很可能会出现拥堵,导致补全响应速度慢

    不过 vim 自己是有维护 buffer 的,所以应该不会卡在文本编辑上

    最好还是花时间尝试下看看效果
    pony279
        10
    pony279  
    OP
       2018-08-01 20:23:01 +08:00
    @jsfaint

    kfce-terminal -> xfce-terminal

    KFC 吃多了一时搞混了
    jsfaint
        11
    jsfaint  
       2018-08-02 00:26:30 +08:00
    @pony279 #10 我用的 deepin terminal,看来确实和终端有关系
    chemzqm
        12
    chemzqm  
       2018-08-02 07:03:44 +08:00
    谈几个 LSP 补全的小问题,不确认 ncm2 解决了没有。
    * 支持 language server 返回的 trigger charactors 触发补全
    * 支持 LSP 中定义的 completion resolve,切换补全项时请求详情,不支持的话某些 language server 看不到文档也支持不了 snippet 等功能。
    * 支持 language server 返回的 snippet,LanguageClient-neovim 这插件根本不支持 lsp 定义的 snippet https://microsoft.github.io/language-server-protocol/specification#snippet-syntax,server 返回的 snippet 会被它截一段关键字插入。

    ncm 的代码:

    au InsertEnter,InsertCharPre,TextChangedI <buffer> call ncm2#auto_trigger()
    func! ncm2#_do_auto_trigger()
    let tick = s:context_tick()
    if tick == s:auto_trigger_tick
    return ''
    endif
    let s:auto_trigger_tick = tick

    " refresh the popup menu to reduce popup flickering
    call s:feedkeys("\<Plug>(ncm2_complete_popup)")

    if g:ncm2#complete_delay == 0
    call s:feedkeys("\<Plug>(_ncm2_auto_trigger)")
    else
    if s:complete_timer
    call timer_stop(s:complete_timer)
    endif
    let s:complete_timer = timer_start(
    \ g:ncm2#complete_delay,
    \ {_ -> s:complete_timer_handler() })
    endif
    return ''
    endfunc

    用户在 fuzzy 补全时也会触发 TextChangedI, 此时 ncm2 会发起新的请求,其实这种请求完全可以避免,因为补全插件在第一次请求后就获取了所有的补全项,除非用户输入的是 trigger charactor,否者只需要过滤第一次获取的结果即可。另外这个 g:ncm2#complete_delay 设的小了可能 language server 还没收到当前的 buffer,导致无法正确补全,设置的大了影响补全的体验。
    pony279
        13
    pony279  
    OP
       2018-08-02 08:54:15 +08:00
    @chemzqm

    前三点都是已解决的问题

    > 用户在 fuzzy 补全时也会触发 TextChangedI, 此时 ncm2 会发起新的请求,其实这种请求完全可以避免,

    ncm2 core 的 on_complete 不代表 source 也能收到 on_complete 请求,内部会有缓存策略。

    > 另外这个 g:ncm2#complete_delay 设的小了可能 language server 还没收到当前的 buffer,导致无法正确补全,设置的大了影响补全的体验。

    这个配置现在是 0。我不认为这会影响 language server 收到的 buffer

    source 收到 on_complete 是在 ncm2 的异步进程处理之后的结果,这个时候 vim/neovim 早已经在它自己的同步的 event loop 里面处理完 autocmd 了。
    chemzqm
        14
    chemzqm  
       2018-08-02 09:51:33 +08:00
    @pony279
    1. ncm2 如何获取当前用户选择了哪一个 complete item ?还是就是补全完成的时候请求一下 https://github.com/ncm2/ncm2_lsp_snippet/blob/master/utils.py#L13
    2. Language client 不可能使用同步方式在 TextChange 触发时发送 document 给 server,因为那样体验肯定会比较差,https://github.com/autozimu/LanguageClient-neovim/blob/next/autoload/LanguageClient.vim#L707
    pony279
        15
    pony279  
    OP
       2018-08-02 10:07:41 +08:00
    @chemzqm

    > 1. ncm2 如何获取当前用户选择了哪一个 complete item ?还是就是补全完成的时候请求一下 https://github.com/ncm2/ncm2_lsp_snippet/blob/master/utils.py#L13

    http://github.com/ncm2/ncm2-ultisnips

    用这个插件配置按键映射,确认了就知道选择了哪一个

    > 2. 2. Language client 不可能使用同步方式在 TextChange 触发时发送 document 给 server,因为那样体验肯定会比较差,https://github.com/autozimu/LanguageClient-neovim/blob/next/autoload/LanguageClient.vim#L707

    这个我没有仔细看。如果 LCN 的 rust 程序是单线程而且没有协程的模型,那么这个做仍然是可靠的,它总是先处理完 did change 再处理 on_complete。
    Yggdroot
        16
    Yggdroot  
       2018-08-02 10:25:29 +08:00
    支持 vim 吗,我主要用 vim,所以你之前的 nvim-completion-manager 我都没试用过。
    pony279
        17
    pony279  
    OP
       2018-08-02 10:30:20 +08:00
    @Yggdroot


    能在 vim8 上跑,需要依赖 https://github.com/roxma/vim-hug-neovim-rpc

    我花在 vim8 上的测试时间比较少,所以如果不可用最好提个 issue
    Yggdroot
        18
    Yggdroot  
       2018-08-02 10:38:13 +08:00
    @pony279 好,有空试一下。
    chemzqm
        19
    chemzqm  
       2018-08-02 11:55:32 +08:00
    @pony279
    > 用这个插件配置按键映射,确认了就知道选择了哪一个

    不是用户确认的哪一个,complete resolve 应该是用户使用 <C-n> 或者 <C-p> 选中时进行调用然后显示文档等信息使用的。
    pony279
        20
    pony279  
    OP
       2018-08-02 12:53:04 +08:00
    > complete resolve 应该是用户使用 <C-n> 或者 <C-p> 选中时进行调用然后显示文档等信息使用的。

    resolve 显示文档还是算了。

    终端代码补全的 preview 出来一次闪一下,体验太差

    不如等有人实现了这个 feature 再考虑: https://github.com/neovim/neovim/issues/396#issuecomment-163760889
    pony279
        21
    pony279  
    OP
       2018-08-02 12:53:14 +08:00
    Yggdroot
        22
    Yggdroot  
       2018-08-03 11:24:20 +08:00
    试用了一下,感觉还不错,说说使用中遇到的问题。
    1. 在 vim 中,还没使用成功。
    一启动就有错
    ```
    Error detected while processing function ncm2#insert_mode_only_key:
    line 3:
    E492: Not an editor command: tmap <Plug>(ncm2_skip_auto_trigger) <nop>
    E492: Not an editor command: tmap <Plug>(ncm2_auto_trigger) <nop>
    E492: Not an editor command: tmap <Plug>(ncm2_manual_trigger) <nop>
    E492: Not an editor command: tmap <Plug>(ncm2_complete_popup) <nop>
    E492: Not an editor command: tmap <Plug>(_ncm2_auto_trigger) <nop>
    ```
    我 vimrc 中只是 https://github.com/ncm2/ncm2#install 里面的内容,后来又加上了 Plug 'roxma/vim-hug-neovim-rpc'。
    tmap 是 neovim 中的命令,在启动 vim 时却报这个错。(怀疑楼主所说的支持 vim 只是理论上,实际上没测过)

    2. 不够 `out of box`
    我把 https://github.com/ncm2/ncm2#install 里的内容添加到 vimrc 里,启动 nvim,发现怎么操作都没有补全;后来猜测可能需要装一些 source,就装了 Plug 'ncm2/ncm2-bufword',敲了几个字母,发现还是没有补全;等敲到第 4 个字母时,才弹出了补全菜单,我一般习惯于 1 个字母就出现补全。我知道这可以设置,就找到了 g:ncm2#complete_length。
    当然这都不是什么问题,我觉得如果 readme 里加一些必要的说明会更好,这样可以让像我这样第一次接触 ncm2 的人能够更容易的去体验这个插件。

    3. fuzzy 算法有待提高
    补全只能识别单词的边界字母,非边界字母就识别不了了。
    例如:`encode`, 如果我输入`end`,`encode`就不会出现在补全列表中了。

    总体来说还不错,不像一些插件补全菜单闪的厉害,会继续试用。
    pony279
        23
    pony279  
    OP
       2018-08-03 12:00:56 +08:00
    > tmap 是 neovim 中的命令,在启动 vim 时却报这个错。(怀疑楼主所说的支持 vim 只是理论上,实际上没测过)

    tmap 在新版的 vim8 也有了,我测试的版本是 8.1.135 http://vimhelp.appspot.com/map.txt.html#%3Atmap


    > 敲了几个字母,发现还是没有补全;等敲到第 4 个字母时,才弹出了补全菜单,我一般习惯于 1 个字母就出现补全。我知道这可以设置,就找到了 g:ncm2#complete_length。

    这个还真是个人习惯,我喜欢稍微安静一些的 popup,每敲击一个字符就弹出会很烦


    > 里的内容添加到 vimrc 里,启动 nvim,发现怎么操作都没有补全

    source 在 README Optional tips 里面有。也许应该移到 Install 部分会比较显眼吧。


    > 3. fuzzy 算法有待提高

    这个是有意而为之

    在旧版本的 ncm 里面有一个 fuzzy matcher 能符合你的描述: https://github.com/roxma/nvim-completion-manager/blob/master/pythonx/cm_matchers/fuzzy_matcher.py

    可是它太过 fuzzy。后来我发现需要过滤的条数越多,得到的结果精准度就越差,到最后我自己又用回了 prefix match

    基于之前的体验,我重新开发了两种 fuzzy,目前默认是 abbrfuzzy,换成最近新增的 substrfuzzy 可能会更加符合你的习惯

    你可以在这里参与讨论 https://github.com/ncm2/ncm2/issues/30,主要是反馈太少,对算法的描述也比较困难,所以大多基于自己的喜好和体验去实现和优化
    pony279
        24
    pony279  
    OP
       2018-08-03 12:01:09 +08:00
    Yggdroot
        25
    Yggdroot  
       2018-08-03 12:24:24 +08:00
    @pony279 tmap 好像是 patch 8.0.1108 加上去的,我使用的是 8.0.771 ,所以最好能在文档中说明一下。
    pony279
        26
    pony279  
    OP
       2018-08-03 13:19:38 +08:00
    @Yggdroot

    忽略掉了: https://github.com/ncm2/ncm2/commit/c78c47118bd99a27dcecfb7a1acb1afbf38ccb4b

    8.0.771 有点旧了,snippet 特性需要这个重要的 patch: https://github.com/neovim/neovim/pull/8003

    至少要 8.0.1493
    ivechan
        27
    ivechan  
       2018-08-04 17:33:14 +08:00
    支持下,下载试用一下.
    希望能替代掉 YCM。
    ivechan
        28
    ivechan  
       2018-08-04 19:35:50 +08:00
    @pony279 一个很奇怪的问题,在补全 python 的第三方包 mxnet 的时候,会出现错误,其他包没有问题。
    windows
    neovim 0.3.2 dev
    python 3.6.6
    已经安装好插件(确认了其他包补全正常),

    复现过程:
    pip install mxnet
    import mxnet
    mxnet.sym.

    这步开始出错,使用其他补全工具不会。
    出错日志有点长,有需要我再贴上来。
    pony279
        29
    pony279  
    OP
       2018-08-05 12:04:54 +08:00
    SpaceVim
        30
    SpaceVim  
       2018-08-05 14:13:17 +08:00
    对于 Vim 插件的开发者,我一至很佩服,前段时间在 reddit 上看到 ncm2 的帖子,跳到 ncm2 github 仓库留言了下,发现还是被作者 block 了,以前的事情过去这么久,这结一直解不开吗?

    @pony279 希望,有机会可以聊聊。关于以前你的另外一个插件的一些言论,我先在这里和你道歉。
    ivechan
        31
    ivechan  
       2018-08-05 22:03:47 +08:00
    @pony279 你给的链接,我无法重现这个错误。
    不过今天我又重新试了一下,发现使用其他包的时候,居然也会出错。
    然后我看错误日志,发现居然传递了之前测试 mxnet 包的补全信息,有点匪夷所思。(会不会有缓存机制?)
    -----------------------------------------------------------------------------------------------------
    我重装了 mxnet 包后,能过正常使用 ncm2 了。
    (之前使用 jedi.vim 的时候能过补全 mxnet,就没有怀疑这个包本身的问题)
    虽然很意外,不能确定哪里出了问题。不过最终还是顺利用上了 ncm2, 综合使用很不错,很感谢你!
    chengmonkey
        33
    chengmonkey  
       2018-09-15 22:49:49 +08:00
    楼主这个 ncm2/ncm2-ultisnips 咋使用啊- -配置模版的地方在哪里- -
    pony279
        34
    pony279  
    OP
       2018-09-19 17:25:07 +08:00
    @chengmonkey

    这个插件只暴露这三个接口,没别的了

    - ncm2_ultisnips#expand_or
    - ncm2_ultisnips#completed_is_snippet()
    - <Plug>(ncm2_ultisnips_expand_completed)

    补全的 snippet 是由 source 提供的
    dangoron
        35
    dangoron  
       2018-10-15 21:33:56 +08:00 via Android
    从第一代就开始在用,现在和 LSP 结合非常舒服,拉了用 unite 的日本学长过来一起用。感谢作者
    hujianxin
        36
    hujianxin  
       2018-12-08 14:31:12 +08:00
    感谢楼主的插件,非常厉害!

    另外,ncm2-tmux 这个插件的作用是?
    pony279
        37
    pony279  
    OP
       2018-12-14 00:01:18 +08:00
    @hujianxin

    ncm2-tmux 会把其他 tmux 窗口的 keyword 抓下来用于补全

    如果你经常用 tmux,可以试试
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1009 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 34ms · UTC 22:02 · PVG 06:02 · LAX 15:02 · JFK 18:02
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.