This documents is an index of features that rust-analyzer language server provides. Shortcuts are for the default VS Code layout. If there's no shortcut, you can use Ctrl+Shift+P to search for the corresponding action. ### Workspace Symbol ctrl+t Uses fuzzy-search to find types, modules and function by name across your project and dependencies. This **the** most useful feature, which improves code navigation tremendously. It mostly works on top of the built-in LSP functionality, however `#` and `*` symbols can be used to narrow down the search. Specifically, - `Foo` searches for `Foo` type in the current workspace - `foo#` searches for `foo` function in the current workspace - `Foo*` searches for `Foo` type among dependencies, including `stdlib` - `foo#*` searches for `foo` function among dependencies. That is, `#` switches from "types" to all symbols, `*` switches from the current workspace to dependencies. ### Document Symbol ctrl+shift+o Provides a tree of the symbols defined in the file. Can be used to * fuzzy search symbol in a file (super useful) * draw breadcrumbs to describe the context around the cursor * draw outline of the file ### On Typing Assists Some features trigger on typing certain characters: - typing `let =` tries to smartly add `;` if `=` is followed by an existing expression. - Enter inside comments automatically inserts `///` - typing `.` in a chain method call auto-indents ### Extend Selection Extends the current selection to the encompassing syntactic construct (expression, statement, item, module, etc). It works with multiple cursors. This is a relatively new feature of LSP: https://github.com/Microsoft/language-server-protocol/issues/613, check your editor's LSP library to see if this feature is supported. ### Go to Definition Navigates to the definition of an identifier. ### Go to Implementation Navigates to the impl block of structs, enums or traits. Also implemented as a code lens. ### Go to Type Defintion Navigates to the type of an identifier. ### Commands ctrl+shift+p #### Run Shows popup suggesting to run a test/benchmark/binary **at the current cursor location**. Super useful for repeatedly running just a single test. Do bind this to a shortcut! #### Parent Module Navigates to the parent module of the current module. #### Matching Brace If the cursor is on any brace (`<>(){}[]`) which is a part of a brace-pair, moves cursor to the matching brace. It uses the actual parser to determine braces, so it won't confuse generics with comparisons. #### Join Lines Join selected lines into one, smartly fixing up whitespace and trailing commas. #### Show Syntax Tree Shows the parse tree of the current file. It exists mostly for debugging rust-analyzer itself. #### Status Shows internal statistic about memory usage of rust-analyzer #### Run garbage collection Manually triggers GC #### Start Cargo Watch Start `cargo watch` for live error highlighting. Will prompt to install if it's not already installed. #### Stop Cargo Watch Stop `cargo watch` ### Code Actions (Assists) These are triggered in a particular context via light bulb. We use custom code on the VS Code side to be able to position cursor. `<|>` signifies cursor - Add `#[derive]` ```rust // before: struct Foo { <|>x: i32 } // after: #[derive(<|>)] struct Foo { x: i32 } ``` - Add `impl` ```rust // before: struct Foo<'a, T: Debug> { <|>t: T } // after: struct Foo<'a, T: Debug> { t: T } impl<'a, T: Debug> Foo<'a, T> { <|> } ``` - Add missing `impl` members ```rust // before: trait Foo { fn foo(&self); fn bar(&self); fn baz(&self); } struct S; impl Foo for S { fn bar(&self) {} <|> } // after: trait Foo { fn foo(&self); fn bar(&self); fn baz(&self); } struct S; impl Foo for S { fn bar(&self) {} fn foo(&self) { unimplemented!() } fn baz(&self) { unimplemented!() }<|> } ``` - Import path ```rust // before: impl std::fmt::Debug<|> for Foo { } // after: use std::fmt::Debug; impl Debug<|> for Foo { } ``` - Change Visibility ```rust // before: <|>fn foo() {} // after: <|>pub(crate) fn foo() {} // after: <|>pub fn foo() {} ``` - Fill match arms ```rust // before: enum A { As, Bs, Cs(String), Ds(String, String), Es{x: usize, y: usize} } fn main() { let a = A::As; match a<|> {} } // after: enum A { As, Bs, Cs(String), Ds(String, String), Es{x: usize, y: usize} } fn main() { let a = A::As; match <|>a { A::As => (), A::Bs => (), A::Cs(_) => (), A::Ds(_, _) => (), A::Es{x, y} => (), } } ``` - Fill struct fields ```rust // before: struct S<'a, D> { a: u32, b: String, c: (i32, i32), d: D, r: &'a str, } fn main() { let s = S<|> {} } // after: struct S<'a, D> { a: u32, b: String, c: (i32, i32), d: D, r: &'a str, } fn main() { let s = <|>S { a: (), b: (), c: (), d: (), r: (), } } ``` - Flip `,` ```rust // before: fn foo(x: usize,<|> dim: (usize, usize)) {} // after: fn foo(dim: (usize, usize), x: usize) {} ``` - Introduce variable: ```rust // before: fn foo() { foo(<|>1 + 1<|>); } // after: fn foo() { let var_name = 1 + 1; foo(var_name); } ``` - Inline local variable: ```rust // before: fn foo() { let a<|> = 1 + 1; let b = a * 10; } // after: fn foo() { let b = (1 + 1) * 10; } ``` - Remove `dbg!` ```rust // before: fn foo(n: usize) { if let Some(_) = dbg!(n.<|>checked_sub(4)) { // ... } } // after: fn foo(n: usize) { if let Some(_) = n.<|>checked_sub(4) { // ... } } ``` - Replace if-let with match: ```rust // before: impl VariantData { pub fn is_struct(&self) -> bool { if <|>let VariantData::Struct(..) = *self { true } else { false } } } // after: impl VariantData { pub fn is_struct(&self) -> bool { <|>match *self { VariantData::Struct(..) => true, _ => false, } } } ``` - Split import ```rust // before: use algo:<|>:visitor::{Visitor, visit}; // after: use algo::{<|>visitor::{Visitor, visit}}; ``` - Flip binary expression ```rust // before: fn foo() { if 1 <<|> 2 { println!("Who would have thought?"); } } // after: fn foo() { if 2 ><|> 1 { println!("Who would have thought?"); } } ``` - Add explicit type ```rust // before: fn foo() { let t<|> = (&2, Some(1)); } // after: fn foo() { let t<|>: (&i32, Option) = (&2, Some(1)); } ``` - Move guard expression to match arm body ```rust // before: fn f() { match x { <|>y @ 4 | y @ 5 if y > 5 => true, _ => false } } // after: fn f() { match x { y @ 4 | y @ 5 => if y > 5 { <|>true }, _ => false } } ``` - Move if condition to match arm guard ```rust // before: fn f() { let mut t = 'a'; let chars = "abcd"; match t { '\r' => if chars.clone().next().is_some() { t = 'e';<|> false }, _ => true } } // after: fn f() { let mut t = 'a'; let chars = "abcd"; match t { '\r' <|>if chars.clone().next().is_some() => { t = 'e'; false }, _ => true } } ``` ### Magic Completions In addition to usual reference completion, rust-analyzer provides some ✨magic✨ completions as well: Keywords like `if`, `else` `while`, `loop` are completed with braces, and cursor is placed at the appropriate position. Even though `if` is easy to type, you still want to complete it, to get ` { }` for free! `return` is inserted with a space or `;` depending on the return type of the function. When completing a function call, `()` are automatically inserted. If function takes arguments, cursor is positioned inside the parenthesis. There are postifx completions, which can be triggerd by typing something like `foo().if`. The word after `.` determines postifx completion, possible variants are: - `expr.if` -> `if expr {}` - `expr.match` -> `match expr {}` - `expr.while` -> `while expr {}` - `expr.ref` -> `&expr` - `expr.refm` -> `&mut expr` - `expr.not` -> `!expr` - `expr.dbg` -> `dbg!(expr)` There also snippet completions: #### Inside Expressions - `pd` -> `println!("{:?}")` - `ppd` -> `println!("{:#?}")` #### Inside Modules - `tfn` -> `#[test] fn f(){}` ### Code highlighting Experimental feature to let rust-analyzer highlight Rust code instead of using the default highlighter. #### Rainbow highlighting Experimental feature that, given code highlighting using rust-analyzer is active, will pick unique colors for identifiers.