Skip to content

Commit 49646b7

Browse files
bors[bot]SomeoneToIgnorelnicola
authored
Merge #11445
11445: Upstream inlay hints r=lnicola a=lnicola Closes #2797 Closes #3394 (since now resolve the hints for the range given only, not for the whole document. We don't actually resolve anything due to [hard requirement](#11445 (comment)) on label being immutable. Any further heavy actions could go to the `resolve` method that's now available via the official Code API for hints) Based on `@SomeoneToIgnore's` branch, with a couple of updates: - I squashed, more or less successfully, the commits on that branch - downloading the `.d.ts` no longer works, but you can get it manually from https://raw.githubusercontent.com/microsoft/vscode/release/1.64/src/vscode-dts/vscode.proposed.inlayHints.d.ts - you might need to pass `--enable-proposed-api matklad.rust-analyzer` - if I'm reading the definition right, `InlayHintKind` needs to be serialized as a number, not string - this doesn't work anyway -- the client-side gets the hints, but they don't display Co-authored-by: Kirill Bulatov <[email protected]> Co-authored-by: Laurențiu Nicola <[email protected]>
2 parents 18d0faf + 88a2141 commit 49646b7

File tree

13 files changed

+555
-643
lines changed

13 files changed

+555
-643
lines changed

crates/ide/src/inlay_hints.rs

Lines changed: 95 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use itertools::Itertools;
55
use stdx::to_lower_snake_case;
66
use syntax::{
77
ast::{self, AstNode, HasArgList, HasName, UnaryOp},
8-
match_ast, Direction, NodeOrToken, SmolStr, SyntaxKind, TextRange, T,
8+
match_ast, Direction, NodeOrToken, SmolStr, SyntaxKind, SyntaxNode, TextRange, T,
99
};
1010

1111
use crate::FileId;
@@ -58,32 +58,58 @@ pub struct InlayHint {
5858
pub(crate) fn inlay_hints(
5959
db: &RootDatabase,
6060
file_id: FileId,
61+
range_limit: Option<FileRange>,
6162
config: &InlayHintsConfig,
6263
) -> Vec<InlayHint> {
6364
let _p = profile::span("inlay_hints");
6465
let sema = Semantics::new(db);
6566
let file = sema.parse(file_id);
6667
let file = file.syntax();
6768

68-
let mut res = Vec::new();
69-
70-
for node in file.descendants() {
71-
if let Some(expr) = ast::Expr::cast(node.clone()) {
72-
get_chaining_hints(&mut res, &sema, config, &expr);
73-
match expr {
74-
ast::Expr::CallExpr(it) => {
75-
get_param_name_hints(&mut res, &sema, config, ast::Expr::from(it));
76-
}
77-
ast::Expr::MethodCallExpr(it) => {
78-
get_param_name_hints(&mut res, &sema, config, ast::Expr::from(it));
69+
let mut hints = Vec::new();
70+
71+
if let Some(range_limit) = range_limit {
72+
let range_limit = range_limit.range;
73+
match file.covering_element(range_limit) {
74+
NodeOrToken::Token(_) => return hints,
75+
NodeOrToken::Node(n) => {
76+
for node in n
77+
.descendants()
78+
.filter(|descendant| range_limit.contains_range(descendant.text_range()))
79+
{
80+
get_hints(&mut hints, &sema, config, node);
7981
}
80-
_ => (),
8182
}
82-
} else if let Some(it) = ast::IdentPat::cast(node.clone()) {
83-
get_bind_pat_hints(&mut res, &sema, config, &it);
8483
}
84+
} else {
85+
for node in file.descendants() {
86+
get_hints(&mut hints, &sema, config, node);
87+
}
88+
}
89+
90+
hints
91+
}
92+
93+
fn get_hints(
94+
hints: &mut Vec<InlayHint>,
95+
sema: &Semantics<RootDatabase>,
96+
config: &InlayHintsConfig,
97+
node: SyntaxNode,
98+
) {
99+
if let Some(expr) = ast::Expr::cast(node.clone()) {
100+
get_chaining_hints(hints, sema, config, &expr);
101+
match expr {
102+
ast::Expr::CallExpr(it) => {
103+
get_param_name_hints(hints, sema, config, ast::Expr::from(it));
104+
}
105+
ast::Expr::MethodCallExpr(it) => {
106+
get_param_name_hints(hints, sema, config, ast::Expr::from(it));
107+
}
108+
_ => (),
109+
}
110+
} else if let Some(it) = ast::IdentPat::cast(node) {
111+
get_bind_pat_hints(hints, sema, config, &it);
85112
}
86-
res
87113
}
88114

89115
fn get_chaining_hints(
@@ -541,6 +567,8 @@ fn get_callable(
541567
#[cfg(test)]
542568
mod tests {
543569
use expect_test::{expect, Expect};
570+
use ide_db::base_db::FileRange;
571+
use syntax::{TextRange, TextSize};
544572
use test_utils::extract_annotations;
545573

546574
use crate::{fixture, inlay_hints::InlayHintsConfig};
@@ -604,7 +632,7 @@ mod tests {
604632
fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) {
605633
let (analysis, file_id) = fixture::file(ra_fixture);
606634
let expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
607-
let inlay_hints = analysis.inlay_hints(&config, file_id).unwrap();
635+
let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap();
608636
let actual =
609637
inlay_hints.into_iter().map(|it| (it.range, it.label.to_string())).collect::<Vec<_>>();
610638
assert_eq!(expected, actual, "\nExpected:\n{:#?}\n\nActual:\n{:#?}", expected, actual);
@@ -613,7 +641,7 @@ mod tests {
613641
#[track_caller]
614642
fn check_expect(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) {
615643
let (analysis, file_id) = fixture::file(ra_fixture);
616-
let inlay_hints = analysis.inlay_hints(&config, file_id).unwrap();
644+
let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap();
617645
expect.assert_debug_eq(&inlay_hints)
618646
}
619647

@@ -1045,6 +1073,55 @@ fn main() {
10451073
)
10461074
}
10471075

1076+
#[test]
1077+
fn check_hint_range_limit() {
1078+
let fixture = r#"
1079+
//- minicore: fn, sized
1080+
fn foo() -> impl Fn() { loop {} }
1081+
fn foo1() -> impl Fn(f64) { loop {} }
1082+
fn foo2() -> impl Fn(f64, f64) { loop {} }
1083+
fn foo3() -> impl Fn(f64, f64) -> u32 { loop {} }
1084+
fn foo4() -> &'static dyn Fn(f64, f64) -> u32 { loop {} }
1085+
fn foo5() -> &'static dyn Fn(&'static dyn Fn(f64, f64) -> u32, f64) -> u32 { loop {} }
1086+
fn foo6() -> impl Fn(f64, f64) -> u32 + Sized { loop {} }
1087+
fn foo7() -> *const (impl Fn(f64, f64) -> u32 + Sized) { loop {} }
1088+
1089+
fn main() {
1090+
let foo = foo();
1091+
let foo = foo1();
1092+
let foo = foo2();
1093+
let foo = foo3();
1094+
// ^^^ impl Fn(f64, f64) -> u32
1095+
let foo = foo4();
1096+
// ^^^ &dyn Fn(f64, f64) -> u32
1097+
let foo = foo5();
1098+
let foo = foo6();
1099+
let foo = foo7();
1100+
}
1101+
"#;
1102+
let (analysis, file_id) = fixture::file(fixture);
1103+
let expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
1104+
let inlay_hints = analysis
1105+
.inlay_hints(
1106+
&InlayHintsConfig {
1107+
parameter_hints: false,
1108+
type_hints: true,
1109+
chaining_hints: false,
1110+
hide_named_constructor_hints: false,
1111+
max_length: None,
1112+
},
1113+
file_id,
1114+
Some(FileRange {
1115+
file_id,
1116+
range: TextRange::new(TextSize::from(500), TextSize::from(600)),
1117+
}),
1118+
)
1119+
.unwrap();
1120+
let actual =
1121+
inlay_hints.into_iter().map(|it| (it.range, it.label.to_string())).collect::<Vec<_>>();
1122+
assert_eq!(expected, actual, "\nExpected:\n{:#?}\n\nActual:\n{:#?}", expected, actual);
1123+
}
1124+
10481125
#[test]
10491126
fn fn_hints_ptr_rpit_fn_parentheses() {
10501127
check_types(

crates/ide/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,8 +358,9 @@ impl Analysis {
358358
&self,
359359
config: &InlayHintsConfig,
360360
file_id: FileId,
361+
range: Option<FileRange>,
361362
) -> Cancellable<Vec<InlayHint>> {
362-
self.with_db(|db| inlay_hints::inlay_hints(db, file_id, config))
363+
self.with_db(|db| inlay_hints::inlay_hints(db, file_id, range, config))
363364
}
364365

365366
/// Returns the set of folding ranges.

crates/ide/src/static_index.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ impl StaticIndex<'_> {
112112
max_length: Some(25),
113113
},
114114
file_id,
115+
None,
115116
)
116117
.unwrap();
117118
// hovers

crates/rust-analyzer/src/caps.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ pub fn server_capabilities(config: &Config) -> ServerCapabilities {
115115
experimental: Some(json!({
116116
"externalDocs": true,
117117
"hoverRange": true,
118+
"inlayHints": true,
118119
"joinLines": true,
119120
"matchingBrace": true,
120121
"moveItem": true,

crates/rust-analyzer/src/handlers.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1318,11 +1318,22 @@ pub(crate) fn handle_inlay_hints(
13181318
params: InlayHintsParams,
13191319
) -> Result<Vec<InlayHint>> {
13201320
let _p = profile::span("handle_inlay_hints");
1321-
let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
1321+
let document_uri = &params.text_document.uri;
1322+
let file_id = from_proto::file_id(&snap, document_uri)?;
13221323
let line_index = snap.file_line_index(file_id)?;
1324+
let range = params
1325+
.range
1326+
.map(|range| {
1327+
from_proto::file_range(
1328+
&snap,
1329+
TextDocumentIdentifier::new(document_uri.to_owned()),
1330+
range,
1331+
)
1332+
})
1333+
.transpose()?;
13231334
Ok(snap
13241335
.analysis
1325-
.inlay_hints(&snap.config.inlay_hints(), file_id)?
1336+
.inlay_hints(&snap.config.inlay_hints(), file_id, range)?
13261337
.into_iter()
13271338
.map(|it| to_proto::inlay_hint(&line_index, it))
13281339
.collect())

crates/rust-analyzer/src/lsp_ext.rs

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -233,27 +233,34 @@ pub enum InlayHints {}
233233
impl Request for InlayHints {
234234
type Params = InlayHintsParams;
235235
type Result = Vec<InlayHint>;
236-
const METHOD: &'static str = "rust-analyzer/inlayHints";
236+
const METHOD: &'static str = "experimental/inlayHints";
237237
}
238238

239239
#[derive(Serialize, Deserialize, Debug)]
240240
#[serde(rename_all = "camelCase")]
241241
pub struct InlayHintsParams {
242242
pub text_document: TextDocumentIdentifier,
243+
pub range: Option<lsp_types::Range>,
243244
}
244245

245-
#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
246-
pub enum InlayKind {
247-
TypeHint,
248-
ParameterHint,
249-
ChainingHint,
246+
#[derive(Eq, PartialEq, Debug, Copy, Clone, Serialize, Deserialize)]
247+
#[serde(transparent)]
248+
pub struct InlayHintKind(u8);
249+
250+
impl InlayHintKind {
251+
pub const TYPE: InlayHintKind = InlayHintKind(1);
252+
pub const PARAMETER: InlayHintKind = InlayHintKind(2);
250253
}
251254

252255
#[derive(Debug, Deserialize, Serialize)]
256+
#[serde(rename_all = "camelCase")]
253257
pub struct InlayHint {
254-
pub range: Range,
255-
pub kind: InlayKind,
256258
pub label: String,
259+
pub position: Position,
260+
pub kind: Option<InlayHintKind>,
261+
pub tooltip: Option<String>,
262+
pub padding_left: Option<bool>,
263+
pub padding_right: Option<bool>,
257264
}
258265

259266
pub enum Ssr {}

crates/rust-analyzer/src/to_proto.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -416,12 +416,18 @@ pub(crate) fn signature_help(
416416
pub(crate) fn inlay_hint(line_index: &LineIndex, inlay_hint: InlayHint) -> lsp_ext::InlayHint {
417417
lsp_ext::InlayHint {
418418
label: inlay_hint.label.to_string(),
419-
range: range(line_index, inlay_hint.range),
419+
position: match inlay_hint.kind {
420+
InlayKind::ParameterHint => position(line_index, inlay_hint.range.start()),
421+
_ => position(line_index, inlay_hint.range.end()),
422+
},
420423
kind: match inlay_hint.kind {
421-
InlayKind::ParameterHint => lsp_ext::InlayKind::ParameterHint,
422-
InlayKind::TypeHint => lsp_ext::InlayKind::TypeHint,
423-
InlayKind::ChainingHint => lsp_ext::InlayKind::ChainingHint,
424+
InlayKind::ParameterHint => Some(lsp_ext::InlayHintKind::PARAMETER),
425+
InlayKind::TypeHint => Some(lsp_ext::InlayHintKind::TYPE),
426+
InlayKind::ChainingHint => None,
424427
},
428+
tooltip: None,
429+
padding_left: Some(true),
430+
padding_right: Some(true),
425431
}
426432
}
427433

docs/dev/lsp-extensions.md

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!---
2-
lsp_ext.rs hash: 5b53b92c9f9d6650
2+
lsp_ext.rs hash: e32fdde032ff6ebc
33
44
If you need to change the above hash to make the test pass, please check if you
55
need to adjust this doc as well and ping this issue:
@@ -562,11 +562,11 @@ Expands macro call at a given position.
562562

563563
## Inlay Hints
564564

565-
**Method:** `rust-analyzer/inlayHints`
565+
**Method:** `experimental/inlayHints`
566566

567567
This request is sent from client to server to render "inlay hints" -- virtual text inserted into editor to show things like inferred types.
568568
Generally, the client should re-query inlay hints after every modification.
569-
Note that we plan to move this request to `experimental/inlayHints`, as it is not really Rust-specific, but the current API is not necessary the right one.
569+
Until it gets upstreamed, this follows the VS Code API.
570570
Upstream issues: https://github.com/microsoft/language-server-protocol/issues/956 , https://github.com/rust-analyzer/rust-analyzer/issues/2797
571571

572572
**Request:**
@@ -581,9 +581,12 @@ interface InlayHintsParams {
581581

582582
```typescript
583583
interface InlayHint {
584-
kind: "TypeHint" | "ParameterHint" | "ChainingHint",
585-
range: Range,
586-
label: string,
584+
position: Position;
585+
label: string | InlayHintLabelPart[];
586+
tooltip?: string | MarkdownString | undefined;
587+
kind?: InlayHintKind;
588+
paddingLeft?: boolean;
589+
paddingRight?: boolean;
587590
}
588591
```
589592

editors/code/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@ node_modules
33
server
44
.vscode-test/
55
*.vsix
6+
bundle
7+
vscode.proposed.d.ts

0 commit comments

Comments
 (0)