mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-31 13:07:46 +00:00
uudoc: support after_help and read directly from markdown file
This commit is contained in:
parent
e307f624e8
commit
e155a5ea6d
2 changed files with 74 additions and 1 deletions
|
@ -91,6 +91,15 @@ fn main() -> io::Result<()> {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let p = format!("docs/src/utils/{}.md", name);
|
let p = format!("docs/src/utils/{}.md", name);
|
||||||
|
|
||||||
|
let markdown = File::open(format!("src/uu/{name}/{name}.md"))
|
||||||
|
.and_then(|mut f: File| {
|
||||||
|
let mut s = String::new();
|
||||||
|
f.read_to_string(&mut s)?;
|
||||||
|
Ok(s)
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
|
||||||
if let Ok(f) = File::create(&p) {
|
if let Ok(f) = File::create(&p) {
|
||||||
MDWriter {
|
MDWriter {
|
||||||
w: Box::new(f),
|
w: Box::new(f),
|
||||||
|
@ -98,6 +107,7 @@ fn main() -> io::Result<()> {
|
||||||
name,
|
name,
|
||||||
tldr_zip: &mut tldr_zip,
|
tldr_zip: &mut tldr_zip,
|
||||||
utils_per_platform: &utils_per_platform,
|
utils_per_platform: &utils_per_platform,
|
||||||
|
markdown,
|
||||||
}
|
}
|
||||||
.markdown()?;
|
.markdown()?;
|
||||||
println!("Wrote to '{}'", p);
|
println!("Wrote to '{}'", p);
|
||||||
|
@ -115,6 +125,7 @@ struct MDWriter<'a, 'b> {
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
tldr_zip: &'b mut Option<ZipArchive<File>>,
|
tldr_zip: &'b mut Option<ZipArchive<File>>,
|
||||||
utils_per_platform: &'b HashMap<&'b str, Vec<String>>,
|
utils_per_platform: &'b HashMap<&'b str, Vec<String>>,
|
||||||
|
markdown: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> MDWriter<'a, 'b> {
|
impl<'a, 'b> MDWriter<'a, 'b> {
|
||||||
|
@ -124,6 +135,7 @@ impl<'a, 'b> MDWriter<'a, 'b> {
|
||||||
self.usage()?;
|
self.usage()?;
|
||||||
self.description()?;
|
self.description()?;
|
||||||
self.options()?;
|
self.options()?;
|
||||||
|
self.after_help()?;
|
||||||
self.examples()
|
self.examples()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,6 +196,10 @@ impl<'a, 'b> MDWriter<'a, 'b> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn description(&mut self) -> io::Result<()> {
|
fn description(&mut self) -> io::Result<()> {
|
||||||
|
if let Some(after_help) = self.markdown_section("about") {
|
||||||
|
return writeln!(self.w, "\n\n{}", after_help);
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(about) = self
|
if let Some(about) = self
|
||||||
.command
|
.command
|
||||||
.get_long_about()
|
.get_long_about()
|
||||||
|
@ -195,6 +211,22 @@ impl<'a, 'b> MDWriter<'a, 'b> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn after_help(&mut self) -> io::Result<()> {
|
||||||
|
if let Some(after_help) = self.markdown_section("after help") {
|
||||||
|
return writeln!(self.w, "\n\n{}", after_help);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(after_help) = self
|
||||||
|
.command
|
||||||
|
.get_after_long_help()
|
||||||
|
.or_else(|| self.command.get_after_help())
|
||||||
|
{
|
||||||
|
writeln!(self.w, "\n\n{}", after_help)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn examples(&mut self) -> io::Result<()> {
|
fn examples(&mut self) -> io::Result<()> {
|
||||||
if let Some(zip) = self.tldr_zip {
|
if let Some(zip) = self.tldr_zip {
|
||||||
let content = if let Some(f) =
|
let content = if let Some(f) =
|
||||||
|
@ -295,6 +327,32 @@ impl<'a, 'b> MDWriter<'a, 'b> {
|
||||||
}
|
}
|
||||||
writeln!(self.w, "</dl>\n")
|
writeln!(self.w, "</dl>\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn markdown_section(&self, section: &str) -> Option<String> {
|
||||||
|
let md = self.markdown.as_ref()?;
|
||||||
|
let section = section.to_lowercase();
|
||||||
|
|
||||||
|
fn is_section_header(line: &str, section: &str) -> bool {
|
||||||
|
line.strip_prefix("##")
|
||||||
|
.map_or(false, |l| l.trim().to_lowercase() == section)
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = md
|
||||||
|
.lines()
|
||||||
|
.skip_while(|&l| !is_section_header(l, §ion))
|
||||||
|
.skip(1)
|
||||||
|
.take_while(|l| !l.starts_with("##"))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("\n")
|
||||||
|
.trim()
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
if result != "" {
|
||||||
|
Some(result)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_zip_content(archive: &mut ZipArchive<impl Read + Seek>, name: &str) -> Option<String> {
|
fn get_zip_content(archive: &mut ZipArchive<impl Read + Seek>, name: &str) -> Option<String> {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
// Copyright (C) ~ Roy Ivy III <rivy.dev@gmail.com>; MIT license
|
// Copyright (C) ~ Roy Ivy III <rivy.dev@gmail.com>; MIT license
|
||||||
|
// spell-checker:ignore backticks
|
||||||
|
|
||||||
extern crate proc_macro;
|
extern crate proc_macro;
|
||||||
use std::{fs::File, io::Read, path::PathBuf};
|
use std::{fs::File, io::Read, path::PathBuf};
|
||||||
|
@ -37,6 +38,19 @@ pub fn main(_args: TokenStream, stream: TokenStream) -> TokenStream {
|
||||||
TokenStream::from(new)
|
TokenStream::from(new)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: This is currently a stub. We could do much more here and could
|
||||||
|
// even pull in a full markdown parser to get better results.
|
||||||
|
/// Render markdown into a format that's easier to read in the terminal.
|
||||||
|
///
|
||||||
|
/// For now, all this function does is remove backticks.
|
||||||
|
/// Some ideas for future improvement:
|
||||||
|
/// - Render headings as bold
|
||||||
|
/// - Convert triple backticks to indented
|
||||||
|
/// - Printing tables in a nice format
|
||||||
|
fn render_markdown(s: &str) -> String {
|
||||||
|
s.replace('`', "")
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the usage from the "Usage" section in the help file.
|
/// Get the usage from the "Usage" section in the help file.
|
||||||
///
|
///
|
||||||
/// The usage is assumed to be surrounded by markdown code fences. It may span
|
/// The usage is assumed to be surrounded by markdown code fences. It may span
|
||||||
|
@ -81,7 +95,8 @@ pub fn help_section(input: TokenStream) -> TokenStream {
|
||||||
let section = get_argument(&input, 0, "section");
|
let section = get_argument(&input, 0, "section");
|
||||||
let filename = get_argument(&input, 1, "filename");
|
let filename = get_argument(&input, 1, "filename");
|
||||||
let text = parse_help(§ion, &filename);
|
let text = parse_help(§ion, &filename);
|
||||||
TokenTree::Literal(Literal::string(&text)).into()
|
let rendered = render_markdown(&text);
|
||||||
|
TokenTree::Literal(Literal::string(&rendered)).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an argument from the input vector of `TokenTree`.
|
/// Get an argument from the input vector of `TokenTree`.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue