mirror of
https://github.com/chmln/sd
synced 2024-11-25 04:30:17 +00:00
docs: use clap_mangen and roff to generate manpage (#231)
This commit is contained in:
parent
05ee953299
commit
4ed733834f
5 changed files with 101 additions and 82 deletions
29
Cargo.lock
generated
29
Cargo.lock
generated
|
@ -183,6 +183,16 @@ version = "0.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
|
||||
|
||||
[[package]]
|
||||
name = "clap_mangen"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b44f35c514163027542f7147797ff930523eea288e03642727348ef1a9666f6b"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"roff",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.0"
|
||||
|
@ -397,15 +407,6 @@ version = "0.4.20"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||
|
||||
[[package]]
|
||||
name = "man"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebf5fa795187a80147b1ac10aaedcf5ffd3bbeb1838bda61801a1c9ad700a1c9"
|
||||
dependencies = [
|
||||
"roff",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.6.3"
|
||||
|
@ -554,9 +555,9 @@ checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
|
|||
|
||||
[[package]]
|
||||
name = "roff"
|
||||
version = "0.1.0"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e33e4fb37ba46888052c763e4ec2acfedd8f00f62897b630cadb6298b833675e"
|
||||
checksum = "b833d8d034ea094b1ea68aa6d5c740e0d04bad9d16568d08ba6f76823a114316"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
|
@ -608,6 +609,7 @@ dependencies = [
|
|||
"anyhow",
|
||||
"assert_cmd",
|
||||
"clap",
|
||||
"clap_mangen",
|
||||
"globwalk",
|
||||
"ignore",
|
||||
"is-terminal",
|
||||
|
@ -837,9 +839,10 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
|||
|
||||
[[package]]
|
||||
name = "xtask"
|
||||
version = "0.1.0"
|
||||
version = "0.7.6"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"clap_complete",
|
||||
"man",
|
||||
"clap_mangen",
|
||||
"roff",
|
||||
]
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[workspace]
|
||||
package.version = "0.7.6"
|
||||
members = [
|
||||
".",
|
||||
"xtask",
|
||||
|
@ -6,7 +7,7 @@ members = [
|
|||
|
||||
[package]
|
||||
name = "sd"
|
||||
version = "0.7.6"
|
||||
version.workspace = true
|
||||
edition = "2018"
|
||||
authors = ["Gregory <gregory.mkv@gmail.com>"]
|
||||
description = "An intuitive find & replace CLI"
|
||||
|
@ -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
|
||||
|
|
|
@ -2,6 +2,7 @@ use clap::Parser;
|
|||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(
|
||||
name = "sd",
|
||||
author,
|
||||
version,
|
||||
about,
|
||||
|
@ -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>,
|
||||
|
@ -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>,
|
||||
}
|
||||
|
|
|
@ -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
xtask/src/gen.rs
136
xtask/src/gen.rs
|
@ -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");
|
||||
|
@ -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 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 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");
|
||||
|
||||
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 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");
|
||||
|
||||
std::fs::write(man_path, page).unwrap();
|
||||
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, buffer).expect("failed to write manpage");
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue