Add typing handler for param list pipe

This commit is contained in:
Lukas Wirth 2024-12-06 15:35:13 +01:00
parent 5dc5107e9c
commit 54dbf1b446
6 changed files with 69 additions and 29 deletions

View file

@ -402,6 +402,8 @@ impl Analysis {
self.with_db(|db| typing::on_enter(db, position)) 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. /// 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 =` /// This is useful for some on-the-fly fixups, like adding `;` to `let =`

View file

@ -32,7 +32,7 @@ use crate::SourceChange;
pub(crate) use on_enter::on_enter; pub(crate) use on_enter::on_enter;
// Don't forget to add new trigger characters to `server_capabilities` in `caps.rs`. // 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 { struct ExtendedTextEdit {
edit: TextEdit, edit: TextEdit,
@ -99,6 +99,7 @@ fn on_char_typed_(
'=' => on_eq_typed(&file.tree(), offset), '=' => on_eq_typed(&file.tree(), offset),
'>' => on_right_angle_typed(&file.tree(), offset), '>' => on_right_angle_typed(&file.tree(), offset),
'{' | '(' | '<' => on_opening_delimiter_typed(file, offset, char_typed, edition), '{' | '(' | '<' => on_opening_delimiter_typed(file, offset, char_typed, edition),
'|' => on_pipe_typed(&file.tree(), offset),
_ => None, _ => None,
} }
.map(conv) .map(conv)
@ -212,10 +213,6 @@ fn on_delimited_node_typed(
// FIXME: use a snippet completion instead of this hack here. // FIXME: use a snippet completion instead of this hack here.
fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> { fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
let text = file.syntax().text(); 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))) let has_newline = iter::successors(Some(offset), |&offset| Some(offset + TextSize::new(1)))
.filter_map(|offset| text.char_at(offset)) .filter_map(|offset| text.char_at(offset))
.find(|&c| !c.is_whitespace() || c == '\n') .find(|&c| !c.is_whitespace() || c == '\n')
@ -308,9 +305,6 @@ fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
/// Returns an edit which should be applied when a dot ('.') is typed on a blank line, indenting the line appropriately. /// 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<TextEdit> { fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
if !stdx::always!(file.syntax().text().char_at(offset) == Some('.')) {
return None;
}
let whitespace = let whitespace =
file.syntax().token_at_offset(offset).left_biased().and_then(ast::Whitespace::cast)?; 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) if ancestors_at_offset(file.syntax(), offset)
.take_while(|n| !ast::Item::can_cast(n.kind())) .take_while(|n| !ast::Item::can_cast(n.kind()))
.any(|n| { .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 // Insert the closing bracket right after
@ -390,12 +386,21 @@ fn on_left_angle_typed(
} }
} }
fn on_pipe_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
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() -> { ... }` /// Adds a space after an arrow when `fn foo() { ... }` is turned into `fn foo() -> { ... }`
fn on_right_angle_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> { fn on_right_angle_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
let file_text = file.syntax().text(); let file_text = file.syntax().text();
if !stdx::always!(file_text.char_at(offset) == Some('>')) {
return None;
}
let after_arrow = offset + TextSize::of('>'); let after_arrow = offset + TextSize::of('>');
if file_text.char_at(after_arrow) != Some('{') { if file_text.char_at(after_arrow) != Some('{') {
return None; return None;
@ -1527,6 +1532,44 @@ fn foo() {
) )
$0 $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
}
"#, "#,
); );
} }

View file

@ -308,8 +308,8 @@ config_data! {
/// Show documentation. /// Show documentation.
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 `(`.
typing_excludeChars: Option<String> = None, typing_excludeChars: Option<String> = Some('<'.to_string()),
/// Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`]. /// Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`].

View file

@ -72,9 +72,12 @@ pub fn server_capabilities(config: &Config) -> ServerCapabilities {
RustfmtConfig::Rustfmt { enable_range_formatting: true, .. } => Some(OneOf::Left(true)), RustfmtConfig::Rustfmt { enable_range_formatting: true, .. } => Some(OneOf::Left(true)),
_ => Some(OneOf::Left(false)), _ => Some(OneOf::Left(false)),
}, },
document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions { document_on_type_formatting_provider: Some({
first_trigger_character: "=".to_owned(), let mut chars = ide::Analysis::SUPPORTED_TRIGGER_CHARS.chars();
more_trigger_character: Some(more_trigger_character(config)), 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)), selection_range_provider: Some(SelectionRangeProviderCapability::Simple(true)),
folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)), folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)),
@ -528,11 +531,3 @@ impl ClientCapabilities {
.unwrap_or_default() .unwrap_or_default()
} }
} }
fn more_trigger_character(config: &Config) -> Vec<String> {
let mut res = vec![".".to_owned(), ">".to_owned(), "{".to_owned(), "(".to_owned()];
if config.snippet_cap().is_some() {
res.push("<".to_owned());
}
res
}

View file

@ -992,10 +992,10 @@ Show full signature of the callable. Only shows parameters if disabled.
-- --
Show documentation. 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`):: [[rust-analyzer.workspace.discoverConfig]]rust-analyzer.workspace.discoverConfig (default: `null`)::
+ +

View file

@ -2606,8 +2606,8 @@
"title": "typing", "title": "typing",
"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 `(`.",
"default": null, "default": "<",
"type": [ "type": [
"null", "null",
"string" "string"