[Lldb-commits] [lldb] be3f8a8 - [commands] Support autorepeat in SBCommands

Walter Erquinigo via lldb-commits lldb-commits at lists.llvm.org
Wed Apr 8 10:59:48 PDT 2020


Author: Walter Erquinigo
Date: 2020-04-08T10:54:14-07:00
New Revision: be3f8a8e1b95db1a8bdcc7a66ba27f8a6ea65469

URL: https://github.com/llvm/llvm-project/commit/be3f8a8e1b95db1a8bdcc7a66ba27f8a6ea65469
DIFF: https://github.com/llvm/llvm-project/commit/be3f8a8e1b95db1a8bdcc7a66ba27f8a6ea65469.diff

LOG: [commands] Support autorepeat in SBCommands

Summary:
This adds support for commands created through the API to support autorepeat.
This covers the case of single word and multiword commands.

Comprehensive tests are included as well.

Reviewers: labath, clayborg

Subscribers: lldb-commits

Tags: #lldb

Differential Revision: https://reviews.llvm.org/D77444

Added: 
    lldb/unittests/API/CMakeLists.txt
    lldb/unittests/API/TestSBCommandInterpreterTest.cpp

Modified: 
    lldb/include/lldb/API/SBCommandInterpreter.h
    lldb/source/API/SBCommandInterpreter.cpp
    lldb/unittests/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/lldb/include/lldb/API/SBCommandInterpreter.h b/lldb/include/lldb/API/SBCommandInterpreter.h
index 485d4ca58de0..e07eeb58bf6a 100644
--- a/lldb/include/lldb/API/SBCommandInterpreter.h
+++ b/lldb/include/lldb/API/SBCommandInterpreter.h
@@ -111,14 +111,86 @@ class SBCommandInterpreter {
 
   lldb::SBCommand AddMultiwordCommand(const char *name, const char *help);
 
+  /// Add a new command to the lldb::CommandInterpreter.
+  ///
+  /// The new command won't support autorepeat. If you need this functionality,
+  /// use the override of this function that accepts the \a auto_repeat_command
+  /// parameter.
+  ///
+  /// \param[in] name
+  ///     The name of the command.
+  ///
+  /// \param[in] impl
+  ///     The handler of this command.
+  ///
+  /// \param[in] help
+  ///     The general description to show as part of the help message of this
+  ///     command.
+  ///
+  /// \return
+  ///     A lldb::SBCommand representing the newly created command.
   lldb::SBCommand AddCommand(const char *name,
                              lldb::SBCommandPluginInterface *impl,
                              const char *help);
 
+  /// Add a new command to the lldb::CommandInterpreter.
+  ///
+  /// The new command won't support autorepeat. If you need this functionality,
+  /// use the override of this function that accepts the \a auto_repeat_command
+  /// parameter.
+  ///
+  /// \param[in] name
+  ///     The name of the command.
+  ///
+  /// \param[in] impl
+  ///     The handler of this command.
+  ///
+  /// \param[in] help
+  ///     The general description to show as part of the help message of this
+  ///     command.
+  ///
+  /// \param[in] syntax
+  ///     The syntax to show as part of the help message of this command. This
+  ///     could include a description of the 
diff erent arguments and flags this
+  ///     command accepts.
+  ///
+  /// \return
+  ///     A lldb::SBCommand representing the newly created command.
   lldb::SBCommand AddCommand(const char *name,
                              lldb::SBCommandPluginInterface *impl,
                              const char *help, const char *syntax);
 
+  /// Add a new command to the lldb::CommandInterpreter.
+  ///
+  /// \param[in] name
+  ///     The name of the command.
+  ///
+  /// \param[in] impl
+  ///     The handler of this command.
+  ///
+  /// \param[in] help
+  ///     The general description to show as part of the help message of this
+  ///     command.
+  ///
+  /// \param[in] syntax
+  ///     The syntax to show as part of the help message of this command. This
+  ///     could include a description of the 
diff erent arguments and flags this
+  ///     command accepts.
+  ///
+  /// \param[in] auto_repeat_command
+  ///     Autorepeating is triggered when the user presses Enter successively
+  ///     after executing a command. If \b nullptr is provided, the previous
+  ///     exact command will be repeated. If \b "" is provided, autorepeating
+  ///     is disabled. Otherwise, the provided string is used as a repeat
+  ///     command.
+  ///
+  /// \return
+  ///     A lldb::SBCommand representing the newly created command.
+  lldb::SBCommand AddCommand(const char *name,
+                             lldb::SBCommandPluginInterface *impl,
+                             const char *help, const char *syntax,
+                             const char *auto_repeat_command);
+
   void SourceInitFileInHomeDirectory(lldb::SBCommandReturnObject &result);
 
   void
@@ -283,14 +355,90 @@ class SBCommand {
   lldb::SBCommand AddMultiwordCommand(const char *name,
                                       const char *help = nullptr);
 
+  /// Add a new subcommand to the lldb::SBCommand.
+  ///
+  /// The new command won't support autorepeat. If you need this functionality,
+  /// use the override of this function that accepts the \a auto_repeat
+  /// parameter.
+  ///
+  /// \param[in] name
+  ///     The name of the command.
+  ///
+  /// \param[in] impl
+  ///     The handler of this command.
+  ///
+  /// \param[in] help
+  ///     The general description to show as part of the help message of this
+  ///     command.
+  ///
+  /// \return
+  ///     A lldb::SBCommand representing the newly created command.
   lldb::SBCommand AddCommand(const char *name,
                              lldb::SBCommandPluginInterface *impl,
                              const char *help = nullptr);
 
+  /// Add a new subcommand to the lldb::SBCommand.
+  ///
+  /// The new command won't support autorepeat. If you need this functionality,
+  /// use the override of this function that accepts the \a auto_repeat_command
+  /// parameter.
+  ///
+  /// \param[in] name
+  ///     The name of the command.
+  ///
+  /// \param[in] impl
+  ///     The handler of this command.
+  ///
+  /// \param[in] help
+  ///     The general description to show as part of the help message of this
+  ///     command.
+  ///
+  /// \param[in] syntax
+  ///     The syntax to show as part of the help message of this command. This
+  ///     could include a description of the 
diff erent arguments and flags this
+  ///     command accepts.
+  ///
+  /// \return
+  ///     A lldb::SBCommand representing the newly created command.
   lldb::SBCommand AddCommand(const char *name,
                              lldb::SBCommandPluginInterface *impl,
                              const char *help, const char *syntax);
 
+  /// Add a new subcommand to the lldb::SBCommand.
+  ///
+  /// The new command won't support autorepeat. If you need this functionality,
+  /// use the override of this function that accepts the \a auto_repeat_command
+  /// parameter.
+  ///
+  /// \param[in] name
+  ///     The name of the command.
+  ///
+  /// \param[in] impl
+  ///     The handler of this command.
+  ///
+  /// \param[in] help
+  ///     The general description to show as part of the help message of this
+  ///     command.
+  ///
+  /// \param[in] syntax
+  ///     The syntax to show as part of the help message of this command. This
+  ///     could include a description of the 
diff erent arguments and flags this
+  ///     command accepts.
+  ///
+  /// \param[in] auto_repeat_command
+  ///     Autorepeating is triggered when the user presses Enter successively
+  ///     after executing a command. If \b nullptr is provided, the previous
+  ///     exact command will be repeated. If \b "" is provided, autorepeating
+  ///     is disabled. Otherwise, the provided string is used as a repeat
+  ///     command.
+  ///
+  /// \return
+  ///     A lldb::SBCommand representing the newly created command.
+  lldb::SBCommand AddCommand(const char *name,
+                             lldb::SBCommandPluginInterface *impl,
+                             const char *help, const char *syntax,
+                             const char *auto_repeat_command);
+
 private:
   friend class SBDebugger;
   friend class SBCommandInterpreter;

diff  --git a/lldb/source/API/SBCommandInterpreter.cpp b/lldb/source/API/SBCommandInterpreter.cpp
index 4198ecc3c73f..14d738b27680 100644
--- a/lldb/source/API/SBCommandInterpreter.cpp
+++ b/lldb/source/API/SBCommandInterpreter.cpp
@@ -154,12 +154,30 @@ class CommandPluginInterfaceImplementation : public CommandObjectParsed {
                                        lldb::SBCommandPluginInterface *backend,
                                        const char *help = nullptr,
                                        const char *syntax = nullptr,
-                                       uint32_t flags = 0)
+                                       uint32_t flags = 0,
+                                       const char *auto_repeat_command = "")
       : CommandObjectParsed(interpreter, name, help, syntax, flags),
-        m_backend(backend) {}
+        m_backend(backend) {
+    m_auto_repeat_command =
+        auto_repeat_command == nullptr
+            ? llvm::None
+            : llvm::Optional<std::string>(auto_repeat_command);
+  }
 
   bool IsRemovable() const override { return true; }
 
+  /// More documentation is available in lldb::CommandObject::GetRepeatCommand,
+  /// but in short, if nullptr is returned, the previous command will be
+  /// repeated, and if an empty string is returned, no commands will be
+  /// executed.
+  const char *GetRepeatCommand(Args &current_command_args,
+                               uint32_t index) override {
+    if (!m_auto_repeat_command)
+      return nullptr;
+    else
+      return m_auto_repeat_command->c_str();
+  }
+
 protected:
   bool DoExecute(Args &command, CommandReturnObject &result) override {
     SBCommandReturnObject sb_return(result);
@@ -170,6 +188,7 @@ class CommandPluginInterfaceImplementation : public CommandObjectParsed {
     return ret;
   }
   std::shared_ptr<lldb::SBCommandPluginInterface> m_backend;
+  llvm::Optional<std::string> m_auto_repeat_command;
 };
 
 SBCommandInterpreter::SBCommandInterpreter(CommandInterpreter *interpreter)
@@ -681,14 +700,8 @@ lldb::SBCommand SBCommandInterpreter::AddCommand(
       (const char *, lldb::SBCommandPluginInterface *, const char *), name,
       impl, help);
 
-  lldb::CommandObjectSP new_command_sp;
-  new_command_sp = std::make_shared<CommandPluginInterfaceImplementation>(
-      *m_opaque_ptr, name, impl, help);
-
-  if (new_command_sp &&
-      m_opaque_ptr->AddUserCommand(name, new_command_sp, true))
-    return LLDB_RECORD_RESULT(lldb::SBCommand(new_command_sp));
-  return LLDB_RECORD_RESULT(lldb::SBCommand());
+  return LLDB_RECORD_RESULT(AddCommand(name, impl, help, /*syntax=*/nullptr,
+                                       /*auto_repeat_command=*/""))
 }
 
 lldb::SBCommand
@@ -699,10 +712,22 @@ SBCommandInterpreter::AddCommand(const char *name,
                      (const char *, lldb::SBCommandPluginInterface *,
                       const char *, const char *),
                      name, impl, help, syntax);
+  return LLDB_RECORD_RESULT(
+      AddCommand(name, impl, help, syntax, /*auto_repeat_command=*/""))
+}
+
+lldb::SBCommand SBCommandInterpreter::AddCommand(
+    const char *name, lldb::SBCommandPluginInterface *impl, const char *help,
+    const char *syntax, const char *auto_repeat_command) {
+  LLDB_RECORD_METHOD(lldb::SBCommand, SBCommandInterpreter, AddCommand,
+                     (const char *, lldb::SBCommandPluginInterface *,
+                      const char *, const char *, const char *),
+                     name, impl, help, syntax, auto_repeat_command);
 
   lldb::CommandObjectSP new_command_sp;
   new_command_sp = std::make_shared<CommandPluginInterfaceImplementation>(
-      *m_opaque_ptr, name, impl, help, syntax);
+      *m_opaque_ptr, name, impl, help, syntax, /*flags=*/0,
+      auto_repeat_command);
 
   if (new_command_sp &&
       m_opaque_ptr->AddUserCommand(name, new_command_sp, true))
@@ -783,17 +808,8 @@ lldb::SBCommand SBCommand::AddCommand(const char *name,
       lldb::SBCommand, SBCommand, AddCommand,
       (const char *, lldb::SBCommandPluginInterface *, const char *), name,
       impl, help);
-
-  if (!IsValid())
-    return LLDB_RECORD_RESULT(lldb::SBCommand());
-  if (!m_opaque_sp->IsMultiwordObject())
-    return LLDB_RECORD_RESULT(lldb::SBCommand());
-  lldb::CommandObjectSP new_command_sp;
-  new_command_sp = std::make_shared<CommandPluginInterfaceImplementation>(
-      m_opaque_sp->GetCommandInterpreter(), name, impl, help);
-  if (new_command_sp && m_opaque_sp->LoadSubCommand(name, new_command_sp))
-    return LLDB_RECORD_RESULT(lldb::SBCommand(new_command_sp));
-  return LLDB_RECORD_RESULT(lldb::SBCommand());
+  return LLDB_RECORD_RESULT(AddCommand(name, impl, help, /*syntax=*/nullptr,
+                                       /*auto_repeat_command=*/""))
 }
 
 lldb::SBCommand SBCommand::AddCommand(const char *name,
@@ -803,6 +819,18 @@ lldb::SBCommand SBCommand::AddCommand(const char *name,
                      (const char *, lldb::SBCommandPluginInterface *,
                       const char *, const char *),
                      name, impl, help, syntax);
+  return LLDB_RECORD_RESULT(
+      AddCommand(name, impl, help, syntax, /*auto_repeat_command=*/""))
+}
+
+lldb::SBCommand SBCommand::AddCommand(const char *name,
+                                      lldb::SBCommandPluginInterface *impl,
+                                      const char *help, const char *syntax,
+                                      const char *auto_repeat_command) {
+  LLDB_RECORD_METHOD(lldb::SBCommand, SBCommand, AddCommand,
+                     (const char *, lldb::SBCommandPluginInterface *,
+                      const char *, const char *, const char *),
+                     name, impl, help, syntax, auto_repeat_command);
 
   if (!IsValid())
     return LLDB_RECORD_RESULT(lldb::SBCommand());
@@ -810,7 +838,8 @@ lldb::SBCommand SBCommand::AddCommand(const char *name,
     return LLDB_RECORD_RESULT(lldb::SBCommand());
   lldb::CommandObjectSP new_command_sp;
   new_command_sp = std::make_shared<CommandPluginInterfaceImplementation>(
-      m_opaque_sp->GetCommandInterpreter(), name, impl, help, syntax);
+      m_opaque_sp->GetCommandInterpreter(), name, impl, help, syntax,
+      /*flags=*/0, auto_repeat_command);
   if (new_command_sp && m_opaque_sp->LoadSubCommand(name, new_command_sp))
     return LLDB_RECORD_RESULT(lldb::SBCommand(new_command_sp));
   return LLDB_RECORD_RESULT(lldb::SBCommand());
@@ -946,6 +975,9 @@ void RegisterMethods<SBCommandInterpreterRunOptions>(Registry &R) {
   LLDB_REGISTER_METHOD(lldb::SBCommand, SBCommandInterpreter, AddCommand,
                        (const char *, lldb::SBCommandPluginInterface *,
                         const char *, const char *));
+  LLDB_REGISTER_METHOD(lldb::SBCommand, SBCommandInterpreter, AddCommand,
+                       (const char *, lldb::SBCommandPluginInterface *,
+                        const char *, const char *, const char *));
   LLDB_REGISTER_CONSTRUCTOR(SBCommand, ());
   LLDB_REGISTER_METHOD(bool, SBCommand, IsValid, ());
   LLDB_REGISTER_METHOD_CONST(bool, SBCommand, operator bool, ());
@@ -962,6 +994,9 @@ void RegisterMethods<SBCommandInterpreterRunOptions>(Registry &R) {
   LLDB_REGISTER_METHOD(lldb::SBCommand, SBCommand, AddCommand,
                        (const char *, lldb::SBCommandPluginInterface *,
                         const char *, const char *));
+  LLDB_REGISTER_METHOD(lldb::SBCommand, SBCommand, AddCommand,
+                       (const char *, lldb::SBCommandPluginInterface *,
+                        const char *, const char *, const char *));
   LLDB_REGISTER_METHOD(uint32_t, SBCommand, GetFlags, ());
   LLDB_REGISTER_METHOD(void, SBCommand, SetFlags, (uint32_t));
 }

diff  --git a/lldb/unittests/API/CMakeLists.txt b/lldb/unittests/API/CMakeLists.txt
new file mode 100644
index 000000000000..50ec47bfe00f
--- /dev/null
+++ b/lldb/unittests/API/CMakeLists.txt
@@ -0,0 +1,6 @@
+add_lldb_unittest(APITests
+  TestSBCommandInterpreterTest.cpp
+
+  LINK_LIBS
+    liblldb
+  )

diff  --git a/lldb/unittests/API/TestSBCommandInterpreterTest.cpp b/lldb/unittests/API/TestSBCommandInterpreterTest.cpp
new file mode 100644
index 000000000000..6194b6536a27
--- /dev/null
+++ b/lldb/unittests/API/TestSBCommandInterpreterTest.cpp
@@ -0,0 +1,138 @@
+//===-- TestSBCommandInterpreterTest.cpp ----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===/
+
+#include "gtest/gtest.h"
+
+#include "lldb/API/SBCommandInterpreter.h"
+#include "lldb/API/SBCommandReturnObject.h"
+#include "lldb/API/SBDebugger.h"
+
+#include <cstring>
+#include <string>
+
+using namespace lldb;
+
+class TestSBCommandInterpreterTest : public testing::Test {
+protected:
+  void SetUp() override {
+    SBDebugger::Initialize();
+    m_dbg = SBDebugger::Create(/*source_init_files=*/false);
+    m_interp = m_dbg.GetCommandInterpreter();
+  }
+
+  SBDebugger m_dbg;
+  SBCommandInterpreter m_interp;
+};
+
+class DummyCommand : public SBCommandPluginInterface {
+public:
+  DummyCommand(const char *message) : m_message(message) {}
+
+  bool DoExecute(SBDebugger dbg, char **command,
+                 SBCommandReturnObject &result) {
+    result.PutCString(m_message.c_str());
+    result.SetStatus(eReturnStatusSuccessFinishResult);
+    return result.Succeeded();
+  }
+
+private:
+  std::string m_message;
+};
+
+TEST_F(TestSBCommandInterpreterTest, SingleWordCommand) {
+  // We first test a command without autorepeat
+  DummyCommand dummy("It worked");
+  m_interp.AddCommand("dummy", &dummy, /*help=*/nullptr);
+  {
+    SBCommandReturnObject result;
+    m_interp.HandleCommand("dummy", result, /*add_to_history=*/true);
+    EXPECT_TRUE(result.Succeeded());
+    EXPECT_STREQ(result.GetOutput(), "It worked\n");
+  }
+  {
+    SBCommandReturnObject result;
+    m_interp.HandleCommand("", result);
+    EXPECT_FALSE(result.Succeeded());
+    EXPECT_STREQ(result.GetError(), "error: No auto repeat.\n");
+  }
+
+  // Now we test a command with autorepeat
+  m_interp.AddCommand("dummy_with_autorepeat", &dummy, /*help=*/nullptr,
+                      /*syntax=*/nullptr, /*auto_repeat_command=*/nullptr);
+  {
+    SBCommandReturnObject result;
+    m_interp.HandleCommand("dummy_with_autorepeat", result,
+                           /*add_to_history=*/true);
+    EXPECT_TRUE(result.Succeeded());
+    EXPECT_STREQ(result.GetOutput(), "It worked\n");
+  }
+  {
+    SBCommandReturnObject result;
+    m_interp.HandleCommand("", result);
+    EXPECT_TRUE(result.Succeeded());
+    EXPECT_STREQ(result.GetOutput(), "It worked\n");
+  }
+}
+
+TEST_F(TestSBCommandInterpreterTest, MultiWordCommand) {
+  auto command = m_interp.AddMultiwordCommand("multicommand", /*help=*/nullptr);
+  // We first test a subcommand without autorepeat
+  DummyCommand subcommand("It worked again");
+  command.AddCommand("subcommand", &subcommand, /*help=*/nullptr);
+  {
+    SBCommandReturnObject result;
+    m_interp.HandleCommand("multicommand subcommand", result,
+                           /*add_to_history=*/true);
+    EXPECT_TRUE(result.Succeeded());
+    EXPECT_STREQ(result.GetOutput(), "It worked again\n");
+  }
+  {
+    SBCommandReturnObject result;
+    m_interp.HandleCommand("", result);
+    EXPECT_FALSE(result.Succeeded());
+    EXPECT_STREQ(result.GetError(), "error: No auto repeat.\n");
+  }
+
+  // We first test a subcommand with autorepeat
+  command.AddCommand("subcommand_with_autorepeat", &subcommand,
+                     /*help=*/nullptr, /*syntax=*/nullptr,
+                     /*auto_repeat_command=*/nullptr);
+  {
+    SBCommandReturnObject result;
+    m_interp.HandleCommand("multicommand subcommand_with_autorepeat", result,
+                           /*add_to_history=*/true);
+    EXPECT_TRUE(result.Succeeded());
+    EXPECT_STREQ(result.GetOutput(), "It worked again\n");
+  }
+  {
+    SBCommandReturnObject result;
+    m_interp.HandleCommand("", result);
+    EXPECT_TRUE(result.Succeeded());
+    EXPECT_STREQ(result.GetOutput(), "It worked again\n");
+  }
+
+  DummyCommand subcommand2("It worked again 2");
+  // We now test a subcommand with autorepeat of the command name
+  command.AddCommand(
+      "subcommand_with_custom_autorepeat", &subcommand2, /*help=*/nullptr,
+      /*syntax=*/nullptr,
+      /*auto_repeat_command=*/"multicommand subcommand_with_autorepeat");
+  {
+    SBCommandReturnObject result;
+    m_interp.HandleCommand("multicommand subcommand_with_custom_autorepeat",
+                           result, /*add_to_history=*/true);
+    EXPECT_TRUE(result.Succeeded());
+    EXPECT_STREQ(result.GetOutput(), "It worked again 2\n");
+  }
+  {
+    SBCommandReturnObject result;
+    m_interp.HandleCommand("", result);
+    EXPECT_TRUE(result.Succeeded());
+    EXPECT_STREQ(result.GetOutput(), "It worked again\n");
+  }
+}

diff  --git a/lldb/unittests/CMakeLists.txt b/lldb/unittests/CMakeLists.txt
index bf117030dd4b..eab053f03650 100644
--- a/lldb/unittests/CMakeLists.txt
+++ b/lldb/unittests/CMakeLists.txt
@@ -59,6 +59,7 @@ function(add_unittest_inputs test_name inputs)
 endfunction()
 
 add_subdirectory(TestingSupport)
+add_subdirectory(API)
 add_subdirectory(Breakpoint)
 add_subdirectory(Core)
 add_subdirectory(DataFormatter)


        


More information about the lldb-commits mailing list