This patch should allow the user to set specific auto-completion type for their custom commands. To do so, we had to hoist the `CompletionType` enum so the user can access it and add a new completion type flag to the CommandScriptAdd Command Object. So now, the user can specify which completion type will be used with their custom command, when they register it. This also makes the `crashlog` custom commands use disk-file completion type, to browse through the user file system and load the report. Differential Revision: https://reviews.llvm.org/D152011 Signed-off-by: Med Ismail Bennani <ismail@bennani.ma>
207 lines
7.4 KiB
C++
207 lines
7.4 KiB
C++
#include "CommandObjectSession.h"
|
|
#include "lldb/Host/OptionParser.h"
|
|
#include "lldb/Interpreter/CommandInterpreter.h"
|
|
#include "lldb/Interpreter/CommandOptionArgumentTable.h"
|
|
#include "lldb/Interpreter/CommandReturnObject.h"
|
|
#include "lldb/Interpreter/OptionArgParser.h"
|
|
#include "lldb/Interpreter/OptionValue.h"
|
|
#include "lldb/Interpreter/OptionValueBoolean.h"
|
|
#include "lldb/Interpreter/OptionValueString.h"
|
|
#include "lldb/Interpreter/OptionValueUInt64.h"
|
|
#include "lldb/Interpreter/Options.h"
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
|
|
class CommandObjectSessionSave : public CommandObjectParsed {
|
|
public:
|
|
CommandObjectSessionSave(CommandInterpreter &interpreter)
|
|
: CommandObjectParsed(interpreter, "session save",
|
|
"Save the current session transcripts to a file.\n"
|
|
"If no file if specified, transcripts will be "
|
|
"saved to a temporary file.",
|
|
"session save [file]") {
|
|
CommandArgumentEntry arg1;
|
|
arg1.emplace_back(eArgTypePath, eArgRepeatOptional);
|
|
m_arguments.push_back(arg1);
|
|
}
|
|
|
|
~CommandObjectSessionSave() override = default;
|
|
|
|
void
|
|
HandleArgumentCompletion(CompletionRequest &request,
|
|
OptionElementVector &opt_element_vector) override {
|
|
lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(
|
|
GetCommandInterpreter(), lldb::eDiskFileCompletion, request, nullptr);
|
|
}
|
|
|
|
protected:
|
|
bool DoExecute(Args &args, CommandReturnObject &result) override {
|
|
llvm::StringRef file_path;
|
|
|
|
if (!args.empty())
|
|
file_path = args[0].ref();
|
|
|
|
if (m_interpreter.SaveTranscript(result, file_path.str()))
|
|
result.SetStatus(eReturnStatusSuccessFinishNoResult);
|
|
else
|
|
result.SetStatus(eReturnStatusFailed);
|
|
return result.Succeeded();
|
|
}
|
|
};
|
|
|
|
#define LLDB_OPTIONS_history
|
|
#include "CommandOptions.inc"
|
|
|
|
class CommandObjectSessionHistory : public CommandObjectParsed {
|
|
public:
|
|
CommandObjectSessionHistory(CommandInterpreter &interpreter)
|
|
: CommandObjectParsed(interpreter, "session history",
|
|
"Dump the history of commands in this session.\n"
|
|
"Commands in the history list can be run again "
|
|
"using \"!<INDEX>\". \"!-<OFFSET>\" will re-run "
|
|
"the command that is <OFFSET> commands from the end"
|
|
" of the list (counting the current command).",
|
|
nullptr) {}
|
|
|
|
~CommandObjectSessionHistory() override = default;
|
|
|
|
Options *GetOptions() override { return &m_options; }
|
|
|
|
protected:
|
|
class CommandOptions : public Options {
|
|
public:
|
|
CommandOptions()
|
|
: m_start_idx(0), m_stop_idx(0), m_count(0), m_clear(false) {}
|
|
|
|
~CommandOptions() override = default;
|
|
|
|
Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
|
|
ExecutionContext *execution_context) override {
|
|
Status error;
|
|
const int short_option = m_getopt_table[option_idx].val;
|
|
|
|
switch (short_option) {
|
|
case 'c':
|
|
error = m_count.SetValueFromString(option_arg, eVarSetOperationAssign);
|
|
break;
|
|
case 's':
|
|
if (option_arg == "end") {
|
|
m_start_idx.SetCurrentValue(UINT64_MAX);
|
|
m_start_idx.SetOptionWasSet();
|
|
} else
|
|
error = m_start_idx.SetValueFromString(option_arg,
|
|
eVarSetOperationAssign);
|
|
break;
|
|
case 'e':
|
|
error =
|
|
m_stop_idx.SetValueFromString(option_arg, eVarSetOperationAssign);
|
|
break;
|
|
case 'C':
|
|
m_clear.SetCurrentValue(true);
|
|
m_clear.SetOptionWasSet();
|
|
break;
|
|
default:
|
|
llvm_unreachable("Unimplemented option");
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
void OptionParsingStarting(ExecutionContext *execution_context) override {
|
|
m_start_idx.Clear();
|
|
m_stop_idx.Clear();
|
|
m_count.Clear();
|
|
m_clear.Clear();
|
|
}
|
|
|
|
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
|
|
return llvm::ArrayRef(g_history_options);
|
|
}
|
|
|
|
// Instance variables to hold the values for command options.
|
|
|
|
OptionValueUInt64 m_start_idx;
|
|
OptionValueUInt64 m_stop_idx;
|
|
OptionValueUInt64 m_count;
|
|
OptionValueBoolean m_clear;
|
|
};
|
|
|
|
bool DoExecute(Args &command, CommandReturnObject &result) override {
|
|
if (m_options.m_clear.GetCurrentValue() &&
|
|
m_options.m_clear.OptionWasSet()) {
|
|
m_interpreter.GetCommandHistory().Clear();
|
|
result.SetStatus(lldb::eReturnStatusSuccessFinishNoResult);
|
|
} else {
|
|
if (m_options.m_start_idx.OptionWasSet() &&
|
|
m_options.m_stop_idx.OptionWasSet() &&
|
|
m_options.m_count.OptionWasSet()) {
|
|
result.AppendError("--count, --start-index and --end-index cannot be "
|
|
"all specified in the same invocation");
|
|
result.SetStatus(lldb::eReturnStatusFailed);
|
|
} else {
|
|
std::pair<bool, uint64_t> start_idx(
|
|
m_options.m_start_idx.OptionWasSet(),
|
|
m_options.m_start_idx.GetCurrentValue());
|
|
std::pair<bool, uint64_t> stop_idx(
|
|
m_options.m_stop_idx.OptionWasSet(),
|
|
m_options.m_stop_idx.GetCurrentValue());
|
|
std::pair<bool, uint64_t> count(m_options.m_count.OptionWasSet(),
|
|
m_options.m_count.GetCurrentValue());
|
|
|
|
const CommandHistory &history(m_interpreter.GetCommandHistory());
|
|
|
|
if (start_idx.first && start_idx.second == UINT64_MAX) {
|
|
if (count.first) {
|
|
start_idx.second = history.GetSize() - count.second;
|
|
stop_idx.second = history.GetSize() - 1;
|
|
} else if (stop_idx.first) {
|
|
start_idx.second = stop_idx.second;
|
|
stop_idx.second = history.GetSize() - 1;
|
|
} else {
|
|
start_idx.second = 0;
|
|
stop_idx.second = history.GetSize() - 1;
|
|
}
|
|
} else {
|
|
if (!start_idx.first && !stop_idx.first && !count.first) {
|
|
start_idx.second = 0;
|
|
stop_idx.second = history.GetSize() - 1;
|
|
} else if (start_idx.first) {
|
|
if (count.first) {
|
|
stop_idx.second = start_idx.second + count.second - 1;
|
|
} else if (!stop_idx.first) {
|
|
stop_idx.second = history.GetSize() - 1;
|
|
}
|
|
} else if (stop_idx.first) {
|
|
if (count.first) {
|
|
if (stop_idx.second >= count.second)
|
|
start_idx.second = stop_idx.second - count.second + 1;
|
|
else
|
|
start_idx.second = 0;
|
|
}
|
|
} else /* if (count.first) */
|
|
{
|
|
start_idx.second = 0;
|
|
stop_idx.second = count.second - 1;
|
|
}
|
|
}
|
|
history.Dump(result.GetOutputStream(), start_idx.second,
|
|
stop_idx.second);
|
|
}
|
|
}
|
|
return result.Succeeded();
|
|
}
|
|
|
|
CommandOptions m_options;
|
|
};
|
|
|
|
CommandObjectSession::CommandObjectSession(CommandInterpreter &interpreter)
|
|
: CommandObjectMultiword(interpreter, "session",
|
|
"Commands controlling LLDB session.",
|
|
"session <subcommand> [<command-options>]") {
|
|
LoadSubCommand("save",
|
|
CommandObjectSP(new CommandObjectSessionSave(interpreter)));
|
|
LoadSubCommand("history",
|
|
CommandObjectSP(new CommandObjectSessionHistory(interpreter)));
|
|
}
|