commit e4b455d4c32c78a0bca657ab19aaedac6d537792 Author: Jorge Bucaran Date: Sat Jan 2 06:12:40 2016 +0900 Ahoy! Fisherman and Happy New Year 2016. 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=