Skip to content

Commit 336ba68

Browse files
committed
feat(rewatch): add watch --clear-screen
Make screen clearing opt-in so scripted stdout stays stable while interactive watch mode can drop stale warnings and errors between rebuilds. Also print explicit rebuild and failure markers so fixed vs broken state is easier to spot. Refs #8139 Signed-off-by: Paul Kim <paul.bushuo@gmail.com>
1 parent a8258b6 commit 336ba68

3 files changed

Lines changed: 114 additions & 28 deletions

File tree

rewatch/src/cli.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,16 @@ mod tests {
383383
}
384384
}
385385

386+
#[test]
387+
fn watch_clear_screen_flag_is_parsed() {
388+
let cli = parse(&["rescript", "watch", "--clear-screen"]).expect("expected watch command");
389+
390+
match cli.command {
391+
Command::Watch(watch_args) => assert!(watch_args.clear_screen),
392+
other => panic!("expected watch command, got {other:?}"),
393+
}
394+
}
395+
386396
#[test]
387397
fn clean_prod_flag_is_parsed() {
388398
let cli = parse(&["rescript", "clean", "--prod"]).expect("expected clean command");
@@ -428,6 +438,10 @@ pub struct WatchArgs {
428438
#[command(flatten)]
429439
pub warn_error: WarnErrorArg,
430440

441+
/// Clear terminal screen before each rebuild in interactive watch mode.
442+
#[arg(long, default_value_t = false)]
443+
pub clear_screen: bool,
444+
431445
/// Skip dev-dependencies and dev sources (type: "dev")
432446
#[arg(long, default_value_t = false)]
433447
pub prod: bool,
@@ -440,6 +454,7 @@ impl From<BuildArgs> for WatchArgs {
440454
filter: build_args.filter,
441455
after_build: build_args.after_build,
442456
warn_error: build_args.warn_error,
457+
clear_screen: false,
443458
prod: build_args.prod,
444459
}
445460
}

rewatch/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ fn main() -> Result<()> {
7979
true, // create_sourcedirs is now always enabled
8080
plain_output,
8181
(*watch_args.warn_error).clone(),
82+
watch_args.clear_screen,
8283
watch_args.prod,
8384
) {
8485
Err(e) => {

rewatch/src/watcher.rs

Lines changed: 98 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use crate::lock::LockKind;
99
use crate::queue::FifoQueue;
1010
use crate::queue::*;
1111
use anyhow::{Context, Result};
12+
use console::Term;
1213
use futures_timer::Delay;
1314
use notify::event::ModifyKind;
1415
use notify::{Config, Error, Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher};
@@ -177,6 +178,31 @@ fn carry_forward_compile_warnings(previous: &BuildCommandState, next: &mut Build
177178
}
178179
}
179180

181+
fn should_clear_screen(
182+
clear_screen: bool,
183+
show_progress: bool,
184+
plain_output: bool,
185+
initial_build: bool,
186+
) -> bool {
187+
clear_screen && show_progress && !plain_output && !initial_build
188+
}
189+
190+
fn clear_terminal_screen() {
191+
let _ = Term::stdout().clear_screen();
192+
}
193+
194+
fn print_rebuild_header(compile_type: CompileType) {
195+
match compile_type {
196+
CompileType::Incremental => println!("Change detected. Rebuilding..."),
197+
CompileType::Full => println!("Change detected. Full rebuild..."),
198+
CompileType::None => (),
199+
}
200+
}
201+
202+
fn print_build_failed_footer() {
203+
println!("\nBuild failed. Watching for changes...");
204+
}
205+
180206
struct AsyncWatchArgs<'a> {
181207
watcher: &'a mut RecommendedWatcher,
182208
current_watch_paths: Vec<(PathBuf, RecursiveMode)>,
@@ -188,6 +214,7 @@ struct AsyncWatchArgs<'a> {
188214
after_build: Option<String>,
189215
create_sourcedirs: bool,
190216
plain_output: bool,
217+
clear_screen: bool,
191218
prod: bool,
192219
}
193220

@@ -203,6 +230,7 @@ async fn async_watch(
203230
after_build,
204231
create_sourcedirs,
205232
plain_output,
233+
clear_screen,
206234
prod,
207235
}: AsyncWatchArgs<'_>,
208236
) -> Result<()> {
@@ -377,40 +405,60 @@ async fn async_watch(
377405

378406
match needs_compile_type {
379407
CompileType::Incremental => {
408+
if should_clear_screen(clear_screen, show_progress, plain_output, initial_build) {
409+
clear_terminal_screen();
410+
print_rebuild_header(CompileType::Incremental);
411+
}
412+
380413
let timing_total = Instant::now();
381-
if let Ok(result) = build::incremental_build(
414+
let result = build::incremental_build(
382415
&mut build_state,
383416
None,
384417
initial_build,
385418
show_progress,
386419
!initial_build,
387420
create_sourcedirs,
388421
plain_output,
389-
) {
390-
if let Some(a) = after_build.clone() {
391-
cmd::run(a)
422+
);
423+
424+
match result {
425+
Ok(result) => {
426+
if let Some(a) = after_build.clone() {
427+
cmd::run(a)
428+
}
429+
let timing_total_elapsed = timing_total.elapsed();
430+
if show_progress {
431+
let compilation_type = if initial_build { "initial" } else { "incremental" };
432+
if plain_output {
433+
println!("Finished {compilation_type} compilation")
434+
} else {
435+
println!(
436+
"\n{}\n",
437+
build::format_finished_compilation_message(
438+
Some(compilation_type),
439+
result,
440+
timing_total_elapsed,
441+
)
442+
);
443+
}
444+
}
392445
}
393-
let timing_total_elapsed = timing_total.elapsed();
394-
if show_progress {
395-
let compilation_type = if initial_build { "initial" } else { "incremental" };
396-
if plain_output {
397-
println!("Finished {compilation_type} compilation")
398-
} else {
399-
println!(
400-
"\n{}\n",
401-
build::format_finished_compilation_message(
402-
Some(compilation_type),
403-
result,
404-
timing_total_elapsed,
405-
)
406-
);
446+
Err(_) => {
447+
if should_clear_screen(clear_screen, show_progress, plain_output, initial_build) {
448+
print_build_failed_footer();
407449
}
408450
}
409451
}
452+
410453
needs_compile_type = CompileType::None;
411454
initial_build = false;
412455
}
413456
CompileType::Full => {
457+
if should_clear_screen(clear_screen, show_progress, plain_output, initial_build) {
458+
clear_terminal_screen();
459+
print_rebuild_header(CompileType::Full);
460+
}
461+
414462
let timing_total = Instant::now();
415463
let mut next_build_state = build::initialize_build(
416464
None,
@@ -443,17 +491,28 @@ async fn async_watch(
443491
create_sourcedirs,
444492
plain_output,
445493
);
446-
if let Ok(result) = result {
447-
if let Some(a) = after_build.clone() {
448-
cmd::run(a)
449-
}
494+
match result {
495+
Ok(result) => {
496+
if let Some(a) = after_build.clone() {
497+
cmd::run(a)
498+
}
450499

451-
let timing_total_elapsed = timing_total.elapsed();
452-
if !plain_output && show_progress {
453-
println!(
454-
"\n{}\n",
455-
build::format_finished_compilation_message(None, result, timing_total_elapsed,)
456-
);
500+
let timing_total_elapsed = timing_total.elapsed();
501+
if !plain_output && show_progress {
502+
println!(
503+
"\n{}\n",
504+
build::format_finished_compilation_message(
505+
None,
506+
result,
507+
timing_total_elapsed,
508+
)
509+
);
510+
}
511+
}
512+
Err(_) => {
513+
if should_clear_screen(clear_screen, show_progress, plain_output, initial_build) {
514+
print_build_failed_footer();
515+
}
457516
}
458517
}
459518

@@ -479,6 +538,7 @@ pub fn start(
479538
create_sourcedirs: bool,
480539
plain_output: bool,
481540
warn_error: Option<String>,
541+
clear_screen: bool,
482542
prod: bool,
483543
) -> Result<()> {
484544
futures::executor::block_on(async {
@@ -518,6 +578,7 @@ pub fn start(
518578
after_build,
519579
create_sourcedirs,
520580
plain_output,
581+
clear_screen,
521582
prod,
522583
})
523584
.await
@@ -648,6 +709,15 @@ mod tests {
648709
}
649710
}
650711

712+
#[test]
713+
fn clears_screen_only_for_non_initial_interactive_rebuilds() {
714+
assert!(should_clear_screen(true, true, false, false));
715+
assert!(!should_clear_screen(true, true, false, true));
716+
assert!(!should_clear_screen(true, true, true, false));
717+
assert!(!should_clear_screen(true, false, false, false));
718+
assert!(!should_clear_screen(false, true, false, false));
719+
}
720+
651721
#[test]
652722
fn carries_forward_implementation_warnings_for_matching_module_paths() {
653723
let previous = test_build_state(

0 commit comments

Comments
 (0)