Skip to content

Commit 1927bda

Browse files
authored
feat: LSP chain inlay hints (#7152)
1 parent 02056d6 commit 1927bda

File tree

3 files changed

+128
-7
lines changed

3 files changed

+128
-7
lines changed

tooling/lsp/src/requests/inlay_hint.rs

+102-7
Original file line numberDiff line numberDiff line change
@@ -83,29 +83,30 @@ impl<'a> InlayHintCollector<'a> {
8383
let location = Location::new(ident.span(), self.file_id);
8484
if let Some(lsp_location) = to_lsp_location(self.files, self.file_id, span) {
8585
if let Some(referenced) = self.interner.find_referenced(location) {
86+
let include_colon = true;
8687
match referenced {
8788
ReferenceId::Global(global_id) => {
8889
let global_info = self.interner.get_global(global_id);
8990
let definition_id = global_info.definition_id;
9091
let typ = self.interner.definition_type(definition_id);
91-
self.push_type_hint(lsp_location, &typ, editable);
92+
self.push_type_hint(lsp_location, &typ, editable, include_colon);
9293
}
9394
ReferenceId::Local(definition_id) => {
9495
let typ = self.interner.definition_type(definition_id);
95-
self.push_type_hint(lsp_location, &typ, editable);
96+
self.push_type_hint(lsp_location, &typ, editable, include_colon);
9697
}
9798
ReferenceId::StructMember(struct_id, field_index) => {
9899
let struct_type = self.interner.get_type(struct_id);
99100
let struct_type = struct_type.borrow();
100101
let field = struct_type.field_at(field_index);
101-
self.push_type_hint(lsp_location, &field.typ, false);
102+
self.push_type_hint(lsp_location, &field.typ, false, include_colon);
102103
}
103104
ReferenceId::EnumVariant(type_id, variant_index) => {
104105
let typ = self.interner.get_type(type_id);
105106
let shared_type = typ.clone();
106107
let typ = typ.borrow();
107108
let variant_type = typ.variant_function_type(variant_index, shared_type);
108-
self.push_type_hint(lsp_location, &variant_type, false);
109+
self.push_type_hint(lsp_location, &variant_type, false, include_colon);
109110
}
110111
ReferenceId::Module(_)
111112
| ReferenceId::Struct(_)
@@ -119,11 +120,21 @@ impl<'a> InlayHintCollector<'a> {
119120
}
120121
}
121122

122-
fn push_type_hint(&mut self, location: lsp_types::Location, typ: &Type, editable: bool) {
123+
fn push_type_hint(
124+
&mut self,
125+
location: lsp_types::Location,
126+
typ: &Type,
127+
editable: bool,
128+
include_colon: bool,
129+
) {
123130
let position = location.range.end;
124131

125132
let mut parts = Vec::new();
126-
parts.push(string_part(": "));
133+
if include_colon {
134+
parts.push(string_part(": "));
135+
} else {
136+
parts.push(string_part(" "));
137+
}
127138
push_type_parts(typ, &mut parts, self.files);
128139

129140
self.inlay_hints.push(InlayHint {
@@ -217,6 +228,36 @@ impl<'a> InlayHintCollector<'a> {
217228
}
218229
}
219230

231+
fn collect_method_call_chain_hints(&mut self, method: &MethodCallExpression) {
232+
let Some(object_lsp_location) =
233+
to_lsp_location(self.files, self.file_id, method.object.span)
234+
else {
235+
return;
236+
};
237+
238+
let Some(name_lsp_location) =
239+
to_lsp_location(self.files, self.file_id, method.method_name.span())
240+
else {
241+
return;
242+
};
243+
244+
if object_lsp_location.range.end.line >= name_lsp_location.range.start.line {
245+
return;
246+
}
247+
248+
let object_location = Location::new(method.object.span, self.file_id);
249+
let Some(typ) = self.interner.type_at_location(object_location) else {
250+
return;
251+
};
252+
253+
self.push_type_hint(
254+
object_lsp_location,
255+
&typ,
256+
false, // not editable
257+
false, // don't include colon
258+
);
259+
}
260+
220261
fn get_pattern_name(&self, pattern: &HirPattern) -> Option<String> {
221262
match pattern {
222263
HirPattern::Identifier(ident) => {
@@ -357,6 +398,10 @@ impl<'a> Visitor for InlayHintCollector<'a> {
357398
&method_call_expression.arguments,
358399
);
359400

401+
if self.options.chaining_hints.enabled {
402+
self.collect_method_call_chain_hints(method_call_expression);
403+
}
404+
360405
true
361406
}
362407

@@ -548,7 +593,9 @@ fn get_expression_name(expression: &Expression) -> Option<String> {
548593
#[cfg(test)]
549594
mod inlay_hints_tests {
550595
use crate::{
551-
requests::{ClosingBraceHintsOptions, ParameterHintsOptions, TypeHintsOptions},
596+
requests::{
597+
ChainingHintsOptions, ClosingBraceHintsOptions, ParameterHintsOptions, TypeHintsOptions,
598+
},
552599
test_utils,
553600
};
554601

@@ -585,6 +632,7 @@ mod inlay_hints_tests {
585632
type_hints: TypeHintsOptions { enabled: false },
586633
parameter_hints: ParameterHintsOptions { enabled: false },
587634
closing_brace_hints: ClosingBraceHintsOptions { enabled: false, min_lines: 25 },
635+
chaining_hints: ChainingHintsOptions { enabled: false },
588636
}
589637
}
590638

@@ -593,6 +641,7 @@ mod inlay_hints_tests {
593641
type_hints: TypeHintsOptions { enabled: true },
594642
parameter_hints: ParameterHintsOptions { enabled: false },
595643
closing_brace_hints: ClosingBraceHintsOptions { enabled: false, min_lines: 25 },
644+
chaining_hints: ChainingHintsOptions { enabled: false },
596645
}
597646
}
598647

@@ -601,6 +650,7 @@ mod inlay_hints_tests {
601650
type_hints: TypeHintsOptions { enabled: false },
602651
parameter_hints: ParameterHintsOptions { enabled: true },
603652
closing_brace_hints: ClosingBraceHintsOptions { enabled: false, min_lines: 25 },
653+
chaining_hints: ChainingHintsOptions { enabled: false },
604654
}
605655
}
606656

@@ -609,6 +659,16 @@ mod inlay_hints_tests {
609659
type_hints: TypeHintsOptions { enabled: false },
610660
parameter_hints: ParameterHintsOptions { enabled: false },
611661
closing_brace_hints: ClosingBraceHintsOptions { enabled: true, min_lines },
662+
chaining_hints: ChainingHintsOptions { enabled: false },
663+
}
664+
}
665+
666+
fn chaining_hints() -> InlayHintsOptions {
667+
InlayHintsOptions {
668+
type_hints: TypeHintsOptions { enabled: false },
669+
parameter_hints: ParameterHintsOptions { enabled: false },
670+
closing_brace_hints: ClosingBraceHintsOptions { enabled: false, min_lines: 0 },
671+
chaining_hints: ChainingHintsOptions { enabled: true },
612672
}
613673
}
614674

@@ -963,4 +1023,39 @@ mod inlay_hints_tests {
9631023
panic!("Expected InlayHintLabel::String, got {:?}", inlay_hint.label);
9641024
}
9651025
}
1026+
1027+
#[test]
1028+
async fn test_shows_receiver_type_in_multiline_method_call() {
1029+
let mut inlay_hints = get_inlay_hints(125, 130, chaining_hints()).await;
1030+
assert_eq!(inlay_hints.len(), 3);
1031+
1032+
inlay_hints.sort_by_key(|hint| hint.position.line);
1033+
1034+
let inlay_hint = &inlay_hints[0];
1035+
assert_eq!(inlay_hint.position.line, 125);
1036+
assert_eq!(inlay_hint.position.character, 59);
1037+
let InlayHintLabel::LabelParts(parts) = &inlay_hint.label else {
1038+
panic!("Expected label parts");
1039+
};
1040+
let label = parts.iter().map(|part| part.value.clone()).collect::<Vec<_>>().join("");
1041+
assert_eq!(label, " [u32; 14]");
1042+
1043+
let inlay_hint = &inlay_hints[1];
1044+
assert_eq!(inlay_hint.position.line, 126);
1045+
assert_eq!(inlay_hint.position.character, 37);
1046+
let InlayHintLabel::LabelParts(parts) = &inlay_hint.label else {
1047+
panic!("Expected label parts");
1048+
};
1049+
let label = parts.iter().map(|part| part.value.clone()).collect::<Vec<_>>().join("");
1050+
assert_eq!(label, " [u32; 14]");
1051+
1052+
let inlay_hint = &inlay_hints[2];
1053+
assert_eq!(inlay_hint.position.line, 127);
1054+
assert_eq!(inlay_hint.position.character, 23);
1055+
let InlayHintLabel::LabelParts(parts) = &inlay_hint.label else {
1056+
panic!("Expected label parts");
1057+
};
1058+
let label = parts.iter().map(|part| part.value.clone()).collect::<Vec<_>>().join("");
1059+
assert_eq!(label, " bool");
1060+
}
9661061
}

tooling/lsp/src/requests/mod.rs

+18
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@ pub(crate) struct InlayHintsOptions {
9090

9191
#[serde(rename = "closingBraceHints", default = "default_closing_brace_hints")]
9292
pub(crate) closing_brace_hints: ClosingBraceHintsOptions,
93+
94+
#[serde(rename = "ChainingHints", default = "default_chaining_hints")]
95+
pub(crate) chaining_hints: ChainingHintsOptions,
9396
}
9497

9598
#[derive(Debug, Deserialize, Serialize, Copy, Clone)]
@@ -113,6 +116,12 @@ pub(crate) struct ClosingBraceHintsOptions {
113116
pub(crate) min_lines: u32,
114117
}
115118

119+
#[derive(Debug, Deserialize, Serialize, Copy, Clone)]
120+
pub(crate) struct ChainingHintsOptions {
121+
#[serde(rename = "enabled", default = "default_chaining_hints_enabled")]
122+
pub(crate) enabled: bool,
123+
}
124+
116125
fn default_enable_code_lens() -> bool {
117126
true
118127
}
@@ -126,6 +135,7 @@ fn default_inlay_hints() -> InlayHintsOptions {
126135
type_hints: default_type_hints(),
127136
parameter_hints: default_parameter_hints(),
128137
closing_brace_hints: default_closing_brace_hints(),
138+
chaining_hints: default_chaining_hints(),
129139
}
130140
}
131141

@@ -160,6 +170,14 @@ fn default_closing_brace_min_lines() -> u32 {
160170
25
161171
}
162172

173+
fn default_chaining_hints() -> ChainingHintsOptions {
174+
ChainingHintsOptions { enabled: default_chaining_hints_enabled() }
175+
}
176+
177+
fn default_chaining_hints_enabled() -> bool {
178+
true
179+
}
180+
163181
impl Default for LspInitializationOptions {
164182
fn default() -> Self {
165183
Self {

tooling/lsp/test_programs/inlay_hints/src/main.nr

+8
Original file line numberDiff line numberDiff line change
@@ -119,4 +119,12 @@ mod some_module {
119119

120120
contract some_contract {
121121

122+
}}
123+
124+
use std::ops::Not;
125+
pub fn chain() {
126+
let _ = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
127+
.map(|x| x + 123456789012345)
128+
.any(|x| x > 5)
129+
.not();
122130
}

0 commit comments

Comments
 (0)