From aa6838a37779f4f700f31e32f208913fe29fdf77 Mon Sep 17 00:00:00 2001 From: Dmitry Vasilevsky Date: Thu, 11 Jul 2024 11:00:08 -0700 Subject: [PATCH 01/12] Added metadata propagation and extraction with rudimentary display --- compiler/qsc_doc_gen/src/generate_docs.rs | 61 +++++++++++++++++++---- vscode/src/documentation.ts | 13 +++++ 2 files changed, 63 insertions(+), 11 deletions(-) diff --git a/compiler/qsc_doc_gen/src/generate_docs.rs b/compiler/qsc_doc_gen/src/generate_docs.rs index 8b0c4fd199..1151bb0d59 100644 --- a/compiler/qsc_doc_gen/src/generate_docs.rs +++ b/compiler/qsc_doc_gen/src/generate_docs.rs @@ -25,6 +25,8 @@ type Files = Vec<(Arc, Arc, Arc)>; struct Compilation { /// Package store, containing the current package and all its dependencies. package_store: PackageStore, + /// Current package id when provided. + current_package_id: Option, } impl Compilation { @@ -38,6 +40,7 @@ impl Compilation { let actual_capabilities = capabilities.unwrap_or_default(); let actual_language_features = language_features.unwrap_or_default(); + let mut current_package_id: Option = None; let package_store = if let Some((mut package_store, dependencies, sources)) = additional_program { let unit = compile( @@ -51,7 +54,7 @@ impl Compilation { // documentation we can produce. In future we may consider // displaying the fact of error presence on documentation page. - package_store.insert(unit); + current_package_id = Some(package_store.insert(unit)); package_store } else { let mut package_store = PackageStore::new(compile::core()); @@ -60,7 +63,10 @@ impl Compilation { package_store }; - Self { package_store } + Self { + package_store, + current_package_id, + } } } @@ -147,10 +153,17 @@ pub fn generate_docs( }; let mut toc: FxHashMap, Vec> = FxHashMap::default(); - for (_, unit) in &compilation.package_store { + for (package_id, unit) in &compilation.package_store { let package = &unit.package; for (_, item) in &package.items { - if let Some((ns, line)) = generate_doc_for_item(package, item, display, &mut files) { + if let Some((ns, line)) = generate_doc_for_item( + package, + &package_id.to_string(), + compilation.current_package_id == Some(package_id), + item, + display, + &mut files, + ) { toc.entry(ns).or_default().push(line); } } @@ -163,12 +176,17 @@ pub fn generate_docs( fn generate_doc_for_item<'a>( package: &'a Package, + package_name: &String, + include_internals: bool, item: &'a Item, display: &'a CodeDisplay, files: &mut Files, ) -> Option<(Rc, String)> { // Filter items - if item.visibility == Visibility::Internal || matches!(item.kind, ItemKind::Namespace(_, _)) { + if !include_internals && (item.visibility == Visibility::Internal) { + return None; + } + if matches!(item.kind, ItemKind::Namespace(_, _)) { return None; } @@ -176,7 +194,7 @@ fn generate_doc_for_item<'a>( let ns = get_namespace(package, item)?; // Add file - let (metadata, content) = generate_file(&ns, item, display)?; + let (metadata, content) = generate_file(package_name, &ns, item, display)?; let file_name: Arc = Arc::from(format!("{ns}/{}.md", metadata.name).as_str()); let file_metadata: Arc = Arc::from(metadata.to_string().as_str()); let file_content: Arc = Arc::from(content.as_str()); @@ -211,8 +229,13 @@ fn get_namespace(package: &Package, item: &Item) -> Option> { } } -fn generate_file(ns: &Rc, item: &Item, display: &CodeDisplay) -> Option<(Metadata, String)> { - let metadata = get_metadata(ns.clone(), item, display)?; +fn generate_file( + package_name: &String, + ns: &Rc, + item: &Item, + display: &CodeDisplay, +) -> Option<(Metadata, String)> { + let metadata = get_metadata(package_name, ns.clone(), item, display)?; let doc = increase_header_level(&item.doc); let title = &metadata.title; @@ -221,7 +244,8 @@ fn generate_file(ns: &Rc, item: &Item, display: &CodeDisplay) -> Option<(Me let content = format!( "# {title} -Namespace: {ns} +* **Package:** {package_name} +* **Namespace:** {ns} ```qsharp {sig} @@ -243,6 +267,7 @@ struct Metadata { title: String, topic: String, kind: MetadataKind, + package: String, namespace: Rc, name: Rc, summary: String, @@ -264,11 +289,19 @@ title: {} ms.date: {{TIMESTAMP}} ms.topic: {} qsharp.kind: {} +qsharp.package: {} qsharp.namespace: {} qsharp.name: {} qsharp.summary: \"{}\" ---", - self.uid, self.title, self.topic, kind, self.namespace, self.name, self.summary + self.uid, + self.title, + self.topic, + kind, + self.package, + self.namespace, + self.name, + self.summary ) } } @@ -279,7 +312,12 @@ enum MetadataKind { Udt, } -fn get_metadata(ns: Rc, item: &Item, display: &CodeDisplay) -> Option { +fn get_metadata( + package_name: &String, + ns: Rc, + item: &Item, + display: &CodeDisplay, +) -> Option { let (name, signature, kind) = match &item.kind { ItemKind::Callable(decl) => Some(( decl.name.name.clone(), @@ -310,6 +348,7 @@ fn get_metadata(ns: Rc, item: &Item, display: &CodeDisplay) -> Option= 0) { + const packageNameMatch = packageNameRegex.exec(file.metadata); + const namespaceMatch = namespaceRegex.exec(file.metadata); + const itemNameMatch = itemNameRegex.exec(file.metadata); + + const namespace = namespaceMatch != null ? namespaceMatch[1] : ""; + const packageName = packageNameMatch != null ? packageNameMatch[1] : ""; + const itemName = itemNameMatch != null ? itemNameMatch[1] : ""; + documentation.push(file.contents); + documentation.push("[" + namespace + ", " + packageName + ", " + itemName + "]"); } } From baf869d32180f20f4bfc164087717a6f3cc34931 Mon Sep 17 00:00:00 2001 From: Dmitry Vasilevsky Date: Mon, 15 Jul 2024 02:49:09 -0700 Subject: [PATCH 02/12] Added sorting and headers --- compiler/qsc_doc_gen/src/generate_docs.rs | 47 ++++++++++++++++++----- vscode/src/documentation.ts | 27 +++++++++---- 2 files changed, 58 insertions(+), 16 deletions(-) diff --git a/compiler/qsc_doc_gen/src/generate_docs.rs b/compiler/qsc_doc_gen/src/generate_docs.rs index 1151bb0d59..cef7ad52db 100644 --- a/compiler/qsc_doc_gen/src/generate_docs.rs +++ b/compiler/qsc_doc_gen/src/generate_docs.rs @@ -19,6 +19,7 @@ use std::rc::Rc; use std::sync::Arc; type Files = Vec<(Arc, Arc, Arc)>; +type FilesWithMetadata = Vec<(Arc, Arc, Arc, Arc)>; /// Represents an immutable compilation state. #[derive(Debug)] @@ -146,7 +147,7 @@ pub fn generate_docs( language_features: Option, ) -> Files { let compilation = Compilation::new(additional_sources, capabilities, language_features); - let mut files: Files = vec![]; + let mut files: FilesWithMetadata = vec![]; let display = &CodeDisplay { compilation: &compilation, @@ -169,9 +170,36 @@ pub fn generate_docs( } } - generate_toc(&mut toc, &mut files); + // We want to sort documentation files in a meaningful way. + // First, we want to put files for the current project, if it exists + // Then we want to put explicit dependencies of the current project, if they exist + // Then we want to add built-in std package. And finally built-in core package. + // Namespaces within packages should be sorted alphabetically and + // items with a namespace should be also sorted alphabetically. + // Also, items without any metadata should come last + // TODO: Implement properly + files.sort_unstable_by(|a, b| { + let package_compare = a.0.package.cmp(&b.0.package); + if package_compare == std::cmp::Ordering::Equal { + let namespace_compare = a.0.namespace.cmp(&b.0.namespace); + if namespace_compare == std::cmp::Ordering::Equal { + a.0.name.cmp(&b.0.name) + } else { + namespace_compare + } + } else { + package_compare.reverse() + } + }); + + let mut result: Files = files + .into_iter() + .map(|(_, name, metadata, content)| (name, metadata, content)) + .collect(); - files + generate_toc(&mut toc, &mut result); + + result } fn generate_doc_for_item<'a>( @@ -180,7 +208,7 @@ fn generate_doc_for_item<'a>( include_internals: bool, item: &'a Item, display: &'a CodeDisplay, - files: &mut Files, + files: &mut FilesWithMetadata, ) -> Option<(Rc, String)> { // Filter items if !include_internals && (item.visibility == Visibility::Internal) { @@ -198,11 +226,13 @@ fn generate_doc_for_item<'a>( let file_name: Arc = Arc::from(format!("{ns}/{}.md", metadata.name).as_str()); let file_metadata: Arc = Arc::from(metadata.to_string().as_str()); let file_content: Arc = Arc::from(content.as_str()); - files.push((file_name, file_metadata, file_content)); // Create toc line let line = format!(" - {{name: {}, uid: {}}}", metadata.name, metadata.uid); + let met: Arc = Arc::from(metadata); + files.push((met, file_name, file_metadata, file_content)); + // Return (ns, line) Some((ns.clone(), line)) } @@ -244,8 +274,7 @@ fn generate_file( let content = format!( "# {title} -* **Package:** {package_name} -* **Namespace:** {ns} +**Namespace:** {ns} ```qsharp {sig} @@ -313,7 +342,7 @@ enum MetadataKind { } fn get_metadata( - package_name: &String, + package_name: &str, ns: Rc, item: &Item, display: &CodeDisplay, @@ -348,7 +377,7 @@ fn get_metadata( }, topic: "managed-reference".to_string(), kind, - package: package_name.clone(), + package: String::from(package_name), namespace: ns, name, summary, diff --git a/vscode/src/documentation.ts b/vscode/src/documentation.ts index 06aa9e7d28..4309153ef2 100644 --- a/vscode/src/documentation.ts +++ b/vscode/src/documentation.ts @@ -28,8 +28,9 @@ export async function showDocumentationCommand(extensionUri: Uri) { const docFiles = await worker.getDocumentation(program.programConfig); const packageNameRegex = new RegExp("^qsharp.package: (.+)$", "m"); + let currentPackage = ""; const namespaceRegex = new RegExp("^qsharp.namespace: (.+)$", "m"); - const itemNameRegex = new RegExp("^qsharp.name: (.+)$", "m"); + let currentNamespace = ""; const documentation: string[] = []; for (const file of docFiles) { @@ -39,15 +40,27 @@ export async function showDocumentationCommand(extensionUri: Uri) { // only files that contain documentation from some qsharp object. if (file.metadata.indexOf("qsharp.name:") >= 0) { const packageNameMatch = packageNameRegex.exec(file.metadata); + const packageName = packageNameMatch != null ? packageNameMatch[1] : ""; const namespaceMatch = namespaceRegex.exec(file.metadata); - const itemNameMatch = itemNameRegex.exec(file.metadata); - const namespace = namespaceMatch != null ? namespaceMatch[1] : ""; - const packageName = packageNameMatch != null ? packageNameMatch[1] : ""; - const itemName = itemNameMatch != null ? itemNameMatch[1] : ""; - documentation.push(file.contents); - documentation.push("[" + namespace + ", " + packageName + ", " + itemName + "]"); + let packageHeader = ""; + if (packageName != currentPackage) { + currentPackage = packageName; + packageHeader = `## Package ${packageName}\n`; + } + + let namespaceHeader = ""; + if (namespace != currentNamespace) { + currentNamespace = namespace; + namespaceHeader = `## Namespace ${namespace}\n`; + } + + if (packageHeader == "" && namespaceHeader == "") { + documentation.push(file.contents); + } else { + documentation.push(packageHeader + namespaceHeader + file.contents); + } } } From cf3c757d3c654ce15e5bd40ca62db9b486850239 Mon Sep 17 00:00:00 2001 From: Dmitry Vasilevsky Date: Mon, 15 Jul 2024 17:47:27 -0700 Subject: [PATCH 03/12] updates to docs in vscode --- compiler/qsc_doc_gen/src/generate_docs.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/qsc_doc_gen/src/generate_docs.rs b/compiler/qsc_doc_gen/src/generate_docs.rs index cef7ad52db..c20202ca9e 100644 --- a/compiler/qsc_doc_gen/src/generate_docs.rs +++ b/compiler/qsc_doc_gen/src/generate_docs.rs @@ -204,7 +204,7 @@ pub fn generate_docs( fn generate_doc_for_item<'a>( package: &'a Package, - package_name: &String, + package_name: &str, include_internals: bool, item: &'a Item, display: &'a CodeDisplay, @@ -260,7 +260,7 @@ fn get_namespace(package: &Package, item: &Item) -> Option> { } fn generate_file( - package_name: &String, + package_name: &str, ns: &Rc, item: &Item, display: &CodeDisplay, From 788513d20e44826bae0159b0a18d2e86356cd89f Mon Sep 17 00:00:00 2001 From: Dmitry Vasilevsky Date: Wed, 14 Aug 2024 17:36:24 -0700 Subject: [PATCH 04/12] Added package aliases --- compiler/qsc_doc_gen/src/generate_docs.rs | 100 +++++++++++++++++----- vscode/src/documentation.ts | 29 +------ 2 files changed, 84 insertions(+), 45 deletions(-) diff --git a/compiler/qsc_doc_gen/src/generate_docs.rs b/compiler/qsc_doc_gen/src/generate_docs.rs index 4c4e952ea8..7c2af9f2b9 100644 --- a/compiler/qsc_doc_gen/src/generate_docs.rs +++ b/compiler/qsc_doc_gen/src/generate_docs.rs @@ -28,6 +28,8 @@ struct Compilation { package_store: PackageStore, /// Current package id when provided. current_package_id: Option, + /// Aliases for packages. + package_aliases: FxHashMap>, } impl Compilation { @@ -42,6 +44,8 @@ impl Compilation { let actual_language_features = language_features.unwrap_or_default(); let mut current_package_id: Option = None; + let mut package_aliases: FxHashMap> = FxHashMap::default(); + let package_store = if let Some((mut package_store, dependencies, sources)) = additional_program { let unit = compile( @@ -55,6 +59,12 @@ impl Compilation { // documentation we can produce. In future we may consider // displaying the fact of error presence on documentation page. + for (package_id, package_alias) in dependencies { + if let Some(package_alias) = package_alias { + package_aliases.insert(*package_id, package_alias.clone()); + } + } + current_package_id = Some(package_store.insert(unit)); package_store } else { @@ -67,6 +77,7 @@ impl Compilation { Self { package_store, current_package_id, + package_aliases, } } } @@ -156,10 +167,28 @@ pub fn generate_docs( let mut toc: FxHashMap, Vec> = FxHashMap::default(); for (package_id, unit) in &compilation.package_store { let package = &unit.package; + // The below is not actually enforced by the compiler, + // but it is currently the case as long as you're compiling with stdlib + let package_kind = match Into::::into(package_id) { + 0 => PackageKind::Core, + 1 => PackageKind::StandardLibrary, + 2 => PackageKind::UserCode, + _ => PackageKind::AliasedPackage( + compilation + .package_aliases + .get(&package_id) + .expect("Non-stdlib dependency did not have alias") + .to_string(), + ), + }; + // let package_alias = match compilation.package_aliases.get(&package_id) { + // Some(Some(alias)) => alias.to_string(), + // _ => String::default(), + // }; for (_, item) in &package.items { if let Some((ns, line)) = generate_doc_for_item( package, - &package_id.to_string(), + package_kind.clone(), compilation.current_package_id == Some(package_id), item, display, @@ -178,18 +207,12 @@ pub fn generate_docs( // items with a namespace should be also sorted alphabetically. // Also, items without any metadata should come last // TODO: Implement properly - files.sort_unstable_by(|a, b| { - let package_compare = a.0.package.cmp(&b.0.package); - if package_compare == std::cmp::Ordering::Equal { - let namespace_compare = a.0.namespace.cmp(&b.0.namespace); - if namespace_compare == std::cmp::Ordering::Equal { - a.0.name.cmp(&b.0.name) - } else { - namespace_compare - } - } else { - package_compare.reverse() - } + files.sort_by_key(|file| { + ( + file.0.package.clone(), + file.0.namespace.clone(), + file.0.name.clone(), + ) }); let mut result: Files = files @@ -204,7 +227,7 @@ pub fn generate_docs( fn generate_doc_for_item<'a>( package: &'a Package, - package_name: &str, + package_kind: PackageKind, include_internals: bool, item: &'a Item, display: &'a CodeDisplay, @@ -222,7 +245,7 @@ fn generate_doc_for_item<'a>( let ns = get_namespace(package, item)?; // Add file - let (metadata, content) = generate_file(package_name, &ns, item, display)?; + let (metadata, content) = generate_file(package_kind, &ns, item, display)?; let file_name: Arc = Arc::from(format!("{ns}/{}.md", metadata.name).as_str()); let file_metadata: Arc = Arc::from(metadata.to_string().as_str()); let file_content: Arc = Arc::from(content.as_str()); @@ -260,12 +283,12 @@ fn get_namespace(package: &Package, item: &Item) -> Option> { } fn generate_file( - package_name: &str, + package_kind: PackageKind, ns: &Rc, item: &Item, display: &CodeDisplay, ) -> Option<(Metadata, String)> { - let metadata = get_metadata(package_name, ns.clone(), item, display)?; + let metadata = get_metadata(package_kind, ns.clone(), item, display)?; let doc = increase_header_level(&item.doc); let title = &metadata.title; @@ -296,13 +319,50 @@ struct Metadata { title: String, topic: String, kind: MetadataKind, - package: String, + package: PackageKind, namespace: Rc, name: Rc, summary: String, signature: String, } +impl Metadata { + fn fully_qualified_name(&self) -> String { + let mut buf = if let PackageKind::AliasedPackage(ref package_alias) = self.package { + vec![format!("{package_alias}")] + } else { + vec![] + }; + + buf.push(self.namespace.to_string()); + buf.push(self.name.to_string()); + buf.join(".") + } +} + +#[derive(PartialOrd, Ord, Eq, PartialEq, Clone)] +enum PackageKind { + UserCode, + AliasedPackage(String), + StandardLibrary, + Core, +} + +impl Display for PackageKind { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!( + f, + "{}", + match self { + PackageKind::UserCode => "", + PackageKind::AliasedPackage(name) => name, + PackageKind::StandardLibrary => "Std", + PackageKind::Core => "Core", + } + ) + } +} + impl Display for Metadata { fn fmt(&self, f: &mut Formatter<'_>) -> Result { let kind = match &self.kind { @@ -344,7 +404,7 @@ enum MetadataKind { } fn get_metadata( - package_name: &str, + package_kind: PackageKind, ns: Rc, item: &Item, display: &CodeDisplay, @@ -386,7 +446,7 @@ fn get_metadata( }, topic: "managed-reference".to_string(), kind, - package: String::from(package_name), + package: package_kind, namespace: ns, name, summary, diff --git a/vscode/src/documentation.ts b/vscode/src/documentation.ts index 4309153ef2..82ac932254 100644 --- a/vscode/src/documentation.ts +++ b/vscode/src/documentation.ts @@ -27,10 +27,10 @@ export async function showDocumentationCommand(extensionUri: Uri) { const worker = getCompilerWorker(compilerWorkerScriptPath); const docFiles = await worker.getDocumentation(program.programConfig); - const packageNameRegex = new RegExp("^qsharp.package: (.+)$", "m"); - let currentPackage = ""; - const namespaceRegex = new RegExp("^qsharp.namespace: (.+)$", "m"); - let currentNamespace = ""; + // const packageNameRegex = new RegExp("^qsharp.package: (.+)$", "m"); + // let currentPackage = ""; + // const namespaceRegex = new RegExp("^qsharp.namespace: (.+)$", "m"); + // let currentNamespace = ""; const documentation: string[] = []; for (const file of docFiles) { @@ -39,28 +39,7 @@ export async function showDocumentationCommand(extensionUri: Uri) { // We check presence of qsharp.name in metadata to make sure we take // only files that contain documentation from some qsharp object. if (file.metadata.indexOf("qsharp.name:") >= 0) { - const packageNameMatch = packageNameRegex.exec(file.metadata); - const packageName = packageNameMatch != null ? packageNameMatch[1] : ""; - const namespaceMatch = namespaceRegex.exec(file.metadata); - const namespace = namespaceMatch != null ? namespaceMatch[1] : ""; - - let packageHeader = ""; - if (packageName != currentPackage) { - currentPackage = packageName; - packageHeader = `## Package ${packageName}\n`; - } - - let namespaceHeader = ""; - if (namespace != currentNamespace) { - currentNamespace = namespace; - namespaceHeader = `## Namespace ${namespace}\n`; - } - - if (packageHeader == "" && namespaceHeader == "") { documentation.push(file.contents); - } else { - documentation.push(packageHeader + namespaceHeader + file.contents); - } } } From d573c337364c5f47685d59891a39869e6312957a Mon Sep 17 00:00:00 2001 From: Dmitry Vasilevsky Date: Thu, 15 Aug 2024 10:01:41 -0700 Subject: [PATCH 05/12] Fixed crash --- compiler/qsc_doc_gen/src/generate_docs.rs | 24 ++++++++++------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/compiler/qsc_doc_gen/src/generate_docs.rs b/compiler/qsc_doc_gen/src/generate_docs.rs index 7c2af9f2b9..32be79236f 100644 --- a/compiler/qsc_doc_gen/src/generate_docs.rs +++ b/compiler/qsc_doc_gen/src/generate_docs.rs @@ -172,19 +172,15 @@ pub fn generate_docs( let package_kind = match Into::::into(package_id) { 0 => PackageKind::Core, 1 => PackageKind::StandardLibrary, - 2 => PackageKind::UserCode, - _ => PackageKind::AliasedPackage( - compilation - .package_aliases - .get(&package_id) - .expect("Non-stdlib dependency did not have alias") - .to_string(), - ), + _ => { + if let Some(package_alias) = compilation.package_aliases.get(&package_id) { + PackageKind::AliasedPackage(package_alias.to_string()) + } else { + PackageKind::UserCode + } + } }; - // let package_alias = match compilation.package_aliases.get(&package_id) { - // Some(Some(alias)) => alias.to_string(), - // _ => String::default(), - // }; + for (_, item) in &package.items { if let Some((ns, line)) = generate_doc_for_item( package, @@ -206,7 +202,6 @@ pub fn generate_docs( // Namespaces within packages should be sorted alphabetically and // items with a namespace should be also sorted alphabetically. // Also, items without any metadata should come last - // TODO: Implement properly files.sort_by_key(|file| { ( file.0.package.clone(), @@ -292,12 +287,13 @@ fn generate_file( let doc = increase_header_level(&item.doc); let title = &metadata.title; + let full_name = metadata.fully_qualified_name(); let sig = &metadata.signature; let content = format!( "# {title} -**Namespace:** {ns} +**Fully qualified name:** {full_name} ```qsharp {sig} From 15c9a8b35b6bb0279f15254a45e8b48f2558c03d Mon Sep 17 00:00:00 2001 From: Dmitry Vasilevsky Date: Thu, 15 Aug 2024 17:19:00 -0700 Subject: [PATCH 06/12] Proper sorting and package aliases: --- compiler/qsc_doc_gen/src/generate_docs.rs | 70 ++++++++++------------- 1 file changed, 29 insertions(+), 41 deletions(-) diff --git a/compiler/qsc_doc_gen/src/generate_docs.rs b/compiler/qsc_doc_gen/src/generate_docs.rs index 32be79236f..7622900805 100644 --- a/compiler/qsc_doc_gen/src/generate_docs.rs +++ b/compiler/qsc_doc_gen/src/generate_docs.rs @@ -19,7 +19,7 @@ use std::rc::Rc; use std::sync::Arc; type Files = Vec<(Arc, Arc, Arc)>; -type FilesWithMetadata = Vec<(Arc, Arc, Arc, Arc)>; +type FilesWithMetadata = Vec<(Arc, Arc, Arc)>; /// Represents an immutable compilation state. #[derive(Debug)] @@ -29,7 +29,7 @@ struct Compilation { /// Current package id when provided. current_package_id: Option, /// Aliases for packages. - package_aliases: FxHashMap>, + package_aliases: FxHashMap>, // TODO: Change to dependencies. } impl Compilation { @@ -165,27 +165,39 @@ pub fn generate_docs( }; let mut toc: FxHashMap, Vec> = FxHashMap::default(); + for (package_id, unit) in &compilation.package_store { - let package = &unit.package; - // The below is not actually enforced by the compiler, - // but it is currently the case as long as you're compiling with stdlib - let package_kind = match Into::::into(package_id) { - 0 => PackageKind::Core, - 1 => PackageKind::StandardLibrary, + let is_current_package = compilation.current_package_id == Some(package_id); + let package_kind; + match package_id { + // Core package is always included in the compilation. + PackageId::CORE => package_kind = PackageKind::Core, + + // Standard package is currently always included, but this isn't enforced by the compiler. + _ if package_id == 1.into() => package_kind = PackageKind::StandardLibrary, + + // This package could be user code if current package is specified. + _ if is_current_package => { + package_kind = PackageKind::UserCode; + } + _ => { - if let Some(package_alias) = compilation.package_aliases.get(&package_id) { - PackageKind::AliasedPackage(package_alias.to_string()) + if let Some(alias) = compilation.package_aliases.get(&package_id) { + // This is a direct dependency of the user code. + package_kind = PackageKind::AliasedPackage(alias.to_string()); } else { - PackageKind::UserCode + // This is not a package user can access (an indirect dependency). + continue; } } - }; + } + let package = &unit.package; for (_, item) in &package.items { if let Some((ns, line)) = generate_doc_for_item( package, package_kind.clone(), - compilation.current_package_id == Some(package_id), + is_current_package, item, display, &mut files, @@ -201,7 +213,7 @@ pub fn generate_docs( // Then we want to add built-in std package. And finally built-in core package. // Namespaces within packages should be sorted alphabetically and // items with a namespace should be also sorted alphabetically. - // Also, items without any metadata should come last + // Also, items without any metadata (table of content) should come last files.sort_by_key(|file| { ( file.0.package.clone(), @@ -212,7 +224,7 @@ pub fn generate_docs( let mut result: Files = files .into_iter() - .map(|(_, name, metadata, content)| (name, metadata, content)) + .map(|(metadata, name, content)| (name, Arc::from(metadata.to_string().as_str()), content)) .collect(); generate_toc(&mut toc, &mut result); @@ -242,14 +254,13 @@ fn generate_doc_for_item<'a>( // Add file let (metadata, content) = generate_file(package_kind, &ns, item, display)?; let file_name: Arc = Arc::from(format!("{ns}/{}.md", metadata.name).as_str()); - let file_metadata: Arc = Arc::from(metadata.to_string().as_str()); let file_content: Arc = Arc::from(content.as_str()); // Create toc line let line = format!(" - {{name: {}, uid: {}}}", metadata.name, metadata.uid); let met: Arc = Arc::from(metadata); - files.push((met, file_name, file_metadata, file_content)); + files.push((met, file_name, file_content)); // Return (ns, line) Some((ns.clone(), line)) @@ -344,21 +355,6 @@ enum PackageKind { Core, } -impl Display for PackageKind { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - write!( - f, - "{}", - match self { - PackageKind::UserCode => "", - PackageKind::AliasedPackage(name) => name, - PackageKind::StandardLibrary => "Std", - PackageKind::Core => "Core", - } - ) - } -} - impl Display for Metadata { fn fmt(&self, f: &mut Formatter<'_>) -> Result { let kind = match &self.kind { @@ -375,19 +371,11 @@ title: {} ms.date: {{TIMESTAMP}} ms.topic: {} qsharp.kind: {} -qsharp.package: {} qsharp.namespace: {} qsharp.name: {} qsharp.summary: \"{}\" ---", - self.uid, - self.title, - self.topic, - kind, - self.package, - self.namespace, - self.name, - self.summary + self.uid, self.title, self.topic, kind, self.namespace, self.name, self.summary ) } } From 2526a01e9fa0af6d1a6c717bcc64b995819cd345 Mon Sep 17 00:00:00 2001 From: Dmitry Vasilevsky Date: Thu, 15 Aug 2024 18:10:17 -0700 Subject: [PATCH 07/12] updates to display of docs --- compiler/qsc_doc_gen/src/generate_docs.rs | 30 +++++++++++++---------- vscode/src/documentation.ts | 7 +----- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/compiler/qsc_doc_gen/src/generate_docs.rs b/compiler/qsc_doc_gen/src/generate_docs.rs index 7622900805..183d340e0e 100644 --- a/compiler/qsc_doc_gen/src/generate_docs.rs +++ b/compiler/qsc_doc_gen/src/generate_docs.rs @@ -29,7 +29,7 @@ struct Compilation { /// Current package id when provided. current_package_id: Option, /// Aliases for packages. - package_aliases: FxHashMap>, // TODO: Change to dependencies. + dependencies: FxHashMap>, } impl Compilation { @@ -77,7 +77,7 @@ impl Compilation { Self { package_store, current_package_id, - package_aliases, + dependencies: package_aliases, } } } @@ -182,7 +182,7 @@ pub fn generate_docs( } _ => { - if let Some(alias) = compilation.package_aliases.get(&package_id) { + if let Some(alias) = compilation.dependencies.get(&package_id) { // This is a direct dependency of the user code. package_kind = PackageKind::AliasedPackage(alias.to_string()); } else { @@ -297,15 +297,12 @@ fn generate_file( let metadata = get_metadata(package_kind, ns.clone(), item, display)?; let doc = increase_header_level(&item.doc); - let title = &metadata.title; - let full_name = metadata.fully_qualified_name(); + let title = format!("{} {}", metadata.fully_qualified_name(), metadata.kind); let sig = &metadata.signature; let content = format!( "# {title} -**Fully qualified name:** {full_name} - ```qsharp {sig} ``` @@ -387,6 +384,18 @@ enum MetadataKind { Export, } +impl Display for MetadataKind { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + let s = match &self { + MetadataKind::Function => "function", + MetadataKind::Operation => "operation", + MetadataKind::Udt => "user defined type", + MetadataKind::Export => "exported item", + }; + write!(f, "{s}") + } +} + fn get_metadata( package_kind: PackageKind, ns: Rc, @@ -422,12 +431,7 @@ fn get_metadata( Some(Metadata { uid: format!("Qdk.{ns}.{name}"), - title: match &kind { - MetadataKind::Function => format!("{name} function"), - MetadataKind::Operation => format!("{name} operation"), - MetadataKind::Udt => format!("{name} user defined type"), - MetadataKind::Export => format!("{name} exported item"), - }, + title: format!("{name} {kind}"), topic: "managed-reference".to_string(), kind, package: package_kind, diff --git a/vscode/src/documentation.ts b/vscode/src/documentation.ts index 82ac932254..f9d068d11d 100644 --- a/vscode/src/documentation.ts +++ b/vscode/src/documentation.ts @@ -27,11 +27,6 @@ export async function showDocumentationCommand(extensionUri: Uri) { const worker = getCompilerWorker(compilerWorkerScriptPath); const docFiles = await worker.getDocumentation(program.programConfig); - // const packageNameRegex = new RegExp("^qsharp.package: (.+)$", "m"); - // let currentPackage = ""; - // const namespaceRegex = new RegExp("^qsharp.namespace: (.+)$", "m"); - // let currentNamespace = ""; - const documentation: string[] = []; for (const file of docFiles) { // Some files may contain information other than documentation @@ -39,7 +34,7 @@ export async function showDocumentationCommand(extensionUri: Uri) { // We check presence of qsharp.name in metadata to make sure we take // only files that contain documentation from some qsharp object. if (file.metadata.indexOf("qsharp.name:") >= 0) { - documentation.push(file.contents); + documentation.push(file.contents); } } From d09978dddd3b791e5314c0b174abd82ad1b7bcfe Mon Sep 17 00:00:00 2001 From: Dmitry Vasilevsky Date: Thu, 15 Aug 2024 18:27:21 -0700 Subject: [PATCH 08/12] Updated tests --- compiler/qsc_doc_gen/src/generate_docs/tests.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler/qsc_doc_gen/src/generate_docs/tests.rs b/compiler/qsc_doc_gen/src/generate_docs/tests.rs index 928e00cb30..0d1e066ef4 100644 --- a/compiler/qsc_doc_gen/src/generate_docs/tests.rs +++ b/compiler/qsc_doc_gen/src/generate_docs/tests.rs @@ -27,9 +27,7 @@ fn docs_generation() { qsharp.summary: "Returns the number of elements in the input array `a`." --- - # Length function - - Namespace: Microsoft.Quantum.Core + # Microsoft.Quantum.Core.Length function ```qsharp function Length<'T>(a : 'T[]) : Int From a04f153d4dedc02323072ac2e4f80d0e93690693 Mon Sep 17 00:00:00 2001 From: DmitryVasilevsky <60718360+DmitryVasilevsky@users.noreply.github.com> Date: Mon, 19 Aug 2024 10:59:02 -0700 Subject: [PATCH 09/12] Update compiler/qsc_doc_gen/src/generate_docs.rs Co-authored-by: Scott Carda <55811729+ScottCarda-MS@users.noreply.github.com> --- compiler/qsc_doc_gen/src/generate_docs.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/qsc_doc_gen/src/generate_docs.rs b/compiler/qsc_doc_gen/src/generate_docs.rs index 183d340e0e..393b923909 100644 --- a/compiler/qsc_doc_gen/src/generate_docs.rs +++ b/compiler/qsc_doc_gen/src/generate_docs.rs @@ -208,12 +208,12 @@ pub fn generate_docs( } // We want to sort documentation files in a meaningful way. - // First, we want to put files for the current project, if it exists - // Then we want to put explicit dependencies of the current project, if they exist + // First, we want to put files for the current project, if it exists. + // Then we want to put explicit dependencies of the current project, if they exist. // Then we want to add built-in std package. And finally built-in core package. // Namespaces within packages should be sorted alphabetically and // items with a namespace should be also sorted alphabetically. - // Also, items without any metadata (table of content) should come last + // Also, items without any metadata (table of content) should come last. files.sort_by_key(|file| { ( file.0.package.clone(), From 858e311e7feeb2a24a19f6f4071635f7d2580334 Mon Sep 17 00:00:00 2001 From: Dmitry Vasilevsky Date: Mon, 19 Aug 2024 12:37:38 -0700 Subject: [PATCH 10/12] PR feedback --- compiler/qsc_doc_gen/src/generate_docs.rs | 43 ++++++++++------------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/compiler/qsc_doc_gen/src/generate_docs.rs b/compiler/qsc_doc_gen/src/generate_docs.rs index 183d340e0e..3c4b6319a0 100644 --- a/compiler/qsc_doc_gen/src/generate_docs.rs +++ b/compiler/qsc_doc_gen/src/generate_docs.rs @@ -18,8 +18,9 @@ use std::fmt::{Display, Formatter, Result}; use std::rc::Rc; use std::sync::Arc; +// Name, Metadata, Content type Files = Vec<(Arc, Arc, Arc)>; -type FilesWithMetadata = Vec<(Arc, Arc, Arc)>; +type FilesWithMetadata = Vec<(Arc, Arc, Arc)>; /// Represents an immutable compilation state. #[derive(Debug)] @@ -169,27 +170,21 @@ pub fn generate_docs( for (package_id, unit) in &compilation.package_store { let is_current_package = compilation.current_package_id == Some(package_id); let package_kind; - match package_id { + if package_id == PackageId::CORE { // Core package is always included in the compilation. - PackageId::CORE => package_kind = PackageKind::Core, - + package_kind = PackageKind::Core; + } else if package_id == 1.into() { // Standard package is currently always included, but this isn't enforced by the compiler. - _ if package_id == 1.into() => package_kind = PackageKind::StandardLibrary, - + package_kind = PackageKind::StandardLibrary; + } else if is_current_package { // This package could be user code if current package is specified. - _ if is_current_package => { - package_kind = PackageKind::UserCode; - } - - _ => { - if let Some(alias) = compilation.dependencies.get(&package_id) { - // This is a direct dependency of the user code. - package_kind = PackageKind::AliasedPackage(alias.to_string()); - } else { - // This is not a package user can access (an indirect dependency). - continue; - } - } + package_kind = PackageKind::UserCode; + } else if let Some(alias) = compilation.dependencies.get(&package_id) { + // This is a direct dependency of the user code. + package_kind = PackageKind::AliasedPackage(alias.to_string()); + } else { + // This is not a package user can access (an indirect dependency). + continue; } let package = &unit.package; @@ -216,15 +211,15 @@ pub fn generate_docs( // Also, items without any metadata (table of content) should come last files.sort_by_key(|file| { ( - file.0.package.clone(), - file.0.namespace.clone(), - file.0.name.clone(), + file.1.package.clone(), + file.1.namespace.clone(), + file.1.name.clone(), ) }); let mut result: Files = files .into_iter() - .map(|(metadata, name, content)| (name, Arc::from(metadata.to_string().as_str()), content)) + .map(|(name, metadata, content)| (name, Arc::from(metadata.to_string().as_str()), content)) .collect(); generate_toc(&mut toc, &mut result); @@ -260,7 +255,7 @@ fn generate_doc_for_item<'a>( let line = format!(" - {{name: {}, uid: {}}}", metadata.name, metadata.uid); let met: Arc = Arc::from(metadata); - files.push((met, file_name, file_content)); + files.push((file_name, met, file_content)); // Return (ns, line) Some((ns.clone(), line)) From 8ee6f3fb7514f0dec033167bf12e7e5cd857e955 Mon Sep 17 00:00:00 2001 From: Dmitry Vasilevsky Date: Tue, 20 Aug 2024 12:53:32 -0700 Subject: [PATCH 11/12] Fully qualified name separate from title --- compiler/qsc_doc_gen/src/generate_docs.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/qsc_doc_gen/src/generate_docs.rs b/compiler/qsc_doc_gen/src/generate_docs.rs index 755a39731e..ab3d660dd1 100644 --- a/compiler/qsc_doc_gen/src/generate_docs.rs +++ b/compiler/qsc_doc_gen/src/generate_docs.rs @@ -292,12 +292,15 @@ fn generate_file( let metadata = get_metadata(package_kind, ns.clone(), item, display)?; let doc = increase_header_level(&item.doc); - let title = format!("{} {}", metadata.fully_qualified_name(), metadata.kind); + let title = &metadata.title; + let fqn = &metadata.fully_qualified_name(); let sig = &metadata.signature; let content = format!( "# {title} +Fully qualified name: {fqn} + ```qsharp {sig} ``` From e893cb66fb78a6ce93fe66c1c84a6e61334b8d94 Mon Sep 17 00:00:00 2001 From: Dmitry Vasilevsky Date: Tue, 20 Aug 2024 12:57:01 -0700 Subject: [PATCH 12/12] Updated test --- compiler/qsc_doc_gen/src/generate_docs/tests.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/qsc_doc_gen/src/generate_docs/tests.rs b/compiler/qsc_doc_gen/src/generate_docs/tests.rs index 0d1e066ef4..8fb8e83225 100644 --- a/compiler/qsc_doc_gen/src/generate_docs/tests.rs +++ b/compiler/qsc_doc_gen/src/generate_docs/tests.rs @@ -27,7 +27,9 @@ fn docs_generation() { qsharp.summary: "Returns the number of elements in the input array `a`." --- - # Microsoft.Quantum.Core.Length function + # Length function + + Fully qualified name: Microsoft.Quantum.Core.Length ```qsharp function Length<'T>(a : 'T[]) : Int