mirror of
https://github.com/BurntSushi/ripgrep.git
synced 2025-11-23 21:54:45 +02:00
Maybe 2024 changes? Note that we now set `edition = "2024"` explicitly in `rustfmt.toml`. Without this, it seems like it's possible in some cases for rustfmt to run under an older edition's style. Not sure how though.
111 lines
3.6 KiB
Rust
111 lines
3.6 KiB
Rust
/*!
|
|
Provides routines for generating ripgrep's man page in `roff` format.
|
|
*/
|
|
|
|
use std::{collections::BTreeMap, fmt::Write};
|
|
|
|
use crate::flags::{Flag, defs::FLAGS, doc::version};
|
|
|
|
const TEMPLATE: &'static str = include_str!("template.rg.1");
|
|
|
|
/// Wraps `std::write!` and asserts there is no failure.
|
|
///
|
|
/// We only write to `String` in this module.
|
|
macro_rules! write {
|
|
($($tt:tt)*) => { std::write!($($tt)*).unwrap(); }
|
|
}
|
|
|
|
/// Wraps `std::writeln!` and asserts there is no failure.
|
|
///
|
|
/// We only write to `String` in this module.
|
|
macro_rules! writeln {
|
|
($($tt:tt)*) => { std::writeln!($($tt)*).unwrap(); }
|
|
}
|
|
|
|
/// Returns a `roff` formatted string corresponding to ripgrep's entire man
|
|
/// page.
|
|
pub(crate) fn generate() -> String {
|
|
let mut cats = BTreeMap::new();
|
|
for flag in FLAGS.iter().copied() {
|
|
let mut cat = cats.entry(flag.doc_category()).or_insert(String::new());
|
|
if !cat.is_empty() {
|
|
writeln!(cat, ".sp");
|
|
}
|
|
generate_flag(flag, &mut cat);
|
|
}
|
|
|
|
let mut out = TEMPLATE.replace("!!VERSION!!", &version::generate_digits());
|
|
for (cat, value) in cats.iter() {
|
|
let var = format!("!!{name}!!", name = cat.as_str());
|
|
out = out.replace(&var, value);
|
|
}
|
|
out
|
|
}
|
|
|
|
/// Writes `roff` formatted documentation for `flag` to `out`.
|
|
fn generate_flag(flag: &'static dyn Flag, out: &mut String) {
|
|
if let Some(byte) = flag.name_short() {
|
|
let name = char::from(byte);
|
|
write!(out, r"\fB\-{name}\fP");
|
|
if let Some(var) = flag.doc_variable() {
|
|
write!(out, r" \fI{var}\fP");
|
|
}
|
|
write!(out, r", ");
|
|
}
|
|
|
|
let name = flag.name_long().replace("-", r"\-");
|
|
write!(out, r"\fB\-\-{name}\fP");
|
|
if let Some(var) = flag.doc_variable() {
|
|
write!(out, r"=\fI{var}\fP");
|
|
}
|
|
write!(out, "\n");
|
|
|
|
writeln!(out, ".RS 4");
|
|
let doc = flag.doc_long().trim();
|
|
// Convert \flag{foo} into something nicer.
|
|
let doc = super::render_custom_markup(doc, "flag", |name, out| {
|
|
let Some(flag) = crate::flags::parse::lookup(name) else {
|
|
unreachable!(r"found unrecognized \flag{{{name}}} in roff docs")
|
|
};
|
|
out.push_str(r"\fB");
|
|
if let Some(name) = flag.name_short() {
|
|
write!(out, r"\-{}/", char::from(name));
|
|
}
|
|
write!(out, r"\-\-{}", flag.name_long().replace("-", r"\-"));
|
|
out.push_str(r"\fP");
|
|
});
|
|
// Convert \flag-negate{foo} into something nicer.
|
|
let doc = super::render_custom_markup(&doc, "flag-negate", |name, out| {
|
|
let Some(flag) = crate::flags::parse::lookup(name) else {
|
|
unreachable!(
|
|
r"found unrecognized \flag-negate{{{name}}} in roff docs"
|
|
)
|
|
};
|
|
let Some(name) = flag.name_negated() else {
|
|
let long = flag.name_long();
|
|
unreachable!(
|
|
"found \\flag-negate{{{long}}} in roff docs but \
|
|
{long} does not have a negation"
|
|
);
|
|
};
|
|
out.push_str(r"\fB");
|
|
write!(out, r"\-\-{name}");
|
|
out.push_str(r"\fP");
|
|
});
|
|
writeln!(out, "{doc}");
|
|
if let Some(negated) = flag.name_negated() {
|
|
// Flags that can be negated that aren't switches, like
|
|
// --context-separator, are somewhat weird. Because of that, the docs
|
|
// for those flags should discuss the semantics of negation explicitly.
|
|
// But for switches, the behavior is always the same.
|
|
if flag.is_switch() {
|
|
writeln!(out, ".sp");
|
|
writeln!(
|
|
out,
|
|
r"This flag can be disabled with \fB\-\-{negated}\fP."
|
|
);
|
|
}
|
|
}
|
|
writeln!(out, ".RE");
|
|
}
|