File: CPP-guide.md

package info (click to toggle)
lsp-mode 9.0.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 22,472 kB
  • sloc: lisp: 27,961; makefile: 62; java: 34; cpp: 33; javascript: 31; xml: 23; python: 14; sh: 2
file content (267 lines) | stat: -rw-r--r-- 11,660 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
---
template: comment.html
root_file: docs/tutorials/CPP-guide.md
---

# Configuring `Emacs` as a `C/C++` IDE

In this guide, I will show you how to configure `lsp-mode` and `dap-mode` for C/C++ development, using `GNU Emacs` as an example code base. At the end of this tutorial, Emacs should provide you with all the language-aware editor features you'd expect from a modern C++ IDE, such as

* Code completion
* Real-time syntax checking
* Code navigation (references/definitions/implementations/symbol search)
* Visual IDE debugger
* Document outline, breadcrumb and modeline widgets

For all `lsp-mode` features, check [here](https://emacs-lsp.github.io/lsp-mode/page/main-features/).

# Obtaining a language server

`lsp-mode` is a client-server application with `Emacs` acting as the client. In
order for it to work, you have to install a separate _language server_ that
understands the specifics of your target language. As of late 2020, there are 2
production-ready language servers for `C/C++` and `Objective-C`,
[clangd](https://clangd.llvm.org/installation.html), and
[ccls](https://github.com/MaskRay/ccls) with `ccls`'s precursor
`cquery` being deprecated.

For some of the language
servers, `lsp-mode` ships with automatic installation scripts but there is no
such script for installing a `C/C++` language server due to their overall
complexity. For this guide we will be using
`clangd` but most of the steps here apply to `ccls` as well. `lsp-mode` is
pre-configured and it will be able to find the language server automatically
when it is installed on the host machine and it is present on the `PATH`. In case
`clangd` is not present on the path `clangd` can be installed following their
[Getting Started](https://clangd.llvm.org/installation.html) instructions.

_Note:_ for `Debian` based system use https://apt.llvm.org/

## Project setup

To understand your source code, `clangd` needs to know the compiler flags that are used to build the project. (This is
just a fact of life in C++, source files are not self-contained).

By default, `clangd` will assume your code is built as `clang some_file.cc`, and
you’ll probably get spurious errors about missing `#include` files, etc. There
are a couple of ways to fix this. You may read about them in [clangd installation](https://clangd.llvm.org/installation.html) or in https://sarcasm.github.io/notes/dev/compilation-database.html.

In order to generate `compile_commands.json` for `Emacs` itself I have found
that [Bear](https://github.com/rizsotto/Bear) works fine.

``` bash
sudo apt-get install bear
```

Setup `Emacs` sources and dependencies:

``` bash
sudo apt-get install -y build-essential git autoconf texinfo libgnutls28-dev libxml2-dev libncurses5-dev libjansson-dev

git clone git://git.sv.gnu.org/emacs.git
cd emacs
./autogen.sh
CFLAGS="-ggdb3 -O0" CXXFLAGS="-ggdb3 -O0" LDFLAGS="-ggdb3" ./configure --with-modules --with-json
bear -- make -j$(nproc)
```

Prefixing `make -j$(nproc)` with `bear` will generate the
`compile_commands.json` which later will be used by `clangd`. `CFLAGS="-ggdb3
-O0" CXXFLAGS="-ggdb3 -O0" LDFLAGS="-ggdb3"` will compile `Emacs` with debug
symbols.

# lsp-mode configuration

Here is a bare-bones `lsp-mode`/`dap-mode` configuration template to get you started with your own `lsp-mode` config, or to try out in a separate one-off session. Please note that Emacs configuration frameworks such as Spacemacs or Doom Emacs often ship with `lsp-mode` settings of their own; should you be using such a framework, and find that `lsp-mode` doesn't behave as intended, please make sure to follow this tutorial from a clean starting point.
in your config or you could run in separate session.

``` emacs-lisp
(require 'package)
(add-to-list 'package-archives '("melpa" . "http://melpa.org/packages/") t)
(package-initialize)

(setq package-selected-packages '(lsp-mode yasnippet lsp-treemacs helm-lsp
    projectile hydra flycheck company avy which-key helm-xref dap-mode))

(when (cl-find-if-not #'package-installed-p package-selected-packages)
  (package-refresh-contents)
  (mapc #'package-install package-selected-packages))

;; sample `helm' configuration use https://github.com/emacs-helm/helm/ for details
(helm-mode)
(require 'helm-xref)
(define-key global-map [remap find-file] #'helm-find-files)
(define-key global-map [remap execute-extended-command] #'helm-M-x)
(define-key global-map [remap switch-to-buffer] #'helm-mini)

(which-key-mode)
(add-hook 'c-mode-hook 'lsp)
(add-hook 'c++-mode-hook 'lsp)

(setq gc-cons-threshold (* 100 1024 1024)
      read-process-output-max (* 1024 1024)
      treemacs-space-between-root-nodes nil
      company-idle-delay 0.0
      company-minimum-prefix-length 1
      lsp-idle-delay 0.1)  ;; clangd is fast

(with-eval-after-load 'lsp-mode
  (add-hook 'lsp-mode-hook #'lsp-enable-which-key-integration)
  (require 'dap-cpptools)
  (yas-global-mode))
```

# Feature overview

And now it is time to start hacking on `Emacs` core!

```
emacs ~/Sources/emacs/src/editfns.c
```

Now, if everything was successful you will be asked to select a project root.

![select project root](images/import-project.png "Select project root")

_Note:_ the project root is needed by the language server in order to know where to
start the project discovery from. Typically, this is the git repository root but since exceptions to this rule have caused us a lot of trouble in the past (monorepos come to mind), `lsp-mode` by default asks the user to manually confirm the project root the first time a project is opened.

### Completion

By default, `lsp-mode` uses `company-mode` as its completion frontend. When
present, `company-mode` will be auto-configured and it will just work.

![company-mode completion](images/completion.png "company-mode completion")

### keybindings/`which-key` integration

`lsp-mode` has smart (almost) complete mnemonic keybindings which auto-enable
when a certain feature is supported by the server and when the
corresponding `Emacs` package is installed. In addition, `lsp-mode`
ships with [which-key](https://github.com/justbur/emacs-which-key) integration for better discoverability.
By default, `lsp-mode`'s keybindings are available under `s-l` (Super-l), e. g. `s-l h h` will
show documentation at point. You may change the default prefix by setting
`lsp-keymap-prefix`.

![select project root](images/which-key.png "Select project root")

### Mouse support
Yeah, `lsp-mode` supports mouse!

![mouse support](images/mouse.png "Mouse support")

### Refactoring/Code actions

- `lsp-rename` (`s-l r r`) - rename symbol/function at point.
- `lsp-execute-code-action` (`s-l a a`) - `clangd` is able to auto-correct some of the errors.

### Navigation
`lsp-mode` has integration with `xref` core package and in addition it has

- `xref-find-definitions`(`M-.` and `s-l g g`) - find definition(s) at point
- `xref-find-references`(`s-l g r`) - find references to the symbol at point
![References](images/references-helm.png "References helm")
- `helm-imenu` - browse symbols in the current document
- `helm-lsp-workspace-symbol` - find symbol in current project
![workspace symbols](images/workspace-symbols.png "workspace symbols")
- `helm-lsp-global-workspaces-symbol` - find symbol in all projects
- `lsp-treemacs-type-hierarchy` - show type hierarchy

_Note:_ if you prefer [ivy](https://github.com/abo-abo/swiper) over `helm` you may check out [lsp-ivy](https://github.com/emacs-lsp/lsp-ivy).

### [lsp-treemacs](https://github.com/emacs-lsp/lsp-treemacs)
`lsp-treemacs` is a package providing integration with
[treemacs](https://github.com/Alexander-Miller/treemacs) and an alternative
tree-view visualization. Refer to the project's readme for further information.

![tree references](images/treemacs-references.png "Treemacs references")

### Help/Documentation
`lsp-mode` automatically enables `eldoc-mode` which will show hover information
in the minibuffer. `lsp-mode` is showing only the one line signature info so if
you want to see the full documentation at point you can use `lsp-describe-thing-at-point`.

![hover](images/hover-info.png)

In addition to that you can also see signature help after pressing `C-M-SPC` or
after pressing trigger char like `(`. If there is more than one applicable signature due to function overloading, you may browse between the available candidates using `M-n/M-p`.
may browse them via `M-n/M-p`

![signature info](images/signature-help.png)

### Diagnostics
For on-the-fly errors `lsp-mode` is using `flycheck` (`flymake` is also
supported). It is configured automatically. In addition to standard `flycheck`
features `lsp-mode` provides a project-wide error list via
`lsp-treemacs-errors-list`.

![errors list](images/errors-list.png)

## Debugging

Just like `lsp-mode` provides editing features through the editor-independent
[Language Server Protocol](https://microsoft.github.io/language-server-protocol/), its sister package `dap-mode` provides debugging
features through the editor-independent [Debug Adapter Protocol](https://microsoft.github.io/debug-adapter-protocol/) and
language-specific debugging servers called _debug adapters_. front-end of the
sister [Debug Adapter Protocol](https://microsoft.github.io/debug-adapter-protocol/). It is again client server like `Language
Sever Protocol`. `dap-mode` provides all of the traditional debugger features -
breakpoints(conditions, hit count, etc), threads, locals.

### Installation

`dap-mode` provides installation commands for most of the debug adapters. In this
tutorial we will use the [vscode-cpptools](https://github.com/microsoft/vscode-cpptools) debug adapter. To install the adapter
do `M-x dap-cpptools-setup`.

_Note:_ you should have `gdb` on path.
_Note:_ on linux you should install [mono](https://www.mono-project.com/).

### Creating debug configuration

`dap-mode` introduces the notion of debug configuration and debug template. The
debug configuration is the settings that are needed by the debug adapter to
start the program to debug. Debug template is a template for such configuration
which will be populated by the user or by `dap-mode`. There are two ways to
manage debug configuration - using `emacs lisp` via `dap-debug-edit-template`
and `dap-register-debug-template`. The second way is using `launch.json`. Put it
in project root and `dap-mode` will pick it up.


``` json
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Debug Emacs",
            "type": "cppdbg",
            "request": "launch",
            "program": "${workspaceFolder}/src/emacs",
            "args": ["-q"],
            "stopAtEntry": false,
            "cwd": "${workspaceFolder}",
            "environment": [],
            "externalConsole": false,
            "MIMode": "gdb"
        }
    ]
}
```

At this point, `dap-mode` does not provide the list of all available properties
for each configuration. The full list can be found in `package.json` in
`/.emacs.d/.extension/vscode/cpptools/extension/package.json` when `dap-mode` is
using `VScode` package or debug adapter docs when not.

### Debugging

Start debugging via `M-x dap-debug` and select `Debug Emacs` from the list.
![select debug template](images/select-template.png "Select debug template").

After pressing `RET` a fresh instance of `Emacs` will pop up. Then do `M-x
helm-lsp-workspace-symbol` and type `message3`. Go to that function and place a
breakpoint. You could do that by clicking in the fringe or by doing `M-x
dap-breakpoint-toggle`. Switch to the debugged instance and do

`M-: (message "Hello, I am debugging Emacs core!") RET`

![debugging](images/debug.png "Debug").