summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2024-10-15 13:55:55 +0200
committerGitHub <noreply@github.com>2024-10-15 11:55:55 +0000
commit89cecb188d3905ecf98f2a6be93cbd7cf6bf8a64 (patch)
tree80caeac50074e5c4d1ff7d4f7da044463cb07c00
parent382787d7995775cc85052419896e308a2de8de28 (diff)
Greet users who run `typst` for the first time (#5210)
-rw-r--r--Cargo.lock49
-rw-r--r--Cargo.toml7
-rw-r--r--crates/typst-cli/Cargo.toml2
-rw-r--r--crates/typst-cli/src/args.rs31
-rw-r--r--crates/typst-cli/src/greet.rs66
-rw-r--r--crates/typst-cli/src/main.rs13
6 files changed, 161 insertions, 7 deletions
diff --git a/Cargo.lock b/Cargo.lock
index a475aa0a..74ad3742 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -339,6 +339,7 @@ dependencies = [
"anstyle",
"clap_lex",
"strsim",
+ "terminal_size",
]
[[package]]
@@ -395,6 +396,27 @@ dependencies = [
]
[[package]]
+name = "color-print"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ee543c60ff3888934877a5671f45494dd27ed4ba25c6670b9a7576b7ed7a8c0"
+dependencies = [
+ "color-print-proc-macro",
+]
+
+[[package]]
+name = "color-print-proc-macro"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77ff1a80c5f3cb1ca7c06ffdd71b6a6dd6d8f896c42141fbd43f50ed28dcdb93"
+dependencies = [
+ "nom",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "color_quant"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1375,6 +1397,12 @@ dependencies = [
]
[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
name = "miniz_oxide"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1426,6 +1454,16 @@ dependencies = [
]
[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
+[[package]]
name = "notify"
version = "6.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2448,6 +2486,16 @@ dependencies = [
]
[[package]]
+name = "terminal_size"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4f599bd7ca042cfdf8f4512b277c02ba102247820f9d9d4a9f521f496751a6ef"
+dependencies = [
+ "rustix",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
name = "thin-vec"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2698,6 +2746,7 @@ dependencies = [
"clap_complete",
"clap_mangen",
"codespan-reporting",
+ "color-print",
"comemo",
"dirs",
"ecow",
diff --git a/Cargo.toml b/Cargo.toml
index 62eed382..d3b1ba64 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -38,13 +38,14 @@ bytemuck = "1"
chinese-number = { version = "0.7.2", default-features = false, features = ["number-to-chinese"] }
chrono = { version = "0.4.24", default-features = false, features = ["clock", "std"] }
ciborium = "0.2.1"
-clap = { version = "4.4", features = ["derive", "env"] }
+clap = { version = "4.4", features = ["derive", "env", "wrap_help"] }
clap_complete = "4.2.1"
clap_mangen = "0.2.10"
-ctrlc = "3.4.1"
codespan-reporting = "0.11"
+color-print = "0.3.6"
comemo = "0.4"
csv = "1"
+ctrlc = "3.4.1"
dirs = "5"
ecow = { version = "0.2", features = ["serde"] }
env_proxy = "0.4"
@@ -84,8 +85,8 @@ png = "0.17"
portable-atomic = "1.6"
proc-macro2 = "1"
pulldown-cmark = "0.9"
-quote = "1"
qcms = "0.3.0"
+quote = "1"
rayon = "1.7.0"
regex = "1"
resvg = { version = "0.43", default-features = false, features = ["raster-images"] }
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 {