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

使用 cquery: C++ language server

  •  7
     
  •   MaskRay · 2017-12-09 13:00:30 +08:00 · 5786 次点击
    这是一个创建于 2566 天前的主题,其中的信息可能已经有所发展或是发生改变。

    原文 https://maskray.me/blog/2017-12-03-c++-language-server-cquery

    C++代码索引工具现状

    Tag system 流派

    clang 流派

    • clang-tags荒废。
    • YouCompleteMe不够好用,因为只处理单一 translation unit,无法查找引用。
    • clangd最有前景,有大厂大项目愿意采用,Xcode 使用。但目前尚无存储系统,因此无法处理多 translation units。作为clang-tools-extra一部分,而 clang+llvm 构建 /贡献门槛高([https://reviews.llvm.org/])。对于这类工具类应用,贡献难易程度是个重要因素。目前有尝试引入存储模型(MarkZ3),但目前设计较为复杂,而实际上不带 garbage collection 的std::vector(cquery 风格)足够应对大部分使用场景。很担心他们走上歧路。
    • Google Kythe,(mostly) language-agnostic,概念复杂,配置困难。不重视 language server protocol,当前仅提供ReferencesProvider,HoverProvider,DefinitionProvider,且交互使用可能有极大延迟。大多数人并不在意 C++ Haskell Python 代码间无缝跳转。
    • rtags可以查找引用,但每个 translation unit 6 个文件info,symbols,symnames,targets,tokens,usrs(过多),没有使用 in-memory 索引,查找引用请求会读项目所有 translation units 的文件。导致性能低下https://github.com/Andersbakken/rtags/issues/1007
    • cquery现阶段的妥协。主要数据结构为不带 garbage collection(变量 /函数 /类型等的 id 不会回收)的std::vector(src/indexer.h)。有一些 Emacs 用户积极贡献code navigation 功能

    IDE(Any sufficiently complicated IDE contains an ad-hoc, informally-specified, bug-ridden, slow implementation of half of C++.)

    cquery 安装、配置

    https://github.com/jacobdufault/cquery。Arch Linux 可用aur/cquery-git

    • 构建 language server 可执行文件
    • 编辑器安装 language client 插件
    • 配置 language client,打开 C++文件时运行 language server 可执行文件,通过 stdio 用JSON-RPC 2.0通信
      • 光标移动时向 language server 发送textDocument/hover请求,language server 返回变量 /函数声明信息
      • 查找定义发送textDocument/definition请求
      • 查找引用发送textDocument/references请求
      • 查找当前文档定义的符号(通常是顶层的 outline)发送textDocument/documentSymbol请求
      • 查找项目定义的符号(只查找 outline 的也很有用)发送workspace/symbol请求
      • 补全textDocument/completion
      • 文档编辑操作发送textDocument/didChange

    Emacs

    安装lsp-mode,参照https://github.com/jacobdufault/cquery/wiki/Emacs配置。lsp-mode 会设置xref-backend-functions,把以下函数

    • xref-find-definitions (默认M-.),对应textDocument/definition
    • xref-find-references (默认M-?),对应textDocument/references
    • xref-find-apropos (默认M-?),对应workspace/symbol
    • lsp-imenu 对应textDocument/documentSymbol
    • LSP 生态系统解决的一大痛点是以前对于不同语言,要使用不同工具,设置不同快捷键。用了 language client 就可以统一了。

    定向到 lsp-mode 中的处理函数,发送textDocument/definition等请求并渲染。

    如果使用helm,可以考虑安装helm-xrefxref-show-xrefs-function会使用helm-xref-show-xrefs。但目前有两个 issues 影响了我的使用体验:

    注意协议中定义返回结果为Location[] | null,只含位置信息,不包含代码行内容,如果要显示行内容。若文件在某个 buffer 内,则显示该 buffer 相应行;若未打开则需要打开该文件。

    Emacs Lisp dynamic scoping 使得我们可以很容易复用已有代码。可以基于 evil-jumps 做一个用于 xref 的 jump list。我喜欢 xref jump list 和正常 jump list 分离。因为查找定义 /引用后会进行一些局部跳转,喜欢有快捷键回到定义 /引用跳转前的位置。并且需要能双向移动,不能只是 jump stack,xref.el用 ring buffer 实现的是 stack。

    (defmacro my-xref//with-evil-jumps (&rest body)
      "Make `evil-jumps.el' commands work on `my-xref--jumps'."
      (declare (indent 1))
      `(let ((evil--jumps-window-jumps ,my-xref--jumps))
         ,@body))
    
    (with-eval-after-load 'evil-jumps
      (evil-define-motion my-xref/evil-jump-backward (count)
        (my-xref//with-evil-jumps
            (evil--jump-backward count)
          (run-hooks 'xref-after-return-hook)))
    
      (evil-define-motion my-xref/evil-jump-forward (count)
        (my-xref//with-evil-jumps
            (evil--jump-forward count)
          (run-hooks 'xref-after-return-hook))))
    

    最近的新包https://github.com/emacs-lsp/lsp-ui包含 code lens、flycheck 等功能,以及一个基于quick-peekfind-{definitions,references,apropos}

    https://github.com/MaskRay/Config/blob/master/home/.emacs.d/layers/%2Bmy/my-code/funcs.el

    Neovim

    参照https://github.com/autozimu/LanguageClient-neovim/wiki/cquery

    nn <leader>ji :Denite documentSymbol<cr>
    nn <leader>jI :Denite workspaceSymbol<cr>
    " 终端限制,<C-,>不可用。ord(`,`) & 64 为 0 无法表示
    nn <M-,> :Denite references<cr>
    nn <silent> <C-j> :MarkPush<cr>:call LanguageClient_textDocument_definition()<cr>
    

    不清楚怎么把定义 /引用改造成使用可双向移动的 jump list。

    生成compile_commands.json

    CMake

    % mkdir build
    % (cd build; cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=YES ..)
    % ln -s build/compile_commands.json
    

    Build EAR

    Bear is a tool that generates a compilation database for clang tooling. It can be used for any project based on Makefile.

    bear make
    # generates compile_commands.json
    

    Ninja

    ninja -t compdb rule_names... > compile_commands.json
    

    问题

    • Task lists https://github.com/jacobdufault/cquery/issues/30 Polish before publishing (to GitHub Marketplace)
    • 需要一个妥善的 on-disk storage。很多轻量级数据库不支持或有较难处理的问题(如果有需求把现在 in-memory+JSON 改成有其他存储模型)。注记,SQLITE_ENABLE_LOCKING_STYLE、flock 很难。

    其他

    索引 Linux kernel

    wget 'https://git.archlinux.org/svntogit/packages.git/plain/trunk/config?h=packages/linux' -O .config
    yes '' | make config
    bear make -j bzImage modules
    

    生成 3GiB 文件。

    索引 llvm,du -sh => 1.1GB,索引完内存占用 2G。

    查看 LSP requests/responses

    sudo sysdig -As999 --unbuffered -p '%evt.type %evt.buffer' "proc.pid=$(pgrep -fn build/app) and fd.type=pipe" | egrep -v '^Content|^$'
    

    生成compile_commands.json,参考https://github.com/jacobdufault/cquery/wiki

    希望有朝一日 Debug Protocol 也能获得重视,https://github.com/Microsoft/vscode-debugadapter-node/blob/master/protocol/src/debugProtocol.ts,让realgud轻松一点。

    和 YouCompleteMe 等项目一样,cquery 默认下载prebuilt clang+llvm,即.h .so .a。用户不需要编译完整的 llvm,开发门槛比 clangd 低。

    感谢 ngkaho1234。

    18 条回复    2018-05-14 14:55:10 +08:00
    glacier2002
        1
    glacier2002  
       2017-12-09 16:30:09 +08:00
    完全没看懂。。。。
    htfy96
        2
    htfy96  
       2017-12-09 16:40:07 +08:00 via Android
    cquery 感觉的确是现代的方案…时间不太够只能维护下 cquery-git 了
    forestyuan
        3
    forestyuan  
       2017-12-09 21:46:23 +08:00
    这是来推广的吗?
    MaskRay
        4
    MaskRay  
    OP
       2017-12-10 05:41:07 +08:00
    @glacier2002 原文裏有幾張圖,內容改了點……這裏編輯困難
    MaskRay
        5
    MaskRay  
    OP
       2017-12-10 05:42:15 +08:00   ❤️ 1
    @forestyuan 是的。希望能找到更多有 C/C++代碼閱讀需求的 geek 用戶貢獻這個項目。很多 code assistant 功能靠 clangd 這種大教堂模式是搞不好的
    YeT9
        6
    YeT9  
       2017-12-10 10:23:58 +08:00
    Ray 教授啊!火钳流明~资瓷一个~
    congeec
        7
    congeec  
       2017-12-10 11:18:29 +08:00
    cquery 目前 hover definition 不能显示函数签名,跟 YouCompleteMe 比还差好多。不过能重构,也是屌屌的
    ivechan
        8
    ivechan  
       2017-12-10 15:48:00 +08:00
    之前试用了一下 clangd, 实在太难用了,有时候性能极差,还不如直接用 fzf ack 这类工具。
    skt041959
        9
    skt041959  
       2017-12-10 16:54:48 +08:00
    LanguageClient-neovim 是 rust 写的,没法编译。试了一下 vim-lsp 好像不会用。有其他支持的 vim/neovim LSP plugin 么?
    bookit
        10
    bookit  
       2017-12-10 20:03:44 +08:00
    善,哪一天能赶上 vax 就好了。
    autozimu
        11
    autozimu  
       2017-12-11 10:49:49 +08:00   ❤️ 1
    @skt041959 什么是没法编译?

    大多数情况应该是不需要编译的, 已经有编译好的 binaries 了呀
    congeec
        12
    congeec  
       2017-12-11 12:46:26 +08:00
    @autozimu 谢谢你的作品
    ivechan
        13
    ivechan  
       2017-12-22 15:27:20 +08:00
    之前看了但是还没尝试, 今天去尝试了一下, 在 vim 下体验比其他的好。谢谢。
    5thcat
        14
    5thcat  
       2018-01-06 01:52:11 +08:00
    非常感谢! 终于找到了失散多年的阅读代码工具!
    skt041959
        15
    skt041959  
       2018-01-08 13:23:44 +08:00
    @autozimu #11 之前理解错了,以为必须编译 rust 才能用。其实不用编译,直接用 python 的就可以了
    bef0rewind
        16
    bef0rewind  
       2018-01-26 09:40:55 +08:00
    很有意思的项目啊
    f2ed
        17
    f2ed  
       2018-04-18 17:38:07 +08:00
    我用的 vscode-cquery,表示 C++符号经常找不到啊,c 语言还可以
    MaskRay
        18
    MaskRay  
    OP
       2018-05-14 14:55:10 +08:00
    原來都已經半年了……我現在用自己的 fork https://github.com/MaskRay/ccls

    移除了不必要的第三方依賴
    import_pipeline.cc 比原來簡單的多,效果更好(index merge, id map 都是 cquery 中不妥當的設計,我上週移除了)
    mem index 有少量提升

    https://github.com/autozimu/LanguageClient-neovim/issues/293 自定義 cross reference 能支持就最好了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1003 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 21:44 · PVG 05:44 · LAX 13:44 · JFK 16:44
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.