Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: use clap_mangen and roff to generate manpage #231

Merged
merged 1 commit into from
Oct 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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");
}
Loading