Allow TUI sub-layouts in "new-layout" command
authorTom Tromey <tom@tromey.com>
Sat, 22 Feb 2020 18:48:26 +0000 (11:48 -0700)
committerTom Tromey <tom@tromey.com>
Sat, 22 Feb 2020 18:48:32 +0000 (11:48 -0700)
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

gdb/ChangeLog
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.tui/new-layout.exp
gdb/tui/tui-layout.c
gdb/tui/tui-layout.h

index ed2f3583eba5545f1727ae861f56aa1031a15016..dc5ed675b873231bfa6d5e22d9d4f1cc9a169c92 100644 (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>
 
        * NEWS: Add "tui new-layout" item.
index c7270b965b6e6b026b63a516cf1aee3fd48c830d..9e825fbd53c2592aaa9f9962f25752b6a28f0edb 100644 (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>
 
        * gdb.tui/new-layout.exp: New file.
index 2efc1728bfad3df1c21f16082c1cd1b79caa8df1..83823229bd43a33552a970840265f5b2f931febf 100644 (file)
@@ -35,12 +35,23 @@ gdb_test "tui new-layout example src 1 src 1" \
     "Window \"src\" seen twice in layout"
 gdb_test "tui new-layout example src 1" \
     "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 "help layout example" \
     "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]} {
     unsupported "TUI not supported"
 }
index 8f5dd6ec516c284b21a61b436acd2608783dda29..6077a9cc68c02e11349180e3ed2c55dae63d1de8 100644 (file)
@@ -400,20 +400,19 @@ tui_layout_window::replace_window (const char *name, const char *new_window)
 /* See tui-layout.h.  */
 
 void
-tui_layout_window::specification (ui_file *output)
+tui_layout_window::specification (ui_file *output, int depth)
 {
   fputs_unfiltered (get_name (), output);
 }
 
 /* See tui-layout.h.  */
 
-tui_layout_split *
-tui_layout_split::add_split (int weight)
+void
+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::unique_ptr<tui_layout_base> (result)};
+  split s = {weight, std::move (layout)};
   m_splits.push_back (std::move (s));
-  return result;
 }
 
 /* See tui-layout.h.  */
@@ -711,17 +710,23 @@ tui_layout_split::replace_window (const char *name, const char *new_window)
 /* See tui-layout.h.  */
 
 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;
   for (auto &item : m_splits)
     {
       if (!first)
        fputs_unfiltered (" ", output);
       first = false;
-      item.layout->specification (output);
+      item.layout->specification (output, depth + 1);
       fprintf_unfiltered (output, " %d", item.weight);
     }
+
+  if (depth > 0)
+    fputs_unfiltered ("}", output);
 }
 
 /* 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;
 
   string_file spec;
-  layout->specification (&spec);
+  layout->specification (&spec, 0);
 
   gdb::unique_xmalloc_ptr<char> doc
     (xstrprintf (_("Apply the \"%s\" layout.\n\
@@ -833,23 +838,60 @@ tui_new_layout_command (const char *spec, int from_tty)
   if (new_name[0] == '-')
     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;
   while (true)
     {
-      std::string name = extract_arg (&spec);
-      if (name.empty ())
+      spec = skip_spaces (spec);
+      if (spec[0] == '\0')
        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 (spec[0] == '{')
+       {
+         splits.emplace_back (new tui_layout_split);
+         ++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)
        error (_("Weight out of range: %s"), pulongest (weight));
-      new_layout->add_window (name.c_str (), weight);
-      seen_windows.insert (name);
+      if (is_close)
+       {
+         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 ())
     error (_("New layout does not contain any windows"));
   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
     = make_unique_xstrdup (new_name.c_str ());
+  std::unique_ptr<tui_layout_split> new_layout = std::move (splits.back ());
   struct cmd_list_element *cmd
     = add_layout_command (cmd_name.get (), new_layout.get ());
   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\
 and can be accessed using \"layout NAME\".\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\
 to be allocated to the window."),
           tui_get_cmd_list ());
index c2249a783f87b5086d4fa51776c92b55d313aa5a..4351e260720a7863814909e59d9f2e27f1cfd8b7 100644 (file)
@@ -74,8 +74,9 @@ public:
      NEW_WINDOW.  */
   virtual void replace_window (const char *name, const char *new_window) = 0;
 
-  /* Append the specification to this window to OUTPUT.  */
-  virtual void specification (ui_file *output) = 0;
+  /* Append the specification to this window to OUTPUT.  DEPTH is the
+     depth of this layout in the hierarchy (zero-based).  */
+  virtual void specification (ui_file *output, int depth) = 0;
 
   /* The most recent space allocation.  */
   int x = 0;
@@ -125,7 +126,7 @@ public:
 
   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:
 
@@ -153,7 +154,7 @@ public:
   /* Add a new split layout to this layout.  WEIGHT is the desired
      size, which is relative to the other weights given in this
      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
      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 specification (ui_file *output) override;
+  void specification (ui_file *output, int depth) override;
 
 protected: