CLI option for emitting diagnostics in a unix-style short format (#1176)

This commit is contained in:
erikwastaken 2023-05-23 12:34:12 +02:00 committed by GitHub
parent 5400570efa
commit 2cbeeae5da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 79 additions and 21 deletions

View File

@ -1,11 +1,16 @@
use std::fmt::{self, Display, Formatter};
use std::path::PathBuf; use std::path::PathBuf;
use clap::{ArgAction, Parser, Subcommand}; use clap::{ArgAction, Parser, Subcommand, ValueEnum};
/// typst creates PDF files from .typ files /// typst creates PDF files from .typ files
#[derive(Debug, Clone, Parser)] #[derive(Debug, Clone, Parser)]
#[clap(name = "typst", version = crate::typst_version(), author)] #[clap(name = "typst", version = crate::typst_version(), author)]
pub struct CliArguments { pub struct CliArguments {
/// The typst command to run
#[command(subcommand)]
pub command: Command,
/// Add additional directories to search for fonts /// Add additional directories to search for fonts
#[clap(long = "font-path", env = "TYPST_FONT_PATHS", value_name = "DIR", action = ArgAction::Append)] #[clap(long = "font-path", env = "TYPST_FONT_PATHS", value_name = "DIR", action = ArgAction::Append)]
pub font_paths: Vec<PathBuf>, pub font_paths: Vec<PathBuf>,
@ -14,16 +19,28 @@ pub struct CliArguments {
#[clap(long = "root", env = "TYPST_ROOT", value_name = "DIR")] #[clap(long = "root", env = "TYPST_ROOT", value_name = "DIR")]
pub root: Option<PathBuf>, pub root: Option<PathBuf>,
/// The typst command to run
#[command(subcommand)]
pub command: Command,
/// Sets the level of logging verbosity: /// Sets the level of logging verbosity:
/// -v = warning & error, -vv = info, -vvv = debug, -vvvv = trace /// -v = warning & error, -vv = info, -vvv = debug, -vvvv = trace
#[clap(short, long, action = ArgAction::Count)] #[clap(short, long, action = ArgAction::Count)]
pub verbosity: u8, pub verbosity: u8,
} }
/// Which format to use for diagnostics.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, ValueEnum)]
pub enum DiagnosticFormat {
Human,
Short,
}
impl Display for DiagnosticFormat {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.to_possible_value()
.expect("no values are skipped")
.get_name()
.fmt(f)
}
}
/// What to do. /// What to do.
#[derive(Debug, Clone, Subcommand)] #[derive(Debug, Clone, Subcommand)]
#[command()] #[command()]
@ -69,13 +86,21 @@ pub struct CompileCommand {
#[arg(long = "open")] #[arg(long = "open")]
pub open: Option<Option<String>>, pub open: Option<Option<String>>,
/// Produces a flamegraph of the compilation process
#[arg(long = "flamegraph", value_name = "OUTPUT_SVG")]
pub flamegraph: Option<Option<PathBuf>>,
/// The PPI to use if exported as PNG /// The PPI to use if exported as PNG
#[arg(long = "ppi")] #[arg(long = "ppi")]
pub ppi: Option<f32>, pub ppi: Option<f32>,
/// In which format to emit diagnostics
#[clap(
long,
default_value_t = DiagnosticFormat::Human,
value_parser = clap::value_parser!(DiagnosticFormat)
)]
pub diagnostic_format: DiagnosticFormat,
/// Produces a flamegraph of the compilation process
#[arg(long = "flamegraph", value_name = "OUTPUT_SVG")]
pub flamegraph: Option<Option<PathBuf>>,
} }
/// List all discovered fonts in system and custom font paths /// List all discovered fonts in system and custom font paths

View File

@ -34,7 +34,7 @@ use typst::util::{Buffer, PathExt};
use typst::World; use typst::World;
use walkdir::WalkDir; use walkdir::WalkDir;
use crate::args::{CliArguments, Command, CompileCommand}; use crate::args::{CliArguments, Command, CompileCommand, DiagnosticFormat};
type CodespanResult<T> = Result<T, CodespanError>; type CodespanResult<T> = Result<T, CodespanError>;
type CodespanError = codespan_reporting::files::Error; type CodespanError = codespan_reporting::files::Error;
@ -111,12 +111,16 @@ struct CompileSettings {
/// The open command to use. /// The open command to use.
open: Option<Option<String>>, open: Option<Option<String>>,
/// The ppi to use for png export /// The PPI to use for PNG export.
ppi: Option<f32>, ppi: Option<f32>,
/// In which format to emit diagnostics
diagnostic_format: DiagnosticFormat,
} }
impl CompileSettings { impl CompileSettings {
/// Create a new compile settings from the field values. /// Create a new compile settings from the field values.
#[allow(clippy::too_many_arguments)]
fn new( fn new(
input: PathBuf, input: PathBuf,
output: Option<PathBuf>, output: Option<PathBuf>,
@ -125,12 +129,22 @@ impl CompileSettings {
font_paths: Vec<PathBuf>, font_paths: Vec<PathBuf>,
open: Option<Option<String>>, open: Option<Option<String>>,
ppi: Option<f32>, ppi: Option<f32>,
diagnostic_format: DiagnosticFormat,
) -> 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, ppi } Self {
input,
output,
watch,
root,
font_paths,
open,
diagnostic_format,
ppi,
}
} }
/// 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.
@ -139,12 +153,23 @@ impl CompileSettings {
/// Panics if the command is not a compile or watch command. /// Panics if the command is not a compile or watch command.
fn with_arguments(args: CliArguments) -> Self { fn with_arguments(args: CliArguments) -> Self {
let watch = matches!(args.command, Command::Watch(_)); let watch = matches!(args.command, Command::Watch(_));
let CompileCommand { input, output, open, ppi, .. } = match args.command { let CompileCommand { input, output, open, ppi, diagnostic_format, .. } =
Command::Compile(command) => command, match args.command {
Command::Watch(command) => command, Command::Compile(command) => command,
_ => unreachable!(), Command::Watch(command) => command,
}; _ => unreachable!(),
Self::new(input, output, watch, args.root, args.font_paths, open, ppi) };
Self::new(
input,
output,
watch,
args.root,
args.font_paths,
open,
ppi,
diagnostic_format,
)
} }
} }
@ -279,7 +304,7 @@ fn compile_once(world: &mut SystemWorld, command: &CompileSettings) -> StrResult
Err(errors) => { Err(errors) => {
set_failed(); set_failed();
status(command, Status::Error).unwrap(); status(command, Status::Error).unwrap();
print_diagnostics(world, *errors) print_diagnostics(world, *errors, command.diagnostic_format)
.map_err(|_| "failed to print diagnostics")?; .map_err(|_| "failed to print diagnostics")?;
tracing::info!("Compilation failed"); tracing::info!("Compilation failed");
Ok(false) Ok(false)
@ -400,9 +425,17 @@ impl Status {
fn print_diagnostics( fn print_diagnostics(
world: &SystemWorld, world: &SystemWorld,
errors: Vec<SourceError>, errors: Vec<SourceError>,
diagnostic_format: DiagnosticFormat,
) -> Result<(), codespan_reporting::files::Error> { ) -> Result<(), codespan_reporting::files::Error> {
let mut w = StandardStream::stderr(ColorChoice::Auto); let mut w = match diagnostic_format {
let config = term::Config { tab_width: 2, ..Default::default() }; DiagnosticFormat::Human => color_stream(),
DiagnosticFormat::Short => StandardStream::stderr(ColorChoice::Never),
};
let mut config = term::Config { tab_width: 2, ..Default::default() };
if diagnostic_format == DiagnosticFormat::Short {
config.display_style = term::DisplayStyle::Short;
}
for error in errors { for error in errors {
// The main diagnostic. // The main diagnostic.