Fangrui Song edited this page Sep 23, 2018 · 16 revisions
  1. See Getting started to build the ccls executable
  2. Install lsp-mode and (optional) lsp-ui
  3. (optionally) Install company-lsp
  4. Install emacs-ccls and configure it
  5. Open a source file where either .ccls or compile_commands.json is in the project root (it may work without them, though not recommended)
  6. M-x lsp-ccls-enable. Don't invoke M-x lsp-mode. lsp-ccls-enable will turn on lsp-mode for you

Install emacs-ccls

https://melpa.org/#/ccls

Configure

The only required configuration is ccls-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 ccls-executable "/path/to/ccls/Release/ccls")
;; ;; Some Linux distributions have packages that install to /usr/bin/ccls
;; (setq ccls-executable "/usr/bin/ccls")

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

A more flexible way is to leave ccls-executable unchanged (default: ccls) and create a shell wrapper named ccls that is in your PATH:

#!/bin/zsh
#export CCLS_TRACEME=1 # if you want to debug ccls, stop it right after main() is called
#export LD_LIBRARY_PATH=~/llvm/release/lib # if you link against shared library libLLVM.so, not statically
exec ~/ccls/Release/ccls --log-file=/tmp/ccls.log "$@"

Some common settings to lsp-mode lsp-ui and emacs-ccls:

Projects with subprojects

When M-x lsp-ccls-enable is invoked, projectile is consulted to locate the project root which determines 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. touch .ccls-root in the root directory 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"
                  ".ccls")
                projectile-project-root-files-top-down-recurring)))

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

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

(use-package ccls
  :commands lsp-ccls-enable
  :init
  (add-hook 'c-mode-hook #'ccls//enable)
  (add-hook 'c++-mode-hook #'ccls//enable)
  )
;; Also see lsp-project-whitelist lsp-project-blacklist ccls-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-ccls stderr* and --log-file=/tmp/cq.log contain logs.

Also refer to Debugging.

Initialization options

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 ccls-extra-init-params '(:index (:comments 2) :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-ccls-enable).

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}

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 (lsp-ui-peek-jump-forward lsp-ui-peek-jump-backward)

lsp-ui-peek-find-references

references + hydra

Refer to MaskRay's doom-emacs configuration for fine-grained textDocument/references (different roles: Read/Write/Dynamic/...)

documentHighlight

The identifier (or user-defined operator) at point is highlighted with its other references. You may customize lsp-face-highlight-*. 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.

Cross reference extensions

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

;; direct callers
(lsp-find-custom "$ccls/call")
;; callers up to 2 levels
(lsp-find-custom "$ccls/call" '(:levels 2))
;; direct callees
(lsp-find-custom "$ccls/call" '(:callee t))

(lsp-find-custom "$ccls/vars")
; Use lsp-goto-implementation or lsp-ui-peek-find-implementation for derived types/functions

;; Alternatively, use lsp-ui-peek interface
(lsp-ui-peek-find-custom 'caller "$ccls/call")
(lsp-ui-peek-find-custom 'callee "$ccls/call" '(:callee t))

(defun ccls/vars (kind) (lsp-ui-peek-find-custom 'vars "$ccls/vars" `(:kind ,kind)))
(ccls/vars 3) ;; field or local variable
(ccls/vars 1) ;; field
(ccls/vars 4) ;; parameter

Recommended helpers:

(defun ccls/callee ()
  (interactive)
  (lsp-ui-peek-find-custom 'callee "$ccls/call" '(:callee t)))
(defun ccls/caller ()
  (interactive)
  (lsp-ui-peek-find-custom 'caller "$ccls/call"))
(defun ccls/vars (kind)
  (lsp-ui-peek-find-custom 'vars "$ccls/vars" `(:kind ,kind)))
(defun ccls/base (levels)
  (lsp-ui-peek-find-custom 'base "$ccls/inheritance" `(:levels ,levels)))
(defun ccls/derived (levels)
  (lsp-ui-peek-find-custom 'derived "$ccls/inheritance" `(:levels ,levels :derived t)))
(defun ccls/member (kind)
  (interactive)
  (lsp-ui-peek-find-custom 'member "$ccls/member" `(:kind ,kind)))

;; ccls/vars ccls/base ccls/derived ccls/members have a parameter while others are interactive.
;; (ccls/base 1)
;; (ccls/derived 1)
;; (ccls/member 2) => 2 (Type) => nested classes / types in a namespace
;; (ccls/member 3) => 3 (Func) => member functions / functions in a namespace
;; (ccls/member 0) => member variables / variables in a namespace
;; (ccls/vars 3) => field or local variable

Hierarchies provide a flattened xref interface:

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

Add company-lsp to company-backends. ccls has a fuzzy matching algorithm to order candidates according to your query. You may want to disable client-side cache and sorting:

(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.

There is an alternative view (irony-mode style):

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

company-lsp + company-quickhelp

lsp-ui-flycheck

Semantic highlighting

To enable semantic highlighting:

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

;; For rainbow semantic highlighting
(ccls-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:

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

;; To customize faces used
(face-spec-set 'ccls-sem-member-face
               '((t :slant "normal"))
               'face-defface-spec)

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

While (setq ccls-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 ccls 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 ccls-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, ccls 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 ccls.

Call/member/inheritance Hierarchies

M-x ccls-member-hierarchy $ccls/member hierarchy:true

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

$ccls/call

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

$ccls/inheritance hierarchy:true

ccls-navigate

Think of them as sp-{down,previous,next,down}-sexp for C/C++, roughly movement among declarations.

(ccls-navigate "D") ;; roughly sp-down-sexp
(ccls-navigate "L")
(ccls-navigate "R")
(ccls-navigate "U")

https://www.reddit.com/r/emacs/comments/9dg13i/cclsnavigate_semantic_navigation_for_cc/

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 (ccls-freshen-index) to rebuild indexes for every file or (ccls-freshen-index (list "^/tmp/c/")) to rebuild indexes for files matched by a regex whitelist.

You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.