diff --git a/language_service/src/completion.rs b/language_service/src/completion.rs index 4836e16c28..10e63377b2 100644 --- a/language_service/src/completion.rs +++ b/language_service/src/completion.rs @@ -9,7 +9,7 @@ use crate::display::CodeDisplay; use crate::protocol::{CompletionItem, CompletionItemKind, CompletionList}; use crate::qsc_utils::{protocol_span, span_contains}; use qsc::ast::visit::{self, Visitor}; -use qsc::hir::{ItemKind, Package, PackageId}; +use qsc::hir::{ItemKind, Package, PackageId, Visibility}; use qsc::resolve::{Local, LocalKind}; use rustc_hash::FxHashSet; use std::rc::Rc; @@ -385,11 +385,33 @@ impl CompletionListBuilder { .package; let display = CodeDisplay { compilation }; + let is_user_package = compilation.user_package_id == package_id; + package.items.values().filter_map(move |i| { // We only want items whose parents are namespaces if let Some(item_id) = i.parent { if let Some(parent) = package.items.get(item_id) { if let ItemKind::Namespace(namespace, _) = &parent.kind { + if namespace.name.starts_with("Microsoft.Quantum.Unstable") { + return None; + } + // If the item's visibility is internal, the item may be ignored + if matches!(i.visibility, Visibility::Internal) { + if !is_user_package { + return None; // ignore item if not in the user's package + } + // ignore item if the user is not in the item's namespace + match ¤t_namespace_name { + Some(curr_ns) => { + if *curr_ns != namespace.name { + return None; + } + } + None => { + return None; + } + } + } return match &i.kind { ItemKind::Callable(callable_decl) => { let name = callable_decl.name.name.as_ref(); @@ -498,10 +520,14 @@ impl CompletionListBuilder { fn get_namespaces(package: &'_ Package) -> impl Iterator + '_ { package.items.values().filter_map(|i| match &i.kind { - ItemKind::Namespace(namespace, _) => Some(CompletionItem::new( - namespace.name.to_string(), - CompletionItemKind::Module, - )), + ItemKind::Namespace(namespace, _) + if !namespace.name.starts_with("Microsoft.Quantum.Unstable") => + { + Some(CompletionItem::new( + namespace.name.to_string(), + CompletionItemKind::Module, + )) + } _ => None, }) } diff --git a/language_service/src/completion/tests.rs b/language_service/src/completion/tests.rs index 1f4747f873..40ff4df7eb 100644 --- a/language_service/src/completion/tests.rs +++ b/language_service/src/completion/tests.rs @@ -94,6 +94,147 @@ fn assert_no_duplicates(mut actual_completions: CompletionList) { assert!(dups.is_empty(), "duplicate completions found: {dups:#?}"); } +#[test] +fn ignore_unstable_namespace() { + check( + r#" + namespace Test { + open ↘ + }"#, + &["FakeStdLib", "Microsoft.Quantum.Unstable"], + &expect![[r#" + [ + Some( + CompletionItem { + label: "FakeStdLib", + kind: Module, + sort_text: Some( + "1101FakeStdLib", + ), + detail: None, + additional_text_edits: None, + }, + ), + None, + ] + "#]], + ); +} + +#[test] +fn ignore_unstable_callable() { + check( + r#" + namespace Test { + open Microsoft.Quantum.Unstable; + operation Foo() : Unit { + ↘ + } + }"#, + &["Fake", "UnstableFake"], + &expect![[r#" + [ + Some( + CompletionItem { + label: "Fake", + kind: Function, + sort_text: Some( + "0700Fake", + ), + detail: Some( + "operation Fake() : Unit", + ), + additional_text_edits: Some( + [ + ( + Span { + start: 38, + end: 38, + }, + "open FakeStdLib;\n ", + ), + ], + ), + }, + ), + None, + ] + "#]], + ); +} + +#[test] +fn ignore_internal_callable() { + check( + r#" + namespace Test { + internal operation Foo() : Unit {} + operation Bar() : Unit { + ↘ + } + } + + namespace Test { + internal operation Baz() : Unit {} + }"#, + &["Fake", "Foo", "Baz", "Hidden"], + &expect![[r#" + [ + Some( + CompletionItem { + label: "Fake", + kind: Function, + sort_text: Some( + "0700Fake", + ), + detail: Some( + "operation Fake() : Unit", + ), + additional_text_edits: Some( + [ + ( + Span { + start: 38, + end: 38, + }, + "open FakeStdLib;\n ", + ), + ], + ), + }, + ), + Some( + CompletionItem { + label: "Foo", + kind: Function, + sort_text: Some( + "0600Foo", + ), + detail: Some( + "operation Foo() : Unit", + ), + additional_text_edits: None, + }, + ), + Some( + CompletionItem { + label: "Baz", + kind: Function, + sort_text: Some( + "0600Baz", + ), + detail: Some( + "operation Baz() : Unit", + ), + additional_text_edits: None, + }, + ), + None, + ] + "#]], + ); +} + #[test] fn in_block_contains_std_functions_from_open_namespace() { check( diff --git a/language_service/src/test_utils.rs b/language_service/src/test_utils.rs index 055577d4ae..0dcd437e7d 100644 --- a/language_service/src/test_utils.rs +++ b/language_service/src/test_utils.rs @@ -134,6 +134,11 @@ fn compile_fake_stdlib() -> (PackageStore, PackageId) { Fake(); } operation FakeWithTypeParam<'A>(a : 'A) : 'A { a } + internal operation Hidden() : Unit {} + } + + namespace Microsoft.Quantum.Unstable { + operation UnstableFake() : Unit {} }"# .into(), )],