Skip to content

Commit

Permalink
feat: Basic support for Darwin (#17)
Browse files Browse the repository at this point in the history
* Update Cargo.lock

* Port Fish's Darwin parser

* Rename darwin_bless test to darwin_bless_json

* fix: Fix quote replacement
  • Loading branch information
ysthakur authored Mar 19, 2024
1 parent cac8ce8 commit 4a849ca
Show file tree
Hide file tree
Showing 11 changed files with 5,668 additions and 277 deletions.
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

0 comments on commit 4a849ca

Please sign in to comment.