CLI: open flag (#480)

This commit is contained in:
Sébastien d'Herbais de Thun 2023-04-04 14:12:19 +02:00 committed by GitHub
parent 2c735294cd
commit 2d1598e51d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 63 additions and 18 deletions

16
Cargo.lock generated
View File

@ -952,12 +952,27 @@ version = "1.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
[[package]]
name = "open"
version = "4.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "075c5203b3a2b698bc72c6c10b1f6263182135751d5013ea66e8a4b3d0562a43"
dependencies = [
"pathdiff",
]
[[package]] [[package]]
name = "paste" name = "paste"
version = "1.0.12" version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79"
[[package]]
name = "pathdiff"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
[[package]] [[package]]
name = "pdf-writer" name = "pdf-writer"
version = "0.6.0" version = "0.6.0"
@ -1539,6 +1554,7 @@ dependencies = [
"memmap2", "memmap2",
"notify", "notify",
"once_cell", "once_cell",
"open",
"same-file", "same-file",
"siphasher", "siphasher",
"typst", "typst",

View File

@ -27,6 +27,7 @@ same-file = "1"
siphasher = "0.3" siphasher = "0.3"
walkdir = "2" walkdir = "2"
clap = { version = "4.2.1", features = ["derive"] } clap = { version = "4.2.1", features = ["derive"] }
open = "4.0.1"
[features] [features]
default = ["embed-fonts"] default = ["embed-fonts"]

View File

@ -57,7 +57,7 @@ enum Command {
/// Watches the input file and recompiles on changes /// Watches the input file and recompiles on changes
#[command(visible_alias = "w")] #[command(visible_alias = "w")]
Watch(WatchCommand), Watch(CompileCommand),
/// List all discovered fonts in system and custom font paths /// List all discovered fonts in system and custom font paths
Fonts(FontsCommand), Fonts(FontsCommand),
@ -71,22 +71,16 @@ pub struct CompileCommand {
/// Path to output PDF file /// Path to output PDF file
output: Option<PathBuf>, output: Option<PathBuf>,
}
/// Watches the input file and recompiles on changes /// Opens the output file after compilation using the default PDF viewer
#[derive(Debug, Clone, Parser)] #[arg(long = "open")]
pub struct WatchCommand { open: Option<Option<String>>,
/// Path to input Typst file
input: PathBuf,
/// Path to output PDF file
output: Option<PathBuf>,
} }
/// List all discovered fonts in system and custom font paths /// List all discovered fonts in system and custom font paths
#[derive(Debug, Clone, Parser)] #[derive(Debug, Clone, Parser)]
pub struct FontsCommand { pub struct FontsCommand {
/// Add additional directories to search for fonts /// Also list style variants of each font family
#[arg(long)] #[arg(long)]
variants: bool, variants: bool,
} }
@ -107,6 +101,9 @@ struct CompileSettings {
/// The paths to search for fonts. /// The paths to search for fonts.
font_paths: Vec<PathBuf>, font_paths: Vec<PathBuf>,
/// The open command to use.
open: Option<Option<String>>,
} }
impl CompileSettings { impl CompileSettings {
@ -117,13 +114,13 @@ impl CompileSettings {
watch: bool, watch: bool,
root: Option<PathBuf>, root: Option<PathBuf>,
font_paths: Vec<PathBuf>, font_paths: Vec<PathBuf>,
open: Option<Option<String>>,
) -> Self { ) -> Self {
let output = match output { let output = match output {
Some(path) => path, Some(path) => path,
None => input.with_extension("pdf"), None => input.with_extension("pdf"),
}; };
Self { input, output, watch, root, font_paths, open }
Self { input, output, watch, root, font_paths }
} }
/// Create a new compile settings from the CLI arguments and a compile command. /// Create a new compile settings from the CLI arguments and a compile command.
@ -131,12 +128,13 @@ impl CompileSettings {
/// # Panics /// # Panics
/// Panics if the command is not a compile or watch command. /// Panics if the command is not a compile or watch command.
pub fn with_arguments(args: CliArguments) -> Self { pub fn with_arguments(args: CliArguments) -> Self {
let (input, output, watch) = match args.command { let watch = matches!(args.command, Command::Watch(_));
Command::Compile(command) => (command.input, command.output, false), let CompileCommand { input, output, open } = match args.command {
Command::Watch(command) => (command.input, command.output, true), Command::Compile(command) => command,
Command::Watch(command) => command,
_ => unreachable!(), _ => unreachable!(),
}; };
Self::new(input, output, watch, args.root, args.font_paths) Self::new(input, output, watch, args.root, args.font_paths, open)
} }
} }
@ -195,7 +193,7 @@ fn print_error(msg: &str) -> io::Result<()> {
} }
/// Execute a compilation command. /// Execute a compilation command.
fn compile(command: CompileSettings) -> StrResult<()> { fn compile(mut command: CompileSettings) -> StrResult<()> {
let root = if let Some(root) = &command.root { let root = if let Some(root) = &command.root {
root.clone() root.clone()
} else if let Some(dir) = command } else if let Some(dir) = command
@ -215,6 +213,14 @@ fn compile(command: CompileSettings) -> StrResult<()> {
// Perform initial compilation. // Perform initial compilation.
let failed = compile_once(&mut world, &command)?; let failed = compile_once(&mut world, &command)?;
// open the file if requested, this must be done on the first **successful** compilation
if !failed {
if let Some(open) = command.open.take() {
open_file(open.as_deref(), &command.output)?;
}
}
if !command.watch { if !command.watch {
// Return with non-zero exit code in case of error. // Return with non-zero exit code in case of error.
if failed { if failed {
@ -258,6 +264,11 @@ fn compile(command: CompileSettings) -> StrResult<()> {
if recompile { if recompile {
compile_once(&mut world, &command)?; compile_once(&mut world, &command)?;
comemo::evict(30); comemo::evict(30);
// open the file if requested, this must be done on the first **successful** compilation
if let Some(open) = command.open.take() {
open_file(open.as_deref(), &command.output)?;
}
} }
} }
} }
@ -381,6 +392,23 @@ fn print_diagnostics(
Ok(()) Ok(())
} }
/// Opens the given file using:
/// - The default file viewer if `open` is `None`.
/// - The given viewer provided by `open` if it is `Some`.
fn open_file(open: Option<&str>, path: &Path) -> StrResult<()> {
if let Some(app) = open {
open::with(path, app).map_err(|err| {
format!("failed to open `{}` with `{}`, reason: {}", path.display(), app, err)
})?;
} else {
open::that(path).map_err(|err| {
format!("failed to open `{}`, reason: {}", path.display(), err)
})?;
}
Ok(())
}
/// Execute a font listing command. /// Execute a font listing command.
fn fonts(command: FontsSettings) -> StrResult<()> { fn fonts(command: FontsSettings) -> StrResult<()> {
let mut searcher = FontSearcher::new(); let mut searcher = FontSearcher::new();