mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-13 05:38:46 +00:00
Merge #4664
4664: Generate feature documentation from code r=matklad a=matklad Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
commit
09df51dab8
33 changed files with 859 additions and 412 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -7,4 +7,4 @@ crates/*/target
|
|||
*.log
|
||||
*.iml
|
||||
.vscode/settings.json
|
||||
cargo-timing*.html
|
||||
*.html
|
||||
|
|
|
@ -4,9 +4,9 @@ use test_utils::mark;
|
|||
|
||||
use crate::{utils::FamousDefs, AssistContext, AssistId, Assists};
|
||||
|
||||
// Assist add_from_impl_for_enum
|
||||
// Assist: add_from_impl_for_enum
|
||||
//
|
||||
// Adds a From impl for an enum variant with one tuple field
|
||||
// Adds a From impl for an enum variant with one tuple field.
|
||||
//
|
||||
// ```
|
||||
// enum A { <|>One(u32) }
|
||||
|
|
|
@ -58,6 +58,25 @@ fn main() {
|
|||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_add_from_impl_for_enum() {
|
||||
check_doc_test(
|
||||
"add_from_impl_for_enum",
|
||||
r#####"
|
||||
enum A { <|>One(u32) }
|
||||
"#####,
|
||||
r#####"
|
||||
enum A { One(u32) }
|
||||
|
||||
impl From<u32> for A {
|
||||
fn from(v: u32) -> Self {
|
||||
A::One(v)
|
||||
}
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_add_function() {
|
||||
check_doc_test(
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
//! FIXME: write short doc here
|
||||
|
||||
mod completion_config;
|
||||
mod completion_item;
|
||||
mod completion_context;
|
||||
|
@ -35,6 +33,51 @@ pub use crate::completion::{
|
|||
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.
|
||||
///
|
||||
/// First, we look at the position and collect a so-called `CompletionContext.
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
//! FIXME: write short doc here
|
||||
|
||||
use ra_assists::utils::TryEnum;
|
||||
use ra_syntax::{
|
||||
ast::{self, AstNode},
|
||||
TextRange, TextSize,
|
||||
};
|
||||
use ra_text_edit::TextEdit;
|
||||
|
||||
use super::completion_config::SnippetCap;
|
||||
use crate::{
|
||||
completion::{
|
||||
completion_context::CompletionContext,
|
||||
|
@ -14,7 +13,8 @@ use crate::{
|
|||
},
|
||||
CompletionItem,
|
||||
};
|
||||
use ra_assists::utils::TryEnum;
|
||||
|
||||
use super::completion_config::SnippetCap;
|
||||
|
||||
pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
|
||||
if !ctx.config.enable_postfix_completions {
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
//! FIXME: write short doc here
|
||||
|
||||
use crate::TextRange;
|
||||
|
||||
use ra_syntax::{
|
||||
ast::{self, AttrsOwner, NameOwner, TypeAscriptionOwner, TypeParamsOwner},
|
||||
match_ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, WalkEvent,
|
||||
match_ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, WalkEvent,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -18,6 +14,19 @@ pub struct StructureNode {
|
|||
pub deprecated: bool,
|
||||
}
|
||||
|
||||
// Feature: File Structure
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// |===
|
||||
// | Editor | Shortcut
|
||||
//
|
||||
// | VS Code | kbd:[Ctrl+Shift+O]
|
||||
// |===
|
||||
pub fn file_structure(file: &SourceFile) -> Vec<StructureNode> {
|
||||
let mut res = Vec::new();
|
||||
let mut stack = Vec::new();
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
//! This modules implements "expand macro" functionality in the IDE
|
||||
|
||||
use hir::Semantics;
|
||||
use ra_ide_db::RootDatabase;
|
||||
use ra_syntax::{
|
||||
|
@ -14,6 +12,15 @@ pub struct ExpandedMacro {
|
|||
pub expansion: String,
|
||||
}
|
||||
|
||||
// Feature: Expand Macro Recursively
|
||||
//
|
||||
// Shows the full macro expansion of the macro at current cursor.
|
||||
//
|
||||
// |===
|
||||
// | Editor | Action Name
|
||||
//
|
||||
// | VS Code | **Rust Analyzer: Expand macro recursively**
|
||||
// |===
|
||||
pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> {
|
||||
let sema = Semantics::new(db);
|
||||
let file = sema.parse(position.file_id);
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
//! FIXME: write short doc here
|
||||
|
||||
use std::iter::successors;
|
||||
|
||||
use hir::Semantics;
|
||||
|
@ -14,6 +12,16 @@ use ra_syntax::{
|
|||
|
||||
use crate::FileRange;
|
||||
|
||||
// Feature: Extend Selection
|
||||
//
|
||||
// Extends the current selection to the encompassing syntactic construct
|
||||
// (expression, statement, item, module, etc). It works with multiple cursors.
|
||||
//
|
||||
// |===
|
||||
// | Editor | Shortcut
|
||||
//
|
||||
// | VS Code | kbd:[Ctrl+Shift+→]
|
||||
// |===
|
||||
pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange {
|
||||
let sema = Semantics::new(db);
|
||||
let src = sema.parse(frange.file_id);
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
//! FIXME: write short doc here
|
||||
|
||||
use hir::Semantics;
|
||||
use ra_ide_db::{
|
||||
defs::{classify_name, classify_name_ref},
|
||||
|
@ -17,6 +15,15 @@ use crate::{
|
|||
FilePosition, NavigationTarget, RangeInfo,
|
||||
};
|
||||
|
||||
// Feature: Go to Definition
|
||||
//
|
||||
// Navigates to the definition of an identifier.
|
||||
//
|
||||
// |===
|
||||
// | Editor | Shortcut
|
||||
//
|
||||
// | VS Code | kbd:[F12]
|
||||
// |===
|
||||
pub(crate) fn goto_definition(
|
||||
db: &RootDatabase,
|
||||
position: FilePosition,
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
//! FIXME: write short doc here
|
||||
|
||||
use hir::{Crate, ImplDef, Semantics};
|
||||
use ra_ide_db::RootDatabase;
|
||||
use ra_syntax::{algo::find_node_at_offset, ast, AstNode};
|
||||
|
||||
use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo};
|
||||
|
||||
// Feature: Go to Implementation
|
||||
//
|
||||
// Navigates to the impl block of structs, enums or traits. Also implemented as a code lens.
|
||||
//
|
||||
// |===
|
||||
// | Editor | Shortcut
|
||||
//
|
||||
// | VS Code | kbd:[Ctrl+F12]
|
||||
// |===
|
||||
pub(crate) fn goto_implementation(
|
||||
db: &RootDatabase,
|
||||
position: FilePosition,
|
|
@ -1,10 +1,17 @@
|
|||
//! FIXME: write short doc here
|
||||
|
||||
use ra_ide_db::RootDatabase;
|
||||
use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset};
|
||||
|
||||
use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo};
|
||||
|
||||
// Feature: Go to Type Definition
|
||||
//
|
||||
// Navigates to the type of an identifier.
|
||||
//
|
||||
// |===
|
||||
// | Editor | Action Name
|
||||
//
|
||||
// | VS Code | **Go to Type Definition*
|
||||
// |===
|
||||
pub(crate) fn goto_type_definition(
|
||||
db: &RootDatabase,
|
||||
position: FilePosition,
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
//! Logic for computing info that is displayed when the user hovers over any
|
||||
//! source code items (e.g. function call, struct field, variable symbol...)
|
||||
use std::iter::once;
|
||||
|
||||
use hir::{
|
||||
Adt, AsAssocItem, AssocItemContainer, FieldSource, HasSource, HirDisplay, ModuleDef,
|
||||
ModuleSource, Semantics,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use ra_db::SourceDatabase;
|
||||
use ra_ide_db::{
|
||||
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},
|
||||
FilePosition, RangeInfo,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use std::iter::once;
|
||||
|
||||
/// Contains the results when hovering over an item
|
||||
#[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(
|
||||
docs: 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> {
|
||||
return tokens.max_by_key(priority);
|
||||
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 ra_ide_db::RootDatabase;
|
||||
use ra_prof::profile;
|
||||
|
@ -39,6 +37,26 @@ pub struct InlayHint {
|
|||
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(
|
||||
db: &RootDatabase,
|
||||
file_id: FileId,
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
//! FIXME: write short doc here
|
||||
|
||||
use itertools::Itertools;
|
||||
use ra_fmt::{compute_ws, extract_trivial_expression};
|
||||
use ra_syntax::{
|
||||
|
@ -11,6 +9,15 @@ use ra_syntax::{
|
|||
};
|
||||
use ra_text_edit::{TextEdit, TextEditBuilder};
|
||||
|
||||
// Feature: Join Lines
|
||||
//
|
||||
// Join selected lines into one, smartly fixing up whitespace, trailing commas, and braces.
|
||||
//
|
||||
// |===
|
||||
// | Editor | Action Name
|
||||
//
|
||||
// | VS Code | **Rust Analyzer: Join lines**
|
||||
// |===
|
||||
pub fn join_lines(file: &SourceFile, range: TextRange) -> TextEdit {
|
||||
let range = if range.is_empty() {
|
||||
let syntax = file.syntax();
|
||||
|
|
|
@ -23,6 +23,7 @@ mod completion;
|
|||
mod runnables;
|
||||
mod goto_definition;
|
||||
mod goto_type_definition;
|
||||
mod goto_implementation;
|
||||
mod extend_selection;
|
||||
mod hover;
|
||||
mod call_hierarchy;
|
||||
|
@ -30,7 +31,6 @@ mod call_info;
|
|||
mod syntax_highlighting;
|
||||
mod parent_module;
|
||||
mod references;
|
||||
mod impls;
|
||||
mod diagnostics;
|
||||
mod syntax_tree;
|
||||
mod folding_ranges;
|
||||
|
@ -373,7 +373,7 @@ impl Analysis {
|
|||
&self,
|
||||
position: FilePosition,
|
||||
) -> Cancelable<Option<RangeInfo<Vec<NavigationTarget>>>> {
|
||||
self.with_db(|db| impls::goto_implementation(db, position))
|
||||
self.with_db(|db| goto_implementation::goto_implementation(db, position))
|
||||
}
|
||||
|
||||
/// Returns the type definitions for the symbol at `position`.
|
||||
|
|
|
@ -1,7 +1,16 @@
|
|||
//! FIXME: write short doc here
|
||||
|
||||
use ra_syntax::{ast::AstNode, SourceFile, SyntaxKind, TextSize, T};
|
||||
|
||||
// Feature: 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.
|
||||
//
|
||||
// |===
|
||||
// | Editor | Action Name
|
||||
//
|
||||
// | VS Code | **Rust Analyzer: Find matching brace**
|
||||
// |===
|
||||
pub fn matching_brace(file: &SourceFile, offset: TextSize) -> Option<TextSize> {
|
||||
const BRACES: &[SyntaxKind] =
|
||||
&[T!['{'], T!['}'], T!['['], T![']'], T!['('], T![')'], T![<], T![>]];
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
//! FIXME: write short doc here
|
||||
|
||||
use hir::Semantics;
|
||||
use ra_db::{CrateId, FileId, FilePosition};
|
||||
use ra_ide_db::RootDatabase;
|
||||
|
@ -11,6 +9,16 @@ use test_utils::mark;
|
|||
|
||||
use crate::NavigationTarget;
|
||||
|
||||
// Feature: Parent Module
|
||||
//
|
||||
// Navigates to the parent module of the current module.
|
||||
//
|
||||
// |===
|
||||
// | Editor | Action Name
|
||||
//
|
||||
// | VS Code | **Rust Analyzer: Locate parent module**
|
||||
// |===
|
||||
|
||||
/// This returns `Vec` because a module may be included from several places. We
|
||||
/// don't handle this case yet though, so the Vec has length at most one.
|
||||
pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<NavigationTarget> {
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
//! FIXME: write short doc here
|
||||
|
||||
use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics};
|
||||
use itertools::Itertools;
|
||||
use ra_ide_db::RootDatabase;
|
||||
|
@ -44,6 +42,17 @@ pub enum RunnableKind {
|
|||
Bin,
|
||||
}
|
||||
|
||||
// Feature: Run
|
||||
//
|
||||
// Shows a 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!
|
||||
//
|
||||
// |===
|
||||
// | Editor | Action Name
|
||||
//
|
||||
// | VS Code | **Rust Analyzer: Run**
|
||||
// |===
|
||||
pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
|
||||
let sema = Semantics::new(db);
|
||||
let source_file = sema.parse(file_id);
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
//! structural search replace
|
||||
|
||||
use std::{collections::HashMap, iter::once, str::FromStr};
|
||||
|
||||
use ra_db::{SourceDatabase, SourceDatabaseExt};
|
||||
|
@ -25,6 +23,28 @@ impl std::fmt::Display for SsrError {
|
|||
|
||||
impl std::error::Error for SsrError {}
|
||||
|
||||
// Feature: Structural Seach and Replace
|
||||
//
|
||||
// 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**
|
||||
// |===
|
||||
pub fn parse_search_replace(
|
||||
query: &str,
|
||||
parse_only: bool,
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
//! FIXME: write short doc here
|
||||
|
||||
use std::{fmt, iter::FromIterator, sync::Arc};
|
||||
|
||||
use hir::MacroFile;
|
||||
|
@ -26,6 +24,15 @@ fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats {
|
|||
db.query(hir::db::ParseMacroQuery).entries::<SyntaxTreeStats>()
|
||||
}
|
||||
|
||||
// Feature: Status
|
||||
//
|
||||
// Shows internal statistic about memory usage of rust-analyzer.
|
||||
//
|
||||
// |===
|
||||
// | Editor | Action Name
|
||||
//
|
||||
// | VS Code | **Rust Analyzer: Status**
|
||||
// |===
|
||||
pub(crate) fn status(db: &RootDatabase) -> String {
|
||||
let files_stats = db.query(FileTextQuery).entries::<FilesStats>();
|
||||
let syntax_tree_stats = syntax_tree_stats(db);
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
//! Implements syntax highlighting.
|
||||
|
||||
mod tags;
|
||||
mod html;
|
||||
#[cfg(test)]
|
||||
|
@ -32,81 +30,15 @@ pub struct HighlightedRange {
|
|||
pub binding_hash: Option<u64>,
|
||||
}
|
||||
|
||||
#[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
|
||||
}
|
||||
}
|
||||
|
||||
// Feature: Semantic Syntax Highlighting
|
||||
//
|
||||
// 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.
|
||||
pub(crate) fn highlight(
|
||||
db: &RootDatabase,
|
||||
file_id: FileId,
|
||||
|
@ -291,6 +223,81 @@ pub(crate) fn highlight(
|
|||
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> {
|
||||
Some(match kind {
|
||||
FormatSpecifier::Open
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
//! FIXME: write short doc here
|
||||
|
||||
use ra_db::SourceDatabase;
|
||||
use ra_db::{FileId, SourceDatabase};
|
||||
use ra_ide_db::RootDatabase;
|
||||
use ra_syntax::{
|
||||
algo, AstNode, NodeOrToken, SourceFile,
|
||||
|
@ -8,8 +6,16 @@ use ra_syntax::{
|
|||
SyntaxToken, TextRange, TextSize,
|
||||
};
|
||||
|
||||
pub use ra_db::FileId;
|
||||
|
||||
// Feature: Show Syntax Tree
|
||||
//
|
||||
// Shows the parse tree of the current file. It exists mostly for debugging
|
||||
// rust-analyzer itself.
|
||||
//
|
||||
// |===
|
||||
// | Editor | Action Name
|
||||
//
|
||||
// | VS Code | **Rust Analyzer: Show Syntax Tree**
|
||||
// |===
|
||||
pub(crate) fn syntax_tree(
|
||||
db: &RootDatabase,
|
||||
file_id: FileId,
|
||||
|
|
|
@ -32,6 +32,13 @@ pub(crate) use on_enter::on_enter;
|
|||
|
||||
pub(crate) const TRIGGER_CHARS: &str = ".=>";
|
||||
|
||||
// Feature: 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
|
||||
pub(crate) fn on_char_typed(
|
||||
db: &RootDatabase,
|
||||
position: FilePosition,
|
||||
|
|
|
@ -110,6 +110,27 @@ fn file_symbols(db: &impl SymbolsDatabase, file_id: FileId) -> Arc<SymbolIndex>
|
|||
Arc::new(SymbolIndex::new(symbols))
|
||||
}
|
||||
|
||||
// Feature: Workspace Symbol
|
||||
//
|
||||
// Uses fuzzy-search to find types, modules and functions by name across your
|
||||
// project and dependencies. This is **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.
|
||||
//
|
||||
// |===
|
||||
// | Editor | Shortcut
|
||||
//
|
||||
// | VS Code | kbd:[Ctrl+T]
|
||||
// |===
|
||||
pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> {
|
||||
/// Need to wrap Snapshot to provide `Clone` impl for `map_with`
|
||||
struct Snap(salsa::Snapshot<RootDatabase>);
|
||||
|
|
|
@ -56,6 +56,24 @@ fn main() {
|
|||
}
|
||||
```
|
||||
|
||||
## `add_from_impl_for_enum`
|
||||
|
||||
Adds a From impl for an enum variant with one tuple field.
|
||||
|
||||
```rust
|
||||
// BEFORE
|
||||
enum A { ┃One(u32) }
|
||||
|
||||
// AFTER
|
||||
enum A { One(u32) }
|
||||
|
||||
impl From<u32> for A {
|
||||
fn from(v: u32) -> Self {
|
||||
A::One(v)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## `add_function`
|
||||
|
||||
Adds a stub function with a signature matching the function under the cursor.
|
||||
|
|
|
@ -1,218 +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.
|
||||
|
||||
### Workspace Symbol <kbd>ctrl+t</kbd>
|
||||
|
||||
Uses fuzzy-search to find types, modules and functions by name across your
|
||||
project and dependencies. This is **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 <kbd>ctrl+shift+o</kbd>
|
||||
|
||||
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 <kbd>ctrl+shift+p</kbd>
|
||||
|
||||
#### Run
|
||||
|
||||
Shows a 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.
|
||||
|
||||
#### Expand Macro Recursively
|
||||
|
||||
Shows the full macro expansion of the macro at current cursor.
|
||||
|
||||
#### Status
|
||||
|
||||
Shows internal statistic about memory usage of rust-analyzer.
|
||||
|
||||
#### Show RA Version
|
||||
|
||||
Show current rust-analyzer version.
|
||||
|
||||
#### 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.
|
||||
|
||||
#### 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`.
|
||||
|
||||
#### Structural Seach and Replace
|
||||
|
||||
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))
|
||||
```
|
||||
|
||||
### Assists (Code Actions)
|
||||
|
||||
Assists, or code actions, are small local refactorings, available in a particular context.
|
||||
They are usually triggered by a shortcut or by clicking a light bulb icon in the editor.
|
||||
|
||||
See [assists.md](./assists.md) for the list of available assists.
|
||||
|
||||
### 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).
|
298
docs/user/generated_features.adoc
Normal file
298
docs/user/generated_features.adoc
Normal file
|
@ -0,0 +1,298 @@
|
|||
=== 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
|
||||
**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/extend_selection.rs[extend_selection.rs]
|
||||
|
||||
Extends the current selection to the encompassing syntactic construct
|
||||
(expression, statement, item, module, etc). It works with multiple cursors.
|
||||
|
||||
|===
|
||||
| Editor | Shortcut
|
||||
|
||||
| VS Code | kbd:[Ctrl+Shift+→]
|
||||
|===
|
||||
|
||||
|
||||
=== File Structure
|
||||
**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/display/structure.rs[structure.rs]
|
||||
|
||||
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
|
||||
|
||||
|===
|
||||
| Editor | Shortcut
|
||||
|
||||
| VS Code | kbd:[Ctrl+Shift+O]
|
||||
|===
|
||||
|
||||
|
||||
=== Go to Definition
|
||||
**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/goto_definition.rs[goto_definition.rs]
|
||||
|
||||
Navigates to the definition of an identifier.
|
||||
|
||||
|===
|
||||
| Editor | Shortcut
|
||||
|
||||
| VS Code | kbd:[F12]
|
||||
|===
|
||||
|
||||
|
||||
=== Go to Implementation
|
||||
**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/goto_implementation.rs[goto_implementation.rs]
|
||||
|
||||
Navigates to the impl block of structs, enums or traits. Also implemented as a code lens.
|
||||
|
||||
|===
|
||||
| Editor | Shortcut
|
||||
|
||||
| VS Code | kbd:[Ctrl+F12]
|
||||
|===
|
||||
|
||||
|
||||
=== Go to Type Definition
|
||||
**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/goto_type_definition.rs[goto_type_definition.rs]
|
||||
|
||||
Navigates to the type of an identifier.
|
||||
|
||||
|===
|
||||
| Editor | Action Name
|
||||
|
||||
| VS Code | **Go to Type Definition*
|
||||
|===
|
||||
|
||||
|
||||
=== 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
|
||||
**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/join_lines.rs[join_lines.rs]
|
||||
|
||||
Join selected lines into one, smartly fixing up whitespace, trailing commas, and braces.
|
||||
|
||||
|===
|
||||
| Editor | Action Name
|
||||
|
||||
| VS Code | **Rust Analyzer: Join lines**
|
||||
|===
|
||||
|
||||
|
||||
=== 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
|
||||
**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/matching_brace.rs[matching_brace.rs]
|
||||
|
||||
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.
|
||||
|
||||
|===
|
||||
| Editor | Action Name
|
||||
|
||||
| VS Code | **Rust Analyzer: Find matching brace**
|
||||
|===
|
||||
|
||||
|
||||
=== On Typing Assists
|
||||
**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/typing.rs[typing.rs]
|
||||
|
||||
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
|
||||
|
||||
|
||||
=== Parent Module
|
||||
**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/parent_module.rs[parent_module.rs]
|
||||
|
||||
Navigates to the parent module of the current module.
|
||||
|
||||
|===
|
||||
| Editor | Action Name
|
||||
|
||||
| VS Code | **Rust Analyzer: Locate parent module**
|
||||
|===
|
||||
|
||||
|
||||
=== Run
|
||||
**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/runnables.rs[runnables.rs]
|
||||
|
||||
Shows a 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!
|
||||
|
||||
|===
|
||||
| Editor | Action Name
|
||||
|
||||
| VS Code | **Rust Analyzer: Run**
|
||||
|===
|
||||
|
||||
|
||||
=== 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
|
||||
**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/syntax_tree.rs[syntax_tree.rs]
|
||||
|
||||
Shows the parse tree of the current file. It exists mostly for debugging
|
||||
rust-analyzer itself.
|
||||
|
||||
|===
|
||||
| Editor | Action Name
|
||||
|
||||
| VS Code | **Rust Analyzer: Show Syntax Tree**
|
||||
|===
|
||||
|
||||
|
||||
=== 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
|
||||
**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide_db/src/symbol_index.rs[symbol_index.rs]
|
||||
|
||||
Uses fuzzy-search to find types, modules and functions by name across your
|
||||
project and dependencies. This is **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.
|
||||
|
||||
|===
|
||||
| Editor | Shortcut
|
||||
|
||||
| VS Code | kbd:[Ctrl+T]
|
||||
|===
|
|
@ -8,6 +8,8 @@
|
|||
:important-caption: :heavy_exclamation_mark:
|
||||
:caution-caption: :fire:
|
||||
:warning-caption: :warning:
|
||||
:source-highlighter: rouge
|
||||
:experimental:
|
||||
|
||||
// Master copy of this document lives in the https://github.com/rust-analyzer/rust-analyzer repository
|
||||
|
||||
|
@ -268,6 +270,13 @@ Gnome Builder currently has support for RLS, and there's no way to configure the
|
|||
1. Rename, symlink or copy the `rust-analyzer` binary to `rls` and place it somewhere Builder can find (in `PATH`, or under `~/.cargo/bin`).
|
||||
2. Enable the Rust Builder plugin.
|
||||
|
||||
== Usage
|
||||
== Features
|
||||
|
||||
See https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/user/features.md[features.md].
|
||||
include::./generated_features.adoc[]
|
||||
|
||||
== Assists (Code Actions)
|
||||
|
||||
Assists, or code actions, are small local refactorings, available in a particular context.
|
||||
They are usually triggered by a shortcut or by clicking a light bulb icon in the editor.
|
||||
|
||||
See [assists.md](./assists.md) for the list of available assists.
|
||||
|
|
|
@ -8,14 +8,15 @@
|
|||
mod gen_syntax;
|
||||
mod gen_parser_tests;
|
||||
mod gen_assists_docs;
|
||||
mod gen_feature_docs;
|
||||
|
||||
use std::{mem, path::Path};
|
||||
|
||||
use crate::{not_bash::fs2, Result};
|
||||
|
||||
pub use self::{
|
||||
gen_assists_docs::generate_assists_docs, gen_parser_tests::generate_parser_tests,
|
||||
gen_syntax::generate_syntax,
|
||||
gen_assists_docs::generate_assists_docs, gen_feature_docs::generate_feature_docs,
|
||||
gen_parser_tests::generate_parser_tests, gen_syntax::generate_syntax,
|
||||
};
|
||||
|
||||
const GRAMMAR_DIR: &str = "crates/ra_parser/src/grammar";
|
||||
|
@ -40,7 +41,7 @@ pub enum Mode {
|
|||
/// With verify = false,
|
||||
fn update(path: &Path, contents: &str, mode: Mode) -> Result<()> {
|
||||
match fs2::read_to_string(path) {
|
||||
Ok(ref old_contents) if normalize(old_contents) == normalize(contents) => {
|
||||
Ok(old_contents) if normalize(&old_contents) == normalize(contents) => {
|
||||
return Ok(());
|
||||
}
|
||||
_ => (),
|
||||
|
@ -61,8 +62,24 @@ fn extract_comment_blocks(text: &str) -> Vec<Vec<String>> {
|
|||
do_extract_comment_blocks(text, false)
|
||||
}
|
||||
|
||||
fn extract_comment_blocks_with_empty_lines(text: &str) -> Vec<Vec<String>> {
|
||||
do_extract_comment_blocks(text, true)
|
||||
fn extract_comment_blocks_with_empty_lines(tag: &str, text: &str) -> Vec<CommentBlock> {
|
||||
assert!(tag.starts_with(char::is_uppercase));
|
||||
let tag = format!("{}:", tag);
|
||||
let mut res = Vec::new();
|
||||
for mut block in do_extract_comment_blocks(text, true) {
|
||||
let first = block.remove(0);
|
||||
if first.starts_with(&tag) {
|
||||
let id = first[tag.len()..].trim().to_string();
|
||||
let block = CommentBlock { id, contents: block };
|
||||
res.push(block);
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
struct CommentBlock {
|
||||
id: String,
|
||||
contents: Vec<String>,
|
||||
}
|
||||
|
||||
fn do_extract_comment_blocks(text: &str, allow_blocks_with_empty_lines: bool) -> Vec<Vec<String>> {
|
||||
|
|
|
@ -33,22 +33,18 @@ impl Assist {
|
|||
|
||||
fn collect_file(acc: &mut Vec<Assist>, path: &Path) -> Result<()> {
|
||||
let text = fs::read_to_string(path)?;
|
||||
let comment_blocks = extract_comment_blocks_with_empty_lines(&text);
|
||||
let comment_blocks = extract_comment_blocks_with_empty_lines("Assist", &text);
|
||||
|
||||
for block in comment_blocks {
|
||||
// FIXME: doesn't support blank lines yet, need to tweak
|
||||
// `extract_comment_blocks` for that.
|
||||
let mut lines = block.iter();
|
||||
let first_line = lines.next().unwrap();
|
||||
if !first_line.starts_with("Assist: ") {
|
||||
continue;
|
||||
}
|
||||
let id = first_line["Assist: ".len()..].to_string();
|
||||
let id = block.id;
|
||||
assert!(
|
||||
id.chars().all(|it| it.is_ascii_lowercase() || it == '_'),
|
||||
"invalid assist id: {:?}",
|
||||
id
|
||||
);
|
||||
let mut lines = block.contents.iter();
|
||||
|
||||
let doc = take_until(lines.by_ref(), "```").trim().to_string();
|
||||
assert!(
|
||||
|
|
88
xtask/src/codegen/gen_feature_docs.rs
Normal file
88
xtask/src/codegen/gen_feature_docs.rs
Normal file
|
@ -0,0 +1,88 @@
|
|||
//! Generates `assists.md` documentation.
|
||||
|
||||
use std::{fmt, fs, path::PathBuf};
|
||||
|
||||
use crate::{
|
||||
codegen::{self, extract_comment_blocks_with_empty_lines, Mode},
|
||||
project_root, rust_files, Result,
|
||||
};
|
||||
|
||||
pub fn generate_feature_docs(mode: Mode) -> Result<()> {
|
||||
let features = Feature::collect()?;
|
||||
let contents = features.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n");
|
||||
let contents = contents.trim().to_string() + "\n";
|
||||
let dst = project_root().join("docs/user/generated_features.adoc");
|
||||
codegen::update(&dst, &contents, mode)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Feature {
|
||||
id: String,
|
||||
path: PathBuf,
|
||||
doc: String,
|
||||
}
|
||||
|
||||
impl Feature {
|
||||
fn collect() -> Result<Vec<Feature>> {
|
||||
let mut res = Vec::new();
|
||||
for path in rust_files(&project_root()) {
|
||||
collect_file(&mut res, path)?;
|
||||
}
|
||||
res.sort_by(|lhs, rhs| lhs.id.cmp(&rhs.id));
|
||||
return Ok(res);
|
||||
|
||||
fn collect_file(acc: &mut Vec<Feature>, path: PathBuf) -> Result<()> {
|
||||
let text = fs::read_to_string(&path)?;
|
||||
let comment_blocks = extract_comment_blocks_with_empty_lines("Feature", &text);
|
||||
|
||||
for block in comment_blocks {
|
||||
let id = block.id;
|
||||
assert!(is_valid_feature_name(&id), "invalid feature name: {:?}", id);
|
||||
let doc = block.contents.join("\n");
|
||||
acc.push(Feature { id, path: path.clone(), doc })
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_valid_feature_name(feature: &str) -> bool {
|
||||
'word: for word in feature.split_whitespace() {
|
||||
for &short in ["to", "and"].iter() {
|
||||
if word == short {
|
||||
continue 'word;
|
||||
}
|
||||
}
|
||||
for &short in ["To", "And"].iter() {
|
||||
if word == short {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if !word.starts_with(char::is_uppercase) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
impl fmt::Display for Feature {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
writeln!(f, "=== {}", self.id)?;
|
||||
let path = self.path.strip_prefix(&project_root()).unwrap().display().to_string();
|
||||
let path = path.replace('\\', "/");
|
||||
let name = self.path.file_name().unwrap();
|
||||
|
||||
//FIXME: generate line number as well
|
||||
writeln!(
|
||||
f,
|
||||
"**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/{}[{}]",
|
||||
path,
|
||||
name.to_str().unwrap(),
|
||||
)?;
|
||||
|
||||
writeln!(f, "{}", self.doc)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -75,6 +75,7 @@ FLAGS:
|
|||
codegen::generate_syntax(Mode::Overwrite)?;
|
||||
codegen::generate_parser_tests(Mode::Overwrite)?;
|
||||
codegen::generate_assists_docs(Mode::Overwrite)?;
|
||||
codegen::generate_feature_docs(Mode::Overwrite)?;
|
||||
Ok(())
|
||||
}
|
||||
"format" => {
|
||||
|
|
|
@ -30,6 +30,13 @@ fn generated_assists_are_fresh() {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generated_features_are_fresh() {
|
||||
if let Err(error) = codegen::generate_feature_docs(Mode::Verify) {
|
||||
panic!("{}. Please update features by running `cargo xtask codegen`", error);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_code_formatting() {
|
||||
if let Err(error) = run_rustfmt(Mode::Verify) {
|
||||
|
@ -95,7 +102,7 @@ impl TidyDocs {
|
|||
fn visit(&mut self, path: &Path, text: &str) {
|
||||
// Test hopefully don't really need comments, and for assists we already
|
||||
// have special comments which are source of doc tests and user docs.
|
||||
if is_exclude_dir(path, &["tests", "test_data", "handlers"]) {
|
||||
if is_exclude_dir(path, &["tests", "test_data"]) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -110,9 +117,12 @@ impl TidyDocs {
|
|||
|
||||
if first_line.starts_with("//!") {
|
||||
if first_line.contains("FIXME") {
|
||||
self.contains_fixme.push(path.to_path_buf())
|
||||
self.contains_fixme.push(path.to_path_buf());
|
||||
}
|
||||
} else {
|
||||
if text.contains("// Feature:") || text.contains("// Assist:") {
|
||||
return;
|
||||
}
|
||||
self.missing_docs.push(path.display().to_string());
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue