Skip to content

Commit

Permalink
Merge pull request uutils#4158 from tertsdiepraam/markdown-in-docs-v1
Browse files Browse the repository at this point in the history
Markdown in docs v1
  • Loading branch information
sylvestre authored Dec 2, 2022
2 parents 403780a + b632ba8 commit 3ca6139
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 56 deletions.
58 changes: 58 additions & 0 deletions src/bin/uudoc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,23 @@ 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),
command: command(),
name,
tldr_zip: &mut tldr_zip,
utils_per_platform: &utils_per_platform,
markdown,
}
.markdown()?;
println!("Wrote to '{}'", p);
Expand All @@ -115,6 +125,7 @@ struct MDWriter<'a, 'b> {
name: &'a str,
tldr_zip: &'b mut Option<ZipArchive<File>>,
utils_per_platform: &'b HashMap<&'b str, Vec<String>>,
markdown: Option<String>,
}

impl<'a, 'b> MDWriter<'a, 'b> {
Expand All @@ -124,6 +135,7 @@ impl<'a, 'b> MDWriter<'a, 'b> {
self.usage()?;
self.description()?;
self.options()?;
self.after_help()?;
self.examples()
}

Expand Down Expand Up @@ -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()
Expand All @@ -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) =
Expand Down Expand Up @@ -295,6 +327,32 @@ impl<'a, 'b> MDWriter<'a, 'b> {
}
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, &section))
.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> {
Expand Down
62 changes: 32 additions & 30 deletions src/uu/expr/expr.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## About

Print the value of EXPRESSION to standard output
Print the value of `EXPRESSION` to standard output

## Usage
```
Expand All @@ -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"
- `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
46 changes: 23 additions & 23 deletions src/uu/numfmt/numfmt.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

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
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

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.
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.
4 changes: 2 additions & 2 deletions src/uu/numfmt/src/numfmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Item = &'a str>, options: &NumfmtOptions) -> UResult<()> {
Expand Down Expand Up @@ -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)
Expand Down
17 changes: 16 additions & 1 deletion src/uucore_procs/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Copyright (C) ~ Roy Ivy III <[email protected]>; MIT license
// spell-checker:ignore backticks

extern crate proc_macro;
use std::{fs::File, io::Read, path::PathBuf};
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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(&section, &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`.
Expand Down

0 comments on commit 3ca6139

Please sign in to comment.