diff --git a/infer/src/base/Logging.ml b/infer/src/base/Logging.ml index a62257d7f..6da67f093 100644 --- a/infer/src/base/Logging.ml +++ b/infer/src/base/Logging.ml @@ -91,12 +91,19 @@ let color_console ?(use_stdout= false) scheme = let can_colorize = Unix.(isatty (if use_stdout then stdout else stderr)) in if can_colorize then ( let styles = term_styles_of_style scheme in + let orig_out_functions = F.pp_get_formatter_out_functions formatter () in let out_string s p n = - let print = if use_stdout then ANSITerminal.print_string else ANSITerminal.prerr_string in - print styles (String.slice s p n) + let s = ANSITerminal.sprintf styles "%s" (String.slice s p n) in + orig_out_functions.F.out_string s 0 (String.length s) + in + let out_newline () = + (* erase to end-of-line to avoid garbage, in particular when writing over the taskbar *) + let erase_eol = "\027[0K" in + orig_out_functions.F.out_string erase_eol 0 (String.length erase_eol) ; + orig_out_functions.F.out_newline () in F.pp_set_formatter_out_functions formatter - {(F.pp_get_formatter_out_functions formatter ()) with F.out_string} ; + {(F.pp_get_formatter_out_functions formatter ()) with F.out_string; out_newline} ; formatter ) else formatter diff --git a/infer/src/base/TaskBar.ml b/infer/src/base/TaskBar.ml index 4ac110465..489cdc37c 100644 --- a/infer/src/base/TaskBar.ml +++ b/infer/src/base/TaskBar.ml @@ -38,13 +38,19 @@ type t = | Dummy (** ignore everything *) (** print [c] [n] times *) -let rec pp_n c oc n = +let rec pp_n c fmt n = if n > 0 then ( - Out_channel.output_char oc c ; - pp_n c oc (n - 1) ) + F.pp_print_char fmt c ; + pp_n c fmt (n - 1) ) -let draw_top_bar ~term_width ~total ~finished ~elapsed = +let move_bol = "\r" + +let move_cursor_up n = Printf.sprintf "\027[%iA" n + +let erase_eol = "\027[0K" + +let draw_top_bar fmt ~term_width ~total ~finished ~elapsed = let tasks_total_string = Int.to_string total in let bar_tasks_num_size = String.length tasks_total_string in let elapsed_string = F.asprintf "%a" Mtime.Span.pp elapsed in @@ -56,7 +62,7 @@ let draw_top_bar ~term_width ~total ~finished ~elapsed = let ( +++ ) (f1, l1) f2 = (f1 ^^ f2, l1 + (string_of_format f2 |> String.length)) in ("%*d", bar_tasks_num_size (* finished *)) +++ "/" ++ ("%s", bar_tasks_num_size (* total *)) +++ " [" ++ ("%a%a", 0 (* progress bar *)) +++ "] " - ++ ("%d%%", 3 (* "xx%", even though sometimes it's just "x%" *)) +++ " " + ++ ("%d%%", 3 (* "xxx%", even though sometimes it's just "x%" *)) +++ " " ++ ( "%s" , max (String.length elapsed_string) 9 (* leave some room for elapsed_string to avoid flicker. 9 characters is "XXhXXmXXs" so it @@ -67,40 +73,38 @@ let draw_top_bar ~term_width ~total ~finished ~elapsed = let progress_bar_size = top_bar_size - size_around_progress_bar in ( if progress_bar_size < min_acceptable_progress_bar then let s = Printf.sprintf "%d/%s %s" finished tasks_total_string elapsed_string in - Out_channel.output_string stderr (String.prefix s term_width) + F.fprintf fmt "%s" (String.prefix s term_width) else let bar_done_size = finished * progress_bar_size / total in - Printf.eprintf top_bar_fmt bar_tasks_num_size finished tasks_total_string (pp_n '#') + F.fprintf fmt top_bar_fmt bar_tasks_num_size finished tasks_total_string (pp_n '#') bar_done_size (pp_n '.') (progress_bar_size - bar_done_size) (finished * 100 / total) elapsed_string ) ; - ANSITerminal.erase Eol ; - Out_channel.output_string stderr "\n" + F.fprintf fmt "%s\n" erase_eol -let draw_job_status ~term_width ~draw_time t ~status ~t0 = +let draw_job_status fmt ~term_width ~draw_time t ~status ~t0 = let length = ref 0 in let job_prefix_size = String.length job_prefix in if term_width > job_prefix_size then ( - ANSITerminal.(prerr_string [Bold; magenta]) job_prefix ; + F.fprintf fmt "%s" (ANSITerminal.(sprintf [Bold; magenta]) "%s" job_prefix) ; length := !length + job_prefix_size ) ; let time_width = 4 + (* actually drawing the time *) 3 (* "[] " *) in if draw_time && term_width > time_width + job_prefix_size then ( let time_running = Mtime.span t0 t |> Mtime.Span.to_s in - Printf.eprintf "[%4.1fs] " time_running ; + F.fprintf fmt "[%4.1fs] " time_running ; length := !length + time_width ) ; - String.prefix status (term_width - !length) |> Out_channel.output_string stderr ; - ANSITerminal.erase Eol ; - Out_channel.output_string stderr "\n" + F.fprintf fmt "%s%s\n" (String.prefix status (term_width - !length)) erase_eol let refresh_multiline task_bar = - ANSITerminal.move_bol () ; let should_draw_progress_bar = task_bar.tasks_total > 0 && task_bar.tasks_done >= 0 in let term_width, _ = ANSITerminal.size () in + F.pp_print_string F.err_formatter move_bol ; if should_draw_progress_bar then - draw_top_bar ~term_width ~total:task_bar.tasks_total ~finished:task_bar.tasks_done + draw_top_bar F.err_formatter ~term_width ~total:task_bar.tasks_total + ~finished:task_bar.tasks_done ~elapsed:(Mtime_clock.count task_bar.start_time) ; let draw_time = (* When there is only 1 job we are careful not to spawn processes needlessly, thus there is no @@ -110,12 +114,12 @@ let refresh_multiline task_bar = in let now = Mtime_clock.now () in Array.iter2_exn task_bar.jobs_statuses task_bar.jobs_start_times ~f:(fun status t0 -> - draw_job_status ~term_width ~draw_time now ~status ~t0 ) ; + draw_job_status F.err_formatter ~term_width ~draw_time now ~status ~t0 ) ; let lines_printed = let progress_bar = if should_draw_progress_bar then 1 else 0 in task_bar.jobs + progress_bar in - ANSITerminal.move_cursor 0 (-lines_printed) ; + F.eprintf "%s%!" (move_cursor_up lines_printed) ; () diff --git a/infer/src/istd/IStd.ml b/infer/src/istd/IStd.ml index 681f103be..49be92638 100644 --- a/infer/src/istd/IStd.ml +++ b/infer/src/istd/IStd.ml @@ -31,60 +31,8 @@ let exit = `In_general_prefer_using_Logging_exit_over_Pervasives_exit module ANSITerminal : module type of ANSITerminal = struct include ANSITerminal - (* from ANSITerminal_unix.ml but using stderr instead of stdout *) - (* Cursor *) - - let set_cursor x y = - if Unix.(isatty stderr) then - if x <= 0 then ( if y > 0 then Printf.eprintf "\027[%id%!" y ) - else if (* x > 0 *) y <= 0 then Printf.eprintf "\027[%iG%!" x - else Printf.eprintf "\027[%i;%iH%!" y x - - - let move_cursor x y = - if Unix.(isatty stderr) then ( - if x > 0 then Printf.eprintf "\027[%iC%!" x - else if x < 0 then Printf.eprintf "\027[%iD%!" (-x) ; - if y > 0 then Printf.eprintf "\027[%iB%!" y - else if y < 0 then Printf.eprintf "\027[%iA%!" (-y) ) - - - let save_cursor () = if Unix.(isatty stderr) then Printf.eprintf "\027[s%!" - - let restore_cursor () = if Unix.(isatty stderr) then Printf.eprintf "\027[u%!" - - let move_bol () = - Out_channel.output_string stderr "\r" ; - Out_channel.flush stderr - - - (* Erasing *) - - let erase loc = - if Unix.(isatty stderr) then ( - Out_channel.output_string stderr - ( match loc with - | Eol -> - "\027[K" - | Above -> - "\027[1J" - | Below -> - "\027[0J" - | Screen -> - "\027[2J" ) ; - Out_channel.flush stderr ) - - - (* Scrolling *) - - let scroll lines = - if Unix.(isatty stderr) then - if lines > 0 then Printf.eprintf "\027[%iS%!" lines - else if lines < 0 then Printf.eprintf "\027[%iT%!" (-lines) - - - (* /from ANSITerminal_unix.ml but using stderr instead of stdout *) (* more careful about when the channel is connected to a tty *) + let print_string = if Unix.(isatty stdout) then print_string else fun _ -> Pervasives.print_string