diff --git a/lldb/include/lldb/API/SBCommandInterpreter.h b/lldb/include/lldb/API/SBCommandInterpreter.h --- a/lldb/include/lldb/API/SBCommandInterpreter.h +++ b/lldb/include/lldb/API/SBCommandInterpreter.h @@ -111,14 +111,86 @@ 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_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 @@ 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_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 --- a/lldb/source/API/SBCommandInterpreter.cpp +++ b/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(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 ¤t_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 m_backend; + llvm::Optional 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( + *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( + 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)); } diff --git a/lldb/unittests/Interpreter/CMakeLists.txt b/lldb/unittests/Interpreter/CMakeLists.txt --- a/lldb/unittests/Interpreter/CMakeLists.txt +++ b/lldb/unittests/Interpreter/CMakeLists.txt @@ -1,8 +1,10 @@ add_lldb_unittest(InterpreterTests TestCompletion.cpp TestOptionArgParser.cpp + TestAutoRepeat.cpp LINK_LIBS + liblldb lldbInterpreter lldbUtilityHelpers ) diff --git a/lldb/unittests/Interpreter/TestAutoRepeat.cpp b/lldb/unittests/Interpreter/TestAutoRepeat.cpp new file mode 100644 --- /dev/null +++ b/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 +#include +#include + +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"); + } +}