wallace updated this revision to Diff 255426.
wallace added a comment.
Herald added a subscriber: mgorny.

- Moved the test to gtest. It's much better this way and I learned gtest
- Changed the API. Some notes:

Within the scope of a subcommand, the subcommand doesn't know the parent's
command name. It only knows its own name and it doesn't have any referrence to
its parent. That makes it very difficult to implement an enum option for
eCommandNameRepeat, as @jingham suggested. The GetRepeatCommand signature also
doesn't provide useful info about the parsed arguments.

I took a look at the existing implementations for GetRepeatCommand, and it seems
that most of them just return nullptr, "", or the same command without flags.

I don't want to change any existing core command interpreter function, so I
think that a simple API that covers most of the cases is just providing nullptr
for repeating the same command, "" for not repeating, or a custom string for
any other case. If in the future anyone needs something very customized, a new
override could be created, but I don't think this will happen anytime soon.

Another option is to provide a callback function instead of a string, but I
don't know if it's too much.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D77444/new/

https://reviews.llvm.org/D77444

Files:
  lldb/include/lldb/API/SBCommandInterpreter.h
  lldb/source/API/SBCommandInterpreter.cpp
  lldb/unittests/Interpreter/CMakeLists.txt
  lldb/unittests/Interpreter/TestAutoRepeat.cpp

Index: lldb/unittests/Interpreter/TestAutoRepeat.cpp
===================================================================
--- /dev/null
+++ lldb/unittests/Interpreter/TestAutoRepeat.cpp
@@ -0,0 +1,141 @@
+//===-- TestAutoRepeat.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 <cassert>
+#include <cstring>
+#include <string>
+
+using namespace lldb;
+
+class TestAutoRepeat : public testing::Test {
+protected:
+  void SetUp() override {
+    SBDebugger::Initialize();
+    m_dbg = SBDebugger::Create(false /*source_init_files*/);
+    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(TestAutoRepeat, SingleWordCommand) {
+  // We first test a command without autorepeat
+  DummyCommand dummy("It worked");
+  m_interp.AddCommand("dummy", &dummy, nullptr /*help*/);
+  {
+    SBCommandReturnObject result;
+    m_interp.HandleCommand("dummy", result, true /*add_to_history*/);
+    EXPECT_TRUE(result.Succeeded());
+    EXPECT_STREQ(result.GetOutput(), "It worked\n");
+  }
+  {
+    SBCommandReturnObject result;
+    m_interp.HandleCommand("", result);
+    assert(!result.Succeeded() &&
+           "The command should fail as a repeated command");
+    EXPECT_STREQ(result.GetError(), "error: No auto repeat.\n");
+  }
+
+  // Now we test a command with autorepeat
+  m_interp.AddCommand("dummy_with_autorepeat", &dummy, nullptr /*help*/,
+                      nullptr /*syntax*/, nullptr /*auto_repeat_command*/);
+  {
+    SBCommandReturnObject result;
+    m_interp.HandleCommand("dummy_with_autorepeat", result,
+                           true /*add_to_history*/);
+    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(TestAutoRepeat, MultiWordCommand) {
+  auto command = m_interp.AddMultiwordCommand("multicommand", nullptr /*help*/);
+  // We first test a subcommand without autorepeat
+  DummyCommand subcommand("It worked again");
+  command.AddCommand("subcommand", &subcommand, nullptr /*help*/);
+  {
+    SBCommandReturnObject result;
+    m_interp.HandleCommand("multicommand subcommand", result,
+                           true /*add_to_history*/);
+    EXPECT_TRUE(result.Succeeded());
+    EXPECT_STREQ(result.GetOutput(), "It worked again\n");
+  }
+  {
+    SBCommandReturnObject result;
+    m_interp.HandleCommand("", result);
+    assert(!result.Succeeded() &&
+           "The command should fail as a repeated command");
+    EXPECT_STREQ(result.GetError(), "error: No auto repeat.\n");
+  }
+
+  // We first test a subcommand with autorepeat
+  command.AddCommand("subcommand_with_autorepeat", &subcommand,
+                     nullptr /*help*/, nullptr /*syntax*/,
+                     nullptr /*auto_repeat_command*/);
+  {
+    SBCommandReturnObject result;
+    m_interp.HandleCommand("multicommand subcommand_with_autorepeat", result,
+                           true /*add_to_history*/);
+    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, nullptr /*help*/,
+      nullptr /*syntax*/,
+      "multicommand subcommand_with_autorepeat" /*auto_repeat_command*/);
+  {
+    SBCommandReturnObject result;
+    m_interp.HandleCommand("multicommand subcommand_with_custom_autorepeat",
+                           result, true /*add_to_history*/);
+    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");
+  }
+}
Index: lldb/unittests/Interpreter/CMakeLists.txt
===================================================================
--- lldb/unittests/Interpreter/CMakeLists.txt
+++ lldb/unittests/Interpreter/CMakeLists.txt
@@ -1,8 +1,10 @@
 add_lldb_unittest(InterpreterTests
   TestCompletion.cpp
   TestOptionArgParser.cpp
+  TestAutoRepeat.cpp
 
   LINK_LIBS
+    liblldb
     lldbInterpreter
     lldbUtilityHelpers
   )
Index: lldb/source/API/SBCommandInterpreter.cpp
===================================================================
--- lldb/source/API/SBCommandInterpreter.cpp
+++ lldb/source/API/SBCommandInterpreter.cpp
@@ -154,12 +154,30 @@
                                        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 @@
     return ret;
   }
   std::shared_ptr<lldb::SBCommandPluginInterface> m_backend;
+  llvm::Optional<std::string> m_auto_repeat_command;
 };
 
 SBCommandInterpreter::SBCommandInterpreter(CommandInterpreter *interpreter)
@@ -710,6 +729,25 @@
   return LLDB_RECORD_RESULT(lldb::SBCommand());
 }
 
+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, 0 /*flags*/,
+      auto_repeat_command);
+
+  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());
+}
+
 SBCommand::SBCommand() { LLDB_RECORD_CONSTRUCTOR_NO_ARGS(SBCommand); }
 
 SBCommand::SBCommand(lldb::CommandObjectSP cmd_sp) : m_opaque_sp(cmd_sp) {}
@@ -816,6 +854,28 @@
   return LLDB_RECORD_RESULT(lldb::SBCommand());
 }
 
+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());
+  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, syntax,
+      0 /*flags*/, 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());
+}
+
 uint32_t SBCommand::GetFlags() {
   LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBCommand, GetFlags);
 
@@ -946,6 +1006,9 @@
   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 +1025,9 @@
   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));
 }
Index: lldb/include/lldb/API/SBCommandInterpreter.h
===================================================================
--- lldb/include/lldb/API/SBCommandInterpreter.h
+++ lldb/include/lldb/API/SBCommandInterpreter.h
@@ -111,14 +111,84 @@
 
   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
+  /// 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
+  /// 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 different 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 different arguments and flags this
+  ///     command accepts.
+  ///
+  /// \param[in] auto_repeat_type
+  ///     Flag that controls whether to accept autorepeating or not.
+  ///     Autorepeating is triggered when the user presses Enter successively
+  ///     after executing a 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 +353,88 @@
   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
+  /// 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 different 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
+  /// 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 different arguments and flags this
+  ///     command accepts.
+  ///
+  /// \param[in] auto_repeat_type
+  ///     Flag that controls whether to accept autorepeating or not.
+  ///     Autorepeating is triggered when the user presses Enter successively
+  ///     after executing a 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;
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to