clap subcommands
This commit is contained in:
parent
adea668355
commit
a2f207272a
2 changed files with 77 additions and 3 deletions
|
|
@ -717,6 +717,65 @@ let parse_manpage_string contents =
|
|||
| Some d -> d | None -> "" in
|
||||
{ result with description }
|
||||
|
||||
(* --- Clap-style SUBCOMMAND section extraction --- *)
|
||||
(* Manpages generated by clap (Rust) put each subcommand under its own
|
||||
.SH SUBCOMMAND header with a Usage: line giving the name. *)
|
||||
|
||||
let extract_subcommand_sections contents =
|
||||
let lines = String.split_on_char '\n' contents in
|
||||
let classified = List.map classify_line lines in
|
||||
(* Split into sections at .SH boundaries *)
|
||||
let rec collect_sections acc current_name current_lines = function
|
||||
| [] ->
|
||||
let acc = match current_name with
|
||||
| Some n -> (n, List.rev current_lines) :: acc
|
||||
| None -> acc in
|
||||
List.rev acc
|
||||
| Macro ("SH", args) :: rest ->
|
||||
let acc = match current_name with
|
||||
| Some n -> (n, List.rev current_lines) :: acc
|
||||
| None -> acc in
|
||||
let name = String.uppercase_ascii (String.trim args) in
|
||||
if name = "SUBCOMMAND" || name = "SUBCOMMANDS" then
|
||||
collect_sections acc (Some name) [] rest
|
||||
else
|
||||
collect_sections acc None [] rest
|
||||
| line :: rest ->
|
||||
collect_sections acc current_name (line :: current_lines) rest
|
||||
in
|
||||
let sections = collect_sections [] None [] classified in
|
||||
(* For each SUBCOMMAND section, extract name from Usage: line and parse entries *)
|
||||
let usage_re = Str.regexp {|Usage: \([a-zA-Z0-9_-]+\)|} in
|
||||
List.filter_map (fun (_header, section_lines) ->
|
||||
(* Find subcommand name from Usage: line *)
|
||||
let name = ref None in
|
||||
let desc_lines = ref [] in
|
||||
List.iter (fun line ->
|
||||
if !name = None then
|
||||
match line with
|
||||
| Text s ->
|
||||
if try ignore (Str.search_forward usage_re s 0); true
|
||||
with Not_found -> false
|
||||
then name := Some (Str.matched_group 1 s)
|
||||
else desc_lines := s :: !desc_lines
|
||||
| Macro (("TP" | "B" | "BI" | "BR"), args) ->
|
||||
let s = strip_inline_macro_args args |> strip_groff_escapes |> String.trim in
|
||||
if try ignore (Str.search_forward usage_re s 0); true
|
||||
with Not_found -> false
|
||||
then name := Some (Str.matched_group 1 s)
|
||||
| _ -> ()
|
||||
) section_lines;
|
||||
match !name with
|
||||
| None -> None
|
||||
| Some subcmd_name ->
|
||||
let entries = extract_entries section_lines in
|
||||
let desc = String.concat " " (List.rev !desc_lines)
|
||||
|> strip_groff_escapes |> String.trim in
|
||||
(* Remove backtick quoting common in clap output *)
|
||||
let desc = Str.global_replace (Str.regexp "`\\([^`]*\\)`") "\\1" desc in
|
||||
Some (subcmd_name, desc, { entries; subcommands = []; positionals = []; description = desc })
|
||||
) sections
|
||||
|
||||
let read_manpage_file path =
|
||||
if Filename.check_suffix path ".gz" then begin
|
||||
let ic = Gzip.open_in path in
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue