Add signature help for tuple patterns and expressions
This commit is contained in:
parent
ecb8616870
commit
42450d2511
@ -15,8 +15,9 @@
|
|||||||
use stdx::format_to;
|
use stdx::format_to;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
algo,
|
algo,
|
||||||
ast::{self, HasArgList},
|
ast::{self, AstChildren, HasArgList},
|
||||||
match_ast, AstNode, Direction, SyntaxElementChildren, SyntaxToken, TextRange, TextSize,
|
match_ast, AstNode, Direction, NodeOrToken, SyntaxElementChildren, SyntaxNode, SyntaxToken,
|
||||||
|
TextRange, TextSize, T,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::RootDatabase;
|
use crate::RootDatabase;
|
||||||
@ -116,6 +117,20 @@ pub(crate) fn signature_help(db: &RootDatabase, position: FilePosition) -> Optio
|
|||||||
}
|
}
|
||||||
return signature_help_for_tuple_struct_pat(&sema, tuple_pat, token);
|
return signature_help_for_tuple_struct_pat(&sema, tuple_pat, token);
|
||||||
},
|
},
|
||||||
|
ast::TuplePat(tuple_pat) => {
|
||||||
|
let cursor_outside = tuple_pat.r_paren_token().as_ref() == Some(&token);
|
||||||
|
if cursor_outside {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return signature_help_for_tuple_pat(&sema, tuple_pat, token);
|
||||||
|
},
|
||||||
|
ast::TupleExpr(tuple_expr) => {
|
||||||
|
let cursor_outside = tuple_expr.r_paren_token().as_ref() == Some(&token);
|
||||||
|
if cursor_outside {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return signature_help_for_tuple_expr(&sema, tuple_expr, token);
|
||||||
|
},
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -395,19 +410,16 @@ fn signature_help_for_tuple_struct_pat(
|
|||||||
pat: ast::TupleStructPat,
|
pat: ast::TupleStructPat,
|
||||||
token: SyntaxToken,
|
token: SyntaxToken,
|
||||||
) -> Option<SignatureHelp> {
|
) -> Option<SignatureHelp> {
|
||||||
let rest_pat = pat.fields().find(|it| matches!(it, ast::Pat::RestPat(_)));
|
let path = pat.path()?;
|
||||||
let is_left_of_rest_pat =
|
let path_res = sema.resolve_path(&path)?;
|
||||||
rest_pat.map_or(true, |it| token.text_range().start() < it.syntax().text_range().end());
|
|
||||||
|
|
||||||
let mut res = SignatureHelp {
|
let mut res = SignatureHelp {
|
||||||
doc: None,
|
doc: None,
|
||||||
signature: String::new(),
|
signature: String::new(),
|
||||||
parameters: vec![],
|
parameters: vec![],
|
||||||
active_parameter: None,
|
active_parameter: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let db = sema.db;
|
let db = sema.db;
|
||||||
let path_res = sema.resolve_path(&pat.path()?)?;
|
|
||||||
let fields: Vec<_> = if let PathResolution::Def(ModuleDef::Variant(variant)) = path_res {
|
let fields: Vec<_> = if let PathResolution::Def(ModuleDef::Variant(variant)) = path_res {
|
||||||
let en = variant.parent_enum(db);
|
let en = variant.parent_enum(db);
|
||||||
|
|
||||||
@ -435,30 +447,72 @@ fn signature_help_for_tuple_struct_pat(
|
|||||||
_ => return None,
|
_ => return None,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let commas = pat
|
Some(signature_help_for_tuple_pat_ish(
|
||||||
.syntax()
|
db,
|
||||||
.children_with_tokens()
|
res,
|
||||||
.filter_map(syntax::NodeOrToken::into_token)
|
pat.syntax(),
|
||||||
.filter(|t| t.kind() == syntax::T![,]);
|
token,
|
||||||
res.active_parameter = Some(if is_left_of_rest_pat {
|
pat.fields(),
|
||||||
commas.take_while(|t| t.text_range().start() <= token.text_range().start()).count()
|
fields.into_iter().map(|it| it.ty(db)),
|
||||||
} else {
|
))
|
||||||
let n_commas = commas
|
}
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.into_iter()
|
|
||||||
.rev()
|
|
||||||
.take_while(|t| t.text_range().start() > token.text_range().start())
|
|
||||||
.count();
|
|
||||||
fields.len().saturating_sub(1).saturating_sub(n_commas)
|
|
||||||
});
|
|
||||||
|
|
||||||
|
fn signature_help_for_tuple_pat(
|
||||||
|
sema: &Semantics<'_, RootDatabase>,
|
||||||
|
pat: ast::TuplePat,
|
||||||
|
token: SyntaxToken,
|
||||||
|
) -> Option<SignatureHelp> {
|
||||||
|
let db = sema.db;
|
||||||
|
let field_pats = pat.fields();
|
||||||
|
let pat = pat.into();
|
||||||
|
let ty = sema.type_of_pat(&pat)?;
|
||||||
|
let fields = ty.original.tuple_fields(db);
|
||||||
|
|
||||||
|
Some(signature_help_for_tuple_pat_ish(
|
||||||
|
db,
|
||||||
|
SignatureHelp {
|
||||||
|
doc: None,
|
||||||
|
signature: String::from('('),
|
||||||
|
parameters: vec![],
|
||||||
|
active_parameter: None,
|
||||||
|
},
|
||||||
|
pat.syntax(),
|
||||||
|
token,
|
||||||
|
field_pats,
|
||||||
|
fields.into_iter(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature_help_for_tuple_expr(
|
||||||
|
sema: &Semantics<'_, RootDatabase>,
|
||||||
|
expr: ast::TupleExpr,
|
||||||
|
token: SyntaxToken,
|
||||||
|
) -> Option<SignatureHelp> {
|
||||||
|
let active_parameter = Some(
|
||||||
|
expr.syntax()
|
||||||
|
.children_with_tokens()
|
||||||
|
.filter_map(NodeOrToken::into_token)
|
||||||
|
.filter(|t| t.kind() == T![,])
|
||||||
|
.take_while(|t| t.text_range().start() <= token.text_range().start())
|
||||||
|
.count(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let db = sema.db;
|
||||||
|
let mut res = SignatureHelp {
|
||||||
|
doc: None,
|
||||||
|
signature: String::from('('),
|
||||||
|
parameters: vec![],
|
||||||
|
active_parameter,
|
||||||
|
};
|
||||||
|
let expr = sema.type_of_expr(&expr.into())?;
|
||||||
|
let fields = expr.original.tuple_fields(db);
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
for ty in fields.into_iter().map(|it| it.ty(db)) {
|
for ty in fields {
|
||||||
format_to!(buf, "{}", ty.display_truncated(db, Some(20)));
|
format_to!(buf, "{}", ty.display_truncated(db, Some(20)));
|
||||||
res.push_call_param(&buf);
|
res.push_call_param(&buf);
|
||||||
buf.clear();
|
buf.clear();
|
||||||
}
|
}
|
||||||
res.signature.push_str(")");
|
res.signature.push(')');
|
||||||
Some(res)
|
Some(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -470,8 +524,8 @@ fn signature_help_for_record_(
|
|||||||
token: SyntaxToken,
|
token: SyntaxToken,
|
||||||
) -> Option<SignatureHelp> {
|
) -> Option<SignatureHelp> {
|
||||||
let active_parameter = field_list_children
|
let active_parameter = field_list_children
|
||||||
.filter_map(syntax::NodeOrToken::into_token)
|
.filter_map(NodeOrToken::into_token)
|
||||||
.filter(|t| t.kind() == syntax::T![,])
|
.filter(|t| t.kind() == T![,])
|
||||||
.take_while(|t| t.text_range().start() <= token.text_range().start())
|
.take_while(|t| t.text_range().start() <= token.text_range().start())
|
||||||
.count();
|
.count();
|
||||||
|
|
||||||
@ -542,6 +596,46 @@ fn signature_help_for_record_(
|
|||||||
Some(res)
|
Some(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn signature_help_for_tuple_pat_ish(
|
||||||
|
db: &RootDatabase,
|
||||||
|
mut res: SignatureHelp,
|
||||||
|
pat: &SyntaxNode,
|
||||||
|
token: SyntaxToken,
|
||||||
|
mut field_pats: AstChildren<ast::Pat>,
|
||||||
|
fields: impl ExactSizeIterator<Item = hir::Type>,
|
||||||
|
) -> SignatureHelp {
|
||||||
|
let rest_pat = field_pats.find(|it| matches!(it, ast::Pat::RestPat(_)));
|
||||||
|
let is_left_of_rest_pat =
|
||||||
|
rest_pat.map_or(true, |it| token.text_range().start() < it.syntax().text_range().end());
|
||||||
|
|
||||||
|
let commas = pat
|
||||||
|
.children_with_tokens()
|
||||||
|
.filter_map(NodeOrToken::into_token)
|
||||||
|
.filter(|t| t.kind() == T![,]);
|
||||||
|
|
||||||
|
res.active_parameter = {
|
||||||
|
Some(if is_left_of_rest_pat {
|
||||||
|
commas.take_while(|t| t.text_range().start() <= token.text_range().start()).count()
|
||||||
|
} else {
|
||||||
|
let n_commas = commas
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.into_iter()
|
||||||
|
.rev()
|
||||||
|
.take_while(|t| t.text_range().start() > token.text_range().start())
|
||||||
|
.count();
|
||||||
|
fields.len().saturating_sub(1).saturating_sub(n_commas)
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut buf = String::new();
|
||||||
|
for ty in fields {
|
||||||
|
format_to!(buf, "{}", ty.display_truncated(db, Some(20)));
|
||||||
|
res.push_call_param(&buf);
|
||||||
|
buf.clear();
|
||||||
|
}
|
||||||
|
res.signature.push_str(")");
|
||||||
|
res
|
||||||
|
}
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::iter;
|
use std::iter;
|
||||||
@ -1851,4 +1945,272 @@ fn bar(_: A)
|
|||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tuple_expr_free() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
(0$0, 1, 3);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
(i32, i32, i32)
|
||||||
|
^^^ --- ---
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
($0 1, 3);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
(i32, i32)
|
||||||
|
^^^ ---
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
(1, 3 $0);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
(i32, i32)
|
||||||
|
--- ^^^
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
(1, 3 $0,);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
(i32, i32)
|
||||||
|
--- ^^^
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tuple_expr_expected() {
|
||||||
|
// FIXME: Seems like we discard valuable results in typeck here
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
let _: (&str, u32, u32)= ($0, 1, 3);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![""],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
let _: (&str, u32, u32, u32)= ($0, 1, 3);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![""],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
let _: (&str, u32, u32)= ($0, 1, 3, 5);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![""],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tuple_pat_free() {
|
||||||
|
// FIXME: Seems like we discard valuable results in typeck here
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
let (0$0, 1, 3);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
(i32, i32, i32)
|
||||||
|
^^^ --- ---
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
let ($0 1, 3);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
(i32, i32)
|
||||||
|
^^^ ---
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
let (1, 3 $0);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
(i32, i32)
|
||||||
|
--- ^^^
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
let (1, 3 $0,);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
(i32, i32)
|
||||||
|
--- ^^^
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
let (1, 3 $0, ..);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
(i32, i32)
|
||||||
|
--- ^^^
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
let (1, 3, .., $0);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
// FIXME: This is wrong, this should not mark the last as active
|
||||||
|
expect![[r#"
|
||||||
|
(i32, i32)
|
||||||
|
--- ^^^
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tuple_pat_expected() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
let (0$0, 1, 3): (i32, i32, i32);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
(i32, i32, i32)
|
||||||
|
^^^ --- ---
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
let ($0 1, 3): (i32, i32, i32);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
// FIXME: tuple pat should be of size 3 ideally
|
||||||
|
expect![[r#"
|
||||||
|
(i32, i32)
|
||||||
|
^^^ ---
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
let (1, 3 $0): (i32,);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
(i32, i32)
|
||||||
|
--- ^^^
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
let (1, 3 $0, ..): (i32, i32, i32, i32);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
(i32, i32, i32, i32)
|
||||||
|
--- ^^^ --- ---
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
let (1, 3, .., $0): (i32, i32, i32);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
(i32, i32, i32)
|
||||||
|
--- --- ^^^
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_tuple_pat_expected_inferred() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
let (0$0, 1, 3) = (1, 2 ,3);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
(i32, i32, i32)
|
||||||
|
^^^ --- ---
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
let ($0 1, 3) = (1, 2, 3);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
// FIXME: tuple pat should be of size 3 ideally
|
||||||
|
expect![[r#"
|
||||||
|
(i32, i32)
|
||||||
|
^^^ ---
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
let (1, 3 $0) = (1,);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
(i32, i32)
|
||||||
|
--- ^^^
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
let (1, 3 $0, ..) = (1, 2, 3, 4);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
(i32, i32, i32, i32)
|
||||||
|
--- ^^^ --- ---
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
let (1, 3, .., $0) = (1, 2, 3);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
(i32, i32, i32)
|
||||||
|
--- --- ^^^
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user