Skip to content

Commit b796fba

Browse files
committed
🔧 use forked clap-markdown
1 parent 63433b6 commit b796fba

3 files changed

Lines changed: 8 additions & 159 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

xtask/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ release = false
1313

1414
[dependencies]
1515
clap = { version = "4.5", features = ["derive"] }
16-
clap-markdown = "0.1"
16+
clap-markdown = { git = "https://github.com/ChanTsune/clap-markdown.git", branch = "issue/54" }
1717
clap_mangen = "0.2"
1818
portable-network-archive = { path = "../cli", default-features = false }

xtask/src/main.rs

Lines changed: 6 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::{fs, path::PathBuf, process};
22

3-
use clap::{CommandFactory, Parser, builder::Resettable};
3+
use clap::{CommandFactory, Parser};
44

55
fn main() {
66
if let Err(e) = run() {
@@ -64,169 +64,19 @@ fn mangen(args: MangenArgs) -> Result<(), Box<dyn std::error::Error>> {
6464
fn docgen(args: DocgenArgs) -> Result<(), Box<dyn std::error::Error>> {
6565
let out_path = &args.output;
6666

67-
// Create parent directory if it doesn't exist
68-
if let Some(parent) = out_path.parent() {
69-
fs::create_dir_all(parent)?;
70-
}
71-
7267
// Get the CLI command and rename to match the binary name
73-
let mut cmd = portable_network_archive::cli::Cli::command().name("pna");
74-
75-
// Build the command to propagate global arguments to subcommands.
76-
// This is necessary because clap-markdown iterates through subcommands
77-
// and global argument references need to be resolved first.
78-
cmd.build();
79-
80-
// After build(), clap sets display_name and bin_name on subcommands to include
81-
// the parent name (e.g., display_name="pna-create", bin_name="pna create").
82-
// This causes two problems in clap-markdown:
83-
// 1. Section headers show "pna pna-create" instead of "pna create"
84-
// (because display_name is used in path building)
85-
// 2. Usage lines show "pna pna create" instead of "pna create"
86-
// (because render_usage() uses bin_name, then clap-markdown prepends parent path)
87-
//
88-
// We need to clear display_name on all subcommands so clap-markdown
89-
// uses the command name instead of display_name for section headers.
90-
clear_display_names(&mut cmd);
68+
let cmd = portable_network_archive::cli::Cli::command().name("pna");
9169

9270
// Generate markdown documentation
9371
let markdown = clap_markdown::help_markdown_command(&cmd);
9472

95-
// Post-process to fix duplicate command paths.
96-
// After build(), clap's render_usage() includes the full path (e.g., "pna create"),
97-
// but clap-markdown also prepends the parent path, resulting in duplicates like:
98-
// - "pna pna create" (for top-level subcommands)
99-
// - "pna xattr pna xattr get" (for nested subcommands)
100-
// - "pna-pna create" (in TOC links/anchors)
101-
let markdown = fix_duplicate_command_paths(&markdown);
73+
// Create a parent directory if it doesn't exist
74+
if let Some(parent) = out_path.parent() {
75+
fs::create_dir_all(parent)?;
76+
}
10277

10378
fs::write(out_path, &markdown)?;
10479

10580
eprintln!("Markdown documentation generated: {}", out_path.display());
10681
Ok(())
10782
}
108-
109-
/// Recursively clear display_name from all subcommands.
110-
///
111-
/// After `Command::build()` is called, clap automatically sets display_name
112-
/// on subcommands (e.g., "pna-create"). This causes clap-markdown to generate
113-
/// incorrect section headers like "pna pna-create" instead of "pna create".
114-
fn clear_display_names(cmd: &mut clap::Command) {
115-
for sub in cmd.get_subcommands_mut() {
116-
let mut owned = std::mem::take(sub);
117-
owned = owned.display_name(Resettable::Reset);
118-
clear_display_names(&mut owned);
119-
*sub = owned;
120-
}
121-
}
122-
123-
/// Fix duplicate command paths in the generated markdown.
124-
///
125-
/// clap-markdown has a bug where it prepends the parent command path to the
126-
/// usage string, but clap's render_usage() already includes the full path.
127-
/// This results in duplicates like "pna xattr pna xattr get".
128-
///
129-
/// This function removes these duplicated path segments using simple string
130-
/// replacement, iterating until no more changes are made.
131-
fn fix_duplicate_command_paths(markdown: &str) -> String {
132-
let mut result = markdown.to_string();
133-
134-
// Keep replacing until no more changes
135-
loop {
136-
let prev = result.clone();
137-
138-
// Fix "pna pna " -> "pna " (simple duplicate)
139-
result = result.replace("pna pna ", "pna ");
140-
141-
// Fix "pna X pna X " patterns where X is a subcommand
142-
// We enumerate known subcommand prefixes that might be duplicated
143-
let subcommands = [
144-
"create",
145-
"append",
146-
"extract",
147-
"list",
148-
"split",
149-
"concat",
150-
"strip",
151-
"xattr",
152-
"complete",
153-
"bug-report",
154-
"experimental",
155-
"help",
156-
"stdio",
157-
"delete",
158-
"update",
159-
"chown",
160-
"chmod",
161-
"acl",
162-
"migrate",
163-
"chunk",
164-
"sort",
165-
"diff",
166-
"get",
167-
"set",
168-
];
169-
170-
for sub in &subcommands {
171-
// Fix patterns like "pna xattr pna xattr " -> "pna xattr "
172-
let dup_pattern = format!("pna {sub} pna {sub} ");
173-
let replacement = format!("pna {sub} ");
174-
result = result.replace(&dup_pattern, &replacement);
175-
176-
// Also handle end-of-command patterns (no trailing space)
177-
let dup_pattern_end = format!("pna {sub} pna {sub}`");
178-
let replacement_end = format!("pna {sub}`");
179-
result = result.replace(&dup_pattern_end, &replacement_end);
180-
}
181-
182-
// Fix nested patterns like "pna experimental stdio pna experimental stdio"
183-
for sub1 in &subcommands {
184-
for sub2 in &subcommands {
185-
let dup = format!("pna {sub1} {sub2} pna {sub1} {sub2}");
186-
let replacement = format!("pna {sub1} {sub2}");
187-
result = result.replace(&dup, &replacement);
188-
}
189-
}
190-
191-
// Fix 3-level nested patterns
192-
for sub1 in &["experimental", "help", "xattr", "acl", "chunk"] {
193-
for sub2 in &subcommands {
194-
for sub3 in &subcommands {
195-
let dup = format!("pna {sub1} {sub2} {sub3} pna {sub1} {sub2} {sub3}");
196-
let replacement = format!("pna {sub1} {sub2} {sub3}");
197-
result = result.replace(&dup, &replacement);
198-
}
199-
}
200-
}
201-
202-
// Fix hyphenated anchors: "#pna-pna-" -> "#pna-"
203-
result = result.replace("#pna-pna-", "#pna-");
204-
205-
// Fix hyphenated duplicates in anchors
206-
for sub in &subcommands {
207-
// Fix "#pna-X-pna-X" -> "#pna-X"
208-
let dup = format!("#pna-{sub}-pna-{sub}");
209-
let replacement = format!("#pna-{sub}");
210-
result = result.replace(&dup, &replacement);
211-
}
212-
213-
// Fix 2-level hyphenated patterns
214-
for sub1 in &subcommands {
215-
for sub2 in &subcommands {
216-
let dup = format!("-pna-{sub1}-{sub2}-pna-{sub1}-{sub2}");
217-
let replacement = format!("-pna-{sub1}-{sub2}");
218-
result = result.replace(&dup, &replacement);
219-
220-
let dup2 = format!("#pna-{sub1}-pna-{sub1}-{sub2}");
221-
let replacement2 = format!("#pna-{sub1}-{sub2}");
222-
result = result.replace(&dup2, &replacement2);
223-
}
224-
}
225-
226-
if result == prev {
227-
break;
228-
}
229-
}
230-
231-
result
232-
}

0 commit comments

Comments
 (0)