mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-10 15:14:32 +00:00
Move the rest of the features to generated docs
This commit is contained in:
parent
b795a07320
commit
1c6a2eb14a
10 changed files with 358 additions and 240 deletions
|
@ -1,5 +1,3 @@
|
||||||
//! FIXME: write short doc here
|
|
||||||
|
|
||||||
mod completion_config;
|
mod completion_config;
|
||||||
mod completion_item;
|
mod completion_item;
|
||||||
mod completion_context;
|
mod completion_context;
|
||||||
|
@ -35,6 +33,51 @@ pub use crate::completion::{
|
||||||
completion_item::{CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat},
|
completion_item::{CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//FIXME: split the following feature into fine-grained features.
|
||||||
|
|
||||||
|
// Feature: 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 a function
|
||||||
|
// takes arguments, the cursor is positioned inside the parenthesis.
|
||||||
|
//
|
||||||
|
// There are postfix completions, which can be triggered by typing something like
|
||||||
|
// `foo().if`. The word after `.` determines postfix completion. Possible variants are:
|
||||||
|
//
|
||||||
|
// - `expr.if` -> `if expr {}` or `if let ... {}` for `Option` or `Result`
|
||||||
|
// - `expr.match` -> `match expr {}`
|
||||||
|
// - `expr.while` -> `while expr {}` or `while let ... {}` for `Option` or `Result`
|
||||||
|
// - `expr.ref` -> `&expr`
|
||||||
|
// - `expr.refm` -> `&mut expr`
|
||||||
|
// - `expr.not` -> `!expr`
|
||||||
|
// - `expr.dbg` -> `dbg!(expr)`
|
||||||
|
//
|
||||||
|
// There also snippet completions:
|
||||||
|
//
|
||||||
|
// .Expressions
|
||||||
|
// - `pd` -> `println!("{:?}")`
|
||||||
|
// - `ppd` -> `println!("{:#?}")`
|
||||||
|
//
|
||||||
|
// .Items
|
||||||
|
// - `tfn` -> `#[test] fn f(){}`
|
||||||
|
// - `tmod` ->
|
||||||
|
// ```rust
|
||||||
|
// #[cfg(test)]
|
||||||
|
// mod tests {
|
||||||
|
// use super::*;
|
||||||
|
//
|
||||||
|
// #[test]
|
||||||
|
// fn test_fn() {}
|
||||||
|
// }
|
||||||
|
// ```
|
||||||
|
|
||||||
/// Main entry point for completion. We run completion as a two-phase process.
|
/// Main entry point for completion. We run completion as a two-phase process.
|
||||||
///
|
///
|
||||||
/// First, we look at the position and collect a so-called `CompletionContext.
|
/// First, we look at the position and collect a so-called `CompletionContext.
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
//! FIXME: write short doc here
|
//! FIXME: write short doc here
|
||||||
|
use ra_assists::utils::TryEnum;
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
ast::{self, AstNode},
|
ast::{self, AstNode},
|
||||||
TextRange, TextSize,
|
TextRange, TextSize,
|
||||||
};
|
};
|
||||||
use ra_text_edit::TextEdit;
|
use ra_text_edit::TextEdit;
|
||||||
|
|
||||||
use super::completion_config::SnippetCap;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
completion::{
|
completion::{
|
||||||
completion_context::CompletionContext,
|
completion_context::CompletionContext,
|
||||||
|
@ -14,7 +13,8 @@ use crate::{
|
||||||
},
|
},
|
||||||
CompletionItem,
|
CompletionItem,
|
||||||
};
|
};
|
||||||
use ra_assists::utils::TryEnum;
|
|
||||||
|
use super::completion_config::SnippetCap;
|
||||||
|
|
||||||
pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
if !ctx.config.enable_postfix_completions {
|
if !ctx.config.enable_postfix_completions {
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
//! Logic for computing info that is displayed when the user hovers over any
|
use std::iter::once;
|
||||||
//! source code items (e.g. function call, struct field, variable symbol...)
|
|
||||||
|
|
||||||
use hir::{
|
use hir::{
|
||||||
Adt, AsAssocItem, AssocItemContainer, FieldSource, HasSource, HirDisplay, ModuleDef,
|
Adt, AsAssocItem, AssocItemContainer, FieldSource, HasSource, HirDisplay, ModuleDef,
|
||||||
ModuleSource, Semantics,
|
ModuleSource, Semantics,
|
||||||
};
|
};
|
||||||
|
use itertools::Itertools;
|
||||||
use ra_db::SourceDatabase;
|
use ra_db::SourceDatabase;
|
||||||
use ra_ide_db::{
|
use ra_ide_db::{
|
||||||
defs::{classify_name, classify_name_ref, Definition},
|
defs::{classify_name, classify_name_ref, Definition},
|
||||||
|
@ -21,8 +21,6 @@ use crate::{
|
||||||
display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel},
|
display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel},
|
||||||
FilePosition, RangeInfo,
|
FilePosition, RangeInfo,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
|
||||||
use std::iter::once;
|
|
||||||
|
|
||||||
/// Contains the results when hovering over an item
|
/// Contains the results when hovering over an item
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
|
@ -62,6 +60,63 @@ impl HoverResult {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Feature: Hover
|
||||||
|
//
|
||||||
|
// Shows additional information, like type of an expression or documentation for definition when "focusing" code.
|
||||||
|
// Focusing is usually hovering with a mouse, but can also be triggered with a shortcut.
|
||||||
|
pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<HoverResult>> {
|
||||||
|
let sema = Semantics::new(db);
|
||||||
|
let file = sema.parse(position.file_id).syntax().clone();
|
||||||
|
let token = pick_best(file.token_at_offset(position.offset))?;
|
||||||
|
let token = sema.descend_into_macros(token);
|
||||||
|
|
||||||
|
let mut res = HoverResult::new();
|
||||||
|
|
||||||
|
if let Some((node, name_kind)) = match_ast! {
|
||||||
|
match (token.parent()) {
|
||||||
|
ast::NameRef(name_ref) => {
|
||||||
|
classify_name_ref(&sema, &name_ref).map(|d| (name_ref.syntax().clone(), d.definition()))
|
||||||
|
},
|
||||||
|
ast::Name(name) => {
|
||||||
|
classify_name(&sema, &name).map(|d| (name.syntax().clone(), d.definition()))
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
} {
|
||||||
|
let range = sema.original_range(&node).range;
|
||||||
|
res.extend(hover_text_from_name_kind(db, name_kind));
|
||||||
|
|
||||||
|
if !res.is_empty() {
|
||||||
|
return Some(RangeInfo::new(range, res));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let node = token
|
||||||
|
.ancestors()
|
||||||
|
.find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?;
|
||||||
|
|
||||||
|
let ty = match_ast! {
|
||||||
|
match node {
|
||||||
|
ast::MacroCall(_it) => {
|
||||||
|
// If this node is a MACRO_CALL, it means that `descend_into_macros` failed to resolve.
|
||||||
|
// (e.g expanding a builtin macro). So we give up here.
|
||||||
|
return None;
|
||||||
|
},
|
||||||
|
ast::Expr(it) => {
|
||||||
|
sema.type_of_expr(&it)
|
||||||
|
},
|
||||||
|
ast::Pat(it) => {
|
||||||
|
sema.type_of_pat(&it)
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}?;
|
||||||
|
|
||||||
|
res.extend(Some(rust_code_markup(&ty.display(db))));
|
||||||
|
let range = sema.original_range(&node).range;
|
||||||
|
Some(RangeInfo::new(range, res))
|
||||||
|
}
|
||||||
|
|
||||||
fn hover_text(
|
fn hover_text(
|
||||||
docs: Option<String>,
|
docs: Option<String>,
|
||||||
desc: Option<String>,
|
desc: Option<String>,
|
||||||
|
@ -160,59 +215,6 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<Strin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<HoverResult>> {
|
|
||||||
let sema = Semantics::new(db);
|
|
||||||
let file = sema.parse(position.file_id).syntax().clone();
|
|
||||||
let token = pick_best(file.token_at_offset(position.offset))?;
|
|
||||||
let token = sema.descend_into_macros(token);
|
|
||||||
|
|
||||||
let mut res = HoverResult::new();
|
|
||||||
|
|
||||||
if let Some((node, name_kind)) = match_ast! {
|
|
||||||
match (token.parent()) {
|
|
||||||
ast::NameRef(name_ref) => {
|
|
||||||
classify_name_ref(&sema, &name_ref).map(|d| (name_ref.syntax().clone(), d.definition()))
|
|
||||||
},
|
|
||||||
ast::Name(name) => {
|
|
||||||
classify_name(&sema, &name).map(|d| (name.syntax().clone(), d.definition()))
|
|
||||||
},
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
} {
|
|
||||||
let range = sema.original_range(&node).range;
|
|
||||||
res.extend(hover_text_from_name_kind(db, name_kind));
|
|
||||||
|
|
||||||
if !res.is_empty() {
|
|
||||||
return Some(RangeInfo::new(range, res));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let node = token
|
|
||||||
.ancestors()
|
|
||||||
.find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?;
|
|
||||||
|
|
||||||
let ty = match_ast! {
|
|
||||||
match node {
|
|
||||||
ast::MacroCall(_it) => {
|
|
||||||
// If this node is a MACRO_CALL, it means that `descend_into_macros` failed to resolve.
|
|
||||||
// (e.g expanding a builtin macro). So we give up here.
|
|
||||||
return None;
|
|
||||||
},
|
|
||||||
ast::Expr(it) => {
|
|
||||||
sema.type_of_expr(&it)
|
|
||||||
},
|
|
||||||
ast::Pat(it) => {
|
|
||||||
sema.type_of_pat(&it)
|
|
||||||
},
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}?;
|
|
||||||
|
|
||||||
res.extend(Some(rust_code_markup(&ty.display(db))));
|
|
||||||
let range = sema.original_range(&node).range;
|
|
||||||
Some(RangeInfo::new(range, res))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
|
fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
|
||||||
return tokens.max_by_key(priority);
|
return tokens.max_by_key(priority);
|
||||||
fn priority(n: &SyntaxToken) -> usize {
|
fn priority(n: &SyntaxToken) -> usize {
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
//! This module defines multiple types of inlay hints and their visibility
|
|
||||||
|
|
||||||
use hir::{Adt, HirDisplay, Semantics, Type};
|
use hir::{Adt, HirDisplay, Semantics, Type};
|
||||||
use ra_ide_db::RootDatabase;
|
use ra_ide_db::RootDatabase;
|
||||||
use ra_prof::profile;
|
use ra_prof::profile;
|
||||||
|
@ -39,6 +37,26 @@ pub struct InlayHint {
|
||||||
pub label: SmolStr,
|
pub label: SmolStr,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Feature: Inlay Hints
|
||||||
|
//
|
||||||
|
// rust-analyzer shows additional information inline with the source code.
|
||||||
|
// Editors usually render this using read-only virtual text snippets interspersed with code.
|
||||||
|
//
|
||||||
|
// rust-analyzer shows hits for
|
||||||
|
//
|
||||||
|
// * types of local variables
|
||||||
|
// * names of function arguments
|
||||||
|
// * types of chained expressions
|
||||||
|
//
|
||||||
|
// **Note:** VS Code does not have native support for inlay hints https://github.com/microsoft/vscode/issues/16221[yet] and the hints are implemented using decorations.
|
||||||
|
// This approach has limitations, the caret movement and bracket highlighting near the edges of the hint may be weird:
|
||||||
|
// https://github.com/rust-analyzer/rust-analyzer/issues/1623[1], https://github.com/rust-analyzer/rust-analyzer/issues/3453[2].
|
||||||
|
//
|
||||||
|
// |===
|
||||||
|
// | Editor | Action Name
|
||||||
|
//
|
||||||
|
// | VS Code | **Rust Analyzer: Toggle inlay hints*
|
||||||
|
// |===
|
||||||
pub(crate) fn inlay_hints(
|
pub(crate) fn inlay_hints(
|
||||||
db: &RootDatabase,
|
db: &RootDatabase,
|
||||||
file_id: FileId,
|
file_id: FileId,
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
//! Implements syntax highlighting.
|
|
||||||
|
|
||||||
mod tags;
|
mod tags;
|
||||||
mod html;
|
mod html;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -32,81 +30,15 @@ pub struct HighlightedRange {
|
||||||
pub binding_hash: Option<u64>,
|
pub binding_hash: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
// Feature: Semantic Syntax Highlighting
|
||||||
struct HighlightedRangeStack {
|
//
|
||||||
stack: Vec<Vec<HighlightedRange>>,
|
// rust-analyzer highlights the code semantically.
|
||||||
}
|
// For example, `bar` in `foo::Bar` might be colored differently depending on whether `Bar` is an enum or a trait.
|
||||||
|
// rust-analyzer does not specify colors directly, instead it assigns tag (like `struct`) and a set of modifiers (like `declaration`) to each token.
|
||||||
/// We use a stack to implement the flattening logic for the highlighted
|
// It's up to the client to map those to specific colors.
|
||||||
/// syntax ranges.
|
//
|
||||||
impl HighlightedRangeStack {
|
// The general rule is that a reference to an entity gets colored the same way as the entity itself.
|
||||||
fn new() -> Self {
|
// We also give special modifier for `mut` and `&mut` local variables.
|
||||||
Self { stack: vec![Vec::new()] }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn push(&mut self) {
|
|
||||||
self.stack.push(Vec::new());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Flattens the highlighted ranges.
|
|
||||||
///
|
|
||||||
/// For example `#[cfg(feature = "foo")]` contains the nested ranges:
|
|
||||||
/// 1) parent-range: Attribute [0, 23)
|
|
||||||
/// 2) child-range: String [16, 21)
|
|
||||||
///
|
|
||||||
/// The following code implements the flattening, for our example this results to:
|
|
||||||
/// `[Attribute [0, 16), String [16, 21), Attribute [21, 23)]`
|
|
||||||
fn pop(&mut self) {
|
|
||||||
let children = self.stack.pop().unwrap();
|
|
||||||
let prev = self.stack.last_mut().unwrap();
|
|
||||||
let needs_flattening = !children.is_empty()
|
|
||||||
&& !prev.is_empty()
|
|
||||||
&& prev.last().unwrap().range.contains_range(children.first().unwrap().range);
|
|
||||||
if !needs_flattening {
|
|
||||||
prev.extend(children);
|
|
||||||
} else {
|
|
||||||
let mut parent = prev.pop().unwrap();
|
|
||||||
for ele in children {
|
|
||||||
assert!(parent.range.contains_range(ele.range));
|
|
||||||
let mut cloned = parent.clone();
|
|
||||||
parent.range = TextRange::new(parent.range.start(), ele.range.start());
|
|
||||||
cloned.range = TextRange::new(ele.range.end(), cloned.range.end());
|
|
||||||
if !parent.range.is_empty() {
|
|
||||||
prev.push(parent);
|
|
||||||
}
|
|
||||||
prev.push(ele);
|
|
||||||
parent = cloned;
|
|
||||||
}
|
|
||||||
if !parent.range.is_empty() {
|
|
||||||
prev.push(parent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add(&mut self, range: HighlightedRange) {
|
|
||||||
self.stack
|
|
||||||
.last_mut()
|
|
||||||
.expect("during DFS traversal, the stack must not be empty")
|
|
||||||
.push(range)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flattened(mut self) -> Vec<HighlightedRange> {
|
|
||||||
assert_eq!(
|
|
||||||
self.stack.len(),
|
|
||||||
1,
|
|
||||||
"after DFS traversal, the stack should only contain a single element"
|
|
||||||
);
|
|
||||||
let mut res = self.stack.pop().unwrap();
|
|
||||||
res.sort_by_key(|range| range.range.start());
|
|
||||||
// Check that ranges are sorted and disjoint
|
|
||||||
assert!(res
|
|
||||||
.iter()
|
|
||||||
.zip(res.iter().skip(1))
|
|
||||||
.all(|(left, right)| left.range.end() <= right.range.start()));
|
|
||||||
res
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn highlight(
|
pub(crate) fn highlight(
|
||||||
db: &RootDatabase,
|
db: &RootDatabase,
|
||||||
file_id: FileId,
|
file_id: FileId,
|
||||||
|
@ -291,6 +223,81 @@ pub(crate) fn highlight(
|
||||||
stack.flattened()
|
stack.flattened()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct HighlightedRangeStack {
|
||||||
|
stack: Vec<Vec<HighlightedRange>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// We use a stack to implement the flattening logic for the highlighted
|
||||||
|
/// syntax ranges.
|
||||||
|
impl HighlightedRangeStack {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self { stack: vec![Vec::new()] }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push(&mut self) {
|
||||||
|
self.stack.push(Vec::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Flattens the highlighted ranges.
|
||||||
|
///
|
||||||
|
/// For example `#[cfg(feature = "foo")]` contains the nested ranges:
|
||||||
|
/// 1) parent-range: Attribute [0, 23)
|
||||||
|
/// 2) child-range: String [16, 21)
|
||||||
|
///
|
||||||
|
/// The following code implements the flattening, for our example this results to:
|
||||||
|
/// `[Attribute [0, 16), String [16, 21), Attribute [21, 23)]`
|
||||||
|
fn pop(&mut self) {
|
||||||
|
let children = self.stack.pop().unwrap();
|
||||||
|
let prev = self.stack.last_mut().unwrap();
|
||||||
|
let needs_flattening = !children.is_empty()
|
||||||
|
&& !prev.is_empty()
|
||||||
|
&& prev.last().unwrap().range.contains_range(children.first().unwrap().range);
|
||||||
|
if !needs_flattening {
|
||||||
|
prev.extend(children);
|
||||||
|
} else {
|
||||||
|
let mut parent = prev.pop().unwrap();
|
||||||
|
for ele in children {
|
||||||
|
assert!(parent.range.contains_range(ele.range));
|
||||||
|
let mut cloned = parent.clone();
|
||||||
|
parent.range = TextRange::new(parent.range.start(), ele.range.start());
|
||||||
|
cloned.range = TextRange::new(ele.range.end(), cloned.range.end());
|
||||||
|
if !parent.range.is_empty() {
|
||||||
|
prev.push(parent);
|
||||||
|
}
|
||||||
|
prev.push(ele);
|
||||||
|
parent = cloned;
|
||||||
|
}
|
||||||
|
if !parent.range.is_empty() {
|
||||||
|
prev.push(parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add(&mut self, range: HighlightedRange) {
|
||||||
|
self.stack
|
||||||
|
.last_mut()
|
||||||
|
.expect("during DFS traversal, the stack must not be empty")
|
||||||
|
.push(range)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flattened(mut self) -> Vec<HighlightedRange> {
|
||||||
|
assert_eq!(
|
||||||
|
self.stack.len(),
|
||||||
|
1,
|
||||||
|
"after DFS traversal, the stack should only contain a single element"
|
||||||
|
);
|
||||||
|
let mut res = self.stack.pop().unwrap();
|
||||||
|
res.sort_by_key(|range| range.range.start());
|
||||||
|
// Check that ranges are sorted and disjoint
|
||||||
|
assert!(res
|
||||||
|
.iter()
|
||||||
|
.zip(res.iter().skip(1))
|
||||||
|
.all(|(left, right)| left.range.end() <= right.range.start()));
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn highlight_format_specifier(kind: FormatSpecifier) -> Option<HighlightTag> {
|
fn highlight_format_specifier(kind: FormatSpecifier) -> Option<HighlightTag> {
|
||||||
Some(match kind {
|
Some(match kind {
|
||||||
FormatSpecifier::Open
|
FormatSpecifier::Open
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use ra_db::SourceDatabase;
|
use ra_db::{FileId, SourceDatabase};
|
||||||
use ra_ide_db::RootDatabase;
|
use ra_ide_db::RootDatabase;
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
algo, AstNode, NodeOrToken, SourceFile,
|
algo, AstNode, NodeOrToken, SourceFile,
|
||||||
|
|
|
@ -1,96 +0,0 @@
|
||||||
This document is an index of features that the rust-analyzer language server
|
|
||||||
provides. Shortcuts are for the default VS Code layout. If there's no shortcut,
|
|
||||||
you can use <kbd>Ctrl+Shift+P</kbd> to search for the corresponding action.
|
|
||||||
|
|
||||||
### Commands <kbd>ctrl+shift+p</kbd>
|
|
||||||
|
|
||||||
|
|
||||||
#### Toggle inlay hints
|
|
||||||
|
|
||||||
Toggle inlay hints view for the current workspace.
|
|
||||||
It is recommended to assign a shortcut for this command to quickly turn off
|
|
||||||
inlay hints when they prevent you from reading/writing the code.
|
|
||||||
|
|
||||||
### 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 a function
|
|
||||||
takes arguments, the cursor is positioned inside the parenthesis.
|
|
||||||
|
|
||||||
There are postfix completions, which can be triggered by typing something like
|
|
||||||
`foo().if`. The word after `.` determines postfix completion. Possible variants are:
|
|
||||||
|
|
||||||
- `expr.if` -> `if expr {}` or `if let ... {}` for `Option` or `Result`
|
|
||||||
- `expr.match` -> `match expr {}`
|
|
||||||
- `expr.while` -> `while expr {}` or `while let ... {}` for `Option` or `Result`
|
|
||||||
- `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(){}`
|
|
||||||
- `tmod` ->
|
|
||||||
```rust
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_fn() {}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 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.
|
|
||||||
|
|
||||||
### Code hints
|
|
||||||
|
|
||||||
Rust-analyzer has two types of hints to show the information about the code:
|
|
||||||
|
|
||||||
* hover hints, appearing on hover on any element.
|
|
||||||
|
|
||||||
These contain extended information on the hovered language item.
|
|
||||||
|
|
||||||
* inlay hints, shown near the element hinted directly in the editor.
|
|
||||||
|
|
||||||
Two types of inlay hints are displayed currently:
|
|
||||||
|
|
||||||
* type hints, displaying the minimal information on the type of the expression (if the information is available)
|
|
||||||
* method chaining hints, type information for multi-line method chains
|
|
||||||
* parameter name hints, displaying the names of the parameters in the corresponding methods
|
|
||||||
|
|
||||||
#### VS Code
|
|
||||||
|
|
||||||
In VS Code, the following settings can be used to configure the inlay hints:
|
|
||||||
|
|
||||||
* `rust-analyzer.inlayHints.typeHints` - enable hints for inferred types.
|
|
||||||
* `rust-analyzer.inlayHints.chainingHints` - enable hints for inferred types on method chains.
|
|
||||||
* `rust-analyzer.inlayHints.parameterHints` - enable hints for function parameters.
|
|
||||||
* `rust-analyzer.inlayHints.maxLength` — shortens the hints if their length exceeds the value specified. If no value is specified (`null`), no shortening is applied.
|
|
||||||
|
|
||||||
**Note:** VS Code does not have native support for inlay hints [yet](https://github.com/microsoft/vscode/issues/16221) and the hints are implemented using decorations.
|
|
||||||
This approach has limitations, the caret movement and bracket highlighting near the edges of the hint may be weird:
|
|
||||||
[1](https://github.com/rust-analyzer/rust-analyzer/issues/1623), [2](https://github.com/rust-analyzer/rust-analyzer/issues/3453).
|
|
|
@ -1,3 +1,16 @@
|
||||||
|
=== Expand Macro Recursively
|
||||||
|
**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/expand_macro.rs[expand_macro.rs]
|
||||||
|
|
||||||
|
|
||||||
|
Shows the full macro expansion of the macro at current cursor.
|
||||||
|
|
||||||
|
|===
|
||||||
|
| Editor | Action Name
|
||||||
|
|
||||||
|
| VS Code | **Rust Analyzer: Expand macro recursively**
|
||||||
|
|===
|
||||||
|
|
||||||
|
|
||||||
=== Extend Selection
|
=== Extend Selection
|
||||||
**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/extend_selection.rs[extend_selection.rs]
|
**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/extend_selection.rs[extend_selection.rs]
|
||||||
|
|
||||||
|
@ -68,6 +81,38 @@ Navigates to the type of an identifier.
|
||||||
|===
|
|===
|
||||||
|
|
||||||
|
|
||||||
|
=== Hover
|
||||||
|
**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/hover.rs[hover.rs]
|
||||||
|
|
||||||
|
|
||||||
|
Shows additional information, like type of an expression or documentation for definition when "focusing" code.
|
||||||
|
Focusing is usually hovering with a mouse, but can also be triggered with a shortcut.
|
||||||
|
|
||||||
|
|
||||||
|
=== Inlay Hints
|
||||||
|
**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/inlay_hints.rs[inlay_hints.rs]
|
||||||
|
|
||||||
|
|
||||||
|
rust-analyzer shows additional information inline with the source code.
|
||||||
|
Editors usually render this using read-only virtual text snippets interspersed with code.
|
||||||
|
|
||||||
|
rust-analyzer shows hits for
|
||||||
|
|
||||||
|
* types of local variables
|
||||||
|
* names of function arguments
|
||||||
|
* types of chained expressions
|
||||||
|
|
||||||
|
**Note:** VS Code does not have native support for inlay hints https://github.com/microsoft/vscode/issues/16221[yet] and the hints are implemented using decorations.
|
||||||
|
This approach has limitations, the caret movement and bracket highlighting near the edges of the hint may be weird:
|
||||||
|
https://github.com/rust-analyzer/rust-analyzer/issues/1623[1], https://github.com/rust-analyzer/rust-analyzer/issues/3453[2].
|
||||||
|
|
||||||
|
|===
|
||||||
|
| Editor | Action Name
|
||||||
|
|
||||||
|
| VS Code | **Rust Analyzer: Toggle inlay hints*
|
||||||
|
|===
|
||||||
|
|
||||||
|
|
||||||
=== Join Lines
|
=== Join Lines
|
||||||
**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/join_lines.rs[join_lines.rs]
|
**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/join_lines.rs[join_lines.rs]
|
||||||
|
|
||||||
|
@ -81,6 +126,52 @@ Join selected lines into one, smartly fixing up whitespace, trailing commas, and
|
||||||
|===
|
|===
|
||||||
|
|
||||||
|
|
||||||
|
=== Magic Completions
|
||||||
|
**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/completion.rs[completion.rs]
|
||||||
|
|
||||||
|
|
||||||
|
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 a function
|
||||||
|
takes arguments, the cursor is positioned inside the parenthesis.
|
||||||
|
|
||||||
|
There are postfix completions, which can be triggered by typing something like
|
||||||
|
`foo().if`. The word after `.` determines postfix completion. Possible variants are:
|
||||||
|
|
||||||
|
- `expr.if` -> `if expr {}` or `if let ... {}` for `Option` or `Result`
|
||||||
|
- `expr.match` -> `match expr {}`
|
||||||
|
- `expr.while` -> `while expr {}` or `while let ... {}` for `Option` or `Result`
|
||||||
|
- `expr.ref` -> `&expr`
|
||||||
|
- `expr.refm` -> `&mut expr`
|
||||||
|
- `expr.not` -> `!expr`
|
||||||
|
- `expr.dbg` -> `dbg!(expr)`
|
||||||
|
|
||||||
|
There also snippet completions:
|
||||||
|
|
||||||
|
.Expressions
|
||||||
|
- `pd` -> `println!("{:?}")`
|
||||||
|
- `ppd` -> `println!("{:#?}")`
|
||||||
|
|
||||||
|
.Items
|
||||||
|
- `tfn` -> `#[test] fn f(){}`
|
||||||
|
- `tmod` ->
|
||||||
|
```rust
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fn() {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
=== Matching Brace
|
=== Matching Brace
|
||||||
**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/matching_brace.rs[matching_brace.rs]
|
**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/matching_brace.rs[matching_brace.rs]
|
||||||
|
|
||||||
|
@ -135,6 +226,19 @@ to a shortcut!
|
||||||
|===
|
|===
|
||||||
|
|
||||||
|
|
||||||
|
=== Semantic Syntax Highlighting
|
||||||
|
**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/syntax_highlighting.rs[syntax_highlighting.rs]
|
||||||
|
|
||||||
|
|
||||||
|
rust-analyzer highlights the code semantically.
|
||||||
|
For example, `bar` in `foo::Bar` might be colored differently depending on whether `Bar` is an enum or a trait.
|
||||||
|
rust-analyzer does not specify colors directly, instead it assigns tag (like `struct`) and a set of modifiers (like `declaration`) to each token.
|
||||||
|
It's up to the client to map those to specific colors.
|
||||||
|
|
||||||
|
The general rule is that a reference to an entity gets colored the same way as the entity itself.
|
||||||
|
We also give special modifier for `mut` and `&mut` local variables.
|
||||||
|
|
||||||
|
|
||||||
=== Show Syntax Tree
|
=== Show Syntax Tree
|
||||||
**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/syntax_tree.rs[syntax_tree.rs]
|
**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/syntax_tree.rs[syntax_tree.rs]
|
||||||
|
|
||||||
|
@ -149,6 +253,45 @@ rust-analyzer itself.
|
||||||
|===
|
|===
|
||||||
|
|
||||||
|
|
||||||
|
=== Status
|
||||||
|
**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/status.rs[status.rs]
|
||||||
|
|
||||||
|
|
||||||
|
Shows internal statistic about memory usage of rust-analyzer.
|
||||||
|
|
||||||
|
|===
|
||||||
|
| Editor | Action Name
|
||||||
|
|
||||||
|
| VS Code | **Rust Analyzer: Status**
|
||||||
|
|===
|
||||||
|
|
||||||
|
|
||||||
|
=== Structural Seach and Replace
|
||||||
|
**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/ssr.rs[ssr.rs]
|
||||||
|
|
||||||
|
|
||||||
|
Search and replace with named wildcards that will match any expression.
|
||||||
|
The syntax for a structural search replace command is `<search_pattern> ==>> <replace_pattern>`.
|
||||||
|
A `$<name>:expr` placeholder in the search pattern will match any expression and `$<name>` will reference it in the replacement.
|
||||||
|
Available via the command `rust-analyzer.ssr`.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// Using structural search replace command [foo($a:expr, $b:expr) ==>> ($a).foo($b)]
|
||||||
|
|
||||||
|
// BEFORE
|
||||||
|
String::from(foo(y + 5, z))
|
||||||
|
|
||||||
|
// AFTER
|
||||||
|
String::from((y + 5).foo(z))
|
||||||
|
```
|
||||||
|
|
||||||
|
|===
|
||||||
|
| Editor | Action Name
|
||||||
|
|
||||||
|
| VS Code | **Rust Analyzer: Structural Search Replace**
|
||||||
|
|===
|
||||||
|
|
||||||
|
|
||||||
=== Workspace Symbol
|
=== Workspace Symbol
|
||||||
**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide_db/src/symbol_index.rs[symbol_index.rs]
|
**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide_db/src/symbol_index.rs[symbol_index.rs]
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
:important-caption: :heavy_exclamation_mark:
|
:important-caption: :heavy_exclamation_mark:
|
||||||
:caution-caption: :fire:
|
:caution-caption: :fire:
|
||||||
:warning-caption: :warning:
|
:warning-caption: :warning:
|
||||||
|
:source-highlighter: rouge
|
||||||
:experimental:
|
:experimental:
|
||||||
|
|
||||||
// Master copy of this document lives in the https://github.com/rust-analyzer/rust-analyzer repository
|
// Master copy of this document lives in the https://github.com/rust-analyzer/rust-analyzer repository
|
||||||
|
|
|
@ -50,12 +50,12 @@ impl Feature {
|
||||||
|
|
||||||
fn is_valid_feature_name(feature: &str) -> bool {
|
fn is_valid_feature_name(feature: &str) -> bool {
|
||||||
'word: for word in feature.split_whitespace() {
|
'word: for word in feature.split_whitespace() {
|
||||||
for &short in ["to"].iter() {
|
for &short in ["to", "and"].iter() {
|
||||||
if word == short {
|
if word == short {
|
||||||
continue 'word;
|
continue 'word;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for &short in ["To"].iter() {
|
for &short in ["To", "And"].iter() {
|
||||||
if word == short {
|
if word == short {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue