diff --git a/src/bin/uudoc.rs b/src/bin/uudoc.rs index 874bdc201..fe28323f2 100644 --- a/src/bin/uudoc.rs +++ b/src/bin/uudoc.rs @@ -91,6 +91,15 @@ fn main() -> io::Result<()> { continue; } 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) { MDWriter { w: Box::new(f), @@ -98,6 +107,7 @@ fn main() -> io::Result<()> { name, tldr_zip: &mut tldr_zip, utils_per_platform: &utils_per_platform, + markdown, } .markdown()?; println!("Wrote to '{}'", p); @@ -115,6 +125,7 @@ struct MDWriter<'a, 'b> { name: &'a str, tldr_zip: &'b mut Option>, utils_per_platform: &'b HashMap<&'b str, Vec>, + markdown: Option, } impl<'a, 'b> MDWriter<'a, 'b> { @@ -124,6 +135,7 @@ impl<'a, 'b> MDWriter<'a, 'b> { self.usage()?; self.description()?; self.options()?; + self.after_help()?; self.examples() } @@ -184,6 +196,10 @@ impl<'a, 'b> MDWriter<'a, 'b> { } 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 .command .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<()> { if let Some(zip) = self.tldr_zip { let content = if let Some(f) = @@ -295,6 +327,32 @@ impl<'a, 'b> MDWriter<'a, 'b> { } writeln!(self.w, "\n") } + + fn markdown_section(&self, section: &str) -> Option { + 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::>() + .join("\n") + .trim() + .to_string(); + + if result != "" { + Some(result) + } else { + None + } + } } fn get_zip_content(archive: &mut ZipArchive, name: &str) -> Option { diff --git a/src/uu/expr/expr.md b/src/uu/expr/expr.md index 1faa88781..6c881eb35 100644 --- a/src/uu/expr/expr.md +++ b/src/uu/expr/expr.md @@ -2,7 +2,7 @@ ## About -Print the value of EXPRESSION to standard output +Print the value of `EXPRESSION` to standard output ## Usage ``` @@ -12,48 +12,50 @@ expr [OPTIONS] ## After help -Print the value of EXPRESSION to standard output. A blank line below -separates increasing precedence groups. EXPRESSION may be: +Print the value of `EXPRESSION` to standard output. A blank line below +separates increasing precedence groups. - ARG1 | ARG2 ARG1 if it is neither null nor 0, otherwise ARG2 +`EXPRESSION` may be: - ARG1 & ARG2 ARG1 if neither argument is null or 0, otherwise 0 + ARG1 | ARG2 ARG1 if it is neither null nor 0, otherwise ARG2 - ARG1 < ARG2 ARG1 is less than ARG2 - ARG1 <= ARG2 ARG1 is less than or equal to ARG2 - ARG1 = ARG2 ARG1 is equal to ARG2 - ARG1 != ARG2 ARG1 is unequal to ARG2 - ARG1 >= ARG2 ARG1 is greater than or equal to ARG2 - ARG1 > ARG2 ARG1 is greater than ARG2 + ARG1 & ARG2 ARG1 if neither argument is null or 0, otherwise 0 - ARG1 + ARG2 arithmetic sum of ARG1 and ARG2 - ARG1 - ARG2 arithmetic difference of ARG1 and ARG2 + ARG1 < ARG2 ARG1 is less than ARG2 + ARG1 <= ARG2 ARG1 is less than or equal to ARG2 + ARG1 = ARG2 ARG1 is equal to ARG2 + ARG1 != ARG2 ARG1 is unequal to ARG2 + ARG1 >= ARG2 ARG1 is greater than or equal to ARG2 + ARG1 > ARG2 ARG1 is greater than ARG2 - ARG1 * ARG2 arithmetic product of ARG1 and ARG2 - ARG1 / ARG2 arithmetic quotient of ARG1 divided by ARG2 - ARG1 % ARG2 arithmetic remainder of ARG1 divided by ARG2 + ARG1 + ARG2 arithmetic sum of ARG1 and ARG2 + ARG1 - ARG2 arithmetic difference of ARG1 and ARG2 - STRING : REGEXP anchored pattern match of REGEXP in STRING + ARG1 * ARG2 arithmetic product of ARG1 and ARG2 + ARG1 / ARG2 arithmetic quotient of ARG1 divided by ARG2 + ARG1 % ARG2 arithmetic remainder of ARG1 divided by ARG2 - match STRING REGEXP same as STRING : REGEXP - substr STRING POS LENGTH substring of STRING, POS counted from 1 - index STRING CHARS index in STRING where any CHARS is found, or 0 - length STRING length of STRING - + TOKEN interpret TOKEN as a string, even if it is a - keyword like 'match' or an operator like '/' + STRING : REGEXP anchored pattern match of REGEXP in STRING - ( EXPRESSION ) value of EXPRESSION + match STRING REGEXP same as STRING : REGEXP + substr STRING POS LENGTH substring of STRING, POS counted from 1 + index STRING CHARS index in STRING where any CHARS is found, or 0 + length STRING length of STRING + + TOKEN interpret TOKEN as a string, even if it is a + keyword like 'match' or an operator like '/' + + ( EXPRESSION ) value of EXPRESSION Beware that many operators need to be escaped or quoted for shells. Comparisons are arithmetic if both ARGs are numbers, else lexicographical. Pattern matches return the string matched between \( and \) or null; if \( and \) are not used, they return the number of characters matched or 0. -Exit status is 0 if EXPRESSION is neither null nor 0, 1 if EXPRESSION is null -or 0, 2 if EXPRESSION is syntactically invalid, and 3 if an error occurred. +Exit status is `0` if `EXPRESSION` is neither null nor `0`, `1` if `EXPRESSION` is null +or `0`, `2` if `EXPRESSION` is syntactically invalid, and `3` if an error occurred. Environment variables: - * EXPR_DEBUG_TOKENS=1 dump expression's tokens - * EXPR_DEBUG_RPN=1 dump expression represented in reverse polish notation - * EXPR_DEBUG_SYA_STEP=1 dump each parser step - * EXPR_DEBUG_AST=1 dump expression represented abstract syntax tree" \ No newline at end of file + - `EXPR_DEBUG_TOKENS=1`: dump expression's tokens + - `EXPR_DEBUG_RPN=1`: dump expression represented in reverse polish notation + - `EXPR_DEBUG_SYA_STEP=1`: dump each parser step + - `EXPR_DEBUG_AST=1`: dump expression represented abstract syntax tree \ No newline at end of file diff --git a/src/uu/numfmt/numfmt.md b/src/uu/numfmt/numfmt.md index 52bd743d8..3216d6eb7 100644 --- a/src/uu/numfmt/numfmt.md +++ b/src/uu/numfmt/numfmt.md @@ -10,38 +10,38 @@ numfmt [OPTION]... [NUMBER]... Convert numbers from/to human-readable strings -## Long Help +## After Help -UNIT options: - none no auto-scaling is done; suffixes will trigger an error +`UNIT` options: + - `none`: no auto-scaling is done; suffixes will trigger an error + - `auto`: accept optional single/two letter suffix: - auto accept optional single/two letter suffix: + 1K = 1000, 1Ki = 1024, 1M = 1000000, 1Mi = 1048576, - 1K = 1000, 1Ki = 1024, 1M = 1000000, 1Mi = 1048576, + - `si`: accept optional single letter suffix: - si accept optional single letter suffix: + 1K = 1000, 1M = 1000000, ... - 1K = 1000, 1M = 1000000, ... + - `iec`: accept optional single letter suffix: - iec accept optional single letter suffix: + 1K = 1024, 1M = 1048576, ... - 1K = 1024, 1M = 1048576, ... +- `iec-i`: accept optional two-letter suffix: - iec-i accept optional two-letter suffix: + 1Ki = 1024, 1Mi = 1048576, ... - 1Ki = 1024, 1Mi = 1048576, ... +`FIELDS` supports `cut(1)` style field ranges: + + N N'th field, counted from 1 + N- from N'th field, to end of line + N-M from N'th to M'th field (inclusive) + -M from first to M'th field (inclusive) + - all fields -FIELDS supports cut(1) style field ranges: - N N'th field, counted from 1 - N- from N'th field, to end of line - N-M from N'th to M'th field (inclusive) - -M from first to M'th field (inclusive) - - all fields Multiple fields/ranges can be separated with commas -FORMAT must be suitable for printing one floating-point argument '%f'. -Optional quote (%'f) will enable --grouping (if supported by current locale). -Optional width value (%10f) will pad output. Optional zero (%010f) width -will zero pad the number. Optional negative values (%-10f) will left align. -Optional precision (%.1f) will override the input determined precision. - +`FORMAT` must be suitable for printing one floating-point argument `%f`. +Optional quote (`%'f`) will enable --grouping (if supported by current locale). +Optional width value (`%10f`) will pad output. Optional zero (`%010f`) width +will zero pad the number. Optional negative values (`%-10f`) will left align. +Optional precision (`%.1f`) will override the input determined precision. diff --git a/src/uu/numfmt/src/numfmt.rs b/src/uu/numfmt/src/numfmt.rs index 1e826e68e..80b604e6d 100644 --- a/src/uu/numfmt/src/numfmt.rs +++ b/src/uu/numfmt/src/numfmt.rs @@ -24,7 +24,7 @@ pub mod options; mod units; const ABOUT: &str = help_section!("about", "numfmt.md"); -const LONG_HELP: &str = help_section!("long help", "numfmt.md"); +const AFTER_HELP: &str = help_section!("after help", "numfmt.md"); const USAGE: &str = help_usage!("numfmt.md"); fn handle_args<'a>(args: impl Iterator, options: &NumfmtOptions) -> UResult<()> { @@ -262,7 +262,7 @@ pub fn uu_app() -> Command { Command::new(uucore::util_name()) .version(crate_version!()) .about(ABOUT) - .after_help(LONG_HELP) + .after_help(AFTER_HELP) .override_usage(format_usage(USAGE)) .allow_negative_numbers(true) .infer_long_args(true) diff --git a/src/uucore_procs/src/lib.rs b/src/uucore_procs/src/lib.rs index 32a247309..89ac1a749 100644 --- a/src/uucore_procs/src/lib.rs +++ b/src/uucore_procs/src/lib.rs @@ -1,4 +1,5 @@ // Copyright (C) ~ Roy Ivy III ; MIT license +// spell-checker:ignore backticks extern crate proc_macro; use std::{fs::File, io::Read, path::PathBuf}; @@ -37,6 +38,19 @@ pub fn main(_args: TokenStream, stream: TokenStream) -> TokenStream { 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. /// /// 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 filename = get_argument(&input, 1, "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`.