]> git.lizzy.rs Git - rust.git/blob - docs/dev/README.md
Merge #4703
[rust.git] / docs / dev / README.md
1 # Contributing Quick Start
2
3 Rust Analyzer is just a usual rust project, which is organized as a Cargo
4 workspace, builds on stable and doesn't depend on C libraries. So, just
5
6 ```
7 $ cargo test
8 ```
9
10 should be enough to get you started!
11
12 To learn more about how rust-analyzer works, see
13 [./architecture.md](./architecture.md) document.
14
15 We also publish rustdoc docs to pages:
16
17 https://rust-analyzer.github.io/rust-analyzer/ra_ide/
18
19 Various organizational and process issues are discussed in this document.
20
21 # Getting in Touch
22
23 Rust Analyzer is a part of [RLS-2.0 working
24 group](https://github.com/rust-lang/compiler-team/tree/6a769c13656c0a6959ebc09e7b1f7c09b86fb9c0/working-groups/rls-2.0).
25 Discussion happens in this Zulip stream:
26
27 https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0
28
29 # Issue Labels
30
31 * [good-first-issue](https://github.com/rust-analyzer/rust-analyzer/labels/good%20first%20issue)
32   are good issues to get into the project.
33 * [E-has-instructions](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-has-instructions)
34   issues have links to the code in question and tests.
35 * [E-easy](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-easy),
36   [E-medium](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-medium),
37   [E-hard](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-hard),
38   labels are *estimates* for how hard would be to write a fix.
39 * [fun](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3Afun)
40   is for cool, but probably hard stuff.
41
42 # CI
43
44 We use GitHub Actions for CI. Most of the things, including formatting, are checked by
45 `cargo test` so, if `cargo test` passes locally, that's a good sign that CI will
46 be green as well. The only exception is that some long-running tests are skipped locally by default.
47 Use `env RUN_SLOW_TESTS=1 cargo test` to run the full suite.
48
49 We use bors-ng to enforce the [not rocket science](https://graydon2.dreamwidth.org/1597.html) rule.
50
51 You can run `cargo xtask install-pre-commit-hook` to install git-hook to run rustfmt on commit.
52
53 # Code organization
54
55 All Rust code lives in the `crates` top-level directory, and is organized as a
56 single Cargo workspace. The `editors` top-level directory contains code for
57 integrating with editors. Currently, it contains the plugin for VS Code (in
58 typescript). The `docs` top-level directory contains both developer and user
59 documentation.
60
61 We have some automation infra in Rust in the `xtask` package. It contains
62 stuff like formatting checking, code generation and powers `cargo xtask install`.
63 The latter syntax is achieved with the help of cargo aliases (see `.cargo`
64 directory).
65
66 # Launching rust-analyzer
67
68 Debugging language server can be tricky: LSP is rather chatty, so driving it
69 from the command line is not really feasible, driving it via VS Code requires
70 interacting with two processes.
71
72 For this reason, the best way to see how rust-analyzer works is to find a
73 relevant test and execute it (VS Code includes an action for running a single
74 test).
75
76 However, launching a VS Code instance with locally build language server is
77 possible. There's **"Run Extension (Debug Build)"** launch configuration for this.
78
79 In general, I use one of the following workflows for fixing bugs and
80 implementing features.
81
82 If the problem concerns only internal parts of rust-analyzer (ie, I don't need
83 to touch `rust-analyzer` crate or typescript code), there is a unit-test for it.
84 So, I use **Rust Analyzer: Run** action in VS Code to run this single test, and
85 then just do printf-driven development/debugging. As a sanity check after I'm
86 done, I use `cargo xtask install --server` and **Reload Window** action in VS
87 Code to sanity check that the thing works as I expect.
88
89 If the problem concerns only the VS Code extension, I use **Run Installed Extension**
90 launch configuration from `launch.json`. Notably, this uses the usual
91 `rust-analyzer` binary from `PATH`. For this it is important to have the following
92 in `setting.json` file:
93 ```json
94 {
95     "rust-analyzer.serverPath": "rust-analyzer"
96 }
97 ```
98 After I am done with the fix, I use `cargo
99 xtask install --client-code` to try the new extension for real.
100
101 If I need to fix something in the `rust-analyzer` crate, I feel sad because it's
102 on the boundary between the two processes, and working there is slow. I usually
103 just `cargo xtask install --server` and poke changes from my live environment.
104 Note that this uses `--release`, which is usually faster overall, because
105 loading stdlib into debug version of rust-analyzer takes a lot of time. To speed
106 things up, sometimes I open a temporary hello-world project which has
107 `"rust-analyzer.withSysroot": false` in `.code/settings.json`. This flag causes
108 rust-analyzer to skip loading the sysroot, which greatly reduces the amount of
109 things rust-analyzer needs to do, and makes printf's more useful. Note that you
110 should only use `eprint!` family of macros for debugging: stdout is used for LSP
111 communication, and `print!` would break it.
112
113 If I need to fix something simultaneously in the server and in the client, I
114 feel even more sad. I don't have a specific workflow for this case.
115
116 Additionally, I use `cargo run --release -p rust-analyzer -- analysis-stats
117 path/to/some/rust/crate` to run a batch analysis. This is primarily useful for
118 performance optimizations, or for bug minimization.
119
120 # Code Style & Review Process
121
122 Our approach to "clean code" is two fold:
123
124 * We generally don't block PRs on style changes.
125 * At the same time, all code in rust-analyzer is constantly refactored.
126
127 It is explicitly OK for reviewer to flag only some nits in the PR, and than send a follow up cleanup PR for things which are easier to explain by example, cc-ing the original author.
128 Sending small cleanup PRs (like rename a single local variable) is encouraged.
129
130 ## Scale of Changes
131
132 Everyone knows that it's better to send small & focused pull requests.
133 The problem is, sometimes you *have* to, eg, rewrite the whole compiler, and that just doesn't fit into a set of isolated PRs.
134
135 The main thing too keep an eye on is the boundaries between various components.
136 There are three kinds of changes:
137
138 1. Internals of a single component are changed.
139    Specifically, you don't change any `pub` items.
140    A good example here would be an addition of a new assist.
141
142 2. API of a component is expanded.
143    Specifically, you add a new `pub` function which wasn't there before.
144    A good example here would be expansion of assist API, for example, to implement lazy assists or assists groups.
145
146 3. A new dependency between components is introduced.
147    Specifically, you add a `pub use` reexport from another crate or you add a new line to `[dependencies]` section of `Cargo.toml`.
148    A good example here would be adding reference search capability to the assists crates.
149
150 For the first group, the change is generally merged as long as:
151
152 * it works for the happy case,
153 * it has tests,
154 * it doesn't panic for unhappy case.
155
156 For the second group, the change would be subjected to quite a bit of scrutiny and iteration.
157 The new API needs to be right (or at least easy to change later).
158 The actual implementation doesn't matter that much.
159 It's very important to minimize the amount of changed lines of code for changes of the second kind.
160 Often, you start doing change of the first kind, only to realise that you need to elevate to a change of the second kind.
161 In this case, we'll probably ask you to split API changes into a separate PR.
162
163 Changes of the third group should be pretty rare, so we don't specify any specific process for them.
164 That said, adding an innocent-looking `pub use` is a very simple way to break encapsulation, keep an eye on it!
165
166 Note: if you enjoyed this abstract hand-waving about boundaries, you might appreciate
167 https://www.tedinski.com/2018/02/06/system-boundaries.html
168
169 ## Order of Imports
170
171 We separate import groups with blank lines
172
173 ```
174 mod x;
175 mod y;
176
177 use std::{ ... }
178
179 use crate_foo::{ ... }
180 use crate_bar::{ ... }
181
182 use crate::{}
183
184 use super::{} // but prefer `use crate::`
185 ```
186
187 ## Order of Items
188
189 Optimize for the reader who sees the file for the first time, and wants to get the general idea about what's going on.
190 People read things from top to bottom, so place most important things first.
191
192 Specifically, if all items except one are private, always put the non-private item on top.
193
194 Put `struct`s and `enum`s first, functions and impls last.
195
196 Do
197
198 ```
199 // Good
200 struct Foo {
201   bars: Vec<Bar>
202 }
203
204 struct Bar;
205 ```
206
207 rather than
208
209 ```
210 // Not as good
211 struct Bar;
212
213 struct Foo {
214   bars: Vec<Bar>
215 }
216 ```
217
218 ## Documentation
219
220 For `.md` and `.adoc` files, prefer a sentence-per-line format, don't wrap lines.
221 If the line is too long, you want to split the sentence in two :-)
222
223 # Logging
224
225 Logging is done by both rust-analyzer and VS Code, so it might be tricky to
226 figure out where logs go.
227
228 Inside rust-analyzer, we use the standard `log` crate for logging, and
229 `env_logger` for logging frontend. By default, log goes to stderr, but the
230 stderr itself is processed by VS Code.
231
232 To see stderr in the running VS Code instance, go to the "Output" tab of the
233 panel and select `rust-analyzer`. This shows `eprintln!` as well. Note that
234 `stdout` is used for the actual protocol, so `println!` will break things.
235
236 To log all communication between the server and the client, there are two choices:
237
238 * you can log on the server side, by running something like
239   ```
240   env RA_LOG=gen_lsp_server=trace code .
241   ```
242
243 * you can log on the client side, by enabling `"rust-analyzer.trace.server":
244   "verbose"` workspace setting. These logs are shown in a separate tab in the
245   output and could be used with LSP inspector. Kudos to
246   [@DJMcNab](https://github.com/DJMcNab) for setting this awesome infra up!
247
248
249 There's also two VS Code commands which might be of interest:
250
251 * `Rust Analyzer: Status` shows some memory-usage statistics. To take full
252   advantage of it, you need to compile rust-analyzer with jemalloc support:
253   ```
254   $ cargo install --path crates/rust-analyzer --force --features jemalloc
255   ```
256
257   There's an alias for this: `cargo xtask install --server --jemalloc`.
258
259 * `Rust Analyzer: Syntax Tree` shows syntax tree of the current file/selection.
260
261   You can hover over syntax nodes in the opened text file to see the appropriate
262   rust code that it refers to and the rust editor will also highlight the proper
263   text range.
264
265   If you press <kbd>Ctrl</kbd> (i.e. trigger goto definition) in the inspected
266   Rust source file the syntax tree read-only editor should scroll to and select the
267   appropriate syntax node token.
268
269   ![demo](https://user-images.githubusercontent.com/36276403/78225773-6636a480-74d3-11ea-9d9f-1c9d42da03b0.png)
270
271 # Profiling
272
273 We have a built-in hierarchical profiler, you can enable it by using `RA_PROFILE` env-var:
274
275 ```
276 RA_PROFILE=*             // dump everything
277 RA_PROFILE=foo|bar|baz   // enabled only selected entries
278 RA_PROFILE=*@3>10        // dump everything, up to depth 3, if it takes more than 10 ms
279 ```
280
281 In particular, I have `export RA_PROFILE='*>10'` in my shell profile.
282
283 To measure time for from-scratch analysis, use something like this:
284
285 ```
286 $ cargo run --release -p rust-analyzer -- analysis-stats ../chalk/
287 ```
288
289 For measuring time of incremental analysis, use either of these:
290
291 ```
292 $ cargo run --release -p rust-analyzer -- analysis-bench ../chalk/ --highlight ../chalk/chalk-engine/src/logic.rs
293 $ cargo run --release -p rust-analyzer -- analysis-bench ../chalk/ --complete ../chalk/chalk-engine/src/logic.rs:94:0
294 ```