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

gar/npm fish #6216

Merged
merged 4 commits into from
Mar 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 16 additions & 14 deletions lib/commands/access.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,20 +53,22 @@ class Access extends BaseCommand {
return commands
}

switch (argv[2]) {
case 'grant':
return ['read-only', 'read-write']
case 'revoke':
return []
case 'list':
case 'ls':
return ['packages', 'collaborators']
case 'get':
return ['status']
case 'set':
return setCommands
default:
throw new Error(argv[2] + ' not recognized')
if (argv.length === 3) {
switch (argv[2]) {
case 'grant':
return ['read-only', 'read-write']
case 'revoke':
return []
case 'list':
case 'ls':
return ['packages', 'collaborators']
case 'get':
return ['status']
case 'set':
return setCommands
default:
throw new Error(argv[2] + ' not recognized')
}
}
}

Expand Down
3 changes: 2 additions & 1 deletion lib/commands/audit.js
Original file line number Diff line number Diff line change
Expand Up @@ -389,11 +389,12 @@ class Audit extends ArboristWorkspaceCmd {
const argv = opts.conf.argv.remain

if (argv.length === 2) {
return ['fix']
return ['fix', 'signatures']
}

switch (argv[2]) {
case 'fix':
case 'signatures':
return []
default:
throw Object.assign(new Error(argv[2] + ' not recognized'), {
Expand Down
7 changes: 3 additions & 4 deletions lib/commands/completion.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,10 @@ class Completion extends BaseCommand {
})
}

const { COMP_CWORD, COMP_LINE, COMP_POINT } = process.env
const { COMP_CWORD, COMP_LINE, COMP_POINT, COMP_FISH } = process.env

// if the COMP_* isn't in the env, then just dump the script.
if (COMP_CWORD === undefined ||
COMP_LINE === undefined ||
COMP_POINT === undefined) {
if (COMP_CWORD === undefined || COMP_LINE === undefined || COMP_POINT === undefined) {
return dumpScript(resolve(this.npm.npmRoot, 'lib', 'utils', 'completion.sh'))
}

Expand All @@ -111,6 +109,7 @@ class Completion extends BaseCommand {
partialWords.push(partialWord)

const opts = {
isFish: COMP_FISH === 'true',
words,
w,
word,
Expand Down
3 changes: 3 additions & 0 deletions lib/commands/run-script.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ class RunScript extends BaseCommand {
// find the script name
const json = resolve(this.npm.localPrefix, 'package.json')
const { scripts = {} } = await rpj(json).catch(er => ({}))
if (opts.isFish) {
return Object.keys(scripts).map(s => `${s}\t${scripts[s].slice(0, 30)}`)
}
return Object.keys(scripts)
}
}
Expand Down
4 changes: 2 additions & 2 deletions lib/utils/cmd-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const aliases = {
v: 'view',
run: 'run-script',
'clean-install': 'ci',
'clean-install-test': 'cit',
'clean-install-test': 'install-ci-test',
x: 'exec',
why: 'explain',
la: 'll',
Expand All @@ -62,7 +62,7 @@ const aliases = {
upgrade: 'update',
udpate: 'update',
rum: 'run-script',
sit: 'cit',
sit: 'install-ci-test',
urn: 'run-script',
ogr: 'org',
'add-user': 'adduser',
Expand Down
40 changes: 40 additions & 0 deletions lib/utils/completion.fish
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# npm completions for Fish shell
# This script is a work in progress and does not fall under the normal semver contract as the rest of npm.

# __fish_npm_needs_command taken from:
# https://stackoverflow.com/questions/16657803/creating-autocomplete-script-with-sub-commands
function __fish_npm_needs_command
set -l cmd (commandline -opc)

if test (count $cmd) -eq 1
return 0
end

return 1
end

# Taken from https://github.com/fish-shell/fish-shell/blob/HEAD/share/completions/npm.fish
function __fish_complete_npm -d "Complete the commandline using npm's 'completion' tool"
# tell npm we are fish shell
set -lx COMP_FISH true
if command -sq npm
# npm completion is bash-centric, so we need to translate fish's "commandline" stuff to bash's $COMP_* stuff
# COMP_LINE is an array with the words in the commandline
set -lx COMP_LINE (commandline -opc)
# COMP_CWORD is the index of the current word in COMP_LINE
# bash starts arrays with 0, so subtract 1
set -lx COMP_CWORD (math (count $COMP_LINE) - 1)
# COMP_POINT is the index of point/cursor when the commandline is viewed as a string
set -lx COMP_POINT (commandline -C)
# If the cursor is after the last word, the empty token will disappear in the expansion
# Readd it
if test (commandline -ct) = ""
set COMP_CWORD (math $COMP_CWORD + 1)
set COMP_LINE $COMP_LINE ""
end
command npm completion -- $COMP_LINE 2>/dev/null
end
end

# flush out what ships with fish
complete -e npm
54 changes: 54 additions & 0 deletions scripts/fish-completion.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/* eslint-disable no-console */
const fs = require('fs/promises')
const { resolve } = require('path')

const { commands, aliases } = require('../lib/utils/cmd-list.js')
const { definitions } = require('../lib/utils/config/index.js')

async function main () {
const file = resolve(__dirname, '..', 'lib', 'utils', 'completion.fish')
console.log(await fs.readFile(file, 'utf-8'))
const cmds = {}
for (const cmd of commands) {
cmds[cmd] = { aliases: [cmd] }
const cmdClass = require(`../lib/commands/${cmd}.js`)
cmds[cmd].description = cmdClass.description
cmds[cmd].params = cmdClass.params
}
for (const alias in aliases) {
cmds[aliases[alias]].aliases.push(alias)
}
for (const cmd in cmds) {
console.log(`# ${cmd}`)
const { aliases: cmdAliases, description, params = [] } = cmds[cmd]
// If npm completion could return all commands in a fish friendly manner
// like we do w/ run-script these wouldn't be needed.
/* eslint-disable-next-line max-len */
console.log(`complete -x -c npm -n __fish_npm_needs_command -a '${cmdAliases.join(' ')}' -d '${description}'`)
const shorts = params.map(p => {
// Our multi-character short params (e.g. -ws) are not very standard and
// don't work with things that assume short params are only ever single
// characters.
if (definitions[p].short?.length === 1) {
return `-s ${definitions[p].short}`
}
}).filter(p => p).join(' ')
// The config descriptions are not appropriate for -d here. We may want to
// consider having a more terse description for these.
// We can also have a mechanism to auto-generate the long form of options
// that have predefined values.
// params completion
/* eslint-disable-next-line max-len */
console.log(`complete -x -c npm -n '__fish_seen_subcommand_from ${cmdAliases.join(' ')}' ${params.map(p => `-l ${p}`).join(' ')} ${shorts}`)
// builtin npm completion
/* eslint-disable-next-line max-len */
console.log(`complete -x -c npm -n '__fish_seen_subcommand_from ${cmdAliases.join(' ')}' -a '(__fish_complete_npm)'`)
}
}

main().then(() => {
return process.exit()
}).catch(err => {
console.error(err)
process.exit(1)
})
8 changes: 4 additions & 4 deletions tap-snapshots/test/lib/docs.js.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ Object {
"c": "config",
"cit": "install-ci-test",
"clean-install": "ci",
"clean-install-test": "cit",
"clean-install-test": "install-ci-test",
"create": "init",
"ddp": "dedupe",
"dist-tags": "dist-tag",
Expand Down Expand Up @@ -421,7 +421,7 @@ Object {
"s": "search",
"se": "search",
"show": "view",
"sit": "cit",
"sit": "install-ci-test",
"t": "test",
"tst": "test",
"udpate": "update",
Expand Down Expand Up @@ -3239,14 +3239,14 @@ Options:
[-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]]
[-ws|--workspaces] [--include-workspace-root] [--install-links]

alias: cit
aliases: cit, clean-install-test, sit

Run "npm help install-ci-test" for more info

\`\`\`bash
npm install-ci-test

alias: cit
aliases: cit, clean-install-test, sit
\`\`\`

#### \`save\`
Expand Down
1 change: 1 addition & 0 deletions test/lib/commands/access.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ t.test('completion', async t => {
])
testComp(['npm', 'access', 'grant'], ['read-only', 'read-write'])
testComp(['npm', 'access', 'revoke'], [])
testComp(['npm', 'access', 'grant', ''], [])

await t.rejects(
access.completion({ conf: { argv: { remain: ['npm', 'access', 'foobar'] } } }),
Expand Down
11 changes: 9 additions & 2 deletions test/lib/commands/run-script.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@ const mockRs = async (t, { windows = false, runScript, ...opts } = {}) => {
}

t.test('completion', async t => {
const completion = async (t, remain, pkg) => {
const completion = async (t, remain, pkg, isFish = false) => {
const { npm } = await mockRs(t,
pkg ? { prefixDir: { 'package.json': JSON.stringify(pkg) } } : {}
)
const cmd = await npm.cmd('run-script')
return cmd.completion({ conf: { argv: { remain } } })
return cmd.completion({ conf: { argv: { remain } }, isFish })
}

t.test('already have a script name', async t => {
Expand All @@ -60,6 +60,13 @@ t.test('completion', async t => {
})
t.strictSame(res, ['hello', 'world'])
})

t.test('fish shell', async t => {
const res = await completion(t, ['npm', 'run'], {
scripts: { hello: 'echo hello', world: 'echo world' },
}, true)
t.strictSame(res, ['hello\techo hello', 'world\techo world'])
})
})

t.test('fail if no package.json', async t => {
Expand Down