diff --git a/.travis.yml b/.travis.yml index d8fd839..2488432 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,5 @@ before_install: - sudo apt-get update - sudo apt-get -y install fish script: - - curl -Lo ~/.config/fish/functions/f.fish --create-dirs git.io/fisher - - fish -c "f --version" - - fish -c "f -h" - - fish -c "f z; f ls" + - curl -Lo ~/.config/fish/functions/fisher.fish --create-dirs git.io/fisher + - fish -c "fisher version" diff --git a/README.md b/README.md index 6416c2c..9ef4b4c 100644 --- a/README.md +++ b/README.md @@ -1,203 +1,278 @@ -# [fisherman] +# Fisher -[![Build Status][travis-badge]][travis-link] +[![Build Status](https://img.shields.io/travis/jorgebucaran/fisher.svg)](https://travis-ci.org/jorgebucaran/fisher) +[![Releases](https://img.shields.io/github/release/jorgebucaran/fisher.svg?label=latest)](https://github.com/jorgebucaran/fisher/releases) -Fisherman is a [fish-shell] plugin manager. +Fisher is a package manager for the [fish shell](https://fishshell.com). It defines a common interface for package authors to build and distribute their shell scripts in a portable way. You can use it to extend your shell capabilities, change the look of your prompt and create repeatable configurations across different systems effortlessly. -## Install +## Features -```sh -curl -Lo ~/.config/fish/functions/fisher.fish --create-dirs https://git.io/fisher +- Zero configuration +- Oh My Fish package support +- High-speed concurrent package downloads⌁! +- If you've installed a package before, then it can be installed again offline +- Add, update and remove functions, completions, keybindings and configuration snippets from a variety of sources using the command line or editing your [fishfile](#using-the-fishfile) + +## Installation + +Download fisher to your fish functions directory or any directory in your $fish_function_path. + + + +```fish +curl https://git.io/fisher --create-dirs -sLo ~/.config/fish/functions/fisher.fish +``` + +If the [XDG_CONFIG_HOME](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html#variables) environment variable is defined on your system, use $XDG_CONFIG_HOME/fish to resolve the path to your fish configuration directory instead of ~/.config/fish. + +### Dependencies + +- [fish](https://github.com/fish-shell/fish-shell) 2.0+ (prefer 2.3 or newer) +- [curl](https://github.com/curl/curl) 7.10.3+ +- [git](https://github.com/git/git) 1.7.12+ + +### Legacy fish support + +Stuck in legacy fish and can't upgrade your shell? You'll need to run some code on startup to support packages that use [configuration snippets](#configuration-snippets). Open your ~/.config/fish/config.fish and add the following code at the beginning of the file. + +```fish +set -q XDG_CONFIG_HOME; or set XDG_CONFIG_HOME ~/.config +for file in $XDG_CONFIG_HOME/conf.d/*.fish + builtin source $file 2>/dev/null +end +``` + +### Bootstrap installation + +To automate installing fisher on a new system, add the following code to your ~/.config/fish/config.fish. This will download fisher and install all the packages listed in your [fishfile](#using-the-fishfile) (if there is one). + +```fish +if not functions -q fisher + echo "Installing fisher for the first time..." >&2 + set -q XDG_CONFIG_HOME; or set XDG_CONFIG_HOME ~/.config + curl https://git.io/fisher --create-dirs -sLo $XDG_CONFIG_HOME/fish/functions/fisher.fish + fisher +end +``` + +### Changing the installation prefix + +Use the `$fisher_path` environment variable to change the prefix location where functions, completions, and configuration snippets will be copied to when a package is installed. The default location is where fisher itself is installed. If you followed the installation instructions above it should be in ~/.config/fish. + +Make sure to append your functions and completions directories to the `$fish_function_path` and `$fish_complete_path` environment variables so that they can be autoloaded by fish in future sessions and to source every fish file inside your conf.d directory to run configuration snippets on startup. + +Here is a boilerplate configuration you can add to your ~/.config/fish/config.fish file to get you started. + +```fish +set -g fisher_path ~/another/path + +set fish_function_path $fish_function_path $fisher_path/functions +set fish_complete_path $fish_complete_path $fisher_path/completions + +for file in $fisher_path/conf.d/*.fish + builtin source $file 2> /dev/null +end ``` ## Usage -Install a plugin. +You've found an interesting utility you'd like to try out. Or perhaps you've [created a package](#creating-your-own-package) yourself. How do you install it on your system? You may want to update or remove it later too. How do you do that? + +You can use fisher to add, update and remove packages interactively, taking advantage of fish tab completions 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 both? + +### Adding packages + +Install packages using the `add` command. ``` -fisher z +fisher add jethrokuan/z rafaelrinaldi/pure ``` -Install several plugins concurrently. +Packages will be downloaded from GitHub if the name of the host is not specified. To install a package hosted anywhere else use the address of the remote server and the path to the repository. ``` -fisher fzf edc/bass omf/thefuck omf/theme-bobthefish +fisher add gitlab.com/owner/foobar bitbucket.org/owner/fumbam ``` -Install a specific branch. -```sh -fisher edc/bass:master -``` - -Install a specific tag. -```sh -fisher edc/bass@1.2.0 -``` - -Install a gist. +Install a package from a tag or a branch. ``` -fisher https://gist.github.com/username/1f40e1c6e0551b2666b2 +fisher add jethrokuan/z@pre27 ``` -Install a local plugin. +Install a package from a local directory. Local packages are managed through [symbolic links](https://en.wikipedia.org/wiki/Symbolic_link), so you can develop and use them at the same time. -```sh -fisher ~/path/to/my_plugin +``` +fisher add ~/myfish/mypkg ``` -Edit your [**fishfile**](#what-is-a-fishfile-and-how-do-i-use-it) and run `fisher` to commit changes, e.g. install missing plugins. +Notice 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. + +### Listing packages + +List all the packages that are currently installed using the `ls` command. This includes packages you didn't install yourself but were installed on your system as a dependency of another package. -```sh -$EDITOR ~/.config/fish/fishfile -fisher ``` - -Show everything you've installed. - -```ApacheConf fisher ls -@ my_plugin # a local plugin -* bobthefish # current theme - bass - fzf - thefuck - z +jethrokuan/z@pre27 +rafaelrinaldi/pure +~/myfish/mypkg +gitlab.com/owner/foobar +bitbucket.org/owner/fumbam ``` -Show everything available to install. +### Removing packages + +Remove packages using the `rm` command. If a package has dependencies, they too will be removed. If any dependencies are still shared by other packages, they will remain installed. ``` -fisher ls-remote +fisher rm rafaelrinaldi/pure ``` -Show additional information about plugins: +You can remove everything that is currently installed in one sweep using the following pipeline. -``` -fisher ls-remote --format="%name(%stars): %info [%url]\n" -``` - -Update everything. - -``` -fisher up -``` - -Update specific plugins. - -``` -fisher up bass z fzf -``` - -Remove plugins. - -``` -fisher rm thefuck -``` - -Remove all the plugins. - -``` +```sh fisher ls | fisher rm ``` -Get help. +### Updating packages + +Run `fisher` to update everything you've installed. There is no dedicated update command. Using the command line to add and remove packages is a facade for modifying and committing changes to your fishfile in a single step. + +If you are looking for a way to update fisher itself, use the `self-update` command. ``` -fisher help z +fisher self-update ``` -## Bootstrap +### Other commands -If you want to automate installing fisherman in a new system when it isn't already installed, add the following at the top of your ~/.config/fish/config.fish. +To display usage help use the `help` command. -This will install fisherman and download all the plugins listed in your _fishfile_. +``` +fisher help +``` + +Last but not least use the `version` command to display the current version of fisher. + +``` +fisher version +``` + +### Using the fishfile + +Whenever you add or remove a package from the command line we'll create 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 packages listed in the file will be installed after fisher returns. If a package is already installed it will be updated. Empty lines and everything after a `#` (comments) will be ignored. ```fish -if not test -f ~/.config/fish/functions/fisher.fish - curl -sLo ~/.config/fish/functions/fisher.fish --create-dirs git.io/fisher - fisher +vi ~/.config/fish/fishfile +``` + +```fish +rafaelrinaldi/pure +jethrokuan/z@pre27 + +# my local packages +~/myfish/mypkg +``` + +``` +fisher +``` + +## Package concepts + +Packages 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 package is not meant to be shared with others, you can benefit from composition and the ability to depend on other packages. + +A package is uniquely identified by the name of its host, owner and root directory. Alas, the lack of private function scope in fish causes all package functions to share the same namespace. A good rule of thumb is to prefix functions intended for private use with the name of your package to reduce the possibility of conflicts. + +The structure of a package can be adopted from the fictional project described below. These are the files that fisher looks for when installing or uninstalling a package. Of course, you can elaborate on this to add tests, documentation, and other files, e.g. README and LICENSE files. The name of the root directory can be anything you wish. I recommend using a naming convention such as fish-_package-name_ for easier classification. + +``` +fish-fly +├── fishfile +├── functions +│ └── fly.fish +├── completions +│ └── fly.fish +└── conf.d + └── fly.fish +``` + +If your project depends on other packages, 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 packages contain every kind of file, some packages contain 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 only one as in the next example. + +``` +fish-fly +└── fly.fish +``` + +### Creating your own package + +The best way to show you how to create your own package 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. + +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 --argument owner repo branch + if test -z "$branch" + set branch master + end + curl -s https://raw.githubusercontent.com/$owner/$repo/$branch/README.md end ``` -## FAQ +You can install it with the `add` command followed by the path to the directory. Local packages are symlinked to your `$fisher_path` so that code changes are instantly reflected during development. -### Is fisherman compatible with oh-my-fish themes and plugins? +``` +fisher add /absolute/path/to/fish-readme +``` -Yes! +The next logical step is to share it with others. How do you do that? Fisher is not a package registry. Its function is to fetch fish scripts and put them in place so that your shell can find them. To publish a package put your code online. You can use GitHub, GitLab or BitBucket or anywhere you like. -### How can I contribute to fisherman? +Now let's install the package again, this time from its new location. Open your ~/.config/fish/fishfile and replace the local version of the package we previously installed with the URL of the remote repository. Save your changes and run `fisher`. You can leave off the github.com part of the URL when adding or removing packages hosted on GitHub. -You are welcome to join the organization. Just [ask](https://fisherman-wharf.herokuapp.com/) and someone will send you an invite. +### Configuration snippets -### Where does fisherman put stuff? +Configuration snippets consist of any fish files in your ~/.config/fish/conf.d directory. They are evaluated on [shell startup](http://fishshell.com/docs/current/index.html#initialization) and often used to modify the shell environment, create key bindings, etc. -The configuration and cache are saved to ~/.config/fisherman and ~/.cache/fisherman respectively. +Unlike functions or completions, which can be erased programmatically, we can't undo a fish file that has been sourced without creating a new shell session. For this reason, packages that use configuration snippets provide custom uninstall logic through an uninstall [event handler](https://fishshell.com/docs/current/#event). -The fishfile and plugins are saved to ~/.config/fish by default. +Let's walk through an example that uses this feature to add a new key binding. Key bindings (or keyboard shortcuts) are sequences of one or more keys mapped to a fish command, builtin or function. The following package maps the sequence `Control-g` to opening your fishfile in the `vi` editor. -To customize this location, add the following to your ~/.config/fish/config.fish file: +``` +fish-fishfile-quick-edit +└── conf.d + └── fishfile-quick-edit.fish +``` ```fish -set -U fish_path ~/my/path +bind \cg "vi ~/.config/fish/fishfile" -set fish_function_path $fish_path/functions $fish_function_path -set fish_complete_path $fish_path/completions $fish_complete_path - -for file in $fish_path/conf.d/*.fish - builtin source $file 2> /dev/null +function fishfile-quick-edit_uninstall --event fishfile-quick-edit_uninstall + bind -e \cg end ``` -### How do I have fisherman copy plugin files instead of linking? +When you uninstall this package, we'll emit a _package-name_\_uninstall event that will call your eponymously named event handler function where the key binding will be erased. -By default, fisherman will create symlinks to plugin files. +> **Note**: Custom key bindings on shell startup are only available on fish 3.0 or newer. To make this package compatible with older versions of fish, you need to add custom key bindings via [jorgebucaran/fish-custom-key-bindings](). -To have fisherman copy files: +## Uninstalling + +You wish to know how to uninstall fisher and everything you've installed with it from your system. Or perhaps something went wrong and you want to start over. This will uninstall all the packages, purge the cache and then remove fisher from your fish functions directory. ```fish -set -U fisher_copy true +fisher self-uninstall ``` -### What is a fishfile and how do I use it? +## License -The fishfile lists what you've installed, and it's automatically updated as you install / remove plugins. - -You can edit this file and run `fisher` to install missing plugins and dependencies. - -### What is a plugin? - -A plugin is: - -1. a directory with one or more .fish functions at the root level of the project or inside a functions/ directory - -2. a theme or prompt: a fish_prompt.fish and/or fish_right_prompt.fish - -3. a snippet: one or more .fish files inside a conf.d/ directory, run by fish at the start of the session - -### How do I create my own plugins? - -You can use [fishkit](https://github.com/fisherman/fishkit) to help you scaffold out a new project from scratch. - -### How can I list plugins as dependencies to my plugin? - -Create a new fishfile at the root level of your project and write the plugin URL like so *github.com/owner/repo*. - -### Why am I receiving errors when running `fisher ls-remote`? - -You can export the GITHUB_USER and GITHUB_TOKEN environment variables in your shell, to prevent GitHub's search API from rejecting anonymous requests: - -```fish -set -x GITHUB_USER your_username -set -x GITHUB_TOKEN your_github_api_token_for_fisherman -``` -If you don't have a GitHub API token, you can [generate one from account settings](https://blog.github.com/2013-05-16-personal-api-tokens/) - -[slack-link]: https://fisherman-wharf.herokuapp.com -[slack-badge]: https://fisherman-wharf.herokuapp.com/badge.svg -[travis-link]: https://travis-ci.org/fisherman/fisherman -[travis-badge]: https://img.shields.io/travis/fisherman/fisherman.svg - -[fish]: https://github.com/fish-shell/fish-shell -[fish-shell]: https://github.com/fish-shell/fish-shell -[fisherman]: https://fisherman.github.io +Fisher is MIT licensed. See the [LICENSE](LICENSE.md) for details. diff --git a/fisher.fish b/fisher.fish index f99e225..e9c2d6e 100644 --- a/fisher.fish +++ b/fisher.fish @@ -1,2521 +1,411 @@ -function fisher -d "fish plugin manager" - switch "$FISH_VERSION" - case 2.1.2 2.1.1 2.1.0 2.0.0 - echo "You need fish 2.2.0 or higher to use fisherman." +set -g fisher_version 3.0.2 - if type brew >/dev/null 2>&1 - echo "Run: brew upgrade fish" - else - echo " - Refer to your package manager documentation for - instructions on how to upgrade your fish build. - " - end +type source >/dev/null; or function source; . $argv; end - return 1 +if command which perl >/dev/null + function _fisher_now -a elapsed + command perl -MTime::HiRes -e 'printf("%.0f\n", (Time::HiRes::time() * 1000) - $ARGV[0])' $elapsed + end +else + function _fisher_now -a elapsed + command date "+%s%3N" | command awk "{ sub(/3N\$/,\"000\"); print \$0 - 0$elapsed }" + end +end - case 2.2.0 - __fisher_log info " - You need fish 2.3.0 or higher to take advantage of snippets. - Without it some plugins might not work. - " - - if type -q brew - __fisher_log info "Please run &brew upgrade fish&" - else - __fisher_log info " - - Refer to your package manager documentation - for instructions on how to upgrade your fish build. - - If you can not upgrade, append the following code - to your ~/.config/fish/config.fish: - - &for file in ~/.config/fish/conf.d/*.fish& - &source \$file& - &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 - set -g fisher_version "2.13.3" - set -g fisher_spinners ⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏ + test -z "$XDG_CACHE_HOME"; and set XDG_CACHE_HOME ~/.cache + test -z "$XDG_CONFIG_HOME"; and set XDG_CONFIG_HOME ~/.config - if [ -e /dev/stdout ] - set -g __fisher_stdout /dev/stdout - else if [ -e /proc/self/fd/1 ] - set -g __fisher_stdout /proc/self/fd/1 - else - echo "No suitable stdout" - return 1 - end + set -g fish_config $XDG_CONFIG_HOME/fish + set -g fisher_cache $XDG_CACHE_HOME/fisher + set -g fisher_config $XDG_CONFIG_HOME/fisher - if [ -e /dev/stderr ] - set -g __fisher_stderr /dev/stderr - else if [ -e /proc/self/fd/2 ] - set -g __fisher_stderr /proc/self/fd/2 - else - echo "No suitable stderr" - return 1 - end + test -z "$fisher_path"; and set -g fisher_path $fish_config - function __fisher_show_spinner - if not set -q __fisher_fg_spinner[1] - set -g __fisher_fg_spinner $fisher_spinners - end + command mkdir -p {$fish_config,$fisher_path}/{functions,completions,conf.d} $fisher_cache - printf " $__fisher_fg_spinner[1]\r" >&2 - - set -e __fisher_fg_spinner[1] - end - - set -l config_home $XDG_CONFIG_HOME - set -l cache_home $XDG_CACHE_HOME - - if test -z "$config_home" - set config_home ~/.config - end - - if test -z "$cache_home" - set cache_home ~/.cache - end - - if test -z "$fish_config" - set -g fish_config "$config_home/fish" - end - - if test -z "$fisher_config" - set -g fisher_config "$config_home/fisherman" - end - - if test -z "$fisher_cache" - set -g fisher_cache "$cache_home/fisherman" - end - - if test -z "$fish_path" - set -g fish_path "$fish_config" - end - - if test -z "$fisher_file" - set -g fisher_file "$fish_path/fishfile" - end - - if test -z "$fisher_copy" - set -g fisher_copy false - end - - switch "$argv[1]" - case --complete - __fisher_complete - return - - case -v --version - __fisher_version - return - - case -h - __fisher_usage >&2 - return - end - - command mkdir -p {"$fish_path","$fish_config"}/{conf.d,functions,completions} "$fisher_config" "$fisher_cache" - or return 1 - - set -l completions "$fish_path/completions/fisher.fish" - - if test ! -e "$completions" - echo "fisher --complete" > "$completions" - __fisher_complete - end - - for i in -q --quiet - if set -l index (builtin contains --index -- $i $argv) - set -e argv[$index] - set __fisher_stdout /dev/null - set __fisher_stderr /dev/null - break - end - end - - set -l cmd - - switch "$argv[1]" - case i install - set -e argv[1] - - if test -z "$argv" - set cmd "default" - else - set cmd "install" - end - - case u up update - set -e argv[1] - set cmd "update" - - case r rm remove uninstall - set -e argv[1] - set cmd "rm" - - case l ls list - set -e argv[1] - set cmd "ls" - - case info ls-remote - set -e argv[1] - set cmd "ls-remote" - - case h help - set -e argv[1] - __fisher_help $argv - return - - case --help - set -e argv[1] - __fisher_help - return - - case -- "" - set -e argv[1] - - if test -z "$argv" - set cmd "default" - else - set cmd "install" - end - - case self-{uninstall,destroy} - set -e argv[1] - __fisher_self_uninstall $argv - return - - case -\*\? - printf "fisher: '%s' is not a valid option\n" "$argv[1]" >&2 - __fisher_usage >&2 - return 1 - - case \* - set cmd "install" - end - - set -l elapsed (__fisher_get_epoch_in_ms) - set -l items ( - if test ! -z "$argv" - printf "%s\n" $argv | __fisher_read_bundle_file - end - ) - - if test -z "$items" -a "$cmd" = "default" - if isatty - command touch "$fisher_file" - - set cmd "install" - set items (__fisher_read_bundle_file < "$fisher_file") - - if test -z "$items" - __fisher_log info " - No plugins to install or dependencies missing. - " >&2 - - __fisher_log info " - See &fisher help& for usage instructions. - " >&2 - return - end - else - set cmd "install" - end + if test ! -e "$fisher_path/completions/fisher.fish" + echo "fisher self-complete" > $fisher_path/completions/fisher.fish + _fisher_self_complete end switch "$cmd" - case install update - if not command -s git > /dev/null - __fisher_log error " - git is required to download plugin repositories. - " >&2 - - __fisher_log info " - Please install git and try again. - Visit <&https://git-scm.com&> for more information. - " >&2 - - return 1 - end - - case ls ls-remote - if not command -s curl > /dev/null - __fisher_log error " - curl is required to query the GitHub API. - " >&2 - - __fisher_log info " - Please install curl and try again. - Refer to your package manager documentation for instructions. - " >&2 - - return 1 - end - end - - switch "$cmd" - case install - if __fisher_install $items - __fisher_log info "Done in &"(__fisher_get_epoch_in_ms $elapsed | __fisher_humanize_duration)"&" "$__fisher_stderr" - end - - case update - if isatty - if test -z "$items" - set items (__fisher_list | command sed 's/^[@* ]*//') - - if not __fisher_self_update - if test -z "$items" - __fisher_log info "Use your package manager to update fisherman." - return 1 - end - end - end - else - __fisher_parse_column_output | __fisher_read_bundle_file | read -laz _items - set items $items $_items - end - - __fisher_update $items - - __fisher_log info "Done in &"(__fisher_get_epoch_in_ms $elapsed | __fisher_humanize_duration)"&" "$__fisher_stderr" - + case self-complete + _fisher_self_complete case ls - if test (count "$argv") -ge 0 -o "$argv" = - - if isatty stdout - __fisher_list | column -c$argv - else - __fisher_list | sed 's|^[@* ]*||' - end - - else - __fisher_list_plugin_directory $argv - end - - case ls-remote - set -l format - - if test ! -z "$argv" - switch "$argv[1]" - case --format\* - set format (printf "%s\n" "$argv[1]" | command sed 's|^--[^= ]*[= ]\(.*\)|\1|') - set -e argv[1] - end - - if test -z "$format" - set format "%info\n%url\n" - end - end - - if test -z "$format" - set format "%name\n" - - if isatty stdout - __fisher_list_remote "$format" $argv | column - else - __fisher_list_remote "$format" $argv - end - else - __fisher_list_remote "$format" $argv - end - - case rm - if test -z "$items" - __fisher_parse_column_output | __fisher_read_bundle_file | read -az items - end - - for i in $items - set -l name (__fisher_plugin_get_names $i)[1] - - if test ! -d "$fisher_config/$name" - set -e items - - if test -L "$fisher_config/$name" - set -l real_path (command readlink "$fisher_config/$name") - - __fisher_log error " - I can't remove &$name& without its real path. - " "$__fisher_stderr" - - __fisher_log info " - Restore &$real_path& and try again. - " "$__fisher_stderr" - else - __fisher_log error "Plugin &$name& is not installed." "$__fisher_stderr" - end - - break - end - end - - if test ! -z "$items" - __fisher_remove $items - __fisher_log info "Done in &"( - __fisher_get_epoch_in_ms $elapsed | __fisher_humanize_duration)"&" "$__fisher_stderr" - end - end - - set -l config_glob $fisher_config/* - set -l config ( - if test ! -z "$config_glob" - command find $config_glob -maxdepth 0 -type d | command sed "s|.*/||" - end - ) - - switch "$cmd" - case ls ls-remote - case \* - if test -z "$config" - echo > "$fisher_file" - set -e fisher_dependency_count - else - __fisher_plugin_get_url_info -- "$fisher_config"/$config > $fisher_file - end - end - - complete -c fisher --erase - - __fisher_complete -end - - -function __fisher_install - if test -z "$argv" - __fisher_read_bundle_file | read -az argv - end - - set -e __fisher_fetch_plugins_state - - if set -l fetched (__fisher_plugin_fetch_items (__fisher_plugin_get_missing $argv)) - if test -z "$fetched" - __fisher_log info " - No plugins to install or dependencies missing. - " "$__fisher_stderr" - - return 1 - end - - for i in $fetched - __fisher_show_spinner - - if test -f "$fisher_config/$i/fishfile" + _fisher_ls | command sed "s|$HOME|~|" + 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 not isatty while read -l i - set -l name (__fisher_plugin_get_names "$i")[1] - - if contains -- "$name" $fetched - if contains -- "$name" $argv - __fisher_plugin_increment_ref_count "$name" - end - else - __fisher_plugin_increment_ref_count "$name" - end - - end < "$fisher_config/$i/fishfile" - end - - __fisher_show_spinner - __fisher_plugin_increment_ref_count "$i" - - set -l path "$fisher_config/$i" - - if __fisher_plugin_is_prompt "$path" - if test ! -z "$fisher_active_prompt" - __fisher_remove "$fisher_active_prompt" - end - - set -U fisher_active_prompt "$i" - end - - __fisher_plugin_enable "$path" - end - else - __fisher_log error " - There was an error installing &$fetched& or more plugin/s. - " "$__fisher_stderr" - - __fisher_log info " - Try using a namespace before the plugin name: &owner&/$fetched - " "$__fisher_stderr" - - return 1 - end -end - - -function __fisher_plugin_fetch_items - __fisher_show_spinner - - set -l jobs - set -l links - set -l white - set -l count (count $argv) - - if test "$count" -eq 0 - return - end - - switch "$__fisher_fetch_plugins_state" - case "" - if test "$count" = 1 -a -d "$argv[1]" - if test "$argv[1]" = "$PWD" - set -l home ~ - set -l name (printf "%s\n" "$argv[1]" | command sed "s|$home|~|") - - __fisher_log info "Installing &$name& " "$__fisher_stderr" - else - set -l name (printf "%s\n" "$argv[1]" | command sed "s|$PWD/||") - - __fisher_log info "Installing &$name& " "$__fisher_stderr" - end - else - __fisher_log info "Installing &$count& plugin/s" "$__fisher_stderr" - end - - set -g __fisher_fetch_plugins_state "fetching" - - case "fetching" - if test "$count" -eq 1 - __fisher_log info "Installing &1& dependency" "$__fisher_stderr" - else - __fisher_log info "Installing &$count& dependencies" "$__fisher_stderr" - end - - set -g __fisher_fetch_plugins_state "done" - - case "done" - end - - for i in $argv - set -l names - set -l branch - - switch "$i" - case \*gist.github.com\* - __fisher_log okay "Resolving gist name." - if not set names (__fisher_get_plugin_name_from_gist "$i") "" - __fisher_log error " - I couldn't clone this gist: - &$i& - " - continue - end - - case \* - printf "%s\n" "$i" | sed 's/[@:]\(.*\)/ \1/' | read i branch - set names (__fisher_plugin_get_names "$i") - end - - if test -d "$i" - command ln -sf "$i" "$fisher_config/$names[1]" - set links $links "$names[1]" - continue - end - - set -l src "$fisher_cache/$names[1]" - - if test -z "$names[2]" - if test -d "$src" -a -z "$branch" - if test ! -d "$fisher_config/$names[1]" - __fisher_log okay "Copy &$names[1]&" "$__fisher_stderr" - end - - if test -L "$src" - command ln -sf "$src" "$fisher_config" - else - command cp -Rf "$src" "$fisher_config" - end - else - set jobs $jobs (__fisher_plugin_url_clone_async "$i" "$names[1]" "$branch") - end - else - if test -d "$src" -a -z "$branch" - set -l real_namespace (__fisher_plugin_get_url_info --dirname "$src") - - if test "$real_namespace" = "$names[2]" - if test ! -d "$fisher_config/$names[1]" - __fisher_log okay "Copy &$names[1]&" "$__fisher_stderr" - end - - command cp -Rf "$src" "$fisher_config" - else - set jobs $jobs (__fisher_plugin_url_clone_async "$i" "$names[1]" "$branch") - end - else - set jobs $jobs (__fisher_plugin_url_clone_async "$i" "$names[1]" "$branch") - end - end - - set fetched $fetched "$names[1]" - end - - __fisher_jobs_await $jobs - - for i in $fetched - if test ! -d "$fisher_cache/$i" - printf "%s\n" "$i" - - for i in $fetched - if test -d "$fisher_config/$i" - command rm -rf "$fisher_config/$i" + set argv $argv $i end end - - return 1 - end - end - - if test ! -z "$fetched" - __fisher_plugin_fetch_items (__fisher_plugin_get_missing $fetched) - printf "%s\n" $fetched - end - - if test ! -z "$links" - __fisher_plugin_fetch_items (__fisher_plugin_get_missing $links) - printf "%s\n" $links - end -end - - -function __fisher_plugin_url_clone_async -a url name branch - switch "$url" - case http://\* https://\* - case {gitlab.com,github.com,bitbucket.org}\* - set url "https://$url" - - case \?\*/\?\* - set url "https://github.com/$url" - + _fisher_commit $argv; or return + _fisher_self_complete case \* - set url "https://github.com/fisherman/$url" - end - - set -l nc (set_color normal) - set -l error (set_color $fish_color_error) - set -l okay (set_color $fish_color_match) - set -l hm_url (printf "%s\n" "$url" | command sed 's|^https://||') - - if test ! -z "$branch" - set hm_url "$hm_url ($branch)" - set branch -b "$branch" - end - - fish -c " - set -lx GIT_ASKPASS /bin/echo - - if test -d '$fisher_cache/$name' - command rm -rf '$fisher_cache/$name' - end - - if command git clone $branch -q --depth 1 '$url' '$fisher_cache/$name' 2> /dev/null - printf '$okay""OK""$nc Fetch $okay%s$nc %s\n' '$name' '$hm_url' > $__fisher_stderr - command cp -Rf '$fisher_cache/$name' '$fisher_config' - else - printf '$error""!""$nc Fetch $error%s$nc %s\n' '$name' '$hm_url' > $__fisher_stderr - end - " >&2 - - __fisher_jobs_get -l -end - - -function __fisher_update - set -l jobs - set -l count (count $argv) - set -l updated - set -l links 0 - - if test "$count" = 0 - return - end - - if test "$count" -eq 1 - __fisher_log info "Updating &$count& plugin" "$__fisher_stderr" - else - __fisher_log info "Updating &$count& plugins" "$__fisher_stderr" - end - - for i in $argv - set -l name (__fisher_plugin_get_names "$i")[1] - set -l path "$fisher_config/$name" - - if test -d "$path" - set updated $updated "$name" - - if test -L "$fisher_config/$name" - set links (math "$links + 1") - continue - end - - set jobs $jobs (__fisher_update_path_async "$name" "$path") - else - __fisher_log error "Skipped &$name&" - end - end - - __fisher_jobs_await $jobs - - set -g __fisher_fetch_plugins_state "fetching" - set -l fetched (__fisher_plugin_fetch_items (__fisher_plugin_get_missing $updated)) - - for i in $updated $fetched - __fisher_plugin_enable "$fisher_config/$i" - end - - if test "$links" -gt 0 - __fisher_log info "Synced &$links& symlink/s" "$__fisher_stderr" - end -end - - -function __fisher_self_update - set -l file (status --current-filename) - - if test "$file" != "$fish_path/functions/fisher.fish" - return 1 - end - - set -l completions "$fish_path/completions/fisher.fish" - set -l raw_url "https://raw.githubusercontent.com/fisherman/fisherman/master/fisher.fish" - set -l fake_qs (date "+%s") - - set -l previous_version "$fisher_version" - - fish -c "curl --max-time 5 -sS '$raw_url?$fake_qs' > $file.$fake_qs" & - - __fisher_jobs_await (__fisher_jobs_get -l) - - if test -s "$file.$fake_qs" - command mv "$file.$fake_qs" "$file" - end - - builtin source "$file" 2> /dev/null - - echo "fisher -v" | source > /dev/null - - set -l new_version "$fisher_version" - - echo "fisher --complete" > "$completions" - builtin source "$completions" 2> /dev/null - - if test "$previous_version" = "$fisher_version" - __fisher_log okay "fisherman is up to date" "$__fisher_stderr" - else - __fisher_log okay "You are running fisherman &$fisher_version&" "$__fisher_stderr" - __fisher_log info "See github.com/fisherman/fisherman/releases" "$__fisher_stderr" - end -end - - -function __fisher_update_path_async -a name path - set -l nc (set_color normal) - set -l error (set_color $fish_color_match) - set -l okay (set_color $fish_color_match) - - fish -c " - - pushd $path - - set -l branch (basename (command git symbolic-ref HEAD 2> /dev/null)) - set -l hm_branch - - if test -z \"\$branch\" - set branch master - end - - if test \"\$branch\" != master - set hm_branch \" (\$branch)\" - end - - if not command git fetch -q origin \$branch 2> /dev/null - printf '$error""!""$nc Fetch $error%s$nc\n' '$name' > $__fisher_stderr - exit - end - - set -l commits (command git rev-list --left-right --count \$branch..FETCH_HEAD 2> /dev/null | cut -d\t -f2) - - command git reset -q --hard FETCH_HEAD 2> /dev/null - command git clean -qdfx - command cp -Rf '$path/.' '$fisher_cache/$name' - - if test -z \"\$commits\" -o \"\$commits\" -eq 0 - printf '$okay""OK""$nc Latest $okay%s$nc%s\n' '$name' \$hm_branch > $__fisher_stderr - else - printf '$okay""OK""$nc Pulled $okay%s$nc new commit/s $okay%s$nc%s\n' \$commits '$name' \$hm_branch > $__fisher_stderr - end - - " >&2 & - - __fisher_jobs_get -l -end - - -function __fisher_plugin_enable -a path - set -l plugin_name (basename $path) - - for file in $path/{functions/*,}*.fish - set -l base (basename "$file") - - if test "$base" = "uninstall.fish" - continue - end - - switch "$base" - case {,fish_{,user_}}key_bindings.fish - __fisher_key_bindings_remove "$plugin_name" - __fisher_key_bindings_append "$plugin_name" "$file" - continue - end - - set -l dir "functions" - - if test "$base" = "init.fish" - set dir "conf.d" - - set base "$plugin_name.$base" - end - - set -l target "$fish_path/$dir/$base" - - if test -e "$target" -a ! -L "$target" - set -l backup_target "$fish_path/$dir/copy-$base" - - __fisher_log info "Backup &$base&" "$__fisher_stderr" - - command mv -f "$target" "$backup_target" 2> /dev/null - end - - if test $fisher_copy = true - command cp -Rf "$file" "$target" - else - command ln -sf "$file" "$target" - end - - builtin source "$target" 2> /dev/null - - if test "$base" = "set_color_custom.fish" - if test ! -s "$fish_path/fish_colors" - __fisher_print_fish_colors > "$fish_path/fish_colors" - end - - set_color_custom - end - end - - for file in $path/{functions/,}*.{py,awk} - set -l base (basename "$file") - if test $fisher_copy = true - command cp -Rf "$file" "$fish_path/functions/$base" - else - command ln -sf "$file" "$fish_path/functions/$base" - end - end - - for file in $path/conf.d/*.{py,awk} - set -l base (basename "$file") - if test $fisher_copy = true - command cp -Rf "$file" "$fish_path/conf.d/$base" - else - command ln -sf "$file" "$fish_path/conf.d/$base" - end - end - - for file in $path/conf.d/*.fish - set -l base (basename "$file") - set -l target "$fish_path/conf.d/$base" - - if test $fisher_copy = true - command cp -Rf "$file" "$target" - else - command ln -sf "$file" "$target" - end - builtin source "$target" 2> /dev/null - end - - for file in $path/completions/*.fish - set -l base (basename "$file") - set -l target "$fish_path/completions/$base" - - if test $fisher_copy = true - command cp -Rf "$file" "$target" - else - command ln -sf "$file" "$target" - end - builtin source "$target" 2> /dev/null - end - - return 0 -end - - -function __fisher_plugin_disable -a path - set -l plugin_name (basename $path) - - for i in "$path/functions/uninstall.fish" "$path/uninstall.fish" - if test -s "$i" - builtin source "$i" 2> /dev/null - break - end - end - - for file in $path/{functions/*,}*.fish - set -l name (basename "$file" .fish) - set -l base "$name.fish" - - if test "$base" = "uninstall.fish" - continue - end - - switch "$base" - case {,fish_}key_bindings.fish - __fisher_key_bindings_remove "$plugin_name" - continue - end - - set -l dir "functions" - - if test "$base" = "init.fish" - set dir "conf.d" - set base "$plugin_name.$base" - end - - set -l target "$fish_path/$dir/$base" - - command rm -f "$target" - - functions -e "$name" - - set -l backup_source "$fish_path/$dir/copy-$base" - - if test -e "$backup_source" - command mv "$backup_source" "$target" - builtin source "$target" 2> /dev/null - end - - if test "$base" = "set_color_custom.fish" - set -l fish_colors_config "$fish_path/fish_colors" - - if test ! -f "$fish_colors_config" - __fisher_reset_default_fish_colors - continue - end - - __fisher_restore_fish_colors < $fish_colors_config | builtin source 2> /dev/null - - command rm -f $fish_colors_config - end - end - - for file in $path/conf.d/*.{py,awk} - set -l base (basename "$file") - command rm -f "$fish_path/conf.d/$base" - end - - for file in $path/{functions/,}*.{py,awk} - set -l base (basename "$file") - command rm -f "$fish_path/functions/$base" - end - - for file in $path/conf.d/*.fish - set -l base (basename "$file") - command rm -f "$fish_path/conf.d/$base" - end - - for file in $path/completions/*.fish - set -l name (basename "$file" .fish) - set -l base "$name.fish" - - command rm -f "$fish_path/completions/$base" - complete -c "$name" --erase - end - - if __fisher_plugin_is_prompt "$path" - set -U fisher_active_prompt - builtin source $__fish_datadir/functions/fish_prompt.fish 2> /dev/null - end - - command rm -rf "$path" >&2 -end - - -function __fisher_remove - if test -z "$argv" - return 1 - end - - set -l orphans - set -l removed - - for i in $argv - set -l name (__fisher_plugin_get_names "$i")[1] - - if test ! -d "$fisher_config/$name" - continue - end - - set removed $removed $name - - __fisher_show_spinner - __fisher_plugin_decrement_ref_count "$name" - - if test -s "$fisher_config/$name/fishfile" - while read -l i - set -l name (__fisher_plugin_get_names "$i")[1] - - if test (__fisher_plugin_get_ref_count "$name") -le 1 - set orphans $orphans "$name" - else - __fisher_plugin_decrement_ref_count "$name" - end - - __fisher_show_spinner - end < "$fisher_config/$name/fishfile" - end - - __fisher_plugin_disable "$fisher_config/$name" - - __fisher_show_spinner - end - - for i in $orphans - __fisher_remove "$i" >&2 - end -end - - -function __fisher_get_plugin_name_from_gist -a url - set -l gist_id (printf "%s\n" "$url" | command sed 's|.*/||') - set -l name (fish -c " - - fisher -v > /dev/null - curl -Ss https://api.github.com/gists/$gist_id & - - __fisher_jobs_await (__fisher_jobs_get -l) - - " | command awk ' - - /"files": / { - files++ - } - - /"[^ ]+.fish": / && files { - gsub("^ *\"|\.fish.*", "") - print - } - - ') - - if test -z "$name" - return 1 - end - - printf "%s\n" $name -end - - -function __fisher_remote_parse_header - command awk ' - - function get_page_count(s, rstart, rlength, pages) { - if (split(substr(s, rstart, rlength), pages, "=")) { - return pages[2] - } - } - - BEGIN { - FS = " <|>; |, <" - } - - /^Link: / { - if (match($2, "&page=[0-9]*")) { - url = substr($2, 1, RSTART - 1) - page_next = get_page_count($2, RSTART, RLENGTH) - - if (page_next) { - page_last = get_page_count($4, RSTART, RLENGTH) - - printf("%s\n%s\n%s", url, page_next, page_last) - } - } - } - - ' -end - - -function __fisher_remote_index_update - set -l index "$fisher_cache/.index" - set -l interval 3240 - set -l players "fisherman" "oh-my-fish" - - if test ! -z "$fisher_index_update_interval" - set interval "$fisher_index_update_interval" - end - - if test -s "$index" - if set -l file_age (__fisher_get_file_age "$index") - if test "$file_age" -lt "$interval" - return - end - end - end - - for i in $players - curl -u$GITHUB_USER:$GITHUB_TOKEN -Is "https://api.github.com/orgs/$i/repos?per_page=100" > "$index-header-$i" & - end - - __fisher_jobs_await (__fisher_jobs_get -l) - - for i in $players - set -l url "https://api.github.com/orgs/$i/repos?per_page=100" - set -l next 0 - set -l last 0 - set -l data - - if test -s "$index-header-$i" - __fisher_remote_parse_header < "$index-header-$i" | read -az data - command rm -f "$index-header-$i" - end - - if set -q data[3] - set -l url "$data[1]" - set -l next "$data[2]" - set -l last "$data[3]" - end - - for page in "" (seq "$next" "$last") - if test "$page" = 0 - continue - end - - set -l next_url "$url" - - if test ! -z "$page" - set next_url "$url&page=$page" - end - - curl -u$GITHUB_USER:$GITHUB_TOKEN --max-time 10 -s "$next_url" | command awk -v ORS='' ' - - { - gsub(/[{}[]]/, "") - - } // - - ' | command awk ' - - { - n = split($0, a, /,[\t ]*"/) - - for (i = 1; i <= n; i++) { - gsub(/"/, "", a[i]) - print(a[i]) - } - } - - ' > "$index-$i-$page" & - end - end - - __fisher_jobs_await (__fisher_jobs_get -l) - - for i in $players - command cat "$index-$i-"* - end > "$index" - - command rm "$index"-* - - if test ! -s "$index" - return 1 - end - - command awk ' - - function quicksort(list, lo, hi, pivot, j, i, t) { - pivot = j = i = t - - if (lo >= hi) { - return - } - - pivot = lo - i = lo - j = hi - - while (i < j) { - while (list[i] <= list[pivot] && i < hi) { - i++ - } - - while (list[j] > list[pivot]) { - j-- - } - - if (i < j) { - t = list[i] - list[i] = list[j] - list[j] = t - } - } - - t = list[pivot] - - list[pivot] = list[j] - list[j] = t - - quicksort(list, lo, j - 1) - quicksort(list, j + 1, hi) - } - - function reset_vars() { - name = info = stars = url = "" - } - - { - if (match($0, /^name:[[:blank:]]*/)) { - name = substr($0, RLENGTH+1) - } - - if (match($0, /^description:[[:blank:]]*/)) { - info = substr($0, RLENGTH+1) - } - - if (match($0, /^stargazers_count:[[:blank:]]*/)) { - stars = substr($0, RLENGTH+1) - } - - if (name != "" && stars != "") { - if (name ~ /oh-my-fish/) { - reset_vars() - next - } - - url = "github.com/fisherman/" name - - if (name ~ /^(plugin|theme)-/) { - gsub(/^plugin-/, "", name) - - if (seen_names[name]) { - reset_vars() - next - } - - url = "github.com/oh-my-fish/" name - name = "omf/" name - - } else { - seen_names[name]++ - } - - if (info == "" || info == "null") { - info = url - } - - if (info ~ /\.$/) { - info = substr(info, 1, length(info) - 1) - } - - records[++record_count] = name "\t" info "\t" url "\t" stars - reset_vars() - } - } - - END { - quicksort(records, 1, record_count) - - for (i = 1; i <= record_count; i++) { - print(records[i]) - } - } - - ' < "$index" > "$index-tab" - - if test ! -s "$index-tab" - command rm "$index" - return 1 - end - - command mv -f "$index-tab" "$index" -end - - -function __fisher_list_remote -a format - set -l index "$fisher_cache/.index" - - if not __fisher_remote_index_update - __fisher_log error "I could not update the remote index." - __fisher_log info " - - This is most likely a problem with http://api.github.com/ - or a connection timeout. If the problem persists, open an - issue in: - " - - return 1 - end - - set -e argv[1] - set -l keys $argv - - command awk -v FS=\t -v format_s="$format" -v keys="$keys" ' - - function basename(s, n, a) { - n = split(s, a, "/") - return a[n] - } - - function record_printf(fmt, name, info, url, stars) { - gsub(/%name/, name, fmt) - gsub(/%stars/, stars, fmt) - gsub(/%url/, url, fmt) - gsub(/%info/, info, fmt) - - printf("%s", fmt) - } - - BEGIN { - keys_n = split(keys, keys_a, " ") - } - - { - if (keys_n > 0) { - for (i = 1; i <= keys_n; i++) { - if (keys_a[i] == $1) { - record_printf(format_s, $1, $2, $3, $4) - next - } - } - } else if ($1 !~ /^fisherman/) { - record_printf(format_s, $1, $2, $3, $4) - } - } - - ' < "$index" -end - - -function __fisher_list - set -l config "$fisher_config"/* - - if test -z "$config" - return 1 - end - - set -l white - set -l links (command find $config -maxdepth 0 -type l ! -name "$fisher_active_prompt" 2> /dev/null) - set -l names (command find $config -maxdepth 0 -type d ! -name "$fisher_active_prompt" 2> /dev/null) - - if test ! -z "$links" - set white " " - printf "%s\n" $links | command sed "s|.*/|@ |" - end - - if test ! -z "$fisher_active_prompt" - set white " " - printf "* %s\n" "$fisher_active_prompt" - end - - if test ! -z "$names" - printf "%s\n" $names | command sed "s|.*/|$white|" - end -end - - -function __fisher_list_plugin_directory - if test -z "$argv" - return 1 - end - - for i in $argv - if test ! -d "$fisher_config/$i" - __fisher_log error "You can only list plugins you've installed." "$__fisher_stderr" - + echo "unknown flag or command \"$cmd\" -- try fisher help" >&2 return 1 - end - end - - set -l fd "$__fisher_stderr" - set -l uniq_items - - for i in $argv - if contains -- "$i" $uniq_items - continue - end - - set uniq_items $uniq_items "$i" - set -l path "$fisher_config/$i" - - pushd "$path" - - set -l color (set_color $fish_color_command) - set -l nc (set_color normal) - set -l previous_tree - - if contains -- --no-color $argv - set color - set nc - set fd "$__fisher_stdout" - end - - printf "$color%s$nc\n" "$PWD" > $fd - - for file in .* ** - if test -f "$file" - switch "$file" - case \*/\* - set -l current_tree (dirname $file) - - if test "$previous_tree" != "$current_tree" - printf " $color%s/$nc\n" $current_tree - end - - printf " %s\n" (basename $file) - - set previous_tree $current_tree - - case \* - printf " %s\n" $file - end - end - end > $fd - - popd end end - -function __fisher_log -a log message fd - if test -z "$argv" - return - end - - switch "$fd" - case "/dev/null" - return - - case "" "/dev/stderr" - set fd $__fisher_stderr - - case \* - set nc "" - set okay "" - set info "" - set error "" - end - - set -l nc (set_color normal) - set -l okay (set_color $fish_color_match) - set -l info (set_color $fish_color_match) - set -l error (set_color $fish_color_error) - - printf "%s\n" "$message" | command awk ' - function okay(s) { - printf("'"$okay"'%s'"$nc"' %s\n", "OK", s) - } - - function info(s) { - printf("%s\n", s) - } - - function error(s) { - printf("'"$error"'%s'"$nc"' %s\n", "!", s) - } - - { - sub(/^[ ]+/, "") - gsub("``", " ") - - if (/&[^&]+&/) { - n = match($0, /&[^&]+&/) - if (n) { - sub(/&[^&]+&/, "'"$$log"'" substr($0, RSTART + 1, RLENGTH - 2) "'"$nc"'", $0) - } - } - - s[++len] = $0 - } - - END { - for (i = 1; i <= len; i++) { - if ((i == 1 || i == len) && (s[i] == "")) { - continue - } - - if (s[i] == "") { - print - } else { - '"$log"'(s[i]) - } - } - } - - ' > "$fd" -end - - -function __fisher_jobs_get - jobs $argv | command awk -v FS=\t ' - /[0-9]+\t/{ - jobs[++job_count] = $1 - } - - END { - for (i = 1; i <= job_count; i++) { - print(jobs[i]) - } - - exit job_count == 0 - } - ' -end - - -function __fisher_jobs_await - if test -z "$argv" - return - end - - while true - for spinner in $fisher_spinners - printf " $spinner \r" >&2 - sleep 0.05 - end - - set -l currently_active_jobs (__fisher_jobs_get) - - if test -z "$currently_active_jobs" - break - end - - set -l has_jobs - - for i in $argv - if builtin contains -- $i $currently_active_jobs - set has_jobs "*" - break - end - end - - if test -z "$has_jobs" - break - end - end -end - - -function __fisher_key_bindings_remove -a plugin_name - set -l user_key_bindings "$fish_path/functions/fish_user_key_bindings.fish" - - if test ! -f "$user_key_bindings" - return - end - - set -l tmp (date "+%s") - - fish_indent < "$user_key_bindings" | command sed -n "/### $plugin_name ###/,/### $plugin_name ###/{s/^ *bind /bind -e /p;};" | builtin source 2> /dev/null - - command sed "/### $plugin_name ###/,/### $plugin_name ###/d" < "$user_key_bindings" > "$user_key_bindings.$tmp" - command mv -f "$user_key_bindings.$tmp" "$user_key_bindings" - - if command awk ' - /^$/ { next } - - /^function fish_user_key_bindings/ { - i++ - next - } - - /^end$/ && 1 == i { - exit 0 - } - - // { - exit 1 - } - - ' < "$user_key_bindings" - - command rm -f "$user_key_bindings" - end -end - - -function __fisher_key_bindings_append -a plugin_name file - set -l user_key_bindings "$fish_path/functions/fish_user_key_bindings.fish" - - command mkdir -p (dirname "$user_key_bindings") - command touch "$user_key_bindings" - - set -l key_bindings_source ( - fish_indent < "$user_key_bindings" | command awk ' - - /^function fish_user_key_bindings/ { - reading_function_source = 1 - next - } - - /^end$/ && reading_function_source { - exit - } - - reading_function_source { - print($0) - next - } - - ' - ) - - set -l plugin_key_bindings_source ( - fish_indent < "$file" | command awk -v name="$plugin_name" ' - - BEGIN { - printf("### %s ###\n", name) - } - - END { - printf("### %s ###\n", name) - } - - /^function (fish_(user_)?)?key_bindings$/ { - is_end = 1 - next - } - - /^end$/ && is_end { - end = 0 - next - } - - !/^ *(#.*)*$/ { - gsub("#.*", "") - printf("%s\n", $0) - } - - ' - ) - - printf "%s\n" $plugin_key_bindings_source | source 2> /dev/null - - fish_indent < "$user_key_bindings" | command awk ' - { - - if ($0 ~ /^function fish_user_key_bindings/) { - reading_function_source = 1 - next - } else if ($0 ~ /^end$/ && reading_function_source) { - reading_function_source = 0 - next - } - - if (!reading_function_source) { - print($0) - } - } - - ' > "$user_key_bindings-copy" - - command mv -f "$user_key_bindings-copy" "$user_key_bindings" - - printf "%s\n" $key_bindings_source $plugin_key_bindings_source | command awk ' - - BEGIN { - print "function fish_user_key_bindings" - } - - // - - END { - print "end" - } - - ' | fish_indent >> "$user_key_bindings" -end - - -function __fisher_plugin_is_prompt -a path - for file in "$path"/{,functions/}{fish_prompt,fish_right_prompt}.fish - if test -e "$file" - return - end - end - - return 1 -end - - -function __fisher_plugin_get_names - printf "%s\n" $argv | command awk ' - - { - sub(/\/$/, "") - n = split($0, s, "/") - sub(/^(omf|omf-theme|omf-plugin|plugin|theme|fish|fisher|fish-plugin|fish-theme)-/, "", s[n]) - - printf("%s\n%s\n", s[n], s[n - 1]) - } - - ' -end - - -function __fisher_plugin_get_url_info -a option - set -e argv[1] - - if test -z "$argv" - return - end - - for dir in $argv - git -C $dir config remote.origin.url 2> /dev/null | command awk -v option="$option" ' - { - n = split($0, s, "/") - - if ($0 ~ /https:\/\/gist/) { - printf("# %s\n", $0) - next - } - - if (option == "--dirname") { - printf("%s\n", s[n - 1]) - - } else if (option == "--basename") { - printf("%s\n", s[n]) - - } else { - printf("%s/%s\n", s[n - 1], s[n]) - } - } - ' - end -end - - -function __fisher_plugin_normalize_path - printf "%s\n" $argv | command awk -v pwd="$PWD" ' - - /^\.$/ { - print(pwd) - next - } - - /^\// { - sub(/\/$/, "") - print($0) - next - } - - { - print(pwd "/" $0) - next - } - - ' -end - - -function __fisher_plugin_get_missing - for i in $argv - if test -d "$i" - set i (__fisher_plugin_normalize_path "$i") - end - - set -l name (__fisher_plugin_get_names "$i")[1] - - if test "$name" = fisherman - - __fisher_log info " - Run &fisher update& to update fisherman. - " >&2 - continue - end - - if set -l path (__fisher_plugin_is_installed "$name") - for file in fishfile bundle - if test -s "$path/$file" - __fisher_plugin_get_missing (__fisher_read_bundle_file < "$path/$file") - end - end - else - printf "%s\n" "$i" - end - end - - __fisher_show_spinner -end - - -function __fisher_plugin_is_installed -a name - if test -z "$name" -o ! -d "$fisher_config/$name" - return 1 - end - - printf "%s\n" "$fisher_config/$name" -end - - -function __fisher_print_fish_colors - printf "%s\n" "$fish_color_normal" "$fish_color_command" "$fish_color_param" "$fish_color_redirection" "$fish_color_comment" "$fish_color_error" "$fish_color_escape" "$fish_color_operator" "$fish_color_end" "$fish_color_quote" "$fish_color_autosuggestion" "$fish_color_user" "$fish_color_valid_path" "$fish_color_cwd" "$fish_color_cwd_root" "$fish_color_match" "$fish_color_search_match" "$fish_color_selection" "$fish_pager_color_prefix" "$fish_pager_color_completion" "$fish_pager_color_description" "$fish_pager_color_progress" "$fish_color_history_current" "$fish_color_host" -end - - -function __fisher_restore_fish_colors - command awk ' - { - if ($0 == "") { - set_option "-e" - } else { - set_option "-U" - } - } - - NR == 1 { - print("set " set_option " fish_color_normal " $0) - } - NR == 2 { - print("set " set_option " fish_color_command " $0) - } - NR == 3 { - print("set " set_option " fish_color_param " $0) - } - NR == 4 { - print("set " set_option " fish_color_redirection " $0) - } - NR == 5 { - print("set " set_option " fish_color_comment " $0) - } - NR == 6 { - print("set " set_option " fish_color_error " $0) - } - NR == 7 { - print("set " set_option " fish_color_escape " $0) - } - NR == 8 { - print("set " set_option " fish_color_operator " $0) - } - NR == 9 { - print("set " set_option " fish_color_end " $0) - } - NR == 10 { - print("set " set_option " fish_color_quote " $0) - } - NR == 11 { - print("set " set_option " fish_color_autosuggestion " $0) - } - NR == 12 { - print("set " set_option " fish_color_user " $0) - } - NR == 13 { - print("set " set_option " fish_color_valid_path " $0) - } - NR == 14 { - print("set " set_option " fish_color_cwd " $0) - } - NR == 15 { - print("set " set_option " fish_color_cwd_root " $0) - } - NR == 16 { - print("set " set_option " fish_color_match " $0) - } - NR == 17 { - print("set " set_option " fish_color_search_match " $0) - } - NR == 18 { - print("set " set_option " fish_color_selection " $0) - } - NR == 19 { - print("set " set_option " fish_pager_color_prefix " $0) - } - NR == 20 { - print("set " set_option " fish_pager_color_completion " $0) - } - NR == 21 { - print("set " set_option " fish_pager_color_description " $0) - } - NR == 22 { - print("set " set_option " fish_pager_color_progress " $0) - } - NR == 23 { - print("set " set_option " fish_color_history_current " $0) - } - NR == 24 { - print("set " set_option " fish_color_host " $0) - } - - ' -end - - -function __fisher_reset_default_fish_colors - set -U fish_color_normal normal - set -U fish_color_command 005fd7 purple - set -U fish_color_param 00afff cyan - set -U fish_color_redirection 005fd7 - set -U fish_color_comment 600 - set -U fish_color_error red --bold - set -U fish_color_escape cyan - set -U fish_color_operator cyan - set -U fish_color_end green - set -U fish_color_quote brown - set -U fish_color_autosuggestion 555 yellow - set -U fish_color_user green - set -U fish_color_valid_path --underline - set -U fish_color_cwd green - set -U fish_color_cwd_root red - set -U fish_color_match cyan - set -U fish_color_search_match --background=purple - set -U fish_color_selection --background=purple - set -U fish_pager_color_prefix cyan - set -U fish_pager_color_completion normal - set -U fish_pager_color_description 555 yellow - set -U fish_pager_color_progress cyan - set -U fish_color_history_current cyan - set -U fish_color_host normal -end - - -function __fisher_read_bundle_file - command awk -v FS=\t ' - /^$/ || /^[ \t]*#/ || /^(--|-).*/ { - next - } - - /^omf\// { - sub(/^omf\//, "oh-my-fish/") - - if ($0 !~ /(theme|plugin)-/) { - sub(/^oh-my-fish\//, "oh-my-fish/plugin-") - } - } - - /^[ \t]*package / { - sub("^[ \t]*package ", "oh-my-fish/plugin-") - } - - { - sub(/\.git$/, "") - sub("^[@* \t]*", "") - - if (!dedupe[$0]++) { - printf("%s\n", $0) - } - } - ' -end - - -function __fisher_plugin_increment_ref_count -a name - set -U fisher_dependency_count $fisher_dependency_count $name -end - - -function __fisher_plugin_decrement_ref_count -a name - if set -l i (contains --index -- "$name" $fisher_dependency_count) - set -e fisher_dependency_count[$i] - end -end - - -function __fisher_plugin_get_ref_count -a name - printf "%s\n" $fisher_dependency_count | command awk -v plugin="$name" ' - - BEGIN { - i = 0 - } - - $0 == plugin { - i++ - } - - END { - print(i) - } - - ' -end - - -function __fisher_complete - complete -xc fisher -n "__fish_use_subcommand" -a install -d "Install plugins" - complete -xc fisher -n "__fish_use_subcommand" -a update -d "Update plugins and self" - complete -xc fisher -n "__fish_use_subcommand" -a rm -d "Remove plugins" - complete -xc fisher -n "__fish_use_subcommand" -a ls -d "List what you've installed" - complete -xc fisher -n "__fish_use_subcommand" -a ls-remote -d "List everything that's available" - complete -xc fisher -n "__fish_use_subcommand" -a help -d "Show help" - - complete -xc fisher -n "__fish_use_subcommand" -s h -l help -d "Show usage help" - complete -xc fisher -n "__fish_use_subcommand" -s v -l version -d "Show version information" - complete -xc fisher -s q -l quiet -d "Enable quiet mode" - - complete -xc fisher -n "__fish_seen_subcommand_from ls-remote" -l "format" -d "Format with verbs: %name, %url, %info and %stars" - - set -l config_glob "$fisher_config"/* - set -l config (printf "%s\n" $config_glob | command sed "s|.*/||") - - if test ! -s "$fisher_cache/.index" - if test ! -z "$config" - complete -xc fisher -n "__fish_seen_subcommand_from l ls list u up update r rm remove h help" -a "$config" - complete -xc fisher -n "__fish_seen_subcommand_from l ls list u up update r rm remove h help" -a "$fisher_active_prompt" -d "Prompt" - end - return - end - - set -l real_home ~ - - for name in (command find $config_glob -maxdepth 0 -type l 2> /dev/null) - set -l path (command readlink "$name") - set -l name (command basename "$name" | sed "s|$real_home|~|") - - complete -xc fisher -n "__fish_seen_subcommand_from l ls list u up update r rm remove h help" -a "$name" -d "$path" - end - - set -l IFS \t - - command awk -v FS=\t -v OFS=\t ' - - { - print($1, $2) - } - - ' "$fisher_cache/.index" 2> /dev/null | while read -l name info - - switch "$name" - case fisherman\* - continue - end - - complete -xc fisher -n "__fish_seen_subcommand_from info ls-remote" -a "$name" -d "$info" - - if contains -- "$name" $config - complete -xc fisher -n "__fish_seen_subcommand_from l ls list u up update r rm remove h help" -a "$name" -d "$info" - else - complete -xc fisher -n "__fish_seen_subcommand_from i in install" -a "$name" -d "$info" - end - end - - if functions -q __fisher_plugin_get_url_info - for i in (__fisher_plugin_get_url_info -- $config_glob) - switch "$i" - case fisherman\* - case \* - set -l name (__fisher_plugin_get_names "$i")[1] - complete -xc fisher -n "__fish_seen_subcommand_from l ls list u up update r rm remove h help" -a "$name" -d "$i" - end - end - end -end - - -function __fisher_humanize_duration - command awk ' - function hmTime(time, stamp) { - split("h:m:s:ms", units, ":") - - for (i = 2; i >= -1; i--) { - if (t = int( i < 0 ? time % 1000 : time / (60 ^ i * 1000) % 60 )) { - stamp = stamp t units[sqrt((i - 2) ^ 2) + 1] " " - } - } - - if (stamp ~ /^ *$/) { - return "0ms" - } - - return substr(stamp, 1, length(stamp) - 1) - } - - { - print hmTime($0) - } - ' -end - -function __fisher_get_key - stty -icanon -echo 2> /dev/null - printf "$argv" >&2 - while true - dd bs=1 count=1 2> /dev/null | read -p "" -l yn - switch "$yn" - case y Y n N - printf "\n" >&2 - printf "%s\n" $yn > /dev/stdout - break - end - end - stty icanon echo > /dev/stderr 2> /dev/null -end - - -switch (command uname) - case Darwin FreeBSD - function __fisher_get_epoch_in_ms -a elapsed - if test -z "$elapsed" - set elapsed 0 - end - - if command -s perl > /dev/null - perl -MTime::HiRes -e 'printf("%.0f\n", (Time::HiRes::time() * 1000) - '$elapsed')' - else - math (command date "+%s") - $elapsed - end - end - - case \* - function __fisher_get_epoch_in_ms -a elapsed - if test -z "$elapsed" - set elapsed 0 - end - math (command date "+%s%3N") - $elapsed - end -end - - -function __fisher_parse_column_output - command awk -v FS=\t ' - { - for (i = 1; i <= NF; i++) { - if ($i != "") { - print $i - } - } - } - ' -end - - -function __fisher_get_file_age -a file - if type -q perl - perl -e "printf(\"%s\n\", time - (stat ('$file'))[9])" 2> /dev/null - - else if type -q python - python -c "from __future__ import print_function; import os, time; print(int(time.time() - os.path.getmtime('$file')))" 2> /dev/null - end -end - - -function __fisher_usage - set -l u (set_color -u) - set -l nc (set_color normal) - - echo "Usage: fisher [COMMAND] [PLUGINS]" - echo - echo "where COMMAND is one of:" - echo " "$u"i"$nc"nstall (default)" - echo " "$u"u"$nc"pdate" - echo " "$u"r"$nc"m" - echo " "$u"l"$nc"s (or ls-remote [--format=FORMAT])" - echo " "$u"h"$nc"elp" -end - - -function __fisher_version - set -l real_home ~ - printf "fisherman version $fisher_version %s\n" ( - __fisher_plugin_normalize_path (status -f) | command sed "s|$real_home|~|;s|$__fish_datadir|\$__fish_datadir|") -end - -function __fisher_help -a cmd number - if test -z "$argv" - set -l page "$fisher_cache/fisher.1" - - if test ! -s "$page" - __fisher_man_page_write > "$page" - end - - if [ "$PREFIX" = "/data/data/com.termux/files/usr" ] - - mandoc -a "$page" - - else - - set -l pager "/usr/bin/less -s" - - if test ! -z "$PAGER" - set pager "$PAGER" - end - - man -P "$pager" -- "$page" - - end - - command rm -f "$page" - - else - if test -z "$number" - set number 1 - end - - set -l page "$fisher_config/$cmd/man/man$number/$cmd.$number" - - if not man "$page" 2> /dev/null - if test -d "$fisher_config/$cmd" - __fisher_log info "There's no manual for this plugin." "$__fisher_stderr" - - set -l url (__fisher_plugin_get_url_info -- "$fisher_config/$cmd") - - __fisher_log info "Try online: <&github.com/$url&>" "$__fisher_stderr" - else - __fisher_log error "This plugin is not installed." "$__fisher_stderr" - end - - return 1 - end - end -end - - -function __fisher_self_uninstall -a yn - set -l file (status --current-filename) - set -l u (set_color -u) - set -l nc (set_color normal) - - switch "$yn" - case -y --yes - case \* - __fisher_log info " - This will permanently remove fisherman from your system. - The following directories and files will be erased: - - $fisher_cache - $fisher_config - $fish_config/functions/fisher.fish - $fish_config/completions/fisher.fish - - " >&2 - - echo -sn "Continue? [Y/n] " >&2 - - __fisher_get_key | read -l yn - - switch "$yn" - case n N - set -l username - - if test ! -z "$USER" - set username " $USER" - end - - __fisher_log okay "As you wish cap!" - return 1 - end - 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_seen_subcommand_from rm" -a $pkg + end +end - __fisher_show_spinner +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/||" +end - echo "fisher ls | fisher rm -q" | source 2> /dev/null +function _fisher_version -a file + echo "fisher version $fisher_version $file" | command sed "s|$HOME|~|" +end - __fisher_show_spinner +function _fisher_help + echo "usage: fisher add add packages" + echo " fisher rm remove packages" + echo " fisher ls list installed packages" + echo " fisher self-update update fisher" + echo " fisher self-uninstall uninstall fisher and all packages" + echo " fisher help show this help" + echo " fisher version show version" + echo + echo "examples:" + echo " fisher add jethrokuan/z rafaelrinaldi/pure" + echo " fisher add gitlab.com/owner/foobar@v2" + echo " fisher add ~/myfish/mypkg" + echo " fisher rm rafaelrinaldi/pure" + echo " fisher ls | fisher rm" +end - command rm -rf "$fisher_cache" "$fisher_config" - command rm -f "$fish_config"/{functions,completions}/fisher.fish "$fisher_file" +function _fisher_self_update -a file + set -l url "https://raw.githubusercontent.com/jorgebucaran/fisher/master/fisher.fish" + echo "fetching $url" >&2 - set -e fish_config - set -e fish_path - set -e fisher_active_prompt + curl -s "$url?nocache" >$file@ + + set -l next_version (awk 'NR == 1 { print $4; exit }' < $file@) + switch "$next_version" + case "" $fisher_version + command rm -f $file@ + if test -z "$next_version" + echo "cannot update fisher -- are you offline?" >&2 + return 1 + 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 fisher to $fisher_version -- hooray!" >&2 + end +end + +function _fisher_self_uninstall + printf "removing %s\n" $fisher_config $fisher_cache $fish_config/fishfile $fisher_path/{functions,completions}/fisher.fish | command sed "s|$HOME|~|" + _fisher_pkg_remove_all $fisher_config/*/*/* >/dev/null + command rm -rf $fisher_config $fisher_cache 2>/dev/null + command rm $fisher_path/{functions,completions}/fisher.fish $fish_config/fishfile 2>/dev/null set -e fisher_cache set -e fisher_config - set -e fisher_file + set -e fisher_path set -e fisher_version - set -e fisher_spinners - - __fisher_log info "Done." "$__fisher_stderr" - - set -l funcs (functions -a | command grep __fisher) - - functions -e $funcs fisher + complete -c fisher --erase + functions -e (functions -a | command awk '/^_fisher/') fisher end +function _fisher_commit + set -l elapsed (_fisher_now) + set -l fishfile $fish_config/fishfile + set -l added_pkgs + set -l updated_pkgs + set -l removed_pkgs -function __fisher_man_page_write - echo '. -.TH "FISHERMAN" "1" "May 2016" "" "fisherman" -. -.SH "NAME" -\fBfisherman\fR \- fish plugin manager -. -.SH "SYNOPSIS" -'"fisher"' [(\fBi\fRnstall | \fBu\fRpdate | \fBl\fRs[\-remote] | \fBr\fRm | \fBh\fRelp) PLUGINS] -. -.br -. -.SH "DESCRIPTION" -A plugin manager for fish\. -. -.SH "OPTIONS" -. -.IP "\(bu" 4 -\-v, \-\-version: Show version information\. -. -.IP "\(bu" 4 -\-h, \-\-help: Show usage help\. Use the long form to display this page\. -. -.IP "\(bu" 4 -\-q, \-\-quiet: Enable quiet mode\. Use to suppress output\. -. -.IP "" 0 -. -.SH "USAGE" -Install a plugin\. -. -.IP "" 4 -. -.nf + if test -z "$cmd" -a ! -e "$fishfile" + echo "fishfile not found -- need help? try fisher help" | command sed "s|$HOME|~|" >&2 + return 1 + end + command touch $fishfile -'"fisher"' mono -. -.fi -. -.IP "" 0 -. -.P -Install some plugins\. -. -.IP "" 4 -. -.nf + _fisher_fishfile_indent (echo -s $argv\;) < $fishfile > $fishfile@ -'"fisher"' z fzf edc/bass omf/tab -. -.fi -. -.IP "" 0 -. -.P -Install a specific branch\. -. -.IP "" 4 -. -.nf + command mv -f $fishfile@ $fishfile + command rm -f $fishfile@ -'"fisher"' edc/bass:master -. -.fi -. -.IP "" 0 -. -.P -Install a specific tag\. -. -.IP "" 4 -. -.nf + set removed_pkgs (_fisher_pkg_remove_all $fisher_config/*/*/*) + command rm -rf $fisher_config + command mkdir -p $fisher_config -'"fisher"' done@1.2.0 -. -.fi -. -.IP "" 0 -. -.P -Install a gist\. -. -.IP "" 4 -. -.nf + set added_pkgs (_fisher_pkg_fetch_all (_fisher_fishfile_load < $fishfile)) + set 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) -'"fisher"' https://gist\.github\.com/username/1f40e1c6e0551b2666b2 -. -.fi -. -.IP "" 0 -. -.P -Install a local directory\. -. -.IP "" 4 -. -.nf - -'"fisher"' ~/my/plugin -. -.fi -. -.IP "" 0 -. -.P -Edit your \fIfishfile\fR and run \fB'"fisher"'\fR to commit changes\. -. -.IP "" 4 -. -.nf - -$EDITOR ~/\.config/fish/fishfile -'"fisher"' -. -.fi -. -.IP "" 0 -. -.P -Show everything you\'ve installed\. -. -.IP "" 4 -. -.nf - -'"fisher"' ls -@ plugin # a local directory -* mono # the current prompt - bass - fzf - thefuck - z -. -.fi -. -.IP "" 0 -. -.P -Show everything that\'s available\. -. -.IP "" 4 -. -.nf - -'"fisher"' ls\-remote -. -.fi -. -.IP "" 0 -. -.P -Update everything\. -. -.IP "" 4 -. -.nf - -'"fisher"' up -. -.fi -. -.IP "" 0 -. -.P -Update some plugins\. -. -.IP "" 4 -. -.nf - -'"fisher"' up bass z fzf -. -.fi -. -.IP "" 0 -. -.P -Remove plugins\. -. -.IP "" 4 -. -.nf - -'"fisher"' rm thefuck -. -.fi -. -.IP "" 0 -. -.P -Remove all the plugins\. -. -.IP "" 4 -. -.nf - -'"fisher"' ls | '"fisher"' rm -. -.fi -. -.IP "" 0 -. -.P -Get help\. -. -.IP "" 4 -. -.nf - -'"fisher"' help z -. -.fi -. -.IP "" 0 -. -.SH "FAQ" -. -.SS "What is the required fish version?" ->=2\.2\.0\. -. -.P -For \fIsnippet\fR support, upgrade to >=2\.3\.0 or append the following code to your \fI~/\.config/fish/config\.fish\fR\. -. -.IP "" 4 -. -.nf - -for file in ~/\.config/fish/conf\.d/*\.fish - source $file + if test ! -z "$added_pkgs$updated_pkgs$removed_pkgs" + echo (count $added_pkgs) (count $updated_pkgs) (count $removed_pkgs) (_fisher_now $elapsed) | _fisher_status_report >&2 + end end -. -.fi -. -.IP "" 0 -. -.SS "Is fisherman compatible with oh\-my\-fish themes and plugins?" -Yes\. -. -.SS "Where does fisherman put stuff?" -The cache and configuration go in \fI~/\.cache/fisherman\fR and \fI~/\.config/fisherman\fR respectively\. -. -.P -The fishfile is saved to \fI~/\.config/fish/fishfile\fR\. -. -.SS "How do I have fisherman copy plugin files instead of linking?" -By default, fisherman will create symlinks to plugin files. -. -.P -To have fisherman copy files: -. -.IP "" 4 -. -.nf= -set -U fisher_copy true -. -.fi -. -.IP "" 0 -. -.SS "What is a fishfile and how do I use it?" -The fishfile \fI~/\.config/fish/fishfile\fR lists what plugins you\'ve installed\. -. -.P -This file is updated automatically as you install / remove plugins. You can also edit this file and run \fBfisher\fR to commit changes\. -. -.P -This mechanism only installs plugins and missing dependencies\. To remove plugins, use \fBfisher rm\fR\. -. -.SS "What is a plugin?" -A plugin is: -. -.IP "1." 4 -a directory or git repo with one or more \fI\.fish\fR functions either at the root level of the project or inside a \fIfunctions\fR directory -. -.IP "2." 4 -a theme or prompt, i\.e, a \fIfish_prompt\.fish\fR, \fIfish_right_prompt\.fish\fR or both files -. -.IP "3." 4 -a snippet, i\.e, one or more \fI\.fish\fR files inside a directory named \fIconf\.d\fR, evaluated by fish at the start of the session -. -.IP "" 0 -. -.SS "How can I list plugins as dependencies to my plugin?" -Create a new \fIfishfile\fR file at the root level of your project and write in the plugins\.' +function _fisher_pkg_remove_all + for pkg in $argv + echo $pkg + _fisher_pkg_uninstall $pkg + end +end + +function _fisher_pkg_fetch_all + set -l pkg_jobs + set -l local_pkgs + set -l actual_pkgs + set -l fetched_pkgs + + for name in $argv + switch $name + case \~\* /\* + set -l path (echo "$name" | command sed "s|~|$HOME|") + if test -e "$path" + set local_pkgs $local_pkgs $path + else + echo "cannot install $name -- is this a valid file?" >&2 + end + continue + case https://\* ssh://\* {github,gitlab}.com/\* bitbucket.org/\* + case \* + set name "github.com/$name" + end + + echo $name | command awk '{ + split($0, tmp, /@/) + + pkg = tmp[1] + tag = tmp[2] ? tmp[2] : "master" + name = tmp[split(pkg, tmp, "/")] + + print (\ + pkg ~ /^github\.com/ ? "https://codeload."pkg"/tar.gz/"tag : \ + pkg ~ /^gitlab\.com/ ? "https://"pkg"/-/archive/"tag"/"name"-"tag".tar.gz" : \ + pkg ~ /^bitbucket\.org/ ? "https://"pkg"/get/"tag".tar.gz" : pkg \ + ) "\t" pkg + }' | read -l url pkg + + fish -c " + echo fetching $url >&2 + command mkdir -p \"$fisher_config/$pkg\" + + if curl -Ss $url 2>&1 | 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/..\" + else if test -d \"$fisher_cache/$pkg\" + echo cannot connect to server -- using data from \"$fisher_cache/$pkg\" | command sed 's|$HOME|~|' >&2 + command cp -Rf \"$fisher_cache/$pkg\" \"$fisher_config/$pkg/..\" + else + command rm -rf \"$fisher_config/$pkg\" + echo cannot install \"$pkg\" -- are you offline\? >&2 + end + " >/dev/null & + + set pkg_jobs $pkg_jobs (_fisher_jobs --last) + set fetched_pkgs $fetched_pkgs "$pkg" + end + + if test ! -z "$pkg_jobs" + _fisher_wait $pkg_jobs + for pkg in $fetched_pkgs + if test -d "$fisher_config/$pkg" + set actual_pkgs $actual_pkgs $pkg + _fisher_pkg_install $fisher_config/$pkg + end + end + end + + for pkg in $local_pkgs + set -l path "local/$USER" + set -l name (echo "$pkg" | command sed 's|^.*/||') + + 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 + end + + if test ! -z "$actual_pkgs" + printf "%s\n" $actual_pkgs + _fisher_pkg_fetch_all (_fisher_pkg_get_deps $actual_pkgs | command sort --unique) + end +end + +function _fisher_pkg_get_deps + for pkg in $argv + set -l path $fisher_config/$pkg + if test ! -d "$path" + echo $pkg + else if test -s "$path/fishfile" + _fisher_pkg_get_deps (_fisher_fishfile_indent < $path/fishfile | _fisher_fishfile_load) + end + end +end + +function _fisher_pkg_install -a pkg + set -l name (echo $pkg | command sed "s|^.*/||") + for source in $pkg/{functions,completions,conf.d,}/*.fish + set -l target (echo "$source" | command sed 's|^.*/||') + switch $source + 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.fish key_bindings.fish + set target $fisher_path/conf.d/$name\_$target + case \* + set target $fisher_path/functions/$target + end + end + echo "linking $target" | command sed "s|$HOME|~|" >&2 + command ln -f $source $target + source $target >/dev/null 2>/dev/null + end +end + +function _fisher_pkg_uninstall -a pkg + set -l name (echo $pkg | command sed "s|^.*/||") + for source in $pkg/{conf.d,completions,functions,}/*.fish + set -l target (echo "$source" | command sed 's|^.*/||') + set -l filename (echo "$target" | command sed 's|.fish||') + switch $source + case $pkg/conf.d\* + emit {$filename}_uninstall + command rm -f $fisher_path/conf.d/$target + case $pkg/completions\* + command rm -f $fisher_path/completions/$target + complete -ec $filename + case $pkg/{,functions}\* + switch $target + case uninstall.fish + source $source + continue + case init.fish key_bindings.fish + set target $fisher_path/conf.d/$name\_$target + case \* + set target $fisher_path/functions/$target + end + command rm -f $target + functions -e $filename + end + end + if not functions -q fish_prompt + source "$__fish_datadir/functions/fish_prompt.fish" + end +end + +function _fisher_fishfile_indent -a pkgs + command awk -v PWD=$PWD -v HOME=$HOME -v PKGS="$pkgs" ' + function normalize(s) { + gsub(/^[ \t]*|[ \t]*$|^https?:\/\/|github\.com\/|\.git$|\/$/, "", s) + sub(/^\.\//, PWD"/", s) + sub(HOME, "~", s) + return s + } + function get_pkg_name(s) { + split(s, tmp, /[@# ]+/) + return tmp[1] + } + BEGIN { + pkg_count = split(PKGS, pkgs, ";") - 1 + cmd = pkgs[1] + for (i = 2; i <= pkg_count; i++) { + pkg_ids[i - 1] = get_pkg_name( pkgs[i] = normalize(pkgs[i]) ) + } + } { + if (NF) { + nl = nl > 0 ? "" : nl + pkg_id = get_pkg_name( $0 = normalize($0) ) + if (/^#/) print nl$0 + else if (!seen[pkg_id]++) { + for (i = 1; i < pkg_count; i++) { + if (pkg_ids[i] == pkg_id) { + if (cmd == "rm") next + $0 = pkgs[i + 1] + break + } + } + print nl$0 + } + nl = NF + } else if (nl) nl = (nl > 0 ? "" : nl)"\n" + } + END { + if (cmd == "rm" || pkg_count <= 1) exit + for (i = 2; i <= pkg_count; i++) { + if (!seen[pkg_ids[i - 1]]) print pkgs[i] + } + } + ' +end + +function _fisher_fishfile_load + command awk -v FS=\# '!/^#/ && NF { print $1 }' +end + +function _fisher_status_report + command awk ' + function msg(res, str, n) { + return (res ? res ", " : "") str " " n " package" (n > 1 ? "s" : "") + } + $1 = $1 - $2 { res = msg(res, "added", $1) } + $2 { res = msg(res, "updated", $2) } + $3 = $3 - $2 { res = msg(res, "removed", $3) } + { printf((res ? res : "done") " in %.2fs\n", ($4 / 1000)) } + ' +end + +function _fisher_jobs + jobs $argv | command awk -v FS=\t ' + /[0-9]+\t/ { + jobs[++n] = $1 + } END { + for (i in jobs) print(jobs[i]) + exit n == 0 + } + ' +end + +function _fisher_wait + while for job in $argv + contains -- $job (_fisher_jobs); and break + end + end end