Fisher 4.0 (#596)

- Introduce new event system. #526, #527 #573.
- Deprecate `init.fish`, `uninstall.fish`, etc. #581
- No cache fallback, no plugin dependencies, no more private
package hosts, and no more gitlab/bitbucket support. #464, #579
- Require fish 3.0, use newer fish features, e.g., use `wait` to
implement concurrent downloads.
- Rely less on external tools. No awk, no sed, no basename/dirname.
Just mv, rm, cp, and mkdir.
- Deprecate `fishfile` in favor of `fish_plugins`. This new file
works like the old fishfile, but without comment support. See #524.
pull/597/head 4.0.0
Jorge Bucaran 4 years ago committed by GitHub
parent c2c091f33e
commit dbf07b1709
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -12,5 +12,5 @@ jobs:
sudo apt-get update
sudo apt-get -y install fish
- run: |
curl https://git.io/fisher --create-dirs -sLo ~/.config/fish/functions/fisher.fish
fish -c "fisher -v && fisher -h"
curl -sL git.io/fisher | source && fisher install jorgebucaran/fisher
fisher -v && fisher -h

@ -2,256 +2,135 @@
> A plugin manager for [fish](https://fishshell.com)—the friendly interactive shell.
Manage functions, completions, bindings, and snippets from the command line. Extend your shell capabilities, change the look of your prompt and create repeatable configurations across different systems effortlessly.
Now you can manage functions, completions, bindings, and snippets from the command line. Extend your shell capabilities, change the look of your prompt and create repeatable configurations across different systems effortlessly.
- Oh My Fish! plugin support.
- Build and distribute shell scripts in a portable way.
- Insanely fast concurrent plugin downloads with built-in cache fallback.
- Zero configuration out of the box. Need to tweak a thing? You can do that too.
- Blazingly fast concurrent plugin downloads.
- 100% pure fish—easy to contribute to or modify.
- Zero configuration out of the box. Need to tweak a thing? [You can do that too](#using-your-fish_plugins-file).
Fishing for plugins? Browse [git.io/awesome.fish](https://git.io/awesome.fish) or [search](https://github.com/topics/fish-packages) [on](https://github.com/topics/fish-plugins) [GitHub](https://github.com/topics/fish-plugin).
Fishing for plugins? Browse [git.io/awesome.fish](https://git.io/awesome.fish) or [search](https://github.com/topics/fish-plugins) [on](https://github.com/topics/fish-package) [GitHub](https://github.com/topics/fish-plugin).
## Installation
Just drop [`fisher.fish`](fisher.fish) on any directory in your function path and you are done.
```console
curl https://git.io/fisher --create-dirs -sLo ~/.config/fish/functions/fisher.fish
curl -sL git.io/fisher | source && fisher install jorgebucaran/fisher
```
## Quickstart
You've found an interesting utility you'd like to try out. Or maybe you've [created a new plugin](#creating-your-own-plugin). How do you install it? How to update or remove it?
You can use Fisher to add, update, and remove plugins interactively, taking advantage of fish [tab completion](https://fishshell.com/docs/current/index.html#completion) and syntax highlighting. Or [edit your fishfile](#using-the-fishfile) and commit your changes. Do you prefer a CLI-centered approach, text-based approach, or a mix of both?
You can install, update, and remove plugins interactively with Fisher, taking advantage of fish [tab completion](https://fishshell.com/docs/current/index.html#completion) and rich syntax highlighting.
### Adding plugins
### Installing plugins
Add plugins using the `add` command followed by the path to the repository on GitHub.
Install plugins using the `install` command followed by the path to the repository on GitHub.
```console
fisher add jethrokuan/z rafaelrinaldi/pure
fisher install ilancosman/tide
```
To add a plugin from a different location, 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 get a specific version of a plugin add an `@` symbol after the plugin name followed by a tag, branch, or [commit](https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddefcommit-ishacommit-ishalsocommittish).
```console
fisher add gitlab.com/big/fish
```
To add a plugin from a private repository set the `fisher_user_api_token` variable to your username followed by a colon and your authorization token or password.
```fish
set -g fisher_user_api_token jorgebucaran:ce04da9bd93ddb5e729cfff4a58c226322c8d142
fisher install jorgebucaran/nvm.fish@1.1.0
```
For a specific version of a plugin add an `@` symbol after the plugin name followed by the tag, branch or [commit-ish](https://git-scm.com/docs/gitglossary#gitglossary-aiddefcommit-ishacommit-ishalsocommittish). Only one plugin version can be installed at any given time.
You can install plugins from a local directory too.
```console
fisher add edc/bass@20f73ef jethrokuan/z@pre27
fisher install ~/path/to/plugin
```
You can add plugins from a local directory too. Local plugins will be installed as [symbolic links](https://en.wikipedia.org/wiki/Symbolic_link) so changes in the original files will be reflected in new shell sessions without having to re-run `fisher`.
```console
fisher add ~/path/to/local/plugin
```
> Fisher expands plugins into your fish configuration directory by default, overwriting existing files. If you wish to change this behavior, set `$fisher_path` to your preferred location and put it in your function path.
### Listing plugins
List all the plugins that are currently installed using the `ls` command. This doesn't show plugin dependencies.
List all the plugins that are currently installed using the `list` command.
```console
fisher ls
jethrokuan/z
rafaelrinaldi/pure
gitlab.com/big/fish
edc/bass
~/path/to/myfish/plugin
$ fisher list
jorgebucaran/fisher
ilancosman/tide
jorgebucaran/nvm.fish@1.1.0
/home/jb/path/to/plugin
```
You can use a regular expression after `ls` to refine the output.
> `jorgebucaran/fisher` is listed because we installed it to start with!
```
fisher ls "^gitlab|fish-.*"
```
### Removing plugins
Remove plugins using the `rm` command. If a plugin has dependencies, they too will be removed. If any dependencies are still shared by other plugins, they will remain installed.
The `list` command also accepts a regular expression to filter the output.
```console
fisher rm rafaelrinaldi/pure
```
You can remove everything that is currently installed in one sweep using the following pipeline.
```console
fisher ls | fisher rm
$ fisher list \^/
/home/jb/path/to/plugin
```
### Updating plugins
Run `fisher` to update everything currently installed. There is no dedicated update command. Using the command line to add and remove plugins is the easiest way to modify and commit changes to your fishfile.
Looking for a way to self update Fisher? Use the `self-update` command.
The `update` command updates one or more plugins to their latest version.
```console
fisher self-update
```
### Other commands
Display usage help on the command line.
```console
fisher --help
```
Display the current version of Fisher.
```console
fisher --version
```
Remove installed plugins, cache, your fishfile, and Fisher.
```console
fisher self-uninstall
```
## Changing the installation path
Fisher expands plugins into your fish configuration directory by default, overwriting existing files. To change this behavior, set `$fisher_path` to your preferred location and put it in your function path.
```fish
set -g fisher_path /path/to/another/location
set -p fish_function_path fish_function_path[1] $fisher_path/functions
set -p fish_complete_path fish_complete_path[1] $fisher_path/completions
for file in $fisher_path/conf.d/*.fish
builtin source $file 2>/dev/null
end
fisher update ilancosman/tide
```
## Using the fishfile
Whenever you add or remove a plugin from the command-line, Fisher writes the exact list of installed plugins to `~/.config/fish/fishfile`. This is your fishfile. Add this file to your dotfiles or version control in order to reproduce your configuration on a different system.
You can also edit this file and run `fisher` to commit your changes. Only the plugins listed in this file will be installed (or remained installed) after `fisher` returns. If a plugin is already installed, it will be updated. Everything after a `#` symbol will be ignored.
> `fisher update` by itself will update everything, including Fisher.
```fish
vi ~/.config/fish/fishfile
```
### Removing plugins
```diff
- rafaelrinaldi/pure
- jethrokuan/z@pre27
gitlab.com/jorgebucaran/kraken
edc/bass
+ FabioAntunes/fish-nvm
~/path/to/myfish/plugin
```
Remove installed plugins using the `remove` command.
```console
fisher
fisher remove jorgebucaran/nvm.fish@1.1.0
```
That will remove **rafaelrinaldi/pure** and **jethrokuan/z**, add **FabioAntunes/fish-nvm** and update the rest.
## What is a plugin?
plugins help you organize shell scripts into reusable, independent components that can be shared through a git URL or the path to a local directory. Even if your plugin is not meant to be shared with others, you can benefit from composition and the ability to depend on other plugins.
The structure of a plugin can be adopted from the fictional project described below. These are the files that Fisher looks for when installing or uninstalling a plugin. The name of the root directory can be anything you like.
Sometimes you may want to remove everything, including Fisher.
```console
fish-kraken
├── fishfile
├── functions
│ └── kraken.fish
├── completions
│ └── kraken.fish
└── conf.d
└── kraken.fish
fisher list | fisher remove
```
If your project depends on other plugins, it should list them as dependencies in a fishfile. There is no need for a fishfile otherwise. The rules concerning the usage of the fishfile are the same rules we've already covered in [using the fishfile](#using-the-fishfile).
While some plugins contain every kind of file, some plugins include only functions or configuration snippets. You are not limited to a single file per directory either. There can be as many files as you need or just one as in the following example.
```
fish-kraken
└── kraken.fish
```
The lack of private scope in fish causes all plugin functions to share the same namespace. A good rule of thumb is to prefix functions intended for private use with the name of your plugin to prevent conflicts.
## Creating your own plugin
## Using your `fish_plugins` file
The best way to show you how to create your own plugin is by building one together. Our first example will be a function that prints the raw non-rendered markdown source of a README file from GitHub to standard output. Its inputs will be the name of the owner, repository, and branch. If no branch is specified, we'll use the master branch.
Whenever you install or remove a plugin from the command line, Fisher will write down your plugins to `~/.config/fish/fish_plugins`. Adding this file to your dotfiles or version control is the easiest way to share your configuration across different systems.
Create the following directory structure and function file. Make sure the function name matches the file name; otherwise fish won't be able to autoload it the first time you try to use it.
```
fish-readme
└── readme.fish
```
```fish
function readme -a owner repo branch
if test -z "$branch"
set branch master
end
curl -s https://raw.githubusercontent.com/$owner/$repo/$branch/README.md
end
```
You can install it with the `add` command followed by the path to the directory.
You can also edit this file and run `fisher update` to commit your changes. Here's an example:
```console
fisher add /absolute/path/to/fish-readme
nano ~/.config/fish/fishfile
```
To publish the plugin upload it to GitHub, GitLab, BitBucket, or anywhere you like. Keep in mind that Fisher is not a plugin registry. Its function is to fetch fish scripts and put them in place so that your shell can find them.
Now let's install the plugin from the net. Open your [fishfile](#using-the-fishfile) and replace the local version of the plugin you added with the URL of the repository. Save your changes and run `fisher`.
```diff
- /absolute/path/to/fish-readme
+ gitlab.com/jorgebucaran/fish-readme
jorgebucaran/fisher
ilancosman/tide
+ jethrokuan/z
- jorgebucaran/nvm.fish@1.1.0
/home/jb/path/to/plugin
```
```console
fisher
fisher update
```
You can leave off the `github.com` part of the URL when adding or removing plugins hosted on GitHub. If your plugin is hosted anywhere else, the address of the server is required.
## Plugin Events
> [Coming in Fisher 4](https://github.com/jorgebucaran/fisher/issues/582): `install` and `update`.
That will install **jethrokuan/z**, remove **jorgebucaran/nvm.fish**, and update everything else.
### `uninstall`
## Event system
Plugins may provide custom uninstall logic through an uninstall [event handler](https://fishshell.com/docs/current/#event).
Fisher leverages the fish [event system](https://fishshell.com/docs/current/cmds/emit.html) to notify plugins when they are being installed, updated, or removed.
Let's walk through an example that uses this feature to add a new key binding for the Control-G sequence. Let's say we want to use it to open the fishfile in the `vi` editor quickly. When you install the plugin, `fishfile_quick_edit_key_bindings.fish` will be sourced, adding the specified key binding and loading the event handler function. When you uninstall it, Fisher will emit an uninstall event where you can erase the bindings!
```
fish-fishfile-quick-edit
└── conf.d
└── fishfile_quick_edit_key_bindings.fish
```
> `--on-event` functions must be loaded when their event is emitted. So put event handlers in your `conf.d` directory.
```fish
bind \cg "vi ~/.config/fish/fishfile"
# Defined in conf.d/foobar.fish
function foobar_install --on-event foobar_install
# Set global variables, create bindings, and other install logic.
end
set -l name (basename (status -f) .fish)_uninstall
function foobar_update --on-event foobar_update
# Migrate resources, print warnings, and other update logic.
end
function $name --on-event $name
bind --erase \cg
function foobar_uninstall --on-event foobar_uninstall
# Erase "private" functions, variables, bindings, and other uninstall logic.
end
```

@ -0,0 +1,11 @@
complete -c fisher -e
complete -c fisher -x -l help -d "print usage help"
complete -c fisher -x -l version -d "print fisher version"
complete -c fisher -x -n "__fish_use_subcommand" -a install -d "install plugins"
complete -c fisher -x -n "__fish_use_subcommand" -a update -d "update installed plugins"
complete -c fisher -x -n "__fish_use_subcommand" -a remove -d "remove installed plugins"
complete -c fisher -x -n "__fish_use_subcommand" -a list -d "list installed plugins matching <regex>"
for plugin in (fisher list)
complete -c fisher -x -n "__fish_seen_subcommand_from update remove" -a $plugin
end

@ -1,436 +1,217 @@
set -g fisher_version 3.3.2
set -g fisher_version 4.0.0
function fisher -a cmd -d "fish plugin manager"
set -q XDG_CACHE_HOME; or set XDG_CACHE_HOME ~/.cache
set -q XDG_CONFIG_HOME; or set XDG_CONFIG_HOME ~/.config
set -q XDG_DATA_HOME; or set XDG_DATA_HOME ~/.local/share
set -g fish_config $XDG_CONFIG_HOME/fish
set -g fisher_cache $XDG_CACHE_HOME/fisher
set -q XDG_DATA_HOME || set XDG_DATA_HOME ~/.local/share
set -q fisher_path || set -g fisher_path $__fish_config_dir
set -g fisher_data $XDG_DATA_HOME/fisher
set -g fish_plugins $__fish_config_dir/fish_plugins
set -q fisher_path; or set -g fisher_path $fish_config
set -g fishfile $fish_config/fishfile
for path in {$fish_config,$fisher_path}/{functions,completions,conf.d} $fisher_cache
if test ! -d $path
command mkdir -p $path
end
end
switch "$cmd"
case -v --version
echo "fisher, version $fisher_version"
case "" -h --help
echo "usage: fisher install <plugins...> install plugins"
echo " fisher update [<plugins...>] update installed plugins"
echo " fisher remove <plugins...> remove installed plugins"
echo " fisher list [<regex>] list installed plugins matching <regex>"
echo "options:"
echo " -v or --version print fisher version"
echo " -h or --help print this help message"
case list
_fisher_list | string match --entire --regex -- "$argv[2]"
case install update remove
set -l install_plugins
set -l update_plugins
set -l remove_plugins
set -l old_plugins (_fisher_list)
set -q fisher_user_api_token && set -l curl_opts -u $fisher_user_api_token
set -l pid_list
if test ! -e $fisher_path/completions/fisher.fish
echo "fisher complete" >$fisher_path/completions/fisher.fish
_fisher_complete
end
if not isatty
read -lz --list list
set -a argv $list
end
if test -e $fisher_path/conf.d/fisher.fish
switch "$version"
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
if not set -q argv[2]
if test "$cmd" != update
echo "fisher: invalid number of arguments (see `fisher -h`)" >&2 && return 1
end
# 2019-10-22: temp code, migrates fishfile from old path back to $fish_config
if test -e "$fisher_path/fishfile"; and test ! -e "$fishfile"
command mv -f "$fisher_path/fishfile" "$fishfile"
end
test -e $fish_plugins && set -l new_plugins (_fisher_plugin_parse (string trim <$fish_plugins))
# 2020-06-23: temp code, migrates fisher data from XDG_CONFIG_HOME to XDG_DATA_HOME
set -l fisher_config $XDG_CONFIG_HOME/fisher
if test -d $fisher_config
echo "migrating local data from $fisher_config to $fisher_data"
command rm -rf $fisher_data
command mv -f $fisher_config $fisher_data
end
for plugin in $new_plugins
if contains -- "$plugin" $old_plugins
set -a update_plugins $plugin
else
set -a install_plugins $plugin
end
end
switch "$cmd"
case {,self-}complete
_fisher_complete
case copy-user-key-bindings
_fisher_copy_user_key_bindings
case ls
set -e argv[1]
if test -s "$fishfile"
set -l file (_fisher_fmt <$fishfile | _fisher_parse -R | command sed "s|@.*||")
_fisher_ls | _fisher_fmt | command awk -v FILE="$file" "
BEGIN { for (n = split(FILE, f); ++i <= n;) file[f[i]] } \$0 in file && /$argv[1]/
" | command sed "s|^$HOME|~|"
end
case self-update
_fisher_self_update (status -f)
case self-uninstall
_fisher_self_uninstall
case {,-}-v{ersion,}
echo "fisher version $fisher_version" (status -f | command sed "s|^$HOME|~|")
case {,-}-h{elp,}
_fisher_help
case ""
_fisher_commit --
case add rm
if not isatty
while read -l arg
set argv $argv $arg
for plugin in $old_plugins
if not contains -- "$plugin" $new_plugins
set -a remove_plugins $plugin
end
end
else
for plugin in (_fisher_plugin_parse $argv[2..-1])
if contains -- "$plugin" $old_plugins
if test "$cmd" = install
echo "fisher: \"$plugin\" is already installed (try `fisher update $plugin`)" >&2 && return 1
else if test "$cmd" = update
set -a update_plugins $plugin
else
set -a remove_plugins $plugin
end
else if test "$cmd" = install
set -a install_plugins $plugin
else if test "$cmd" = update || test "$cmd" = remove
echo "fisher: plugin \"$plugin\" not found (is this a valid plugin?)" >&2 && return 1
end
end
end
if test (count $argv) = 1
echo "fisher: invalid number of arguments" >&2
_fisher_help >&2
return 1
for plugin in $install_plugins $update_plugins
fish -c "
if test -e $plugin
command mkdir -p $fisher_data/@$USER
set -l target $fisher_data/@$USER/(string replace --all --regex '^.*/|\.fish\$' \"\" $plugin)
if test ! -L \$target
command ln -sf $plugin \$target
end
else
set name_tag (string split \@ $plugin) || set name_tag[2] HEAD
set url https://codeload.github.com/\$name_tag[1]/tar.gz/\$name_tag[2]
set tmp (command mktemp -d)
echo fetching \$url >&2
if command curl $curl_opts -Ss -w \"\" \$url 2>&1 | command tar -xzf- -C \$tmp 2>/dev/null
command rm -rf $fisher_data/$plugin
command mkdir -p $fisher_data/$plugin
command cp -Rf \$tmp/*/* $fisher_data/$plugin
command rm -rf \$tmp
else
echo fisher: cannot install \"$plugin\" \(is this a valid plugin\?\) >&2
end
end
" >/dev/null &
set -a pid_list $last_pid
end
wait $pid_list 2>/dev/null
_fisher_commit $argv
case \*
echo "fisher: unknown flag or command \"$cmd\"" >&2
_fisher_help >&2
return 1
end
end
function _fisher_complete
complete -ec fisher
complete -xc fisher -n __fish_use_subcommand -a add -d "Add plugins"
complete -xc fisher -n __fish_use_subcommand -a rm -d "Remove plugins"
complete -xc fisher -n __fish_use_subcommand -a ls -d "List installed plugins matching REGEX"
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)
complete -xc fisher -n "__fish_seen_subcommand_from rm" -a $pkg
end
end
function _fisher_copy_user_key_bindings
if functions -q fish_user_key_bindings
functions -c fish_user_key_bindings fish_user_key_bindings_copy
end
function fish_user_key_bindings
for file in $fisher_path/conf.d/*_key_bindings.fish
source $file >/dev/null 2>/dev/null
end
if functions -q fish_user_key_bindings_copy
fish_user_key_bindings_copy
end
end
end
function _fisher_ls
for pkg in $fisher_data/*/*/*
command readlink $pkg; or echo $pkg
end
end
for plugin in $install_plugins $update_plugins
set -l data $fisher_data/$plugin
test -e $plugin && set data $fisher_data/@$USER/(string replace --all --regex '^.*/' "" $plugin)
test -e $data || continue
function _fisher_fmt
command sed "s|^[[:space:]]*||;s|^$fisher_data/||;s|^~|$HOME|;s|^\.\/*|$PWD/|;s|^https*:/*||;s|^github\.com/||;s|/*\$||"
end
contains -- "$plugin" $install_plugins && set -l event install || set -l event update
function _fisher_help
echo "usage: fisher add <plugin...> Add plugin/s"
echo " fisher rm <plugin...> Remove plugin/s"
echo " fisher Update all plugins"
echo " fisher ls [<regex>] List installed plugins matching <regex>"
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 "examples:"
echo " fisher add jorgebucaran/z rafaelrinaldi/pure"
echo " fisher add gitlab.com/foo/bar@v2"
echo " fisher add ~/path/to/local/pkg"
echo " fisher add <file"
echo " fisher rm rafaelrinaldi/pure"
echo " fisher ls | fisher rm"
end
set -l funcs $data/*.fish
set -l files $data/{functions,conf.d}/*.fish
set -q files[1] && set files (string replace --all $data $fisher_path $files)
set -q funcs[1] && set files (string replace --all $data $fisher_path/functions $funcs) $files
function _fisher_self_update -a file
set -l url "https://raw.githubusercontent.com/jorgebucaran/fisher/main/fisher.fish"
echo "fetching $url" >&2
command curl -s "$url?nocache" >$file.
command cp -Rf $funcs $fisher_path/functions 2>/dev/null
command cp -Rf $data/{functions,completions,conf.d} $fisher_path 2>/dev/null
set -l next_version (command awk '$4 ~ /^[0-9]+\.[0-9]+\.[0-9]+$/ { print v=$4 } { exit !v }' <$file.)
switch "$next_version"
case "" $fisher_version
command rm -f $file.
if test -z "$next_version"
echo "fisher: cannot update fisher -- are you offline?" >&2
return 1
for file in $files
echo "sourcing $file" >&2
source $file
if string match --quiet --regex -- conf\.d/ $file
emit (string replace --all --regex '^.*/|\.fish$' "" $file)_$event
end
end
end
echo "fisher is already up-to-date" >&2
case \*
echo "linking $file" | command sed "s|$HOME|~|" >&2
command mv -f $file. $file
source $file
echo "updated to fisher $fisher_version -- hooray!" >&2
_fisher_complete
end
end
function _fisher_self_uninstall
for pkg in (_fisher_ls)
_fisher_rm $pkg
end
for file in $fisher_cache $fisher_data $fisher_path/{functions,completions,conf.d}/fisher.fish $fishfile
echo "removing $file"
command rm -Rf $file 2>/dev/null
end | command sed "s|$HOME|~|" >&2
for plugin in $remove_plugins
set -l data $fisher_data/$plugin
test -e $plugin && set data $fisher_data/@$USER/(string replace --all --regex '^.*/' "" $plugin)
for name in (set -n | command awk '/^fisher_/')
set -e "$name"
end
set -l funcs $data/*.fish
set -l files $data/{conf.d,functions,completions}/*
set -q files[1] && set files (string replace --all $data $fisher_path $files)
set -q funcs[1] && set files (string replace --all $data $fisher_path/functions $funcs) $files
functions -e (functions -a | command awk '/^_fisher/') fisher
complete -c fisher --erase
end
for file in $data/conf.d/*.fish
emit (string replace --all --regex '^.*/|\.fish$' "" $file)_uninstall
end
function _fisher_commit -a cmd
set -e argv[1]
set -l elapsed (_fisher_now)
printf "removing %s\n" $files >&2
command rm -rf $files $data
command rm -df (string split --right --max=1 / $data)[1] 2>/dev/null
functions -e (string replace --all --regex '^.*/|\.fish$' "" $files)
functions -q fish_prompt || source $__fish_data_dir/functions/fish_prompt.fish
end
if test ! -e "$fishfile"
command touch $fishfile
echo "created new fishfile in $fishfile" | command sed "s|$HOME|~|" >&2
end
source $fisher_path/completions/fisher.fish 2>/dev/null
set -l old_pkgs (_fisher_ls | _fisher_fmt)
for pkg in (_fisher_ls)
_fisher_rm $pkg
end
command rm -Rf $fisher_data
command mkdir -p $fisher_data
_fisher_list >$fish_plugins
set -l next_pkgs (_fisher_fmt <$fishfile | _fisher_parse -R $cmd (printf "%s\n" $argv | _fisher_fmt))
set -l actual_pkgs (_fisher_fetch $next_pkgs)
set -l updated_pkgs
for pkg in $old_pkgs
if contains -- $pkg $actual_pkgs
set updated_pkgs $updated_pkgs $pkg
end
end
set -l total (count $install_plugins) (count $update_plugins) (count $remove_plugins)
if test "$total" != "0 0 0"
echo "done," (string join ", " (
test $total[1] = 0 || echo "$total[1] installed") (
test $total[2] = 0 || echo "$total[2] updated") (
test $total[3] = 0 || echo "$total[3] removed")
) "plugin/s" >&2
end
if test -z "$actual_pkgs$updated_pkgs$old_pkgs$next_pkgs"
echo "fisher: nothing to commit -- try adding some plugins" >&2
return 1
test -s $fish_plugins || test "$cmd" = remove
case \*
echo "fisher: unknown flag or command \"$cmd\" (see `fisher -h)`" >&2
return 1
end
end
set -l out_pkgs
if test "$cmd" = "rm"
set out_pkgs $next_pkgs
else
for pkg in $next_pkgs
if contains -- (echo $pkg | command sed "s|@.*||") $actual_pkgs
set out_pkgs $out_pkgs $pkg
end
function _fisher_plugin_parse
for plugin in $argv
switch $plugin
case \~\*
string replace --all --regex '^~' ~ "$plugin"
case \*/ /\* \*../\* ./\*
realpath $plugin 2>/dev/null
case \*/\*
echo $plugin
case ""
case \*
_fisher_plugin_parse ./$plugin
end
end
printf "%s\n" (_fisher_fmt <$fishfile | _fisher_parse -W $cmd $out_pkgs | command sed "s|^$HOME|~|") >$fishfile
_fisher_complete
command awk -v A=(count $actual_pkgs) -v U=(count $updated_pkgs) -v O=(count $old_pkgs) -v E=(_fisher_now $elapsed) '
BEGIN {
res = fmt("removed", O - U, fmt("updated", U, fmt("added", A - U)))
printf((res ? res : "done") " in %.2fs\n", E / 1000)
}
function fmt(action, n, s) {
return n ? (s ? s ", " : s) action " " n " plugin" (n > 1 ? "s" : "") : s
}
' >&2
end
function _fisher_parse -a mode cmd
set -e argv[1..2]
command awk -v FS="[[:space:]]*#+" -v MODE="$mode" -v CMD="$cmd" -v ARGSTR="$argv" '
BEGIN {
for (n = split(ARGSTR, a, " "); i++ < n;) pkgs[getkey(a[i])] = a[i]
}
!NF { next } { k = getkey($1) }
MODE == "-R" && !(k in pkgs) && ($0 = $1)
MODE == "-W" && (/^#/ || k in pkgs || CMD != "rm") { print pkgs[k] (sub($1, "") ? $0 : "") }
MODE == "-W" || CMD == "rm" { delete pkgs[k] }
END {
for (k in pkgs) {
if (CMD != "rm" || MODE == "-W") print pkgs[k]
else print "fisher: cannot remove \""k"\" -- plugin is not in fishfile" > "/dev/stderr"
}
}
function getkey(s, a) {
return (split(s, a, /@+|:/) > 2) ? a[2]"/"a[1]"/"a[3] : a[1]
}
'
end
function _fisher_fetch
set -l pkg_jobs
set -l out_pkgs
set -l next_pkgs
set -l local_pkgs
set -q fisher_user_api_token; and set -l curl_opts -u $fisher_user_api_token
for pkg in $argv
switch $pkg
case \~\* /\*
set -l path (echo "$pkg" | command sed "s|^~|$HOME|")
if test -e "$path"
set local_pkgs $local_pkgs $path
else
echo "fisher: cannot add \"$pkg\" -- is this a valid file?" >&2
end
continue
end
command awk -v PKG="$pkg" -v FS=/ '
BEGIN {
split(PKG, tmp, /@/)
pkg = split(PKG, _, "/") <= 2 ? "github.com/"tmp[1] : tmp[1]
tag = tmp[2] ? tmp[2] : "HEAD"
print pkg "\t" (\
pkg ~ /^github/ ? "https://codeload."pkg"/tar.gz/"tag : \
pkg ~ /^gitlab/ ? "https://"pkg"/-/archive/"tag"/"tmp[split(pkg, tmp, "/")]"-"tag".tar.gz" : \
pkg ~ /^bitbucket/ ? "https://"pkg"/get/"tag".tar.gz" : pkg\
)
}
' | read -l pkg url
if test ! -d "$fisher_data/$pkg"
fish -c "
echo fetching $url >&2
command mkdir -p $fisher_data/$pkg $fisher_cache/(command dirname $pkg)
if command curl $curl_opts -Ss -w \"\" $url 2>&1 | command tar -xzf- -C $fisher_data/$pkg 2>/dev/null
command rm -Rf $fisher_cache/$pkg
command mv -f $fisher_data/$pkg/* $fisher_cache/$pkg
command rm -Rf $fisher_data/$pkg
command cp -Rf {$fisher_cache,$fisher_data}/$pkg
else if test -d \"$fisher_cache/$pkg\"
echo fisher: cannot connect to server -- looking in \"$fisher_cache/$pkg\" | command sed 's|$HOME|~|' >&2
command cp -Rf $fisher_cache/$pkg $fisher_data/$pkg/..
else
command rm -Rf $fisher_data/$pkg
echo fisher: cannot add \"$pkg\" -- is this a valid plugin\? >&2
end
" >/dev/null &
set pkg_jobs $pkg_jobs (_fisher_jobs --last)
set next_pkgs $next_pkgs "$fisher_data/$pkg"
function _fisher_list
for path in $fisher_data/*/*
if test -L $path
realpath $path
else
string replace --all $fisher_data/ "" $path
end
end
end
if set -q pkg_jobs[1]
while for job in $pkg_jobs
contains -- $job (_fisher_jobs); and break
# Migrate to fisher 4 (supports both self-update and new curl | source)
if functions -q _fisher_self_update || test -e $__fish_config_dir/fishfile
function _fisher_migrate
function _fisher_complete
if not _fisher_list | string match --entire --regex --quiet -- jorgebucaran/fisher
fisher install jorgebucaran/fisher
end
functions -e _fisher_complete
end
for pkg in $next_pkgs
if test -d "$pkg"
set out_pkgs $out_pkgs $pkg
_fisher_add $pkg
end
end
end
set -q XDG_DATA_HOME || set XDG_DATA_HOME ~/.local/share
set -q XDG_CACHE_HOME || set XDG_CACHE_HOME ~/.cache
set -q XDG_CONFIG_HOME || set XDG_CONFIG_HOME ~/.config
set -q fisher_path || set fisher_path $__fish_config_dir
set -l local_prefix $fisher_data/local/$USER
if test ! -d "$local_prefix"
command mkdir -p $local_prefix
end
for pkg in $local_pkgs
set -l target $local_prefix/(command basename $pkg)
if test ! -L "$target"
command ln -sf $pkg $target
set out_pkgs $out_pkgs $pkg
_fisher_add $pkg --link
if test -e $__fish_config_dir/fishfile
command awk '/#|^gitlab|^ *$/ { next } $0' <$__fish_config_dir/fishfile >>$__fish_config_dir/fish_plugins
end
end
if set -q out_pkgs[1]
_fisher_fetch (
for pkg in $out_pkgs
if test -s "$pkg/fishfile"
_fisher_fmt <$pkg/fishfile | _fisher_parse -R
end
end)
printf "%s\n" $out_pkgs | _fisher_fmt
end
end
function _fisher_add -a pkg opts
for src in $pkg/{functions,completions,conf.d}/**.* $pkg/*.fish
set -l target (command basename $src)
switch $src
case $pkg/conf.d\*
set target $fisher_path/conf.d/$target
case $pkg/completions\*
set target $fisher_path/completions/$target
case $pkg/{functions,}\*
switch $target
case uninstall.fish
continue
case {init,key_bindings}.fish
set target $fisher_path/conf.d/(command basename $pkg)\_$target
case \*
set target $fisher_path/functions/$target
end
end
echo "linking $target" | command sed "s|$HOME|~|" >&2
if set -q opts[1]
command ln -sf $src $target
else
command cp -f $src $target
end
switch $target
case \*.fish
source $target >/dev/null 2>/dev/null
end
end
end
command rm -rf $__fish_config_dir/fishfile $fisher_path/{conf.d,completions}/fisher.fish {$XDG_DATA_HOME,$XDG_CACHE_HOME,$XDG_CONFIG_HOME}/fisher
functions -e _fisher_migrate _fisher_copy_user_key_bindings _fisher_ls _fisher_fmt _fisher_self_update _fisher_self_uninstall _fisher_commit _fisher_parse _fisher_fetch _fisher_add _fisher_rm _fisher_jobs _fisher_now _fisher_help
function _fisher_rm -a pkg
for src in $pkg/{conf.d,completions,functions}/**.* $pkg/*.fish
set -l target (command basename $src)
set -l filename (command basename $target .fish)
switch $src
case $pkg/conf.d\*
test "$filename.fish" = "$target"; and emit "$filename"_uninstall
set target conf.d/$target
case $pkg/completions\*
test "$filename.fish" = "$target"; and complete -ec $filename
set target completions/$target
case $pkg/{,functions}\*
test "$filename.fish" = "$target"; and functions -e $filename
switch $target
case uninstall.fish
source $src
continue
case {init,key_bindings}.fish
set target conf.d/(command basename $pkg)\_$target
case \*
set target functions/$target
end
end
command rm -f $fisher_path/$target
fisher update
end
if not functions -q fish_prompt
source "$__fish_datadir$__fish_data_dir/functions/fish_prompt.fish"
end
end
function _fisher_jobs
jobs $argv | command awk '/^[0-9]+\t/ { print $1 }'
end
echo "bootstrapping fisher $fisher_version for the first time, learn more at "(set_color --bold --underline)"https://git.io/fisher-4"(set_color normal) >&2
function _fisher_now -a elapsed
switch (command uname)
case Darwin \*BSD
command perl -MTime::HiRes -e 'printf("%.0f\n", (Time::HiRes::time() * 1000) - $ARGV[0])' $elapsed
case \*
math (command date "+%s%3N") - "0$elapsed"
end
end
_fisher_migrate
end
Loading…
Cancel
Save