Add the "tui 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:31 +0000 (11:48 -0700)
This adds a new command, "tui new-layout".  This command can be used
to define a new TUI window layout.

The command is used like:

(gdb) tui new-layout name src 1 regs 1 status 0 cmd 1

The first argument is the name of the layout.  In this example, it is
"name", so the new layout could be seen by "layout name".

Subsequent arguments come in pairs, where the first item in a pair is
the name of a window, and the second item in a pair is the window's
weight.  A weight is just an integer -- a window's allocated size is
proportional to the total of the weights given.  So, in the above
example, all windows will have the same size (the status windows's
weight does not matter, because it has fixed height).

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

* NEWS: Add "tui new-layout" item.
* tui/tui-layout.c (add_layout_command): Return cmd_list_element.
Add new-layout command to help text.
(validate_window_name): New function.
(tui_new_layout_command): New function.
(_initialize_tui_layout): Register "new-layout".
(tui_layout_window::specification): New method.
(tui_layout_window::specification): New method.
* tui/tui-layout.h (class tui_layout_base) <specification>: New
method.
(class tui_layout_window) <specification>: New method.
(class tui_layout_split) <specification>: New method.

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

* gdb.texinfo (TUI Overview): Mention user layouts.
(TUI Commands): Document "tui new-layout".

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

* gdb.tui/new-layout.exp: New file.

Change-Id: Id7c3ace20ab1e8924f8f4ad788f40210f58a5c05

gdb/ChangeLog
gdb/NEWS
gdb/doc/ChangeLog
gdb/doc/gdb.texinfo
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.tui/new-layout.exp [new file with mode: 0644]
gdb/tui/tui-layout.c
gdb/tui/tui-layout.h

index a7e565abcce15c09e0215f95c8be81c7630d4966..ed2f3583eba5545f1727ae861f56aa1031a15016 100644 (file)
@@ -1,3 +1,18 @@
+2020-02-22  Tom Tromey  <tom@tromey.com>
+
+       * NEWS: Add "tui new-layout" item.
+       * tui/tui-layout.c (add_layout_command): Return cmd_list_element.
+       Add new-layout command to help text.
+       (validate_window_name): New function.
+       (tui_new_layout_command): New function.
+       (_initialize_tui_layout): Register "new-layout".
+       (tui_layout_window::specification): New method.
+       (tui_layout_window::specification): New method.
+       * tui/tui-layout.h (class tui_layout_base) <specification>: New
+       method.
+       (class tui_layout_window) <specification>: New method.
+       (class tui_layout_split) <specification>: New method.
+
 2020-02-22  Tom Tromey  <tom@tromey.com>
 
        * tui/tui.c (tui_enable): Call tui_set_initial_layout.
index 91add18d6f591aff77049be5f484505040aed4f8..830653f555c8f41f22953dc6b89eadb740e5f49d 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -30,6 +30,10 @@ show exec-file-mismatch -- Show exec-file-mismatch handling (ask|warn|off).
   whether to load the process executable file; if 'warn', just display
   a warning; if 'off', don't attempt to detect a mismatch.
 
+tui new-layout NAME WINDOW WEIGHT [WINDOW WEIGHT]...
+  Define a new TUI layout, specifying its name and the windows that
+  will be displayed.
+
 * New targets
 
 GNU/Linux/RISC-V (gdbserver)   riscv*-*-linux*
index 4900480200306b8abb36a3650f30c3f5e3e1c2c6..99f2d9397604ec9d0205180a4fa08a1a06a09a4e 100644 (file)
@@ -1,3 +1,8 @@
+2020-02-22  Tom Tromey  <tom@tromey.com>
+
+       * gdb.texinfo (TUI Overview): Mention user layouts.
+       (TUI Commands): Document "tui new-layout".
+
 2020-01-26  Tom Tromey  <tromey@adacore.com>
 
        * gdb.texinfo (M68K Features): Document floating-point feature
index a2866a28333f902fa4f507df9e6ba0558219b2fd..c2617386869651f92893f1ecee16808a45fbbcd9 100644 (file)
@@ -27755,6 +27755,8 @@ source and registers, or
 assembly and registers.
 @end itemize
 
+These are the standard layouts, but other layouts can be defined.
+
 A status line above the command window shows the following information:
 
 @table @emph
@@ -27991,11 +27993,40 @@ Disable TUI mode, returning to the console interpreter.
 @kindex info win
 List and give the size of all displayed windows.
 
+@item tui new-layout @var{name} @var{window} @var{weight} @r{[}@var{window} @var{weight}@dots{}@r{]}
+@kindex tui new-layout
+Create a new TUI layout.  The new layout will be named @var{name}, and
+can be accessed using the @code{layout} command (see below).
+
+Each @var{window} parameter is the name of a window to display.  The
+windows will be displayed from top to bottom in the order listed.  The
+names of the windows are the same as the ones given to the
+@code{focus} command (see below); additional, the @code{status}
+window can be specified.
+
+Each @var{weight} is an integer.  It is the weight of this window
+relative to all the other windows in the layout.  These numbers are
+used to calculate how much of the screen is given to each window.
+
+For example:
+
+@example
+(gdb) tui new-layout example src 1 regs 1 status 0 cmd 1
+@end example
+
+Here, the new layout is called @samp{example}.  It shows the source
+and register windows, followed by the status window, and then finally
+the command window.  The non-status windows all have the same weight,
+so the terminal will be split into three roughly equal sections.
+
 @item layout @var{name}
 @kindex layout
-Changes which TUI windows are displayed.  In each layout the command
-window is always displayed, the @var{name} parameter controls which
-additional windows are displayed, and can be any of the following:
+Changes which TUI windows are displayed.  The @var{name} parameter
+controls which layout is shown.  It can be either one of the built-in
+layout names, or the name of a layout defined by the user using
+@code{tui new-layout}.
+
+The built-in layouts are as follows:
 
 @table @code
 @item next
index d3f555ef5b3b7ed4d71caaa0ea47645b4213c304..c7270b965b6e6b026b63a516cf1aee3fd48c830d 100644 (file)
@@ -1,3 +1,7 @@
+2020-02-22  Tom Tromey  <tom@tromey.com>
+
+       * gdb.tui/new-layout.exp: New file.
+
 2020-02-22  Tom Tromey  <tom@tromey.com>
 
        * gdb.rust/rust-style.rs: New file.
diff --git a/gdb/testsuite/gdb.tui/new-layout.exp b/gdb/testsuite/gdb.tui/new-layout.exp
new file mode 100644 (file)
index 0000000..2efc172
--- /dev/null
@@ -0,0 +1,54 @@
+# Copyright 2020 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test "tui new-layout".
+
+load_lib "tuiterm.exp"
+
+standard_testfile tui-layout.c
+
+if {[build_executable "failed to prepare" ${testfile} ${srcfile}] == -1} {
+    return -1
+}
+
+Term::clean_restart 24 80 $testfile
+
+gdb_test "tui new-layout" \
+    "No layout name specified"
+gdb_test "tui new-layout example" \
+    "New layout does not contain any windows"
+gdb_test "tui new-layout example zzq" \
+    "Unknown window \"zzq\""
+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_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"
+
+if {![Term::enter_tui]} {
+    unsupported "TUI not supported"
+}
+
+set text [Term::get_all_lines]
+gdb_assert {![string match "No Source Available" $text]} \
+    "initial source listing"
+
+Term::command "layout example"
+Term::check_contents "example layout shows assembly" \
+    "No Assembly Available"
index e2ddbfb0d2cc3c5735801ae2c5b7d7c85e1c067b..8f5dd6ec516c284b21a61b436acd2608783dda29 100644 (file)
@@ -27,7 +27,9 @@
 #include "source.h"
 #include "cli/cli-cmds.h"
 #include "cli/cli-decode.h"
+#include "cli/cli-utils.h"
 #include <ctype.h>
+#include <unordered_set>
 
 #include "tui/tui.h"
 #include "tui/tui-command.h"
@@ -397,6 +399,14 @@ tui_layout_window::replace_window (const char *name, const char *new_window)
 
 /* See tui-layout.h.  */
 
+void
+tui_layout_window::specification (ui_file *output)
+{
+  fputs_unfiltered (get_name (), output);
+}
+
+/* See tui-layout.h.  */
+
 tui_layout_split *
 tui_layout_split::add_split (int weight)
 {
@@ -698,6 +708,22 @@ tui_layout_split::replace_window (const char *name, const char *new_window)
     item.layout->replace_window (name, new_window);
 }
 
+/* See tui-layout.h.  */
+
+void
+tui_layout_split::specification (ui_file *output)
+{
+  bool first = true;
+  for (auto &item : m_splits)
+    {
+      if (!first)
+       fputs_unfiltered (" ", output);
+      first = false;
+      item.layout->specification (output);
+      fprintf_unfiltered (output, " %d", item.weight);
+    }
+}
+
 /* Destroy the layout associated with SELF.  */
 
 static void
@@ -714,13 +740,19 @@ static struct cmd_list_element *layout_list;
 
 /* Add a "layout" command with name NAME that switches to LAYOUT.  */
 
-static void
+static struct cmd_list_element *
 add_layout_command (const char *name, tui_layout_split *layout)
 {
   struct cmd_list_element *cmd;
 
-  gdb::unique_xmalloc_ptr<char> doc (xstrprintf (_("Apply the \"%s\" layout"),
-                                                name));
+  string_file spec;
+  layout->specification (&spec);
+
+  gdb::unique_xmalloc_ptr<char> doc
+    (xstrprintf (_("Apply the \"%s\" layout.\n\
+This layout was created using:\n\
+  tui new-layout %s %s"),
+                name, name, spec.c_str ()));
 
   cmd = add_cmd (name, class_tui, nullptr, doc.get (), &layout_list);
   set_cmd_context (cmd, layout);
@@ -730,6 +762,8 @@ add_layout_command (const char *name, tui_layout_split *layout)
   cmd->doc_allocated = 1;
   doc.release ();
   layouts.emplace_back (layout);
+
+  return cmd;
 }
 
 /* Initialize the standard layouts.  */
@@ -777,6 +811,59 @@ initialize_layouts ()
 
 \f
 
+/* A helper function that returns true if NAME is the name of an
+   available window.  */
+
+static bool
+validate_window_name (const std::string &name)
+{
+  return (name == "src" || name == "cmd"
+         || name == "regs" || name == "asm"
+         || name == "status");
+}
+
+/* Implementation of the "tui new-layout" command.  */
+
+static void
+tui_new_layout_command (const char *spec, int from_tty)
+{
+  std::string new_name = extract_arg (&spec);
+  if (new_name.empty ())
+    error (_("No layout name specified"));
+  if (new_name[0] == '-')
+    error (_("Layout name cannot start with '-'"));
+
+  std::unique_ptr<tui_layout_split> new_layout (new tui_layout_split);
+  std::unordered_set<std::string> seen_windows;
+  while (true)
+    {
+      std::string 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 (seen_windows.empty ())
+    error (_("New layout does not contain any windows"));
+  if (seen_windows.find ("cmd") == seen_windows.end ())
+    error (_("New layout does not contain the \"cmd\" window"));
+
+  gdb::unique_xmalloc_ptr<char> cmd_name
+    = make_unique_xstrdup (new_name.c_str ());
+  struct cmd_list_element *cmd
+    = add_layout_command (cmd_name.get (), new_layout.get ());
+  cmd->name_allocated = 1;
+  cmd_name.release ();
+  new_layout.release ();
+}
+
 /* Base command for "layout".  */
 
 static void
@@ -807,5 +894,15 @@ Usage: layout prev | next | LAYOUT-NAME"),
           _("Apply the TUI register layout"),
           &layout_list);
 
+  add_cmd ("new-layout", class_tui, tui_new_layout_command,
+          _("Create a new TUI layout.\n\
+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\
+Each WEIGHT is an integer, which holds the relative size\n\
+to be allocated to the window."),
+          tui_get_cmd_list ());
+
   initialize_layouts ();
 }
index 98bd548aaee9d3db1db57b262eef828bb8d78a96..c2249a783f87b5086d4fa51776c92b55d313aa5a 100644 (file)
@@ -22,6 +22,8 @@
 #ifndef TUI_TUI_LAYOUT_H
 #define TUI_TUI_LAYOUT_H
 
+#include "ui-file.h"
+
 #include "tui/tui.h"
 #include "tui/tui-data.h"
 
@@ -72,6 +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;
+
   /* The most recent space allocation.  */
   int x = 0;
   int y = 0;
@@ -120,6 +125,8 @@ public:
 
   void replace_window (const char *name, const char *new_window) override;
 
+  void specification (ui_file *output) override;
+
 protected:
 
   void get_sizes (int *min_height, int *max_height) override;
@@ -167,6 +174,8 @@ public:
 
   void replace_window (const char *name, const char *new_window) override;
 
+  void specification (ui_file *output) override;
+
 protected:
 
   void get_sizes (int *min_height, int *max_height) override;