Skip to content

Commit a54ea2d

Browse files
committed
Add a --quiet mode
1 parent 35a33f0 commit a54ea2d

File tree

5 files changed

+227
-46
lines changed

5 files changed

+227
-46
lines changed

crates/ty/src/lib.rs

Lines changed: 67 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
11
mod args;
22
mod logging;
3+
mod printer;
34
mod python_version;
45
mod version;
56

67
pub use args::Cli;
78
use ty_static::EnvVars;
89

9-
use std::io::{self, BufWriter, Write, stdout};
10+
use std::fmt::Write;
11+
use std::io::stdout;
1012
use std::process::{ExitCode, Termination};
1113

1214
use anyhow::Result;
1315
use std::sync::Mutex;
1416

1517
use crate::args::{CheckCommand, Command, TerminalColor};
1618
use crate::logging::setup_tracing;
19+
use crate::printer::Printer;
1720
use anyhow::{Context, anyhow};
1821
use clap::{CommandFactory, Parser};
1922
use colored::Colorize;
@@ -25,7 +28,7 @@ use ruff_db::system::{OsSystem, SystemPath, SystemPathBuf};
2528
use salsa::plumbing::ZalsaDatabase;
2629
use ty_project::metadata::options::ProjectOptionsOverrides;
2730
use ty_project::watch::ProjectWatcher;
28-
use ty_project::{Db, DummyReporter, Reporter, watch};
31+
use ty_project::{Db, watch};
2932
use ty_project::{ProjectDatabase, ProjectMetadata};
3033
use ty_server::run_server;
3134

@@ -49,7 +52,7 @@ pub fn run() -> anyhow::Result<ExitStatus> {
4952
}
5053

5154
pub(crate) fn version() -> Result<()> {
52-
let mut stdout = BufWriter::new(io::stdout().lock());
55+
let mut stdout = Printer::default().stdout_important().lock();
5356
let version_info = crate::version::version();
5457
writeln!(stdout, "ty {}", &version_info)?;
5558
Ok(())
@@ -62,6 +65,8 @@ fn run_check(args: CheckCommand) -> anyhow::Result<ExitStatus> {
6265
countme::enable(verbosity.is_trace());
6366
let _guard = setup_tracing(verbosity, args.color.unwrap_or_default())?;
6467

68+
let printer = Printer::default().with_verbosity(verbosity);
69+
6570
tracing::warn!(
6671
"ty is pre-release software and not ready for production use. \
6772
Expect to encounter bugs, missing features, and fatal errors.",
@@ -126,7 +131,8 @@ fn run_check(args: CheckCommand) -> anyhow::Result<ExitStatus> {
126131
}
127132

128133
let project_options_overrides = ProjectOptionsOverrides::new(config_file, options);
129-
let (main_loop, main_loop_cancellation_token) = MainLoop::new(project_options_overrides);
134+
let (main_loop, main_loop_cancellation_token) =
135+
MainLoop::new(project_options_overrides, printer);
130136

131137
// Listen to Ctrl+C and abort the watch mode.
132138
let main_loop_cancellation_token = Mutex::new(Some(main_loop_cancellation_token));
@@ -144,7 +150,7 @@ fn run_check(args: CheckCommand) -> anyhow::Result<ExitStatus> {
144150
main_loop.run(&mut db)?
145151
};
146152

147-
let mut stdout = stdout().lock();
153+
let mut stdout = printer.stdout_important().lock();
148154
match std::env::var(EnvVars::TY_MEMORY_REPORT).as_deref() {
149155
Ok("short") => write!(stdout, "{}", db.salsa_memory_dump().display_short())?,
150156
Ok("mypy_primer") => write!(stdout, "{}", db.salsa_memory_dump().display_mypy_primer())?,
@@ -195,12 +201,16 @@ struct MainLoop {
195201
/// The file system watcher, if running in watch mode.
196202
watcher: Option<ProjectWatcher>,
197203

204+
/// Interface for displaying information to the user.
205+
printer: Printer,
206+
198207
project_options_overrides: ProjectOptionsOverrides,
199208
}
200209

201210
impl MainLoop {
202211
fn new(
203212
project_options_overrides: ProjectOptionsOverrides,
213+
printer: Printer,
204214
) -> (Self, MainLoopCancellationToken) {
205215
let (sender, receiver) = crossbeam_channel::bounded(10);
206216

@@ -210,6 +220,7 @@ impl MainLoop {
210220
receiver,
211221
watcher: None,
212222
project_options_overrides,
223+
printer,
213224
},
214225
MainLoopCancellationToken { sender },
215226
)
@@ -226,32 +237,24 @@ impl MainLoop {
226237

227238
// Do not show progress bars with `--watch`, indicatif does not seem to
228239
// handle cancelling independent progress bars very well.
229-
self.run_with_progress::<DummyReporter>(db)?;
240+
// TODO(zanieb): We can probably use `MultiProgress` to handle this case in the future.
241+
self.printer = self.printer.with_no_progress();
242+
self.run(db)?;
230243

231244
Ok(ExitStatus::Success)
232245
}
233246

234247
fn run(self, db: &mut ProjectDatabase) -> Result<ExitStatus> {
235-
self.run_with_progress::<IndicatifReporter>(db)
236-
}
237-
238-
fn run_with_progress<R>(mut self, db: &mut ProjectDatabase) -> Result<ExitStatus>
239-
where
240-
R: Reporter + Default + 'static,
241-
{
242248
self.sender.send(MainLoopMessage::CheckWorkspace).unwrap();
243249

244-
let result = self.main_loop::<R>(db);
250+
let result = self.main_loop(db);
245251

246252
tracing::debug!("Exiting main loop");
247253

248254
result
249255
}
250256

251-
fn main_loop<R>(&mut self, db: &mut ProjectDatabase) -> Result<ExitStatus>
252-
where
253-
R: Reporter + Default + 'static,
254-
{
257+
fn main_loop(mut self, db: &mut ProjectDatabase) -> Result<ExitStatus> {
255258
// Schedule the first check.
256259
tracing::debug!("Starting main loop");
257260

@@ -267,7 +270,7 @@ impl MainLoop {
267270
// to prevent blocking the main loop here.
268271
rayon::spawn(move || {
269272
match salsa::Cancelled::catch(|| {
270-
let mut reporter = R::default();
273+
let mut reporter = IndicatifReporter::from(self.printer);
271274
db.check_with_reporter(&mut reporter)
272275
}) {
273276
Ok(result) => {
@@ -297,15 +300,12 @@ impl MainLoop {
297300
tracing::warn!("No python files found under the given path(s)");
298301
}
299302

300-
// TODO: We should have an official flag to silence workspace diagnostics.
301-
if std::env::var("TY_MEMORY_REPORT").as_deref() == Ok("mypy_primer") {
302-
return Ok(ExitStatus::Success);
303-
}
304-
305-
let mut stdout = stdout().lock();
306-
307303
if result.is_empty() {
308-
writeln!(stdout, "{}", "All checks passed!".green().bold())?;
304+
writeln!(
305+
self.printer.stdout(),
306+
"{}",
307+
"All checks passed!".green().bold()
308+
)?;
309309

310310
if self.watcher.is_none() {
311311
return Ok(ExitStatus::Success);
@@ -314,14 +314,16 @@ impl MainLoop {
314314
let mut max_severity = Severity::Info;
315315
let diagnostics_count = result.len();
316316

317+
let mut stdout = self.printer.stdout().lock();
318+
317319
for diagnostic in result {
318320
write!(stdout, "{}", diagnostic.display(db, &display_config))?;
319321

320322
max_severity = max_severity.max(diagnostic.severity());
321323
}
322324

323325
writeln!(
324-
stdout,
326+
self.printer.stdout_important(),
325327
"Found {} diagnostic{}",
326328
diagnostics_count,
327329
if diagnostics_count > 1 { "s" } else { "" }
@@ -383,27 +385,54 @@ impl MainLoop {
383385
}
384386

385387
/// A progress reporter for `ty check`.
386-
#[derive(Default)]
387-
struct IndicatifReporter(Option<indicatif::ProgressBar>);
388+
struct IndicatifReporter {
389+
/// The progress bar to report to.
390+
///
391+
/// Initialization of the bar is deferred to [`ty_project::ProgressReporter::set_files`] so we
392+
/// do not initialize the bar too early as it may take a while to collect the number of files to
393+
/// process and we don't want to display an empty "0/0" bar.
394+
bar: Option<indicatif::ProgressBar>,
395+
/// The target for the progress bar.
396+
///
397+
/// This is primarily used to determine if the progress bar is hidden.
398+
target: Option<indicatif::ProgressDrawTarget>,
399+
}
388400

389-
impl ty_project::Reporter for IndicatifReporter {
401+
impl From<Printer> for IndicatifReporter {
402+
fn from(printer: Printer) -> Self {
403+
Self {
404+
bar: None,
405+
target: Some(printer.progress_target()),
406+
}
407+
}
408+
}
409+
410+
impl ty_project::ProgressReporter for IndicatifReporter {
390411
fn set_files(&mut self, files: usize) {
391-
let progress = indicatif::ProgressBar::new(files as u64);
392-
progress.set_style(
412+
assert!(
413+
self.bar.is_none(),
414+
"The progress reporter should only be initialized once"
415+
);
416+
let target = self
417+
.target
418+
.take()
419+
.expect("The progress reporter must be constructed with a target");
420+
421+
let bar = indicatif::ProgressBar::with_draw_target(Some(files as u64), target);
422+
bar.set_style(
393423
indicatif::ProgressStyle::with_template(
394424
"{msg:8.dim} {bar:60.green/dim} {pos}/{len} files",
395425
)
396426
.unwrap()
397427
.progress_chars("--"),
398428
);
399-
progress.set_message("Checking");
400-
401-
self.0 = Some(progress);
429+
bar.set_message("Checking");
430+
self.bar = Some(bar);
402431
}
403432

404433
fn report_file(&self, _file: &ruff_db::files::File) {
405-
if let Some(ref progress_bar) = self.0 {
406-
progress_bar.inc(1);
434+
if let Some(bar) = &self.bar {
435+
bar.inc(1);
407436
}
408437
}
409438
}

crates/ty/src/logging.rs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,32 @@ pub(crate) struct Verbosity {
2424
help = "Use verbose output (or `-vv` and `-vvv` for more verbose output)",
2525
action = clap::ArgAction::Count,
2626
global = true,
27+
overrides_with = "quiet",
2728
)]
2829
verbose: u8,
30+
31+
#[arg(
32+
long,
33+
help = "Use quiet output",
34+
action = clap::ArgAction::Count,
35+
global = true,
36+
overrides_with = "verbose",
37+
)]
38+
quiet: u8,
2939
}
3040

3141
impl Verbosity {
32-
/// Returns the verbosity level based on the number of `-v` flags.
42+
/// Returns the verbosity level based on the number of `-v` and `-q` flags.
3343
///
3444
/// Returns `None` if the user did not specify any verbosity flags.
3545
pub(crate) fn level(&self) -> VerbosityLevel {
46+
// `--quiet` and `--verbose` are mutually exclusive in Clap, so we can just check one first.
47+
match self.quiet {
48+
0 => {}
49+
_ => return VerbosityLevel::Quiet,
50+
// TODO(zanieb): Add support for `-qq` with a "silent" mode
51+
}
52+
3653
match self.verbose {
3754
0 => VerbosityLevel::Default,
3855
1 => VerbosityLevel::Verbose,
@@ -42,9 +59,14 @@ impl Verbosity {
4259
}
4360
}
4461

45-
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
62+
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default)]
4663
pub(crate) enum VerbosityLevel {
64+
/// Quiet output. Only shows Ruff and ty events up to the [`ERROR`](tracing::Level::ERROR).
65+
/// Silences output except for summary information.
66+
Quiet,
67+
4768
/// Default output level. Only shows Ruff and ty events up to the [`WARN`](tracing::Level::WARN).
69+
#[default]
4870
Default,
4971

5072
/// Enables verbose output. Emits Ruff and ty events up to the [`INFO`](tracing::Level::INFO).
@@ -62,6 +84,7 @@ pub(crate) enum VerbosityLevel {
6284
impl VerbosityLevel {
6385
const fn level_filter(self) -> LevelFilter {
6486
match self {
87+
VerbosityLevel::Quiet => LevelFilter::ERROR,
6588
VerbosityLevel::Default => LevelFilter::WARN,
6689
VerbosityLevel::Verbose => LevelFilter::INFO,
6790
VerbosityLevel::ExtraVerbose => LevelFilter::DEBUG,

0 commit comments

Comments
 (0)