diff options
Diffstat (limited to 'crates')
| -rw-r--r-- | crates/typst-cli/Cargo.toml | 2 | ||||
| -rw-r--r-- | crates/typst-cli/src/args.rs | 31 | ||||
| -rw-r--r-- | crates/typst-cli/src/greet.rs | 66 | ||||
| -rw-r--r-- | crates/typst-cli/src/main.rs | 13 |
4 files changed, 108 insertions, 4 deletions
diff --git a/crates/typst-cli/Cargo.toml b/crates/typst-cli/Cargo.toml index fd707ebe..1c622d1e 100644 --- a/crates/typst-cli/Cargo.toml +++ b/crates/typst-cli/Cargo.toml @@ -28,6 +28,7 @@ typst-svg = { workspace = true } typst-timing = { workspace = true } chrono = { workspace = true } clap = { workspace = true } +color-print = { workspace = true } codespan-reporting = { workspace = true } comemo = { workspace = true } dirs = { workspace = true } @@ -59,6 +60,7 @@ chrono = { workspace = true } clap = { workspace = true, features = ["string"] } clap_complete = { workspace = true } clap_mangen = { workspace = true } +color-print = { workspace = true } semver = { workspace = true } [features] diff --git a/crates/typst-cli/src/args.rs b/crates/typst-cli/src/args.rs index 65259ab7..7436d25c 100644 --- a/crates/typst-cli/src/args.rs +++ b/crates/typst-cli/src/args.rs @@ -13,9 +13,36 @@ use semver::Version; /// in environment variables. const ENV_PATH_SEP: char = if cfg!(windows) { ';' } else { ':' }; -/// The Typst compiler. +/// The overall structure of the help. +#[rustfmt::skip] +const HELP_TEMPLATE: &str = "\ +Typst {version} + +{usage-heading} {usage} + +{all-args}{after-help}\ +"; + +/// Adds a list of useful links after the normal help. +#[rustfmt::skip] +const AFTER_HELP: &str = color_print::cstr!("\ +<s><u>Resources:</></> + <s>Tutorial:</> https://typst.app/docs/tutorial/ + <s>Reference documentation:</> https://typst.app/docs/reference/ + <s>Templates & Packages:</> https://typst.app/universe/ + <s>Forum for questions:</> https://forum.typst.app/ +"); + +/// The Typst compiler #[derive(Debug, Clone, Parser)] -#[clap(name = "typst", version = crate::typst_version(), author)] +#[clap( + name = "typst", + version = crate::typst_version(), + author, + help_template = HELP_TEMPLATE, + after_help = AFTER_HELP, + max_term_width = 80, +)] pub struct CliArguments { /// The command to run #[command(subcommand)] diff --git a/crates/typst-cli/src/greet.rs b/crates/typst-cli/src/greet.rs new file mode 100644 index 00000000..01f273fc --- /dev/null +++ b/crates/typst-cli/src/greet.rs @@ -0,0 +1,66 @@ +use std::io::{self, Read}; + +/// This is shown to users who just type `typst` the first time. +#[rustfmt::skip] +const GREETING: &str = color_print::cstr!("\ +<s>Welcome to Typst, we are glad to have you here!</> ❤️ + +If you are new to Typst, <s>start with the tutorial</> at \ +<u>https://typst.app/docs/tutorial/</>. To get a quick start with your first \ +project, <s>choose a template</> on <u>https://typst.app/universe/</>. + +Here are the <s>most important commands</> you will be using: + +- Compile a file once: <c!>typst compile file.typ</> +- Compile a file on every change: <c!>typst watch file.typ</> +- Set up a project from a template: <c!>typst init @preview/<<TEMPLATE>></> + +Learn more about these commands by running <c!>typst help</>. + +If you have a question, we and our community would be glad to help you out on \ +the <s>Typst Forum</> at <u>https://forum.typst.app/</>. + +Happy Typsting! +"); + +/// Greets (and exists) if not yet greeted. +pub fn greet() { + let Some(data_dir) = dirs::data_dir() else { return }; + let path = data_dir.join("typst").join("greeted"); + + let prev_greet = std::fs::read_to_string(&path).ok(); + if prev_greet.as_deref() == Some(crate::typst_version()) { + return; + }; + + std::fs::write(&path, crate::typst_version()).ok(); + print_and_exit(GREETING); +} + +/// Prints a colorized and line-wrapped message. +fn print_and_exit(message: &'static str) -> ! { + // Abuse clap for line wrapping ... + let err = clap::Command::new("typst") + .max_term_width(80) + .help_template("{about}") + .about(message) + .try_get_matches_from(["typst", "--help"]) + .unwrap_err(); + let _ = err.print(); + + // Windows users might have double-clicked the .exe file and have no chance + // to read it before the terminal closes. + if cfg!(windows) { + pause(); + } + + std::process::exit(err.exit_code()); +} + +/// Waits for the user. +#[allow(clippy::unused_io_amount)] +fn pause() { + eprintln!(); + eprintln!("Press enter to continue..."); + io::stdin().lock().read(&mut [0]).unwrap(); +} diff --git a/crates/typst-cli/src/main.rs b/crates/typst-cli/src/main.rs index 283d17e2..b14e687e 100644 --- a/crates/typst-cli/src/main.rs +++ b/crates/typst-cli/src/main.rs @@ -2,6 +2,7 @@ mod args; mod compile; mod download; mod fonts; +mod greet; mod init; mod package; mod query; @@ -16,6 +17,7 @@ use std::cell::Cell; use std::io::{self, Write}; use std::process::ExitCode; +use clap::error::ErrorKind; use clap::Parser; use codespan_reporting::term; use codespan_reporting::term::termcolor::WriteColor; @@ -30,8 +32,15 @@ thread_local! { static EXIT: Cell<ExitCode> = const { Cell::new(ExitCode::SUCCESS) }; } -/// The parsed commandline arguments. -static ARGS: Lazy<CliArguments> = Lazy::new(CliArguments::parse); +/// The parsed command line arguments. +static ARGS: Lazy<CliArguments> = Lazy::new(|| { + CliArguments::try_parse().unwrap_or_else(|error| { + if error.kind() == ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand { + crate::greet::greet(); + } + error.exit(); + }) +}); /// Entry point. fn main() -> ExitCode { |
