Skip to content

Add the ability to define custom completers to the parsed_cmd template. #109062

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Sep 24, 2024

Conversation

jimingham
Copy link
Collaborator

If your arguments or option values are of a type that naturally uses one of our common completion mechanisms, you will get completion for free. But if you have your own custom values or if you want to do fancy things like have break set -s foo.dylib -n ba<TAB> only complete on symbols in foo.dylib, you can use this new mechanism to achieve that.

and arguments to the parsed_cmd template.
@jimingham jimingham requested review from medismailben, bulbazord and JDevlieghere and removed request for JDevlieghere September 17, 2024 23:30
@llvmbot llvmbot added the lldb label Sep 17, 2024
@llvmbot
Copy link
Member

llvmbot commented Sep 17, 2024

@llvm/pr-subscribers-lldb

Author: None (jimingham)

Changes

If your arguments or option values are of a type that naturally uses one of our common completion mechanisms, you will get completion for free. But if you have your own custom values or if you want to do fancy things like have break set -s foo.dylib -n ba&lt;TAB&gt; only complete on symbols in foo.dylib, you can use this new mechanism to achieve that.


Patch is 48.25 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/109062.diff

14 Files Affected:

  • (modified) lldb/bindings/python/python-wrapper.swig (+73)
  • (modified) lldb/docs/use/python-reference.rst (+143-1)
  • (modified) lldb/examples/python/cmdtemplate.py (+8-7)
  • (modified) lldb/examples/python/templates/parsed_cmd.py (+79-5)
  • (modified) lldb/include/lldb/Interpreter/ScriptInterpreter.h (+15)
  • (modified) lldb/include/lldb/Utility/CompletionRequest.h (+2)
  • (modified) lldb/source/Commands/CommandObjectCommands.cpp (+187)
  • (modified) lldb/source/Interpreter/Options.cpp (+3-2)
  • (modified) lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h (+8)
  • (modified) lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp (+40)
  • (modified) lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h (+8)
  • (modified) lldb/test/API/commands/command/script/add/TestAddParsedCommand.py (+81-34)
  • (modified) lldb/test/API/commands/command/script/add/test_commands.py (+46-18)
  • (modified) lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp (+13)
diff --git a/lldb/bindings/python/python-wrapper.swig b/lldb/bindings/python/python-wrapper.swig
index 810673aaec5d19..1f5012af99a291 100644
--- a/lldb/bindings/python/python-wrapper.swig
+++ b/lldb/bindings/python/python-wrapper.swig
@@ -752,6 +752,79 @@ lldb_private::python::SWIGBridge::LLDBSwigPythonGetRepeatCommandForScriptedComma
   return result.Str().GetString().str();
 }
 
+StructuredData::DictionarySP
+lldb_private::python::SWIGBridge::LLDBSwigPythonHandleArgumentCompletionForScriptedCommand(PyObject *implementor,
+    std::vector<llvm::StringRef> &args_vec, size_t args_pos, size_t pos_in_arg) {
+
+  PyErr_Cleaner py_err_cleaner(true);
+
+  PythonObject self(PyRefType::Borrowed, implementor);
+  auto pfunc = self.ResolveName<PythonCallable>("handle_argument_completion");
+  // If this isn't implemented, return an empty dict to signal falling back to default completion:
+  if (!pfunc.IsAllocated())
+    return {};
+
+  PythonList args_list(PyInitialValue::Empty);
+  for (auto elem : args_vec)
+    args_list.AppendItem(PythonString(elem));
+
+  PythonObject result = pfunc(args_list, PythonInteger(args_pos), PythonInteger(pos_in_arg));
+  // Returning None means do the ordinary completion
+  if (result.IsNone())
+    return {};
+
+  // Convert the return dictionary to a DictionarySP.
+  StructuredData::ObjectSP result_obj_sp = result.CreateStructuredObject();
+  if (!result_obj_sp)
+    return {};
+
+  StructuredData::DictionarySP dict_sp(new StructuredData::Dictionary(result_obj_sp));
+  if (dict_sp->GetType() == lldb::eStructuredDataTypeInvalid)
+    return {};
+  return dict_sp;
+}
+
+StructuredData::DictionarySP
+lldb_private::python::SWIGBridge::LLDBSwigPythonHandleOptionArgumentCompletionForScriptedCommand(PyObject *implementor,
+    llvm::StringRef &long_option, size_t pos_in_arg) {
+
+  PyErr_Cleaner py_err_cleaner(true);
+
+  PythonObject self(PyRefType::Borrowed, implementor);
+  auto pfunc = self.ResolveName<PythonCallable>("handle_option_argument_completion");
+  // If this isn't implemented, return an empty dict to signal falling back to default completion:
+  if (!pfunc.IsAllocated())
+    return {};
+
+  PythonObject result = pfunc(PythonString(long_option), PythonInteger(pos_in_arg));
+  // Returning None means do the ordinary completion
+  if (result.IsNone())
+    return {};
+
+  // Returning a boolean:
+  // True means the completion was handled, but there were no completions
+  // False means that the completion was not handled, again, do the ordinary completion:
+  if (result.GetObjectType() == PyObjectType::Boolean) {
+    if (!result.IsTrue())
+      return {};
+    // Make up a completion dictionary with the right element:
+    StructuredData::DictionarySP dict_sp(new StructuredData::Dictionary());
+    dict_sp->AddBooleanItem("no-completion", true);
+    return dict_sp;
+  }
+    
+
+  // Convert the return dictionary to a DictionarySP.
+  StructuredData::ObjectSP result_obj_sp = result.CreateStructuredObject();
+  if (!result_obj_sp)
+    return {};
+
+  StructuredData::DictionarySP dict_sp(new StructuredData::Dictionary(result_obj_sp));
+  if (dict_sp->GetType() == lldb::eStructuredDataTypeInvalid)
+    return {};
+  return dict_sp;
+}
+
 #include "lldb/Interpreter/CommandReturnObject.h"
 
 bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallParsedCommandObject(
diff --git a/lldb/docs/use/python-reference.rst b/lldb/docs/use/python-reference.rst
index 041e541a96f083..de4f219a13292f 100644
--- a/lldb/docs/use/python-reference.rst
+++ b/lldb/docs/use/python-reference.rst
@@ -587,8 +587,150 @@ say
 SBCommandReturnObject and SBStream both support this file-like behavior by
 providing write() and flush() calls at the Python layer.
 
+The commands that are added using this Class definition are what lldb calls
+"raw" commands.  The command interpreter doesn't attempt to parse the command,
+doesn't handle option values, neither generating help for them, or their
+completion.  Raw commands are useful when the arguments passed to the command
+are unstructured, and having to protect them against lldb command parsing would
+be onerous.  For instance, "expr" is a raw command.
+
+You can also add scripted commands that implement the "parsed command", where
+the options and their types are specified, as well as the argument and argument
+types.  These commands look and act like the majority of lldb commands, and you
+can also add custom completions for the options and/or the arguments if you have
+special needs.
+
+The easiest way to do this is to derive your new command from the lldb.ParsedCommand
+class.  That responds in the same way to the help & repeat command interfaces, and
+provides some convenience methods, and most importantly an LLDBOptionValueParser,
+accessed throught lldb.ParsedCommand.get_parser().  The parser is used to set
+your command definitions, and to retrieve option values in the __call__ method.
+
+To set the command definition, implement the ParsedCommand abstract method:
+
+::
+
+   def setup_command_definition(self):
+
+This is called when your command is added to lldb.  In this method you add the
+options and their types, the option help strings, etc. to the command using the API:
+
+::
+
+    def add_option(self, short_option, long_option, help, default,
+                   dest = None, required=False, groups = None,
+                   value_type=lldb.eArgTypeNone, completion_type=None,
+                   enum_values=None):
+        """
+        short_option: one character, must be unique, not required
+        long_option:  no spaces, must be unique, required
+        help:         a usage string for this option, will print in the command help
+        default:      the initial value for this option (if it has a value)
+        dest:         the name of the property that gives you access to the value for
+                      this value.  Defaults to the long option if not provided.
+        required: if true, this option must be provided or the command will error out
+        groups: Which "option groups" does this option belong to.  This can either be
+                a simple list (e.g. [1, 3, 4, 5]) or you can specify ranges by sublists:
+                so [1, [3,5]] is the same as [1, 3, 4, 5].
+        value_type: one of the lldb.eArgType enum values.  Some of the common arg
+                    types also have default completers, which will be applied automatically.
+        completion_type: currently these are values form the lldb.CompletionType enum.	If
+                         you need custom completions, implement	handle_option_argument_completion.
+        enum_values: An array of duples: ["element_name", "element_help"].  If provided,
+                     only one of the enum elements is allowed.  The value will be the
+                     element_name for the chosen enum element as a string.
+        """
+
+Similarly, you can add argument types to the command:
+
+::
+
+    def make_argument_element(self, arg_type, repeat = "optional", groups = None):
+        """
+      	arg_type: The argument type, one of the	lldb.eArgType enum values.
+      	repeat:	Choose from the	following options:
+      	      	"plain"	- one value
+      	      	"optional" - zero or more values
+      	      	"plus" - one or	more values
+      	groups:	As with	add_option.
+        """
+
+Then implement the body of the command by defining:
+
+::
+
+    def __call__(self, debugger, args_array, exe_ctx, result):
+        """This is the command callback.  The option values are
+        provided by the 'dest' properties on the parser.
+
+        args_array: This is the list of arguments provided.
+        exe_ctx: Gives the SBExecutionContext on which the
+                 command should operate.
+        result:  Any results of the command should be
+                 written into this SBCommandReturnObject.
+        """
+
+This differs from the "raw" command's __call__ in that the arguments are already
+parsed into the args_array, and the option values are set in the parser, and
+can be accessed using their property name.  The LLDBOptionValueParser class has
+a couple of other handy methods:
+
+::
+    def was_set(self, long_option_name):
+
+returns True if the option was specified on the command line.
+
+::
+    def dest_for_option(self, long_option_name):
+    """
+    This will return the value of the dest variable you defined for opt_name.
+    Mostly useful for handle_completion where you get passed the long option.
+    """
+
+lldb will handle completing your option names, and all your enum values
+automatically.  If your option or argument types have associated built-in completers,
+then lldb will also handle that completion for you.  But if you have a need for
+custom completions, either in your arguments or option values, you can handle
+completion by hand as well.  To handle completion of option value arguments,
+your lldb.ParsedCommand subclass should implement:
+
+::
+    def handle_option_argument_completion(self, args, arg_pos, cursor_pos):
+    """
+    args: A list of the arguments to the command
+    arg_pos: An index into the args list of the argument with the cursor
+    cursor_pos: The cursor position in the arg specified by arg_pos
+    """
+
+When this command is called, the command line has been parsed up to the word
+containing the cursor, and any option values set in that part of the command
+string are available from the option value parser.  That's useful for instance
+if you have a --shared-library option that would constrain the completions for,
+say, a symbol name option or argument.
+
+The return value specifies what the completion options are.  You have four
+choices:
+
+True: the completion was handled with no completions.
+
+False: the completion was not handled, forward it to the regular
+completion machinery.
+
+A dictionary with the key: "completion": there is one candidate,
+whose value is the value of the "completion" key.  Optionally you can pass a
+"mode" key whose value is either "partial" or "complete".  Return partial if
+the "completion" string is a prefix for all the completed value, and "complete"
+if it is the full completion. The default is "complete".
+
+A dictionary with the key: "values" whose value is a list of candidate completion
+strings.  The command interpreter will present those strings as the available choices.
+You can optionally include a "descriptions" key, whose value is a parallel array
+of description strings, and the completion will show the description next to
+each completion.
+
+
 One other handy convenience when defining lldb command-line commands is the
-command command script import which will import a module specified by file
+command "command script import" which will import a module specified by file
 path, so you don't have to change your PYTHONPATH for temporary scripts. It
 also has another convenience that if your new script module has a function of
 the form:
diff --git a/lldb/examples/python/cmdtemplate.py b/lldb/examples/python/cmdtemplate.py
index 9a96888508b6f2..bbdc579f7e8f75 100644
--- a/lldb/examples/python/cmdtemplate.py
+++ b/lldb/examples/python/cmdtemplate.py
@@ -26,8 +26,8 @@ def register_lldb_command(cls, debugger, module_name):
         )
 
     def setup_command_definition(self):
-
-        self.ov_parser.add_option(
+        ov_parser = self.get_parser()
+        ov_parser.add_option(
             "i",
             "in-scope",
             help = "in_scope_only = True",
@@ -36,7 +36,7 @@ def setup_command_definition(self):
             default = True,
         )
 
-        self.ov_parser.add_option(
+        ov_parser.add_option(
             "i",
             "in-scope",
             help = "in_scope_only = True",
@@ -45,7 +45,7 @@ def setup_command_definition(self):
             default=True,
         )
         
-        self.ov_parser.add_option(
+        ov_parser.add_option(
             "a",
             "arguments",
             help = "arguments = True",
@@ -54,7 +54,7 @@ def setup_command_definition(self):
             default = True,
         )
 
-        self.ov_parser.add_option(
+        ov_parser.add_option(
             "l",
             "locals",
             help = "locals = True",
@@ -63,7 +63,7 @@ def setup_command_definition(self):
             default = True,
         )
 
-        self.ov_parser.add_option(
+        ov_parser.add_option(
             "s",
             "statics",
             help = "statics = True",
@@ -100,8 +100,9 @@ def __call__(self, debugger, command, exe_ctx, result):
             result.SetError("invalid frame")
             return
 
+        ov_parser = self.get_parser()
         variables_list = frame.GetVariables(
-            self.ov_parser.arguments, self.ov_parser.locals, self.ov_parser.statics, self.ov_parser.inscope
+            ov_parser.arguments, ov_parser.locals, ov_parser.statics, ov_parser.inscope
         )
         variables_count = variables_list.GetSize()
         if variables_count == 0:
diff --git a/lldb/examples/python/templates/parsed_cmd.py b/lldb/examples/python/templates/parsed_cmd.py
index 06124adf43420a..c2be2d8d835f33 100644
--- a/lldb/examples/python/templates/parsed_cmd.py
+++ b/lldb/examples/python/templates/parsed_cmd.py
@@ -43,7 +43,65 @@ def __call__(self, debugger, args_list, exe_ctx, result):
 will return True if the user set this option, and False if it was left at its default
 value.
 
-There are example commands in the lldb testsuite at:
+Custom Completions:
+
+You can also implement custom completers for your custom command, either for the
+arguments to your command or to the option values in your command.  If you use enum
+values or if your option/argument uses is one of the types we have completers for,
+you should not need to do this.  But if you have your own completeable types, or if
+you want completion of one option to be conditioned by other options on the command
+line, you can use this interface to take over the completion.  
+
+You can choose to add a completion for the option values defined for your command,
+or for the arguments, separately.  For the option values, define:
+
+def handle_option_argument_completion(self, long_option, cursor_pos):
+
+The line to be completed will be parsed up to the option containint the cursor position, 
+and the values will be set in the OptionValue parser object.  long_option will be
+the option name containing the cursor, and cursor_pos will be the position of the cursor
+in that option's value.  You can call the OVParser.dest_for_option(long_option) to get the
+value for that option.  The other options that came before the cursor in the command
+line will also be set in the OV Parser when the completion handler is called.
+
+For argument values, define:
+
+def handle_argument_completion(self, args, arg_pos, cursor_pos):
+
+Again, the command line will be parsed up to the cursor position, and all the options
+before the cursor pose will be set in the OVParser.  args is a python list of the
+arguments, arg_pos is the index of the argument with the cursor, and cursor_pos is
+the position of the cursor in the argument.
+
+In both cases, the return value determines the completion.
+
+Return False to mean "Not Handled" - in which case lldb will fall back on the
+standard completion machinery.
+
+Return True to mean "Handled with no completions".
+
+If there is a single unique completion, return a Python dictionary with two elements:
+
+return {"completion" : "completed_value", "mode" : <"partial", "complete">}
+
+If the mode is "partial", then the completion is to a common base, if it is "complete"
+then the argument is considered done - mostly meaning lldb will put a space after the
+completion string.  "complete" is the default if no "mode" is specified.
+
+If there are multiple completion options, then return:
+
+return {"values" : ["option1", "option2"]}
+
+Optionally, you can return a parallel array of "descriptions" which the completer will 
+print alongside the options:
+
+return {"values" : ["option1", "option2"], "descriptions" : ["the first option", "the second option"]}
+
+The cmdtemplate example currently uses the parsed command infrastructure:
+
+llvm-project/lldb/examples/python/cmdtemplate.py
+
+There are also a few example commands in the lldb testsuite at:
 
 llvm-project/lldb/test/API/commands/command/script/add/test_commands.py
 """
@@ -229,7 +287,11 @@ def was_set(self, opt_name):
         """ Call this in the __call__ method of your command to determine
             whether this option was set on the command line.  It is sometimes
             useful to know whether an option has the default value because the
-            user set it explicitly (was_set -> True) or not.  """
+            user set it explicitly (was_set -> True) or not.
+            You can also call this in a handle_completion method, but it will
+            currently only report true values for the options mentioned 
+            BEFORE the cursor point in the command line.
+        """
 
         elem = self.get_option_element(opt_name)
         if not elem:
@@ -239,6 +301,16 @@ def was_set(self, opt_name):
         except AttributeError:
             return False
 
+    def dest_for_option(self, opt_name):
+        """ This will return the value of the dest variable you defined for opt_name.
+            Mostly useful for handle_completion where you get passed the long option.
+        """
+        elem = self.get_option_element(opt_name)
+        if not elem:
+            return None
+        value = self.__dict__[elem["dest"]]
+        return value
+
     def add_option(self, short_option, long_option, help, default,
                    dest = None, required=False, groups = None,
                    value_type=lldb.eArgTypeNone, completion_type=None,
@@ -251,11 +323,13 @@ def add_option(self, short_option, long_option, help, default,
         dest: the name of the property that gives you access to the value for
                  this value.  Defaults to the long option if not provided.
         required: if true, this option must be provided or the command will error out
-        groups: Which "option groups" does this option belong to
+        groups: Which "option groups" does this option belong to.  This can either be
+                a simple list (e.g. [1, 3, 4, 5]) or you can specify ranges by sublists:
+                so [1, [3,5]] is the same as [1, 3, 4, 5].
         value_type: one of the lldb.eArgType enum values.  Some of the common arg
                     types also have default completers, which will be applied automatically.
-        completion_type: currently these are values form the lldb.CompletionType enum, I
-                         haven't done custom completions yet.
+        completion_type: currently these are values form the lldb.CompletionType enum.  If
+                         you need custom completions, implement handle_option_argument_completion.
         enum_values: An array of duples: ["element_name", "element_help"].  If provided,
                      only one of the enum elements is allowed.  The value will be the 
                      element_name for the chosen enum element as a string. 
diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
index addb1394ab5652..6fc97466892ca9 100644
--- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h
+++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
@@ -436,6 +436,21 @@ class ScriptInterpreter : public PluginInterface {
                                      Args &args) {
     return std::nullopt;
   }
+  
+  virtual StructuredData::DictionarySP HandleArgumentCompletionForScriptedCommand(
+      StructuredData::GenericSP impl_obj_sp, std::vector<llvm::StringRef> &args, 
+      size_t args_pos, size_t char_in_arg)
+  {
+    return {};
+  }
+
+  virtual StructuredData::DictionarySP 
+  HandleOptionArgumentCompletionForScriptedCommand(
+      StructuredData::GenericSP impl_obj_sp, llvm::StringRef &long_name, 
+      size_t char_in_arg)
+  {
+    return {};
+  }
 
   virtual bool RunScriptFormatKeyword(const char *impl_function,
                                       Process *process, std::string &output,
diff --git a/lldb/include/lldb/Utility/CompletionRequest.h b/lldb/include/lldb/Utility/CompletionRequest.h
index 1a2b1d639950fc..242ff383047410 100644
--- a/lldb/include/lldb/Utility/...
[truncated]

Copy link

github-actions bot commented Sep 17, 2024

✅ With the latest revision this PR passed the Python code formatter.

Copy link

github-actions bot commented Sep 17, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

Copy link
Member

@medismailben medismailben left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, this looks fine. It would be nice to surround the keywords with backticks and the function definition with a .. code-block:: python so it gets rendered properly on the website.

accessed throught lldb.ParsedCommand.get_parser(). The parser is used to set
your command definitions, and to retrieve option values in the __call__ method.

To set the command definition, implement the ParsedCommand abstract method:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we missing a word here ?

Suggested change
To set the command definition, implement the ParsedCommand abstract method:
To set up the command definition, implement the ParsedCommand abstract method:

Comment on lines 698 to 703
def handle_option_argument_completion(self, args, arg_pos, cursor_pos):
"""
args: A list of the arguments to the command
arg_pos: An index into the args list of the argument with the cursor
cursor_pos: The cursor position in the arg specified by arg_pos
"""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the same method used to implement custom completion for both option values and arguments ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, I stopped half way through...

Comment on lines 719 to 723
A dictionary with the key: "completion": there is one candidate,
whose value is the value of the "completion" key. Optionally you can pass a
"mode" key whose value is either "partial" or "complete". Return partial if
the "completion" string is a prefix for all the completed value, and "complete"
if it is the full completion. The default is "complete".
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This paragraph is a bit confusing to me, an example would be useful or may be could be re-written.

Comment on lines 725 to 729
A dictionary with the key: "values" whose value is a list of candidate completion
strings. The command interpreter will present those strings as the available choices.
You can optionally include a "descriptions" key, whose value is a parallel array
of description strings, and the completion will show the description next to
each completion.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto: an example might be useful here

Comment on lines +57 to +59

def handle_option_argument_completion(self, long_option, cursor_pos):

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use .. code-block:: python so this gets rendered differently on the website

Suggested change
def handle_option_argument_completion(self, long_option, cursor_pos):
.. code-block:: python
def handle_option_argument_completion(self, long_option, cursor_pos):

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did this throughout this section, though if we're being consistent there are probably a bunch of other places in this doc that we should do that.

for (size_t idx = 0; idx < num_completions; idx++) {
auto val = completions->GetItemAtIndexAsString(idx);
if (!val)
// FIXME: How do I report this error?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

todo ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More of an existential question. We're in the middle of handling completion, we really don't have any acceptable way to stick an error message in the middle of that. So I actually don't currently know how to tell the user. We could log this, but that's not somewhere folks who are fiddling around with this would know to look.

I think more generally we should have a "python developer's" mode where we dump any python errors to the immediate debugger stdout so they will be obvious even if they mangle up the terminal contents. Then you could run in this verbose mode while developing extensions, but not inflict the damage on your users. But that's a separate feature...

Copy link
Member

@medismailben medismailben left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Thanks for addressing my comments :)

@jimingham jimingham merged commit 04b443e into llvm:main Sep 24, 2024
8 checks passed
@jimingham jimingham deleted the parsed-command-completion branch September 24, 2024 17:00
jimingham added a commit to jimingham/from-apple-llvm-project that referenced this pull request Feb 11, 2025
…e. (llvm#109062)

If your arguments or option values are of a type that naturally uses one
of our common completion mechanisms, you will get completion for free.
But if you have your own custom values or if you want to do fancy things
like have `break set -s foo.dylib -n ba<TAB>` only complete on symbols
in foo.dylib, you can use this new mechanism to achieve that.

(cherry picked from commit 04b443e)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants