Skip to content

Commit

Permalink
feat(html): extract JSDoc summary/title from jsdoc body (#623)
Browse files Browse the repository at this point in the history
Co-authored-by: Yoshiya Hinosawa <[email protected]>
  • Loading branch information
crowlKats and kt3k authored Aug 28, 2024
1 parent b14e8ad commit c9af343
Show file tree
Hide file tree
Showing 10 changed files with 112 additions and 78 deletions.
96 changes: 49 additions & 47 deletions src/html/jsdoc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,19 +205,15 @@ fn parse_links<'a>(md: &'a str, ctx: &RenderContext) -> Cow<'a, str> {
})
}

fn split_markdown_title(
md: &str,
prefer_title: bool,
) -> (Option<&str>, Option<&str>) {
fn split_markdown_title(md: &str) -> (Option<&str>, Option<&str>) {
let newline = md.find("\n\n").unwrap_or(usize::MAX);
let codeblock = md.find("```").unwrap_or(usize::MAX);

let index = newline.min(codeblock).min(md.len());

match md.split_at(index) {
("", body) => (None, Some(body)),
(title, "") if prefer_title => (Some(title), None),
(title, "") if !prefer_title => (None, Some(title)),
(title, "") => (None, Some(title)),
(title, body) => (Some(title), Some(body)),
}
}
Expand Down Expand Up @@ -393,6 +389,34 @@ fn walk_node<'a>(
}
}

fn walk_node_title<'a>(node: &'a AstNode<'a>) {
for child in node.children() {
if matches!(
child.data.borrow().value,
NodeValue::Document
| NodeValue::Paragraph
| NodeValue::Heading(_)
| NodeValue::Text(_)
| NodeValue::Code(_)
| NodeValue::HtmlInline(_)
| NodeValue::Emph
| NodeValue::Strong
| NodeValue::Strikethrough
| NodeValue::Superscript
| NodeValue::Link(_)
| NodeValue::Math(_)
| NodeValue::Escaped
| NodeValue::WikiLink(_)
| NodeValue::Underline
) {
walk_node_title(child);
} else {
// delete the node
child.detach();
}
}
}

fn render_node<'a>(
node: &'a AstNode<'a>,
options: &comrak::Options,
Expand All @@ -404,8 +428,7 @@ fn render_node<'a>(
}

pub struct MarkdownToHTMLOptions {
pub summary: bool,
pub summary_prefer_title: bool,
pub title_only: bool,
pub no_toc: bool,
}

Expand Down Expand Up @@ -474,7 +497,7 @@ pub fn markdown_to_html(

let mut plugins = comrak::Plugins::default();

if !render_options.summary {
if !render_options.title_only {
plugins.render.codefence_syntax_highlighter =
Some(&render_ctx.ctx.highlight_adapter);
if !render_options.no_toc {
Expand All @@ -484,31 +507,28 @@ pub fn markdown_to_html(

let md = parse_links(md, render_ctx);

let md = if render_options.summary {
let (title, _body) =
split_markdown_title(&md, render_options.summary_prefer_title);
title.unwrap_or_default()
} else {
md.as_ref()
};

if md.is_empty() {
return None;
}

let class_name = if render_options.summary {
let class_name = if render_options.title_only {
"markdown_summary"
} else {
"markdown"
};

let html = {
let arena = Arena::new();
let root = comrak::parse_document(&arena, md, &options);
let root = comrak::parse_document(&arena, &md, &options);

walk_node(&arena, root, &options, &plugins);
if render_options.title_only {
walk_node_title(root);

render_node(root, &options, &plugins)
if let Some(child) = root.first_child() {
render_node(child, &options, &plugins)
} else {
return None;
}
} else {
walk_node(&arena, root, &options, &plugins);
render_node(root, &options, &plugins)
}
};

#[cfg(feature = "ammonia")]
Expand All @@ -533,22 +553,6 @@ pub fn markdown_to_html(
}
}

pub(crate) fn render_markdown_summary(
render_ctx: &RenderContext,
md: &str,
) -> String {
markdown_to_html(
render_ctx,
md,
MarkdownToHTMLOptions {
summary: true,
summary_prefer_title: true,
no_toc: false,
},
)
.unwrap_or_default()
}

pub(crate) fn render_markdown(
render_ctx: &RenderContext,
md: &str,
Expand All @@ -558,8 +562,7 @@ pub(crate) fn render_markdown(
render_ctx,
md,
MarkdownToHTMLOptions {
summary: false,
summary_prefer_title: false,
title_only: false,
no_toc,
},
)
Expand All @@ -576,8 +579,7 @@ pub(crate) fn jsdoc_body_to_html(
ctx,
doc,
MarkdownToHTMLOptions {
summary,
summary_prefer_title: true,
title_only: summary,
no_toc: false,
},
)
Expand Down Expand Up @@ -632,14 +634,14 @@ impl ExampleCtx {
pub fn new(render_ctx: &RenderContext, example: &str, i: usize) -> Self {
let id = name_to_id("example", &i.to_string());

let (maybe_title, body) = split_markdown_title(example, false);
let (maybe_title, body) = split_markdown_title(example);
let title = if let Some(title) = maybe_title {
title.to_string()
} else {
format!("Example {}", i + 1)
};

let markdown_title = render_markdown_summary(render_ctx, &title);
let markdown_title = render_markdown(render_ctx, &title, false);
let markdown_body =
render_markdown(render_ctx, body.unwrap_or_default(), true);

Expand Down
3 changes: 1 addition & 2 deletions src/html/pages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,8 +298,7 @@ impl IndexCtx {
&render_ctx,
&doc,
super::jsdoc::MarkdownToHTMLOptions {
summary: false,
summary_prefer_title: false,
title_only: false,
no_toc: false,
},
)
Expand Down
3 changes: 1 addition & 2 deletions src/html/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -457,8 +457,7 @@ impl SectionHeaderCtx {
render_ctx,
doc,
MarkdownToHTMLOptions {
summary: true,
summary_prefer_title: true,
title_only: true,
no_toc: false,
},
)
Expand Down
13 changes: 12 additions & 1 deletion tests/snapshots/html_test__html_doc_files_rewrite-12.snap
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,18 @@ expression: "files.get(\"./~/Foobar.html\").unwrap()"
<div>
<div class="text-2xl leading-none break-all">
<span class="text-Class">class</span>&nbsp;<span class="font-bold">Foobar</span>
</div><div class="symbolSubtitle"></div></div></div><div><div class="space-y-7" id=""></div>
</div><div class="symbolSubtitle"></div></div></div><div><div class="space-y-7" id=""><div class="markdown"><pre class="highlight"><code>// This code block is ignored when getting the title of this doc
const foobar = new Foobar();
</code><button class="context_button" data-copy="// This code block is ignored when getting the title of this doc
const foobar = new Foobar();
"><svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="2" y="2" width="7" height="7" fill="none"></rect>
<rect x="6" y="6" width="7" height="7" fill="none"></rect>
<path d="M1.55566 2.7C1.55566 2.03726 2.09292 1.5 2.75566 1.5H8.75566C9.41841 1.5 9.95566 2.03726 9.95566 2.7V5.1H12.3557C13.0184 5.1 13.5557 5.63726 13.5557 6.3V12.3C13.5557 12.9627 13.0184 13.5 12.3557 13.5H6.35566C5.69292 13.5 5.15566 12.9627 5.15566 12.3V9.9H2.75566C2.09292 9.9 1.55566 9.36274 1.55566 8.7V2.7ZM6.35566 9.9V12.3H12.3557V6.3H9.95566V8.7C9.95566 9.36274 9.41841 9.9 8.75566 9.9H6.35566ZM8.75566 8.7V2.7H2.75566V8.7H8.75566Z" fill="#232323"></path>
</svg>
</button><code></code></pre>
<p>Foobar docs</p>
</div></div>
</div>
</article></main>
<div class="toc">
Expand Down
2 changes: 1 addition & 1 deletion tests/snapshots/html_test__html_doc_files_rewrite-21.snap
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ source: tests/html_test.rs
expression: "files.get(\"search_index.js\").unwrap()"
---
(function () {
window.DENO_DOC_SEARCH_INDEX = {"nodes":[{"kind":["class"],"name":"A","file":".","doc":"","location":{"filename":"default","line":45,"col":0,"byteIndex":741},"url":"././~/A.html","category":"","declarationKind":"private","deprecated":false},{"kind":["class"],"name":"B","file":".","doc":"","location":{"filename":"default","line":48,"col":0,"byteIndex":770},"url":"././~/B.html","category":"","declarationKind":"export","deprecated":false},{"kind":["class"],"name":"Bar","file":".","doc":"","location":{"filename":"default","line":31,"col":0,"byteIndex":588},"url":"././~/Bar.html","category":"","declarationKind":"export","deprecated":false},{"kind":["typeAlias"],"name":"Baz","file":".","doc":"","location":{"filename":"default","line":41,"col":0,"byteIndex":702},"url":"././~/Baz.html","category":"","declarationKind":"export","deprecated":false},{"kind":["class"],"name":"Foo","file":".","doc":"some Foo docs","location":{"filename":"default","line":25,"col":0,"byteIndex":488},"url":"././~/Foo.html","category":"","declarationKind":"export","deprecated":false},{"kind":["class"],"name":"Foobar","file":".","doc":"","location":{"filename":"default","line":34,"col":0,"byteIndex":622},"url":"././~/Foobar.html","category":"","declarationKind":"export","deprecated":false},{"kind":["interface"],"name":"Hello","file":".","doc":"","location":{"filename":"default","line":37,"col":0,"byteIndex":655},"url":"././~/Hello.html","category":"","declarationKind":"export","deprecated":false},{"kind":["function"],"name":"c","file":".","doc":"","location":{"filename":"default","line":57,"col":13,"byteIndex":933},"url":"././~/c.html","category":"","declarationKind":"export","deprecated":false},{"kind":["function"],"name":"d","file":".","doc":"","location":{"filename":"default","line":59,"col":0,"byteIndex":961},"url":"././~/d.html","category":"","declarationKind":"export","deprecated":false},{"kind":["function","function","function"],"name":"qaz","file":".","doc":"","location":{"filename":"default","line":53,"col":0,"byteIndex":812},"url":"././~/qaz.html","category":"","declarationKind":"export","deprecated":false},{"kind":["variable"],"name":"default","file":"foo","doc":"The default export item.\n\nThis item reproduces the issue reported in {@link https://github.com/jsr-io/jsr/issues/459}","location":{"filename":"foo","line":7,"col":6,"byteIndex":167},"url":"./foo/~/default.html","category":"","declarationKind":"export","deprecated":false},{"kind":["function"],"name":"x","file":"foo","doc":"","location":{"filename":"foo","line":1,"col":0,"byteIndex":0},"url":"./foo/~/x.html","category":"","declarationKind":"export","deprecated":false}]};
window.DENO_DOC_SEARCH_INDEX = {"nodes":[{"kind":["class"],"name":"A","file":".","doc":"","location":{"filename":"default","line":60,"col":0,"byteIndex":1006},"url":"././~/A.html","category":"","declarationKind":"private","deprecated":false},{"kind":["class"],"name":"B","file":".","doc":"","location":{"filename":"default","line":63,"col":0,"byteIndex":1035},"url":"././~/B.html","category":"","declarationKind":"export","deprecated":false},{"kind":["class"],"name":"Bar","file":".","doc":"> Some quote in bar docs\n> This quote part is ignored\n> when getting the title of this doc\n\nBar docs","location":{"filename":"default","line":38,"col":0,"byteIndex":711},"url":"././~/Bar.html","category":"","declarationKind":"export","deprecated":false},{"kind":["typeAlias"],"name":"Baz","file":".","doc":"","location":{"filename":"default","line":56,"col":0,"byteIndex":967},"url":"././~/Baz.html","category":"","declarationKind":"export","deprecated":false},{"kind":["class"],"name":"Foo","file":".","doc":"some Foo docs","location":{"filename":"default","line":25,"col":0,"byteIndex":488},"url":"././~/Foo.html","category":"","declarationKind":"export","deprecated":false},{"kind":["class"],"name":"Foobar","file":".","doc":"```ts\n// This code block is ignored when getting the title of this doc\nconst foobar = new Foobar();\n```\n\nFoobar docs","location":{"filename":"default","line":49,"col":0,"byteIndex":887},"url":"././~/Foobar.html","category":"","declarationKind":"export","deprecated":false},{"kind":["interface"],"name":"Hello","file":".","doc":"","location":{"filename":"default","line":52,"col":0,"byteIndex":920},"url":"././~/Hello.html","category":"","declarationKind":"export","deprecated":false},{"kind":["function"],"name":"c","file":".","doc":"","location":{"filename":"default","line":72,"col":13,"byteIndex":1198},"url":"././~/c.html","category":"","declarationKind":"export","deprecated":false},{"kind":["function"],"name":"d","file":".","doc":"","location":{"filename":"default","line":74,"col":0,"byteIndex":1226},"url":"././~/d.html","category":"","declarationKind":"export","deprecated":false},{"kind":["function","function","function"],"name":"qaz","file":".","doc":"","location":{"filename":"default","line":68,"col":0,"byteIndex":1077},"url":"././~/qaz.html","category":"","declarationKind":"export","deprecated":false},{"kind":["variable"],"name":"default","file":"foo","doc":"The default export item.\n\nThis item reproduces the issue reported in {@link https://github.com/jsr-io/jsr/issues/459}","location":{"filename":"foo","line":7,"col":6,"byteIndex":167},"url":"./foo/~/default.html","category":"","declarationKind":"export","deprecated":false},{"kind":["function"],"name":"x","file":"foo","doc":"","location":{"filename":"foo","line":1,"col":0,"byteIndex":0},"url":"./foo/~/x.html","category":"","declarationKind":"export","deprecated":false}]};
})()
8 changes: 7 additions & 1 deletion tests/snapshots/html_test__html_doc_files_rewrite-3.snap
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,13 @@ expression: "files.get(\"./~/Bar.html\").unwrap()"
<span class="text-Class">class</span>&nbsp;<span class="font-bold">Bar</span>
</div><div class="symbolSubtitle"><div>
<span class="type"> extends </span><a class="link" href="..&#x2F;.&#x2F;.&#x2F;~&#x2F;Foo.html">Foo</a><span> </span>
</div></div></div></div><div><div class="space-y-7" id=""></div>
</div></div></div></div><div><div class="space-y-7" id=""><div class="markdown"><blockquote>
<p>Some quote in bar docs
This quote part is ignored
when getting the title of this doc</p>
</blockquote>
<p>Bar docs</p>
</div></div>
</div>
</article></main>
<div class="toc">
Expand Down
6 changes: 4 additions & 2 deletions tests/snapshots/html_test__html_doc_files_rewrite.snap
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ expression: "files.get(\"./all_symbols.html\").unwrap()"
<div class="namespaceItemContent">
<a href=".&#x2F;.&#x2F;.&#x2F;~&#x2F;Bar.html" title="Bar">Bar</a>

<div><span class="italic">No documentation available</span></div>
<div><div class="markdown_summary"><p>Bar docs</p>
</div></div>
</div>
</div><div id="namespace_baz" class="namespaceItem" ><div class="docNodeKindIcon"><div class="text-TypeAlias bg-TypeAlias/15" title="Type Alias">T</div></div>
<div class="namespaceItemContent">
Expand Down Expand Up @@ -101,7 +102,8 @@ expression: "files.get(\"./all_symbols.html\").unwrap()"
<div class="namespaceItemContent">
<a href=".&#x2F;.&#x2F;.&#x2F;~&#x2F;Foobar.html" title="Foobar">Foobar</a>

<div><span class="italic">No documentation available</span></div>
<div><div class="markdown_summary"><p>Foobar docs</p>
</div></div>
</div>
</div><div id="namespace_hello" class="namespaceItem" ><div class="docNodeKindIcon"><div class="text-Interface bg-Interface/15" title="Interface">I</div></div>
<div class="namespaceItemContent">
Expand Down
4 changes: 2 additions & 2 deletions tests/snapshots/html_test__symbol_group.snap
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ expression: files
"kind": "other",
"value": {
"id": "",
"docs": null,
"docs": "<div class=\"markdown\"><blockquote>\n<p>Some quote in bar docs\nThis quote part is ignored\nwhen getting the title of this doc</p>\n</blockquote>\n<p>Bar docs</p>\n</div>",
"sections": []
}
}
Expand Down Expand Up @@ -556,7 +556,7 @@ expression: files
"kind": "other",
"value": {
"id": "",
"docs": null,
"docs": "<div class=\"markdown\"><pre class=\"highlight\"><code>// This code block is ignored when getting the title of this doc\nconst foobar = new Foobar();\n</code><button class=\"context_button\" data-copy=\"// This code block is ignored when getting the title of this doc\nconst foobar = new Foobar();\n\"><svg width=\"15\" height=\"15\" viewBox=\"0 0 15 15\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n<rect x=\"2\" y=\"2\" width=\"7\" height=\"7\" fill=\"none\"></rect>\n<rect x=\"6\" y=\"6\" width=\"7\" height=\"7\" fill=\"none\"></rect>\n<path d=\"M1.55566 2.7C1.55566 2.03726 2.09292 1.5 2.75566 1.5H8.75566C9.41841 1.5 9.95566 2.03726 9.95566 2.7V5.1H12.3557C13.0184 5.1 13.5557 5.63726 13.5557 6.3V12.3C13.5557 12.9627 13.0184 13.5 12.3557 13.5H6.35566C5.69292 13.5 5.15566 12.9627 5.15566 12.3V9.9H2.75566C2.09292 9.9 1.55566 9.36274 1.55566 8.7V2.7ZM6.35566 9.9V12.3H12.3557V6.3H9.95566V8.7C9.95566 9.36274 9.41841 9.9 8.75566 9.9H6.35566ZM8.75566 8.7V2.7H2.75566V8.7H8.75566Z\" fill=\"#232323\"></path>\n</svg>\n</button><code></code></pre>\n<p>Foobar docs</p>\n</div>",
"sections": []
}
}
Expand Down
40 changes: 20 additions & 20 deletions tests/snapshots/html_test__symbol_search.snap
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ expression: search_index
"doc": "",
"location": {
"filename": "default",
"line": 45,
"line": 60,
"col": 0,
"byteIndex": 741
"byteIndex": 1006
},
"url": "././~/A.html",
"category": "",
Expand All @@ -31,9 +31,9 @@ expression: search_index
"doc": "",
"location": {
"filename": "default",
"line": 48,
"line": 63,
"col": 0,
"byteIndex": 770
"byteIndex": 1035
},
"url": "././~/B.html",
"category": "",
Expand All @@ -46,12 +46,12 @@ expression: search_index
],
"name": "Bar",
"file": ".",
"doc": "",
"doc": "> Some quote in bar docs\n> This quote part is ignored\n> when getting the title of this doc\n\nBar docs",
"location": {
"filename": "default",
"line": 31,
"line": 38,
"col": 0,
"byteIndex": 588
"byteIndex": 711
},
"url": "././~/Bar.html",
"category": "",
Expand All @@ -67,9 +67,9 @@ expression: search_index
"doc": "",
"location": {
"filename": "default",
"line": 41,
"line": 56,
"col": 0,
"byteIndex": 702
"byteIndex": 967
},
"url": "././~/Baz.html",
"category": "",
Expand Down Expand Up @@ -100,12 +100,12 @@ expression: search_index
],
"name": "Foobar",
"file": ".",
"doc": "",
"doc": "```ts\n// This code block is ignored when getting the title of this doc\nconst foobar = new Foobar();\n```\n\nFoobar docs",
"location": {
"filename": "default",
"line": 34,
"line": 49,
"col": 0,
"byteIndex": 622
"byteIndex": 887
},
"url": "././~/Foobar.html",
"category": "",
Expand All @@ -121,9 +121,9 @@ expression: search_index
"doc": "",
"location": {
"filename": "default",
"line": 37,
"line": 52,
"col": 0,
"byteIndex": 655
"byteIndex": 920
},
"url": "././~/Hello.html",
"category": "",
Expand All @@ -139,9 +139,9 @@ expression: search_index
"doc": "",
"location": {
"filename": "default",
"line": 57,
"line": 72,
"col": 13,
"byteIndex": 933
"byteIndex": 1198
},
"url": "././~/c.html",
"category": "",
Expand All @@ -157,9 +157,9 @@ expression: search_index
"doc": "",
"location": {
"filename": "default",
"line": 59,
"line": 74,
"col": 0,
"byteIndex": 961
"byteIndex": 1226
},
"url": "././~/d.html",
"category": "",
Expand All @@ -177,9 +177,9 @@ expression: search_index
"doc": "",
"location": {
"filename": "default",
"line": 53,
"line": 68,
"col": 0,
"byteIndex": 812
"byteIndex": 1077
},
"url": "././~/qaz.html",
"category": "",
Expand Down
Loading

0 comments on commit c9af343

Please sign in to comment.