From 2d6b6a19265060bee39539ad4db96489de4079c7 Mon Sep 17 00:00:00 2001 From: Jorge Bucaran Date: Sat, 27 Oct 2018 21:37:16 +0900 Subject: [PATCH 1/5] feature: don't add invalid packages to fishfile; fix #484 --- fisher.fish | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/fisher.fish b/fisher.fish index 12fcdcb..0bcd7d7 100644 --- a/fisher.fish +++ b/fisher.fish @@ -189,10 +189,7 @@ function _fisher_commit command touch $fishfile echo "created empty fishfile in $fishfile" | command sed "s|$HOME|~|" >&2 end - printf "%s\n" (_fisher_fishfile_format ( - echo -s $argv\;) ( - echo -s $removed_pkgs\; - ) < $fishfile) > $fishfile + printf "%s\n" (_fisher_fishfile_format (echo -s $argv\;) < $fishfile) > $fishfile set -l expected_pkgs (_fisher_fishfile_read < $fishfile) set -l added_pkgs (_fisher_pkg_fetch_all $expected_pkgs) @@ -377,8 +374,8 @@ function _fisher_fishfile_read command awk -v FS=\# '!/^#/ && NF { print $1 }' end -function _fisher_fishfile_format -a pkgs removed_pkgs - command awk -v PWD=$PWD -v HOME=$HOME -v PKGS="$pkgs" -v REMOVED_PKGS="$removed_pkgs" ' +function _fisher_fishfile_format -a pkgs + command awk -v PWD=$PWD -v HOME=$HOME -v PKGS="$pkgs" ' BEGIN { pkg_count = split(PKGS, pkgs, ";") - 1 cmd = pkgs[1] @@ -404,15 +401,10 @@ function _fisher_fishfile_format -a pkgs removed_pkgs } else if (newln) newln = "\n"(newln > 0 ? "" : newln) } END { - if (cmd == "rm" || pkg_count <= 1) { - split(REMOVED_PKGS, tmp, ";") - for (i in tmp) removed_pkgs[normalize(tmp[i])] = i - for (i in pkg_ids) if (!(pkg_ids[i] in removed_pkgs)) { - print "cannot remove \"" pkg_ids[i] "\" -- package not found" > "/dev/stderr" - } - exit + if (cmd == "rm" || pkg_count <= 1) exit + for (i = 2; i <= pkg_count; i++) { + if (!seen_pkgs[pkg_ids[i - 1]]) print pkgs[i] } - for (i in pkg_ids) if (!seen_pkgs[pkg_ids[i]]) print pkgs[i+1] } function normalize(s) { gsub(/^[ \t]*(https?:\/\/)?(.*github\.com\/)?|[\/ \t]*$/, "", s) From 2934a92cb6eebdbc0f743d43a14408f4722a9cb7 Mon Sep 17 00:00:00 2001 From: Jorge Bucaran Date: Thu, 8 Nov 2018 15:05:25 +0900 Subject: [PATCH 2/5] docs: improve fishfile section --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 3544001..78ec177 100644 --- a/README.md +++ b/README.md @@ -162,7 +162,7 @@ fisher version Whenever you add or remove a package from the command line we'll write to a text file in `~/.config/fish/fishfile`. This is your fishfile. It lists every package that is currently installed on your system. You should add this file to your dotfiles or version control if you want to reproduce your configuration on a different system. -You can edit this file to add or remove packages and then run `fisher` to commit your changes. Only the packages listed in this file will be installed after `fisher` returns. If a package is already installed, it will be updated. Empty lines and anything after a `#` symbol (comments) will be ignored. +You can edit this file to add or remove packages and then run `fisher` to commit your changes. Only the packages listed in this file will be installed after `fisher` returns. If a package is already installed, it will be updated. Empty lines and anything after a `#` symbol will be ignored. ```fish vi ~/.config/fish/fishfile @@ -174,8 +174,6 @@ vi ~/.config/fish/fishfile gitlab.com/jorgebucaran/mermaid edc/bass + FabioAntunes/fish-nvm - -# my local packages ~/path/to/myfish/pkg ``` From 6e5a751e166523a0c2b86ae78f9e59d7913f0ab0 Mon Sep 17 00:00:00 2001 From: Bjorn Neergaard Date: Thu, 22 Nov 2018 07:46:42 -0700 Subject: [PATCH 3/5] Optimize fisher startup path (#494) * With the addition of keybinding support for fish 2.x, fisher greatly increases shell startup time (50-100ms in my testing). This is due to a number of reasons, including using less-than-ideal builtins, unconditionally running mkdir, unconditionally recreating conf.d/fisher.fish, and checking for curl on startup (instead of when curl is used). * This patch cuts fisher-related startup time to roughly 25ms, simplifying the code paths as much as I am able. * The source function is removed as a compromise for speed. Fish 2.1 and greater support `source`, and supporting fish 2.0 is not worth a hefty speed penalty in my opinion. * Remove curl checks, invert version logic. * Update README to reflect fish 2.1+ requirement --- README.md | 2 +- fisher.fish | 59 ++++++++++++++++++++++++++--------------------------- 2 files changed, 30 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 78ec177..afe550c 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ Your shell can take a few seconds before refreshing the function path. If `fishe ### Dependencies -- [fish](https://github.com/fish-shell/fish-shell) 2.0+ (prefer 2.3 or newer) +- [fish](https://github.com/fish-shell/fish-shell) 2.1+ (prefer 2.3 or newer) - [curl](https://github.com/curl/curl) 7.10.3+ - [git](https://github.com/git/git) 1.7.12+ diff --git a/fisher.fish b/fisher.fish index 0bcd7d7..6b875b3 100644 --- a/fisher.fish +++ b/fisher.fish @@ -1,48 +1,38 @@ set -g fisher_version 3.1.1 -type source >/dev/null; or function source; . $argv; end - -switch (command uname) - case Darwin FreeBSD - function _fisher_now -a elapsed - command perl -MTime::HiRes -e 'printf("%.0f\n", (Time::HiRes::time() * 1000) - $ARGV[0])' $elapsed - end - case \* - function _fisher_now -a elapsed - command date "+%s%3N" | command awk -v ELAPSED="$elapsed" '{ sub(/%?3N$/, "000") } $0 -= ELAPSED' - end -end - function fisher -a cmd -d "fish package manager" - if not command which curl >/dev/null - echo "curl is required to use fisher -- install curl and try again" >&2 - return 1 - end - - test -z "$XDG_CACHE_HOME"; and set XDG_CACHE_HOME ~/.cache - test -z "$XDG_CONFIG_HOME"; and set XDG_CONFIG_HOME ~/.config + set -q XDG_CACHE_HOME; or set XDG_CACHE_HOME ~/.cache + set -q XDG_CONFIG_HOME; or set XDG_CONFIG_HOME ~/.config set -g fish_config $XDG_CONFIG_HOME/fish set -g fisher_cache $XDG_CACHE_HOME/fisher set -g fisher_config $XDG_CONFIG_HOME/fisher - test -z "$fisher_path"; and set -g fisher_path $fish_config + set -q fisher_path; or set -g fisher_path $fish_config - command mkdir -p {$fish_config,$fisher_path}/{functions,completions,conf.d} $fisher_cache + for dir in {$fish_config,$fisher_path}/{functions,completions,conf.d} $fisher_cache + if test ! -e $dir + command mkdir -p $dir + end + end if test ! -e "$fisher_path/completions/fisher.fish" echo "fisher self-complete" > $fisher_path/completions/fisher.fish _fisher_self_complete end - if test -e "$fisher_path/conf.d/fisher.fish" - command rm -f $fisher_path/conf.d/fisher.fish - end + if test ! -e "$fisher_path/conf.d/fisher.fish" + switch "$version" + case 2\* \*-\* + echo "fisher copy-user-key-bindings" > $fisher_path/conf.d/fisher.fish + end - switch "$version" - case \*-\* - case 2\* - echo "fisher copy-user-key-bindings" > $fisher_path/conf.d/fisher.fish + else + switch "$version" + case 2\* \*-\* + case \* + command rm -f $fisher_path/conf.d/fisher.fish + end end switch "$cmd" @@ -140,7 +130,7 @@ function _fisher_self_update -a file set -l url "https://raw.githubusercontent.com/jorgebucaran/fisher/master/fisher.fish" echo "fetching $url" >&2 - curl -s "$url?nocache" >$file@ + command curl -s "$url?nocache" >$file@ set -l next_version (awk 'NR == 1 { print $4; exit }' < $file@) switch "$next_version" @@ -442,3 +432,12 @@ function _fisher_wait end end end + +function _fisher_now -a elapsed + switch (command uname) + case Darwin FreeBSD + command perl -MTime::HiRes -e 'printf("%.0f\n", (Time::HiRes::time() * 1000) - $ARGV[0])' $elapsed + case \* + command date "+%s%3N" | command awk -v ELAPSED="$elapsed" '{ sub(/%?3N$/, "000") } $0 -= ELAPSED' + end +end From d65b86487bb1e444f0b9b0401b556338cdf50ca7 Mon Sep 17 00:00:00 2001 From: Sefa Eyeoglu Date: Thu, 22 Nov 2018 15:48:27 +0100 Subject: [PATCH 4/5] Read fishfile from $fisher_path instead of $fish_config (#480) --- fisher.fish | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fisher.fish b/fisher.fish index 6b875b3..f6495e1 100644 --- a/fisher.fish +++ b/fisher.fish @@ -151,7 +151,7 @@ end function _fisher_self_uninstall set -l current_pkgs $fisher_config/*/*/* - for path in $fisher_cache (_fisher_pkg_remove_all $current_pkgs) $fisher_config $fisher_path/{functions,completions,conf.d}/fisher.fish $fish_config/fishfile + for path in $fisher_cache (_fisher_pkg_remove_all $current_pkgs) $fisher_config $fisher_path/{functions,completions,conf.d}/fisher.fish $fisher_path/fishfile echo "removing $path" command rm -rf $path 2>/dev/null end | command sed "s|$HOME|~|" >&2 @@ -174,7 +174,7 @@ function _fisher_commit command rm -rf $fisher_config command mkdir -p $fisher_config - set -l fishfile $fish_config/fishfile + set -l fishfile $fisher_path/fishfile if test ! -e "$fishfile" command touch $fishfile echo "created empty fishfile in $fishfile" | command sed "s|$HOME|~|" >&2 From 39e9f3f34079a1705f4580fa232fe15d10b632c0 Mon Sep 17 00:00:00 2001 From: Jorge Bucaran Date: Tue, 1 Jan 2019 02:49:03 +0900 Subject: [PATCH 5/5] 3.2.0 - Optimize startup path #494 - Cuts fisher-related startup time to roughly 25ms - Reduce legacy fish support (~~>=2.0~~ >=2.1) - Read fishfile from `$fisher_path` instead of `$fish_config` #480, #479 - Don't add invalid packages to fishfile / validate fishfile #478 #484 #492 - Rewrite tar expansion logic / don't use `--strip-components` #489 #496 - Better OpenBSD support - Better msys2 on Windows 10 support - Create symbolic links of original files when adding local packages #502 --- README.md | 22 +-- fisher.fish | 429 ++++++++++++++++++++++++++++------------------------ 2 files changed, 244 insertions(+), 207 deletions(-) diff --git a/README.md b/README.md index afe550c..13b9127 100644 --- a/README.md +++ b/README.md @@ -81,31 +81,35 @@ You can use Fisher to add, update, and remove packages interactively, taking adv ### Adding packages -Install packages using the `add` command followed by the path to the repository on GitHub. +Add packages using the `add` command followed by the path to the repository on GitHub. ``` fisher add jethrokuan/z rafaelrinaldi/pure ``` -A username or organization name is required. To install a package from anywhere else, use the address of the server and the path to the repository. HTTPS is always assumed so you don't need to specify the protocol. +To add a package from anywhere other than GitHub, use the address of the server and the path to the repository. HTTPS is always assumed so you don't need to specify the protocol. ``` fisher add gitlab.com/jorgebucaran/mermaid ``` -To install a package from a tag, branch or a [commit-ish](https://git-scm.com/docs/gitglossary#gitglossary-aiddefcommit-ishacommit-ishalsocommittish), specify the reference following an `@` symbol after the package name. If none is given, we'll use the latest code. +To add a specific version of a package use an `@` symbol after the package name followed by the tag, branch or [commit-ish](https://git-scm.com/docs/gitglossary#gitglossary-aiddefcommit-ishacommit-ishalsocommittish) you want. Only one package version can be installed at any given time. ``` fisher add edc/bass@20f73ef jethrokuan/z@pre27 ``` -You can also install packages from a local directory. Local packages are managed through [symbolic links](https://en.wikipedia.org/wiki/Symbolic_link) so that they can be developed and used at the same time. +To add a package from a private repository set the `fisher_user_api_token` variable to your username followed by a colon and your authorization token or password. -``` -fisher add ~/path/to/myfish/pkg +```fish +set -g fisher_user_api_token jorgebucaran:ce04da9bd93ddb5e729cfff4a58c226322c8d142 ``` -You can only install one package version at a time. If two packages depend on a different version of the same package, the first one that gets installed will take precedence over the other. +You can also add packages from a local directory. Local packages will be copied as [symbolic links](https://en.wikipedia.org/wiki/Symbolic_link) so changes in the original files will be reflected in future shell sessions without the need to run `fisher` again. + +``` +fisher add ~/path/to/local/pkg +``` ### Listing packages @@ -162,7 +166,7 @@ fisher version Whenever you add or remove a package from the command line we'll write to a text file in `~/.config/fish/fishfile`. This is your fishfile. It lists every package that is currently installed on your system. You should add this file to your dotfiles or version control if you want to reproduce your configuration on a different system. -You can edit this file to add or remove packages and then run `fisher` to commit your changes. Only the packages listed in this file will be installed after `fisher` returns. If a package is already installed, it will be updated. Empty lines and anything after a `#` symbol will be ignored. +You can edit this file to add or remove packages and run `fisher` to commit your changes. Only the packages listed in this file will be installed after `fisher` returns. If a package is already installed, it will be updated. Everything after a `#` symbol (comments) will be ignored. ```fish vi ~/.config/fish/fishfile @@ -271,7 +275,7 @@ bind \cg "vi ~/.config/fish/fishfile" set -l name (basename (status -f) .fish){_uninstall} -function $name --on-event $name +function $name --event $name bind -e \cg end ``` diff --git a/fisher.fish b/fisher.fish index f6495e1..4dc6e7d 100644 --- a/fisher.fish +++ b/fisher.fish @@ -1,4 +1,4 @@ -set -g fisher_version 3.1.1 +set -g fisher_version 3.2.0 function fisher -a cmd -d "fish package manager" set -q XDG_CACHE_HOME; or set XDG_CACHE_HOME ~/.cache @@ -10,29 +10,31 @@ function fisher -a cmd -d "fish package manager" set -q fisher_path; or set -g fisher_path $fish_config - for dir in {$fish_config,$fisher_path}/{functions,completions,conf.d} $fisher_cache - if test ! -e $dir - command mkdir -p $dir + for path in {$fish_config,$fisher_path}/{functions,completions,conf.d} $fisher_cache + if test ! -d $path + command mkdir -p $path end end - if test ! -e "$fisher_path/completions/fisher.fish" - echo "fisher self-complete" > $fisher_path/completions/fisher.fish + if test ! -e $fisher_path/completions/fisher.fish + echo "fisher self-complete" >$fisher_path/completions/fisher.fish _fisher_self_complete end - if test ! -e "$fisher_path/conf.d/fisher.fish" + if test -e $fisher_path/conf.d/fisher.fish switch "$version" - case 2\* \*-\* - echo "fisher copy-user-key-bindings" > $fisher_path/conf.d/fisher.fish - end - - else - switch "$version" - case 2\* \*-\* + case \*-\* + command rm -f $fisher_path/conf.d/fisher.fish + case 2\* case \* command rm -f $fisher_path/conf.d/fisher.fish end + else + switch "$version" + case \*-\* + case 2\* + echo "fisher copy-user-key-bindings" >$fisher_path/conf.d/fisher.fish + end end switch "$cmd" @@ -41,28 +43,33 @@ function fisher -a cmd -d "fish package manager" case copy-user-key-bindings _fisher_copy_user_key_bindings case ls - _fisher_ls | command sed "s|$HOME|~|" + _fisher_ls | _fisher_fmt + case self-update + _fisher_self_update (status -f) + case self-uninstall + _fisher_self_uninstall case -v {,--}version _fisher_version (status -f) case -h {,--}help _fisher_help - case self-update - _fisher_self_update (status -f); or return - _fisher_self_complete - case self-uninstall - _fisher_self_uninstall - case add rm "" - if test ! -z "$argv" - if not isatty - while read -l i - set argv $argv $i - end + case "" + _fisher_commit -- $argv + case add rm + if not isatty + while read -l arg + set argv $argv $arg end end - _fisher_commit $argv; or return - _fisher_self_complete + + if test (count $argv) = 1 + echo "invalid number of arguments" >&2 + _fisher_help >&2 + return 1 + end + + _fisher_commit $argv case \* - echo "error: unknown flag or command \"$cmd\"" >&2 + echo "unknown flag or command \"$cmd\"" >&2 _fisher_help >&2 return 1 end @@ -70,13 +77,13 @@ end function _fisher_self_complete complete -c fisher --erase - complete -xc fisher -n __fish_use_subcommand -a version -d "Show version" - complete -xc fisher -n __fish_use_subcommand -a help -d "Show help" - complete -xc fisher -n __fish_use_subcommand -a self-update -d "Update fisher" - complete -xc fisher -n __fish_use_subcommand -a ls -d "List installed packages" - complete -xc fisher -n __fish_use_subcommand -a rm -d "Remove packages" complete -xc fisher -n __fish_use_subcommand -a add -d "Add packages" - for pkg in (_fisher_ls) + complete -xc fisher -n __fish_use_subcommand -a rm -d "Remove packages" + complete -xc fisher -n __fish_use_subcommand -a ls -d "List added packages" + complete -xc fisher -n __fish_use_subcommand -a help -d "Show usage help" + complete -xc fisher -n __fish_use_subcommand -a version -d "$fisher_version" + complete -xc fisher -n __fish_use_subcommand -a self-update -d "Update to the latest version" + for pkg in (_fisher_ls | _fisher_fmt) complete -xc fisher -n "__fish_seen_subcommand_from rm" -a $pkg end end @@ -98,8 +105,8 @@ end function _fisher_ls set -l pkgs $fisher_config/*/*/* for pkg in $pkgs - command readlink $pkg; and continue; or echo $pkg - end | command sed "s|$fisher_config/*||;s|github\.com/||" + command readlink $pkg; or echo $pkg + end end function _fisher_version -a file @@ -107,35 +114,33 @@ function _fisher_version -a file end function _fisher_help - echo "usage: " - echo " fisher add add packages" - echo " fisher rm remove packages" - echo " fisher update installed packages" - echo " fisher ls show installed packages" - echo " fisher help show this help" - echo " fisher version show version" - echo " fisher self-update update fisher" - echo " fisher self-uninstall uninstall fisher" + echo "usage:" + echo " fisher add Add packages" + echo " fisher rm Remove packages" + echo " fisher Update all packages" + echo " fisher ls List added packages" + echo " fisher help Show this help" + echo " fisher version Show the current version" + echo " fisher self-update Update to the latest version" + echo " fisher self-uninstall Uninstall from your system" echo echo "examples:" echo " fisher add jethrokuan/z rafaelrinaldi/pure" - echo " fisher add gitlab.com/owner/foobar@v2" - echo " fisher add ~/path/to/myfish/pkg" + echo " fisher add gitlab.com/foo/bar@v2" + echo " fisher add ~/path/to/local/pkg" echo " fisher rm rafaelrinaldi/pure" - echo " fisher ls | fisher rm" echo " fisher add < bundle" end function _fisher_self_update -a file set -l url "https://raw.githubusercontent.com/jorgebucaran/fisher/master/fisher.fish" echo "fetching $url" >&2 + command curl -s "$url?nocache" >$file. - command curl -s "$url?nocache" >$file@ - - set -l next_version (awk 'NR == 1 { print $4; exit }' < $file@) + set -l next_version (command awk 'NR == 1 { print $4 }' < $file.) switch "$next_version" case "" $fisher_version - command rm -f $file@ + command rm -f $file. if test -z "$next_version" echo "cannot update fisher -- are you offline?" >&2 return 1 @@ -143,17 +148,21 @@ function _fisher_self_update -a file echo "fisher is already up-to-date" >&2 case \* echo "linking $file" | command sed "s|$HOME|~|" >&2 - command mv -f $file@ $file + command mv -f $file. $file source $file - echo "updated fisher to $fisher_version -- hooray!" >&2 + echo "updated to $fisher_version -- hooray!" >&2 + _fisher_self_complete end end function _fisher_self_uninstall - set -l current_pkgs $fisher_config/*/*/* - for path in $fisher_cache (_fisher_pkg_remove_all $current_pkgs) $fisher_config $fisher_path/{functions,completions,conf.d}/fisher.fish $fisher_path/fishfile - echo "removing $path" - command rm -rf $path 2>/dev/null + for pkg in (_fisher_ls) + _fisher_rm $pkg + end + + for file in $fisher_cache $fisher_config $fisher_path/{functions,completions,conf.d}/fisher.fish $fisher_path/fishfile + echo "removing $file" + command rm -Rf $file 2>/dev/null end | command sed "s|$HOME|~|" >&2 set -e fisher_cache @@ -167,69 +176,150 @@ function _fisher_self_uninstall echo "done -- see you again!" >&2 end -function _fisher_commit +function _fisher_commit -a cmd + set -e argv[1] set -l elapsed (_fisher_now) - set -l current_pkgs $fisher_config/*/*/* - set -l removed_pkgs (_fisher_pkg_remove_all $current_pkgs) - command rm -rf $fisher_config - command mkdir -p $fisher_config - set -l fishfile $fisher_path/fishfile + if test ! -e "$fishfile" command touch $fishfile - echo "created empty fishfile in $fishfile" | command sed "s|$HOME|~|" >&2 + echo "created new fishfile in $fishfile" | command sed "s|$HOME|~|" >&2 end - printf "%s\n" (_fisher_fishfile_format (echo -s $argv\;) < $fishfile) > $fishfile - set -l expected_pkgs (_fisher_fishfile_read < $fishfile) - set -l added_pkgs (_fisher_pkg_fetch_all $expected_pkgs) - set -l updated_pkgs ( - for pkg in $removed_pkgs - set pkg (echo $pkg | command sed "s|$fisher_config/*||") - if contains -- $pkg $added_pkgs - echo $pkg - end - end) + set -l rm_pkgs (_fisher_ls | _fisher_fmt) + for pkg in (_fisher_ls) + _fisher_rm $pkg + end + command rm -Rf $fisher_config + command mkdir -p $fisher_config - if test -z "$added_pkgs$updated_pkgs$removed_pkgs$expected_pkgs" + set -l next_pkgs (_fisher_fmt < $fishfile | _fisher_read $cmd (printf "%s\n" $argv | _fisher_fmt)) + set -l new_pkgs (_fisher_fetch $next_pkgs) + set -l old_pkgs + for pkg in $rm_pkgs + if contains -- $pkg $new_pkgs + set old_pkgs $old_pkgs $pkg + end + end + + if test -z "$new_pkgs$old_pkgs$rm_pkgs$next_pkgs" echo "nothing to commit -- try adding some packages" >&2 return 1 end - _fisher_status (count $added_pkgs) (count $updated_pkgs) (count $removed_pkgs) (_fisher_now $elapsed) >&2 -end - -function _fisher_pkg_remove_all - for pkg in $argv - echo $pkg - _fisher_pkg_uninstall $pkg + set -l actual_pkgs + if test "$cmd" = "rm" + set actual_pkgs $next_pkgs + else + for pkg in $next_pkgs + if contains -- (echo $pkg | command sed "s|@.*||") $new_pkgs + set actual_pkgs $actual_pkgs $pkg + end + end end + + _fisher_fmt <$fishfile | _fisher_write $cmd $actual_pkgs >$fishfile. + command mv -f $fishfile. $fishfile + + _fisher_self_complete + + command awk -v N=(count $new_pkgs) -v O=(count $old_pkgs) -v R=(count $rm_pkgs) -v E=(_fisher_now $elapsed) ' + BEGIN { + if (N = N - O) res = msg(res, "added", N) + if (O) res = msg(res, "updated", O) + if (R = R - O) res = msg(res, "removed", R) + printf((res ? res : "done") " in %.2fs\n", E / 1000) + } + function msg(res, str, n) { + return (res ? res ", " : "") str " " n " package" (n > 1 ? "s" : "") + } + ' >&2 end -function _fisher_pkg_fetch_all +function _fisher_fmt + command sed "s|^[ \t]*||;s|^$fisher_config/||;s|^$HOME|~|;s|^\.\/|$PWD/|;s|^github\.com/||;s|^https*://||;s|/*\$||" +end + +function _fisher_read -a cmd + set -e argv[1] + command awk -v FS=\# -v CMD="$cmd" -v ARGS="$argv" ' + BEGIN { + split(ARGS, args, " ") + for (i in args) { + if (!((k = getkey(args[i])) in pkgs)) { + pkgs[k] = args[i] + if (CMD == "add") out[n++] = args[i] + } + } + } + !/^#/ && NF { + if (!file[k = getkey($1)]++ && !(k in pkgs)) out[n++] = $1 + } + END { + for (i = 0; i < n; i++) print out[i] + if (CMD == "rm") { + for (pkg in pkgs) { + if (!(pkg in file)) { + print "cannot remove \"" pkg "\" -- package not found" > "/dev/stderr" + } + } + } + } + function getkey(s) { + return (split(s, a, /@+|:/) > 2) ? a[2]"/"a[1]"/"a[3] : a[1] + } + ' +end + +function _fisher_write -a cmd + set -e argv[1] + command awk -v CMD="$cmd" -v ARGS="$argv" ' + BEGIN { + split(ARGS, args, " ") + for (i in args) pkgs[getkey(args[i])] = args[i] + } + { + if (/^#/ || !NF) print $0 + else { + k = getkey($0) + if (out = pkgs[k] != 0 ? pkgs[k] : CMD != "rm" ? $0 : "") print out + pkgs[k] = 0 + } + } + END { + for (k in pkgs) if (pkgs[k]) print pkgs[k] + } + function getkey(s) { + return (split(s, a, /@+|:/) > 2) ? a[2]"/"a[1]"/"a[3] : a[1] + } + ' +end + +function _fisher_fetch set -l pkg_jobs + set -l next_pkgs set -l local_pkgs set -l actual_pkgs - set -l expected_pkgs + set -q fisher_user_api_token; and set -l user_info -u $fisher_user_api_token - for id in $argv - switch $id + for i in $argv + switch $i case \~\* /\* - set -l path (echo "$id" | command sed "s|~|$HOME|") + set -l path (echo "$i" | command sed "s|~|$HOME|") if test -e "$path" set local_pkgs $local_pkgs $path else - echo "cannot install \"$id\" -- is this a valid file?" >&2 + echo "cannot add \"$i\" -- is this a valid file?" >&2 end continue end - command awk -v ID=$id -v FS=/ 'BEGIN { - if (split(ID, tmp, /@+|:/) > 2) { - if (tmp[4]) sub("@"tmp[4], "", ID) - print ID "\t" tmp[2]"/"tmp[1]"/"tmp[3] "\t" (tmp[4] ? tmp[4] : "master") + command awk -v NAME=$i -v FS=/ 'BEGIN { + if (split(NAME, tmp, /@+|:/) > 2) { + if (tmp[4]) sub("@"tmp[4], "", NAME) + print NAME "\t" tmp[2]"/"tmp[1]"/"tmp[3] "\t" (tmp[4] ? tmp[4] : "master") } else { - pkg = split(ID, _, "/") <= 2 ? "github.com/"tmp[1] : tmp[1] + pkg = split(NAME, _, "/") <= 2 ? "github.com/"tmp[1] : tmp[1] tag = tmp[2] ? tmp[2] : "master" print (\ pkg ~ /^github/ ? "https://codeload."pkg"/tar.gz/"tag : \ @@ -237,74 +327,74 @@ function _fisher_pkg_fetch_all pkg ~ /^bitbucket/ ? "https://"pkg"/get/"tag".tar.gz" : pkg \ ) "\t" pkg } - }' | read -l url pkg tag + }' | read -l url pkg branch if test ! -d "$fisher_config/$pkg" fish -c " echo fetching $url >&2 - command mkdir -p \"$fisher_config/$pkg\" - if test ! -z \"$tag\" - command git clone $url \"$fisher_config/$pkg\" --branch $tag --depth 1 2>/dev/null - or echo cannot clone \"$url\" -- is this a valid url\? >&2 - else if command curl -Ss $url 2>&1 | command tar -xzf- -C \"$fisher_config/$pkg\" --strip-components=1 2>/dev/null - command mkdir -p \"$fisher_cache/$pkg\" - command cp -Rf \"$fisher_config/$pkg\" \"$fisher_cache/$pkg/..\" + command mkdir -p {$fisher_config,$fisher_cache}/$pkg + if test ! -z \"$branch\" + command git clone $url $fisher_config/$pkg --branch $branch --depth 1 2>/dev/null + or echo cannot clone \"$url\" -- is this a valid url\? >&2 + else if command curl $user_info -Ss $url 2>&1 | command tar -xzf- -C $fisher_config/$pkg 2>/dev/null + command rm -Rf $fisher_cache/$pkg + command mv -f $fisher_config/$pkg/* $fisher_cache/$pkg + command rm -Rf $fisher_config/$pkg + command cp -Rf {$fisher_cache,$fisher_config}/$pkg else if test -d \"$fisher_cache/$pkg\" echo cannot connect to server -- searching in \"$fisher_cache/$pkg\" | command sed 's|$HOME|~|' >&2 - command cp -Rf \"$fisher_cache/$pkg\" \"$fisher_config/$pkg/..\" + command cp -Rf $fisher_cache/$pkg $fisher_config/$pkg/.. else - command rm -rf \"$fisher_config/$pkg\" - echo cannot install \"$pkg\" -- is this a valid package\? >&2 + command rm -Rf $fisher_config/$pkg + echo cannot add \"$pkg\" -- is this a valid package\? >&2 end " >/dev/null & set pkg_jobs $pkg_jobs (_fisher_jobs --last) - set expected_pkgs $expected_pkgs "$pkg" + set next_pkgs $next_pkgs "$fisher_config/$pkg" end end if test ! -z "$pkg_jobs" _fisher_wait $pkg_jobs - for pkg in $expected_pkgs - if test -d "$fisher_config/$pkg" + for pkg in $next_pkgs + if test -d "$pkg" set actual_pkgs $actual_pkgs $pkg - _fisher_pkg_install $fisher_config/$pkg + _fisher_add $pkg end end end - for pkg in $local_pkgs - set -l path local/$USER - set -l name (command basename $pkg) - command mkdir -p $fisher_config/$path - command ln -sf $pkg $fisher_config/$path - set actual_pkgs $actual_pkgs $path/$name - _fisher_pkg_install $fisher_config/$path/$name + set -l local_path $fisher_config/local/$USER + for src in $local_pkgs + command mkdir -p $local_path + command ln -sf $src $local_path/(command basename $src) + set actual_pkgs $actual_pkgs $src + _fisher_add $src --link end if test ! -z "$actual_pkgs" - _fisher_pkg_fetch_all (_fisher_pkg_get_deps $actual_pkgs | command sort --unique) - printf "%s\n" $actual_pkgs + _fisher_fetch (_fisher_deps $actual_pkgs | command awk '!seen[$0]++') + printf "%s\n" $actual_pkgs | _fisher_fmt end end -function _fisher_pkg_get_deps +function _fisher_deps for pkg in $argv - set -l path $fisher_config/$pkg - if test ! -d "$path" + if test ! -d "$pkg" echo $pkg - else if test -s "$path/fishfile" - _fisher_pkg_get_deps (_fisher_fishfile_format < $path/fishfile | _fisher_fishfile_read) + else if test -s "$pkg/fishfile" + _fisher_deps (_fisher_fmt < $pkg/fishfile | _fisher_read) end end end -function _fisher_pkg_install -a pkg +function _fisher_add -a pkg opts set -l name (command basename $pkg) set -l files $pkg/{functions,completions,conf.d}/**.* $pkg/*.fish - for source in $files - set -l target (command basename $source) - switch $source + for src in $files + set -l target (command basename $src) + switch $src case $pkg/conf.d\* set target $fisher_path/conf.d/$target case $pkg/completions\* @@ -320,7 +410,11 @@ function _fisher_pkg_install -a pkg end end echo "linking $target" | command sed "s|$HOME|~|" >&2 - command cp -f $source $target + if test -z "$opts" + command cp -f $src $target + else + command ln -sf $src $target + end switch $target case \*.fish source $target >/dev/null 2>/dev/null @@ -328,13 +422,13 @@ function _fisher_pkg_install -a pkg end end -function _fisher_pkg_uninstall -a pkg +function _fisher_rm -a pkg set -l name (command basename $pkg) set -l files $pkg/{conf.d,completions,functions}/**.* $pkg/*.fish - for source in $files - set -l target (command basename $source) + for src in $files + set -l target (command basename $src) set -l filename (command basename $target .fish) - switch $source + switch $src case $pkg/conf.d\* test "$filename.fish" = "$target"; and emit "$filename"_uninstall set target conf.d/$target @@ -345,7 +439,7 @@ function _fisher_pkg_uninstall -a pkg test "$filename.fish" = "$target"; and functions -e $filename switch $target case uninstall.fish - source $source + source $src continue case init.fish key_bindings.fish set target conf.d/$name\_$target @@ -360,75 +454,14 @@ function _fisher_pkg_uninstall -a pkg end end -function _fisher_fishfile_read - command awk -v FS=\# '!/^#/ && NF { print $1 }' -end - -function _fisher_fishfile_format -a pkgs - command awk -v PWD=$PWD -v HOME=$HOME -v PKGS="$pkgs" ' - BEGIN { - pkg_count = split(PKGS, pkgs, ";") - 1 - cmd = pkgs[1] - for (i = 2; i <= pkg_count; i++) { - pkg_ids[i - 1] = get_pkg_id( pkgs[i] = normalize(pkgs[i]) ) - } - } { - if (NF) { - $0 = normalize($0) - newln = newln > 0 ? "" : newln - if (/^#/) print newln$0 - else if (!seen_pkgs[(pkg_id = get_pkg_id($0))]++) { - for (i = 1; i < pkg_count; i++) { - if (pkg_ids[i] == pkg_id) { - if (cmd == "rm") next - $0 = pkgs[i + 1] - break - } - } - print newln$0 - } - newln = NF - } else if (newln) newln = "\n"(newln > 0 ? "" : newln) - } - END { - if (cmd == "rm" || pkg_count <= 1) exit - for (i = 2; i <= pkg_count; i++) { - if (!seen_pkgs[pkg_ids[i - 1]]) print pkgs[i] - } - } - function normalize(s) { - gsub(/^[ \t]*(https?:\/\/)?(.*github\.com\/)?|[\/ \t]*$/, "", s) - sub(/^\.\//, PWD"/", s) - sub(HOME, "~", s) - return s - } - function get_pkg_id(s) { - return (split(s, tmp, /@+|:/) > 2) ? tmp[2]"/"tmp[1]"/"tmp[3] : tmp[1] - } - ' -end - -function _fisher_status -a added updated removed elapsed - command awk -v ADDED=$added -v UPDATED=$updated -v REMOVED=$removed -v ELAPSED=$elapsed ' - BEGIN { - if (ADDED = ADDED - UPDATED) res = msg(res, "added", ADDED) - if (UPDATED) res = msg(res, "updated", UPDATED) - if (REMOVED = REMOVED - UPDATED) res = msg(res, "removed", REMOVED) - printf((res ? res : "done") " in %.2fs\n", ELAPSED / 1000) - } - function msg(res, str, n) { - return (res ? res ", " : "") str " " n " package" (n > 1 ? "s" : "") - } - ' -end - function _fisher_jobs - jobs $argv | command awk '/[0-9]+\t/ { print $1 }' + builtin jobs $argv | command awk '/[0-9]+\t/ { print $1 }' end function _fisher_wait while for job in $argv - contains -- $job (_fisher_jobs); and break + contains -- $job (_fisher_jobs) + and break end end end