Format text output look like man

With this patch, we change our rich and plain viewer implementations to
format their output similar to the output of man.  This means:
- Adding a title with the current crate, documentation item and
  “rusty-man”
- Printing headings at indent levels 0, 3, 6 and printing content with
  indent 6, 12
- Printing headings bold and uppercase (level 1) or bold (levels 2, 3)
master
Robin Krahl 4 years ago
parent 162cae5bee
commit fc7d2c7ee9
No known key found for this signature in database
GPG Key ID: 8E9B0870524F69D8

@ -205,7 +205,7 @@ fn get_methods(
while let Some(subheading) = &next {
if is_element(subheading, &local_name!("h3")) && has_class(subheading, "impl") {
if let Some(title_element) = subheading.first_child() {
let title = get_html(&title_element)?;
let title = title_element.text_contents();
next = subheading.next_sibling();
if let Some(impl_items) = &next {
if is_element(impl_items, &local_name!("div"))
@ -234,7 +234,7 @@ fn get_methods(
let heading = select_first(document, "#deref-methods")?;
if let Some(heading) = heading {
let title = get_html(heading.as_node())?;
let title = heading.as_node().text_contents();
if let Some(impl_items) = heading.as_node().next_sibling() {
let group = get_method_group(
parent,

@ -11,9 +11,11 @@ use crate::doc;
use crate::viewer;
pub trait Printer: fmt::Debug {
fn print_heading(&self, level: usize, s: &str) -> io::Result<()>;
fn print_title(&self, left: &str, middle: &str, right: &str) -> io::Result<()>;
fn print_html(&self, s: &str) -> io::Result<()>;
fn print_heading(&self, indent: usize, level: usize, s: &str) -> io::Result<()>;
fn print_html(&self, indent: usize, s: &str, show_links: bool) -> io::Result<()>;
fn println(&self) -> io::Result<()>;
}
@ -29,41 +31,33 @@ impl<P: Printer> TextViewer<P> {
}
fn print_doc(&self, doc: &doc::Doc) -> io::Result<()> {
let title = format!("{} {}", doc.ty.name(), doc.name.as_ref());
self.printer
.print_heading(1, &format!("{} {}", doc.ty.name(), doc.name.as_ref()))?;
self.print_opt(doc.definition.as_deref())?;
self.print_opt(doc.description.as_deref())?;
.print_title(doc.name.krate(), &title, "rusty-man")?;
self.print_opt("SYNOPSIS", doc.definition.as_deref(), false)?;
self.print_opt("DESCRIPTION", doc.description.as_deref(), true)?;
for (ty, groups) in &doc.groups {
self.printer.println()?;
self.printer.print_heading(2, ty.group_name())?;
self.print_heading(1, ty.group_name())?;
for group in groups {
if let Some(title) = &group.title {
self.printer.println()?;
self.printer.print_heading(3, title)?;
self.print_heading(2, title)?;
}
if doc.ty == doc::ItemType::Module {
self.print_list(group.members.iter().map(|i| {
if let Some(description) = &i.description {
format!("{}<br/>{}", i.name.last(), description)
} else {
i.name.last().to_owned()
}
}))?;
} else {
for member in &group.members {
for member in &group.members {
// TODO: use something link strip_prefix instead of last()
self.print_heading(3, member.name.last())?;
if let Some(definition) = &member.definition {
self.printer.print_html(12, definition, false)?;
}
if member.definition.is_some() && member.description.is_some() {
self.printer.println()?;
}
if let Some(description) = &member.description {
self.printer.print_html(12, description, true)?;
}
if member.definition.is_some() || member.description.is_some() {
self.printer.println()?;
self.printer.print_heading(4, member.name.last())?;
if let Some(definition) = &member.definition {
self.printer.print_html(definition)?;
}
if member.definition.is_some() && member.description.is_some() {
self.printer.println()?;
}
if let Some(description) = &member.description {
self.printer.print_html(description)?;
}
}
}
}
@ -71,25 +65,27 @@ impl<P: Printer> TextViewer<P> {
Ok(())
}
fn print_opt(&self, s: Option<&str>) -> io::Result<()> {
fn print_opt(&self, title: &str, s: Option<&str>, show_links: bool) -> io::Result<()> {
if let Some(s) = s {
self.printer.println()?;
self.printer.print_html(s)
self.print_heading(1, title)?;
self.printer.print_html(6, s, show_links)?;
self.printer.println()
} else {
Ok(())
}
}
fn print_list<I, D>(&self, items: I) -> io::Result<()>
where
I: Iterator<Item = D>,
D: fmt::Display,
{
let html = items
.map(|i| format!("<li>{}</li>", i))
.collect::<Vec<_>>()
.join("");
self.printer.print_html(&format!("<ul>{}</ul>", html))
fn print_heading(&self, level: usize, s: &str) -> io::Result<()> {
let text = match level {
1 => std::borrow::Cow::from(s.to_uppercase()),
_ => std::borrow::Cow::from(s),
};
let indent = match level {
1 => 0,
2 => 3,
_ => 6,
};
self.printer.print_heading(indent, level, text.as_ref())
}
}
@ -129,3 +125,30 @@ fn ignore_pipe_error(error: io::Error) -> io::Result<()> {
Err(error)
}
}
pub fn print_title(line_length: usize, left: &str, middle: &str, right: &str) -> io::Result<()> {
use io::Write;
write!(io::stdout(), "{}", left)?;
let mut idx = left.len();
let middle_idx = line_length / 2;
let offset = middle.len() / 2;
let spacing = if idx + offset >= middle_idx {
1
} else {
middle_idx - offset - idx
};
write!(io::stdout(), "{}{}", " ".repeat(spacing), middle)?;
idx += middle.len() + spacing;
let end_idx = line_length;
let offset = right.len();
let spacing = if idx + offset >= end_idx {
1
} else {
end_idx - offset - idx
};
writeln!(io::stdout(), "{}{}", " ".repeat(spacing), right)
}

@ -15,6 +15,7 @@ pub struct PlainTextRenderer {
struct Decorator {
links: Vec<String>,
ignore_next_link: bool,
show_links: bool,
}
impl PlainTextRenderer {
@ -26,20 +27,25 @@ impl PlainTextRenderer {
}
impl super::Printer for PlainTextRenderer {
fn print_html(&self, s: &str) -> io::Result<()> {
writeln!(
io::stdout(),
"{}",
html2text::from_read_with_decorator(s.as_bytes(), self.line_length, Decorator::new())
)
fn print_title(&self, left: &str, middle: &str, right: &str) -> io::Result<()> {
super::print_title(self.line_length, left, middle, right)?;
writeln!(io::stdout())
}
fn print_heading(&self, level: usize, s: &str) -> io::Result<()> {
self.print_html(&format!(
"<h{level}>{text}</h{level}>",
level = level,
text = s
))
fn print_html(&self, indent: usize, s: &str, show_links: bool) -> io::Result<()> {
let lines = html2text::from_read_with_decorator(
s.as_bytes(),
self.line_length - indent,
Decorator::new(show_links),
);
for line in lines.trim().split('\n') {
writeln!(io::stdout(), "{}{}", " ".repeat(indent), line)?;
}
Ok(())
}
fn print_heading(&self, indent: usize, _level: usize, s: &str) -> io::Result<()> {
writeln!(io::stdout(), "{}{}", " ".repeat(indent), s)
}
fn println(&self) -> io::Result<()> {
@ -48,16 +54,18 @@ impl super::Printer for PlainTextRenderer {
}
impl Decorator {
pub fn new() -> Self {
pub fn new(show_links: bool) -> Self {
Self {
links: Vec::new(),
ignore_next_link: false,
show_links,
}
}
fn show_link(&self, url: &str) -> bool {
// only show absolute links -- local links are most likely not helpful
(url.starts_with("http") || url.starts_with("https")) &&
self.show_links &&
// only show absolute links -- local links are most likely not helpful
(url.starts_with("http") || url.starts_with("https")) &&
// ignore playground links -- typically, these links are too long to display in a
// sensible fasshion
!url.starts_with("http://play.rust-lang.org") &&
@ -129,6 +137,6 @@ impl text_renderer::TextDecorator for Decorator {
}
fn make_subblock_decorator(&self) -> Self {
Decorator::new()
Decorator::new(self.show_links)
}
}

@ -29,9 +29,21 @@ impl RichTextRenderer {
}
impl super::Printer for RichTextRenderer {
fn print_html(&self, s: &str) -> io::Result<()> {
let lines = html2text::from_read_rich(s.as_bytes(), self.line_length);
fn print_title(&self, left: &str, middle: &str, right: &str) -> io::Result<()> {
write!(io::stdout(), "{}", termion::style::Bold)?;
super::print_title(self.line_length, left, middle, right)?;
writeln!(io::stdout(), "{}", termion::style::Reset)
}
fn print_html(&self, indent: usize, s: &str, _show_links: bool) -> io::Result<()> {
let indent = if indent >= self.line_length / 2 {
0
} else {
indent
};
let lines = html2text::from_read_rich(s.as_bytes(), self.line_length - indent);
for line in lines {
write!(io::stdout(), "{}", " ".repeat(indent))?;
for element in line.iter() {
if let text_renderer::TaggedLineElement::Str(ts) = element {
self.render_string(ts)?;
@ -42,16 +54,12 @@ impl super::Printer for RichTextRenderer {
Ok(())
}
fn print_heading(&self, level: usize, s: &str) -> io::Result<()> {
if level < 4 {
write!(io::stdout(), "{}", termion::style::Bold)?;
}
let heading = format!("<h{level}>{text}</h{level}>", level = level, text = s);
self.print_html(&heading)?;
if level < 4 {
write!(io::stdout(), "{}", termion::style::Reset)?;
}
Ok(())
fn print_heading(&self, indent: usize, level: usize, s: &str) -> io::Result<()> {
let text = match level {
1..=3 => format!("{}{}{}", termion::style::Bold, s, termion::style::Reset),
_ => s.to_owned(),
};
writeln!(io::stdout(), "{}{}", " ".repeat(indent), &text)
}
fn println(&self) -> io::Result<()> {

Loading…
Cancel
Save