Skip to content

Commit 3268245

Browse files
LS Completions Details (#511)
1 parent 9826391 commit 3268245

File tree

5 files changed

+122
-43
lines changed

5 files changed

+122
-43
lines changed

language_service/src/completion.rs

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#[cfg(test)]
55
mod tests;
66

7+
use crate::display::CodeDisplay;
78
use crate::qsc_utils::{map_offset, span_contains, Compilation};
89
use qsc::ast::visit::{self, Visitor};
910
use qsc::hir::{ItemKind, Package, PackageId};
@@ -32,6 +33,7 @@ pub struct CompletionItem {
3233
pub label: String,
3334
pub kind: CompletionItemKind,
3435
pub sort_text: Option<String>,
36+
pub detail: Option<String>,
3537
}
3638

3739
pub(crate) fn get_completions(
@@ -152,9 +154,20 @@ impl CompletionListBuilder {
152154
.expect("expected to find core package")
153155
.package;
154156

155-
self.push_sorted_completions(Self::get_callables(current), CompletionItemKind::Function);
156-
self.push_sorted_completions(Self::get_callables(std), CompletionItemKind::Function);
157-
self.push_sorted_completions(Self::get_callables(core), CompletionItemKind::Function);
157+
let display = CodeDisplay { compilation };
158+
159+
self.push_sorted_completions(
160+
Self::get_callables(current, &display),
161+
CompletionItemKind::Function,
162+
);
163+
self.push_sorted_completions(
164+
Self::get_callables(std, &display),
165+
CompletionItemKind::Function,
166+
);
167+
self.push_sorted_completions(
168+
Self::get_callables(core, &display),
169+
CompletionItemKind::Function,
170+
);
158171
self.push_completions(Self::get_namespaces(current), CompletionItemKind::Module);
159172
self.push_completions(Self::get_namespaces(std), CompletionItemKind::Module);
160173
self.push_completions(Self::get_namespaces(core), CompletionItemKind::Module);
@@ -179,10 +192,11 @@ impl CompletionListBuilder {
179192
/// in the eventual completion list, the groups of items show up in the
180193
/// order they were added.
181194
/// The items are then sorted according to the input list order (not alphabetical)
182-
fn push_completions<'a, I>(&mut self, iter: I, kind: CompletionItemKind)
183-
where
184-
I: Iterator<Item = &'a str>,
185-
{
195+
fn push_completions<'a>(
196+
&mut self,
197+
iter: impl Iterator<Item = &'a str>,
198+
kind: CompletionItemKind,
199+
) {
186200
let mut current_sort_prefix = 0;
187201

188202
self.items.extend(iter.map(|name| CompletionItem {
@@ -195,36 +209,43 @@ impl CompletionListBuilder {
195209
self.current_sort_group, current_sort_prefix, name
196210
))
197211
},
212+
detail: None,
198213
}));
199214

200215
self.current_sort_group += 1;
201216
}
202217

203218
/// Push a group of completions that are themselves sorted into subgroups
204-
fn push_sorted_completions<'a, I>(&mut self, iter: I, kind: CompletionItemKind)
205-
where
206-
I: Iterator<Item = (&'a str, u32)>,
207-
{
219+
fn push_sorted_completions<'a>(
220+
&mut self,
221+
iter: impl Iterator<Item = (&'a str, Option<String>, u32)>,
222+
kind: CompletionItemKind,
223+
) {
208224
self.items
209-
.extend(iter.map(|(name, item_sort_group)| CompletionItem {
225+
.extend(iter.map(|(name, detail, item_sort_group)| CompletionItem {
210226
label: name.to_string(),
211227
kind,
212228
sort_text: Some(format!(
213229
"{:02}{:02}{}",
214230
self.current_sort_group, item_sort_group, name
215231
)),
232+
detail,
216233
}));
217234

218235
self.current_sort_group += 1;
219236
}
220237

221-
fn get_callables(package: &Package) -> impl Iterator<Item = (&str, u32)> {
238+
fn get_callables<'a>(
239+
package: &'a Package,
240+
display: &'a CodeDisplay,
241+
) -> impl Iterator<Item = (&'a str, Option<String>, u32)> {
222242
package.items.values().filter_map(|i| match &i.kind {
223243
ItemKind::Callable(callable_decl) => Some({
224244
let name = callable_decl.name.name.as_ref();
245+
let detail = Some(display.hir_callable_decl(callable_decl).to_string());
225246
// Everything that starts with a __ goes last in the list
226247
let sort_group = u32::from(name.starts_with("__"));
227-
(name, sort_group)
248+
(name, detail, sort_group)
228249
}),
229250
_ => None,
230251
})

language_service/src/completion/tests.rs

Lines changed: 82 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,118 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4+
use expect_test::{expect, Expect};
5+
46
use super::{get_completions, CompletionItemKind};
57
use crate::test_utils::{compile_with_fake_stdlib, get_source_and_marker_offsets};
68

9+
fn check(source_with_cursor: &str, completions_to_check: &[&str], expect: &Expect) {
10+
let (source, cursor_offset, _) = get_source_and_marker_offsets(source_with_cursor);
11+
let compilation = compile_with_fake_stdlib("<source>", &source);
12+
let actual_completions = get_completions(&compilation, "<source>", cursor_offset[0]);
13+
let checked_completions: Vec<Option<(&String, CompletionItemKind, &Option<String>)>> =
14+
completions_to_check
15+
.iter()
16+
.map(|comp| {
17+
actual_completions.items.iter().find_map(|item| {
18+
if item.label == **comp {
19+
Some((&item.label, item.kind, &item.detail))
20+
} else {
21+
None
22+
}
23+
})
24+
})
25+
.collect();
26+
27+
expect.assert_debug_eq(&checked_completions);
28+
}
29+
730
#[test]
831
fn in_block_contains_std_functions() {
9-
assert_completions_contain(
32+
check(
1033
r#"
1134
namespace Test {
1235
operation Test() : Unit {
1336
1437
}
1538
}"#,
16-
&[
17-
("Fake", CompletionItemKind::Function),
18-
("FakeStdLib", CompletionItemKind::Module),
19-
],
39+
&["Fake", "FakeWithParam", "FakeCtlAdj"],
40+
&expect![[r#"
41+
[
42+
Some(
43+
(
44+
"Fake",
45+
Function,
46+
Some(
47+
"operation Fake() : Unit",
48+
),
49+
),
50+
),
51+
Some(
52+
(
53+
"FakeWithParam",
54+
Function,
55+
Some(
56+
"operation FakeWithParam(x: Int) : Unit",
57+
),
58+
),
59+
),
60+
Some(
61+
(
62+
"FakeCtlAdj",
63+
Function,
64+
Some(
65+
"operation FakeCtlAdj() : Unit is Adj + Ctl",
66+
),
67+
),
68+
),
69+
]
70+
"#]],
2071
);
2172
}
2273

2374
#[test]
2475
fn in_namespace_contains_open() {
25-
assert_completions_contain(
76+
check(
2677
r#"
2778
namespace Test {
2879
2980
operation Test() : Unit {
3081
}
3182
}"#,
32-
&[("open", CompletionItemKind::Keyword)],
83+
&["open"],
84+
&expect![[r#"
85+
[
86+
Some(
87+
(
88+
"open",
89+
Keyword,
90+
None,
91+
),
92+
),
93+
]
94+
"#]],
3395
);
3496
}
3597

3698
#[test]
3799
fn top_level_contains_namespace() {
38-
assert_completions_contain(
100+
check(
39101
r#"
40102
namespace Test {}
41103
42104
"#,
43-
&[("namespace", CompletionItemKind::Keyword)],
105+
&["namespace"],
106+
&expect![[r#"
107+
[
108+
Some(
109+
(
110+
"namespace",
111+
Keyword,
112+
None,
113+
),
114+
),
115+
]
116+
"#]],
44117
);
45118
}
46-
47-
/// Asserts that the completion list at the given cursor position contains the expected completions.
48-
/// The cursor position is indicated by a `↘` marker in the source text.
49-
fn assert_completions_contain(
50-
source_with_cursor: &str,
51-
completions: &[(&str, CompletionItemKind)],
52-
) {
53-
let (source, cursor_offset, _) = get_source_and_marker_offsets(source_with_cursor);
54-
let compilation = compile_with_fake_stdlib("<source>", &source);
55-
let actual_completions = get_completions(&compilation, "<source>", cursor_offset[0]);
56-
for expected_completion in completions.iter() {
57-
assert!(
58-
actual_completions
59-
.items
60-
.iter()
61-
.any(|c| c.kind == expected_completion.1 && c.label == expected_completion.0),
62-
"expected to find\n{expected_completion:?}\nin:\n{actual_completions:?}"
63-
);
64-
}
65-
}

playground/src/main.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ function registerMonacoLanguageServiceProviders(
226226
kind: kind,
227227
insertText: i.label,
228228
sortText: i.sortText,
229+
detail: i.detail,
229230
range: undefined,
230231
};
231232
}),

vscode/src/completion.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ class QSharpCompletionItemProvider implements vscode.CompletionItemProvider {
4444
}
4545
const item = new CompletionItem(c.label, kind);
4646
item.sortText = c.sortText;
47+
item.detail = c.detail;
4748
return item;
4849
});
4950
}

wasm/src/language_service.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ impl LanguageService {
6565
})
6666
.to_string(),
6767
sortText: i.sort_text,
68+
detail: i.detail,
6869
})
6970
.collect(),
7071
})?)
@@ -108,6 +109,7 @@ export interface ICompletionList {
108109
label: string;
109110
kind: "function" | "interface" | "keyword" | "module";
110111
sortText?: string;
112+
detail?: string;
111113
}>
112114
}
113115
"#;
@@ -123,6 +125,7 @@ pub struct CompletionItem {
123125
pub label: String,
124126
pub sortText: Option<String>,
125127
pub kind: String,
128+
pub detail: Option<String>,
126129
}
127130

128131
#[wasm_bindgen(typescript_custom_section)]

0 commit comments

Comments
 (0)