mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-12 21:28:51 +00:00
Make bracket typing handler work on more things
This commit is contained in:
parent
cddaf7485f
commit
bac0ed579f
5 changed files with 200 additions and 131 deletions
|
@ -16,11 +16,11 @@
|
||||||
mod on_enter;
|
mod on_enter;
|
||||||
|
|
||||||
use ide_db::{base_db::SourceDatabase, FilePosition, RootDatabase};
|
use ide_db::{base_db::SourceDatabase, FilePosition, RootDatabase};
|
||||||
use span::EditionedFileId;
|
use span::{Edition, EditionedFileId};
|
||||||
use syntax::{
|
use syntax::{
|
||||||
algo::{ancestors_at_offset, find_node_at_offset},
|
algo::{ancestors_at_offset, find_node_at_offset},
|
||||||
ast::{self, edit::IndentLevel, AstToken},
|
ast::{self, edit::IndentLevel, AstToken},
|
||||||
AstNode, Parse, SourceFile, SyntaxKind, TextRange, TextSize, T,
|
AstNode, Parse, SourceFile, SyntaxKind, TextRange, TextSize,
|
||||||
};
|
};
|
||||||
|
|
||||||
use ide_db::text_edit::TextEdit;
|
use ide_db::text_edit::TextEdit;
|
||||||
|
@ -47,6 +47,7 @@ struct ExtendedTextEdit {
|
||||||
// - typing `.` in a chain method call auto-indents
|
// - typing `.` in a chain method call auto-indents
|
||||||
// - typing `{` or `(` in front of an expression inserts a closing `}` or `)` after the expression
|
// - typing `{` or `(` in front of an expression inserts a closing `}` or `)` after the expression
|
||||||
// - typing `{` in a use item adds a closing `}` in the right place
|
// - typing `{` in a use item adds a closing `}` in the right place
|
||||||
|
// - typing `>` to complete a return type `->` will insert a whitespace after it
|
||||||
//
|
//
|
||||||
// VS Code::
|
// VS Code::
|
||||||
//
|
//
|
||||||
|
@ -66,55 +67,65 @@ pub(crate) fn on_char_typed(
|
||||||
if !stdx::always!(TRIGGER_CHARS.contains(char_typed)) {
|
if !stdx::always!(TRIGGER_CHARS.contains(char_typed)) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let file = &db.parse(EditionedFileId::current_edition(position.file_id));
|
// FIXME: We need to figure out the edition of the file here, but that means hitting the
|
||||||
if !stdx::always!(file.tree().syntax().text().char_at(position.offset) == Some(char_typed)) {
|
// database for more than just parsing the file which is bad.
|
||||||
|
// FIXME: We are hitting the database here, if we are unlucky this call might block momentarily
|
||||||
|
// causing the editor to feel sluggish!
|
||||||
|
let edition = Edition::CURRENT_FIXME;
|
||||||
|
let file = &db.parse(EditionedFileId::new(position.file_id, edition));
|
||||||
|
let char_matches_position =
|
||||||
|
file.tree().syntax().text().char_at(position.offset) == Some(char_typed);
|
||||||
|
if !stdx::always!(char_matches_position) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let edit = on_char_typed_inner(file, position.offset, char_typed)?;
|
|
||||||
|
let edit = on_char_typed_(file, position.offset, char_typed, edition)?;
|
||||||
|
|
||||||
let mut sc = SourceChange::from_text_edit(position.file_id, edit.edit);
|
let mut sc = SourceChange::from_text_edit(position.file_id, edit.edit);
|
||||||
sc.is_snippet = edit.is_snippet;
|
sc.is_snippet = edit.is_snippet;
|
||||||
Some(sc)
|
Some(sc)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_char_typed_inner(
|
fn on_char_typed_(
|
||||||
file: &Parse<SourceFile>,
|
file: &Parse<SourceFile>,
|
||||||
offset: TextSize,
|
offset: TextSize,
|
||||||
char_typed: char,
|
char_typed: char,
|
||||||
|
edition: Edition,
|
||||||
) -> Option<ExtendedTextEdit> {
|
) -> Option<ExtendedTextEdit> {
|
||||||
if !stdx::always!(TRIGGER_CHARS.contains(char_typed)) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let conv = |text_edit: Option<TextEdit>| {
|
|
||||||
Some(ExtendedTextEdit { edit: text_edit?, is_snippet: false })
|
|
||||||
};
|
|
||||||
match char_typed {
|
match char_typed {
|
||||||
'.' => conv(on_dot_typed(&file.tree(), offset)),
|
'.' => on_dot_typed(&file.tree(), offset),
|
||||||
'=' => conv(on_eq_typed(&file.tree(), offset)),
|
'=' => on_eq_typed(&file.tree(), offset),
|
||||||
'<' => on_left_angle_typed(&file.tree(), offset),
|
'>' => on_right_angle_typed(&file.tree(), offset),
|
||||||
'>' => conv(on_right_angle_typed(&file.tree(), offset)),
|
'{' | '(' | '<' => on_opening_delimiter_typed(file, offset, char_typed, edition),
|
||||||
'{' => conv(on_opening_bracket_typed(file, offset, '{')),
|
|
||||||
'(' => conv(on_opening_bracket_typed(file, offset, '(')),
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
.map(conv)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inserts a closing bracket when the user types an opening bracket, wrapping an existing expression in a
|
fn conv(edit: TextEdit) -> ExtendedTextEdit {
|
||||||
|
ExtendedTextEdit { edit, is_snippet: false }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inserts a closing delimiter when the user types an opening bracket, wrapping an existing expression in a
|
||||||
/// block, or a part of a `use` item (for `{`).
|
/// block, or a part of a `use` item (for `{`).
|
||||||
fn on_opening_bracket_typed(
|
fn on_opening_delimiter_typed(
|
||||||
file: &Parse<SourceFile>,
|
file: &Parse<SourceFile>,
|
||||||
offset: TextSize,
|
offset: TextSize,
|
||||||
opening_bracket: char,
|
opening_bracket: char,
|
||||||
|
edition: Edition,
|
||||||
) -> Option<TextEdit> {
|
) -> Option<TextEdit> {
|
||||||
let (closing_bracket, expected_ast_bracket) = match opening_bracket {
|
type FilterFn = fn(SyntaxKind) -> bool;
|
||||||
'{' => ('}', SyntaxKind::L_CURLY),
|
let (closing_bracket, expected_ast_bracket, allowed_kinds) = match opening_bracket {
|
||||||
'(' => (')', SyntaxKind::L_PAREN),
|
'{' => ('}', SyntaxKind::L_CURLY, &[ast::Expr::can_cast as FilterFn] as &[FilterFn]),
|
||||||
|
'(' => (
|
||||||
|
')',
|
||||||
|
SyntaxKind::L_PAREN,
|
||||||
|
&[ast::Expr::can_cast, ast::Pat::can_cast, ast::Type::can_cast] as &[FilterFn],
|
||||||
|
),
|
||||||
|
'<' => ('>', SyntaxKind::L_ANGLE, &[ast::Type::can_cast as FilterFn] as &[FilterFn]),
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
if !stdx::always!(file.tree().syntax().text().char_at(offset) == Some(opening_bracket)) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let brace_token = file.tree().syntax().token_at_offset(offset).right_biased()?;
|
let brace_token = file.tree().syntax().token_at_offset(offset).right_biased()?;
|
||||||
if brace_token.kind() != expected_ast_bracket {
|
if brace_token.kind() != expected_ast_bracket {
|
||||||
return None;
|
return None;
|
||||||
|
@ -125,58 +136,53 @@ fn on_opening_bracket_typed(
|
||||||
if !stdx::always!(range.len() == TextSize::of(opening_bracket)) {
|
if !stdx::always!(range.len() == TextSize::of(opening_bracket)) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
// FIXME: Edition
|
let reparsed = file.reparse(range, "", edition).tree();
|
||||||
let file = file.reparse(range, "", span::Edition::CURRENT_FIXME);
|
|
||||||
|
|
||||||
if let Some(edit) = bracket_expr(&file.tree(), offset, opening_bracket, closing_bracket) {
|
if let Some(edit) =
|
||||||
|
on_delimited_node_typed(&reparsed, offset, opening_bracket, closing_bracket, allowed_kinds)
|
||||||
|
{
|
||||||
return Some(edit);
|
return Some(edit);
|
||||||
}
|
}
|
||||||
|
|
||||||
if closing_bracket == '}' {
|
match opening_bracket {
|
||||||
if let Some(edit) = brace_use_path(&file.tree(), offset) {
|
'{' => on_left_brace_typed(&reparsed, offset),
|
||||||
return Some(edit);
|
'<' => on_left_angle_typed(&file.tree(), &reparsed, offset),
|
||||||
}
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_left_brace_typed(reparsed: &SourceFile, offset: TextSize) -> Option<TextEdit> {
|
||||||
|
let segment: ast::PathSegment = find_node_at_offset(reparsed.syntax(), offset)?;
|
||||||
|
if segment.syntax().text_range().start() != offset {
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
return None;
|
let tree: ast::UseTree = find_node_at_offset(reparsed.syntax(), offset)?;
|
||||||
|
|
||||||
fn brace_use_path(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
|
Some(TextEdit::insert(tree.syntax().text_range().end() + TextSize::of("{"), "}".to_owned()))
|
||||||
let segment: ast::PathSegment = find_node_at_offset(file.syntax(), offset)?;
|
}
|
||||||
if segment.syntax().text_range().start() != offset {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let tree: ast::UseTree = find_node_at_offset(file.syntax(), offset)?;
|
fn on_delimited_node_typed(
|
||||||
|
reparsed: &SourceFile,
|
||||||
|
offset: TextSize,
|
||||||
|
opening_bracket: char,
|
||||||
|
closing_bracket: char,
|
||||||
|
kinds: &[fn(SyntaxKind) -> bool],
|
||||||
|
) -> Option<TextEdit> {
|
||||||
|
let t = reparsed.syntax().token_at_offset(offset).right_biased()?;
|
||||||
|
let (filter, node) = t
|
||||||
|
.parent_ancestors()
|
||||||
|
.take_while(|n| n.text_range().start() == offset)
|
||||||
|
.find_map(|n| kinds.iter().find(|&kind_filter| kind_filter(n.kind())).zip(Some(n)))?;
|
||||||
|
let mut node = node
|
||||||
|
.ancestors()
|
||||||
|
.take_while(|n| n.text_range().start() == offset && filter(n.kind()))
|
||||||
|
.last()?;
|
||||||
|
|
||||||
Some(TextEdit::insert(tree.syntax().text_range().end() + TextSize::of("{"), "}".to_owned()))
|
if let Some(parent) = node.parent().filter(|it| filter(it.kind())) {
|
||||||
}
|
let all_prev_sib_attr = {
|
||||||
|
let mut node = node.clone();
|
||||||
fn bracket_expr(
|
loop {
|
||||||
file: &SourceFile,
|
|
||||||
offset: TextSize,
|
|
||||||
opening_bracket: char,
|
|
||||||
closing_bracket: char,
|
|
||||||
) -> Option<TextEdit> {
|
|
||||||
let mut expr: ast::Expr = find_node_at_offset(file.syntax(), offset)?;
|
|
||||||
if expr.syntax().text_range().start() != offset {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enclose the outermost expression starting at `offset`
|
|
||||||
while let Some(parent) = expr.syntax().parent() {
|
|
||||||
if parent.text_range().start() != expr.syntax().text_range().start() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
match ast::Expr::cast(parent) {
|
|
||||||
Some(parent) => expr = parent,
|
|
||||||
None => break,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(parent) = expr.syntax().parent().and_then(ast::Expr::cast) {
|
|
||||||
let mut node = expr.syntax().clone();
|
|
||||||
let all_prev_sib_attr = loop {
|
|
||||||
match node.prev_sibling() {
|
match node.prev_sibling() {
|
||||||
Some(sib) if sib.kind().is_trivia() || sib.kind() == SyntaxKind::ATTR => {
|
Some(sib) if sib.kind().is_trivia() || sib.kind() == SyntaxKind::ATTR => {
|
||||||
node = sib
|
node = sib
|
||||||
|
@ -184,21 +190,20 @@ fn on_opening_bracket_typed(
|
||||||
Some(_) => break false,
|
Some(_) => break false,
|
||||||
None => break true,
|
None => break true,
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
||||||
if all_prev_sib_attr {
|
|
||||||
expr = parent;
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if all_prev_sib_attr {
|
||||||
|
node = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert the closing bracket right after the expression.
|
|
||||||
Some(TextEdit::insert(
|
|
||||||
expr.syntax().text_range().end() + TextSize::of(opening_bracket),
|
|
||||||
closing_bracket.to_string(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
// Insert the closing bracket right after the node.
|
||||||
|
Some(TextEdit::insert(
|
||||||
|
node.text_range().end() + TextSize::of(opening_bracket),
|
||||||
|
closing_bracket.to_string(),
|
||||||
|
))
|
||||||
|
}
|
||||||
/// Returns an edit which should be applied after `=` was typed. Primarily,
|
/// Returns an edit which should be applied after `=` was typed. Primarily,
|
||||||
/// this works when adding `let =`.
|
/// this works when adding `let =`.
|
||||||
// FIXME: use a snippet completion instead of this hack here.
|
// FIXME: use a snippet completion instead of this hack here.
|
||||||
|
@ -342,14 +347,15 @@ fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add closing `>` for generic arguments/parameters.
|
/// Add closing `>` for generic arguments/parameters.
|
||||||
fn on_left_angle_typed(file: &SourceFile, offset: TextSize) -> Option<ExtendedTextEdit> {
|
fn on_left_angle_typed(
|
||||||
let file_text = file.syntax().text();
|
file: &SourceFile,
|
||||||
if !stdx::always!(file_text.char_at(offset) == Some('<')) {
|
reparsed: &SourceFile,
|
||||||
return None;
|
offset: TextSize,
|
||||||
}
|
) -> Option<TextEdit> {
|
||||||
|
let file_text = reparsed.syntax().text();
|
||||||
|
|
||||||
// Find the next non-whitespace char in the line.
|
// Find the next non-whitespace char in the line, check if its a `>`
|
||||||
let mut next_offset = offset + TextSize::of('<');
|
let mut next_offset = offset;
|
||||||
while file_text.char_at(next_offset) == Some(' ') {
|
while file_text.char_at(next_offset) == Some(' ') {
|
||||||
next_offset += TextSize::of(' ')
|
next_offset += TextSize::of(' ')
|
||||||
}
|
}
|
||||||
|
@ -357,23 +363,14 @@ fn on_left_angle_typed(file: &SourceFile, offset: TextSize) -> Option<ExtendedTe
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let range = TextRange::at(offset, TextSize::of('<'));
|
if ancestors_at_offset(file.syntax(), offset)
|
||||||
if let Some(t) = file.syntax().token_at_offset(offset).left_biased() {
|
.take_while(|n| !ast::Item::can_cast(n.kind()))
|
||||||
if T![impl] == t.kind() {
|
.any(|n| {
|
||||||
return Some(ExtendedTextEdit {
|
ast::GenericParamList::can_cast(n.kind()) || ast::GenericArgList::can_cast(n.kind())
|
||||||
edit: TextEdit::replace(range, "<$0>".to_owned()),
|
|
||||||
is_snippet: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ancestors_at_offset(file.syntax(), offset).any(|n| {
|
|
||||||
ast::GenericParamList::can_cast(n.kind()) || ast::GenericArgList::can_cast(n.kind())
|
|
||||||
}) {
|
|
||||||
Some(ExtendedTextEdit {
|
|
||||||
edit: TextEdit::replace(range, "<$0>".to_owned()),
|
|
||||||
is_snippet: true,
|
|
||||||
})
|
})
|
||||||
|
{
|
||||||
|
// Insert the closing bracket right after
|
||||||
|
Some(TextEdit::insert(offset + TextSize::of('<'), '>'.to_string()))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -411,7 +408,7 @@ mod tests {
|
||||||
let edit = TextEdit::insert(offset, char_typed.to_string());
|
let edit = TextEdit::insert(offset, char_typed.to_string());
|
||||||
edit.apply(&mut before);
|
edit.apply(&mut before);
|
||||||
let parse = SourceFile::parse(&before, span::Edition::CURRENT_FIXME);
|
let parse = SourceFile::parse(&before, span::Edition::CURRENT_FIXME);
|
||||||
on_char_typed_inner(&parse, offset, char_typed).map(|it| {
|
on_char_typed_(&parse, offset, char_typed, span::Edition::CURRENT_FIXME).map(|it| {
|
||||||
it.apply(&mut before);
|
it.apply(&mut before);
|
||||||
before.to_string()
|
before.to_string()
|
||||||
})
|
})
|
||||||
|
@ -426,7 +423,7 @@ mod tests {
|
||||||
|
|
||||||
fn type_char_noop(char_typed: char, ra_fixture_before: &str) {
|
fn type_char_noop(char_typed: char, ra_fixture_before: &str) {
|
||||||
let file_change = do_type_char(char_typed, ra_fixture_before);
|
let file_change = do_type_char(char_typed, ra_fixture_before);
|
||||||
assert!(file_change.is_none())
|
assert_eq!(file_change, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1066,6 +1063,81 @@ fn f() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn adds_closing_parenthesis_for_pat() {
|
||||||
|
type_char(
|
||||||
|
'(',
|
||||||
|
r#"
|
||||||
|
fn f() { match () { $0() => () } }
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn f() { match () { (()) => () } }
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
type_char(
|
||||||
|
'(',
|
||||||
|
r#"
|
||||||
|
fn f($0n: ()) {}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn f((n): ()) {}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn adds_closing_parenthesis_for_ty() {
|
||||||
|
type_char(
|
||||||
|
'(',
|
||||||
|
r#"
|
||||||
|
fn f(n: $0()) {}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn f(n: (())) {}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
type_char(
|
||||||
|
'(',
|
||||||
|
r#"
|
||||||
|
fn f(n: $0a::b::<d>::c) {}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn f(n: (a::b::<d>::c)) {}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn adds_closing_angles_for_ty() {
|
||||||
|
type_char(
|
||||||
|
'<',
|
||||||
|
r#"
|
||||||
|
fn f(n: $0()) {}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn f(n: <()>) {}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
type_char(
|
||||||
|
'<',
|
||||||
|
r#"
|
||||||
|
fn f(n: $0a::b::<d>::c) {}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn f(n: <a::b::<d>::c>) {}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
type_char(
|
||||||
|
'<',
|
||||||
|
r#"
|
||||||
|
fn f(n: a$0b::<d>::c) {}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn f(n: a<>b::<d>::c) {}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parenthesis_noop_in_string_literal() {
|
fn parenthesis_noop_in_string_literal() {
|
||||||
// Regression test for #9351
|
// Regression test for #9351
|
||||||
|
@ -1154,6 +1226,12 @@ use $0Thing as _;
|
||||||
type_char_noop(
|
type_char_noop(
|
||||||
'(',
|
'(',
|
||||||
r#"
|
r#"
|
||||||
|
use some::pa$0th::to::Item;
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
type_char_noop(
|
||||||
|
'<',
|
||||||
|
r#"
|
||||||
use some::pa$0th::to::Item;
|
use some::pa$0th::to::Item;
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
@ -1170,7 +1248,7 @@ fn foo() {
|
||||||
"#,
|
"#,
|
||||||
r#"
|
r#"
|
||||||
fn foo() {
|
fn foo() {
|
||||||
bar::<$0>
|
bar::<>
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
@ -1184,7 +1262,7 @@ fn foo(bar: &[u64]) {
|
||||||
"#,
|
"#,
|
||||||
r#"
|
r#"
|
||||||
fn foo(bar: &[u64]) {
|
fn foo(bar: &[u64]) {
|
||||||
bar.iter().collect::<$0>();
|
bar.iter().collect::<>();
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
@ -1198,7 +1276,7 @@ fn foo(bar: &[u64]) {
|
||||||
fn foo$0() {}
|
fn foo$0() {}
|
||||||
"#,
|
"#,
|
||||||
r#"
|
r#"
|
||||||
fn foo<$0>() {}
|
fn foo<>() {}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
type_char(
|
type_char(
|
||||||
|
@ -1207,7 +1285,7 @@ fn foo<$0>() {}
|
||||||
fn foo$0
|
fn foo$0
|
||||||
"#,
|
"#,
|
||||||
r#"
|
r#"
|
||||||
fn foo<$0>
|
fn foo<>
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
type_char(
|
type_char(
|
||||||
|
@ -1216,7 +1294,7 @@ fn foo<$0>
|
||||||
struct Foo$0 {}
|
struct Foo$0 {}
|
||||||
"#,
|
"#,
|
||||||
r#"
|
r#"
|
||||||
struct Foo<$0> {}
|
struct Foo<> {}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
type_char(
|
type_char(
|
||||||
|
@ -1225,7 +1303,7 @@ struct Foo<$0> {}
|
||||||
struct Foo$0();
|
struct Foo$0();
|
||||||
"#,
|
"#,
|
||||||
r#"
|
r#"
|
||||||
struct Foo<$0>();
|
struct Foo<>();
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
type_char(
|
type_char(
|
||||||
|
@ -1234,7 +1312,7 @@ struct Foo<$0>();
|
||||||
struct Foo$0
|
struct Foo$0
|
||||||
"#,
|
"#,
|
||||||
r#"
|
r#"
|
||||||
struct Foo<$0>
|
struct Foo<>
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
type_char(
|
type_char(
|
||||||
|
@ -1243,7 +1321,7 @@ struct Foo<$0>
|
||||||
enum Foo$0
|
enum Foo$0
|
||||||
"#,
|
"#,
|
||||||
r#"
|
r#"
|
||||||
enum Foo<$0>
|
enum Foo<>
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
type_char(
|
type_char(
|
||||||
|
@ -1252,7 +1330,7 @@ enum Foo<$0>
|
||||||
trait Foo$0
|
trait Foo$0
|
||||||
"#,
|
"#,
|
||||||
r#"
|
r#"
|
||||||
trait Foo<$0>
|
trait Foo<>
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
type_char(
|
type_char(
|
||||||
|
@ -1261,16 +1339,7 @@ trait Foo<$0>
|
||||||
type Foo$0 = Bar;
|
type Foo$0 = Bar;
|
||||||
"#,
|
"#,
|
||||||
r#"
|
r#"
|
||||||
type Foo<$0> = Bar;
|
type Foo<> = Bar;
|
||||||
"#,
|
|
||||||
);
|
|
||||||
type_char(
|
|
||||||
'<',
|
|
||||||
r#"
|
|
||||||
impl$0 Foo {}
|
|
||||||
"#,
|
|
||||||
r#"
|
|
||||||
impl<$0> Foo {}
|
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
type_char(
|
type_char(
|
||||||
|
@ -1279,7 +1348,7 @@ impl<$0> Foo {}
|
||||||
impl<T> Foo$0 {}
|
impl<T> Foo$0 {}
|
||||||
"#,
|
"#,
|
||||||
r#"
|
r#"
|
||||||
impl<T> Foo<$0> {}
|
impl<T> Foo<> {}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
type_char(
|
type_char(
|
||||||
|
@ -1288,7 +1357,7 @@ impl<T> Foo<$0> {}
|
||||||
impl Foo$0 {}
|
impl Foo$0 {}
|
||||||
"#,
|
"#,
|
||||||
r#"
|
r#"
|
||||||
impl Foo<$0> {}
|
impl Foo<> {}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -309,7 +309,7 @@ config_data! {
|
||||||
signatureInfo_documentation_enable: bool = true,
|
signatureInfo_documentation_enable: bool = true,
|
||||||
|
|
||||||
/// Specify the characters to exclude from triggering typing assists. The default trigger characters are `.`, `=`, `<`, `>`, `{`, and `(`. Setting this to a string will disable typing assists for the specified characters.
|
/// Specify the characters to exclude from triggering typing assists. The default trigger characters are `.`, `=`, `<`, `>`, `{`, and `(`. Setting this to a string will disable typing assists for the specified characters.
|
||||||
typing_excludeChars: Option<String> = Some("<".to_owned()),
|
typing_excludeChars: Option<String> = None,
|
||||||
|
|
||||||
|
|
||||||
/// Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`].
|
/// Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`].
|
||||||
|
|
|
@ -992,7 +992,7 @@ Show full signature of the callable. Only shows parameters if disabled.
|
||||||
--
|
--
|
||||||
Show documentation.
|
Show documentation.
|
||||||
--
|
--
|
||||||
[[rust-analyzer.typing.excludeChars]]rust-analyzer.typing.excludeChars (default: `"<"`)::
|
[[rust-analyzer.typing.excludeChars]]rust-analyzer.typing.excludeChars (default: `null`)::
|
||||||
+
|
+
|
||||||
--
|
--
|
||||||
Specify the characters to exclude from triggering typing assists. The default trigger characters are `.`, `=`, `<`, `>`, `{`, and `(`. Setting this to a string will disable typing assists for the specified characters.
|
Specify the characters to exclude from triggering typing assists. The default trigger characters are `.`, `=`, `<`, `>`, `{`, and `(`. Setting this to a string will disable typing assists for the specified characters.
|
||||||
|
|
|
@ -2607,7 +2607,7 @@
|
||||||
"properties": {
|
"properties": {
|
||||||
"rust-analyzer.typing.excludeChars": {
|
"rust-analyzer.typing.excludeChars": {
|
||||||
"markdownDescription": "Specify the characters to exclude from triggering typing assists. The default trigger characters are `.`, `=`, `<`, `>`, `{`, and `(`. Setting this to a string will disable typing assists for the specified characters.",
|
"markdownDescription": "Specify the characters to exclude from triggering typing assists. The default trigger characters are `.`, `=`, `<`, `>`, `{`, and `(`. Setting this to a string will disable typing assists for the specified characters.",
|
||||||
"default": "<",
|
"default": null,
|
||||||
"type": [
|
"type": [
|
||||||
"null",
|
"null",
|
||||||
"string"
|
"string"
|
||||||
|
|
|
@ -324,7 +324,7 @@ class ExperimentalFeatures implements lc.StaticFeature {
|
||||||
}
|
}
|
||||||
fillClientCapabilities(capabilities: lc.ClientCapabilities): void {
|
fillClientCapabilities(capabilities: lc.ClientCapabilities): void {
|
||||||
capabilities.experimental = {
|
capabilities.experimental = {
|
||||||
snippetTextEdit: true,
|
snippetTextEdit: false,
|
||||||
codeActionGroup: true,
|
codeActionGroup: true,
|
||||||
hoverActions: true,
|
hoverActions: true,
|
||||||
serverStatusNotification: true,
|
serverStatusNotification: true,
|
||||||
|
|
Loading…
Reference in a new issue