[gdb/cli] Add a progress meter This patch adds a progress bar. It's not used anywhere yet. gdb/ChangeLog: 2020-12-11 Tom Tromey Tom de Vries * utils.h (get_chars_per_line): Declare. * utils.c (get_chars_per_line): New function. (fputs_maybe_filtered): Handle '\r'. * ui-out.h (ui_out::progress_meter): New class. (ui_out::progress, ui_out::do_progress_start) (ui_out::do_progress_notify, ui_out::do_progress_end): New methods. * ui-out.c (do_progress_end) (make_cleanup_ui_out_progress_begin_end, ui_out_progress): New functions. * mi/mi-out.h (mi_ui_out::do_progress_start) (mi_ui_out::do_progress_notify, mi_ui_out::do_progress_end): New methods. * cli-out.h (struct cli_ui_out) : New methods. : New. : New member. * cli-out.c (cli_ui_out::do_progress_start) (cli_ui_out::do_progress_notify, cli_ui_out::do_progress_end): New methods. --- gdb/cli-out.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ gdb/cli-out.h | 29 ++++++++++++++++++++ gdb/mi/mi-out.h | 12 ++++++++ gdb/ui-out.h | 36 ++++++++++++++++++++++++ gdb/utils.c | 8 ++++++ gdb/utils.h | 4 +++ 6 files changed, 174 insertions(+) diff --git a/gdb/cli-out.c b/gdb/cli-out.c index e47272ad87..5d07426b73 100644 --- a/gdb/cli-out.c +++ b/gdb/cli-out.c @@ -265,6 +265,91 @@ cli_ui_out::do_redirect (ui_file *outstream) m_streams.pop_back (); } +void +cli_ui_out::do_progress_start (const std::string &name, int should_print) +{ + struct ui_file *stream = m_streams.back (); + cli_progress_info meter; + + meter.last_value = 0; + meter.name = name; + if (!stream->isatty ()) + { + fprintf_unfiltered (stream, "%s...", meter.name.c_str ()); + gdb_flush (stream); + meter.printing = WORKING; + } + else + { + /* Don't actually emit anything until the first call notify us + of progress. This makes it so a second progress message can + be started before the first one has been notified, without + messy output. */ + meter.printing = should_print ? START : NO_PRINT; + } + + m_meters.push_back (meter); +} + +void +cli_ui_out::do_progress_notify (double howmuch) +{ + struct ui_file *stream = m_streams.back (); + cli_progress_info &meter (m_meters.back ()); + + if (meter.printing == NO_PRINT) + return; + + if (meter.printing == START) + { + fprintf_unfiltered (stream, "%s\n", meter.name.c_str ()); + gdb_flush (stream); + meter.printing = WORKING; + } + + if (stream->isatty ()) + { + int i, max; + int width = get_chars_per_line () - 3; + + max = width * howmuch; + fprintf_unfiltered (stream, "\r["); + for (i = 0; i < width; ++i) + fprintf_unfiltered (stream, i < max ? "#" : " "); + fprintf_unfiltered (stream, "]"); + gdb_flush (stream); + } +} + +void +cli_ui_out::do_progress_end () +{ + struct ui_file *stream = m_streams.back (); + cli_progress_info &meter = m_meters.back (); + + if (meter.printing != NO_PRINT) + { + if (stream->isatty ()) + { + int i; + int width = get_chars_per_line () - 3; + + fprintf_unfiltered (stream, "\r"); + for (i = 0; i < width + 2; ++i) + fprintf_unfiltered (stream, " "); + fprintf_unfiltered (stream, "\r"); + gdb_flush (stream); + } + else + { + fprintf_unfiltered (stream, "done.\n"); + gdb_flush (stream); + } + } + + m_meters.pop_back (); +} + /* local functions */ void diff --git a/gdb/cli-out.h b/gdb/cli-out.h index 84e957ca89..0c45b77d07 100644 --- a/gdb/cli-out.h +++ b/gdb/cli-out.h @@ -71,6 +71,10 @@ class cli_ui_out : public ui_out virtual void do_flush () override; virtual void do_redirect (struct ui_file *outstream) override; + virtual void do_progress_start (const std::string &, int) override; + virtual void do_progress_notify (double) override; + virtual void do_progress_end () override; + bool suppress_output () { return m_suppress_output; } @@ -80,6 +84,31 @@ class cli_ui_out : public ui_out std::vector m_streams; bool m_suppress_output; + + /* Represents the printing state of a progress meter. */ + enum meter_state + { + /* Printing will start with the next output. */ + START, + /* Printing has already started. */ + WORKING, + /* Printing should not be done. */ + NO_PRINT + }; + + /* The state of a recent progress meter. */ + struct cli_progress_info + { + /* The current state. */ + enum meter_state printing; + /* The name to print. */ + std::string name; + /* The last notification value. */ + double last_value; + }; + + /* Stack of progress meters. */ + std::vector m_meters; }; extern cli_ui_out *cli_out_new (struct ui_file *stream); diff --git a/gdb/mi/mi-out.h b/gdb/mi/mi-out.h index de4b3e01a4..4be0decd14 100644 --- a/gdb/mi/mi-out.h +++ b/gdb/mi/mi-out.h @@ -83,6 +83,18 @@ class mi_ui_out : public ui_out virtual bool do_is_mi_like_p () const override { return true; } + virtual void do_progress_start (const std::string &, int) override + { + } + + virtual void do_progress_notify (double) override + { + } + + virtual void do_progress_end () override + { + } + private: void field_separator (); diff --git a/gdb/ui-out.h b/gdb/ui-out.h index 9fc60614d6..9ae4eeb83a 100644 --- a/gdb/ui-out.h +++ b/gdb/ui-out.h @@ -275,6 +275,38 @@ class ui_out escapes. */ virtual bool can_emit_style_escape () const = 0; + /* An object that starts and finishes a progress meter. */ + class progress_meter + { + public: + progress_meter (struct ui_out *uiout, const std::string &name, + int should_print) + : m_uiout (uiout) + { + m_uiout->do_progress_start (name, should_print); + } + + ~progress_meter () + { + m_uiout->do_progress_notify (1.0); + m_uiout->do_progress_end (); + } + + progress_meter (const progress_meter &) = delete; + progress_meter &operator= (const progress_meter &) = delete; + + private: + + struct ui_out *m_uiout; + }; + + /* Emit some progress corresponding to the most recently created + progress meter. HOWMUCH may range from 0.0 to 1.0. */ + void progress (double howmuch) + { + do_progress_notify (howmuch); + } + protected: virtual void do_table_begin (int nbrofcols, int nr_rows, const char *tblid) @@ -309,6 +341,10 @@ class ui_out virtual void do_flush () = 0; virtual void do_redirect (struct ui_file *outstream) = 0; + virtual void do_progress_start (const std::string &, int) = 0; + virtual void do_progress_notify (double) = 0; + virtual void do_progress_end () = 0; + /* Set as not MI-like by default. It is overridden in subclasses if necessary. */ diff --git a/gdb/utils.c b/gdb/utils.c index 3226656e2c..b1d1694584 100644 --- a/gdb/utils.c +++ b/gdb/utils.c @@ -1579,6 +1579,14 @@ gdb_flush (struct ui_file *stream) stream->flush (); } +/* See utils.h. */ + +int +get_chars_per_line (void) +{ + return chars_per_line; +} + /* Indicate that if the next sequence of characters overflows the line, a newline should be inserted here rather than when it hits the end. If INDENT is non-null, it is a string to be printed to indent the diff --git a/gdb/utils.h b/gdb/utils.h index a8c65ed817..5ae97b7469 100644 --- a/gdb/utils.h +++ b/gdb/utils.h @@ -364,6 +364,10 @@ extern void wrap_here (const char *); extern void reinitialize_more_filter (void); +/* Return the number of characters in a line. */ + +extern int get_chars_per_line (void); + extern bool pagination_enabled; extern struct ui_file **current_ui_gdb_stdout_ptr (void);