Table of Contents
- Install lsp-mode and optionally, lsp-ui +
company-lsp(company-lsp was deprecated and no longer works with lsp-mode, reference https://github.com/scalameta/metals/issues/2641. You only can install it manually) - Install emacs-ccls (Melpa: https://melpa.org/#/ccls) and configure it
- Open a source file where either .ccls or compile_commands.json is in the project root (it works without them, if you don't need extra compiler command line options like
-I
) (require 'ccls)
,M-x lsp
Configure
doom-emacs
Set (cc +lsp)
in your ~/.config/doom/init.el
spacemacs
set c-c++-backend
to lsp-ccls
in the +lang/c-c++
layer
use-package
(use-package lsp-mode :commands lsp)
(use-package lsp-ui :commands lsp-ui-mode)
(use-package ccls
:hook ((c-mode c++-mode objc-mode cuda-mode) .
(lambda () (require 'ccls) (lsp))))
The only required configuration is ccls-executable
. Others have good defaults.
(setq ccls-executable "/path/to/ccls/Release/ccls")
;; (setq ccls-args '("--log-file=/tmp/ccls.log"))
You may leave ccls-executable
unchanged (default: ccls
) and create a shell script wrapper.
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.
Projects with subprojects
When M-x lsp
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.
flycheck
flymake is used by default.
To use flycheck, (setq lsp-prefer-flymake nil)
. You may also need (setq-default flycheck-disabled-checkers '(c/c++-clang c/c++-cppcheck c/c++-gcc))
to disable other checkers.
Diagnostics
(lsp--client-capabilities)
LSP workspace is initialized correctlyM-: xref-backend-functions
is(lsp--xref-backend)
for cross referencesM-: 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
You need to customize initialization options in S-exp.
Use t
for true, :json-false
for false, :json-null
for null.
(setq ccls-initialization-options '(: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
There are two ways to invoke textDocument/documentSymbol
(outline):
M-x imenu
. Requires lsp-imenu.elM-x lsp-ui-imenu
. Requires lsp-ui-imenu.el.
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
See LSP Extensions for the description of these extensions.
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 (textDocument/implementation) for derived types/functions
; $ccls/inheritance is more general
;; Alternatively, use lsp-ui-peek interface
(lsp-ui-peek-find-custom "$ccls/call")
(lsp-ui-peek-find-custom "$ccls/call" '(:callee t))
Recommended helpers:
(defun ccls/callee () (interactive) (lsp-ui-peek-find-custom "$ccls/call" '(:callee t)))
(defun ccls/caller () (interactive) (lsp-ui-peek-find-custom "$ccls/call"))
(defun ccls/vars (kind) (lsp-ui-peek-find-custom "$ccls/vars" `(:kind ,kind)))
(defun ccls/base (levels) (lsp-ui-peek-find-custom "$ccls/inheritance" `(:levels ,levels)))
(defun ccls/derived (levels) (lsp-ui-peek-find-custom "$ccls/inheritance" `(:levels ,levels :derived t)))
(defun ccls/member (kind) (interactive) (lsp-ui-peek-find-custom "$ccls/member" `(:kind ,kind)))
;; References w/ Role::Role
(defun ccls/references-read () (interactive)
(lsp-ui-peek-find-custom "textDocument/references"
(plist-put (lsp--text-document-position-params) :role 8)))
;; References w/ Role::Write
(defun ccls/references-write ()
(interactive)
(lsp-ui-peek-find-custom "textDocument/references"
(plist-put (lsp--text-document-position-params) :role 16)))
;; References w/ Role::Dynamic bit (macro expansions)
(defun ccls/references-macro () (interactive)
(lsp-ui-peek-find-custom "textDocument/references"
(plist-put (lsp--text-document-position-params) :role 64)))
;; References w/o Role::Call bit (e.g. where functions are taken addresses)
(defun ccls/references-not-call () (interactive)
(lsp-ui-peek-find-custom "textDocument/references"
(plist-put (lsp--text-document-position-params) :excludeRole 32)))
;; ccls/vars ccls/base ccls/derived ccls/members have a parameter while others are interactive.
;; (ccls/base 1) direct bases
;; (ccls/derived 1) direct derived
;; (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 1) => field
;; (ccls/vars 2) => local variable
;; (ccls/vars 3) => field or local variable. 3 = 1 | 2
;; (ccls/vars 4) => parameter
;; References whose filenames are under this project
(lsp-ui-peek-find-references nil (list :folders (vector (projectile-project-root))))
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
DO NOT install company-lsp by melpa. This package is deprecated and no longer works with lsp-mode, from https://github.com/scalameta/metals/issues/2641. You can install it manually.
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)
Install company-quickhelp
to see the attached comments for the selected completion item.
If completion does not work for you, make sure company-lsp is used: run M-x company-diag
and make sureUsed backend:
includes company-lsp
.
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-mode
or ccls.
Call/member/inheritance Hierarchies || primary template/partial specialization
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
Code lens
M-x ccls-code-lens-mode
You may customize ccls-code-lens-position
. It is 'end
by default, which puts code lenses at line ends (implemented with overlay's 'display
property). There is an issue that opening new lines may drag the lens to the next line.
ccls-navigate
Think of them as sp-{down,previous,next,down}-sexp
for C/C++, roughly semantic 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-reload)
to reload/rebuild indexes for every file.
- Performance of lsp-ui-flycheck https://github.com/emacs-lsp/lsp-ui/issues/45