Allow TUI sub-layouts in "new-layout" command

The new TUI layout engine has support for "sub-layouts" -- this is a
layout that includes another layout as a child.  A sub-layout is
treated as a unit when allocating space.

There's not a very strong reason to use sub-layouts currently.  This
patch exists to introduce the idea, and to simplify the subsequent
patch that adds horizontal layouts -- where sub-layouts are needed.

Because this patch won't go in on its own, I chose to defer
documenting this change until the subsequent horizontal layout patch.

gdb/ChangeLog
2020-02-22  Tom Tromey  <tom@tromey.com>

	* tui/tui-layout.h (class tui_layout_split) <add_split>: Change
	parameter and return types.
	(class tui_layout_base) <specification>: Add "depth".
	(class tui_layout_window) <specification>: Add "depth".
	(class tui_layout_split) <specification>: Add "depth".
	* tui/tui-layout.c (tui_layout_split::add_split): Change parameter
	and return types.
	(tui_new_layout_command): Parse sub-layouts.
	(_initialize_tui_layout): Update help string.
	(tui_layout_window::specification): Add "depth".
	(add_layout_command): Update.

gdb/testsuite/ChangeLog
2020-02-22  Tom Tromey  <tom@tromey.com>

	* gdb.tui/new-layout.exp: Add sub-layout tests.

Change-Id: Iddf52d067a552c168b8a67f29caf7ac86404b10c
This commit is contained in:
Tom Tromey
2020-02-22 11:48:26 -07:00
parent ee325b61cd
commit c22fef7e4c
5 changed files with 100 additions and 24 deletions

View File

@ -1,3 +1,17 @@
2020-02-22 Tom Tromey <tom@tromey.com>
* tui/tui-layout.h (class tui_layout_split) <add_split>: Change
parameter and return types.
(class tui_layout_base) <specification>: Add "depth".
(class tui_layout_window) <specification>: Add "depth".
(class tui_layout_split) <specification>: Add "depth".
* tui/tui-layout.c (tui_layout_split::add_split): Change parameter
and return types.
(tui_new_layout_command): Parse sub-layouts.
(_initialize_tui_layout): Update help string.
(tui_layout_window::specification): Add "depth".
(add_layout_command): Update.
2020-02-22 Tom Tromey <tom@tromey.com> 2020-02-22 Tom Tromey <tom@tromey.com>
* NEWS: Add "tui new-layout" item. * NEWS: Add "tui new-layout" item.

View File

@ -1,3 +1,7 @@
2020-02-22 Tom Tromey <tom@tromey.com>
* gdb.tui/new-layout.exp: Add sub-layout tests.
2020-02-22 Tom Tromey <tom@tromey.com> 2020-02-22 Tom Tromey <tom@tromey.com>
* gdb.tui/new-layout.exp: New file. * gdb.tui/new-layout.exp: New file.

View File

@ -35,12 +35,23 @@ gdb_test "tui new-layout example src 1 src 1" \
"Window \"src\" seen twice in layout" "Window \"src\" seen twice in layout"
gdb_test "tui new-layout example src 1" \ gdb_test "tui new-layout example src 1" \
"New layout does not contain the \"cmd\" window" "New layout does not contain the \"cmd\" window"
gdb_test "tui new-layout example src 1}" \
"Extra '}' in layout specification"
gdb_test "tui new-layout example {src 1} 1}" \
"Extra '}' in layout specification"
gdb_test "tui new-layout example {src 1" \
"Missing '}' in layout specification"
gdb_test_no_output "tui new-layout example asm 1 status 0 cmd 1" gdb_test_no_output "tui new-layout example asm 1 status 0 cmd 1"
gdb_test "help layout example" \ gdb_test "help layout example" \
"Apply the \"example\" layout.*tui new-layout example asm 1 status 0 cmd 1" "Apply the \"example\" layout.*tui new-layout example asm 1 status 0 cmd 1"
gdb_test_no_output "tui new-layout example2 {asm 1 status 0} 1 cmd 1"
gdb_test "help layout example2" \
"Apply the \"example2\" layout.*tui new-layout example2 {asm 1 status 0} 1 cmd 1"
if {![Term::enter_tui]} { if {![Term::enter_tui]} {
unsupported "TUI not supported" unsupported "TUI not supported"
} }

View File

@ -400,20 +400,19 @@ tui_layout_window::replace_window (const char *name, const char *new_window)
/* See tui-layout.h. */ /* See tui-layout.h. */
void void
tui_layout_window::specification (ui_file *output) tui_layout_window::specification (ui_file *output, int depth)
{ {
fputs_unfiltered (get_name (), output); fputs_unfiltered (get_name (), output);
} }
/* See tui-layout.h. */ /* See tui-layout.h. */
tui_layout_split * void
tui_layout_split::add_split (int weight) tui_layout_split::add_split (std::unique_ptr<tui_layout_split> &&layout,
int weight)
{ {
tui_layout_split *result = new tui_layout_split (); split s = {weight, std::move (layout)};
split s = {weight, std::unique_ptr<tui_layout_base> (result)};
m_splits.push_back (std::move (s)); m_splits.push_back (std::move (s));
return result;
} }
/* See tui-layout.h. */ /* See tui-layout.h. */
@ -711,17 +710,23 @@ tui_layout_split::replace_window (const char *name, const char *new_window)
/* See tui-layout.h. */ /* See tui-layout.h. */
void void
tui_layout_split::specification (ui_file *output) tui_layout_split::specification (ui_file *output, int depth)
{ {
if (depth > 0)
fputs_unfiltered ("{", output);
bool first = true; bool first = true;
for (auto &item : m_splits) for (auto &item : m_splits)
{ {
if (!first) if (!first)
fputs_unfiltered (" ", output); fputs_unfiltered (" ", output);
first = false; first = false;
item.layout->specification (output); item.layout->specification (output, depth + 1);
fprintf_unfiltered (output, " %d", item.weight); fprintf_unfiltered (output, " %d", item.weight);
} }
if (depth > 0)
fputs_unfiltered ("}", output);
} }
/* Destroy the layout associated with SELF. */ /* Destroy the layout associated with SELF. */
@ -746,7 +751,7 @@ add_layout_command (const char *name, tui_layout_split *layout)
struct cmd_list_element *cmd; struct cmd_list_element *cmd;
string_file spec; string_file spec;
layout->specification (&spec); layout->specification (&spec, 0);
gdb::unique_xmalloc_ptr<char> doc gdb::unique_xmalloc_ptr<char> doc
(xstrprintf (_("Apply the \"%s\" layout.\n\ (xstrprintf (_("Apply the \"%s\" layout.\n\
@ -833,23 +838,60 @@ tui_new_layout_command (const char *spec, int from_tty)
if (new_name[0] == '-') if (new_name[0] == '-')
error (_("Layout name cannot start with '-'")); error (_("Layout name cannot start with '-'"));
std::unique_ptr<tui_layout_split> new_layout (new tui_layout_split); std::vector<std::unique_ptr<tui_layout_split>> splits;
splits.emplace_back (new tui_layout_split);
std::unordered_set<std::string> seen_windows; std::unordered_set<std::string> seen_windows;
while (true) while (true)
{ {
std::string name = extract_arg (&spec); spec = skip_spaces (spec);
if (name.empty ()) if (spec[0] == '\0')
break; break;
if (!validate_window_name (name))
error (_("Unknown window \"%s\""), name.c_str ()); if (spec[0] == '{')
if (seen_windows.find (name) != seen_windows.end ()) {
error (_("Window \"%s\" seen twice in layout"), name.c_str ()); splits.emplace_back (new tui_layout_split);
ULONGEST weight = get_ulongest (&spec); ++spec;
continue;
}
bool is_close = false;
std::string name;
if (spec[0] == '}')
{
is_close = true;
++spec;
if (splits.size () == 1)
error (_("Extra '}' in layout specification"));
}
else
{
name = extract_arg (&spec);
if (name.empty ())
break;
if (!validate_window_name (name))
error (_("Unknown window \"%s\""), name.c_str ());
if (seen_windows.find (name) != seen_windows.end ())
error (_("Window \"%s\" seen twice in layout"), name.c_str ());
}
ULONGEST weight = get_ulongest (&spec, '}');
if ((int) weight != weight) if ((int) weight != weight)
error (_("Weight out of range: %s"), pulongest (weight)); error (_("Weight out of range: %s"), pulongest (weight));
new_layout->add_window (name.c_str (), weight); if (is_close)
seen_windows.insert (name); {
std::unique_ptr<tui_layout_split> last_split
= std::move (splits.back ());
splits.pop_back ();
splits.back ()->add_split (std::move (last_split), weight);
}
else
{
splits.back ()->add_window (name.c_str (), weight);
seen_windows.insert (name);
}
} }
if (splits.size () > 1)
error (_("Missing '}' in layout specification"));
if (seen_windows.empty ()) if (seen_windows.empty ())
error (_("New layout does not contain any windows")); error (_("New layout does not contain any windows"));
if (seen_windows.find ("cmd") == seen_windows.end ()) if (seen_windows.find ("cmd") == seen_windows.end ())
@ -857,6 +899,7 @@ tui_new_layout_command (const char *spec, int from_tty)
gdb::unique_xmalloc_ptr<char> cmd_name gdb::unique_xmalloc_ptr<char> cmd_name
= make_unique_xstrdup (new_name.c_str ()); = make_unique_xstrdup (new_name.c_str ());
std::unique_ptr<tui_layout_split> new_layout = std::move (splits.back ());
struct cmd_list_element *cmd struct cmd_list_element *cmd
= add_layout_command (cmd_name.get (), new_layout.get ()); = add_layout_command (cmd_name.get (), new_layout.get ());
cmd->name_allocated = 1; cmd->name_allocated = 1;
@ -900,6 +943,9 @@ Usage: tui new-layout NAME WINDOW WEIGHT [WINDOW WEIGHT]...\n\
Create a new TUI layout. The new layout will be named NAME,\n\ Create a new TUI layout. The new layout will be named NAME,\n\
and can be accessed using \"layout NAME\".\n\ and can be accessed using \"layout NAME\".\n\
The windows will be displayed in the specified order.\n\ The windows will be displayed in the specified order.\n\
A WINDOW can also be of the form:\n\
{ NAME WEIGHT [NAME WEIGHT]... }\n\
This form indicates a sub-frame.\n\
Each WEIGHT is an integer, which holds the relative size\n\ Each WEIGHT is an integer, which holds the relative size\n\
to be allocated to the window."), to be allocated to the window."),
tui_get_cmd_list ()); tui_get_cmd_list ());

View File

@ -74,8 +74,9 @@ public:
NEW_WINDOW. */ NEW_WINDOW. */
virtual void replace_window (const char *name, const char *new_window) = 0; virtual void replace_window (const char *name, const char *new_window) = 0;
/* Append the specification to this window to OUTPUT. */ /* Append the specification to this window to OUTPUT. DEPTH is the
virtual void specification (ui_file *output) = 0; depth of this layout in the hierarchy (zero-based). */
virtual void specification (ui_file *output, int depth) = 0;
/* The most recent space allocation. */ /* The most recent space allocation. */
int x = 0; int x = 0;
@ -125,7 +126,7 @@ public:
void replace_window (const char *name, const char *new_window) override; void replace_window (const char *name, const char *new_window) override;
void specification (ui_file *output) override; void specification (ui_file *output, int depth) override;
protected: protected:
@ -153,7 +154,7 @@ public:
/* Add a new split layout to this layout. WEIGHT is the desired /* Add a new split layout to this layout. WEIGHT is the desired
size, which is relative to the other weights given in this size, which is relative to the other weights given in this
layout. */ layout. */
tui_layout_split *add_split (int weight); void add_split (std::unique_ptr<tui_layout_split> &&layout, int weight);
/* Add a new window to this layout. NAME is the name of the window /* Add a new window to this layout. NAME is the name of the window
to add. WEIGHT is the desired size, which is relative to the to add. WEIGHT is the desired size, which is relative to the
@ -174,7 +175,7 @@ public:
void replace_window (const char *name, const char *new_window) override; void replace_window (const char *name, const char *new_window) override;
void specification (ui_file *output) override; void specification (ui_file *output, int depth) override;
protected: protected: