Skip to content

Add a .NET API to add native completers #10722

Open

Description

@rjmholt

Summary of the new feature/enhancement

Currently there are ways to register a scriptblock as a completer with Register-ArgumentCompleter, and on cmdlets it's possible to use the [ArgumentCompleter()] attribute to specify an IArgumentCompleter for a parameter. But there's no way to specify a completer for an ordinary command that I know of that doesn't involve executing a scriptblock.

By this I mean, you can use Register-ArgumentCompleter or you can use override TabCompletion2, but I'd really like a nicer hook for:

  • General completions
  • Command completions
  • Native command completions, especially

Activity

  1. SeeminglyScience commented on Oct 7, 2019

    @SeeminglyScience
    Contributor

    I'd love to see more ways of extending completion! One of the biggest things that's always stopped me from working on anything to extend general completion is that it's all or nothing. If you override TabExpansion2, then anyone else trying to do the same will completely override you. I'd love to see something like an interface that can be registered in the engine to incrementally augment completion. Something like:

    public interface ICompletionExtension
    {
        IEnumerable<CompletionResult> CompleteComment(CompletionContext context);
    
        IEnumerable<CompletionResult> CompleteMember(CompletionContext context);
    
        IEnumerable<CompletionResult> CompleteCommandParameter(CompletionContext context);
    
        IEnumerable<CompletionResult> CompleteCommandArgument(CompletionContext context);
    
        // Other cherry picked methods from CompletionCompleters/CompletionAnalysis here.
    }
    
    public class CompletionEngine
    {
        public void RegisterCompletionExtension(ICompletionExtension extension);
    
        public void UnregisterCompletionExtension(ICompletionExtension extension);
    
        // Mirroring CommandCompletion.CompleteInput
        public CommandCompletion CompleteInput(
            Ast ast,
            Token[] tokens,
            IScriptPosition positionOfCursor,
            Hashtable options);
    }
    
    public partial class EngineIntrinsics
    {
        public CompletionEngine Completion { get; }
    }
  2. iSazonov commented on Oct 9, 2019

    @iSazonov
    Collaborator

    I hope we combine this in one engine with #10741.

  3. iSazonov commented on Jul 29, 2020

    @iSazonov
    Collaborator

    Thinking more about the request I believe it is an edge case. Can you show other use cases (besides UnixCompleters)?

    If we compare PowerShell and Bash design both do the some - register a script for completion of a command. So who else would need a rapper like UnixCompleters?

  4. rkeithhill commented on Aug 1, 2020

    @rkeithhill
    Collaborator

    Ilya (@iSazonov) Wouldn't this feature apply to any completions that someone wants to implement in a binary (C#-based) module?

  5. iSazonov commented on Aug 2, 2020

    @iSazonov
    Collaborator

    Keith Hill (@rkeithhill) I want to see examples. I only see UnixCompleters scenario and it is very specific, edge scenario. PowerShell already implement completions and API for customizations. We need to be sensitive to what exactly we need to add if necessary.

  6. SeeminglyScience commented on Aug 2, 2020

    @SeeminglyScience
    Contributor

    Ilya (@iSazonov) that's gonna be hard to show. Any time I've had a similar idea I've thrown it away once I got to the "oh I have to make 100 script block wrappers" step because it felt real dirty.

  7. SeeminglyScience commented on Aug 2, 2020

    @SeeminglyScience
    Contributor

    Also probably wouldn't be too hard to add. Might be able to get away with switching the internal dictionary to store Func<string, CommandAst, int, object> and generating a thin wrapper delegate around registered script blocks.

  8. iSazonov commented on Aug 2, 2020

    @iSazonov
    Collaborator

    On the other hand, small and simple scripts can be more attractive (easy to write, easy to debug) than one huge script or code.

  9. SeeminglyScience commented on Aug 2, 2020

    @SeeminglyScience
    Contributor

    Yeah for sure it can be. Sometimes code is complicated though 🤷

  10. iSazonov commented on Aug 3, 2020

    @iSazonov
    Collaborator

    Thanks for confirmation my thoughts.

    I also feel that sometimes it is convenient for authors of binary modules to write a completer on C# than a script block.
    For them, we could enhance the Register-ArgumentCompleter cmdlet and allow registering code that implements a new interface.

  11. rjmholt commented on Aug 3, 2020

    @rjmholt
    CollaboratorAuthor

    enhance the Register-ArgumentCompleter cmdlet

    Still requires running a pipeline though. That's too slow for something executed in a profile or for many commands. We need a lightweight registration mechanism that doesn't require a runspace, the pipeline thread or parameter binding.

  12. iSazonov commented on Aug 3, 2020

    @iSazonov
    Collaborator

    Still requires running a pipeline though.

    If you agreed that the cmdlet could register a binary completer our next step would be to recognize the need to add new public API to do this.

  13. rjmholt commented on Aug 3, 2020

    @rjmholt
    CollaboratorAuthor

    If you agreed that the cmdlet could register a binary completer

    Oh, yes, absolutely; Register-ArgumentCompleter should support a parameter set that takes a completer object instead of a ScriptBlock

  14. iSazonov commented on Aug 4, 2020

    @iSazonov
    Collaborator

    So we could discuss follow signatures:

    Register-ArgumentCompleter -Command <string[]>  -CompleterType [Custom type with ICommandCompleter]
    Register-ArgumentCompleter -ParameterName <string> [-Command <string[]>]  -CompleterType [Custom type with ICommandCompleter]
    public void RegisterArgumentCompleter(string commandName, ICommandCompleter completer);
    public void RegisterArgumentCompleter(string[] commandName, ICommandCompleter completer);
    public void RegisterArgumentCompleter(IEnumerable<string> commandName, ICommandCompleter completer);
    
    public void RegisterArgumentCompleter(string? commandName, string parameterName, IArgumentCompleter completer);
    public void RegisterArgumentCompleter((string? commandName, string parameterName) commandparameterPair, IArgumentCompleter completer);
    public void RegisterArgumentCompleter(IEnumerable<string?, string> commandparameterPair, IArgumentCompleter completer);
    
  15. microsoft-github-policy-service commented on Nov 16, 2023

    @microsoft-github-policy-service
  16. microsoft-github-policy-service commented on Nov 16, 2023

    @microsoft-github-policy-service
  17. microsoft-github-policy-service commented on Nov 23, 2023

    @microsoft-github-policy-service
  18. added
    KeepOpenThe bot will ignore these and not auto-close
    and removed
    Resolution-No ActivityIssue has had no activity for 6 months or more
    on Mar 17, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Issue-Enhancementthe issue is more of a feature request than a bugKeepOpenThe bot will ignore these and not auto-closeWG-Enginecore PowerShell engine, interpreter, and runtime

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions