Skip to content

Commit 2c25e43

Browse files
authored
Merge pull request #20015 from Veykril/push-wsxzsuurqwwr
feat: Insert required parentheses when typing `+` in dyn trait type
2 parents a67e2ba + b1824c3 commit 2c25e43

File tree

6 files changed

+92
-8
lines changed

6 files changed

+92
-8
lines changed

crates/ide/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,7 @@ impl Analysis {
409409
self.with_db(|db| typing::on_enter(db, position))
410410
}
411411

412-
pub const SUPPORTED_TRIGGER_CHARS: &'static str = typing::TRIGGER_CHARS;
412+
pub const SUPPORTED_TRIGGER_CHARS: &[char] = typing::TRIGGER_CHARS;
413413

414414
/// Returns an edit which should be applied after a character was typed.
415415
///
@@ -421,7 +421,7 @@ impl Analysis {
421421
char_typed: char,
422422
) -> Cancellable<Option<SourceChange>> {
423423
// Fast path to not even parse the file.
424-
if !typing::TRIGGER_CHARS.contains(char_typed) {
424+
if !typing::TRIGGER_CHARS.contains(&char_typed) {
425425
return Ok(None);
426426
}
427427

crates/ide/src/typing.rs

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
1616
mod on_enter;
1717

18+
use either::Either;
1819
use hir::EditionedFileId;
1920
use ide_db::{FilePosition, RootDatabase, base_db::RootQueryDb};
2021
use span::Edition;
@@ -33,7 +34,7 @@ use crate::SourceChange;
3334
pub(crate) use on_enter::on_enter;
3435

3536
// Don't forget to add new trigger characters to `server_capabilities` in `caps.rs`.
36-
pub(crate) const TRIGGER_CHARS: &str = ".=<>{(|";
37+
pub(crate) const TRIGGER_CHARS: &[char] = &['.', '=', '<', '>', '{', '(', '|', '+'];
3738

3839
struct ExtendedTextEdit {
3940
edit: TextEdit,
@@ -66,7 +67,7 @@ pub(crate) fn on_char_typed(
6667
position: FilePosition,
6768
char_typed: char,
6869
) -> Option<SourceChange> {
69-
if !stdx::always!(TRIGGER_CHARS.contains(char_typed)) {
70+
if !TRIGGER_CHARS.contains(&char_typed) {
7071
return None;
7172
}
7273
// FIXME: We need to figure out the edition of the file here, but that means hitting the
@@ -101,6 +102,7 @@ fn on_char_typed_(
101102
'>' => on_right_angle_typed(&file.tree(), offset),
102103
'{' | '(' | '<' => on_opening_delimiter_typed(file, offset, char_typed, edition),
103104
'|' => on_pipe_typed(&file.tree(), offset),
105+
'+' => on_plus_typed(&file.tree(), offset),
104106
_ => None,
105107
}
106108
.map(conv)
@@ -402,6 +404,28 @@ fn on_pipe_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
402404
Some(TextEdit::insert(after_lpipe, "|".to_owned()))
403405
}
404406

407+
fn on_plus_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
408+
let plus_token = file.syntax().token_at_offset(offset).right_biased()?;
409+
if plus_token.kind() != SyntaxKind::PLUS {
410+
return None;
411+
}
412+
let mut ancestors = plus_token.parent_ancestors();
413+
ancestors.next().and_then(ast::TypeBoundList::cast)?;
414+
let trait_type =
415+
ancestors.next().and_then(<Either<ast::DynTraitType, ast::ImplTraitType>>::cast)?;
416+
let kind = ancestors.next()?.kind();
417+
418+
if ast::RefType::can_cast(kind) || ast::PtrType::can_cast(kind) || ast::RetType::can_cast(kind)
419+
{
420+
let mut builder = TextEdit::builder();
421+
builder.insert(trait_type.syntax().text_range().start(), "(".to_owned());
422+
builder.insert(trait_type.syntax().text_range().end(), ")".to_owned());
423+
Some(builder.finish())
424+
} else {
425+
None
426+
}
427+
}
428+
405429
/// Adds a space after an arrow when `fn foo() { ... }` is turned into `fn foo() -> { ... }`
406430
fn on_right_angle_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
407431
let file_text = file.syntax().text();
@@ -1594,6 +1618,66 @@ fn foo() {
15941618
fn foo() {
15951619
let $0
15961620
}
1621+
"#,
1622+
);
1623+
}
1624+
1625+
#[test]
1626+
fn adds_parentheses_around_trait_object_in_ref_type() {
1627+
type_char(
1628+
'+',
1629+
r#"
1630+
fn foo(x: &dyn A$0) {}
1631+
"#,
1632+
r#"
1633+
fn foo(x: &(dyn A+)) {}
1634+
"#,
1635+
);
1636+
type_char(
1637+
'+',
1638+
r#"
1639+
fn foo(x: &'static dyn A$0B) {}
1640+
"#,
1641+
r#"
1642+
fn foo(x: &'static (dyn A+B)) {}
1643+
"#,
1644+
);
1645+
type_char_noop(
1646+
'+',
1647+
r#"
1648+
fn foo(x: &(dyn A$0)) {}
1649+
"#,
1650+
);
1651+
type_char_noop(
1652+
'+',
1653+
r#"
1654+
fn foo(x: Box<dyn A$0>) {}
1655+
"#,
1656+
);
1657+
}
1658+
1659+
#[test]
1660+
fn adds_parentheses_around_trait_object_in_ptr_type() {
1661+
type_char(
1662+
'+',
1663+
r#"
1664+
fn foo(x: *const dyn A$0) {}
1665+
"#,
1666+
r#"
1667+
fn foo(x: *const (dyn A+)) {}
1668+
"#,
1669+
);
1670+
}
1671+
1672+
#[test]
1673+
fn adds_parentheses_around_trait_object_in_return_type() {
1674+
type_char(
1675+
'+',
1676+
r#"
1677+
fn foo(x: fn() -> dyn A$0) {}
1678+
"#,
1679+
r#"
1680+
fn foo(x: fn() -> (dyn A+)) {}
15971681
"#,
15981682
);
15991683
}

crates/rust-analyzer/src/config.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ config_data! {
344344
/// - typing `{` in a use item adds a closing `}` in the right place
345345
/// - typing `>` to complete a return type `->` will insert a whitespace after it
346346
/// - typing `<` in a path or type position inserts a closing `>` after the path or type.
347-
typing_triggerChars: Option<String> = Some("=.".to_owned()),
347+
typing_triggerChars: Option<String> = Some("=.+".to_owned()),
348348

349349

350350
/// Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`].

crates/rust-analyzer/src/lsp/capabilities.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ pub fn server_capabilities(config: &Config) -> ServerCapabilities {
7777
_ => Some(OneOf::Left(false)),
7878
},
7979
document_on_type_formatting_provider: Some({
80-
let mut chars = ide::Analysis::SUPPORTED_TRIGGER_CHARS.chars();
80+
let mut chars = ide::Analysis::SUPPORTED_TRIGGER_CHARS.iter();
8181
DocumentOnTypeFormattingOptions {
8282
first_trigger_character: chars.next().unwrap().to_string(),
8383
more_trigger_character: Some(chars.map(|c| c.to_string()).collect()),

docs/book/src/configuration_generated.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1410,7 +1410,7 @@ Show documentation.
14101410

14111411
## rust-analyzer.typing.triggerChars {#typing.triggerChars}
14121412

1413-
Default: `"=."`
1413+
Default: `"=.+"`
14141414

14151415
Specify the characters allowed to invoke special on typing triggers.
14161416
- typing `=` after `let` tries to smartly add `;` if `=` is followed by an existing expression

editors/code/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2836,7 +2836,7 @@
28362836
"properties": {
28372837
"rust-analyzer.typing.triggerChars": {
28382838
"markdownDescription": "Specify the characters allowed to invoke special on typing triggers.\n- typing `=` after `let` tries to smartly add `;` if `=` is followed by an existing expression\n- typing `=` between two expressions adds `;` when in statement position\n- typing `=` to turn an assignment into an equality comparison removes `;` when in expression position\n- typing `.` in a chain method call auto-indents\n- typing `{` or `(` in front of an expression inserts a closing `}` or `)` after the expression\n- typing `{` in a use item adds a closing `}` in the right place\n- typing `>` to complete a return type `->` will insert a whitespace after it\n- typing `<` in a path or type position inserts a closing `>` after the path or type.",
2839-
"default": "=.",
2839+
"default": "=.+",
28402840
"type": [
28412841
"null",
28422842
"string"

0 commit comments

Comments
 (0)