我今天給 emacs-cquery 加了實驗性的 rainbow semantic highlighting 。順便說下,已嘗試添加 cquery.el 到 Melpa,等待維護者批准。
cquery 是一個 C/C++/Obj-C language server,實現查找定義、引用、符號搜索、顯示光標處符號類型簽名等功能。 semantic highlighting 是cquery擴展功能,LSP協議中未定義。
原理是索引 C/C++ 文件時記錄符號(var/func/type)的 ClangSymbolKind,如:Struct Variable Parameter EnumConstant
等。
文檔新打開或保存時,cquery會發送semantic highlighting信息。EmitSemanticHighlighting 製作信息後發給 language client。Semantic highlighting 功能 cquery原本就有,VSCode 的插件支持,但Emacs裏用得人很少,只有單色。這次我把符號種類從原來的 {member,free}{function,variable}/type 5種細分了下,再把VSCode用的配色方案導入到cquery.el,根據stableId
渲染符號,把 face 的:foreground
設置爲colors[stableId % len(colors)]
。我的elisp技能有限,各位感興趣的話求幫忙改進
Emacs cquery 安裝配置參見 wiki Emacs · jacobdufault/cquery Wiki · GitHub , 啓用彩虹只需
(cquery-use-default-rainbow-sem-highlight)
另:渲染這個10000多行的文件,不管用 (setq cquery-sem-highlight-method 'overlay)
還是'font-lock
,都很慢的。使用 'overlay
的話用Emacs noverlay分支能改善。
(defun cquery--make-sem-highlight (region buffer face)
"."
(pcase cquery-sem-highlight-method
('overlay
(let ((ov (make-overlay (car region) (cdr region) buffer)))
(overlay-put ov 'face face)
(overlay-put ov 'cquery-sem-highlight t)))
('font-lock
(put-text-property (car region) (cdr region) 'font-lock-face face buffer))))
(defun cquery--publish-semantic-highlighting (_workspace params)
"."
(when cquery-enable-sem-highlight
(let* ((file (cquery--uri-to-file (gethash "uri" params)))
(buffer (find-buffer-visiting file))
(symbols (gethash "symbols" params)))
(when buffer
(with-current-buffer buffer
(save-excursion
(with-silent-modifications
(cquery--clear-sem-highlights)
(dolist (symbol symbols)
(-when-let (face (funcall cquery-sem-face-function symbol))
(dolist (range
(mapcar 'cquery--read-range (gethash "ranges" symbol)))
(cquery--make-sem-highlight range buffer face)))))))))))
Rainbow semantic highlighting樣式:func/var/type 各預設了10種顏色,member func/var用相應顏色的斜體。
現在 (defcustom cquery-sem-member-func-faces [cquery-sem-member-func-face]
這樣的定製方式是很笨拙的。我的這種根據 ClangSymbolKind
劃分色譜區間取色的方式對於顏色定製也不夠方便:
(pcase kind
;; var
(4 (funcall fn0 cquery-sem-free-var-faces 600 700)) ; Macro
(13 (funcall fn0 cquery-sem-free-var-faces 0 600)) ; Variable
(25 (funcall fn0 cquery-sem-free-var-faces 700 1000)) ; Parameter
(14 (funcall fn0 cquery-sem-member-var-faces 400 1000)) ; Field
(15 (funcall fn0 cquery-sem-member-var-faces 200 400)) ; EnumConstant
(21 (funcall fn0 cquery-sem-member-var-faces 0 200)) ; StaticProperty
;; func
(12 (funcall fn0 cquery-sem-free-func-faces 0 800)) ; Function
(18 (funcall fn0 cquery-sem-free-func-faces 800 1000)) ; StaticMethod
(22 (funcall fn0 cquery-sem-member-func-faces 800 1000)) ; Constructor
(23 (funcall fn0 cquery-sem-member-func-faces 1000 1000)) ; Destructor
(24 (funcall fn0 cquery-sem-member-func-faces 1000 1000)) ; ConversionFunction
(16 (funcall fn0 cquery-sem-member-func-faces 0 800)) ; InstanceMethod
;; type
((or 6 7) (funcall fn0 cquery-sem-type-faces 0 700)) ; Struct | Class
(10 (funcall fn0 cquery-sem-type-faces 1000 1000)) ; Union
(11 (funcall fn0 cquery-sem-type-faces 700 1000)) ; TypeAlias