From afcfb4da0c54751a93bf1f591f214036dc2c5827 Mon Sep 17 00:00:00 2001 From: Branden Cash Date: Tue, 28 Jan 2020 16:04:45 -0700 Subject: [PATCH 1/3] ensure ls is not colorizing output --- zsh-better-npm-completion.plugin.zsh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zsh-better-npm-completion.plugin.zsh b/zsh-better-npm-completion.plugin.zsh index 38a8d68..6ef6d1a 100644 --- a/zsh-better-npm-completion.plugin.zsh +++ b/zsh-better-npm-completion.plugin.zsh @@ -11,7 +11,7 @@ _zbnc_no_of_npm_args() { } _zbnc_list_cached_modules() { - ls ~/.npm 2>/dev/null + ls --color=never ~/.npm 2>/dev/null } _zbnc_recursively_look_for() { From 4dc5120d50f3650b5260eebb41cfaa6a2b54bf20 Mon Sep 17 00:00:00 2001 From: Branden Cash Date: Tue, 28 Jan 2020 20:26:26 -0700 Subject: [PATCH 2/3] support the new(-ish) cache mechanism in npm which uses cacache --- README.md | 33 ++++++++++++++++++ zsh-better-npm-completion.plugin.zsh | 51 +++++++++++++++++++++++++++- 2 files changed, 83 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8943f91..438c973 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,39 @@ Then source it in your `.zshrc` source ~/.zsh-better-npm-completion/zsh-better-npm-completion.plugin.zsh ``` +## Module Install Cache + +When running `npm install `, it will look in your local npm cache directory for package suggestions. +However building this list is still relatively slow. This completion makes use of the zsh completion +caching mechanism to cache the module list if you have caching enabled. + +Caching can be enabled for all completions like this: + +```zsh +zstyle ':completion:*' use-cache on +``` + +Or specifically for the npm completion like this: + +```zsh +zstyle ':completion::complete:npm::' use-cache on +``` + +By default the cache will be valid for 1 hour. But can be modified by setting a cache policy like this: + +```zsh +_npm_install_cache_policy() { + # rebuild if cache is more than 24 hours old + local -a oldp + # See http://zsh.sourceforge.net/Doc/Release/Expansion.html#Glob-Qualifiers + # Nmh+24 ... N == NULL_GLOB, m == modified time, h == hour, +24 == +24 units (i.e. [M]onth, [w]weeks, [h]ours, [m]inutes, [s]econds) + oldp=( "$1"(Nmh+24) ) + (( $#oldp )) +} +zstyle ':completion::complete:npm::' cache-policy _npm_install_cache_policy +``` + + ## Related - [`zsh-nvm`](https://github.com/lukechilds/zsh-nvm) - Zsh plugin for installing, updating and loading `nvm` diff --git a/zsh-better-npm-completion.plugin.zsh b/zsh-better-npm-completion.plugin.zsh index 6ef6d1a..2c89b28 100644 --- a/zsh-better-npm-completion.plugin.zsh +++ b/zsh-better-npm-completion.plugin.zsh @@ -11,7 +11,56 @@ _zbnc_no_of_npm_args() { } _zbnc_list_cached_modules() { - ls --color=never ~/.npm 2>/dev/null + local update_policy + zstyle -s ":completion:${curcontext}:" cache-policy update_policy + if [[ -z "$update_policy" ]]; then + zstyle ":completion:${curcontext}:" cache-policy _zbnc_list_cached_modules_policy + fi + + if _cache_invalid zbnc_cached_modules || ! _retrieve_cache zbnc_cached_modules; then + _modules=$(_zbnc_list_cached_modules_no_cache) + + if [ $? -eq 0 ]; then + _store_cache zbnc_cached_modules _modules + else + # some error occurred, the user is probably not logged in + # set _modules to an empty string so that no completion is attempted + _modules="" + fi + else + _retrieve_cache zbnc_cached_modules + fi + echo $_modules +} + +_zbnc_list_cached_modules_policy() { + # rebuild if cache is more than an hour old + local -a oldp + # See http://zsh.sourceforge.net/Doc/Release/Expansion.html#Glob-Qualifiers + oldp=( "$1"(Nmh+1) ) + (( $#oldp )) +} + +_zbnc_list_cached_modules_no_cache() { + local cache_dir="$(npm config get cache)/_cacache" + export NODE_PATH="${NODE_PATH}:$(npm prefix -g)/lib/node_modules" + node --eval="require('cacache');" &>/dev/null || npm install -g cacache &>/dev/null + if [ -d "${cache_dir}" ]; then + node </dev/null +const cacache = require('cacache'); +cacache.ls('${cache_dir}').then(cache => { + const packages = Object.values(cache).forEach(entry => { + const id = ((entry || {}).metadata || {}).id; + if (id) { + console.log(id.substr(0, id.lastIndexOf('@'))); + } + }); +}); +CACHE_LS + else + # Fallback to older cache location ... i think node < 10 + ls --color=never ~/.npm 2>/dev/null + fi } _zbnc_recursively_look_for() { From f30bb60743b0191abc4e78c487f8fbde9ad3cf2e Mon Sep 17 00:00:00 2001 From: Branden Cash Date: Wed, 29 Jan 2020 23:20:45 -0700 Subject: [PATCH 3/3] updates to perform npm search to find packages and include local files in the results --- README.md | 14 ++--- zsh-better-npm-completion.plugin.zsh | 77 ++++++++++++++++++++++------ 2 files changed, 70 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 438c973..e366b65 100644 --- a/README.md +++ b/README.md @@ -61,17 +61,19 @@ source ~/.zsh-better-npm-completion/zsh-better-npm-completion.plugin.zsh ## Module Install Cache -When running `npm install `, it will look in your local npm cache directory for package suggestions. -However building this list is still relatively slow. This completion makes use of the zsh completion -caching mechanism to cache the module list if you have caching enabled. +When running `npm install rea`, it will perform an `npm search` for matching packages. In addition +it will look in your local npm cache directory for package suggestions. However building this list +can be pretty slow. This completion makes use of the zsh completion caching mechanism to cache the +module list if you have caching enabled. -Caching can be enabled for all completions like this: +We try to enable it by default, however if you have something +like below in your zshrc forces cache for all completions to be turned off. ```zsh -zstyle ':completion:*' use-cache on +zstyle ':completion:*' use-cache off ``` -Or specifically for the npm completion like this: +To specifically turn npm cache back on, you may add the following: ```zsh zstyle ':completion::complete:npm::' use-cache on diff --git a/zsh-better-npm-completion.plugin.zsh b/zsh-better-npm-completion.plugin.zsh index 2c89b28..fa06a2f 100644 --- a/zsh-better-npm-completion.plugin.zsh +++ b/zsh-better-npm-completion.plugin.zsh @@ -11,24 +11,46 @@ _zbnc_no_of_npm_args() { } _zbnc_list_cached_modules() { + local term="${words[$CURRENT]}" + + case $term in + '.'* | '/'* | '~'* | '-'* | '_'*) + return + esac + + # enable cache if the user hasn't explicitly set it + local use_cache + zstyle -s ":completion:${curcontext}:" use-cache use_cache + if [[ -z "$use_cache" ]]; then + zstyle ":completion:${curcontext}:" use-cache on + fi + + # set default cache policy if the user hasn't set it local update_policy zstyle -s ":completion:${curcontext}:" cache-policy update_policy if [[ -z "$update_policy" ]]; then zstyle ":completion:${curcontext}:" cache-policy _zbnc_list_cached_modules_policy fi - if _cache_invalid zbnc_cached_modules || ! _retrieve_cache zbnc_cached_modules; then - _modules=$(_zbnc_list_cached_modules_no_cache) + local hash=$(echo "$term" | md5) + cache_name="zbnc_cached_modules_$hash" + + if _cache_invalid $cache_name || ! _retrieve_cache $cache_name; then + if [[ -z "$term" ]]; then + _modules=$(_zbnc_list_cached_modules_no_cache) + else + _modules=$(_zbnc_list_search_modules) + fi if [ $? -eq 0 ]; then - _store_cache zbnc_cached_modules _modules + _store_cache $cache_name _modules else # some error occurred, the user is probably not logged in # set _modules to an empty string so that no completion is attempted _modules="" fi else - _retrieve_cache zbnc_cached_modules + _retrieve_cache $cache_name fi echo $_modules } @@ -41,9 +63,15 @@ _zbnc_list_cached_modules_policy() { (( $#oldp )) } +_zbnc_list_search_modules() { + local term="${words[$CURRENT]}" + [[ ! -z "$term" ]] && NPM_CONFIG_SEARCHLIMIT=1000 npm search --no-description --parseable "$term" 2>/dev/null | awk '{print $1}' + _zbnc_list_cached_modules_no_cache +} + _zbnc_list_cached_modules_no_cache() { local cache_dir="$(npm config get cache)/_cacache" - export NODE_PATH="${NODE_PATH}:$(npm prefix -g)/lib/node_modules" + export NODE_PATH="${NODE_PATH}:$(npm prefix -g)/lib/node_modules:$(npm prefix -g)/lib/node_modules/npm/node_modules" node --eval="require('cacache');" &>/dev/null || npm install -g cacache &>/dev/null if [ -d "${cache_dir}" ]; then node </dev/null @@ -105,13 +133,15 @@ _zbnc_parse_package_json_for_deps() { _zbnc_npm_install_completion() { # Only run on `npm install ?` - [[ ! "$(_zbnc_no_of_npm_args)" = "3" ]] && return + [[ $(_zbnc_no_of_npm_args) -lt 3 ]] && return - # Return if we don't have any cached modules - [[ "$(_zbnc_list_cached_modules)" = "" ]] && return + local modules=($(_zbnc_list_cached_modules)) + + # Add modules if we found some + [[ ${#modules[@]} -gt 0 ]] && _values $modules - # If we do, recommend them - _values $(_zbnc_list_cached_modules) + # Include local files + _files # Make sure we don't run default completion custom_completion=true @@ -168,17 +198,34 @@ _zbnc_default_npm_completion() { } _zbnc_zsh_better_npm_completion() { - # Store custom completion status local custom_completion=false # Load custom completion commands case "$(_zbnc_npm_command)" in - i|install) - _zbnc_npm_install_completion + i|install|add) + _arguments -n -C \ + '(-g --global)'{-g,--global}'[Package will be installed as a global package.]' \ + '(-P --save-prod -D --save-dev -O --save-optional --no-save)'{-P,--save-prod}'[Package will appear in your dependencies. This is the default unless -D or -O are present.]' \ + '(-D --save-dev -P --save-prod -O --save-optional --no-save)'{-D,--save-dev}'[Package will appear in your devDependencies.]' \ + '(-O --save-optional -P --save-prod -D --save-dev --no-save)'{-O,--save-optional}'[Package will appear in your optionalDependencies.]' \ + '(--no-save -P --save-prod -D --save-dev -O --save-optional -E --save-exact -B --save-bundle)--no-save[Prevents saving to dependencies.]' \ + '(-E --save-exact --no-save)'{-E,--save-exact}'[Saved dependencies will be configured with an exact version rather than using npm’s default semver range operator.]' \ + '(-B --save-bundle --no-save)'{-B,--save-bundle}'[Saved dependencies will also be added to your bundleDependencies list.]' \ + '(- *)--help[show help message.]' \ + "(- *)--version[show program's version number and exit.]" \ + '*:args:_zbnc_npm_install_completion' && return 0 ;; - r|uninstall) - _zbnc_npm_uninstall_completion + r|uninstall|remove|rm|un|unlink) + _arguments -n -C \ + '(-g --global)'{-g,--global}'[Package will be removed from global packages.]' \ + '(-P --save-prod -D --save-dev -O --save-optional --no-save)'{-P,--save-prod}'[Package will be removed from your dependencies.]' \ + '(-D --save-dev -P --save-prod -O --save-optional --no-save)'{-D,--save-dev}'[Package will be removed from your devDependencies.]' \ + '(-O --save-optional -P --save-prod -D --save-dev --no-save)'{-O,--save-optional}'[Package will be removed from your optionalDependencies.]' \ + '(--no-save -P --save-prod -D --save-dev -O --save-optional)--no-save[Package will not be removed from your package.json file.]' \ + '(- *)--help[show help message.]' \ + "(- *)--version[show program's version number and exit.]" \ + '*:args:_zbnc_npm_uninstall_completion' && return 0 ;; run) _zbnc_npm_run_completion