cquery最近改动
- 原作者创建了organization,我据此把几个插件分离了: emacs-cquery 和 vscode-cquery ,等候吉日再来一次
git filter-branch --index-filter
减小主 repo 的体积 - 检索Doxygen注释标记,及可选的普通(行注释
//
块注释/**/
)。在initialization options里配置"enableComments": 2,
- cache文件(
cacheDirectory
目录下的文件)原来为JSON,现在支持序列化成MessagePack,请在initialization options里配置**“cacheFormat”: “msgpack”,` - 感谢bennyyip添加了 archlinuxcn/cquery-git ,Arch Linux用户要体验cquery更加方便了
- 光标处符号的类型信息改进了,根据romix的提议,对于函数,找出函数类型和返回值类型字串形式第一个不同的位置,多半是插入函数名的位置。但参数名插入仍然困难,都怪C的inside-out类型表示语法
参见wiki中的 Initialization options ,邀请大家多编辑wiki为后人提供方便。

Unified Symbol Resolution优化
Clang Index使用Unified Symbol Resolution标识程序中的function/variable/type等。cache文件中一个函数可能存储为这样:
{ "id": 0, "is_operator": false, "usr": "c:@FT@>2#T#pTMakeUnique#P&&t0.1#*t0.0#", "short_name": "MakeUnique", "detailed_name": "T *MakeUnique(Args &&... args)", "kind": 12, "declarations": [], "definition_spelling": "4:4-4:14", "definition_extent": "4:1-6:2", "base": [], "derived": [], "locals": [], "callers": ["2@14:3-14:13", "2@15:3-15:13", "2@16:3-16:13"], "callees": [] },
对于C++程序,嵌套几个namespace、class后USR就会很长,占用不少空间。原作者提起clangd用了SHA-1 ( std::array<uint8_t, 20>
)来省空间,我感觉160-bit太浪费了,想找个64-bit non-cryptographic hash,经quininer推荐选取了SipHash 64-bit,能用 uint64_t
代替原来的 std::string usr
,省空间。
const uint8_t k[16] = {0xd0, 0xe5, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x52, 0x61, 0x79, 0xea, 0x70, 0xca, 0x70, 0xf0, 0x0d}; (void)siphash(reinterpret_cast<const uint8_t*>(usr), strlen(usr), k, out, 8); return ret;
rapidjson 也能序列化 int64_t/uint64_t
,而不会因为使用了 double
导致精度丢失到53-bit。JavaScript的Number类型真是一个陷阱。
用hash做主键,在这里非常合适。因为假使两个键冲突,观察到的后果也就是两个变量/函数(他们的数量远多于类型)合并了,所有功能都能照常使用。不会有blockchain hash冲突的灾难性后果。
在LLVM上观察cache空间占用, stat -c %s .vscode/cquery_cached_index/**/*.mpack | awk '{s+=$1}END{print s}'
,从720055341字节降到265784325字节。
根据khng300索引Linux kernel的结果:
# QueryFunc/QueryFile/... db.funcs.size() = 346583 db.files.size() = 25553 db.symbols.size() = 2891931
层级diff
因为性能问题,cquery当前只在用户保存文档时调用libclang重新索引用到当前文件的translation units,对于 .cc/.cpp
通常只会编译一个translation unit。打开文件后进行修改,文件的buffer版本就和索引时版本不同了。类型/函数/变量的交叉引用信息仍为索引时的版本,怎么对应到buffer版本呢?index版本和buffer版本的映射是双向的:
- buffer到index。用户在buffer某处使用definition/references等功能,需要把buffer光标位置映射到index版本,查找符号信息
- index到buffer。对于hover/references等功能,找到一个符号所有出现的位置后,需要把index映射到buffer版本,显示出来
对于一个编辑中的文件,很可能无法编译通过,甚至难以保证能语法分析成功,我今天加了一些文档diff算法,启发式地处理这个问题。以按buffer 光标行列找index对应行列为例。
- 先用Paul Heckel’s diff algorithm找出index与buffer的映射关系,实质上把文档分块了
- 对于一个行文本,用Eugene Wimberly Myers’ O(ND) diff algorithm找块间最相似的行
- 对于行文本和列位置,在找到的最相似的行中找最合适的列
这样就把index的一个行列位置转换到buffer的行列位置。
Paul Heckel’s diff algorithm
大意是如果一个行在index中只出现一次,在buffer中也只出现一次,可以看作一个锚点,认为buffer中出现的行号映射到index中出现的行号。
之后处理锚点邻接的行,如果在index和buffer中相同,那么也认为buffer中出现的行号到index出现的行号有一个映射关系。据此把所有邻接行都标记上映射关系。对于这种类型的flood fill,只需要从上到下扫描和从下到上扫描两遍文档。
块间找最相似的行
如果询问的buffer行 i
恰好有个到index的Paul Heckel映射关系 buffer_to_index[i]
,直接返回。否则就上下找到最接近的含有映射关系的行 up,down
,在映射后的区间 [buffer_to_index[up],buffer_to_index[down]]
内用edit distance找和 buffer[i]
最相似的行 j
。
把buffer第i行第c列映射到index第j行最优列
再用一次edit distance,在 index[j]
中找一个列 w
,使得 align(buffer_line[0:c], index_line[0:w]) + align(buffer_line[c:], index_line[w:])
最小。可以认为index行的第 w
列即对应buffer行的第 c
列。这实际上是Hirschberg’s algorithm的一步。
取到最小值的可能有多个列,我用了一个简单的方法。一般询问是关于buffer一个区间对应index中的哪一个区间。对于区间起点,我希望映射后的位置尽可能靠后;对于区间终点,希望映射后的位置尽可能靠前。
实现了以上buffer到index的双向映射后,可以用index版本给正在编辑中的buffer版本提供信息了。效果见下图,编辑了光标处的函数名, textDocument/documentHighlight
依然可用:

其他
Emacs用户请关注这些repo和issue:
- emacs-cquery ,添加到 Melpa
- lsp-mode
- lsp-ui
- 对于spacemacs用户,请关注我呼吁 支持lsp、添加cquery到c-c++ layer的issue
Vim用户还没有很好看的UI,需要人拯救。
另外, 把字符单位从UTF-16换成Unicode codepoint ,Microsoft和JavaScript受害者真是对UCS-2/UTF-16孜孜不倦。感谢 Riatre的分析 。这个问题修不好,我们在Emacs或VSCode里使用emoji会有很大阻碍。
cquery中Language Server Protocol中物件的序列化仍挺麻烦的。TypeScript中interface可以继承,我也在Emacs lsp-mode里看到了有些 TextDocumentIdentifier
带有子类 VersionedTextDocumentIdentifier
的版本字段。这个在C++里序列化就不方便了。
如果要进一步优化启动时cache加载速度,需要考虑lmdb之类存储。
https://microsoft.github.io/language-server-protocol/specification#versionedtextdocumentidentifier
interface VersionedTextDocumentIdentifier extends TextDocumentIdentifier
注:本文内容来自互联网,旨在为开发者提供分享、交流的平台。如有涉及文章版权等事宜,请你联系站长进行处理。