Skip to content

Commit

Permalink
Only Display Summary on Hover (#449)
Browse files Browse the repository at this point in the history
  • Loading branch information
ScottCarda-MS authored Jul 14, 2023
1 parent 19fb576 commit 53267ee
Show file tree
Hide file tree
Showing 9 changed files with 242 additions and 51 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ num-bigint = "0.4.3"
num-complex = "0.4"
num-traits = "0.2.15"
indenter = "0.2"
regex-lite = "0.1.0"
serde = "1.0"
serde-wasm-bindgen = "0.5"
wasm-bindgen = "0.2.84"
Expand Down
1 change: 1 addition & 0 deletions language_service/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ log = { workspace = true }
miette = { workspace = true }
qsc = { path = "../compiler/qsc" }
enum-iterator = { workspace = true }
regex-lite = { workspace = true }
27 changes: 25 additions & 2 deletions language_service/src/hover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::display::CodeDisplay;
use crate::qsc_utils::{find_item, map_offset, span_contains, Compilation};
use qsc::ast::visit::{walk_callable_decl, walk_expr, walk_pat, walk_ty_def, Visitor};
use qsc::{ast, hir, resolve};
use regex_lite::Regex;
use std::fmt::Display;
use std::rc::Rc;

Expand All @@ -23,6 +24,10 @@ pub struct Span {
pub end: u32,
}

struct Documentation {
summary: String,
}

pub(crate) fn get_hover(
compilation: &Compilation,
source_name: &str,
Expand Down Expand Up @@ -188,18 +193,36 @@ impl Visitor<'_> for HoverVisitor<'_> {
}

fn markdown_with_doc(doc: &Rc<str>, code: impl Display) -> String {
if doc.is_empty() {
let parsed_doc = parse_doc(doc);
if parsed_doc.summary.is_empty() {
markdown_fenced_block(code)
} else {
format!(
"{}
{}",
doc,
parsed_doc.summary,
markdown_fenced_block(code)
)
}
}

fn parse_doc(doc: &str) -> Documentation {
let re = Regex::new(r"(?mi)(?:^# Summary$)([\s\S]*?)(?:(^# .*)|\z)").expect("Invalid regex");
let summary = match re.captures(doc) {
Some(captures) => {
let capture = captures
.get(1)
.expect("Didn't find the capture for the given regex");
capture.as_str()
}
None => doc,
}
.trim()
.to_string();

Documentation { summary }
}

fn markdown_fenced_block(code: impl Display) -> String {
format!(
"```qsharp
Expand Down
173 changes: 171 additions & 2 deletions language_service/src/hover/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,13 @@ fn hover_callable_unit_types() {
check(
indoc! {r#"
namespace Test {
/// Doc comment!
/// Doc comment
/// with multiple lines!
operation ◉B↘ar◉() : Unit {}
}
"#},
&expect![[r#"
"Doc comment!\n```qsharp\noperation Bar Unit => Unit\n```\n"
"Doc comment\nwith multiple lines!\n```qsharp\noperation Bar Unit => Unit\n```\n"
"#]],
);
}
Expand Down Expand Up @@ -516,3 +517,171 @@ fn hover_foreign_call_functors() {
"#]],
);
}

#[test]
fn hover_callable_summary() {
check(
indoc! {r#"
namespace Test {
/// # Summary
/// This is a
/// multi-line summary!
operation ◉F↘oo◉() : Unit {}
}
"#},
&expect![[r#"
"This is a\nmulti-line summary!\n```qsharp\noperation Foo Unit => Unit\n```\n"
"#]],
);
}

#[test]
fn hover_callable_summary_stuff_before() {
check(
indoc! {r#"
namespace Test {
/// not the summary
/// # Summary
/// This is a
/// multi-line summary!
operation ◉F↘oo◉() : Unit {}
}
"#},
&expect![[r#"
"This is a\nmulti-line summary!\n```qsharp\noperation Foo Unit => Unit\n```\n"
"#]],
);
}

#[test]
fn hover_callable_summary_other_header_before() {
check(
indoc! {r#"
namespace Test {
/// # Not The Summary
/// This stuff is not the summary.
/// # Summary
/// This is a
/// multi-line summary!
operation ◉F↘oo◉() : Unit {}
}
"#},
&expect![[r#"
"This is a\nmulti-line summary!\n```qsharp\noperation Foo Unit => Unit\n```\n"
"#]],
);
}

#[test]
fn hover_callable_summary_other_header_after() {
check(
indoc! {r#"
namespace Test {
/// # Summary
/// This is a
/// multi-line summary!
/// # Not The Summary
/// This stuff is not the summary.
operation ◉F↘oo◉() : Unit {}
}
"#},
&expect![[r#"
"This is a\nmulti-line summary!\n```qsharp\noperation Foo Unit => Unit\n```\n"
"#]],
);
}

#[test]
fn hover_callable_summary_other_headers() {
check(
indoc! {r#"
namespace Test {
/// # Not The Summary
/// This stuff is not the summary.
/// # Summary
/// This is a
/// multi-line summary!
/// # Also Not The Summary
/// This stuff is also not the summary.
operation ◉F↘oo◉() : Unit {}
}
"#},
&expect![[r#"
"This is a\nmulti-line summary!\n```qsharp\noperation Foo Unit => Unit\n```\n"
"#]],
);
}

#[test]
fn hover_callable_headers_but_no_summary() {
check(
indoc! {r#"
namespace Test {
/// # Not The Summary
/// This stuff is not the summary.
/// # Also Not The Summary
/// This stuff is also not the summary.
operation ◉F↘oo◉() : Unit {}
}
"#},
&expect![[r##"
"# Not The Summary\nThis stuff is not the summary.\n# Also Not The Summary\nThis stuff is also not the summary.\n```qsharp\noperation Foo Unit => Unit\n```\n"
"##]],
);
}

#[test]
fn hover_callable_summary_only_header_matches() {
check(
indoc! {r#"
namespace Test {
/// # Not The Summary
/// This stuff is not the # Summary.
/// # Summary
/// This is a
/// multi-line # Summary!
/// # Also Not The Summary
/// This stuff is also not the # Summary.
operation ◉F↘oo◉() : Unit {}
}
"#},
&expect![[r#"
"This is a\nmulti-line # Summary!\n```qsharp\noperation Foo Unit => Unit\n```\n"
"#]],
);
}

#[test]
fn hover_callable_summary_successive_headers() {
check(
indoc! {r#"
namespace Test {
/// # Not The Summary
/// # Summary
/// This is a
/// multi-line summary!
operation ◉F↘oo◉() : Unit {}
}
"#},
&expect![[r#"
"This is a\nmulti-line summary!\n```qsharp\noperation Foo Unit => Unit\n```\n"
"#]],
);
}

#[test]
fn hover_callable_empty_summary() {
check(
indoc! {r#"
namespace Test {
/// # Not The Summary
/// # Summary
/// # Also Not The Summary
operation ◉F↘oo◉() : Unit {}
}
"#},
&expect![[r##"
"```qsharp\noperation Foo Unit => Unit\n```\n"
"##]],
);
}
34 changes: 8 additions & 26 deletions language_service/src/qsc_utils.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

use qsc::hir::visit::Visitor;
use qsc::hir::{Item, ItemId, LocalItemId, PackageId};
use qsc::hir::{Item, ItemId, PackageId};
use qsc::{
compile::{self, Error},
PackageStore, SourceMap,
Expand Down Expand Up @@ -46,32 +45,15 @@ pub(crate) fn map_offset(source_map: &SourceMap, source_name: &str, source_offse
}

pub(crate) fn find_item<'a>(compilation: &'a Compilation, id: &ItemId) -> Option<&'a Item> {
let mut finder_pass = FindItem {
id: &id.item,
item: None,
};
let package = if let Some(package_id) = id.package {
&compilation
.package_store
.get(package_id)
.unwrap_or_else(|| panic!("bad package id: {package_id}"))
.package
match &compilation.package_store.get(package_id) {
Some(compilation) => &compilation.package,
None => {
return None;
}
}
} else {
&compilation.unit.package
};
finder_pass.visit_package(package);
finder_pass.item
}

struct FindItem<'a, 'b> {
pub id: &'a LocalItemId,
pub item: Option<&'b Item>,
}

impl<'a, 'b> Visitor<'b> for FindItem<'a, 'b> {
fn visit_item(&mut self, item: &'b Item) {
if item.id == *self.id {
self.item = Some(item);
}
}
package.items.get(id.item)
}
4 changes: 3 additions & 1 deletion library/std/arithmetic.qs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ namespace Microsoft.Quantum.Arithmetic {

/// # Summary
/// Reversible, in-place ripple-carry addition of two integers.
///
/// # Description
/// Given two $n$-bit integers encoded in LittleEndian registers `xs` and `ys`,
/// and a qubit carry, the operation computes the sum of the two integers
/// where the $n$ least significant bits of the result are held in `ys` and
Expand Down Expand Up @@ -274,7 +276,7 @@ namespace Microsoft.Quantum.Arithmetic {
Fact(Length(xs) > 0, "Array should not be empty.");


let nQubits = Length(xs);
let nQubits = Length(xs);
for idx in 0..nQubits - 2 {
CCNOT(xs[idx], ys[idx], xs[idx+1]);
}
Expand Down
Loading

0 comments on commit 53267ee

Please sign in to comment.