Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign upEmacs
- See Getting started to build the
cclsexecutable - Install lsp-mode and (optional) lsp-ui
- (optionally) Install company-lsp
- Install emacs-ccls and configure it
- Open a source file where either .ccls or compile_commands.json is in the project root (it may work without them, though not recommended)
-
M-x lsp-ccls-enable. Don't invokeM-x lsp-mode.lsp-ccls-enablewill turn onlsp-modefor you
Install emacs-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:
- spacemacs
+tools/lspand https://github.com/syl20bnr/spacemacs/pull/11242 - doom-emacs https://github.com/hlissner/doom-emacs/pull/716
- MaskRay's doom-emacs configuration: https://github.com/MaskRay/Config/blob/master/home/.config/doom/modules/private/my-cc/config.el
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-matchersDiagnostics
-
M-x lsp-capabilitiesLSP workspace is initialized correctly -
M-: xref-backend-functionsis(lsp--xref-backend)for cross references -
M-: completion-at-point-functionsis(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)
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-*.
imenu/workspace symbol search
Use lsp-imenu.el for textDocument/documentSymbol (outline). lsp-ui-imenu.el provides a sidebar.
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) ;; parameterRecommended 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 variableHierarchies 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 infoCompletion
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)))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):
-
while this patch by Stefan Monnier does not completely solve the underlying performance issue, it significantly alleviates it for usual buffer sizes
-
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
-
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 affectingline-number-at-pos, but it has not been tested whether disabling multibyte handling causes other issues withlsp-modeor ccls.
Call/member/inheritance Hierarchies
M-x ccls-member-hierarchy
(ccls-call-hierarchy nil) ; caller hierarchy
(ccls-call-hierarchy t) ; callee hierarchy(ccls-inheritance-hierarchy nil) ; base hierarchy
(ccls-inheritance-hierarchy t) ; derived hierarchy
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.
- Discussion of symbol hierarchies (member hierarchy, inheritance hierarchy, call hierarchy) https://github.com/emacs-lsp/lsp-ui/issues/73
- Performance of lsp-ui-flycheck https://github.com/emacs-lsp/lsp-ui/issues/45
- A proposal to mention ccls in eglot https://github.com/joaotavora/eglot/pull/94










