Add args: --edit-page and --edit-patch (#388)

Fix https://github.com/tealdeer-rs/tealdeer/issues/383

Co-authored-by: Niklas Mohrin <dev@niklasmohrin.de>
This commit is contained in:
lyj 2025-02-27 05:03:18 +08:00 committed by GitHub
commit 55f401df30
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 130 additions and 12 deletions

View file

@ -8,6 +8,8 @@ Arguments:
Options:
-l, --list List all commands in the cache
--edit-page Edit custom page with `EDITOR`
--edit-patch Edit custom patch with `EDITOR`
-f, --render <FILE> Render a specific markdown file
-p, --platform <PLATFORM> Override the operating system, can be specified multiple times in order
of preference [possible values: linux, macos, sunos, windows, android,

View file

@ -32,6 +32,14 @@ pub(crate) struct Cli {
#[arg(short = 'l', long = "list")]
pub list: bool,
/// Edit custom page with `EDITOR`
#[arg(long, requires = "command")]
pub edit_page: bool,
/// Edit custom patch with `EDITOR`
#[arg(long, requires = "command", conflicts_with = "edit_page")]
pub edit_patch: bool,
/// Render a specific markdown file
#[arg(
short = 'f',

View file

@ -27,10 +27,15 @@ compile_error!(
use std::{
env,
fs::create_dir_all,
io::{self, IsTerminal},
path::Path,
process,
process::Command,
};
use anyhow::anyhow;
use anyhow::Context;
use app_dirs::AppInfo;
use clap::Parser;
@ -235,6 +240,27 @@ fn get_languages_from_env() -> Vec<String> {
)
}
fn spawn_editor(custom_pages_dir: &Path, file_name: &str) -> anyhow::Result<()> {
create_dir_all(custom_pages_dir).context("Failed to create custom pages directory")?;
let custom_page_path = custom_pages_dir.join(file_name);
let Some(custom_page_path) = custom_page_path.to_str() else {
return Err(anyhow!("`custom_page_path.to_str()` failed"));
};
let Ok(editor) = env::var("EDITOR") else {
return Err(anyhow!(
"To edit a custom page, please set the `EDITOR` environment variable."
));
};
println!("Editing {custom_page_path:?}");
let status = Command::new(&editor).arg(custom_page_path).status()?;
if !status.success() {
return Err(anyhow!("{editor} exit with code {:?}", status.code()));
}
Ok(())
}
fn main() {
// Initialize logger
init_log();
@ -266,6 +292,34 @@ fn main() {
}
};
let custom_pages_dir = config
.directories
.custom_pages_dir
.as_ref()
.map(PathWithSource::path);
// Note: According to the TLDR client spec, page names must be transparently
// lowercased before lookup:
// https://github.com/tldr-pages/tldr/blob/main/CLIENT-SPECIFICATION.md#page-names
let command = args.command.join("-").to_lowercase();
if args.edit_patch || args.edit_page {
let file_name = if args.edit_patch {
format!("{command}.patch.md")
} else {
format!("{command}.page.md")
};
custom_pages_dir
.context("To edit custom pages/patches, please specify a custom pages directory.")
.and_then(|custom_pages_dir| spawn_editor(custom_pages_dir, &file_name))
.unwrap_or_else(|err| {
print_error(enable_styles, &err);
process::exit(1);
});
return;
}
// Show various paths
if args.show_paths {
show_paths(&config);
@ -312,7 +366,7 @@ fn main() {
// Check cache presence and freshness
if !cache_updated
&& (args.list || !args.command.is_empty())
&& (args.list || !command.is_empty())
&& check_cache(&cache, &args, enable_styles) == CheckCacheResult::CacheMissing
{
process::exit(1);
@ -320,11 +374,6 @@ fn main() {
// List cached commands and exit
if args.list {
let custom_pages_dir = config
.directories
.custom_pages_dir
.as_ref()
.map(PathWithSource::path);
println!(
"{}",
cache.list_pages(custom_pages_dir, &platforms).join("\n")
@ -333,12 +382,7 @@ fn main() {
}
// Show command from cache
if !args.command.is_empty() {
// Note: According to the TLDR client spec, page names must be transparently
// lowercased before lookup:
// https://github.com/tldr-pages/tldr/blob/main/CLIENT-SPECIFICATION.md#page-names
let command = args.command.join("-").to_lowercase();
if !command.is_empty() {
// Collect languages
let languages = args
.language

View file

@ -1031,3 +1031,67 @@ fn test_raw_render_file() {
.success()
.stdout(diff(include_str!("cache/pages/common/inkscape-v1.md")));
}
fn touch_custom_page(testenv: &TestEnv) {
let args = vec!["--edit-page", "foo"];
testenv
.command()
.args(&args)
.env("EDITOR", "touch")
.assert()
.success();
assert!(testenv.custom_pages_dir.path().join("foo.page.md").exists());
}
fn touch_custom_patch(testenv: &TestEnv) {
let args = vec!["--edit-patch", "foo"];
testenv
.command()
.args(&args)
.env("EDITOR", "touch")
.assert()
.success();
assert!(testenv
.custom_pages_dir
.path()
.join("foo.patch.md")
.exists());
}
#[test]
fn test_edit_page() {
let testenv = TestEnv::new().write_custom_pages_config();
touch_custom_page(&testenv);
}
#[test]
fn test_edit_patch() {
let testenv = TestEnv::new().write_custom_pages_config();
touch_custom_patch(&testenv);
}
#[test]
fn test_recreate_dir() {
let testenv = TestEnv::new().write_custom_pages_config();
touch_custom_patch(&testenv);
touch_custom_page(&testenv);
}
#[test]
fn test_custom_pages_dir_is_not_dir() {
let testenv = TestEnv::new().write_custom_pages_config();
let _ = std::fs::remove_dir_all(testenv.custom_pages_dir.path());
let _ = File::create(testenv.custom_pages_dir.path()).unwrap();
assert!(testenv.custom_pages_dir.path().is_file());
let args = vec!["--edit-patch", "foo"];
testenv
.command()
.args(&args)
.env("EDITOR", "touch")
.assert()
.failure();
}