List members in module documentation
This patch adds support for listing the module members on its documentation page. This is quite easy as the items are displayed in a single table per item type.
This commit is contained in:
parent
3d4fac0d5c
commit
d0c73d2523
32
src/doc.rs
32
src/doc.rs
@ -1,6 +1,7 @@
|
||||
// SPDX-FileCopyrightText: 2020 Robin Krahl <robin.krahl@ireas.org>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use std::fmt;
|
||||
use std::path;
|
||||
|
||||
use crate::parser;
|
||||
@ -23,6 +24,7 @@ pub struct Doc {
|
||||
pub title: String,
|
||||
pub description: Option<String>,
|
||||
pub definition: Option<String>,
|
||||
pub members: Vec<(String, Vec<Doc>)>,
|
||||
}
|
||||
|
||||
impl Crate {
|
||||
@ -31,10 +33,9 @@ impl Crate {
|
||||
}
|
||||
|
||||
pub fn find_item(&self, item: &[&str]) -> anyhow::Result<Option<Item>> {
|
||||
let name = item.join("::");
|
||||
// TODO: add crate to name?
|
||||
let (name, full_name) = self.get_names(item);
|
||||
parser::find_item(self.path.join("all.html"), &name)
|
||||
.map(|o| o.map(|s| Item::new(name, self.path.join(path::PathBuf::from(s)), None)))
|
||||
.map(|o| o.map(|s| Item::new(full_name, self.path.join(path::PathBuf::from(s)), None)))
|
||||
}
|
||||
|
||||
pub fn find_module(&self, item: &[&str]) -> Option<Item> {
|
||||
@ -43,7 +44,8 @@ impl Crate {
|
||||
.join(path::PathBuf::from(item.join("/")))
|
||||
.join("index.html");
|
||||
if path.is_file() {
|
||||
Some(Item::new(item.join("::"), path, None))
|
||||
let (_, full_name) = self.get_names(item);
|
||||
Some(Item::new(full_name, path, None))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -58,6 +60,16 @@ impl Crate {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn get_names(&self, item: &[&str]) -> (String, String) {
|
||||
let name = item.join("::");
|
||||
let full_name = if item.is_empty() {
|
||||
self.name.clone()
|
||||
} else {
|
||||
format!("{}::{}", &self.name, &name)
|
||||
};
|
||||
(name, full_name)
|
||||
}
|
||||
}
|
||||
|
||||
impl Item {
|
||||
@ -69,7 +81,7 @@ impl Item {
|
||||
if let Some(member) = &self.member {
|
||||
parser::parse_member_doc(&self.path, &self.name, member)
|
||||
} else {
|
||||
parser::parse_item_doc(&self.path)
|
||||
parser::parse_item_doc(&self.path, &self.name)
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,3 +107,13 @@ impl Doc {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Doc {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if let Some(description) = &self.description {
|
||||
write!(f, "{}: {}", &self.title, description)
|
||||
} else {
|
||||
write!(f, "{}", &self.title)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -126,7 +126,7 @@ impl Index {
|
||||
// Skip associated types (== item type 16)
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
let full_path = match item.parent {
|
||||
Some(idx) => {
|
||||
let parent = &data.paths[idx].1;
|
||||
|
@ -59,7 +59,23 @@ fn select_first(
|
||||
select(element, selector).map(|mut i| i.next())
|
||||
}
|
||||
|
||||
pub fn parse_item_doc<P: AsRef<path::Path>>(path: P) -> anyhow::Result<doc::Doc> {
|
||||
const ITEM_MEMBERS: &[(&str, &str)] = &[
|
||||
("Extern Crates", "extern-crates"),
|
||||
("Imports", "imports"),
|
||||
("Primitives", "primitives"),
|
||||
("Modules", "modules"),
|
||||
("Macros", "macros"),
|
||||
("Structs", "structs"),
|
||||
("Enums", "enums"),
|
||||
("Constants", "constants"),
|
||||
("Statics", "statics"),
|
||||
("Traits", "traits"),
|
||||
("Functions", "functions"),
|
||||
("Typedefs", "typedefs"),
|
||||
("Unions", "unions"),
|
||||
];
|
||||
|
||||
pub fn parse_item_doc<P: AsRef<path::Path>>(path: P, name: &str) -> anyhow::Result<doc::Doc> {
|
||||
let document = parse_file(path)?;
|
||||
let heading = select_first(&document, ".fqn .in-band")?.context("Could not find heading")?;
|
||||
let definition = select_first(&document, ".docblock.type-decl")?;
|
||||
@ -68,6 +84,12 @@ pub fn parse_item_doc<P: AsRef<path::Path>>(path: P) -> anyhow::Result<doc::Doc>
|
||||
let mut doc = doc::Doc::new(get_html(heading.as_node())?);
|
||||
doc.description = description.map(|n| get_html(n.as_node())).transpose()?;
|
||||
doc.definition = definition.map(|n| get_html(n.as_node())).transpose()?;
|
||||
for (heading, id) in ITEM_MEMBERS {
|
||||
let members = get_members(&document, name, id)?;
|
||||
if !members.is_empty() {
|
||||
doc.members.push((heading.to_string(), members));
|
||||
}
|
||||
}
|
||||
Ok(doc)
|
||||
}
|
||||
|
||||
@ -91,6 +113,23 @@ pub fn parse_member_doc<P: AsRef<path::Path>>(
|
||||
Ok(doc)
|
||||
}
|
||||
|
||||
fn get_members(
|
||||
document: &kuchiki::NodeRef,
|
||||
base_name: &str,
|
||||
id: &str,
|
||||
) -> anyhow::Result<Vec<doc::Doc>> {
|
||||
let mut members: Vec<doc::Doc> = Vec::new();
|
||||
if let Some(table) = select_first(document, &format!("#{} + table", id))? {
|
||||
// On module pages, the members are listed in tables
|
||||
let items = select(table.as_node(), "td:first-child :first-child")?;
|
||||
for item in items {
|
||||
let name = format!("{}::{}", base_name, get_html(item.as_node())?);
|
||||
members.push(doc::Doc::new(name))
|
||||
}
|
||||
}
|
||||
Ok(members)
|
||||
}
|
||||
|
||||
fn get_member(
|
||||
document: &kuchiki::NodeRef,
|
||||
name: &str,
|
||||
@ -129,7 +168,7 @@ mod tests {
|
||||
fn test_parse_item_doc() {
|
||||
let path = crate::tests::ensure_docs();
|
||||
let path = path.join("kuchiki").join("struct.NodeRef.html");
|
||||
let doc = super::parse_item_doc(&path).unwrap();
|
||||
let doc = super::parse_item_doc(&path, "kuchiki::NodeRef").unwrap();
|
||||
|
||||
assert_eq!(
|
||||
"<span class=\"in-band\">\
|
||||
|
@ -1,6 +1,7 @@
|
||||
// SPDX-FileCopyrightText: 2020 Robin Krahl <robin.krahl@ireas.org>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use std::fmt;
|
||||
use std::io::{self, Write};
|
||||
|
||||
use html2text::render::text_renderer;
|
||||
@ -26,6 +27,13 @@ impl RichViewer {
|
||||
self.print_heading(&doc.title, 1)?;
|
||||
self.print_opt(doc.definition.as_deref())?;
|
||||
self.print_opt(doc.description.as_deref())?;
|
||||
for (heading, items) in &doc.members {
|
||||
if !items.is_empty() {
|
||||
writeln!(io::stdout())?;
|
||||
self.print_heading(heading, 2)?;
|
||||
self.print_list(items)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -58,6 +66,15 @@ impl RichViewer {
|
||||
write!(io::stdout(), "{}", termion::style::Reset)
|
||||
}
|
||||
|
||||
fn print_list(&self, items: &[impl fmt::Display]) -> io::Result<()> {
|
||||
let html = items
|
||||
.iter()
|
||||
.map(|i| format!("<li>{}</li>", i))
|
||||
.collect::<Vec<_>>()
|
||||
.join("");
|
||||
self.print(&format!("<ul>{}</ul>", html))
|
||||
}
|
||||
|
||||
fn render_string(&self, ts: &RichString) -> io::Result<()> {
|
||||
let start_style = get_style(ts, get_start_style);
|
||||
let end_style = get_style(ts, get_end_style);
|
||||
|
@ -1,6 +1,8 @@
|
||||
// SPDX-FileCopyrightText: 2020 Robin Krahl <robin.krahl@ireas.org>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use html2text::render::text_renderer;
|
||||
|
||||
use crate::doc;
|
||||
@ -35,15 +37,37 @@ impl TextViewer {
|
||||
self.print(s);
|
||||
}
|
||||
}
|
||||
|
||||
fn print_heading(&self, s: &str, level: usize) {
|
||||
let prefix = "#".repeat(level);
|
||||
print!("{} ", prefix);
|
||||
self.print(s);
|
||||
}
|
||||
|
||||
fn print_list(&self, items: &[impl fmt::Display]) {
|
||||
let html = items
|
||||
.iter()
|
||||
.map(|i| format!("<li>{}</li>", i))
|
||||
.collect::<Vec<_>>()
|
||||
.join("");
|
||||
self.print(&format!("<ul>{}</ul>", html));
|
||||
}
|
||||
}
|
||||
|
||||
impl viewer::Viewer for TextViewer {
|
||||
fn open(&self, doc: &doc::Doc) -> anyhow::Result<()> {
|
||||
viewer::spawn_pager();
|
||||
|
||||
self.print(&doc.title);
|
||||
self.print_heading(&doc.title, 1);
|
||||
self.print_opt(doc.definition.as_deref());
|
||||
self.print_opt(doc.description.as_deref());
|
||||
for (heading, items) in &doc.members {
|
||||
if !items.is_empty() {
|
||||
println!();
|
||||
self.print_heading(heading, 2);
|
||||
self.print_list(items);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user