Skip to content

Commit

Permalink
docs: use clap_mangen and roff to generate manpage (#231)
Browse files Browse the repository at this point in the history
  • Loading branch information
nc7s authored Oct 2, 2023
1 parent 05ee953 commit 4ed7338
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 82 deletions.
29 changes: 16 additions & 13 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
[workspace]
package.version = "0.7.6"
members = [
".",
"xtask",
]

[package]
name = "sd"
version = "0.7.6"
version.workspace = true
edition = "2018"
authors = ["Gregory <gregory.mkv@gmail.com>"]
description = "An intuitive find & replace CLI"
Expand All @@ -33,6 +34,7 @@ clap = { version = "4.4.3", features = ["derive", "deprecated", "wrap_help"] }
[dev-dependencies]
assert_cmd = "2.0.12"
anyhow = "1.0.75"
clap_mangen = "0.2.14"

[profile.release]
opt-level = 3
Expand Down
9 changes: 8 additions & 1 deletion src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use clap::Parser;

#[derive(Parser, Debug)]
#[command(
name = "sd",
author,
version,
about,
Expand Down Expand Up @@ -40,10 +41,15 @@ pub struct Options {
/** Regex flags. May be combined (like `-f mc`).
c - case-sensitive
e - disable multi-line matching
i - case-insensitive
m - multi-line matching
s - make `.` match newlines
w - match full words only
*/
pub flags: Option<String>,
Expand All @@ -56,7 +62,8 @@ w - match full words only
pub replace_with: String,

/// The path to file(s). This is optional - sd can also read from STDIN.
///{n}{n}Note: sd modifies files in-place by default. See documentation for
///
/// Note: sd modifies files in-place by default. See documentation for
/// examples.
pub files: Vec<std::path::PathBuf>,
}
Expand Down
5 changes: 3 additions & 2 deletions xtask/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
[package]
name = "xtask"
version = "0.1.0"
version.workspace = true
edition = "2021"
publish = false

[dependencies]
clap = "4.4.3"
clap_complete = "4.4.1"
man = "0.3.0"
clap_mangen = "0.2.14"
roff = "0.2.1"
136 changes: 71 additions & 65 deletions xtask/src/gen.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
include!("../../src/cli.rs");
mod sd {
include!("../../src/cli.rs");
}
use sd::Options;

use std::{fs, path::Path};

use clap::{CommandFactory, ValueEnum};
use clap_complete::{generate_to, Shell};
use man::prelude::*;
use roff::{bold, roman, Roff};

pub fn gen() {
let gen_dir = Path::new("gen");
Expand All @@ -24,70 +27,73 @@ fn gen_shell(base_dir: &Path) {

fn gen_man(base_dir: &Path) {
let man_path = base_dir.join("sd.1");
let cmd = Options::command();
let mut buffer: Vec<u8> = Vec::new();

let man = clap_mangen::Man::new(cmd);
man.render_title(&mut buffer)
.expect("failed to render title section");
man.render_name_section(&mut buffer)
.expect("failed to render name section");
man.render_synopsis_section(&mut buffer)
.expect("failed to render synopsis section");
man.render_description_section(&mut buffer)
.expect("failed to render description section");
man.render_options_section(&mut buffer)
.expect("failed to render options section");

let page = Manual::new("sd")
.flag(
Flag::new()
.short("-p")
.long("--preview")
.help("Emit the replacement to STDOUT"),
)
.flag(
Flag::new()
.short("-s")
.long("--string-mode")
.help("Treat expressions as non-regex strings."),
)
.flag(Flag::new().short("-f").long("--flags").help(
r#"Regex flags. May be combined (like `-f mc`).
let statuses = [
("0", "Successful program execution."),
("1", "Unsuccessful program execution."),
("101", "The program panicked."),
];
let mut sect = Roff::new();
sect.control("SH", ["EXIT STATUS"]);
for (code, reason) in statuses {
sect.control("IP", [code]).text([roman(reason)]);
}
sect.to_writer(&mut buffer)
.expect("failed to render exit status section");

c - case-sensitive
i - case-insensitive
m - multi-line matching
w - match full words only
"#,
))
.arg(Arg::new("find"))
.arg(Arg::new("replace_with"))
.arg(Arg::new("[FILES]"))
.example(
Example::new()
.text("String-literal mode")
.command(
"echo 'lots((([]))) of special chars' | sd -s '((([])))' \
''",
)
.output("lots of special chars"),
)
.example(
Example::new()
.text("Regex use. Let's trim some trailing whitespace")
.command("echo 'lorem ipsum 23 ' | sd '\\s+$' ''")
.output("lorem ipsum 23"),
)
.example(
Example::new()
.text("Indexed capture groups")
.command(r#"echo 'cargo +nightly watch' | sd '(\w+)\s+\+(\w+)\s+(\w+)' 'cmd: $1, channel: $2, subcmd: $3'"#)
.output("cmd: cargo, channel: nightly, subcmd: watch")
)
.example(
Example::new()
.text("Named capture groups")
.command(r#"echo "123.45" | sd '(?P<dollars>\d+)\.(?P<cents>\d+)' '$dollars dollars and $cents cents'"#)
.output("123 dollars and 45 cents")
)
.example(
Example::new()
.text("Find & replace in file")
.command(r#"sd 'window.fetch' 'fetch' http.js"#)
)
.example(
Example::new()
.text("Find & replace from STDIN an emit to STDOUT")
.command(r#"sd 'window.fetch' 'fetch' < http.js"#)
)
.render();
let examples = [
// (description, command, result), result can be empty
(
"String-literal mode",
"echo 'lots((([]))) of special chars' | sd -s '((([])))'",
"lots of special chars",
),
(
"Regex use. Let's trim some trailing whitespace",
"echo 'lorem ipsum 23 ' | sd '\\s+$' ''",
"lorem ipsum 23",
),
(
"Indexed capture groups",
r"echo 'cargo +nightly watch' | sd '(\w+)\s+\+(\w+)\s+(\w+)' 'cmd: $1, channel: $2, subcmd: $3'",
"123 dollars and 45 cents",
),
(
"Find & replace in file",
r#"sd 'window.fetch' 'fetch' http.js"#,
"",
),
(
"Find & replace from STDIN an emit to STDOUT",
r#"sd 'window.fetch' 'fetch' < http.js"#,
"",
),
];
let mut sect = Roff::new();
sect.control("SH", ["EXAMPLES"]);
for (desc, command, result) in examples {
sect.control("TP", [])
.text([roman(desc)])
.text([bold(format!("$ {}", command))])
.control("br", [])
.text([roman(result)]);
}
sect.to_writer(&mut buffer)
.expect("failed to render example section");

std::fs::write(man_path, page).unwrap();
std::fs::write(man_path, buffer).expect("failed to write manpage");
}

0 comments on commit 4ed7338

Please sign in to comment.