From a1087b0be372391ecb20543848738cfccb18880d Mon Sep 17 00:00:00 2001 From: Jorge Bucaran Date: Sat, 2 Jan 2016 06:12:40 +0900 Subject: [PATCH] Ahoy! Fisherman and Happy New Year 2016. --- .editorconfig | 7 + .gitignore | 1 + .travis.yml | 7 + AUTHORS.md | 2 + CHANGELOG.md | 7 + CONTRIBUTING.md | 38 ++++ FAQ.md | 0 LICENSE | 21 ++ Makefile | 72 ++++++ README.md | 42 ++++ VERSION | 1 + completions/fisher.fish | 46 ++++ completions/getopts.fish | 1 + completions/wait.fish | 10 + config.fish | 11 + functions/__fish_parse_usage.fish | 43 ++++ functions/fisher.fish | 161 +++++++++++++ functions/fisher_help.fish | 109 +++++++++ functions/fisher_install.fish | 159 +++++++++++++ functions/fisher_search.fish | 173 ++++++++++++++ functions/fisher_uninstall.fish | 129 +++++++++++ functions/fisher_update.fish | 149 ++++++++++++ functions/getopts.fish | 33 +++ functions/man.fish | 4 + functions/wait.fish | 131 +++++++++++ man/man1/fisher-help.1 | 91 ++++++++ man/man1/fisher-help.md | 60 +++++ man/man1/fisher-install.1 | 136 +++++++++++ man/man1/fisher-install.md | 71 ++++++ man/man1/fisher-search.1 | 249 ++++++++++++++++++++ man/man1/fisher-search.md | 143 ++++++++++++ man/man1/fisher-uninstall.1 | 72 ++++++ man/man1/fisher-uninstall.md | 48 ++++ man/man1/fisher-update.1 | 66 ++++++ man/man1/fisher-update.md | 42 ++++ man/man1/fisher.1 | 135 +++++++++++ man/man1/fisher.md | 75 ++++++ man/man1/fisherman.1 | 1 + man/man1/getopts.1 | 145 ++++++++++++ man/man1/getopts.md | 108 +++++++++ man/man1/wait.1 | 137 +++++++++++ man/man1/wait.md | 89 ++++++++ man/man5/fisher-fishfile.5 | 61 +++++ man/man5/fisher-fishfile.md | 41 ++++ man/man7/fisher-commands.7 | 82 +++++++ man/man7/fisher-commands.md | 52 +++++ man/man7/fisher-config.7 | 80 +++++++ man/man7/fisher-config.md | 58 +++++ man/man7/fisher-faq.7 | 189 +++++++++++++++ man/man7/fisher-faq.md | 146 ++++++++++++ man/man7/fisher-plugins.7 | 215 +++++++++++++++++ man/man7/fisher-plugins.md | 126 ++++++++++ man/man7/fisher.7 | 329 +++++++++++++++++++++++++++ man/man7/fisher.md | 199 ++++++++++++++++ package.json | 42 ++++ test/fisher.fish | 50 ++++ test/fixtures/fishfile | 8 + test/getopts.fish | 112 +++++++++ test/help.fish | 40 ++++ test/helpers/create_mock_source.fish | 28 +++ test/install.fish | 66 ++++++ test/other.fish | 19 ++ test/search.fish | 141 ++++++++++++ test/uninstall.fish | 120 ++++++++++ test/update-self.fish | 36 +++ test/update.fish | 64 ++++++ test/validate.fish | 63 +++++ test/wait.fish | 37 +++ 68 files changed, 5429 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 AUTHORS.md create mode 100644 CHANGELOG.md create mode 100644 CONTRIBUTING.md create mode 100644 FAQ.md create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 VERSION create mode 100644 completions/fisher.fish create mode 100644 completions/getopts.fish create mode 100644 completions/wait.fish create mode 100644 config.fish create mode 100644 functions/__fish_parse_usage.fish create mode 100644 functions/fisher.fish create mode 100644 functions/fisher_help.fish create mode 100644 functions/fisher_install.fish create mode 100644 functions/fisher_search.fish create mode 100644 functions/fisher_uninstall.fish create mode 100644 functions/fisher_update.fish create mode 100644 functions/getopts.fish create mode 100644 functions/man.fish create mode 100644 functions/wait.fish create mode 100644 man/man1/fisher-help.1 create mode 100644 man/man1/fisher-help.md create mode 100644 man/man1/fisher-install.1 create mode 100644 man/man1/fisher-install.md create mode 100644 man/man1/fisher-search.1 create mode 100644 man/man1/fisher-search.md create mode 100644 man/man1/fisher-uninstall.1 create mode 100644 man/man1/fisher-uninstall.md create mode 100644 man/man1/fisher-update.1 create mode 100644 man/man1/fisher-update.md create mode 100644 man/man1/fisher.1 create mode 100644 man/man1/fisher.md create mode 120000 man/man1/fisherman.1 create mode 100644 man/man1/getopts.1 create mode 100644 man/man1/getopts.md create mode 100644 man/man1/wait.1 create mode 100644 man/man1/wait.md create mode 100644 man/man5/fisher-fishfile.5 create mode 100644 man/man5/fisher-fishfile.md create mode 100644 man/man7/fisher-commands.7 create mode 100644 man/man7/fisher-commands.md create mode 100644 man/man7/fisher-config.7 create mode 100644 man/man7/fisher-config.md create mode 100644 man/man7/fisher-faq.7 create mode 100644 man/man7/fisher-faq.md create mode 100644 man/man7/fisher-plugins.7 create mode 100644 man/man7/fisher-plugins.md create mode 100644 man/man7/fisher.7 create mode 100644 man/man7/fisher.md create mode 100644 package.json create mode 100644 test/fisher.fish create mode 100644 test/fixtures/fishfile create mode 100644 test/getopts.fish create mode 100644 test/help.fish create mode 100644 test/helpers/create_mock_source.fish create mode 100644 test/install.fish create mode 100644 test/other.fish create mode 100644 test/search.fish create mode 100644 test/uninstall.fish create mode 100644 test/update-self.fish create mode 100644 test/update.fish create mode 100644 test/validate.fish create mode 100644 test/wait.fish diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..79a5fca --- /dev/null +++ b/.editorconfig @@ -0,0 +1,7 @@ +root = true +[*] +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e43b0f9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Store diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..fd29abc --- /dev/null +++ b/.travis.yml @@ -0,0 +1,7 @@ +sudo: required +before_install: + - sudo add-apt-repository -y ppa:fish-shell/release-2 + - sudo apt-get update + - sudo apt-get -y install fish +script: + - make && fish -c "fisher install fishtape; fishtape test/*.fish" diff --git a/AUTHORS.md b/AUTHORS.md new file mode 100644 index 0000000..e430612 --- /dev/null +++ b/AUTHORS.md @@ -0,0 +1,2 @@ +# Authors +* Jorge Bucaran <[j@bucaran.me](mailto:j@bucaran.me)> diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..fa1178d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,7 @@ +# Changelog + +* [0.1.0](#010) + +## 0.1.0 - 2016-01-01 + +* Initial commit. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..99b8b98 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,38 @@ +# Contributing to Fisherman + +You can start contributing right away: + +* [Join][join] the community. +* [Spread][tweet] the voice. +* [Bugs][bugs]? :beetle: + +If you are looking for other ways to help, peruse [open issues][issues]. If you are already working on something, feel free to send us a PR. + +## Guidelines + +* Fork the repo and create your feature branch from master. + +* Test before you push. Get familiar with [Fishtape][fishtape]. + +* If you've changed APIs, update the documentation. + +* Follow the [seven rules][seven-rules] of a great git commit message. + + +## Addons + +Third-party plugins are essential for keeping this project exciting. To learn how to create your own plugins and other extensions see `fisher help plugins`. + +To browse the available content use `fisher search` or see the [Fisherman Index][fisher-index]. + + + +[org]: https://github.com/fisherman +[fishtape]: https://github.com/fisherman/fishtape +[join]: https://gitter.im/fisherman/wharf +[bugs]: https://github.com/fisherman/fisherman/issues +[issues]: https://github.com/fisherman/fisherman/issues?q=is%3Aopen+is%3Aissue +[fish-docs]: http://fishshell.com/docs/current/index.html +[seven-rules]: http://chris.beams.io/posts/git-commit/#seven-rules +[tweet]: https://twitter.com/intent/tweet?url=https%3A%2F%2Fgit.io%2Ffisher&via=jbucaran&text=Check+out+%23Fisherman+for+the+%23fishshell +[fisher-index]: https://github.com/fisherman/fisher-index diff --git a/FAQ.md b/FAQ.md new file mode 100644 index 0000000..e69de29 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..1a6e9b2 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Jorge Bucaran + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ab52f9f --- /dev/null +++ b/Makefile @@ -0,0 +1,72 @@ +SHELL:=/bin/bash -O nullglob + +XDG_CONFIG_HOME ?= $$HOME/.config +FISH_CONFIG := $(XDG_CONFIG_HOME)/fish/config.fish + +FISHER_HOME := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) +FISHER_CONFIG := $(XDG_CONFIG_HOME)/fisherman +FISHER_CACHE := $(FISHER_CONFIG)/cache + +MAN := $(FISHER_HOME)/man +MAN1 := $(wildcard $(MAN)/man1/*.md) +MAN5 := $(wildcard $(MAN)/man5/*.md) +MAN7 := $(wildcard $(MAN)/man7/*.md) +DOCS := $(MAN1:%.md=%.1) $(MAN5:%.md=%.5) $(MAN7:%.md=%.7) + +AUTHORS = $(FISHER_HOME)/AUTHORS.md +VERSION = `cat $(FISHER_HOME)/VERSION` + +MSG = printf "\033[47m\033[30m%s\033[0m\n" $(1) +TILDEIFY = sed "s|$$HOME|~|" + +.PHONY: all test flush uninstall release + +all: $(FISH_CONFIG) $(FISHER_CACHE) $(AUTHORS) $(DOCS) + @$(call MSG,"Reset your shell to apply changes") + +test: + @fish -c "fishtape test/*.fish" + +flush: + @rm -rf $(FISHER_CONFIG)/* + +uninstall: + @echo "Removing configuration from $(FISH_CONFIG)" | $(TILDEIFY) + @sed -E '/set (fisher_home|fisher_config) /d;/source \$$fisher_home/d' \ + $(FISH_CONFIG) > $(FISH_CONFIG).tmp + @mv $(FISH_CONFIG).tmp $(FISH_CONFIG) + @$(call MSG,"Reset your shell to apply changes") + +release: $(FISHER_HOME) + @if [ "`git -C $^ status --short --porcelain | xargs`" = "M VERSION" ]; then\ + echo "`git -C $^ describe --abbrev=0 2>/dev/null || echo \*` -> $(VERSION)";\ + sed "s/fisherman-v.\..\..-00B9FF/fisherman-v$(VERSION)-00B9FF/" $^/README.md > $^/README.md.swap;\ + mv $^/README.md.swap $^/README.md;\ + git -C $^ add README.md;\ + git -C $^ add $^/VERSION;\ + git -C $^ commit --quiet -m $(VERSION);\ + git -C $^ tag $(VERSION) -m v$(VERSION) --force >/dev/null;\ + else\ + echo "Commit changes and update VERSION to tag a new release.";\ + fi + +$(FISH_CONFIG): + @echo "Adding configuration to $@" | $(TILDEIFY) + @mkdir -p $(dir $@) && touch $@ + @echo "set fisher_home $(FISHER_HOME)" | sed "s|/$$||;s|$$HOME|~|" > $@.fisher + @echo "set fisher_config $(FISHER_CONFIG)" | sed "s|$$HOME|~|" >> $@.fisher + @echo "source \$$fisher_home/config.fish" >> $@.fisher + @awk '!config[$$0]++' $@.fisher $@ > $@.tmp + @mv $@.tmp $@ && rm $@.fisher + +$(FISHER_CACHE): + @[ -d $@ ] || echo "Creating $@" | $(TILDEIFY) + @mkdir -p $@ + +$(AUTHORS): $(FISHER_HOME) + @echo "# Authors" > $@ + @git -C $^ shortlog -sne | cut -f2- | \ + sed -E 's/([^<>]+)<([^<>]*)>/* \1 \<[\2](mailto:\2)\>/' >> $@ + +%.1 %.5 %.7: %.md + @ronn --manual=fisherman --roff $? 2>&1 | sed 's/^ *roff: *//;s|//|/|g' diff --git a/README.md b/README.md new file mode 100644 index 0000000..605952c --- /dev/null +++ b/README.md @@ -0,0 +1,42 @@ +

+ + Fisherman + +

+ + +[![Build Status][travis-badge]][travis-link] +![Fisherman Version][fisherman-version] + +## About + +Fisherman is a shell manager for [fish][fish] that lets you share and reuse code, prompts and configurations easily. See [FAQ][faq]. + +## Install + +```fish +git clone https://github.com/fisherman/fisherman +cd fisherman +make +``` + +## Contributing + +Check out the [contribution](CONTRIBUTING.md) guidelines. + +## Help + +See [`fisherman(1)`][fisherman-1] and [`fisherman(7)`][fisherman-7] for usage and documentation. For support and feedback join the [Gitter room][wharf] or browse the [issues][issues]. + + + + +[fish]: https://github.com/fish-shell/fish-shell +[faq]: https://github.com/fisherman/fisherman/wiki/FAQ +[issues]: http://github.com/fisherman/fisherman/issues +[wharf]: https://gitter.im/fisherman/wharf +[fisherman-1]: man/man1/fisher.md +[fisherman-7]: man/man7/fisher.md +[travis-link]: https://travis-ci.org/fisherman/fisherman +[travis-badge]: https://img.shields.io/travis/fisherman/fisherman.svg?style=flat-square +[fisherman-version]: https://img.shields.io/badge/fisherman-v0.1.0-00B9FF.svg?style=flat-square diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..77d6f4c --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.0.0 diff --git a/completions/fisher.fish b/completions/fisher.fish new file mode 100644 index 0000000..d638a3b --- /dev/null +++ b/completions/fisher.fish @@ -0,0 +1,46 @@ +complete -c fisher -n "__fish_use_subcommand" -s v -l version -d "Show version information" +complete -c fisher -n "__fish_use_subcommand" -s h -l help -d "Display help" +complete -c fisher -n "__fish_use_subcommand" -s f -l file -d "Read fishfile" + +if test -e $fisher_cache/.index + set -l cache (for file in $fisher_cache/* + if test -d $file + basename $file + end + end) + + awk -v FS='\n' -v RS='' -v OFS=' ' '/^ *#/ { next } { print $1,$3 }' $fisher_cache/.index \ + | while read -l name info + switch "$name" + case $cache + for cmd in update uninstall + complete -c fisher -n "__fish_seen_subcommand_from $cmd" -a "$name" -d "$info" + end + + case \* + complete -c fisher -n "__fish_seen_subcommand_from install" -a "$name" -d "$info" + end + end +end + +for option in commands guides + set -l IFS ";" + + fisher help --$option=bare | sed -E 's/^ *([^ ]+) *(.*)/\1;\2/' | while read -l func info + + complete -c fisher -n "__fish_seen_subcommand_from help" -a $func -d "$info" + + if test $option = guides + continue + end + + complete -c fisher -n "__fish_use_subcommand" -a $func -d "$info" + + fisher_help --usage=$func | __fish_parse_usage OFS=';' | while read -l 1 2 3 + complete -c fisher -n "__fish_seen_subcommand_from $func" -s "$3" -l "$2" -d "$1" + end + end +end + +complete -c fisher -n "__fish_seen_subcommand_from search" -a "\t" +complete -xc fisher -d "Fisherman" diff --git a/completions/getopts.fish b/completions/getopts.fish new file mode 100644 index 0000000..5b117b6 --- /dev/null +++ b/completions/getopts.fish @@ -0,0 +1 @@ +complete -xc getopts -d "Parse CLI options" -a "\t" diff --git a/completions/wait.fish b/completions/wait.fish new file mode 100644 index 0000000..e09df34 --- /dev/null +++ b/completions/wait.fish @@ -0,0 +1,10 @@ +set -l spinners arc star pipe ball flip mixer caret bar1 bar2 bar3 +complete -xc wait -n "__fish_seen_subcommand_from --spin" -a "$spinners" + +complete -xc wait -d "Run commands and wait with a spin" +complete -xc wait -n "not __fish_seen_subcommand_from --spin" -a "\t" + +set -l IFS \t +wait --help | __fish_parse_usage | while read -l 1 2 3 + complete -c wait -s "$3" -l "$2" -d "$1" +end diff --git a/config.fish b/config.fish new file mode 100644 index 0000000..4c8ad33 --- /dev/null +++ b/config.fish @@ -0,0 +1,11 @@ +set fisher_cache $fisher_config/cache +set fisher_index https://raw.githubusercontent.com/fisherman/fisher-index/master/INDEX + +set fisher_error_log $fisher_cache/.debug_log + +set fish_function_path {$fisher_config,$fisher_home}/functions $fish_function_path +set fish_complete_path {$fisher_config,$fisher_home}/completions $fish_complete_path + +for file in $fisher_config/functions/*.config.fish + source $file +end diff --git a/functions/__fish_parse_usage.fish b/functions/__fish_parse_usage.fish new file mode 100644 index 0000000..cce00dd --- /dev/null +++ b/functions/__fish_parse_usage.fish @@ -0,0 +1,43 @@ +function __fish_parse_usage -d "Parse usage help output" + switch "$argv[1]" + case -h --help + printf "Usage\n" + printf " __fish_parse_usage [awk_var=value] [--help]\n\n" + + printf "Example\n" + printf " set -l IFS \\\t\n" + printf " STDIN | __fish_parse_usage | while read -l d l s\n" + printf " complete -c -s $s -l $l -d $d\n" + printf " end\n" + return + end + + switch "$argv" + case \*OFS=\* + case \* + set argv $argv OFS=\t + end + + awk (printf "-v\n%s\n" $argv) ' + /^ *--?[A-Za-z0-9-]+[, ]?/ { + re = "[<=\\\[]" + for (n = 1; n <= NF; n++) { + + if ($n ~ /^--/ && !long) { + long = substr($n, 3) + split(long, _, re) + long = substr(_[1], 1) + + } else if ($n ~ /^-.,?$/ && !short) { + short = substr($n, 2, 1) + + } else if ($n !~ re) { + info = (info ? info" " : info)$n + } + } + + print info, long, short + info = short = long = "" + } + ' +end diff --git a/functions/fisher.fish b/functions/fisher.fish new file mode 100644 index 0000000..d3cf035 --- /dev/null +++ b/functions/fisher.fish @@ -0,0 +1,161 @@ +function fisher -d "Fish Shell Manager" + if not set -q argv[1] + fisher --help + return 1 + end + + set -l option + set -l value + set -l quiet -E . + + getopts $argv | while read -l 1 2 + switch "$1" + case _ + set option command + set value $2 + break + + case h help + set option help + set value $value $2 + + case path-in-cache + set option path-in-cache + set value $2 + + case f file + set option file + set value $2 + + case V validate + set option validate + set value $2 + + case v version + set option version + + case q quiet + set quiet -q . + + case \* + printf "fisher: '%s' is not a valid option\n" $1 >& 2 + fisher --help >& 2 + return 1 + end + end + + switch "$option" + case command + printf "%s\n" $fisher_alias | sed 's/[=,]/ /g' | while read -la alias + if set -q alias[2] + switch "$value" + case $alias[2..-1] + set value $alias[1] + break + end + end + end + + if not functions -q "fisher_$value" + printf "fisher: '%s' is not a valid command\n" "$value" >& 2 + fisher --help >& 2 | head -n1 >& 2 + return 1 + end + + set -e argv[1] + + if not eval "fisher_$value" (printf "%s\n" "'"$argv"'") + return 1 + end + + case validate + if not test -z "$value" + printf "%s\n" $value | fisher --validate | grep $quiet + return + end + + if not set -q fisher_default_host + set fisher_default_host https://github.com + end + + set -l id "[A-Za-z0-9_-]" + + sed -En " + s#/\$## + s#\.git\$## + s#^(https?):*/* *(.*\$)#\1://\2#p + s#^(@|(gh:)|(github(.com)?[/:]))/?($id+)/($id+)\$#https://github.com/\5/\6#p + s#^(bb:)/?($id+)/($id+)\$#https://bitbucket.org/\2/\3#p + s#^(gl:)/?($id+)/($id+)\$#https://gitlab.com/\2/\3#p + s#^($id+)/($id+)\$#$fisher_default_host/\1/\2#p + /^file:\/\/\/.*/p + /^[a-z]+([._-]?[a-z0-9]+)*\$/p + " | grep $quiet + + case path-in-cache + while read --prompt= -l item + switch "$item" + case \*/\* + for file in $fisher_cache/* + switch "$item" + case (git -C $file ls-remote --get-url | fisher --validate) + printf "%s\n" $file + break + end + end + + case \* + printf "%s\n" $fisher_cache/$item + end + end + + case file + switch "$value" + case "-" + set value /dev/stdin + case "" + set value $fisher_config/fishfile + end + + awk ' + !/^ *(#.*)*$/ { + gsub("#.*", "") + + if (/^ *package .+/) { + $1 = $2 + } + + if (!k[$1]++) { + printf("%s\n", $1) + } + } + ' $value + + case version + sed 's/^/fisher version /;q' $fisher_home/VERSION + + case help + if test -z "$value" + set value commands + end + + printf "usage: fisher [] [--version] [--help]\n\n" + + switch commands + case $value + printf "Available Fisherman commands:\n" + fisher_help --commands=bare + echo + end + + switch guides + case $value + printf "Available Fisherman guides:\n" + fisher_help --guides=bare + echo + end + + printf "Use 'fisher help -g' to list guides and other documentation.\n" + printf "See 'fisher help ' to access a man page.\n" + end +end diff --git a/functions/fisher_help.fish b/functions/fisher_help.fish new file mode 100644 index 0000000..dbd4c31 --- /dev/null +++ b/functions/fisher_help.fish @@ -0,0 +1,109 @@ +function fisher_help -d "Display Help Information" + if not set -q argv[1] + fisher --help + return 1 + end + + set -l option + set -l value + + getopts $argv | while read -l 1 2 + switch "$1" + case _ + set option manual + set value $2 + + case a all + set option guides commands + set value $2 + + case g guides + set option $option guides + set value $2 + + case commands + set option $option commands + set value $2 + + case u usage + set option usage + set value $value $2 + + case h help + printf "usage: fisher help [] [--all] [--guides]\n" + printf " [--usage[=]] [--help]\n\n" + + printf " -a --all List commands and guides \n" + printf " -g --guides List documentation guides \n" + printf " -u --usage[=] Display command usage help \n" + printf " -h --help Show usage help \n" + return + + case \* + printf "fisher: '%s' is not a valid option\n" $1 >& 2 + fisher_help --help >& 2 + return 1 + end + end + + if not set -q option[1] + set option commands + end + + switch "$option" + case manual + switch "$value" + case fisherman + man $value 7 + case fisher me @ + man fisher + case \* + man fisher-$value + end + + case usage + if test -z "$value" + sed -E 's/^ *([^ ]+).*/\1/' | while read -l value + if functions -q fisher_$value + fisher $value --help + end + end + else + printf "%s\n" $value | fisher_help --usage + end + + case \* + switch "$value" + case bare + case \* + fisher --help=$option + return + end + + switch commands + case $option + functions -a | grep '^fisher_[^_]*$' | while read -l f + functions $f | awk ' + /^$/ { next } + { + printf(" %s\t", substr($2, 8)) + gsub("\'","") + + for (i=4; i<=NF && $i!~/^--.*/; i++) { + printf("%s ", $i) + } + + print "" + exit + }' + end | column -ts\t + end + + switch guides + case $option + sed -nE 's/(fisher-)?(.+)\([0-9]\) -- (.+)/ \2'\t'\3/p' \ + {$fisher_home,$fisher_config}/man/man{5,7}/fisher*.md | sort -r + + end | column -ts\t + end +end diff --git a/functions/fisher_install.fish b/functions/fisher_install.fish new file mode 100644 index 0000000..f87ffe7 --- /dev/null +++ b/functions/fisher_install.fish @@ -0,0 +1,159 @@ +function fisher_install -d "Enable / Install Plugins" + set -l items + set -l error /dev/stderr + + getopts $argv | while read -l 1 2 + switch "$1" + case _ + set items $items $2 + + case q quiet + set error /dev/null + + case h help + printf "usage: fisher install [ ...] [--quiet] [--help]\n\n" + + printf " -q --quiet Enable quiet mode\n" + printf " -h --help Show usage help\n" + return + + case \* + printf "fisher: '%s' is not a valid option\n" $1 >& 2 + fisher_install --help >& 2 + return 1 + end + end + + set -l count 0 + set -l duration (date +%s) + set -l total (count $items) + + if set -q items[1] + printf "%s\n" $items + else + fisher --file=- + + end | fisher --validate | while read -l item + + switch "$item" + case \*/\* + printf "%s %s\n" $item ( + if not fisher_search --url=$item --field=name + printf "%s" $item | sed -E ' + s/.*\/(.*)/\1/ + s/^(plugin|theme|pkg|fish)-//' + end) + + case \* + if set -l url (fisher_search --name=$item --field=url) + printf "%s %s\n" $url $item + + else if test -d $fisher_cache/$item + printf "%s %s\n" (git -C $fisher_cache/$item ls-remote --get-url) $item + + else + printf "fisher: '%s' not found\n" $item > $error + end + end + + end | while read -l url name + + printf "Installing " > $error + + switch $total + case 0 1 + printf ">> %s\n" $name > $error + + case \* + printf "(%s of %s) >> %s\n" (math 1 + $count) $total $name > $error + end + + mkdir -p $fisher_config/{cache,functions,completions,man} + + set -l path $fisher_cache/$name + + if not test -e $path + if not wait --spin=pipe --log=$fisher_error_log " + git clone --quiet --depth 1 $url $path" + + printf "fisher: Repository not found: '%s'\n" $url > $error + continue + end + end + + set -l bundle $path/fishfile + + if test -e $path/bundle + set bundle $path/bundle + end + + if test -e $bundle + printf "Resolving dependencies in %s\n" $name/(basename $bundle) > $error + + set -l deps (fisher --file=$bundle \ + | fisher_install ^&1 | sed -En 's/([0-9]+) plugin\/s.*/\1/p') + + set count (math $count + 0$deps) + end + + if test -s $path/Makefile -o -s $path/makefile + pushd $path + if not make > /dev/null ^ $error + popd + printf "fisher: Can't make '%s'\n" $name > $error + continue + end + popd + end + + if test -e $path/fish_prompt.fish -o -e $path/fish_right_prompt.fish + rm -f $fisher_config/functions/{fish_prompt,fish_right_prompt}.fish + end + + for file in $path/{*,functions{/*,/**/*}}.fish + set -l base (basename $file) + switch $base + case {init,uninstall}.fish + set base $name.config.$base + end + cp -f $file $fisher_config/functions/$base + end + + source $fisher_config/functions/$name.fish > /dev/null ^& 1 + + cp -f $path/completions/*.fish $fisher_config/completions/ ^ /dev/null + + for n in (seq 9) + if test -d $path/man/man$n + mkdir -p $fisher_config/man/man$n + end + cp -f $path/man/man$n/*.$n $fisher_config/man/man$n/ ^ /dev/null + end + + set count (math $count + 1) + + set -l file $fisher_config/fishfile + + if test -s $file + if fisher --file=$file | grep -Eq "^$name\$|^$url\$" + continue + end + end + + touch $file + + if not fisher_search --name=$name --field=name --quiet + set name $url + end + + printf "%s\n" "$name" >> $file + + end + + printf "%d plugin/s installed (%0.fs)\n" (math $count) ( + math (date +%s) - $duration) > $error + + if not test $count -gt 0 + return 1 + end +end diff --git a/functions/fisher_search.fish b/functions/fisher_search.fish new file mode 100644 index 0000000..02a993d --- /dev/null +++ b/functions/fisher_search.fish @@ -0,0 +1,173 @@ +function fisher_search -d "Search Fisherman Index" + set -l index $fisher_cache/.index + + set -l select all + set -l fields + set -l join "||" + set -l query + set -l quiet 0 + + getopts $argv | while read -l 1 2 3 + switch "$1" + case _ name url info author tag{,s} + switch "$1" + case _ + switch "$2" + case \*/\* + set 1 url + + set -l url (fisher --validate=$2) + if not test -z "$url" + set 2 $url + end + + case \* + set 1 name + end + + case tag{,s} + set 1 "find(tags, \"$2\")" + if test -z "$2" + set 1 "show(tags)" + end + end + + switch "$2" + case "" + set fields $fields $1 + continue + + case {~,!~}\* + set 2 "$3$2" + + case \?\* + if test "$3" = ! + set 2 "!=\"$2\"" + else + set 2 "==\"$2\"" + end + end + + set query "$query$join$1$2" + + case s select + set select $2 + + case f field{,s} + switch "$2" + case T tag{,s} + set 2 "show(tags)" + end + set fields $fields $2 + + case a and + set join "&&" + + case o or + set join "||" + + case Q query + set query $query $2 + + case q quiet + set quiet 1 + + case h help + printf "usage: fisher search [] [--select=] [--field=]\n" + printf " [--or|--and] [--quiet] [--help]\n\n" + + printf " -s --select= Select all, cache or remote plugins \n" + printf " -f --field= Filter by name, url, info, tag or author \n" + printf " -a --and Join query with AND operator \n" + printf " -o --or Join query with OR operator \n" + printf " -q --quiet Enable quiet mode \n" + printf " -h --help Show usage help \n" + return + + case \* + printf "fisher: '%s' is not a valid option\n" $1 >& 2 + fisher_search --help >& 2 + return 1 + end + end + + if not set -q fields[1] + set fields '$0' + end + + set fields (printf "%s\n" $fields | paste -sd, -) + set query (printf "%s\n" $query | sed -E 's/^[\|&]+//') + + switch "$select" + case all + if not fisher_update --quiet --index + if test -s $fisher_index + set index $fisher_index + end + end + + if not cat $index ^ /dev/null + printf "fisher: '%s' invalid path or url\n" $index >& 2 + return 1 + end + + case remote + fisher_search -a --name!=( + fisher_search --field=name --select=cache) + + case cache + set -l names (for file in $fisher_cache/* + if test -d $file + basename $file + end + end) + + if test -z "$names" + return 1 + end + + fisher_search --select=all --name=$names + + end | awk -F'\n' -v RS='' -v OFS=';' ( + if test "$fields" = '$0' + printf "%s\nORS=%s" -v '\\n\\n' + end) " + + function find(array, item) { + for (i in array) { + if (array[i] == item) { + return item + } + } + } + + function show(array) { + for (i in array) { + printf(\"%s \", array[i]) + } + } + + /^ *#/ { next } + + { + delete tags + if (\$4) { + split(\$4, tags, \" \") + } + + name = \$1 + url = \$2 + info = \$3 + author = \$5 + } + + $query { + print $fields + } + " | sed '${/^$/d;}' | awk -v q=$quiet ' + !/^ *$/ { ret = 1 } + !q { print } + q && !ret { exit !ret } + END { exit !ret } + ' +end diff --git a/functions/fisher_uninstall.fish b/functions/fisher_uninstall.fish new file mode 100644 index 0000000..7acc52d --- /dev/null +++ b/functions/fisher_uninstall.fish @@ -0,0 +1,129 @@ +function fisher_uninstall -d "Disable / Uninstall Plugins" + set -l option + set -l items + set -l error /dev/stderr + + getopts $argv | while read -l 1 2 + switch "$1" + case _ + set items $items $2 + + case a all + set option $option all + + case f force + set option $option force + + case q quiet + set error /dev/null + + case help h + printf "usage: fisher uninstall [ ...] [--all] [--force] [--quiet]\n" + printf " [--help]\n\n" + + printf " -a --all Uninstall all plugins \n" + printf " -f --force Delete copy from cache \n" + printf " -q --quiet Enable quiet mode \n" + printf " -h --help Show usage help \n" + return + + case \* + printf "fisher: '%s' is not a valid option\n" $1 >& 2 + fisher_uninstall --help >& 2 + return 1 + end + end + + set -l count 0 + set -l duration (date +%s) + set -l total (count $items) + + switch all + case $option + set -l cache (find $fisher_cache/* -maxdepth 0 -type d) + set total (count $cache) + printf "%s\n" $cache + + case \* + if set -q items[1] + printf "%s\n" $items + else + fisher --file=- + + end | fisher --validate | fisher --path-in-cache + + end | while read -l path + + if not test -d "$path" + printf "fisher: '%s' not found\n" $path > $error + continue + end + + set -l name (basename $path) + + printf "Uninstalling " > $error + + switch $total + case 0 1 + printf ">> %s\n" $name > $error + + case \* + printf "(%s of %s) >> %s\n" (math 1 + $count) $total $name > $error + end + + set count (math $count + 1) + + functions -e $name + + for file in $path/{*,functions{/*,/**/*}}.fish + set -l base (basename $file) + + switch $base + case {init,before.init,uninstall}.fish + set base $name.$base + end + + rm -f $fisher_config/functions/$base + end + + for file in $path/completions/*.fish + rm -f $fisher_config/completions/(basename $file) + end + + for n in (seq 9) + if test -d $path/man/man$n + for file in $path/man/man$n/*.$n + rm -f $fisher_config/man/man$n/(basename $file) + end + end + end + + git -C $path ls-remote --get-url ^ /dev/null | fisher --validate | read -l url + + switch force + case $option + rm -rf $path + end + + set -l file $fisher_config/fishfile + + if not fisher --file=$file | grep -Eq "^$name\$|^$url\$" + continue + end + + set -l tmp (mktemp) + + if not sed -E '/^ *'(printf "%s|%s" $name $url | sed 's|/|\\\/|g' + )'([ #].*)*$/d' < $file > $tmp + rm -f $tmp + printf "fisher: can't delete '%s' from %s\n" $name $file > $error + return 1 + end + + mv -f $tmp $file + end + + printf "%d plugin/s uninstalled (%0.fs)\n" $count (math (date +%s) - $duration) > $error + + test $count -gt 0 +end diff --git a/functions/fisher_update.fish b/functions/fisher_update.fish new file mode 100644 index 0000000..2e0bd32 --- /dev/null +++ b/functions/fisher_update.fish @@ -0,0 +1,149 @@ +function fisher_update -d "Fisherman Update Manager" + set -l option + set -l path + set -l items + set -l error /dev/stderr + + getopts $argv | while read -l 1 2 + switch "$1" + case _ + set items $items $2 + + case index + set option $option index + + case s {,it,my,one}self fisher{,man} + set option $option self + + case c cache + set option $option cache + + case path + set option path + set path $2 + + case q quiet + if test -z "$2" + set 2 /dev/null + end + set error $2 + + case help h + printf "usage: fisher update [ ...] [--self] [--cache]\n" + printf " [--path=] [--quiet] [--help]\n\n" + + printf " -s --self Update Fisherman \n" + printf " -c --cache Update cached plugins \n" + printf " --path= Update repository at given path \n" + printf " -q --quiet Enable quiet mode \n" + printf " -h --help Show usage help \n" + return + + case \* + printf "fisher: '%s' is not a valid option\n" $1 >& 2 + fisher_update --help >& 2 + return 1 + end + end + + switch path + case $option + if not test -d $path + printf "fisher: '%s' invalid path\n" $path > $error + return 1 + end + + wait --spin=pipe --log=$fisher_error_log " + git -C $path checkout --quiet master ^/dev/null + git -C $path pull --quiet --rebase origin master" + + return + end + + switch index + case $option + mkdir -p $fisher_cache + set -l index $fisher_cache/.(random) + + if not wait --spin=pipe --log=$fisher_error_log "curl -sS $fisher_index > $index" + rm -f $index + cat $fisher_error_log > $error + + return 1 + end + + mv -f $index $fisher_cache/.index + end + + switch self + case $option + set -l elap (date +%s) + + printf "Updating >> Fisherman\n" > $error + + if not fisher_update --path=$fisher_home --quiet=$error + return 1 + end + + printf "Done without errors (%0.fs)\n" (math (date +%s) - $elap) > $error + end + + set -l elap (date +%s) + set -l count 0 + set -l total (count $items) + + switch cache + case $option + set -l cache (find $fisher_cache/* -maxdepth 0 -type d) + set total (count $cache) + printf "%s\n" $cache + + case \* + if set -q items[1] + printf "%s\n" $items + else + + if contains -- $option self + return + end + + if contains -- $option index + return + end + + fisher --file=- + end | fisher --validate | fisher --path-in-cache + + end | while read -l path + + if not test -d "$path" + printf "fisher: '%s' not found\n" $path > $error + continue + end + + set -l name (basename $path) + + printf "Updating " > $error + + switch $total + case 0 1 + printf ">> %s\n" $name > $error + + case \* + printf "(%s of %s) >> %s\n" (math 1 + $count) $total $name > $error + end + + if not fisher_update --path=$path --quiet=$error + sed -nE 's/.*(error|fatal): (.*)/error: \2/p' $fisher_error_log > $error + continue + end + + fisher install $name --quiet + + set count (math $count + 1) + end + + printf "%d plugin/s updated (%0.fs)\n" $count (math (date +%s) - $elap) > $error + + test $count -gt 0 +end diff --git a/functions/getopts.fish b/functions/getopts.fish new file mode 100644 index 0000000..7d04010 --- /dev/null +++ b/functions/getopts.fish @@ -0,0 +1,33 @@ +function getopts -d "Parse CLI options" + if not set -q argv[1] + return 1 + end + + printf "%s\n" $argv | sed -E ' + s/^-([A-Za-z]+)/- \1 / + s/^--([A-Za-z0-9_-]+)(!?)=?(.*)/-- \1 \3 \2 /' | \ + awk ' + function add(k, v) { if (seen[k v]++) return; print k (v == "" ? "" : " "v) } + function pop() { return size <= 0 ? "_" : stack[size--] } + function push(k) { stack[++size] = k } + function flush() { while (size) add(pop()) } + + $0 == "" { next } + done { add("_" , $1$2$3); next } + + $1 == "--" && !$2 { done = 1 ; next } + $1 !~ /^-|^--/ { add( pop(), $0 ) ; next } + + size { flush() } + $3 { for (i = 4; i <= NF; i++) $3 = $3" "$i } + + $1 == "--" { if ($3 == "") push($2); else add($2, $3) } + $1 == "-" { + if ($2 == "") { print $1; next } else n = split($2, keys, "") + if ($3 == "") push(keys[n]); else add(keys[n], $3) + for (i = 1; i < n; i++) add(keys[i]) + } + + END { flush() } + ' +end diff --git a/functions/man.fish b/functions/man.fish new file mode 100644 index 0000000..fc89591 --- /dev/null +++ b/functions/man.fish @@ -0,0 +1,4 @@ +function man -d "Format and display manual pages" + set -lx MANPATH {$__fish_datadir,$fisher_config,$fisher_home}/man $MANPATH "" + command man $argv +end diff --git a/functions/wait.fish b/functions/wait.fish new file mode 100644 index 0000000..b8b5099 --- /dev/null +++ b/functions/wait.fish @@ -0,0 +1,131 @@ +function wait -d "Run commands and wait with a spin" + set -l commands + set -l spinners + set -l time 0.03 + set -l log + set -l format "@\r" + + getopts $argv | while read -l 1 2 + switch "$1" + case - + case _ + set commands $commands ";$2" + + case s spin spinner{,s} style + set spinners $spinners $2 + + case t time + set time $2 + + case l log + set log $2 + + case f format + set format $2 + + case h help + printf "usage: wait [--spin=