Skip to content
This repository has been archived by the owner on Jul 30, 2020. It is now read-only.
Ting-Yu Lin edited this page Jan 29, 2019 · 67 revisions
  1. Install lsp-mode
  2. Install emacs-cquery and configure it
  3. Open a source file where either .cquery or compile_commands.json is in the project root (it may work without them, though not recommended)
  4. M-x lsp.

Install emacs-cquery

https://melpa.org/#/cquery

Configure

The only required configuration is cquery-executable. Others have good defaults.

xref-find-definitions (default: M-.) / highlighting of the symbol at point / type signature in echo area should work out-of-box. Read on for customization and more features.

(setq cquery-executable "/path/to/cquery/build/release/bin/cquery")
;; ;; Arch Linux aur/cquery-git aur/cquery
;; (setq cquery-executable "/usr/bin/cquery")

;; ;; Log file
;; (setq cquery-extra-args '("--log-file=/tmp/cq.log"))
;; ;; Cache directory, both relative and absolute paths are supported
;; (setq cquery-cache-dir ".cquery_cached_index")
;; ;; Initialization options
;; (setq cquery-extra-init-params '(:cacheFormat "msgpack"))

Projects with subprojects

For each source file that has turned on lsp, variable cquery-project-roots, projectile, compile_commands.json are consulted to locate the project root and the associated lsp-mode workspace. If your project has subprojects, (projectile-project-root) may think files in the subproject belong to the child workspace, which is not desired. (setq cquery-project-roots '("~/my-root-project" )) to override projectile roots.

If you do not mind files in subprojects are treated as part of the whole project in projectile:

(with-eval-after-load 'projectile
  (setq projectile-project-root-files-top-down-recurring
        (append '("compile_commands.json"
                  ".cquery")
                projectile-project-root-files-top-down-recurring)))

To turn on cquery for all C/C++ modes:

(defun cquery//enable ()
  (condition-case nil
      (lsp)
    (user-error nil)))

  (use-package cquery
    :commands lsp
    :init (add-hook 'c-mode-hook #'cquery//enable)
          (add-hook 'c++-mode-hook #'cquery//enable))
;; Also see lsp-project-whitelist lsp-project-blacklist cquery-root-matchers

Diagnostics

  • M-x lsp-capabilities LSP workspace is initialized correctly
  • M-: xref-backend-functions is (lsp--xref-backend) for cross references
  • M-: completion-at-point-functions is (lsp-completion-at-point) for completion

The buffer *lsp-cquery stderr* and --log-file=/tmp/cq.log contain logs.

Initialization options are defined in config.h, but you need to customize them in S-exp. Use t for true, :json-false for false, :json-null for null.

(setq cquery-extra-init-params '(:index (:comments 2) :cacheFormat "msgpack" :completion (:detailedLabel t)))

Find definitions/references

xref-find-definitions (default: M-.) should work out-of-box. If not, check if M-: xref-backend-functions is (lsp--xref-backend), which should be set by lsp-mode when you execute (lsp).

There is heuristic to make textDocument/definition work in comments and macro replacement-list, which does something similar to rtags-find-symbol-at-point

xref-find-references (default: M-?) will give a prompt. To inhibit the prompt, add xref-find-references to xref-prompt-for-identifier. (See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=29619 for changing the default behavior.)

There are Helm/Ivy alternatives for xref-find-{definitions,references,apropos}

Check https://github.com/emacs-lsp/lsp-ui for a supplement of lsp-mode. It provides some higher level UI features.

lsp-ui-peek-find-{definitions,references} from lsp-ui-peek.el provide peek views. An additional benefit is you get a window local jump list dedicated to cross references which I bind to C-p and C-t

  (define-key evil-normal-state-map (kbd "C-p") 'lsp-ui-peek-jump-forward)
  (define-key evil-normal-state-map (kbd "C-t") 'lsp-ui-peek-jump-backward)

lsp-ui-peek-find-references

references + hydra

Symbol highlight

The identifier (or user-defined operator) at point is highlighted with its other references. If you build trunk libclang, the references will be differentiated for different usage: read/write.

textDocument/documentHighlight

imenu/workspace symbol search

Use lsp-imenu.el for textDocument/documentSymbol (outline). lsp-ui-imenu.el provides a sidebar.

lsp-imenu + hydra

xref-find-apropos (default: C-M-.) invokes workspace/symbol to fuzzy-find symbols in the project. However, the function tries to be smart and splits the pattern and regexp-quote them for you, which actually gets in the way. Consider using lsp-ui{,-peek}-workspace-symbol in the lsp-ui package.

For a symbol named Name::FooBar, all of FooBar, foo bar, nafoba find them, but with different priorities.

cquery cross-reference extension

Aside from definitions/references/workspace symbol, cquery provides some LSP extensions that find base classes/methods, vars of a type, callers of a function. You may call:

(cquery-xref-find-custom "$cquery/base")
(cquery-xref-find-custom "$cquery/callers")
(cquery-xref-find-custom "$cquery/vars")

;; Alternatively, use lsp-ui-peek interface
(lsp-ui-peek-find-custom 'base "$cquery/base")
(lsp-ui-peek-find-custom 'callers "$cquery/callers")
(lsp-ui-peek-find-custom 'random "$cquery/random") ;; jump to a random declaration

"$cquery/derived" has been removed. To find derived classes/methods, use lsp-goto-implementation or lsp-ui-peek-find-implementation.

Documentation (comments)

lsp-ui-doc.el renders comments in a child frame (Emacs >= 26) or inline (< 26).

    (setq lsp-ui-doc-include-signature nil)  ; don't include type signature in the child frame
    (setq lsp-ui-sideline-show-symbol nil)  ; don't show symbol on the right of info

Completion

Install company-lsp and add company-lsp to company-backends. Consider disabling client-side cache and sorting because the server does a better job.

(setq company-transformers nil company-lsp-async t company-lsp-cache-candidates nil)

Type #i" (or #include ") for quote-style includes and #i< (or #include <) for system headers.

See https://github.com/cquery-project/cquery/pull/391#issuecomment-362872732 for an alternative view (contextual parent as detail, signature as label)

(setq cquery-extra-init-params '(:completion (:detailedLabel t)))

company-lsp + company-quickhelp

lsp-ui-flycheck

Semantic highlighting

To enable semantic highlighting:

(setq cquery-sem-highlight-method 'font-lock)
;; alternatively, (setq cquery-sem-highlight-method 'overlay)

;; For rainbow semantic highlighting
(cquery-use-default-rainbow-sem-highlight)

Different variables/functions/types will be assigned different faces, while uses of the same variable/function/type share the same face. The colors can be customized:

cquery-sem-function-colors
cquery-sem-macro-colors
;; ...
cquery-sem-member-face  ;; defaults to t :slant italic

by default only one face is used for each symbol kind (type/function/variable/member function)

While (setq cquery-sem-highlight-method 'overlay) is more accurate than 'font-lock, it may cause severe performance issues. The short story is that the large number of overlays generated by cquery creates a similarly large number of markers. If the buffer currently edited is a multibyte buffer and contains at least one non-ASCII character, these markers may slow down line-number-at-pos (a function heavily relied upon by lsp-mode) by orders of magnitude; the effect gets worse with increasing distance from (point-min). For the long story, refer to the corresponding emacs-devel thread; if you wish to use cquery-sem-highlight without the associated slowdown, the following options exist (should you find more, please add them to this wiki page):

  1. while this patch by Stefan Monnier does not completely solve the underlying performance issue, it significantly alleviates it for usual buffer sizes

  2. the noverlay branch of Emacs reimplements overlays in a way that avoids markers. Although large numbers of markers would still lead to performance issues with the noverlay branch, cquery performance should be excellent

  3. both 1. and 2. require recompiling Emacs from sources; if that is unacceptable, performance can be restored using (set-buffer-multibyte nil). Note, however, that this will lead to non-ASCII characters being displayed as escape sequences. Also, this workaround has been verified to remove the performance penalty affecting line-number-at-pos, but it has not been tested whether disabling multibyte handling causes other issues with lsp-mode or cquery.

Call/member/inheritance Hierarchies

M-x cquery-member-hierarchy $cquery/memberHierarchy

(cquery-call-hierarchy nil) ; caller hierarchy
(cquery-call-hierarchy t) ; callee hierarchy

$cquery/callHierarchy

(cquery-inheritance-hierarchy nil) ; base hierarchy
(cquery-inheritance-hierarchy t) ; derived hierarchy

$cquery/inheritanceHierarchy

Misc

For out-of-band changes to the files in the workspace that are not made in the LSP client (e.g. git pull), call (cquery-freshen-index) to rebuild indexes for every file or (cquery-freshen-index (list "^/tmp/c/")) to rebuild indexes for files matched by a regex whitelist.