ncm2 - 更加专注 & 可拓展的 vim/neovim 代码补全框架 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
pony279
V2EX    Vim

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

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

    ncm2/ncm2 的前身是 roxma/nim-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
    nG29DOMuRYTWfcSr
        30
    nG29DOMuRYTWfcSr  
       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     Solana     1216 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 37ms UTC 17:22 PVG 01:22 LAX 09:22 JFK 12:22
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86