6

$ccls/navigate和index.multiVersion

 3 years ago
source link: http://maskray.me/blog/2018-09-03-ccls-navigate-and-index-multiversion
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

$ccls/navigate和index.multiVersion

忙碌的Labor Day长周末。Growth hacking(希望Hudson River Trading用上无进展,和前clangd、Eclipse CDT开发者交谈、cdt-lsp计划,给LanguageClient-neovim vim-lsp加wiki页面成功),增加了少量stargazers。学习clangd并悄悄改typo、摸索musl的开发模式,希望r2走上正途(C11/C99),LeetCode第一次用上std::adjacent_find……

Language Server Protocol中最新的textDocument/documentSymbol可以返回DocumentSymbol[],带层次结构。但lsp-mode中尚未支持。 C/C++中的难点是out-of-line definition,declaration可以和definition分离,lexical/semantic parent的区别,可以声明多次(如namespace)。能稍微改进我的imenu显示效果,但实用性并不是特别大。

而利用document symbol进行declaration间快速移动更有用。我决定加个方法$ccls/navigate支持上下左右移动。实现很简单:https://github.com/MaskRay/ccls/blob/master/src/messages/ccls_navigate.cc

(ccls-navigate "D")


(ccls-navigate "L")


(ccls-navigate "R")


(ccls-navigate "U")

LanguageClient-neovim用户可以使用(我觉得x不值得一个单独的键,挪用作前缀键了)

nn <silent> xh :call LanguageClient#findLocations({'method':'$ccls/navigate','direction':'L'})<cr>
nn <silent> xj :call LanguageClient#findLocations({'method':'$ccls/navigate','direction':'D'})<cr>
nn <silent> xk :call LanguageClient#findLocations({'method':'$ccls/navigate','direction':'U'})<cr>
nn <silent> xl :call LanguageClient#findLocations({'method':'$ccls/navigate','direction':'R'})<cr>
nn xx x

index.multiVersion

一直以来,ccls使用继承自cquery的每个文件只检索一次的模型,一个文件即使被不同方式编译(如#include前使用不同的macro),也只有一个版本被记录下来。好处是不同translation unit间索引的东西基本不重复,大大减少索引文件的尺寸。

但缺陷是对于下面的例子只能索引到一个分支:

#ifdef D
// foo() A
#else
// foo() B
#endif
// a.h
template<class T>
int bar(T a) { return a.foo(); } // foo或者跳到a.cc中,或者跳到b.cc中,取决于谁最晚索引

// a.cc
#include "a.h"
struct A { int foo() { return 0; } };
int a = bar(A());

// b.cc
#include "a.h"
struct B { int foo() { return 0; } };
int b = bar(B());

这个改动需要变更索引数据结构和pipeline,把all_symbols outline的构建时刻从indexer推迟到main thread合并indexer信息时。示意:

struct Reference {
Range range; // 8 bytes
Usr usr; // 8 bytes
SymbolKind kind; // 1 byte followed by 1-byte padding
Role role; // 2 bytes
};
struct SymbolRef : Reference {};

// before
struct QueryFile {
std::vector<SymbolRef> all_symbols;
std::vector<SymbolRef> outline;
};

// after
struct QueryFile {
llvm::DenseMap<SymbolRef, int> symbol2refcnt;
llvm::DenseMap<SymbolRef, int> outline2refcnt;
};

开启方式:

{
"index": {
"multiVersion": 1,
"multiVersionBlacklist": ["^/usr/include"]
}
}

用blacklist让系统headers仍然只索引一遍,否则C++ headers会大幅膨胀索引文件(在一个例子中两倍体积)。

glibc中很多文件会被非PIC和PIC编译两遍,产生重复的compile_commands.json a.c条目,index.multiVersion: 1并不解决这个问题。另外a.h保存后是否所有包含它的*.c都要重新索引呢?我现在仍然使用textDocument/didSave时只索引一个的方案,否则太浪费。

内存和索引文件体积比较。下面RSS为索引结束后resident set size,其实如果重启language server减少内存碎片,可以少一些,ccls-cache为保存在磁盘上的索引目录的du -sh。

musl
RSS: 72M, ccls-cache: 16M
RSS: 196M, ccls-cache: 16M

radare2
RSS: 388M, ccls-cache: 112M
RSS: 1297M, ccls-cache: 496M

ccls
RSS: 319M, ccls-cache: 51M
RSS: 874M, ccls-cache: 204M

上周用上了clangd的StoreInMemory preamble技巧改进completion速度、移除ASTUnit优化diagnostics。

有人愿意帮忙往ale(Vim插件)、eglot、Oni(Neovim GUI)、theia-ide等提issue要求支持ccls我会很感激(自己不好意思做嘛)

其他可以帮忙的地方至少有:

  • 改进vscode插件。上游的vscode-cquery也很久没动了……我讨厌VSCode肯定不愿意多动vscode-ccls
  • 弄清楚Windows msys2用新gcc链接llvm https://github.com/MaskRay/ccls/issues/59
  • 加FreeBSD port、加Gentoo ebuild、弄MacPorts Homebrew等……
  • spacemacs https://github.com/syl20bnr/spacemacs/pull/11242

Share Comments


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK