Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 27 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,46 +1,57 @@
### mfunc
#### a function wrapper plugin for ZSH
# mfunc

Allows you to define persistent functions on-the-fly, without the need to add
them to your config files. Your functions are permanently available until you
delete them.
A function wrapper plugin for ZSH.

Define, view, edit and delete persistent functions on-the-fly, without extra steps. They are permanently available until you delete them.

The plugin defines 3 functions:

| `Command` | Action
|-------------------------|----------------------------------------
|-------------------------|-----------------------------------------
| `mfunc name [name] ...` | create new function(s) interactively
| `rfunc name [name] ...` | delete existing user-defined function(s)
| `lfunc` | list all user-defined functions
| `lfunc [-h\|v]` | list all user-defined functions

functions are stored as plain text in $ZSH/functions/ and made available via
Functions are stored as plain text in `$MFUNDCIR` and made available via
the autoload builtin (i.e. they are only loaded into memory when called for the
first time).

#### Installation
## Installation

######a) As an oh-my-zsh plugin
### a) As an oh-my-zsh plugin
1. Run:
`cd $ZSH/custom && git clone https://github.com/hlohm/mfunc.git plugins/mfunc`
`cd $ZSH/custom && git clone https://github.com/amogus07/mfunc.git plugins/mfunc`

2. Add `mfunc` to your plugins in your `.zshrc`. The relevant line should
look something like this:
`plugins=(git mfunc)`

######b) using antigen
### b) using antigen
1. Add this line where you load your antigen bundles in your `.zshrc`:

`antigen bundle hlohm/mfunc`
######c) with vanilla ZSH
`antigen bundle amogus07/mfunc`
### c) with vanilla ZSH
1. `git clone` this repo to a location of your choice

2. add a line `source /location/of/your/choice/mfunc.plugin.zsh` to your .zshrc

Upon its first run the plugin will notify you that it created the directory in
which it stores your functions.

#### Disclaimer
## Configuration

| Environment Variable | Default | Description
|----------------------|------------------------------------------|------------------
| `$MFUNCDIR` | `${ZDOTDIR/functions:-$HOME/.functions}` | functions storage

## Optional dependencies

| Dependency | Description
|-------------------------------------|-----------------------------------------------------------
| [highlight](https://repology.org/project/highlight) | enables syntax highlighting for `lfunc -v`

## Disclaimer

This is an early version covering only the most basic functionality. There are
no safeguards whatsover, so use at you own risk. Things like tab completion,
no safeguards whatsover, so use at you own risk. Things like tab completion,
input sanitization and the like are on the TODO list.
9 changes: 9 additions & 0 deletions functions/_lf_help
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
echo """\
Part of the mfunc zsh plugin
Lists all the functions in $MFUNCDIR

Usage: $0 [-v|h]
Options:
-v Enable verbose mode (print the content of each function)
-h Print this help message\
"""
29 changes: 29 additions & 0 deletions functions/_lf_verbose
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Ensure MFUNCDIR is set
if ! [[ -v MFUNCDIR ]]
then
if [[ -v ZDOTDIR ]]
then
MFUNCDIR="${ZDOTDIR}/functions"
else
MFUNCDIR="${HOME}/.functions"
fi
fi

# get a list of functions managed by mfunc
local functions_array=( "$MFUNCDIR"/* )

# strip the paths
local functions="${functions_array[@]:t}"

(
# load functions
autoload +X "${(z)functions[@]}"

if ((${+commands[highlight]}))
then
highlight -S zsh -i <(where "${(z)functions[@]}")
else
where "${(z)functions[@]}"
echo "Hint: install 'highlight' to enable syntax highlighting" >&2
fi
)
21 changes: 21 additions & 0 deletions functions/_mf_define
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
if [[ $EDITOR == *vim ]]; then
local ftopts="-c 'set ft=zsh'"
elif [[ $EDITOR == *micro ]]; then
local ftopts="-filetype=zsh"
fi

eval "$EDITOR" "$ftopts" "${MFUNCDIR}/$1"

if ! [[ -f "${MFUNCDIR}/$1" ]]; then
echo "aborting..."
return 1
else
[[ ${functions[${1}]} ]] && unfunction "$1" # just in case
autoload "$1"
fi

if "$edit"; then
echo "updated function '${1}'"
else
echo "new function '${1}' created in $MFUNCDIR"
fi
16 changes: 16 additions & 0 deletions functions/lfunc
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# TODO: specific functions, wildcards
if getopts "hv" opt; then
shift $((OPTIND - 1))
case ${opt} in
h)
_lf_help
return
;;
v)
_lf_verbose
return
;;
esac
fi

command ls "$MFUNCDIR"
27 changes: 27 additions & 0 deletions functions/mfunc
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# berate user if no arguments given
# TODO: interactive mode
(($# == 0)) && {
echo "usage: mfunc [function name]"
return 1
}

for i; do
# TODO: input sanitization
local edit=false
if [[ -e $MFUNCDIR/$i ]]; then
if ! read -rq "choice?function $i already exists, press Y/y to edit with $EDITOR or any other key to abort: "; then
echo
return
fi
echo
unfunction "$i" # forget the old version first
edit=true
fi

_mf_define "$i" || return
done

echo -n "function"
(($# == 1)) &&
echo -n " is" || echo -n "s are"
echo " now available"
31 changes: 31 additions & 0 deletions functions/rfunc
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# demand argument
# TODO: interactive mode
if (($# == 0)); then
echo "please name at least one function to delete"
return 1
fi

# TODO: autocompletion/wildcards
local count
typeset -i count=0
for i; do
if ! [[ -f "$MFUNCDIR/$i" ]]; then
echo "function $i not found"
return 2
fi

autoload +X "$i"
rm -iv "$MFUNCDIR/$i"
[[ -f $MFUNCDIR/$i ]] || count+=1
done

((count == 0)) &&
return

echo -n "function"
if ((count == 1)); then
echo -n " is"
else
echo -n "s are"
fi
echo " still available until next login and can be viewed with the 'where' builtin"
139 changes: 20 additions & 119 deletions mfunc.plugin.zsh
Original file line number Diff line number Diff line change
Expand Up @@ -6,130 +6,31 @@
# github.com/hlohm
##################


#
# init
######

# this is where our functions live
fdir=$HOME/.functions

# check if functions directory exists, create if it doesn't
if [[ ! -d $fdir/ ]]; then
mkdir $fdir/
echo "mfunc init: functions directory created in $fdir"
fi

# check if fpath contains our fdir, add it if it doesn't
if (( ! ${fpath[(I)$fdir]} )); then
fpath=($fdir $fpath)
fi

# autoload any functions in functions directory
if [[ -e $fdir/* ]]; then
autoload $(ls $fdir/)
fi

#
#functions
##########

# helpers
_mf_yesorno()
{
# variables
local question="${1}"
local prompt="${question} "
local yes_RETVAL="0"
local no_RETVAL="3"
local RETVAL=""
local answer=""

# read-eval loop
while true ; do
printf $prompt
read -r answer

case ${answer:=${default}} in
Y|y|YES|yes|Yes )
RETVAL=${yes_RETVAL} && \
break
;;
N|n|NO|no|No )
RETVAL=${no_RETVAL} && \
break
;;
* )
echo "Please provide a valid answer (y or n)"
;;
esac
done

return ${RETVAL}
}

function _mf_define() {
touch $fdir/$i
chmod +x $fdir/$i
echo "enter function '$i' and finish with CTRL-D"
cat >$HOME/.functions/$i
echo "new function '$i' created in $fdir"
autoload $(ls $fdir/)
echo "function is now available"
}

# make function(s)
function mfunc() {

# berate user if no arguments given
# TODO: interactive mode
if (($# == 0))
if ! [[ -v MFUNCDIR ]]
then
if [[ -v ZDOTDIR ]]
then
echo "usage: mfunc [function name]"
MFUNCDIR="${ZDOTDIR}/functions"
else
for i do;
# TODO: input sanitization
if [[ -e $fdir/$i ]]
then
if _mf_yesorno "function $i already exists, overwrite? (Y/n)"
then
unfunction $1 # forget the old version first
_mf_define $i
else
echo "aborted"
fi
else
_mf_define $i
fi
done
MFUNCDIR="${HOME}/.functions"
fi
}
fi

# remove function(s)
function rfunc() {
# check if functions directory exists, create if it doesn't
if ! [[ -d "${MFUNCDIR}/" ]]
then
mkdir -p "${MFUNCDIR}/"
echo "mfunc init: functions directory created in $MFUNCDIR"
fi

# demand argument
# TODO: interactive mode
if (($# == 0)) ; then
echo "please name at least one function to delete";
fi
# check if fpath contains MFUNC_FUNCTIONS_D, add it if it doesn't
MFUNC_FUNCTIONS_D="${0:h}/functions"
(( ${fpath[(I)$MFUNC_FUNCTIONS_D]} )) || fpath=($MFUNC_FUNCTIONS_D "${fpath[@]}")

# TODO: autocompletion/wildcards
for i; do
if [ -e $fdir/$i ]; then
rm $fdir/$i
echo "function $i removed"
else
echo "function $i not found"
fi
done
echo "functions might still be available until next login"
}
# check if fpath contains our MFUNCDIR, add it if it doesn't
(( ${fpath[(I)$MFUNCDIR]} )) || fpath=($MFUNCDIR "${fpath[@]}")

# list functions
function lfunc() {
# TODO: specific functions, wildcards, names only OR name + definition
for f in $(ls $fdir); do
echo $f "() {"; cat $fdir/$f; echo "}\n"
done
}
# autoload functions
files=($MFUNC_FUNCTIONS_D/* $MFUNCDIR/*)
autoload ${(z)files[@]:t}