aboutsummaryrefslogtreecommitdiff
path: root/lldb/source/Commands/CommandObjectCommands.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lldb/source/Commands/CommandObjectCommands.cpp')
-rw-r--r--lldb/source/Commands/CommandObjectCommands.cpp191
1 files changed, 191 insertions, 0 deletions
diff --git a/lldb/source/Commands/CommandObjectCommands.cpp b/lldb/source/Commands/CommandObjectCommands.cpp
index e3291640..845b89a 100644
--- a/lldb/source/Commands/CommandObjectCommands.cpp
+++ b/lldb/source/Commands/CommandObjectCommands.cpp
@@ -1637,6 +1637,129 @@ private:
size_t GetNumOptions() { return m_num_options; }
+ void PrepareOptionsForCompletion(CompletionRequest &request,
+ OptionElementVector &option_vec,
+ ExecutionContext *exe_ctx) {
+ // I'm not sure if we'll get into trouble doing an option parsing start
+ // and end in this context. If so, then I'll have to directly tell the
+ // scripter to do this.
+ OptionParsingStarting(exe_ctx);
+ auto opt_defs = GetDefinitions();
+
+ // Iterate through the options we found so far, and push them into
+ // the scripted side.
+ for (auto option_elem : option_vec) {
+ int cur_defs_index = option_elem.opt_defs_index;
+ // If we don't recognize this option we can't set it.
+ if (cur_defs_index == OptionArgElement::eUnrecognizedArg ||
+ cur_defs_index == OptionArgElement::eBareDash ||
+ cur_defs_index == OptionArgElement::eBareDoubleDash)
+ continue;
+ bool option_has_arg = opt_defs[cur_defs_index].option_has_arg;
+ llvm::StringRef cur_arg_value;
+ if (option_has_arg) {
+ int cur_arg_pos = option_elem.opt_arg_pos;
+ if (cur_arg_pos != OptionArgElement::eUnrecognizedArg &&
+ cur_arg_pos != OptionArgElement::eBareDash &&
+ cur_arg_pos != OptionArgElement::eBareDoubleDash) {
+ cur_arg_value =
+ request.GetParsedLine().GetArgumentAtIndex(cur_arg_pos);
+ }
+ }
+ SetOptionValue(cur_defs_index, cur_arg_value, exe_ctx);
+ }
+ OptionParsingFinished(exe_ctx);
+ }
+
+ void
+ ProcessCompletionDict(CompletionRequest &request,
+ StructuredData::DictionarySP &completion_dict_sp) {
+ // We don't know how to process an empty completion dict, our callers have
+ // to do that.
+ assert(completion_dict_sp && "Must have valid completion dict");
+ // First handle the case of a single completion:
+ llvm::StringRef completion;
+ // If the dictionary has one element "no-completion" then we return here
+ if (completion_dict_sp->GetValueForKeyAsString("no-completion",
+ completion))
+ return;
+
+ if (completion_dict_sp->GetValueForKeyAsString("completion",
+ completion)) {
+ llvm::StringRef mode_str;
+ CompletionMode mode = CompletionMode::Normal;
+ if (completion_dict_sp->GetValueForKeyAsString("mode", mode_str)) {
+ if (mode_str == "complete")
+ mode = CompletionMode::Normal;
+ else if (mode_str == "partial")
+ mode = CompletionMode::Partial;
+ else {
+ // FIXME - how do I report errors here?
+ return;
+ }
+ }
+ request.AddCompletion(completion, "", mode);
+ return;
+ }
+ // The completions are required, the descriptions are not:
+ StructuredData::Array *completions;
+ StructuredData::Array *descriptions;
+ if (completion_dict_sp->GetValueForKeyAsArray("values", completions)) {
+ completion_dict_sp->GetValueForKeyAsArray("descriptions", descriptions);
+ size_t num_completions = completions->GetSize();
+ for (size_t idx = 0; idx < num_completions; idx++) {
+ auto val = completions->GetItemAtIndexAsString(idx);
+ if (!val)
+ // FIXME: How do I report this error?
+ return;
+
+ if (descriptions) {
+ auto desc = descriptions->GetItemAtIndexAsString(idx);
+ request.AddCompletion(*val, desc ? *desc : "");
+ } else
+ request.AddCompletion(*val);
+ }
+ }
+ }
+
+ void
+ HandleOptionArgumentCompletion(lldb_private::CompletionRequest &request,
+ OptionElementVector &option_vec,
+ int opt_element_index,
+ CommandInterpreter &interpreter) override {
+ ScriptInterpreter *scripter =
+ interpreter.GetDebugger().GetScriptInterpreter();
+
+ if (!scripter)
+ return;
+
+ ExecutionContext exe_ctx = interpreter.GetExecutionContext();
+ PrepareOptionsForCompletion(request, option_vec, &exe_ctx);
+
+ auto defs = GetDefinitions();
+
+ size_t defs_index = option_vec[opt_element_index].opt_defs_index;
+ llvm::StringRef option_name = defs[defs_index].long_option;
+ bool is_enum = defs[defs_index].enum_values.size() != 0;
+ if (option_name.empty())
+ return;
+ // If this is an enum, we don't call the custom completer, just let the
+ // regular option completer handle that:
+ StructuredData::DictionarySP completion_dict_sp;
+ if (!is_enum)
+ completion_dict_sp =
+ scripter->HandleOptionArgumentCompletionForScriptedCommand(
+ m_cmd_obj_sp, option_name, request.GetCursorCharPos());
+
+ if (!completion_dict_sp) {
+ Options::HandleOptionArgumentCompletion(request, option_vec,
+ opt_element_index, interpreter);
+ return;
+ }
+
+ ProcessCompletionDict(request, completion_dict_sp);
+ }
+
private:
struct EnumValueStorage {
EnumValueStorage() {
@@ -1878,6 +2001,74 @@ public:
Status GetArgsError() { return m_args_error.Clone(); }
bool WantsCompletion() override { return true; }
+private:
+ void PrepareOptionsForCompletion(CompletionRequest &request,
+ OptionElementVector &option_vec) {
+ // First, we have to tell the Scripted side to set the values in its
+ // option store, then we call into the handle_completion passing in
+ // an array of the args, the arg index and the cursor position in the arg.
+ // We want the script side to have a chance to clear its state, so tell
+ // it argument parsing has started:
+ Options *options = GetOptions();
+ // If there are not options, this will be nullptr, and in that case we
+ // can just skip setting the options on the scripted side:
+ if (options)
+ m_options.PrepareOptionsForCompletion(request, option_vec, &m_exe_ctx);
+ }
+
+public:
+ void HandleArgumentCompletion(CompletionRequest &request,
+ OptionElementVector &option_vec) override {
+ ScriptInterpreter *scripter = GetDebugger().GetScriptInterpreter();
+
+ if (!scripter)
+ return;
+
+ // Set up the options values on the scripted side:
+ PrepareOptionsForCompletion(request, option_vec);
+
+ // Now we have to make up the argument list.
+ // The ParseForCompletion only identifies tokens in the m_parsed_line
+ // it doesn't remove the options leaving only the args as it does for
+ // the regular Parse, so we have to filter out the option ones using the
+ // option_element_vector:
+
+ Options *options = GetOptions();
+ auto defs = options->GetDefinitions();
+
+ std::unordered_set<size_t> option_slots;
+ for (const auto &elem : option_vec) {
+ if (elem.opt_defs_index == -1)
+ continue;
+ option_slots.insert(elem.opt_pos);
+ if (defs[elem.opt_defs_index].option_has_arg)
+ option_slots.insert(elem.opt_arg_pos);
+ }
+
+ std::vector<llvm::StringRef> args_vec;
+ Args &args = request.GetParsedLine();
+ size_t num_args = args.GetArgumentCount();
+ size_t cursor_idx = request.GetCursorIndex();
+ size_t args_elem_pos = cursor_idx;
+
+ for (size_t idx = 0; idx < num_args; idx++) {
+ if (option_slots.count(idx) == 0)
+ args_vec.push_back(args[idx].ref());
+ else if (idx < cursor_idx)
+ args_elem_pos--;
+ }
+ StructuredData::DictionarySP completion_dict_sp =
+ scripter->HandleArgumentCompletionForScriptedCommand(
+ m_cmd_obj_sp, args_vec, args_elem_pos, request.GetCursorCharPos());
+
+ if (!completion_dict_sp) {
+ CommandObject::HandleArgumentCompletion(request, option_vec);
+ return;
+ }
+
+ m_options.ProcessCompletionDict(request, completion_dict_sp);
+ }
+
bool IsRemovable() const override { return true; }
ScriptedCommandSynchronicity GetSynchronicity() { return m_synchro; }