diff options
-rw-r--r-- | gdb/testsuite/gdb.tui/tui-focus.exp | 85 | ||||
-rw-r--r-- | gdb/tui/tui-layout.c | 15 | ||||
-rw-r--r-- | gdb/tui/tui-layout.h | 49 | ||||
-rw-r--r-- | gdb/tui/tui-win.c | 40 |
4 files changed, 179 insertions, 10 deletions
diff --git a/gdb/testsuite/gdb.tui/tui-focus.exp b/gdb/testsuite/gdb.tui/tui-focus.exp index 9e2a1cd..72f8052 100644 --- a/gdb/testsuite/gdb.tui/tui-focus.exp +++ b/gdb/testsuite/gdb.tui/tui-focus.exp @@ -13,12 +13,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -# Minimal testcase that just checks that the various "layout $foo" -# commands do not cause gdb to crash. +# Testcase that just checks the tui 'focus' command works as expected. require allow_tui_tests tuiterm_env +load_lib gdb-python.exp standard_testfile @@ -31,7 +31,8 @@ if {[prepare_for_testing "failed to prepare" ${testfile} ${srcfile}]} { # Each test specification is a tuple where the first item is the name of a # window, and the second item is a boolean indicating if we expect that # window to be present in the default (src) layout. -foreach spec {{src true} {cmd true} {status true} {regs false} {asm false}} { +foreach spec {{src true} {cmd true} {status true} {regs false} \ + {asm false} {unknown false}} { lassign $spec window valid_p with_test_prefix "window=$window" { @@ -54,8 +55,13 @@ foreach spec {{src true} {cmd true} {status true} {regs false} {asm false}} { "^Focus set to $window window\\.\\s*" } } else { - Term::check_region_contents "check focus error" 0 16 80 1 \ - "^Unrecognized window name \"$window\"\\s*" + if {$window == "unknown"} { + Term::check_region_contents "check focus error" 0 16 80 1 \ + "^Unrecognized window name \"$window\"\\s*" + } else { + Term::check_region_contents "check focus error" 0 16 80 1 \ + "^Window \"$window\" is not in the current layout\\s*" + } } Term::check_box "check src box" 0 0 80 15 @@ -66,3 +72,72 @@ foreach spec {{src true} {cmd true} {status true} {regs false} {asm false}} { Term::command "focus prev" } } + +# Use the Python TUI API to exercise some of the ambigous window name +# handling parts of the 'focus' command. +Term::clean_restart 24 80 $binfile +if {[allow_python_tests]} { + # Create a very simple tui window. + gdb_py_test_silent_cmd \ + [multi_line_input \ + "python" \ + "class TestWindow:" \ + " def __init__(self, win):" \ + " pass" \ + "" \ + " def render(self):" \ + " pass" \ + "end"] \ + "setup dummy window class" \ + true + + # Register the window with a set of similar names. + gdb_test_no_output "python gdb.register_window_type(\"test1\", TestWindow)" + gdb_test_no_output "python gdb.register_window_type(\"test2\", TestWindow)" + gdb_test_no_output "python gdb.register_window_type(\"test3\", TestWindow)" + + # Create a layout containing just one of the above windows. + gdb_test_no_output "tui new-layout example1 test2 1 status 1 cmd 1" + + # Create a layout containing two of the above windows. + gdb_test_no_output "tui new-layout example2 test1 1 test2 1 status 1 cmd 1" + + if {![Term::prepare_for_tui]} { + unsupported "TUI not supported" + return + } + + # Try to focus using an ambiguous, partial window name. This + # should fail as the default layout (src) doesn't include any + # windows matching this name. + Term::command_no_prompt_prefix "focus test" + Term::check_region_contents "check no matching window focus message" \ + 0 16 80 1 \ + "^No windows matching \"test\" in the current layout\\s*" + + # Now select a layout that includes a single window that matches + # the ambiguous, partial name 'test', and disable tui mode. + Term::command "layout example1" + send_gdb "tui disable\n" + + # Reactivate tui mode and try to set focus using the ambiguous, + # partial window name. This should succeed though, as, within the + # current layout, the partial name is not actually ambiguous. + send_gdb "focus test\n" + gdb_assert [Term::wait_for_region_contents 0 19 80 1 \ + "^Focus set to test2 window\\.\\s*"] \ + "check test2 focus message" + + # Now select a layout that includes two windows that matches the + # ambiguous, partial name 'test', and disable tui mode. + Term::command "layout example2" + send_gdb "tui disable\n" + + # Reactivate tui mode and try to set focus using the ambiguous, + # partial window name. This will fail as now the layout includes + # multiple windows that match 'test'. + send_gdb "focus test\n" + gdb_assert [Term::wait_for_region_contents 0 22 80 1 \ + "^Window name \"test\" is ambiguous\\s*"] \ + "check ambiguous focus message" +} diff --git a/gdb/tui/tui-layout.c b/gdb/tui/tui-layout.c index 27abee0..ecdcd48 100644 --- a/gdb/tui/tui-layout.c +++ b/gdb/tui/tui-layout.c @@ -29,7 +29,6 @@ #include "cli/cli-decode.h" #include "cli/cli-utils.h" #include <ctype.h> -#include <unordered_map> #include <unordered_set> #include "tui/tui.h" @@ -351,7 +350,17 @@ make_standard_window (const char *) shut down, causing crashes if any window destruction requires running Python code. */ -static std::unordered_map<std::string, window_factory> *known_window_types; +static window_types_map *known_window_types; + +/* See tui-layout.h. */ + +known_window_names_range +all_known_window_names () +{ + auto begin = known_window_names_iterator (known_window_types->begin ()); + auto end = known_window_names_iterator (known_window_types->end ()); + return known_window_names_range (begin, end); +} /* Helper function that returns a TUI window, given its name. */ @@ -377,7 +386,7 @@ tui_get_window_by_name (const std::string &name) static void initialize_known_windows () { - known_window_types = new std::unordered_map<std::string, window_factory>; + known_window_types = new window_types_map; known_window_types->emplace (SRC_NAME, make_standard_window<SRC_WIN, diff --git a/gdb/tui/tui-layout.h b/gdb/tui/tui-layout.h index 206f111..ff354fb 100644 --- a/gdb/tui/tui-layout.h +++ b/gdb/tui/tui-layout.h @@ -26,6 +26,9 @@ #include "tui/tui.h" #include "tui/tui-data.h" +#include "gdbsupport/iterator-range.h" + +#include <unordered_map> /* Values that can be returned when handling a request to adjust a window's size. */ @@ -358,10 +361,56 @@ extern void tui_adjust_window_width (struct tui_win_info *win, typedef std::function<tui_win_info * (const char *name)> window_factory; +/* The type for a data structure that maps a window name to that window's + factory function. */ +typedef std::unordered_map<std::string, window_factory> window_types_map; + /* Register a new TUI window type. NAME is the name of the window type. FACTORY is a function that can be called to instantiate the window. */ extern void tui_register_window (const char *name, window_factory &&factory); +/* An iterator class that exposes just the window names from the + known_window_types map in tui-layout.c. This is just a wrapper around + an iterator of the underlying known_window_types map, but this just + exposes the window names. */ + +struct known_window_names_iterator +{ + using known_window_types_iterator = window_types_map::iterator; + + known_window_names_iterator (known_window_types_iterator &&iter) + : m_iter (std::move (iter)) + { /* Nothing. */ } + + known_window_names_iterator &operator++ () + { + ++m_iter; + return *this; + } + + const std::string &operator* () const + { return (*m_iter).first; } + + bool operator!= (const known_window_names_iterator &other) const + { return m_iter != other.m_iter; } + +private: + + /* The underlying iterator. */ + known_window_types_iterator m_iter; +}; + +/* A range adapter that makes it possible to iterate over the names of all + known tui windows. */ + +using known_window_names_range + = iterator_range<known_window_names_iterator>; + +/* Return a range that can be used to walk over the name of all known tui + windows in a range-for loop. */ + +extern known_window_names_range all_known_window_names (); + #endif /* TUI_TUI_LAYOUT_H */ diff --git a/gdb/tui/tui-win.c b/gdb/tui/tui-win.c index 492a519..008189e 100644 --- a/gdb/tui/tui-win.c +++ b/gdb/tui/tui-win.c @@ -728,8 +728,44 @@ tui_set_focus_command (const char *arg, int from_tty) else win_info = tui_partial_win_by_name (arg); - if (win_info == NULL) - error (_("Unrecognized window name \"%s\""), arg); + if (win_info == nullptr) + { + /* When WIN_INFO is nullptr this can either mean that the window name + is unknown to GDB, or that the window is not in the current + layout. To try and help the user, give a different error + depending on which of these is the case. */ + std::string matching_window_name; + bool is_ambiguous = false; + + for (const std::string &name : all_known_window_names ()) + { + /* Look through all windows in the current layout, if the window + is in the current layout then we're not interested is it. */ + for (tui_win_info *item : all_tui_windows ()) + if (item->name () == name) + continue; + + if (startswith (name, arg)) + { + if (matching_window_name.empty ()) + matching_window_name = name; + else + is_ambiguous = true; + } + }; + + if (!matching_window_name.empty ()) + { + if (is_ambiguous) + error (_("No windows matching \"%s\" in the current layout"), + arg); + else + error (_("Window \"%s\" is not in the current layout"), + matching_window_name.c_str ()); + } + else + error (_("Unrecognized window name \"%s\""), arg); + } /* If a window is part of the current layout then it will have a tui_win_info associated with it and be visible, otherwise, there will |