diff options
-rw-r--r-- | guide.md | 72 |
1 files changed, 36 insertions, 36 deletions
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | ## About the guide | 3 | ## About the guide |
4 | 4 | ||
5 | This guide describes the current start of the rust-analyzer as of 2019-01-20 | 5 | This guide describes the current state of `rust-analyzer` as of 2019-01-20 |
6 | (git tag [guide-2019-01]). Its purpose is to document various problems and | 6 | (git tag [guide-2019-01]). Its purpose is to document various problems and |
7 | architectural solutions related to the problem of building IDE-first compiler | 7 | architectural solutions related to the problem of building IDE-first compiler |
8 | for Rust. | 8 | for Rust. |
@@ -11,24 +11,24 @@ for Rust. | |||
11 | 11 | ||
12 | ## The big picture | 12 | ## The big picture |
13 | 13 | ||
14 | On the highest possible level, rust analyzer is a stateful component. Client may | 14 | On the highest possible level, rust analyzer is a stateful component. A client may |
15 | apply changes to the analyzer (new contents of `foo.rs` file is "fn main() {}") | 15 | apply changes to the analyzer (new contents of `foo.rs` file is "fn main() {}") |
16 | and it may ask semantic questions about the current state (what is the | 16 | and it may ask semantic questions about the current state (what is the |
17 | definition of the identifier with offset 92 in file `bar.rs`?). Two important | 17 | definition of the identifier with offset 92 in file `bar.rs`?). Two important |
18 | properties hold: | 18 | properties hold: |
19 | 19 | ||
20 | * Analyzer does not do any IO. It starts in an empty state and all input data is | 20 | * Analyzer does not do any I/O. It starts in an empty state and all input data is |
21 | provided via `apply_change` API. | 21 | provided via `apply_change` API. |
22 | 22 | ||
23 | * Only queries about the current state are supported. One can, of course, | 23 | * Only queries about the current state are supported. One can, of course, |
24 | simulate undo and redo by keeping log of changes and inverse-changes. | 24 | simulate undo and redo by keeping a log of changes and inverse changes respectively. |
25 | 25 | ||
26 | ## IDE API | 26 | ## IDE API |
27 | 27 | ||
28 | To see this big picture, let's take a look at the [`AnalysisHost`] and | 28 | To see the bigger picture of how the IDE features works, let's take a look at the [`AnalysisHost`] and |
29 | [`Analysis`] pair of types. `AnalysisHost` has three methods: | 29 | [`Analysis`] pair of types. `AnalysisHost` has three methods: |
30 | 30 | ||
31 | * `default` for creating an empty analysis | 31 | * `default()` for creating an empty analysis instance |
32 | * `apply_change(&mut self)` to make changes (this is how you get from an empty | 32 | * `apply_change(&mut self)` to make changes (this is how you get from an empty |
33 | state to something interesting) | 33 | state to something interesting) |
34 | * `analysis(&self)` to get an instance of `Analysis` | 34 | * `analysis(&self)` to get an instance of `Analysis` |
@@ -42,28 +42,28 @@ stack, we'll talk about it later. | |||
42 | [`AnalysisHost`]: https://github.com/rust-analyzer/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/lib.rs#L265-L284 | 42 | [`AnalysisHost`]: https://github.com/rust-analyzer/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/lib.rs#L265-L284 |
43 | [`Analysis`]: https://github.com/rust-analyzer/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/lib.rs#L291-L478 | 43 | [`Analysis`]: https://github.com/rust-analyzer/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/lib.rs#L291-L478 |
44 | 44 | ||
45 | The reason for `Analysis` and `AnalysisHost` separation is that we want apply | 45 | The reason for this separation of `Analysis` and `AnalysisHost` is that we want to apply |
46 | changes "uniquely", but we might want to fork an `Analysis` and send it to | 46 | changes "uniquely", but we might also want to fork an `Analysis` and send it to |
47 | another thread for background processing. That is, there is only a single | 47 | another thread for background processing. That is, there is only a single |
48 | `AnalysisHost`, but there may be several (equivalent) `Analysis`. | 48 | `AnalysisHost`, but there may be several (equivalent) `Analysis`. |
49 | 49 | ||
50 | Note that all of the `Analysis` API return `Cancelable<T>`. This is required to | 50 | Note that all of the `Analysis` API return `Cancelable<T>`. This is required to |
51 | be responsive in IDE setting. Sometimes a long-running query is being computed | 51 | be responsive in an IDE setting. Sometimes a long-running query is being computed |
52 | and the user types something in the editor and asks for completion. In this | 52 | and the user types something in the editor and asks for completion. In this |
53 | case, we cancel the long-running computation (so it returns `Err(Canceled)`), | 53 | case, we cancel the long-running computation (so it returns `Err(Canceled)`), |
54 | apply the change and execute request for completion. We never use stale data to | 54 | apply the change and execute request for completion. We never use stale data to |
55 | answer requests. Under the cover, `AnalysisHost` "remembers" all outstanding | 55 | answer requests. Under the cover, `AnalysisHost` "remembers" all outstanding |
56 | `Analysis` instances. `AnalysisHost::apply_change` method cancels all | 56 | `Analysis` instances. The `AnalysisHost::apply_change` method cancels all |
57 | `Analysis`es, blocks until of them are `Dropped` and then applies change | 57 | `Analysis`es, blocks until all of them are `Dropped` and then applies changes |
58 | in-place. This is the familiar to rustaceans read-write lock interior | 58 | in-place. This may be familiar to Rustaceans who use read-write locks for interior |
59 | mutability. | 59 | mutability. |
60 | 60 | ||
61 | Next, lets talk about what are inputs to the Analysis, precisely. | 61 | Next, let's talk about what the inputs to the `Analysis` are, precisely. |
62 | 62 | ||
63 | ## Inputs | 63 | ## Inputs |
64 | 64 | ||
65 | Rust Analyzer never does any IO itself, all inputs get passed explicitly via | 65 | Rust Analyzer never does any I/O itself, all inputs get passed explicitly via |
66 | `AnalysisHost::apply_change` method, which accepts a single argument: | 66 | the `AnalysisHost::apply_change` method, which accepts a single argument, a |
67 | `AnalysisChange`. [`AnalysisChange`] is a builder for a single change | 67 | `AnalysisChange`. [`AnalysisChange`] is a builder for a single change |
68 | "transaction", so it suffices to study its methods to understand all of the | 68 | "transaction", so it suffices to study its methods to understand all of the |
69 | input data. | 69 | input data. |
@@ -72,12 +72,12 @@ input data. | |||
72 | 72 | ||
73 | The `(add|change|remove)_file` methods control the set of the input files, where | 73 | The `(add|change|remove)_file` methods control the set of the input files, where |
74 | each file has an integer id (`FileId`, picked by the client), text (`String`) | 74 | each file has an integer id (`FileId`, picked by the client), text (`String`) |
75 | and a filesystem path. Paths are tricky, they'll be explained in source roots | 75 | and a filesystem path. Paths are tricky; they'll be explained below, in source roots |
76 | section, together with `add_root` method. `add_library` method allows to add a | 76 | section, together with the `add_root` method. The `add_library` method allows us to add a |
77 | group of files which are assumed to rarely change. It's mostly an optimization | 77 | group of files which are assumed to rarely change. It's mostly an optimization |
78 | and does not change fundamental picture. | 78 | and does not change the fundamental picture. |
79 | 79 | ||
80 | `set_crate_graph` method allows to control how the input files are partitioned | 80 | The `set_crate_graph` method allows us to control how the input files are partitioned |
81 | into compilation unites -- crates. It also controls (in theory, not implemented | 81 | into compilation unites -- crates. It also controls (in theory, not implemented |
82 | yet) `cfg` flags. `CrateGraph` is a directed acyclic graph of crates. Each crate | 82 | yet) `cfg` flags. `CrateGraph` is a directed acyclic graph of crates. Each crate |
83 | has a root `FileId`, a set of active `cfg` flags and a set of dependencies. Each | 83 | has a root `FileId`, a set of active `cfg` flags and a set of dependencies. Each |
@@ -220,21 +220,21 @@ of type V. Queries come in two basic varieties: | |||
220 | 220 | ||
221 | * **Functions**: pure functions (no side effects) that transform your inputs | 221 | * **Functions**: pure functions (no side effects) that transform your inputs |
222 | into other values. The results of queries is memoized to avoid recomputing | 222 | into other values. The results of queries is memoized to avoid recomputing |
223 | them a lot. When you make changes to the inputs, we'll figure out (fairlywe | 223 | them a lot. When you make changes to the inputs, we'll figure out (fairly |
224 | intelligently) when we can re-use these memoized values and when we have we | 224 | intelligently) when we can re-use these memoized values and when we have to |
225 | recompute them. | 225 | recompute them. |
226 | 226 | ||
227 | 227 | ||
228 | For further discussion, its important to understand one bit of "fairly | 228 | For further discussion, its important to understand one bit of "fairly |
229 | intelligently". Suppose we have to functions, `f1` and `f2`, and one input,we | 229 | intelligently". Suppose we have two functions, `f1` and `f2`, and one input, `z`. |
230 | We call `f1(X)` which in turn calls `f2(Y)` which inspects `i(Z)`. `i(Z)` | 230 | We call `f1(X)` which in turn calls `f2(Y)` which inspects `i(Z)`. `i(Z)` |
231 | returns some value `V1`, `f2` uses that and returns `R1`, `f1` uses that anwe | 231 | returns some value `V1`, `f2` uses that and returns `R1`, `f1` uses that and |
232 | returns `O`. Now, let's change `i` at `Z` to `V2` from `V1` and try to compwe | 232 | returns `O`. Now, let's change `i` at `Z` to `V2` from `V1` and try to compute |
233 | `f1(X)` again. Because `f1(X)` (transitively) depends on `i(Z)`, we can't jwe | 233 | `f1(X)` again. Because `f1(X)` (transitively) depends on `i(Z)`, we can't just |
234 | reuse its value as is. However, if `f2(Y)` is *still* equal to `R1` (despitwe | 234 | reuse its value as is. However, if `f2(Y)` is *still* equal to `R1` (despite |
235 | `i`'s change), we, in fact, *can* reuse `O` as result of `f1(X)`. And that'we | 235 | `i`'s change), we, in fact, *can* reuse `O` as result of `f1(X)`. And that's how |
236 | salsa works: it recomputes results in *reverse* order, starting from inputswe | 236 | salsa works: it recomputes results in *reverse* order, starting from inputs and |
237 | progressing towards outputs, stopping as soon as it sees an intermediate vawe | 237 | progressing towards outputs, stopping as soon as it sees an intermediate value |
238 | that hasn't changed. | 238 | that hasn't changed. |
239 | 239 | ||
240 | ## Salsa Input Queries | 240 | ## Salsa Input Queries |
@@ -312,7 +312,7 @@ of the syntax trees: | |||
312 | `Option`s. The tree for `fn foo` will contain a function declaration with | 312 | `Option`s. The tree for `fn foo` will contain a function declaration with |
313 | `None` for parameter list and body. | 313 | `None` for parameter list and body. |
314 | 314 | ||
315 | * Syntax trees do not know the file they are build from, they only know about | 315 | * Syntax trees do not know the file they are built from, they only know about |
316 | the text. | 316 | the text. |
317 | 317 | ||
318 | The implementation is based on the generic [rowan] crate on top of which a | 318 | The implementation is based on the generic [rowan] crate on top of which a |
@@ -335,10 +335,10 @@ declarations and recursively process child modules. This is handled by the | |||
335 | 335 | ||
336 | First, rust analyzer builds a module tree for all crates in a source root | 336 | First, rust analyzer builds a module tree for all crates in a source root |
337 | simultaneously. The main reason for this is historical (`module_tree` predates | 337 | simultaneously. The main reason for this is historical (`module_tree` predates |
338 | `CrateGraph`), but this approach also allows to account for files which are not | 338 | `CrateGraph`), but this approach also enables accounting for files which are not |
339 | part of any crate. That is, if you create a file but do not include it as a | 339 | part of any crate. That is, if you create a file but do not include it as a |
340 | submodule anywhere, you still get semantic completion, and you get a warning | 340 | submodule anywhere, you still get semantic completion, and you get a warning |
341 | about free-floating module (the actual warning is not implemented yet). | 341 | about a free-floating module (the actual warning is not implemented yet). |
342 | 342 | ||
343 | The second difference is that `module_tree_query` does not *directly* depend on | 343 | The second difference is that `module_tree_query` does not *directly* depend on |
344 | the "parse" query (which is confusingly called `source_file`). Why would calling | 344 | the "parse" query (which is confusingly called `source_file`). Why would calling |
@@ -347,14 +347,14 @@ an insignificant whitespace. Adding whitespace changes the parse tree (because | |||
347 | it includes whitespace), and that means recomputing the whole module tree. | 347 | it includes whitespace), and that means recomputing the whole module tree. |
348 | 348 | ||
349 | We deal with this problem by introducing an intermediate [`submodules_query`]. | 349 | We deal with this problem by introducing an intermediate [`submodules_query`]. |
350 | This query processes the syntax tree an extract a set of declared submodule | 350 | This query processes the syntax tree and extracts a set of declared submodule |
351 | names. Now, changing the whitespace results in `submodules_query` being | 351 | names. Now, changing the whitespace results in `submodules_query` being |
352 | re-executed for a *single* module, but because the result of this query stays | 352 | re-executed for a *single* module, but because the result of this query stays |
353 | the same, we don't have to re-execute [`module_tree_query`]. In fact, we only | 353 | the same, we don't have to re-execute [`module_tree_query`]. In fact, we only |
354 | need to re-execute it when we add/remove new files or when we change mod | 354 | need to re-execute it when we add/remove new files or when we change mod |
355 | declarations. | 355 | declarations. |
356 | 356 | ||
357 | [`submodules_query`]: https://github.com/rust-analyzer/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/module_tree.rs#L41) | 357 | [`submodules_query`]: https://github.com/rust-analyzer/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/module_tree.rs#L41 |
358 | 358 | ||
359 | We store the resulting modules in a `Vec`-based indexed arena. The indices in | 359 | We store the resulting modules in a `Vec`-based indexed arena. The indices in |
360 | the arena becomes module ids. And this brings us to the next topic: | 360 | the arena becomes module ids. And this brings us to the next topic: |
@@ -445,7 +445,7 @@ we modify bodies of the items. After that we [loop] resolving all imports until | |||
445 | we've reached a fixed point. | 445 | we've reached a fixed point. |
446 | 446 | ||
447 | [lower]: https://github.com/rust-analyzer/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/nameres/lower.rs#L113-L117 | 447 | [lower]: https://github.com/rust-analyzer/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/nameres/lower.rs#L113-L117 |
448 | [loop]: https://github.com/rust-analyzer/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/nameres/lower.rs#L113-L117 | 448 | [loop]: https://github.com/rust-analyzer/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/nameres.rs#L186-L196 |
449 | 449 | ||
450 | And, given all our preparation with ids and position-independent representation, | 450 | And, given all our preparation with ids and position-independent representation, |
451 | it is satisfying to [test] that typing inside function body does not invalidate | 451 | it is satisfying to [test] that typing inside function body does not invalidate |
@@ -514,7 +514,7 @@ construct a mapping from `ExprId`s to types. | |||
514 | [lower ast]: https://github.com/rust-analyzer/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/expr.rs | 514 | [lower ast]: https://github.com/rust-analyzer/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/expr.rs |
515 | [positional id]: https://github.com/rust-analyzer/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/expr.rs#L13-L15 | 515 | [positional id]: https://github.com/rust-analyzer/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/expr.rs#L13-L15 |
516 | [a source map]: https://github.com/rust-analyzer/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/expr.rs#L41-L44 | 516 | [a source map]: https://github.com/rust-analyzer/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/expr.rs#L41-L44 |
517 | [type-inference]: https://github.com/rust-analyzer/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/ty.rs#L1208-L1223 | 517 | [type inference]: https://github.com/rust-analyzer/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/ty.rs#L1208-L1223 |
518 | 518 | ||
519 | ## Tying it all together: completion | 519 | ## Tying it all together: completion |
520 | 520 | ||