1
1
mod args;
2
2
mod logging;
3
+ mod printer;
3
4
mod python_version;
4
5
mod version;
5
6
6
7
pub use args:: Cli ;
7
8
use ty_static:: EnvVars ;
8
9
9
- use std:: io :: { self , BufWriter , Write , stdout } ;
10
+ use std:: fmt :: Write ;
10
11
use std:: process:: { ExitCode , Termination } ;
11
12
12
13
use anyhow:: Result ;
13
14
use std:: sync:: Mutex ;
14
15
15
16
use crate :: args:: { CheckCommand , Command , TerminalColor } ;
16
17
use crate :: logging:: setup_tracing;
18
+ use crate :: printer:: Printer ;
17
19
use anyhow:: { Context , anyhow} ;
18
20
use clap:: { CommandFactory , Parser } ;
19
21
use colored:: Colorize ;
@@ -25,7 +27,7 @@ use ruff_db::system::{OsSystem, SystemPath, SystemPathBuf};
25
27
use salsa:: plumbing:: ZalsaDatabase ;
26
28
use ty_project:: metadata:: options:: ProjectOptionsOverrides ;
27
29
use ty_project:: watch:: ProjectWatcher ;
28
- use ty_project:: { Db , DummyReporter , Reporter , watch} ;
30
+ use ty_project:: { Db , watch} ;
29
31
use ty_project:: { ProjectDatabase , ProjectMetadata } ;
30
32
use ty_server:: run_server;
31
33
@@ -42,14 +44,16 @@ pub fn run() -> anyhow::Result<ExitStatus> {
42
44
Command :: Check ( check_args) => run_check ( check_args) ,
43
45
Command :: Version => version ( ) . map ( |( ) | ExitStatus :: Success ) ,
44
46
Command :: GenerateShellCompletion { shell } => {
47
+ use std:: io:: stdout;
48
+
45
49
shell. generate ( & mut Cli :: command ( ) , & mut stdout ( ) ) ;
46
50
Ok ( ExitStatus :: Success )
47
51
}
48
52
}
49
53
}
50
54
51
55
pub ( crate ) fn version ( ) -> Result < ( ) > {
52
- let mut stdout = BufWriter :: new ( io :: stdout ( ) . lock ( ) ) ;
56
+ let mut stdout = Printer :: default ( ) . stream_for_requested_summary ( ) . lock ( ) ;
53
57
let version_info = crate :: version:: version ( ) ;
54
58
writeln ! ( stdout, "ty {}" , & version_info) ?;
55
59
Ok ( ( ) )
@@ -61,6 +65,8 @@ fn run_check(args: CheckCommand) -> anyhow::Result<ExitStatus> {
61
65
let verbosity = args. verbosity . level ( ) ;
62
66
let _guard = setup_tracing ( verbosity, args. color . unwrap_or_default ( ) ) ?;
63
67
68
+ let printer = Printer :: default ( ) . with_verbosity ( verbosity) ;
69
+
64
70
tracing:: warn!(
65
71
"ty is pre-release software and not ready for production use. \
66
72
Expect to encounter bugs, missing features, and fatal errors.",
@@ -125,7 +131,8 @@ fn run_check(args: CheckCommand) -> anyhow::Result<ExitStatus> {
125
131
}
126
132
127
133
let project_options_overrides = ProjectOptionsOverrides :: new ( config_file, options) ;
128
- 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) ;
129
136
130
137
// Listen to Ctrl+C and abort the watch mode.
131
138
let main_loop_cancellation_token = Mutex :: new ( Some ( main_loop_cancellation_token) ) ;
@@ -143,7 +150,7 @@ fn run_check(args: CheckCommand) -> anyhow::Result<ExitStatus> {
143
150
main_loop. run ( & mut db) ?
144
151
} ;
145
152
146
- let mut stdout = stdout ( ) . lock ( ) ;
153
+ let mut stdout = printer . stream_for_requested_summary ( ) . lock ( ) ;
147
154
match std:: env:: var ( EnvVars :: TY_MEMORY_REPORT ) . as_deref ( ) {
148
155
Ok ( "short" ) => write ! ( stdout, "{}" , db. salsa_memory_dump( ) . display_short( ) ) ?,
149
156
Ok ( "mypy_primer" ) => write ! ( stdout, "{}" , db. salsa_memory_dump( ) . display_mypy_primer( ) ) ?,
@@ -192,12 +199,16 @@ struct MainLoop {
192
199
/// The file system watcher, if running in watch mode.
193
200
watcher : Option < ProjectWatcher > ,
194
201
202
+ /// Interface for displaying information to the user.
203
+ printer : Printer ,
204
+
195
205
project_options_overrides : ProjectOptionsOverrides ,
196
206
}
197
207
198
208
impl MainLoop {
199
209
fn new (
200
210
project_options_overrides : ProjectOptionsOverrides ,
211
+ printer : Printer ,
201
212
) -> ( Self , MainLoopCancellationToken ) {
202
213
let ( sender, receiver) = crossbeam_channel:: bounded ( 10 ) ;
203
214
@@ -207,6 +218,7 @@ impl MainLoop {
207
218
receiver,
208
219
watcher : None ,
209
220
project_options_overrides,
221
+ printer,
210
222
} ,
211
223
MainLoopCancellationToken { sender } ,
212
224
)
@@ -223,32 +235,24 @@ impl MainLoop {
223
235
224
236
// Do not show progress bars with `--watch`, indicatif does not seem to
225
237
// handle cancelling independent progress bars very well.
226
- self . run_with_progress :: < DummyReporter > ( db) ?;
238
+ // TODO(zanieb): We can probably use `MultiProgress` to handle this case in the future.
239
+ self . printer = self . printer . with_no_progress ( ) ;
240
+ self . run ( db) ?;
227
241
228
242
Ok ( ExitStatus :: Success )
229
243
}
230
244
231
245
fn run ( self , db : & mut ProjectDatabase ) -> Result < ExitStatus > {
232
- self . run_with_progress :: < IndicatifReporter > ( db)
233
- }
234
-
235
- fn run_with_progress < R > ( mut self , db : & mut ProjectDatabase ) -> Result < ExitStatus >
236
- where
237
- R : Reporter + Default + ' static ,
238
- {
239
246
self . sender . send ( MainLoopMessage :: CheckWorkspace ) . unwrap ( ) ;
240
247
241
- let result = self . main_loop :: < R > ( db) ;
248
+ let result = self . main_loop ( db) ;
242
249
243
250
tracing:: debug!( "Exiting main loop" ) ;
244
251
245
252
result
246
253
}
247
254
248
- fn main_loop < R > ( & mut self , db : & mut ProjectDatabase ) -> Result < ExitStatus >
249
- where
250
- R : Reporter + Default + ' static ,
251
- {
255
+ fn main_loop ( mut self , db : & mut ProjectDatabase ) -> Result < ExitStatus > {
252
256
// Schedule the first check.
253
257
tracing:: debug!( "Starting main loop" ) ;
254
258
@@ -264,7 +268,7 @@ impl MainLoop {
264
268
// to prevent blocking the main loop here.
265
269
rayon:: spawn ( move || {
266
270
match salsa:: Cancelled :: catch ( || {
267
- let mut reporter = R :: default ( ) ;
271
+ let mut reporter = IndicatifReporter :: from ( self . printer ) ;
268
272
db. check_with_reporter ( & mut reporter)
269
273
} ) {
270
274
Ok ( result) => {
@@ -299,10 +303,12 @@ impl MainLoop {
299
303
return Ok ( ExitStatus :: Success ) ;
300
304
}
301
305
302
- let mut stdout = stdout ( ) . lock ( ) ;
303
-
304
306
if result. is_empty ( ) {
305
- writeln ! ( stdout, "{}" , "All checks passed!" . green( ) . bold( ) ) ?;
307
+ writeln ! (
308
+ self . printer. stream_for_success_summary( ) ,
309
+ "{}" ,
310
+ "All checks passed!" . green( ) . bold( )
311
+ ) ?;
306
312
307
313
if self . watcher . is_none ( ) {
308
314
return Ok ( ExitStatus :: Success ) ;
@@ -311,14 +317,19 @@ impl MainLoop {
311
317
let mut max_severity = Severity :: Info ;
312
318
let diagnostics_count = result. len ( ) ;
313
319
320
+ let mut stdout = self . printer . stream_for_details ( ) . lock ( ) ;
314
321
for diagnostic in result {
315
- write ! ( stdout, "{}" , diagnostic. display( db, & display_config) ) ?;
322
+ // Only render diagnostics if they're going to be displayed, since doing
323
+ // so is expensive.
324
+ if stdout. is_enabled ( ) {
325
+ write ! ( stdout, "{}" , diagnostic. display( db, & display_config) ) ?;
326
+ }
316
327
317
328
max_severity = max_severity. max ( diagnostic. severity ( ) ) ;
318
329
}
319
330
320
331
writeln ! (
321
- stdout ,
332
+ self . printer . stream_for_failure_summary ( ) ,
322
333
"Found {} diagnostic{}" ,
323
334
diagnostics_count,
324
335
if diagnostics_count > 1 { "s" } else { "" }
@@ -378,27 +389,53 @@ impl MainLoop {
378
389
}
379
390
380
391
/// A progress reporter for `ty check`.
381
- #[ derive( Default ) ]
382
- struct IndicatifReporter ( Option < indicatif:: ProgressBar > ) ;
392
+ enum IndicatifReporter {
393
+ /// A constructed reporter that is not yet ready, contains the target for the progress bar.
394
+ Pending ( indicatif:: ProgressDrawTarget ) ,
395
+ /// A reporter that is ready, containing a progress bar to report to.
396
+ ///
397
+ /// Initialization of the bar is deferred to [`ty_project::ProgressReporter::set_files`] so we
398
+ /// do not initialize the bar too early as it may take a while to collect the number of files to
399
+ /// process and we don't want to display an empty "0/0" bar.
400
+ Initialized ( indicatif:: ProgressBar ) ,
401
+ }
402
+
403
+ impl From < Printer > for IndicatifReporter {
404
+ fn from ( printer : Printer ) -> Self {
405
+ Self :: Pending ( printer. progress_target ( ) )
406
+ }
407
+ }
383
408
384
- impl ty_project:: Reporter for IndicatifReporter {
409
+ impl ty_project:: ProgressReporter for IndicatifReporter {
385
410
fn set_files ( & mut self , files : usize ) {
386
- let progress = indicatif:: ProgressBar :: new ( files as u64 ) ;
387
- progress. set_style (
411
+ let target = match std:: mem:: replace (
412
+ self ,
413
+ IndicatifReporter :: Pending ( indicatif:: ProgressDrawTarget :: hidden ( ) ) ,
414
+ ) {
415
+ Self :: Pending ( target) => target,
416
+ Self :: Initialized ( _) => panic ! ( "The progress reporter should only be initialized once" ) ,
417
+ } ;
418
+
419
+ let bar = indicatif:: ProgressBar :: with_draw_target ( Some ( files as u64 ) , target) ;
420
+ bar. set_style (
388
421
indicatif:: ProgressStyle :: with_template (
389
422
"{msg:8.dim} {bar:60.green/dim} {pos}/{len} files" ,
390
423
)
391
424
. unwrap ( )
392
425
. progress_chars ( "--" ) ,
393
426
) ;
394
- progress. set_message ( "Checking" ) ;
395
-
396
- self . 0 = Some ( progress) ;
427
+ bar. set_message ( "Checking" ) ;
428
+ * self = Self :: Initialized ( bar) ;
397
429
}
398
430
399
431
fn report_file ( & self , _file : & ruff_db:: files:: File ) {
400
- if let Some ( ref progress_bar) = self . 0 {
401
- progress_bar. inc ( 1 ) ;
432
+ match self {
433
+ IndicatifReporter :: Initialized ( progress_bar) => {
434
+ progress_bar. inc ( 1 ) ;
435
+ }
436
+ IndicatifReporter :: Pending ( _) => {
437
+ panic ! ( "`report_file` called before `set_files`" )
438
+ }
402
439
}
403
440
}
404
441
}
0 commit comments