Added file/url completion for "install" command. #2612

Closed
wants to merge 4 commits into
from
View
77 lib/install.js
@@ -25,16 +25,74 @@ install.usage = "npm install <tarball file>"
+ "\npresent, installs dependencies specified in the shrinkwrap."
+ "\nOtherwise, installs dependencies from ./package.json."
+var npm = require("./npm.js")
+ , semver = require("semver")
+ , readJson = require("read-package-json")
+ , log = require("npmlog")
+ , path = require("path")
+ , fs = require("graceful-fs")
+ , cache = require("./cache.js")
+ , asyncMap = require("slide").asyncMap
+ , chain = require("slide").chain
+ , output
+ , url = require("url")
+ , mkdir = require("mkdirp")
+ , lifecycle = require("./utils/lifecycle.js")
+ , archy = require("archy")
+
install.completion = function (opts, cb) {
// install can complete to a folder with a package.json, or any package.
// if it has a slash, then it's gotta be a folder
// if it starts with https?://, then just give up, because it's a url
- // for now, not yet implemented.
+ if (/^https?:\/\//.test(opts.partialWord)) {
+ // do not complete to URLs
+ return cb(null, [])
+ }
+
+ if (/\//.test(opts.partialWord)) {
+ // Complete fully to folder if there is exactly one match and it
+ // is a folder containing a package.json file. If that is not the
+ // case we return 0 matches, which will trigger the default bash
+ // complete.
+ var lastSlashIdx = opts.partialWord.lastIndexOf("/")
+ var partialName = opts.partialWord.slice(lastSlashIdx + 1)
+ var partialPath = opts.partialWord.slice(0, lastSlashIdx)
+ if (partialPath === "")
+ partialPath = "/"
+ function annotatePackageDirMatch (sibling, cb) {
+ var fullPath = path.join(partialPath, sibling)
+ if (sibling.slice(0, partialName.length) !== partialName)
+ return cb(null, null) // not name match
+ fs.readdir(fullPath, function (err, contents) {
+ if (err) return cb(null, { isPackage: false })
+ cb(null, { fullPath: fullPath
+ , isPackage: contents.indexOf("package.json") !== -1})
+ })
+ }
+ fs.readdir(partialPath, function (err, siblings) {
+ if (err) return cb(null, []) // invalid dir: no matching
+ asyncMap( siblings
+ , annotatePackageDirMatch
+ , function (err, matches) {
+ if (err) return cb(err)
+ var cleaned = matches.filter(function (x) { return x !== null })
+ if (cleaned.length !== 1)
+ return cb(null, [])
+ if (!cleaned[0].isPackage)
+ return cb(null, [])
+ // Success - only one match and it is a package dir
+ return cb(null, [cleaned[0].fullPath])
+ })
+ })
+ return
+ }
+
+ // complete to registry
var registry = npm.registry
registry.get("/-/short", function (er, pkgs) {
if (er) return cb()
if (!opts.partialWord) return cb(null, pkgs)
-
+
var name = opts.partialWord.split("@").shift()
pkgs = pkgs.filter(function (p) {
return p.indexOf(name) === 0
@@ -55,21 +113,6 @@ install.completion = function (opts, cb) {
})
}
-var npm = require("./npm.js")
- , semver = require("semver")
- , readJson = require("read-package-json")
- , log = require("npmlog")
- , path = require("path")
- , fs = require("graceful-fs")
- , cache = require("./cache.js")
- , asyncMap = require("slide").asyncMap
- , chain = require("slide").chain
- , output
- , url = require("url")
- , mkdir = require("mkdirp")
- , lifecycle = require("./utils/lifecycle.js")
- , archy = require("archy")
-
function install (args, cb_) {
function cb (er, installed) {
View
2 lib/utils/completion.sh
@@ -21,7 +21,7 @@ if type complete &>/dev/null; then
2>/dev/null)) || return $?
IFS="$si"
}
- complete -F _npm_completion npm
+ complete -o default -F _npm_completion npm
elif type compdef &>/dev/null; then
_npm_completion() {
si=$IFS