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 ;
11
+ use std:: io:: stdout;
10
12
use std:: process:: { ExitCode , Termination } ;
11
13
12
14
use anyhow:: Result ;
13
15
use std:: sync:: Mutex ;
14
16
15
17
use crate :: args:: { CheckCommand , Command , TerminalColor } ;
16
18
use crate :: logging:: setup_tracing;
19
+ use crate :: printer:: Printer ;
17
20
use anyhow:: { Context , anyhow} ;
18
21
use clap:: { CommandFactory , Parser } ;
19
22
use colored:: Colorize ;
@@ -25,7 +28,7 @@ use ruff_db::system::{OsSystem, SystemPath, SystemPathBuf};
25
28
use salsa:: plumbing:: ZalsaDatabase ;
26
29
use ty_project:: metadata:: options:: ProjectOptionsOverrides ;
27
30
use ty_project:: watch:: ProjectWatcher ;
28
- use ty_project:: { Db , DummyReporter , Reporter , watch} ;
31
+ use ty_project:: { Db , watch} ;
29
32
use ty_project:: { ProjectDatabase , ProjectMetadata } ;
30
33
use ty_server:: run_server;
31
34
@@ -49,7 +52,7 @@ pub fn run() -> anyhow::Result<ExitStatus> {
49
52
}
50
53
51
54
pub ( crate ) fn version ( ) -> Result < ( ) > {
52
- let mut stdout = BufWriter :: new ( io :: stdout ( ) . lock ( ) ) ;
55
+ let mut stdout = Printer :: default ( ) . stdout_important ( ) . lock ( ) ;
53
56
let version_info = crate :: version:: version ( ) ;
54
57
writeln ! ( stdout, "ty {}" , & version_info) ?;
55
58
Ok ( ( ) )
@@ -62,6 +65,8 @@ fn run_check(args: CheckCommand) -> anyhow::Result<ExitStatus> {
62
65
countme:: enable ( verbosity. is_trace ( ) ) ;
63
66
let _guard = setup_tracing ( verbosity, args. color . unwrap_or_default ( ) ) ?;
64
67
68
+ let printer = Printer :: default ( ) . with_verbosity ( verbosity) ;
69
+
65
70
tracing:: warn!(
66
71
"ty is pre-release software and not ready for production use. \
67
72
Expect to encounter bugs, missing features, and fatal errors.",
@@ -126,7 +131,8 @@ fn run_check(args: CheckCommand) -> anyhow::Result<ExitStatus> {
126
131
}
127
132
128
133
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) ;
130
136
131
137
// Listen to Ctrl+C and abort the watch mode.
132
138
let main_loop_cancellation_token = Mutex :: new ( Some ( main_loop_cancellation_token) ) ;
@@ -144,7 +150,7 @@ fn run_check(args: CheckCommand) -> anyhow::Result<ExitStatus> {
144
150
main_loop. run ( & mut db) ?
145
151
} ;
146
152
147
- let mut stdout = stdout ( ) . lock ( ) ;
153
+ let mut stdout = printer . stdout_important ( ) . lock ( ) ;
148
154
match std:: env:: var ( EnvVars :: TY_MEMORY_REPORT ) . as_deref ( ) {
149
155
Ok ( "short" ) => write ! ( stdout, "{}" , db. salsa_memory_dump( ) . display_short( ) ) ?,
150
156
Ok ( "mypy_primer" ) => write ! ( stdout, "{}" , db. salsa_memory_dump( ) . display_mypy_primer( ) ) ?,
@@ -195,12 +201,16 @@ struct MainLoop {
195
201
/// The file system watcher, if running in watch mode.
196
202
watcher : Option < ProjectWatcher > ,
197
203
204
+ /// Interface for displaying information to the user.
205
+ printer : Printer ,
206
+
198
207
project_options_overrides : ProjectOptionsOverrides ,
199
208
}
200
209
201
210
impl MainLoop {
202
211
fn new (
203
212
project_options_overrides : ProjectOptionsOverrides ,
213
+ printer : Printer ,
204
214
) -> ( Self , MainLoopCancellationToken ) {
205
215
let ( sender, receiver) = crossbeam_channel:: bounded ( 10 ) ;
206
216
@@ -210,6 +220,7 @@ impl MainLoop {
210
220
receiver,
211
221
watcher : None ,
212
222
project_options_overrides,
223
+ printer,
213
224
} ,
214
225
MainLoopCancellationToken { sender } ,
215
226
)
@@ -226,32 +237,24 @@ impl MainLoop {
226
237
227
238
// Do not show progress bars with `--watch`, indicatif does not seem to
228
239
// 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) ?;
230
243
231
244
Ok ( ExitStatus :: Success )
232
245
}
233
246
234
247
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
- {
242
248
self . sender . send ( MainLoopMessage :: CheckWorkspace ) . unwrap ( ) ;
243
249
244
- let result = self . main_loop :: < R > ( db) ;
250
+ let result = self . main_loop ( db) ;
245
251
246
252
tracing:: debug!( "Exiting main loop" ) ;
247
253
248
254
result
249
255
}
250
256
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 > {
255
258
// Schedule the first check.
256
259
tracing:: debug!( "Starting main loop" ) ;
257
260
@@ -267,7 +270,7 @@ impl MainLoop {
267
270
// to prevent blocking the main loop here.
268
271
rayon:: spawn ( move || {
269
272
match salsa:: Cancelled :: catch ( || {
270
- let mut reporter = R :: default ( ) ;
273
+ let mut reporter = IndicatifReporter :: from ( self . printer ) ;
271
274
db. check_with_reporter ( & mut reporter)
272
275
} ) {
273
276
Ok ( result) => {
@@ -297,15 +300,12 @@ impl MainLoop {
297
300
tracing:: warn!( "No python files found under the given path(s)" ) ;
298
301
}
299
302
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
-
307
303
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
+ ) ?;
309
309
310
310
if self . watcher . is_none ( ) {
311
311
return Ok ( ExitStatus :: Success ) ;
@@ -314,14 +314,16 @@ impl MainLoop {
314
314
let mut max_severity = Severity :: Info ;
315
315
let diagnostics_count = result. len ( ) ;
316
316
317
+ let mut stdout = self . printer . stdout ( ) . lock ( ) ;
318
+
317
319
for diagnostic in result {
318
320
write ! ( stdout, "{}" , diagnostic. display( db, & display_config) ) ?;
319
321
320
322
max_severity = max_severity. max ( diagnostic. severity ( ) ) ;
321
323
}
322
324
323
325
writeln ! (
324
- stdout ,
326
+ self . printer . stdout_important ( ) ,
325
327
"Found {} diagnostic{}" ,
326
328
diagnostics_count,
327
329
if diagnostics_count > 1 { "s" } else { "" }
@@ -383,27 +385,54 @@ impl MainLoop {
383
385
}
384
386
385
387
/// 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
+ }
388
400
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 {
390
411
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 (
393
423
indicatif:: ProgressStyle :: with_template (
394
424
"{msg:8.dim} {bar:60.green/dim} {pos}/{len} files" ,
395
425
)
396
426
. unwrap ( )
397
427
. progress_chars ( "--" ) ,
398
428
) ;
399
- progress. set_message ( "Checking" ) ;
400
-
401
- self . 0 = Some ( progress) ;
429
+ bar. set_message ( "Checking" ) ;
430
+ self . bar = Some ( bar) ;
402
431
}
403
432
404
433
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 ) ;
407
436
}
408
437
}
409
438
}
0 commit comments