aboutsummaryrefslogtreecommitdiff
path: root/docs/dev
diff options
context:
space:
mode:
Diffstat (limited to 'docs/dev')
-rw-r--r--docs/dev/README.md84
-rw-r--r--docs/dev/architecture.md13
-rw-r--r--docs/dev/lsp-extensions.md51
-rw-r--r--docs/dev/style.md82
-rw-r--r--docs/dev/syntax.md8
5 files changed, 162 insertions, 76 deletions
diff --git a/docs/dev/README.md b/docs/dev/README.md
index 57162a47d..d0e6d29d8 100644
--- a/docs/dev/README.md
+++ b/docs/dev/README.md
@@ -1,7 +1,7 @@
1# Contributing Quick Start 1# Contributing Quick Start
2 2
3Rust Analyzer is an ordinary Rust project, which is organized as a Cargo 3Rust Analyzer is an ordinary Rust project, which is organized as a Cargo workspace, builds on stable and doesn't depend on C libraries.
4workspace, builds on stable and doesn't depend on C libraries. So, just 4So, just
5 5
6``` 6```
7$ cargo test 7$ cargo test
@@ -13,9 +13,8 @@ To learn more about how rust-analyzer works, see [./architecture.md](./architect
13It also explains the high-level layout of the source code. 13It also explains the high-level layout of the source code.
14Do skim through that document. 14Do skim through that document.
15 15
16We also publish rustdoc docs to pages: 16We also publish rustdoc docs to pages: https://rust-analyzer.github.io/rust-analyzer/ide/.
17 17Note though, that internal documentation is very incomplete.
18https://rust-analyzer.github.io/rust-analyzer/ide/
19 18
20Various organizational and process issues are discussed in this document. 19Various organizational and process issues are discussed in this document.
21 20
@@ -25,7 +24,7 @@ Rust Analyzer is a part of [RLS-2.0 working
25group](https://github.com/rust-lang/compiler-team/tree/6a769c13656c0a6959ebc09e7b1f7c09b86fb9c0/working-groups/rls-2.0). 24group](https://github.com/rust-lang/compiler-team/tree/6a769c13656c0a6959ebc09e7b1f7c09b86fb9c0/working-groups/rls-2.0).
26Discussion happens in this Zulip stream: 25Discussion happens in this Zulip stream:
27 26
28https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0 27https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frust-analyzer
29 28
30# Issue Labels 29# Issue Labels
31 30
@@ -33,6 +32,8 @@ https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0
33 are good issues to get into the project. 32 are good issues to get into the project.
34* [E-has-instructions](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-has-instructions) 33* [E-has-instructions](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-has-instructions)
35 issues have links to the code in question and tests. 34 issues have links to the code in question and tests.
35* [Broken Window](https://github.com/rust-analyzer/rust-analyzer/issues?q=is:issue+is:open+label:%22Broken+Window%22)
36 are issues which are not critical by themselves, but which should be fixed ASAP regardless, to avoid accumulation of technical debt.
36* [E-easy](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-easy), 37* [E-easy](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-easy),
37 [E-medium](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-medium), 38 [E-medium](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-medium),
38 [E-hard](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-hard), 39 [E-hard](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-hard),
@@ -49,21 +50,28 @@ https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0
49 Also a kind of fun. 50 Also a kind of fun.
50 These issues should generally include a link to a Zulip discussion thread. 51 These issues should generally include a link to a Zulip discussion thread.
51 52
52# CI 53# Code Style & Review Process
54
55Do see [./style.md](./style.md).
53 56
54We use GitHub Actions for CI. Most of the things, including formatting, are checked by 57# Cookbook
55`cargo test` so, if `cargo test` passes locally, that's a good sign that CI will 58
56be green as well. The only exception is that some long-running tests are skipped locally by default. 59## CI
60
61We use GitHub Actions for CI.
62Most of the things, including formatting, are checked by `cargo test`.
63If `cargo test` passes locally, that's a good sign that CI will be green as well.
64The only exception is that some long-running tests are skipped locally by default.
57Use `env RUN_SLOW_TESTS=1 cargo test` to run the full suite. 65Use `env RUN_SLOW_TESTS=1 cargo test` to run the full suite.
58 66
59We use bors-ng to enforce the [not rocket science](https://graydon2.dreamwidth.org/1597.html) rule. 67We use bors-ng to enforce the [not rocket science](https://graydon2.dreamwidth.org/1597.html) rule.
60 68
61# Launching rust-analyzer 69## Launching rust-analyzer
62 70
63Debugging the language server can be tricky. 71Debugging the language server can be tricky.
64LSP is rather chatty, so driving it from the command line is not really feasible, driving it via VS Code requires interacting with two processes. 72LSP is rather chatty, so driving it from the command line is not really feasible, driving it via VS Code requires interacting with two processes.
65 73
66For this reason, the best way to see how rust-analyzer works is to find a relevant test and execute it. 74For this reason, the best way to see how rust-analyzer works is to **find a relevant test and execute it**.
67VS Code & Emacs include an action for running a single test. 75VS Code & Emacs include an action for running a single test.
68 76
69Launching a VS Code instance with a locally built language server is also possible. 77Launching a VS Code instance with a locally built language server is also possible.
@@ -107,12 +115,7 @@ cd editors/code
107npm ci 115npm ci
108npm run lint 116npm run lint
109``` 117```
110 118## How to ...
111# Code Style & Review Process
112
113Do see [./style.md](./style.md).
114
115# How to ...
116 119
117* ... add an assist? [#7535](https://github.com/rust-analyzer/rust-analyzer/pull/7535) 120* ... add an assist? [#7535](https://github.com/rust-analyzer/rust-analyzer/pull/7535)
118* ... add a new protocol extension? [#4569](https://github.com/rust-analyzer/rust-analyzer/pull/4569) 121* ... add a new protocol extension? [#4569](https://github.com/rust-analyzer/rust-analyzer/pull/4569)
@@ -120,33 +123,30 @@ Do see [./style.md](./style.md).
120* ... add a new completion? [#6964](https://github.com/rust-analyzer/rust-analyzer/pull/6964) 123* ... add a new completion? [#6964](https://github.com/rust-analyzer/rust-analyzer/pull/6964)
121* ... allow new syntax in the parser? [#7338](https://github.com/rust-analyzer/rust-analyzer/pull/7338) 124* ... allow new syntax in the parser? [#7338](https://github.com/rust-analyzer/rust-analyzer/pull/7338)
122 125
123# Logging 126## Logging
124 127
125Logging is done by both rust-analyzer and VS Code, so it might be tricky to 128Logging is done by both rust-analyzer and VS Code, so it might be tricky to figure out where logs go.
126figure out where logs go.
127 129
128Inside rust-analyzer, we use the standard `log` crate for logging, and 130Inside rust-analyzer, we use the standard `log` crate for logging, and `env_logger` for logging frontend.
129`env_logger` for logging frontend. By default, log goes to stderr, but the 131By default, log goes to stderr, but the stderr itself is processed by VS Code.
130stderr itself is processed by VS Code. 132`--log-file <PATH>` CLI argument allows logging to file.
131 133
132To see stderr in the running VS Code instance, go to the "Output" tab of the 134To see stderr in the running VS Code instance, go to the "Output" tab of the panel and select `rust-analyzer`.
133panel and select `rust-analyzer`. This shows `eprintln!` as well. Note that 135This shows `eprintln!` as well.
134`stdout` is used for the actual protocol, so `println!` will break things. 136Note that `stdout` is used for the actual protocol, so `println!` will break things.
135 137
136To log all communication between the server and the client, there are two choices: 138To log all communication between the server and the client, there are two choices:
137 139
138* you can log on the server side, by running something like 140* You can log on the server side, by running something like
139 ``` 141 ```
140 env RA_LOG=lsp_server=debug code . 142 env RA_LOG=lsp_server=debug code .
141 ``` 143 ```
144* You can log on the client side, by enabling `"rust-analyzer.trace.server": "verbose"` workspace setting.
145 These logs are shown in a separate tab in the output and could be used with LSP inspector.
146 Kudos to [@DJMcNab](https://github.com/DJMcNab) for setting this awesome infra up!
142 147
143* you can log on the client side, by enabling `"rust-analyzer.trace.server":
144 "verbose"` workspace setting. These logs are shown in a separate tab in the
145 output and could be used with LSP inspector. Kudos to
146 [@DJMcNab](https://github.com/DJMcNab) for setting this awesome infra up!
147 148
148 149There are also several VS Code commands which might be of interest:
149There are also two VS Code commands which might be of interest:
150 150
151* `Rust Analyzer: Status` shows some memory-usage statistics. 151* `Rust Analyzer: Status` shows some memory-usage statistics.
152 152
@@ -164,7 +164,7 @@ There are also two VS Code commands which might be of interest:
164 164
165 ![demo](https://user-images.githubusercontent.com/36276403/78225773-6636a480-74d3-11ea-9d9f-1c9d42da03b0.png) 165 ![demo](https://user-images.githubusercontent.com/36276403/78225773-6636a480-74d3-11ea-9d9f-1c9d42da03b0.png)
166 166
167# Profiling 167## Profiling
168 168
169We have a built-in hierarchical profiler, you can enable it by using `RA_PROFILE` env-var: 169We have a built-in hierarchical profiler, you can enable it by using `RA_PROFILE` env-var:
170 170
@@ -192,7 +192,9 @@ $ cargo run --release -p rust-analyzer -- analysis-bench ../chalk/ --highlight .
192$ cargo run --release -p rust-analyzer -- analysis-bench ../chalk/ --complete ../chalk/chalk-engine/src/logic.rs:94:0 192$ cargo run --release -p rust-analyzer -- analysis-bench ../chalk/ --complete ../chalk/chalk-engine/src/logic.rs:94:0
193``` 193```
194 194
195# Release Process 195Look for `fn benchmark_xxx` tests for a quick way to reproduce performance problems.
196
197## Release Process
196 198
197Release process is handled by `release`, `dist` and `promote` xtasks, `release` being the main one. 199Release process is handled by `release`, `dist` and `promote` xtasks, `release` being the main one.
198 200
@@ -229,16 +231,18 @@ Make sure to remove the new changelog post created when running `cargo xtask rel
229We release "nightly" every night automatically and promote the latest nightly to "stable" manually, every week. 231We release "nightly" every night automatically and promote the latest nightly to "stable" manually, every week.
230We don't do "patch" releases, unless something truly egregious comes up. 232We don't do "patch" releases, unless something truly egregious comes up.
231 233
232# Permissions 234## Permissions
233 235
234There are three sets of people with extra permissions: 236There are three sets of people with extra permissions:
235 237
236* rust-analyzer GitHub organization **admins** (which include current t-compiler leads). 238* rust-analyzer GitHub organization [**admins**](https://github.com/orgs/rust-analyzer/people?query=role:owner) (which include current t-compiler leads).
237 Admins have full access to the org. 239 Admins have full access to the org.
238* **review** team in the organization. 240* [**review**](https://github.com/orgs/rust-analyzer/teams/review) team in the organization.
239 Reviewers have `r+` access to all of organization's repositories and publish rights on crates.io. 241 Reviewers have `r+` access to all of organization's repositories and publish rights on crates.io.
240 They also have direct commit access, but all changes should via bors queue. 242 They also have direct commit access, but all changes should via bors queue.
241 It's ok to self-approve if you think you know what you are doing! 243 It's ok to self-approve if you think you know what you are doing!
242 bors should automatically sync the permissions. 244 bors should automatically sync the permissions.
243* **triage** team in the organization. 245* [**triage**](https://github.com/orgs/rust-analyzer/teams/triage) team in the organization.
244 This team can label and close issues. 246 This team can label and close issues.
247
248Note that at the time being you need to be a member of the org yourself to view the links.
diff --git a/docs/dev/architecture.md b/docs/dev/architecture.md
index fb991133a..39edf9e19 100644
--- a/docs/dev/architecture.md
+++ b/docs/dev/architecture.md
@@ -42,7 +42,7 @@ The underlying engine makes sure that model is computed lazily (on-demand) and c
42## Entry Points 42## Entry Points
43 43
44`crates/rust-analyzer/src/bin/main.rs` contains the main function which spawns LSP. 44`crates/rust-analyzer/src/bin/main.rs` contains the main function which spawns LSP.
45This is *the* entry point, but it front-loads a lot of complexity, so its fine to just skim through it. 45This is *the* entry point, but it front-loads a lot of complexity, so it's fine to just skim through it.
46 46
47`crates/rust-analyzer/src/handlers.rs` implements all LSP requests and is a great place to start if you are already familiar with LSP. 47`crates/rust-analyzer/src/handlers.rs` implements all LSP requests and is a great place to start if you are already familiar with LSP.
48 48
@@ -67,7 +67,7 @@ They are handled by Rust code in the xtask directory.
67 67
68VS Code plugin. 68VS Code plugin.
69 69
70### `libs/` 70### `lib/`
71 71
72rust-analyzer independent libraries which we publish to crates.io. 72rust-analyzer independent libraries which we publish to crates.io.
73It's not heavily utilized at the moment. 73It's not heavily utilized at the moment.
@@ -139,7 +139,8 @@ If an AST method returns an `Option`, it *can* be `None` at runtime, even if thi
139### `crates/base_db` 139### `crates/base_db`
140 140
141We use the [salsa](https://github.com/salsa-rs/salsa) crate for incremental and on-demand computation. 141We use the [salsa](https://github.com/salsa-rs/salsa) crate for incremental and on-demand computation.
142Roughly, you can think of salsa as a key-value store, but it can also compute derived values using specified functions. The `base_db` crate provides basic infrastructure for interacting with salsa. 142Roughly, you can think of salsa as a key-value store, but it can also compute derived values using specified functions.
143The `base_db` crate provides basic infrastructure for interacting with salsa.
143Crucially, it defines most of the "input" queries: facts supplied by the client of the analyzer. 144Crucially, it defines most of the "input" queries: facts supplied by the client of the analyzer.
144Reading the docs of the `base_db::input` module should be useful: everything else is strictly derived from those inputs. 145Reading the docs of the `base_db::input` module should be useful: everything else is strictly derived from those inputs.
145 146
@@ -221,7 +222,7 @@ Internally, `ide` is split across several crates. `ide_assists`, `ide_completion
221The `ide` contains a public API/façade, as well as implementation for a plethora of smaller features. 222The `ide` contains a public API/façade, as well as implementation for a plethora of smaller features.
222 223
223**Architecture Invariant:** `ide` crate strives to provide a _perfect_ API. 224**Architecture Invariant:** `ide` crate strives to provide a _perfect_ API.
224Although at the moment it has only one consumer, the LSP server, LSP *does not* influence it's API design. 225Although at the moment it has only one consumer, the LSP server, LSP *does not* influence its API design.
225Instead, we keep in mind a hypothetical _ideal_ client -- an IDE tailored specifically for rust, every nook and cranny of which is packed with Rust-specific goodies. 226Instead, we keep in mind a hypothetical _ideal_ client -- an IDE tailored specifically for rust, every nook and cranny of which is packed with Rust-specific goodies.
226 227
227### `crates/rust-analyzer` 228### `crates/rust-analyzer`
@@ -307,7 +308,7 @@ This sections talks about the things which are everywhere and nowhere in particu
307 308
308### Code generation 309### Code generation
309 310
310Some of the components of this repository are generated through automatic processes. 311Some components in this repository are generated through automatic processes.
311Generated code is updated automatically on `cargo test`. 312Generated code is updated automatically on `cargo test`.
312Generated code is generally committed to the git repository. 313Generated code is generally committed to the git repository.
313 314
@@ -389,7 +390,7 @@ fn spam() {
389``` 390```
390 391
391To specify input data, we use a single string literal in a special format, which can describe a set of rust files. 392To specify input data, we use a single string literal in a special format, which can describe a set of rust files.
392See the `Fixture` type. 393See the `Fixture` its module for fixture examples and documentation.
393 394
394**Architecture Invariant:** all code invariants are tested by `#[test]` tests. 395**Architecture Invariant:** all code invariants are tested by `#[test]` tests.
395There's no additional checks in CI, formatting and tidy tests are run with `cargo test`. 396There's no additional checks in CI, formatting and tidy tests are run with `cargo test`.
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index 8a6f9f06e..a4d92242b 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -1,8 +1,8 @@
1<!--- 1<!---
2lsp_ext.rs hash: e8a7502bd2b2c2f5 2lsp_ext.rs hash: b19ddc3ab8767af9
3 3
4If you need to change the above hash to make the test pass, please check if you 4If you need to change the above hash to make the test pass, please check if you
5need to adjust this doc as well and ping this issue: 5need to adjust this doc as well and ping this issue:
6 6
7 https://github.com/rust-analyzer/rust-analyzer/issues/4604 7 https://github.com/rust-analyzer/rust-analyzer/issues/4604
8 8
@@ -51,8 +51,8 @@ interface SnippetTextEdit extends TextEdit {
51 51
52```typescript 52```typescript
53export interface TextDocumentEdit { 53export interface TextDocumentEdit {
54 textDocument: OptionalVersionedTextDocumentIdentifier; 54 textDocument: OptionalVersionedTextDocumentIdentifier;
55 edits: (TextEdit | SnippetTextEdit)[]; 55 edits: (TextEdit | SnippetTextEdit)[];
56} 56}
57``` 57```
58 58
@@ -145,9 +145,9 @@ mod foo;
145### Unresolved Question 145### Unresolved Question
146 146
147* An alternative would be to use a more general "gotoSuper" request, which would work for super methods, super classes and super modules. 147* An alternative would be to use a more general "gotoSuper" request, which would work for super methods, super classes and super modules.
148 This is the approach IntelliJ Rust is takeing. 148 This is the approach IntelliJ Rust is taking.
149 However, experience shows that super module (which generally has a feeling of navigation between files) should be separate. 149 However, experience shows that super module (which generally has a feeling of navigation between files) should be separate.
150 If you want super module, but the cursor happens to be inside an overriden function, the behavior with single "gotoSuper" request is surprising. 150 If you want super module, but the cursor happens to be inside an overridden function, the behavior with single "gotoSuper" request is surprising.
151 151
152## Join Lines 152## Join Lines
153 153
@@ -193,7 +193,7 @@ fn main() {
193### Unresolved Question 193### Unresolved Question
194 194
195* What is the position of the cursor after `joinLines`? 195* What is the position of the cursor after `joinLines`?
196 Currently this is left to editor's discretion, but it might be useful to specify on the server via snippets. 196 Currently, this is left to editor's discretion, but it might be useful to specify on the server via snippets.
197 However, it then becomes unclear how it works with multi cursor. 197 However, it then becomes unclear how it works with multi cursor.
198 198
199## On Enter 199## On Enter
@@ -330,7 +330,7 @@ Moreover, it would be cool if editors didn't need to implement even basic langua
330 330
331### Unresolved Question 331### Unresolved Question
332 332
333* Should we return a a nested brace structure, to allow paredit-like actions of jump *out* of the current brace pair? 333* Should we return a nested brace structure, to allow paredit-like actions of jump *out* of the current brace pair?
334 This is how `SelectionRange` request works. 334 This is how `SelectionRange` request works.
335* Alternatively, should we perhaps flag certain `SelectionRange`s as being brace pairs? 335* Alternatively, should we perhaps flag certain `SelectionRange`s as being brace pairs?
336 336
@@ -419,23 +419,37 @@ Returns internal status message, mostly for debugging purposes.
419 419
420Reloads project information (that is, re-executes `cargo metadata`). 420Reloads project information (that is, re-executes `cargo metadata`).
421 421
422## Status Notification 422## Server Status
423 423
424**Experimental Client Capability:** `{ "statusNotification": boolean }` 424**Experimental Client Capability:** `{ "serverStatus": boolean }`
425 425
426**Method:** `rust-analyzer/status` 426**Method:** `experimental/serverStatus`
427 427
428**Notification:** 428**Notification:**
429 429
430```typescript 430```typescript
431interface StatusParams { 431interface ServerStatusParams {
432 status: "loading" | "readyPartial" | "ready" | "invalid" | "needsReload", 432 /// `ok` means that the server is completely functional.
433 ///
434 /// `warning` means that the server is partially functional.
435 /// It can server requests, but some results might be wrong due to,
436 /// for example, some missing dependencies.
437 ///
438 /// `error` means that the server is not functional. For example,
439 /// there's a fatal build configuration problem.
440 health: "ok" | "warning" | "error",
441 /// Is there any pending background work which might change the status?
442 /// For example, are dependencies being downloaded?
443 quiescent: bool,
444 /// Explanatory message to show on hover.
445 message?: string,
433} 446}
434``` 447```
435 448
436This notification is sent from server to client. 449This notification is sent from server to client.
437The client can use it to display persistent status to the user (in modline). 450The client can use it to display *persistent* status to the user (in modline).
438For `needsReload` state, the client can provide a context-menu action to run `rust-analyzer/reloadWorkspace` request. 451It is similar to the `showMessage`, but is intended for stares rather than point-in-time events.
452
439 453
440## Syntax Tree 454## Syntax Tree
441 455
@@ -497,7 +511,7 @@ Expands macro call at a given position.
497This request is sent from client to server to render "inlay hints" -- virtual text inserted into editor to show things like inferred types. 511This request is sent from client to server to render "inlay hints" -- virtual text inserted into editor to show things like inferred types.
498Generally, the client should re-query inlay hints after every modification. 512Generally, the client should re-query inlay hints after every modification.
499Note that we plan to move this request to `experimental/inlayHints`, as it is not really Rust-specific, but the current API is not necessary the right one. 513Note that we plan to move this request to `experimental/inlayHints`, as it is not really Rust-specific, but the current API is not necessary the right one.
500Upstream issue: https://github.com/microsoft/language-server-protocol/issues/956 514Upstream issues: https://github.com/microsoft/language-server-protocol/issues/956 , https://github.com/rust-analyzer/rust-analyzer/issues/2797
501 515
502**Request:** 516**Request:**
503 517
@@ -602,12 +616,11 @@ interface TestInfo {
602 616
603This request is sent from client to server to move item under cursor or selection in some direction. 617This request is sent from client to server to move item under cursor or selection in some direction.
604 618
605**Method:** `experimental/moveItemUp` 619**Method:** `experimental/moveItem`
606**Method:** `experimental/moveItemDown`
607 620
608**Request:** `MoveItemParams` 621**Request:** `MoveItemParams`
609 622
610**Response:** `TextDocumentEdit | null` 623**Response:** `SnippetTextEdit[]`
611 624
612```typescript 625```typescript
613export interface MoveItemParams { 626export interface MoveItemParams {
diff --git a/docs/dev/style.md b/docs/dev/style.md
index e4a1672ca..078c478d4 100644
--- a/docs/dev/style.md
+++ b/docs/dev/style.md
@@ -53,11 +53,11 @@ https://www.tedinski.com/2018/02/06/system-boundaries.html
53## Crates.io Dependencies 53## Crates.io Dependencies
54 54
55We try to be very conservative with usage of crates.io dependencies. 55We try to be very conservative with usage of crates.io dependencies.
56Don't use small "helper" crates (exception: `itertools` is allowed). 56Don't use small "helper" crates (exception: `itertools` and `either` are allowed).
57If there's some general reusable bit of code you need, consider adding it to the `stdx` crate. 57If there's some general reusable bit of code you need, consider adding it to the `stdx` crate.
58A useful exercise is to read Cargo.lock and see if some *transitive* dependencies do not make sense for rust-analyzer.
58 59
59**Rationale:** keep compile times low, create ecosystem pressure for faster 60**Rationale:** keep compile times low, create ecosystem pressure for faster compiles, reduce the number of things which might break.
60compiles, reduce the number of things which might break.
61 61
62## Commit Style 62## Commit Style
63 63
@@ -152,6 +152,16 @@ Do not reuse marks between several tests.
152 152
153**Rationale:** marks provide an easy way to find the canonical test for each bit of code. 153**Rationale:** marks provide an easy way to find the canonical test for each bit of code.
154This makes it much easier to understand. 154This makes it much easier to understand.
155More than one mark per test / code branch doesn't add significantly to understanding.
156
157## `#[should_panic]`
158
159Do not use `#[should_panic]` tests.
160Instead, explicitly check for `None`, `Err`, etc.
161
162**Rationale:** `#[should_panic]` is a tool for library authors, to makes sure that API does not fail silently, when misused.
163`rust-analyzer` is not a library, we don't need to test for API misuse, and we have to handle any user input without panics.
164Panic messages in the logs from the `#[should_panic]` tests are confusing.
155 165
156## Function Preconditions 166## Function Preconditions
157 167
@@ -330,7 +340,7 @@ When implementing `do_thing`, it might be very useful to create a context object
330 340
331```rust 341```rust
332pub fn do_thing(arg1: Arg1, arg2: Arg2) -> Res { 342pub fn do_thing(arg1: Arg1, arg2: Arg2) -> Res {
333 let mut ctx = Ctx { arg1, arg2 } 343 let mut ctx = Ctx { arg1, arg2 };
334 ctx.run() 344 ctx.run()
335} 345}
336 346
@@ -586,7 +596,7 @@ use super::{}
586 596
587**Rationale:** consistency. 597**Rationale:** consistency.
588Reading order is important for new contributors. 598Reading order is important for new contributors.
589Grouping by crate allows to spot unwanted dependencies easier. 599Grouping by crate allows spotting unwanted dependencies easier.
590 600
591## Import Style 601## Import Style
592 602
@@ -779,7 +789,7 @@ assert!(x < y);
779assert!(x > 0); 789assert!(x > 0);
780 790
781// BAD 791// BAD
782assert!(x >= lo && x <= hi>); 792assert!(x >= lo && x <= hi);
783assert!(r1 < l2 || l1 > r2); 793assert!(r1 < l2 || l1 > r2);
784assert!(y > x); 794assert!(y > x);
785assert!(0 > x); 795assert!(0 > x);
@@ -806,9 +816,67 @@ if let Some(expected_type) = ctx.expected_type.as_ref() {
806} 816}
807``` 817```
808 818
809**Rational:** `match` is almost always more compact. 819**Rationale:** `match` is almost always more compact.
810The `else` branch can get a more precise pattern: `None` or `Err(_)` instead of `_`. 820The `else` branch can get a more precise pattern: `None` or `Err(_)` instead of `_`.
811 821
822## Helper Functions
823
824Avoid creating singe-use helper functions:
825
826```rust
827// GOOD
828let buf = {
829 let mut buf = get_empty_buf(&mut arena);
830 buf.add_item(item);
831 buf
832};
833
834// BAD
835
836let buf = prepare_buf(&mut arena, item);
837
838...
839
840fn prepare_buf(arena: &mut Arena, item: Item) -> ItemBuf {
841 let mut res = get_empty_buf(&mut arena);
842 res.add_item(item);
843 res
844}
845```
846
847Exception: if you want to make use of `return` or `?`.
848
849**Rationale:** single-use functions change frequently, adding or removing parameters adds churn.
850A block serves just as well to delineate a bit of logic, but has access to all the context.
851Re-using originally single-purpose function often leads to bad coupling.
852
853## Helper Variables
854
855Introduce helper variables freely, especially for multiline conditions:
856
857```rust
858// GOOD
859let rustfmt_not_installed =
860 captured_stderr.contains("not installed") || captured_stderr.contains("not available");
861
862match output.status.code() {
863 Some(1) if !rustfmt_not_installed => Ok(None),
864 _ => Err(format_err!("rustfmt failed:\n{}", captured_stderr)),
865};
866
867// BAD
868match output.status.code() {
869 Some(1)
870 if !captured_stderr.contains("not installed")
871 && !captured_stderr.contains("not available") => Ok(None),
872 _ => Err(format_err!("rustfmt failed:\n{}", captured_stderr)),
873};
874```
875
876**Rationale:** like blocks, single-use variables are a cognitively cheap abstraction, as they have access to all the context.
877Extra variables help during debugging, they make it easy to print/view important intermediate results.
878Giving a name to a condition in `if` expression often improves clarity and leads to a nicer formatted code.
879
812## Token names 880## Token names
813 881
814Use `T![foo]` instead of `SyntaxKind::FOO_KW`. 882Use `T![foo]` instead of `SyntaxKind::FOO_KW`.
diff --git a/docs/dev/syntax.md b/docs/dev/syntax.md
index 737cc7a72..f7a0c09fc 100644
--- a/docs/dev/syntax.md
+++ b/docs/dev/syntax.md
@@ -145,7 +145,7 @@ Another alternative (used by swift and roslyn) is to explicitly divide the set o
145 145
146```rust 146```rust
147struct Token { 147struct Token {
148 kind: NonTriviaTokenKind 148 kind: NonTriviaTokenKind,
149 text: String, 149 text: String,
150 leading_trivia: Vec<TriviaToken>, 150 leading_trivia: Vec<TriviaToken>,
151 trailing_trivia: Vec<TriviaToken>, 151 trailing_trivia: Vec<TriviaToken>,
@@ -240,7 +240,7 @@ impl SyntaxNode {
240 let child_offset = offset; 240 let child_offset = offset;
241 offset += green_child.text_len; 241 offset += green_child.text_len;
242 Arc::new(SyntaxData { 242 Arc::new(SyntaxData {
243 offset: child_offset; 243 offset: child_offset,
244 parent: Some(Arc::clone(self)), 244 parent: Some(Arc::clone(self)),
245 green: Arc::clone(green_child), 245 green: Arc::clone(green_child),
246 }) 246 })
@@ -249,7 +249,7 @@ impl SyntaxNode {
249} 249}
250 250
251impl PartialEq for SyntaxNode { 251impl PartialEq for SyntaxNode {
252 fn eq(&self, other: &SyntaxNode) { 252 fn eq(&self, other: &SyntaxNode) -> bool {
253 self.offset == other.offset 253 self.offset == other.offset
254 && Arc::ptr_eq(&self.green, &other.green) 254 && Arc::ptr_eq(&self.green, &other.green)
255 } 255 }
@@ -273,7 +273,7 @@ This is OK because trees traversals mostly (always, in case of rust-analyzer) ru
273The other thread can restore the `SyntaxNode` by traversing from the root green node and looking for a node with specified range. 273The other thread can restore the `SyntaxNode` by traversing from the root green node and looking for a node with specified range.
274You can also use the similar trick to store a `SyntaxNode`. 274You can also use the similar trick to store a `SyntaxNode`.
275That is, a data structure that holds a `(GreenNode, Range<usize>)` will be `Sync`. 275That is, a data structure that holds a `(GreenNode, Range<usize>)` will be `Sync`.
276However rust-analyzer goes even further. 276However, rust-analyzer goes even further.
277It treats trees as semi-transient and instead of storing a `GreenNode`, it generally stores just the id of the file from which the tree originated: `(FileId, Range<usize>)`. 277It treats trees as semi-transient and instead of storing a `GreenNode`, it generally stores just the id of the file from which the tree originated: `(FileId, Range<usize>)`.
278The `SyntaxNode` is the restored by reparsing the file and traversing it from root. 278The `SyntaxNode` is the restored by reparsing the file and traversing it from root.
279With this trick, rust-analyzer holds only a small amount of trees in memory at the same time, which reduces memory usage. 279With this trick, rust-analyzer holds only a small amount of trees in memory at the same time, which reduces memory usage.