Skip to content

Commit b33495d

Browse files
authored
feat: allow inserting LSP inlay type hints (#5620)
# Description ## Problem Resolves #5527 ## Summary Makes type hints insertable, though not all of them can be inserted. For example a for variable can't have a type annotation, and a struct member pattern can't either. I also added a test for when the type hint is shown for a struct member pattern, which was missing (mainly to assert that the type there isn't insertable). https://github.com/user-attachments/assets/b3a02f2b-be82-49b5-9ac5-cebf8cb83214 ## Additional Context None. ## Documentation Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings.
1 parent b3c408b commit b33495d

2 files changed

Lines changed: 78 additions & 13 deletions

File tree

tooling/lsp/src/requests/inlay_hint.rs

Lines changed: 75 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ use std::future::{self, Future};
44
use async_lsp::ResponseError;
55
use fm::{FileId, FileMap, PathString};
66
use lsp_types::{
7-
InlayHint, InlayHintKind, InlayHintLabel, InlayHintLabelPart, InlayHintParams, Position,
8-
TextDocumentPositionParams,
7+
InlayHint, InlayHintKind, InlayHintLabel, InlayHintLabelPart, InlayHintParams, Position, Range,
8+
TextDocumentPositionParams, TextEdit,
99
};
1010
use noirc_errors::{Location, Span};
1111
use noirc_frontend::{
@@ -173,7 +173,7 @@ impl<'a> InlayHintCollector<'a> {
173173
self.collect_in_expression(&assign_statement.expression);
174174
}
175175
StatementKind::For(for_loop_statement) => {
176-
self.collect_in_ident(&for_loop_statement.identifier);
176+
self.collect_in_ident(&for_loop_statement.identifier, false);
177177
self.collect_in_expression(&for_loop_statement.block);
178178
}
179179
StatementKind::Comptime(statement) => self.collect_in_statement(statement),
@@ -276,7 +276,7 @@ impl<'a> InlayHintCollector<'a> {
276276

277277
match pattern {
278278
Pattern::Identifier(ident) => {
279-
self.collect_in_ident(ident);
279+
self.collect_in_ident(ident, true);
280280
}
281281
Pattern::Mutable(pattern, _span, _is_synthesized) => {
282282
self.collect_in_pattern(pattern);
@@ -294,7 +294,7 @@ impl<'a> InlayHintCollector<'a> {
294294
}
295295
}
296296

297-
fn collect_in_ident(&mut self, ident: &Ident) {
297+
fn collect_in_ident(&mut self, ident: &Ident, editable: bool) {
298298
if !self.options.type_hints.enabled {
299299
return;
300300
}
@@ -308,17 +308,17 @@ impl<'a> InlayHintCollector<'a> {
308308
let global_info = self.interner.get_global(global_id);
309309
let definition_id = global_info.definition_id;
310310
let typ = self.interner.definition_type(definition_id);
311-
self.push_type_hint(lsp_location, &typ);
311+
self.push_type_hint(lsp_location, &typ, editable);
312312
}
313313
ReferenceId::Local(definition_id) => {
314314
let typ = self.interner.definition_type(definition_id);
315-
self.push_type_hint(lsp_location, &typ);
315+
self.push_type_hint(lsp_location, &typ, editable);
316316
}
317317
ReferenceId::StructMember(struct_id, field_index) => {
318318
let struct_type = self.interner.get_struct(struct_id);
319319
let struct_type = struct_type.borrow();
320320
let (_field_name, field_type) = struct_type.field_at(field_index);
321-
self.push_type_hint(lsp_location, field_type);
321+
self.push_type_hint(lsp_location, field_type, false);
322322
}
323323
ReferenceId::Module(_)
324324
| ReferenceId::Struct(_)
@@ -331,7 +331,7 @@ impl<'a> InlayHintCollector<'a> {
331331
}
332332
}
333333

334-
fn push_type_hint(&mut self, location: lsp_types::Location, typ: &Type) {
334+
fn push_type_hint(&mut self, location: lsp_types::Location, typ: &Type, editable: bool) {
335335
let position = location.range.end;
336336

337337
let mut parts = Vec::new();
@@ -342,7 +342,14 @@ impl<'a> InlayHintCollector<'a> {
342342
position,
343343
label: InlayHintLabel::LabelParts(parts),
344344
kind: Some(InlayHintKind::TYPE),
345-
text_edits: None,
345+
text_edits: if editable {
346+
Some(vec![TextEdit {
347+
range: Range { start: location.range.end, end: location.range.end },
348+
new_text: format!(": {}", typ),
349+
}])
350+
} else {
351+
None
352+
},
346353
tooltip: None,
347354
padding_left: None,
348355
padding_right: None,
@@ -756,8 +763,10 @@ mod inlay_hints_tests {
756763
let inlay_hints = get_inlay_hints(0, 3, type_hints()).await;
757764
assert_eq!(inlay_hints.len(), 1);
758765

766+
let position = Position { line: 1, character: 11 };
767+
759768
let inlay_hint = &inlay_hints[0];
760-
assert_eq!(inlay_hint.position, Position { line: 1, character: 11 });
769+
assert_eq!(inlay_hint.position, position);
761770

762771
if let InlayHintLabel::LabelParts(labels) = &inlay_hint.label {
763772
assert_eq!(labels.len(), 2);
@@ -770,15 +779,25 @@ mod inlay_hints_tests {
770779
} else {
771780
panic!("Expected InlayHintLabel::LabelParts, got {:?}", inlay_hint.label);
772781
}
782+
783+
assert_eq!(
784+
inlay_hint.text_edits,
785+
Some(vec![TextEdit {
786+
range: Range { start: position, end: position },
787+
new_text: ": Field".to_string(),
788+
}])
789+
);
773790
}
774791

775792
#[test]
776793
async fn test_type_inlay_hints_with_location() {
777794
let inlay_hints = get_inlay_hints(12, 15, type_hints()).await;
778795
assert_eq!(inlay_hints.len(), 1);
779796

797+
let position = Position { line: 13, character: 11 };
798+
780799
let inlay_hint = &inlay_hints[0];
781-
assert_eq!(inlay_hint.position, Position { line: 13, character: 11 });
800+
assert_eq!(inlay_hint.position, position);
782801

783802
if let InlayHintLabel::LabelParts(labels) = &inlay_hint.label {
784803
assert_eq!(labels.len(), 2);
@@ -798,6 +817,34 @@ mod inlay_hints_tests {
798817
} else {
799818
panic!("Expected InlayHintLabel::LabelParts, got {:?}", inlay_hint.label);
800819
}
820+
821+
assert_eq!(
822+
inlay_hint.text_edits,
823+
Some(vec![TextEdit {
824+
range: Range { start: position, end: position },
825+
new_text: ": Foo".to_string(),
826+
}])
827+
);
828+
}
829+
830+
#[test]
831+
async fn test_type_inlay_hints_in_struct_member_pattern() {
832+
let inlay_hints = get_inlay_hints(94, 96, type_hints()).await;
833+
assert_eq!(inlay_hints.len(), 1);
834+
835+
let inlay_hint = &inlay_hints[0];
836+
assert_eq!(inlay_hint.position, Position { line: 95, character: 24 });
837+
838+
if let InlayHintLabel::LabelParts(labels) = &inlay_hint.label {
839+
assert_eq!(labels.len(), 2);
840+
assert_eq!(labels[0].value, ": ");
841+
assert_eq!(labels[0].location, None);
842+
assert_eq!(labels[1].value, "i32");
843+
} else {
844+
panic!("Expected InlayHintLabel::LabelParts, got {:?}", inlay_hint.label);
845+
}
846+
847+
assert_eq!(inlay_hint.text_edits, None);
801848
}
802849

803850
#[test]
@@ -816,15 +863,19 @@ mod inlay_hints_tests {
816863
} else {
817864
panic!("Expected InlayHintLabel::LabelParts, got {:?}", inlay_hint.label);
818865
}
866+
867+
assert_eq!(inlay_hint.text_edits, None);
819868
}
820869

821870
#[test]
822871
async fn test_type_inlay_hints_in_global() {
823872
let inlay_hints = get_inlay_hints(19, 21, type_hints()).await;
824873
assert_eq!(inlay_hints.len(), 1);
825874

875+
let position = Position { line: 20, character: 10 };
876+
826877
let inlay_hint = &inlay_hints[0];
827-
assert_eq!(inlay_hint.position, Position { line: 20, character: 10 });
878+
assert_eq!(inlay_hint.position, position);
828879

829880
if let InlayHintLabel::LabelParts(labels) = &inlay_hint.label {
830881
assert_eq!(labels.len(), 2);
@@ -834,6 +885,14 @@ mod inlay_hints_tests {
834885
} else {
835886
panic!("Expected InlayHintLabel::LabelParts, got {:?}", inlay_hint.label);
836887
}
888+
889+
assert_eq!(
890+
inlay_hint.text_edits,
891+
Some(vec![TextEdit {
892+
range: Range { start: position, end: position },
893+
new_text: ": Field".to_string(),
894+
}])
895+
);
837896
}
838897

839898
#[test]
@@ -855,6 +914,7 @@ mod inlay_hints_tests {
855914

856915
let inlay_hint = &inlay_hints[0];
857916
assert_eq!(inlay_hint.position, Position { line: 25, character: 12 });
917+
assert_eq!(inlay_hint.text_edits, None);
858918
if let InlayHintLabel::String(label) = &inlay_hint.label {
859919
assert_eq!(label, "one: ");
860920
} else {
@@ -863,6 +923,7 @@ mod inlay_hints_tests {
863923

864924
let inlay_hint = &inlay_hints[1];
865925
assert_eq!(inlay_hint.position, Position { line: 25, character: 15 });
926+
assert_eq!(inlay_hint.text_edits, None);
866927
if let InlayHintLabel::String(label) = &inlay_hint.label {
867928
assert_eq!(label, "two: ");
868929
} else {
@@ -877,6 +938,7 @@ mod inlay_hints_tests {
877938

878939
let inlay_hint = &inlay_hints[0];
879940
assert_eq!(inlay_hint.position, Position { line: 38, character: 18 });
941+
assert_eq!(inlay_hint.text_edits, None);
880942
if let InlayHintLabel::String(label) = &inlay_hint.label {
881943
assert_eq!(label, "one: ");
882944
} else {

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,3 +92,6 @@ fn call_yet_another_function() {
9292
yet_another_function(some_name) // Should not show parameter names ("name" is a suffix of "some_name")
9393
}
9494

95+
fn struct_member_hint() {
96+
let SomeStruct { one } = SomeStruct { one: 1 };
97+
}

0 commit comments

Comments
 (0)