Skip to content
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

feat: allow to access output of default completion in custom completion #1878

Merged
merged 2 commits into from Feb 28, 2021

Conversation

jacobator
Copy link
Contributor

@jacobator jacobator commented Feb 26, 2021

Allow to access output of default completion in custom completion

Problem:

When using default completion both positional and option arguments are treated the same. This results in completion suggesting positional arguments in an option form, which is incorrect:

yargs
  .command(
    'exec <pos-a> <pos-b>',
    'Execute command.',
    (yargs) => {
      return yargs
        .positional('pos-a', { type: 'string', desc: 'Pos A' })
        .positional('pos-b', { type: 'string', desc: 'Pos B' })
        .option({
          'opt-a': { type: 'boolean', alias: 'oa', desc: 'Option A' },
          'opt-b': { type: 'string', requiresArg: true, desc: 'Option B' },
        });
    },
  )
  .completion('completion', 'Completion.').argv;
$ ./index.js exec --
--help     -- Show help
--opt-a    -- Option A
--opt-b    -- Option B
--pos-a    -- Pos A
--pos-b    -- Pos B
--version  -- Show version number
$ ./index.js exec --pos-a --
--help     -- Show help
--opt-a    -- Option A
--opt-b    -- Option B
--pos-b    -- Pos B
--version  -- Show version number

Currently, the solution is to write your on competion funtion from the ground up. There is no way to apply custom completion function that would be able to utilize default completion function output. But this would help significantly in the case above.

Solution:

This PR adds ability to pass onCompleted callback to defaultCompletion function to replace the done callback, allowing us to do this:

yargs
  .command(
    'exec <pos-a> <pos-b>',
    'Execute command.',
    (yargs) => {
      return yargs
        .positional('pos-a', { type: 'string', desc: 'Pos A' })
        .positional('pos-b', { type: 'string', desc: 'Pos B' })
        .option({
          'opt-a': { type: 'boolean', alias: 'oa', desc: 'Option A' },
          'opt-b': { type: 'string', requiresArg: true, desc: 'Option B' },
        });
    },
  )
  .completion(
    'completion',
    'Completion.',
    (current, argv, defaultCompletion, done) => {
      // Execute `defaultCompletion` to get default `completions`
      defaultCompletion((_err, completions) => {
        const filteredCompletions = completions.filter(
          (c) => !c.includes('pos'),
        );
        // Execute `done` with `filteredCompletions`
        done(filteredCompletions);
      });
    },
  ).argv;

The example above is naive and just filtering based on the pos text, but illustrates the point.

$ ./index.js exec --
--help     -- Show help
--opt-a    -- Option A
--opt-b    -- Option B
--version  -- Show version number

Ideally, the default completion should ignore positional arguments out of the box, but this is a simple solution to fix completion right now.

@bcoe
Copy link
Member

bcoe commented Feb 28, 2021

Ideally, the default completion should ignore positional arguments out of the box, but this is a simple solution to fix completion right now.

@jacobator I'd rather try to fix the specific bug, rather than growing yargs' API. The problem is we'd then have a new method to support forever.

It seems like we should be able to take the disjunction between positionals and options when showing completion, and figure out how to not suggest them?

@jacobator
Copy link
Contributor Author

@bcoe I agree that the disjunction of positionals/options should be done. I will try to look into that to see if I can assist with it.

But the PR here is not only about that. Even when the positionals are excluded from the suggestion list, you would still want to modify the default suggestions. E.g. remove descriptions for all options, reorder options, etc.

The way the default completion function access was added here #1855 does not make a lot of sense. You can either use default or fully custom completion for every call. This means you have to implement a full-fledged completion for some cases and would probably not require the default completion function for the others.

Having access to default completion suggestions output serves as a great starting point, helping with the custom completion implementation w/o having to do it from the ground-up.

@@ -278,7 +279,7 @@ interface FallbackCompletionFunction {
(
current: string,
argv: Arguments,
defaultCompletion: () => any,
defaultCompletion: (onCompleted?: CompletionCallback) => any,
Copy link
Member

Choose a reason for hiding this comment

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

I would be tempted to call this completionFilter.

@@ -193,7 +193,8 @@ export class Completion implements CompletionInstance {
return (this.customCompletionFunction as FallbackCompletionFunction)(
current,
argv,
() => this.defaultCompletion(args, argv, current, done),
(onCompleted = done) =>
Copy link
Member

Choose a reason for hiding this comment

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

We should add a test:

it('allows custom completion to be combined with default completion, using filter').

@jacobator jacobator requested a review from bcoe February 28, 2021 20:30
Copy link
Member

@bcoe bcoe left a comment

Choose a reason for hiding this comment

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

This LGTM, as a follow up PR, it would be good to add documentation. I'm fine with that being in a separate PR.

@bcoe bcoe merged commit 01619f6 into yargs:master Feb 28, 2021
@jacobator
Copy link
Contributor Author

I will update the docs tomorrow

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants