diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index b43685ffee..c960b88a3e 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -402,6 +402,8 @@ impl Analysis { self.with_db(|db| typing::on_enter(db, position)) } + pub const SUPPORTED_TRIGGER_CHARS: &'static str = typing::TRIGGER_CHARS; + /// Returns an edit which should be applied after a character was typed. /// /// This is useful for some on-the-fly fixups, like adding `;` to `let =` diff --git a/crates/ide/src/typing.rs b/crates/ide/src/typing.rs index cfa1d6ff03..3d9146cc4c 100644 --- a/crates/ide/src/typing.rs +++ b/crates/ide/src/typing.rs @@ -32,7 +32,7 @@ use crate::SourceChange; pub(crate) use on_enter::on_enter; // Don't forget to add new trigger characters to `server_capabilities` in `caps.rs`. -pub(crate) const TRIGGER_CHARS: &str = ".=<>{("; +pub(crate) const TRIGGER_CHARS: &str = ".=<>{(|"; struct ExtendedTextEdit { edit: TextEdit, @@ -99,6 +99,7 @@ fn on_char_typed_( '=' => on_eq_typed(&file.tree(), offset), '>' => on_right_angle_typed(&file.tree(), offset), '{' | '(' | '<' => on_opening_delimiter_typed(file, offset, char_typed, edition), + '|' => on_pipe_typed(&file.tree(), offset), _ => None, } .map(conv) @@ -212,10 +213,6 @@ fn on_delimited_node_typed( // FIXME: use a snippet completion instead of this hack here. fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option { let text = file.syntax().text(); - if !stdx::always!(text.char_at(offset) == Some('=')) { - return None; - } - let has_newline = iter::successors(Some(offset), |&offset| Some(offset + TextSize::new(1))) .filter_map(|offset| text.char_at(offset)) .find(|&c| !c.is_whitespace() || c == '\n') @@ -308,9 +305,6 @@ fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option { /// Returns an edit which should be applied when a dot ('.') is typed on a blank line, indenting the line appropriately. fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option { - if !stdx::always!(file.syntax().text().char_at(offset) == Some('.')) { - return None; - } let whitespace = file.syntax().token_at_offset(offset).left_biased().and_then(ast::Whitespace::cast)?; @@ -380,7 +374,9 @@ fn on_left_angle_typed( if ancestors_at_offset(file.syntax(), offset) .take_while(|n| !ast::Item::can_cast(n.kind())) .any(|n| { - ast::GenericParamList::can_cast(n.kind()) || ast::GenericArgList::can_cast(n.kind()) + ast::GenericParamList::can_cast(n.kind()) + || ast::GenericArgList::can_cast(n.kind()) + || ast::UseBoundGenericArgs::can_cast(n.kind()) }) { // Insert the closing bracket right after @@ -390,12 +386,21 @@ fn on_left_angle_typed( } } +fn on_pipe_typed(file: &SourceFile, offset: TextSize) -> Option { + let pipe_token = file.syntax().token_at_offset(offset).right_biased()?; + if pipe_token.kind() != SyntaxKind::PIPE { + return None; + } + if pipe_token.parent().and_then(ast::ParamList::cast)?.r_paren_token().is_some() { + return None; + } + let after_lpipe = offset + TextSize::of('|'); + Some(TextEdit::insert(after_lpipe, "|".to_owned())) +} + /// Adds a space after an arrow when `fn foo() { ... }` is turned into `fn foo() -> { ... }` fn on_right_angle_typed(file: &SourceFile, offset: TextSize) -> Option { let file_text = file.syntax().text(); - if !stdx::always!(file_text.char_at(offset) == Some('>')) { - return None; - } let after_arrow = offset + TextSize::of('>'); if file_text.char_at(after_arrow) != Some('{') { return None; @@ -1527,6 +1532,44 @@ fn foo() { ) $0 } +"#, + ); + } + + #[test] + fn completes_pipe_param_list() { + type_char( + '|', + r#" +fn foo() { + $0 +} +"#, + r#" +fn foo() { + || +} +"#, + ); + type_char( + '|', + r#" +fn foo() { + $0 a +} +"#, + r#" +fn foo() { + || a +} +"#, + ); + type_char_noop( + '|', + r#" +fn foo() { + let $0 +} "#, ); } diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 392bfbf15f..0fdc48a0e7 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -308,8 +308,8 @@ config_data! { /// Show documentation. 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. - typing_excludeChars: Option = None, + /// Specify the characters to exclude from triggering typing assists. The default trigger characters are `.`, `=`, `<`, `>`, `{`, and `(`. + typing_excludeChars: Option = Some('<'.to_string()), /// Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`]. diff --git a/crates/rust-analyzer/src/lsp/capabilities.rs b/crates/rust-analyzer/src/lsp/capabilities.rs index bd496e8ddc..82e6ae2b49 100644 --- a/crates/rust-analyzer/src/lsp/capabilities.rs +++ b/crates/rust-analyzer/src/lsp/capabilities.rs @@ -72,9 +72,12 @@ pub fn server_capabilities(config: &Config) -> ServerCapabilities { RustfmtConfig::Rustfmt { enable_range_formatting: true, .. } => Some(OneOf::Left(true)), _ => Some(OneOf::Left(false)), }, - document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions { - first_trigger_character: "=".to_owned(), - more_trigger_character: Some(more_trigger_character(config)), + document_on_type_formatting_provider: Some({ + let mut chars = ide::Analysis::SUPPORTED_TRIGGER_CHARS.chars(); + DocumentOnTypeFormattingOptions { + first_trigger_character: chars.next().unwrap().to_string(), + more_trigger_character: Some(chars.map(|c| c.to_string()).collect()), + } }), selection_range_provider: Some(SelectionRangeProviderCapability::Simple(true)), folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)), @@ -528,11 +531,3 @@ impl ClientCapabilities { .unwrap_or_default() } } - -fn more_trigger_character(config: &Config) -> Vec { - let mut res = vec![".".to_owned(), ">".to_owned(), "{".to_owned(), "(".to_owned()]; - if config.snippet_cap().is_some() { - res.push("<".to_owned()); - } - res -} diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index a3172c7ca2..715f8d43ad 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -992,10 +992,10 @@ Show full signature of the callable. Only shows parameters if disabled. -- Show documentation. -- -[[rust-analyzer.typing.excludeChars]]rust-analyzer.typing.excludeChars (default: `null`):: +[[rust-analyzer.typing.excludeChars]]rust-analyzer.typing.excludeChars (default: `"<"`):: + -- -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 `(`. -- [[rust-analyzer.workspace.discoverConfig]]rust-analyzer.workspace.discoverConfig (default: `null`):: + diff --git a/editors/code/package.json b/editors/code/package.json index 68c61e4bf6..70d26c9707 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -2606,8 +2606,8 @@ "title": "typing", "properties": { "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.", - "default": null, + "markdownDescription": "Specify the characters to exclude from triggering typing assists. The default trigger characters are `.`, `=`, `<`, `>`, `{`, and `(`.", + "default": "<", "type": [ "null", "string"