Skip to content

Commit 4bb5df0

Browse files
author
Jonas Schievink
committed
Avoid signature help inside multiline expressions
Fixes #11768
1 parent c9cefb9 commit 4bb5df0

File tree

2 files changed

+88
-11
lines changed

2 files changed

+88
-11
lines changed

crates/ide/src/signature_help.rs

Lines changed: 79 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,15 @@
44
use either::Either;
55
use hir::{HasAttrs, HirDisplay, Semantics};
66
use ide_db::{
7-
active_parameter::{callable_for_token, generics_for_token},
7+
active_parameter::{callable_for_node, generics_for_token},
88
base_db::FilePosition,
99
};
1010
use stdx::format_to;
11-
use syntax::{algo, AstNode, Direction, TextRange, TextSize};
11+
use syntax::{
12+
algo,
13+
ast::{self, HasArgList},
14+
AstNode, Direction, SyntaxToken, TextRange, TextSize,
15+
};
1216

1317
use crate::RootDatabase;
1418

@@ -65,8 +69,8 @@ pub(crate) fn signature_help(db: &RootDatabase, position: FilePosition) -> Optio
6569
.and_then(|tok| algo::skip_trivia_token(tok, Direction::Prev))?;
6670
let token = sema.descend_into_macros_single(token);
6771

68-
if let Some((callable, active_parameter)) = callable_for_token(&sema, token.clone()) {
69-
return Some(signature_help_for_callable(db, callable, active_parameter));
72+
if let Some(help) = signature_help_for_call(&sema, &token) {
73+
return Some(help);
7074
}
7175

7276
if let Some((generic_def, active_parameter)) = generics_for_token(&sema, token.clone()) {
@@ -76,14 +80,39 @@ pub(crate) fn signature_help(db: &RootDatabase, position: FilePosition) -> Optio
7680
None
7781
}
7882

79-
fn signature_help_for_callable(
80-
db: &RootDatabase,
81-
callable: hir::Callable,
82-
active_parameter: Option<usize>,
83-
) -> SignatureHelp {
83+
fn signature_help_for_call(
84+
sema: &Semantics<RootDatabase>,
85+
token: &SyntaxToken,
86+
) -> Option<SignatureHelp> {
87+
// Find the calling expression and its NameRef
88+
let mut node = token.parent()?;
89+
let calling_node = loop {
90+
if let Some(callable) = ast::CallableExpr::cast(node.clone()) {
91+
if callable
92+
.arg_list()
93+
.map_or(false, |it| it.syntax().text_range().contains(token.text_range().start()))
94+
{
95+
break callable;
96+
}
97+
}
98+
99+
// Stop at multi-line expressions, since the signature of the outer call is not very
100+
// helpful inside them.
101+
if let Some(expr) = ast::Expr::cast(node.clone()) {
102+
if expr.syntax().text().contains_char('\n') {
103+
return None;
104+
}
105+
}
106+
107+
node = node.parent()?;
108+
};
109+
110+
let (callable, active_parameter) = callable_for_node(sema, &calling_node, token)?;
111+
84112
let mut res =
85113
SignatureHelp { doc: None, signature: String::new(), parameters: vec![], active_parameter };
86114

115+
let db = sema.db;
87116
match callable.kind() {
88117
hir::CallableKind::Function(func) => {
89118
res.doc = func.docs(db).map(|it| it.into());
@@ -134,7 +163,7 @@ fn signature_help_for_callable(
134163
}
135164
hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_) => {}
136165
}
137-
res
166+
Some(res)
138167
}
139168

140169
fn signature_help_for_generics(
@@ -786,6 +815,46 @@ fn main() {
786815
)
787816
}
788817

818+
#[test]
819+
fn test_multiline_argument() {
820+
check(
821+
r#"
822+
fn callee(a: u8, b: u8) {}
823+
fn main() {
824+
callee(match 0 {
825+
0 => 1,$0
826+
})
827+
}"#,
828+
expect![[r#""#]],
829+
);
830+
check(
831+
r#"
832+
fn callee(a: u8, b: u8) {}
833+
fn main() {
834+
callee(match 0 {
835+
0 => 1,
836+
},$0)
837+
}"#,
838+
expect![[r#"
839+
fn callee(a: u8, b: u8)
840+
----- ^^^^^
841+
"#]],
842+
);
843+
check(
844+
r#"
845+
fn callee(a: u8, b: u8) {}
846+
fn main() {
847+
callee($0match 0 {
848+
0 => 1,
849+
})
850+
}"#,
851+
expect![[r#"
852+
fn callee(a: u8, b: u8)
853+
^^^^^ -----
854+
"#]],
855+
);
856+
}
857+
789858
#[test]
790859
fn test_generics_simple() {
791860
check(

crates/ide_db/src/active_parameter.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,21 @@ pub fn callable_for_token(
4343
sema: &Semantics<RootDatabase>,
4444
token: SyntaxToken,
4545
) -> Option<(hir::Callable, Option<usize>)> {
46-
// Find the calling expression and it's NameRef
46+
// Find the calling expression and its NameRef
4747
let parent = token.parent()?;
4848
let calling_node = parent.ancestors().filter_map(ast::CallableExpr::cast).find(|it| {
4949
it.arg_list()
5050
.map_or(false, |it| it.syntax().text_range().contains(token.text_range().start()))
5151
})?;
5252

53+
callable_for_node(sema, &calling_node, &token)
54+
}
55+
56+
pub fn callable_for_node(
57+
sema: &Semantics<RootDatabase>,
58+
calling_node: &ast::CallableExpr,
59+
token: &SyntaxToken,
60+
) -> Option<(hir::Callable, Option<usize>)> {
5361
let callable = match &calling_node {
5462
ast::CallableExpr::Call(call) => {
5563
let expr = call.expr()?;

0 commit comments

Comments
 (0)