mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-16 09:48:10 +00:00
Merge #8382
8382: Make Fixture docs more accessible and fix small doc issues r=SomeoneToIgnore a=SomeoneToIgnore Follow up of https://github.com/rust-analyzer/rust-analyzer/pull/8302#discussion_r607054896 Co-authored-by: Kirill Bulatov <mail4score@gmail.com>
This commit is contained in:
commit
a35f7cb635
8 changed files with 88 additions and 85 deletions
|
@ -14,4 +14,4 @@ Any other editor plugins that integrate with `rust-analyzer` are not under the c
|
||||||
|
|
||||||
## Others
|
## Others
|
||||||
|
|
||||||
If `cargo check` is enabled (the default), any build scripts or procedural macros used by the project or its dependencies will be executed. This is also the case when `cargo check` is disabled, but build script or procedural macro support is enabled in `rust-analyzer` (off by default).
|
If `cargo check` is enabled (the default), any build scripts or procedural macros used by the project or its dependencies will be executed. This is also the case when `cargo check` is disabled, but build script or procedural macro support is enabled in `rust-analyzer` (on by default).
|
||||||
|
|
|
@ -33,9 +33,9 @@ For usage and troubleshooting requests, please use "IDEs and Editors" category o
|
||||||
|
|
||||||
https://users.rust-lang.org/c/ide/14
|
https://users.rust-lang.org/c/ide/14
|
||||||
|
|
||||||
For questions about development and implementation, join rls-2.0 working group on Zulip:
|
For questions about development and implementation, join rust-analyzer working group on Zulip:
|
||||||
|
|
||||||
https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frls-2.2E0
|
https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frust-analyzer
|
||||||
|
|
||||||
## Quick Links
|
## Quick Links
|
||||||
|
|
||||||
|
|
|
@ -1,62 +1,4 @@
|
||||||
//! Fixtures are strings containing rust source code with optional metadata.
|
//! A set of high-level utility fixture methods to use in tests.
|
||||||
//! A fixture without metadata is parsed into a single source file.
|
|
||||||
//! Use this to test functionality local to one file.
|
|
||||||
//!
|
|
||||||
//! Simple Example:
|
|
||||||
//! ```
|
|
||||||
//! r#"
|
|
||||||
//! fn main() {
|
|
||||||
//! println!("Hello World")
|
|
||||||
//! }
|
|
||||||
//! "#
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! Metadata can be added to a fixture after a `//-` comment.
|
|
||||||
//! The basic form is specifying filenames,
|
|
||||||
//! which is also how to define multiple files in a single test fixture
|
|
||||||
//!
|
|
||||||
//! Example using two files in the same crate:
|
|
||||||
//! ```
|
|
||||||
//! "
|
|
||||||
//! //- /main.rs
|
|
||||||
//! mod foo;
|
|
||||||
//! fn main() {
|
|
||||||
//! foo::bar();
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! //- /foo.rs
|
|
||||||
//! pub fn bar() {}
|
|
||||||
//! "
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! Example using two crates with one file each, with one crate depending on the other:
|
|
||||||
//! ```
|
|
||||||
//! r#"
|
|
||||||
//! //- /main.rs crate:a deps:b
|
|
||||||
//! fn main() {
|
|
||||||
//! b::foo();
|
|
||||||
//! }
|
|
||||||
//! //- /lib.rs crate:b
|
|
||||||
//! pub fn b() {
|
|
||||||
//! println!("Hello World")
|
|
||||||
//! }
|
|
||||||
//! "#
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! Metadata allows specifying all settings and variables
|
|
||||||
//! that are available in a real rust project:
|
|
||||||
//! - crate names via `crate:cratename`
|
|
||||||
//! - dependencies via `deps:dep1,dep2`
|
|
||||||
//! - configuration settings via `cfg:dbg=false,opt_level=2`
|
|
||||||
//! - environment variables via `env:PATH=/bin,RUST_LOG=debug`
|
|
||||||
//!
|
|
||||||
//! Example using all available metadata:
|
|
||||||
//! ```
|
|
||||||
//! "
|
|
||||||
//! //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo
|
|
||||||
//! fn insert_source_code_here() {}
|
|
||||||
//! "
|
|
||||||
//! ```
|
|
||||||
use std::{mem, str::FromStr, sync::Arc};
|
use std::{mem, str::FromStr, sync::Arc};
|
||||||
|
|
||||||
use cfg::CfgOptions;
|
use cfg::CfgOptions;
|
||||||
|
|
|
@ -1,5 +1,65 @@
|
||||||
//! Defines `Fixture` -- a convenient way to describe the initial state of
|
//! Defines `Fixture` -- a convenient way to describe the initial state of
|
||||||
//! rust-analyzer database from a single string.
|
//! rust-analyzer database from a single string.
|
||||||
|
//!
|
||||||
|
//! Fixtures are strings containing rust source code with optional metadata.
|
||||||
|
//! A fixture without metadata is parsed into a single source file.
|
||||||
|
//! Use this to test functionality local to one file.
|
||||||
|
//!
|
||||||
|
//! Simple Example:
|
||||||
|
//! ```
|
||||||
|
//! r#"
|
||||||
|
//! fn main() {
|
||||||
|
//! println!("Hello World")
|
||||||
|
//! }
|
||||||
|
//! "#
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Metadata can be added to a fixture after a `//-` comment.
|
||||||
|
//! The basic form is specifying filenames,
|
||||||
|
//! which is also how to define multiple files in a single test fixture
|
||||||
|
//!
|
||||||
|
//! Example using two files in the same crate:
|
||||||
|
//! ```
|
||||||
|
//! "
|
||||||
|
//! //- /main.rs
|
||||||
|
//! mod foo;
|
||||||
|
//! fn main() {
|
||||||
|
//! foo::bar();
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! //- /foo.rs
|
||||||
|
//! pub fn bar() {}
|
||||||
|
//! "
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Example using two crates with one file each, with one crate depending on the other:
|
||||||
|
//! ```
|
||||||
|
//! r#"
|
||||||
|
//! //- /main.rs crate:a deps:b
|
||||||
|
//! fn main() {
|
||||||
|
//! b::foo();
|
||||||
|
//! }
|
||||||
|
//! //- /lib.rs crate:b
|
||||||
|
//! pub fn b() {
|
||||||
|
//! println!("Hello World")
|
||||||
|
//! }
|
||||||
|
//! "#
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Metadata allows specifying all settings and variables
|
||||||
|
//! that are available in a real rust project:
|
||||||
|
//! - crate names via `crate:cratename`
|
||||||
|
//! - dependencies via `deps:dep1,dep2`
|
||||||
|
//! - configuration settings via `cfg:dbg=false,opt_level=2`
|
||||||
|
//! - environment variables via `env:PATH=/bin,RUST_LOG=debug`
|
||||||
|
//!
|
||||||
|
//! Example using all available metadata:
|
||||||
|
//! ```
|
||||||
|
//! "
|
||||||
|
//! //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo
|
||||||
|
//! fn insert_source_code_here() {}
|
||||||
|
//! "
|
||||||
|
//! ```
|
||||||
|
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use stdx::{lines_with_ends, split_once, trim_indent};
|
use stdx::{lines_with_ends, split_once, trim_indent};
|
||||||
|
@ -24,7 +84,7 @@ impl Fixture {
|
||||||
/// //- some meta
|
/// //- some meta
|
||||||
/// line 1
|
/// line 1
|
||||||
/// line 2
|
/// line 2
|
||||||
/// // - other meta
|
/// //- other meta
|
||||||
/// ```
|
/// ```
|
||||||
pub fn parse(ra_fixture: &str) -> Vec<Fixture> {
|
pub fn parse(ra_fixture: &str) -> Vec<Fixture> {
|
||||||
let fixture = trim_indent(ra_fixture);
|
let fixture = trim_indent(ra_fixture);
|
||||||
|
|
|
@ -42,7 +42,7 @@ The underlying engine makes sure that model is computed lazily (on-demand) and c
|
||||||
## Entry Points
|
## Entry Points
|
||||||
|
|
||||||
`crates/rust-analyzer/src/bin/main.rs` contains the main function which spawns LSP.
|
`crates/rust-analyzer/src/bin/main.rs` contains the main function which spawns LSP.
|
||||||
This is *the* entry point, but it front-loads a lot of complexity, so its fine to just skim through it.
|
This is *the* entry point, but it front-loads a lot of complexity, so it's fine to just skim through it.
|
||||||
|
|
||||||
`crates/rust-analyzer/src/handlers.rs` implements all LSP requests and is a great place to start if you are already familiar with LSP.
|
`crates/rust-analyzer/src/handlers.rs` implements all LSP requests and is a great place to start if you are already familiar with LSP.
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ They are handled by Rust code in the xtask directory.
|
||||||
|
|
||||||
VS Code plugin.
|
VS Code plugin.
|
||||||
|
|
||||||
### `libs/`
|
### `lib/`
|
||||||
|
|
||||||
rust-analyzer independent libraries which we publish to crates.io.
|
rust-analyzer independent libraries which we publish to crates.io.
|
||||||
It's not heavily utilized at the moment.
|
It'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
|
||||||
### `crates/base_db`
|
### `crates/base_db`
|
||||||
|
|
||||||
We use the [salsa](https://github.com/salsa-rs/salsa) crate for incremental and on-demand computation.
|
We use the [salsa](https://github.com/salsa-rs/salsa) crate for incremental and on-demand computation.
|
||||||
Roughly, 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.
|
Roughly, 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.
|
||||||
Crucially, it defines most of the "input" queries: facts supplied by the client of the analyzer.
|
Crucially, it defines most of the "input" queries: facts supplied by the client of the analyzer.
|
||||||
Reading the docs of the `base_db::input` module should be useful: everything else is strictly derived from those inputs.
|
Reading the docs of the `base_db::input` module should be useful: everything else is strictly derived from those inputs.
|
||||||
|
|
||||||
|
@ -221,7 +222,7 @@ Internally, `ide` is split across several crates. `ide_assists`, `ide_completion
|
||||||
The `ide` contains a public API/façade, as well as implementation for a plethora of smaller features.
|
The `ide` contains a public API/façade, as well as implementation for a plethora of smaller features.
|
||||||
|
|
||||||
**Architecture Invariant:** `ide` crate strives to provide a _perfect_ API.
|
**Architecture Invariant:** `ide` crate strives to provide a _perfect_ API.
|
||||||
Although at the moment it has only one consumer, the LSP server, LSP *does not* influence it's API design.
|
Although at the moment it has only one consumer, the LSP server, LSP *does not* influence its API design.
|
||||||
Instead, 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.
|
Instead, 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.
|
||||||
|
|
||||||
### `crates/rust-analyzer`
|
### `crates/rust-analyzer`
|
||||||
|
@ -307,7 +308,7 @@ This sections talks about the things which are everywhere and nowhere in particu
|
||||||
|
|
||||||
### Code generation
|
### Code generation
|
||||||
|
|
||||||
Some of the components of this repository are generated through automatic processes.
|
Some ]components in this repository are generated through automatic processes.
|
||||||
Generated code is updated automatically on `cargo test`.
|
Generated code is updated automatically on `cargo test`.
|
||||||
Generated code is generally committed to the git repository.
|
Generated code is generally committed to the git repository.
|
||||||
|
|
||||||
|
@ -389,7 +390,7 @@ fn spam() {
|
||||||
```
|
```
|
||||||
|
|
||||||
To specify input data, we use a single string literal in a special format, which can describe a set of rust files.
|
To specify input data, we use a single string literal in a special format, which can describe a set of rust files.
|
||||||
See the `Fixture` type.
|
See the `Fixture` its module for fixture examples and documentation.
|
||||||
|
|
||||||
**Architecture Invariant:** all code invariants are tested by `#[test]` tests.
|
**Architecture Invariant:** all code invariants are tested by `#[test]` tests.
|
||||||
There's no additional checks in CI, formatting and tidy tests are run with `cargo test`.
|
There's no additional checks in CI, formatting and tidy tests are run with `cargo test`.
|
||||||
|
|
|
@ -51,8 +51,8 @@ interface SnippetTextEdit extends TextEdit {
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
export interface TextDocumentEdit {
|
export interface TextDocumentEdit {
|
||||||
textDocument: OptionalVersionedTextDocumentIdentifier;
|
textDocument: OptionalVersionedTextDocumentIdentifier;
|
||||||
edits: (TextEdit | SnippetTextEdit)[];
|
edits: (TextEdit | SnippetTextEdit)[];
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -145,9 +145,9 @@ mod foo;
|
||||||
### Unresolved Question
|
### Unresolved Question
|
||||||
|
|
||||||
* An alternative would be to use a more general "gotoSuper" request, which would work for super methods, super classes and super modules.
|
* An alternative would be to use a more general "gotoSuper" request, which would work for super methods, super classes and super modules.
|
||||||
This is the approach IntelliJ Rust is takeing.
|
This is the approach IntelliJ Rust is taking.
|
||||||
However, experience shows that super module (which generally has a feeling of navigation between files) should be separate.
|
However, experience shows that super module (which generally has a feeling of navigation between files) should be separate.
|
||||||
If you want super module, but the cursor happens to be inside an overriden function, the behavior with single "gotoSuper" request is surprising.
|
If you want super module, but the cursor happens to be inside an overridden function, the behavior with single "gotoSuper" request is surprising.
|
||||||
|
|
||||||
## Join Lines
|
## Join Lines
|
||||||
|
|
||||||
|
@ -193,7 +193,7 @@ fn main() {
|
||||||
### Unresolved Question
|
### Unresolved Question
|
||||||
|
|
||||||
* What is the position of the cursor after `joinLines`?
|
* What is the position of the cursor after `joinLines`?
|
||||||
Currently this is left to editor's discretion, but it might be useful to specify on the server via snippets.
|
Currently, this is left to editor's discretion, but it might be useful to specify on the server via snippets.
|
||||||
However, it then becomes unclear how it works with multi cursor.
|
However, it then becomes unclear how it works with multi cursor.
|
||||||
|
|
||||||
## On Enter
|
## On Enter
|
||||||
|
@ -330,7 +330,7 @@ Moreover, it would be cool if editors didn't need to implement even basic langua
|
||||||
|
|
||||||
### Unresolved Question
|
### Unresolved Question
|
||||||
|
|
||||||
* Should we return a a nested brace structure, to allow paredit-like actions of jump *out* of the current brace pair?
|
* Should we return a nested brace structure, to allow paredit-like actions of jump *out* of the current brace pair?
|
||||||
This is how `SelectionRange` request works.
|
This is how `SelectionRange` request works.
|
||||||
* Alternatively, should we perhaps flag certain `SelectionRange`s as being brace pairs?
|
* Alternatively, should we perhaps flag certain `SelectionRange`s as being brace pairs?
|
||||||
|
|
||||||
|
@ -511,7 +511,7 @@ Expands macro call at a given position.
|
||||||
This request is sent from client to server to render "inlay hints" -- virtual text inserted into editor to show things like inferred types.
|
This request is sent from client to server to render "inlay hints" -- virtual text inserted into editor to show things like inferred types.
|
||||||
Generally, the client should re-query inlay hints after every modification.
|
Generally, the client should re-query inlay hints after every modification.
|
||||||
Note 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.
|
Note 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.
|
||||||
Upstream issue: https://github.com/microsoft/language-server-protocol/issues/956
|
Upstream issues: https://github.com/microsoft/language-server-protocol/issues/956 , https://github.com/rust-analyzer/rust-analyzer/issues/2797
|
||||||
|
|
||||||
**Request:**
|
**Request:**
|
||||||
|
|
||||||
|
|
|
@ -53,9 +53,9 @@ https://www.tedinski.com/2018/02/06/system-boundaries.html
|
||||||
## Crates.io Dependencies
|
## Crates.io Dependencies
|
||||||
|
|
||||||
We try to be very conservative with usage of crates.io dependencies.
|
We try to be very conservative with usage of crates.io dependencies.
|
||||||
Don't use small "helper" crates (exception: `itertools` is allowed).
|
Don't use small "helper" crates (exception: `itertools` and `either` are allowed).
|
||||||
If there's some general reusable bit of code you need, consider adding it to the `stdx` crate.
|
If there's some general reusable bit of code you need, consider adding it to the `stdx` crate.
|
||||||
A useful exercise is to read Cargo.lock and see if some of the *transitive* dependencies do not make sense for rust-analyzer.
|
A useful exercise is to read Cargo.lock and see if some *transitive* dependencies do not make sense for rust-analyzer.
|
||||||
|
|
||||||
**Rationale:** keep compile times low, create ecosystem pressure for faster compiles, reduce the number of things which might break.
|
**Rationale:** keep compile times low, create ecosystem pressure for faster compiles, reduce the number of things which might break.
|
||||||
|
|
||||||
|
@ -330,7 +330,7 @@ When implementing `do_thing`, it might be very useful to create a context object
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
pub fn do_thing(arg1: Arg1, arg2: Arg2) -> Res {
|
pub fn do_thing(arg1: Arg1, arg2: Arg2) -> Res {
|
||||||
let mut ctx = Ctx { arg1, arg2 }
|
let mut ctx = Ctx { arg1, arg2 };
|
||||||
ctx.run()
|
ctx.run()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -586,7 +586,7 @@ use super::{}
|
||||||
|
|
||||||
**Rationale:** consistency.
|
**Rationale:** consistency.
|
||||||
Reading order is important for new contributors.
|
Reading order is important for new contributors.
|
||||||
Grouping by crate allows to spot unwanted dependencies easier.
|
Grouping by crate allows spotting unwanted dependencies easier.
|
||||||
|
|
||||||
## Import Style
|
## Import Style
|
||||||
|
|
||||||
|
@ -779,7 +779,7 @@ assert!(x < y);
|
||||||
assert!(x > 0);
|
assert!(x > 0);
|
||||||
|
|
||||||
// BAD
|
// BAD
|
||||||
assert!(x >= lo && x <= hi>);
|
assert!(x >= lo && x <= hi);
|
||||||
assert!(r1 < l2 || l1 > r2);
|
assert!(r1 < l2 || l1 > r2);
|
||||||
assert!(y > x);
|
assert!(y > x);
|
||||||
assert!(0 > x);
|
assert!(0 > x);
|
||||||
|
|
|
@ -145,7 +145,7 @@ Another alternative (used by swift and roslyn) is to explicitly divide the set o
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
struct Token {
|
struct Token {
|
||||||
kind: NonTriviaTokenKind
|
kind: NonTriviaTokenKind,
|
||||||
text: String,
|
text: String,
|
||||||
leading_trivia: Vec<TriviaToken>,
|
leading_trivia: Vec<TriviaToken>,
|
||||||
trailing_trivia: Vec<TriviaToken>,
|
trailing_trivia: Vec<TriviaToken>,
|
||||||
|
@ -240,7 +240,7 @@ impl SyntaxNode {
|
||||||
let child_offset = offset;
|
let child_offset = offset;
|
||||||
offset += green_child.text_len;
|
offset += green_child.text_len;
|
||||||
Arc::new(SyntaxData {
|
Arc::new(SyntaxData {
|
||||||
offset: child_offset;
|
offset: child_offset,
|
||||||
parent: Some(Arc::clone(self)),
|
parent: Some(Arc::clone(self)),
|
||||||
green: Arc::clone(green_child),
|
green: Arc::clone(green_child),
|
||||||
})
|
})
|
||||||
|
@ -249,7 +249,7 @@ impl SyntaxNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for SyntaxNode {
|
impl PartialEq for SyntaxNode {
|
||||||
fn eq(&self, other: &SyntaxNode) {
|
fn eq(&self, other: &SyntaxNode) -> bool {
|
||||||
self.offset == other.offset
|
self.offset == other.offset
|
||||||
&& Arc::ptr_eq(&self.green, &other.green)
|
&& Arc::ptr_eq(&self.green, &other.green)
|
||||||
}
|
}
|
||||||
|
@ -273,7 +273,7 @@ This is OK because trees traversals mostly (always, in case of rust-analyzer) ru
|
||||||
The other thread can restore the `SyntaxNode` by traversing from the root green node and looking for a node with specified range.
|
The other thread can restore the `SyntaxNode` by traversing from the root green node and looking for a node with specified range.
|
||||||
You can also use the similar trick to store a `SyntaxNode`.
|
You can also use the similar trick to store a `SyntaxNode`.
|
||||||
That is, a data structure that holds a `(GreenNode, Range<usize>)` will be `Sync`.
|
That is, a data structure that holds a `(GreenNode, Range<usize>)` will be `Sync`.
|
||||||
However rust-analyzer goes even further.
|
However, rust-analyzer goes even further.
|
||||||
It 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>)`.
|
It 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>)`.
|
||||||
The `SyntaxNode` is the restored by reparsing the file and traversing it from root.
|
The `SyntaxNode` is the restored by reparsing the file and traversing it from root.
|
||||||
With this trick, rust-analyzer holds only a small amount of trees in memory at the same time, which reduces memory usage.
|
With this trick, rust-analyzer holds only a small amount of trees in memory at the same time, which reduces memory usage.
|
||||||
|
|
Loading…
Reference in a new issue