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

feat: Basic support for Darwin #17

Merged
merged 5 commits into from
Mar 19, 2024
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
420 changes: 153 additions & 267 deletions Cargo.lock

Large diffs are not rendered by default.

548 changes: 548 additions & 0 deletions samples/darwin/bless.8

Large diffs are not rendered by default.

3,659 changes: 3,659 additions & 0 deletions samples/darwin/brew.1

Large diffs are not rendered by default.

695 changes: 695 additions & 0 deletions samples/darwin/htop.1

Large diffs are not rendered by default.

46 changes: 46 additions & 0 deletions src/parse_man/darwin.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use regex::{Regex, RegexBuilder};

use super::util;
use crate::Flag;

/// For parsing Darwin man pages (ported from Fish)
#[allow(
clippy::case_sensitive_file_extension_comparisons,
clippy::doc_markdown
)]
pub fn parse(cmd_name: &str, page_text: &str) -> Vec<Flag> {
let Some(start_ind) = page_text.find(".Sh DESCRIPTION") else {
return Vec::new();
};

let mut flags = Vec::new();

// Can't use util::get_section because we also want sections after DESCRIPTION
let content = &page_text[start_ind..];

// Replace '.It Fl' and the like with '.It -' (`Fl` is a dash)
let fl_re = Regex::new(r"(\...) Fl ").expect("Regex should be valid");
let content = fl_re.replace_all(content, "$1 -").replace(".Fl ", "-");

let desc_end = RegexBuilder::new(r"\n\.El.*+")
.dot_matches_new_line(true)
.build()
.expect("Regex should be valid");

let mut paras = content.split(".It");
paras.next(); // Discard the part before the first option
for para in paras {
let mut pieces = para.splitn(2, '\n');
if let Some(options) = pieces.next() {
let desc = pieces.next().map(|desc| {
let desc = desc.replace(".Nm", cmd_name);
desc_end.replace(&desc, "").to_string()
});
if let Some(flag) = util::make_flag(options, desc.as_deref()) {
flags.push(flag);
}
}
}

flags
}
2 changes: 2 additions & 0 deletions src/parse_man/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//! For parsing command information from man pages
mod darwin;
pub mod error;
mod podman;
mod scdoc;
Expand Down Expand Up @@ -69,6 +70,7 @@ pub fn parse_manpage_text(
type4::parse(cmd_name, text),
scdoc::parse(cmd_name, text),
podman::parse(cmd_name, text),
darwin::parse(cmd_name, text),
]
.into_iter()
.flatten()
Expand Down
19 changes: 10 additions & 9 deletions src/parse_man/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ pub fn trim_desc(desc: &str) -> String {

/// Get the contents of a section with the given title
pub fn get_section(title: &str, text: &str) -> Option<String> {
// [hH] is necessary for Darwin
let re = RegexBuilder::new(&format!(r#"\.S[hH] {title}(.*?)(\.S[hH]|\z)"#))
let re = RegexBuilder::new(&format!(r#"\.SH {title}(.*?)(\.SH|\z)"#))
.multi_line(true)
.dot_matches_new_line(true)
.build()
Expand All @@ -35,14 +34,15 @@ pub fn remove_groff_formatting(data: &str) -> String {
.replace(r"\fB", "")
.replace(r"\fR", "")
.replace(r"\e", "");

// TODO check if this one is necessary
// also, fish uses a slightly different regex: `.PD( \d+)`, check if that's
// fine
let re = Regex::new(r"\.PD \d+").unwrap();
let data = re.replace_all(&data, "");

let data = data
.replace(".BI", "")
.replace(".BR", "")
.replace(".B", "")
.replace("0.5i", "")
.replace(".rb", "")
.replace(r"\^", "")
Expand All @@ -51,12 +51,13 @@ pub fn remove_groff_formatting(data: &str) -> String {
.replace(r"\ ", " ")
.replace(r"\-", "-")
.replace(r"\&", "")
.replace(".B", "")
.replace(r"\-", "-")
// .replace(".I", "") // This breaks podman since it removes .IX
.replace('\u{C}', "")
.replace(r"\(cq", "'")
.replace(r"\(aq", "'"); // Added by me, not from Fish. May need to remove all \(xx
.replace('\u{C}', "");

let quotes = Regex::new(r"\\\([ocadlr]q").unwrap();
let data = quotes.replace_all(&data, "'");

let data = data.replace(".Pp", ""); // Fish only replaces this one on MacOS

// todo Fish doesn't do this, see how it handles .sp
let re = Regex::new(&format!(r"\.sp( {NUM_RE}v?)?")).unwrap();
Expand Down
5 changes: 5 additions & 0 deletions tests/man_integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,8 @@ fn scdoc_sway_json() {
fn podman_ncdu_json() {
run_test("json", &["ncdu"], &["--cmds", "^ncdu"]);
}

#[test]
fn darwin_bless_json() {
run_test("json", &["bless"], &["--cmds", "^bless"]);
}
1 change: 1 addition & 0 deletions tests/resources/man/expected/bless.json

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

Loading
Loading