From 7f428485e75e9b1b0db1320216d1c31469770563 Mon Sep 17 00:00:00 2001 From: Alan Agius Date: Thu, 27 Apr 2023 21:49:20 +0200 Subject: [PATCH] fix: do not crash completion when having negated options (#2322) Current completion crashes when using ZSH and having negated options. --- lib/completion.ts | 44 +++++++++++++++++++++++++------------------- test/completion.cjs | 22 ++++++++++++++++++++++ 2 files changed, 47 insertions(+), 19 deletions(-) diff --git a/lib/completion.ts b/lib/completion.ts index 73c12c177..15d39d556 100644 --- a/lib/completion.ts +++ b/lib/completion.ts @@ -130,9 +130,12 @@ export class Completion implements CompletionInstance { !options.hiddenOptions.includes(key) && !this.argsContainKey(args, key, negable) ) { - this.completeOptionKey(key, completions, current); - if (negable && !!options.default[key]) - this.completeOptionKey(`no-${key}`, completions, current); + this.completeOptionKey( + key, + completions, + current, + negable && !!options.default[key] + ); } }); } @@ -248,28 +251,31 @@ export class Completion implements CompletionInstance { private completeOptionKey( key: string, completions: string[], - current: string + current: string, + negable: boolean ) { - const descs = this.usage.getDescriptions(); - const startsByTwoDashes = (s: string) => /^--/.test(s); - const isShortOption = (s: string) => /^[^0-9]$/.test(s); - const dashes = - !startsByTwoDashes(current) && isShortOption(key) ? '-' : '--'; - if (!this.zshShell) { - completions.push(dashes + key); - } else { - const aliasKey = this?.aliases?.[key].find(alias => { + let keyWithDesc = key; + if (this.zshShell) { + const descs = this.usage.getDescriptions(); + const aliasKey = this?.aliases?.[key]?.find(alias => { const desc = descs[alias]; return typeof desc === 'string' && desc.length > 0; }); const descFromAlias = aliasKey ? descs[aliasKey] : undefined; const desc = descs[key] ?? descFromAlias ?? ''; - completions.push( - dashes + - `${key.replace(/:/g, '\\:')}:${desc - .replace('__yargsString__:', '') - .replace(/(\r\n|\n|\r)/gm, ' ')}` - ); + keyWithDesc = `${key.replace(/:/g, '\\:')}:${desc + .replace('__yargsString__:', '') + .replace(/(\r\n|\n|\r)/gm, ' ')}`; + } + + const startsByTwoDashes = (s: string) => /^--/.test(s); + const isShortOption = (s: string) => /^[^0-9]$/.test(s); + const dashes = + !startsByTwoDashes(current) && isShortOption(key) ? '-' : '--'; + + completions.push(dashes + keyWithDesc); + if (negable) { + completions.push(dashes + 'no-' + keyWithDesc); } } diff --git a/test/completion.cjs b/test/completion.cjs index 4e2d6bef0..14d8ef99c 100644 --- a/test/completion.cjs +++ b/test/completion.cjs @@ -1139,6 +1139,28 @@ describe('Completion', () => { r.logs.should.include('--foo:bar'); }); + it('completes with no- prefix flags defaulting to true when boolean-negation is set', () => { + process.env.SHELL = '/bin/zsh'; + + const r = checkUsage( + () => + yargs(['./completion', '--get-yargs-completions', '--']) + .options({ + foo: {describe: 'foo flag', type: 'boolean', default: true}, + bar: {describe: 'bar flag', type: 'boolean'}, + }) + .parserConfiguration({'boolean-negation': true}).argv + ); + + r.logs.should.eql([ + '--help:Show help', + '--version:Show version number', + '--foo:foo flag', + '--no-foo:foo flag', + '--bar:bar flag', + ]); + }); + it('bails out early when full command matches', () => { process.env.SHELL = '/bin/zsh'; const r = checkUsage(() => {