diff --git a/extra/_beet b/extra/_beet index 23155b8e5..5b715dcef 100644 --- a/extra/_beet +++ b/extra/_beet @@ -2,28 +2,29 @@ # zsh completion for beets music library manager and MusicBrainz tagger: http://beets.radbox.org/ -# NOTE: it will be very slow the first time you try to complete in a zsh shell (especially if you've enable many plugins) -# You can make it faster in future by creating a cached version: -# 1) perform a query completion with this file (_beet), e.g. do: beet list artist:" -# to create the completion function (takes a few seconds) -# 2) save a copy of the completion function: which _beet > _beet_cached -# 3) save a copy of the query completion function: which _beet_query > _beet_query_cached -# 4) copy the contents of _beet_query_cached to the top of _beet_cached -# 5) copy and paste the _beet_field_values function from _beet to the top of _beet_cached -# 6) add the following line to the top of _beet_cached: #compdef beet -# 7) add the following line to the bottom of _beet_cached: _beet "$@" -# 8) save _beet_cached to your completions directory (e.g. /usr/share/zsh/functions/Completion) -# 9) add the following line to your .zshrc file: compdef _beet_cached beet -# You will need to repeat this proceedure each time you enable new plugins if you want them to complete properly. +# Cache will be updated if it is older than the beets database or binary. +# Need to set BEETS_LIBRARY to some preliminary value since it is used by the cache checking function. +typeset -g BEETS_LIBRARY=~/.config/beets/library.db +zstyle ":completion:${curcontext}:" cache-policy _beet_check_cache +_beet_check_cache () { + [[ ! -a "${1}" ]] || [[ ! -a ${~BEETS_LIBRARY} ]] || [[ "${1}" -ot ${~BEETS_LIBRARY} ]] || [[ "${1}" -ot =beet ]] +} +# Try to retrieve the cache, and find out if it needs to be updated +if ! _retrieve_cache beets || _cache_invalid beets; then + local updatecache=1 + # Location of database + typeset -g BEETS_LIBRARY="$(beet config|grep library|cut -f 2 -d ' ')" + # List of all fields + local -a fields + fields=(`beet fields | grep -G '^ ' | sort -u | colrm 1 2`) +fi # useful: argument to _regex_arguments for matching any word local matchany=/$'[^\0]##\0'/ # Deal with completions for querying and modifying fields.. local fieldargs matchquery matchmodify -local -a fields -# get list of all fields -fields=(`beet fields | grep -G '^ ' | sort -u | colrm 1 2`) + # regexps for matching query and modify terms on the command line matchquery=/"(${(j/|/)fields[@]})"$':[^\0]##\0'/ matchmodify=/"(${(j/|/)fields[@]})"$'(=[^\0]##|!)\0'/ @@ -43,14 +44,17 @@ function _join_lines() { function _beet_field_values() { local -a output fieldvals - local library="$(beet config|grep library|cut -f 2 -d ' ')" - output=$(sqlite3 ${~library} "select distinct $1 from items;") + local sqlcmd="select distinct $1 from items;" case $1 in lyrics) fieldvals= ;; *) + if [[ "$(sqlite3 ${~BEETS_LIBRARY} ${sqlcmd} 2>&1)" =~ "no such column" ]]; then + sqlcmd="select distinct value from item_attributes where key=='$1' and value!='';" + fi + output="$(sqlite3 ${~BEETS_LIBRARY} ${sqlcmd} 2>/dev/null | sed -rn '/^-+$/,${{/^[- ]+$/n};p}')" fieldvals=("${(f)output[@]}") ;; esac @@ -68,8 +72,7 @@ queryelem="_values -S : 'query field (add an extra : to match by regexp)' '::' $ # store call to _values function for completing modify terms (no need to complete field values) modifyelem="_values -S = 'modify field (replace = with ! to remove field)' $(echo "'${^fields[@]}:: '")" # Create completion function for queries -_regex_arguments _beet_query "$matchany" \# \( "$matchquery" ":query:query string:$queryelem" \) \ - \( "$matchquery" ":query:query string:$queryelem" \) \# +_regex_arguments _beet_query "$matchany" \# \( "$matchquery" ":query:query string:$queryelem" \) \( "$matchquery" ":query:query string:$queryelem" \) \# # store regexps for completing lists of queries and modifications local -a query modify query=( \( "$matchquery" ":query:query string:{_beet_query}" \) \( "$matchquery" ":query:query string:{_beet_query}" \) \# ) @@ -108,7 +111,7 @@ retagopt='-L:retag items matching a query:${query[@]}' skipopt='-i:skip already-imported directories' noskipopt='-I:do not skip already-imported directories' flatopt='--flat:import an entire tree as a single album' -groupopt='-g:group tracks in a folder into separate albums' +groupopt='-g:group tracks in a folder into separate albums' editopt='-e:edit user configuration with $EDITOR' defaultopt='-d:include the default configuration' copynomoveopt='-c:copy instead of moving' @@ -174,73 +177,76 @@ function _beet_subcmd_options() } # Now build the arguments to _regex_arguments for each subcommand. -local -a options regex_words_subcmds regex_words_help -local subcmd cmddesc -for i in ${${(f)"$(beet help | _join_lines ' ' 3 'Commands:')"[@]}[@]} -do - subcmd="${i[(w)1]}" - # remove first word and parenthesised alias, replace : with -, [ with (, ] with ), and remove single quotes - cmddesc="${${${${${i[(w)2,-1]##\(*\) #}//:/-}//\[/(}//\]/)}//\'/}" - case $subcmd +if [[ -n $updatecache ]]; then + local -a options regex_words_subcmds regex_words_help + local subcmd cmddesc + for i in ${${(f)"$(beet help | _join_lines ' ' 3 'Commands:')"[@]}[@]} + do + subcmd="${i[(w)1]}" + # remove first word and parenthesised alias, replace : with -, [ with (, ] with ), and remove single quotes + cmddesc="${${${${${i[(w)2,-1]##\(*\) #}//:/-}//\[/(}//\]/)}//\'/}" + case $subcmd in - (config) - _regex_words options "config options" "$helpopt" "$pathopt" "$editopt" "$defaultopt" - options=("${reply[@]}") - ;; - (import) - _regex_words options "import options" "$helpopt" "$writeopt" "$nowriteopt" "$copyopt" "$nocopyopt"\ - "$inferopt" "$noinferopt" "$resumeopt" "$noresumeopt" "$nopromptopt" "$logopt" "$individualopt" "$confirmopt"\ - "$retagopt" "$skipopt" "$noskipopt" "$flatopt" "$groupopt" - options=( "${reply[@]}" \# "${files[@]}" \# ) - ;; - (list) - _regex_words options "list options" "$helpopt" "$pathopt" "$albumopt" "$formatopt" - options=( "$reply[@]" \# "${query[@]}" ) - ;; - (modify) - _regex_words options "modify options" "$helpopt" "$dontmoveopt" "$writeopt" "$nowriteopt" "$albumopt" \ - "$noconfirmopt" "$formatopt" - options=( "${reply[@]}" \# "${query[@]}" "${modify[@]}" ) - ;; - (move) - _regex_words options "move options" "$helpopt" "$albumopt" "$destopt" "$copynomoveopt" - options=( "${reply[@]}" \# "${query[@]}") - ;; - (remove) - _regex_words options "remove options" "$helpopt" "$albumopt" "$removeopt" - options=( "${reply[@]}" \# "${query[@]}" ) - ;; - (stats) - _regex_words options "stats options" "$helpopt" "$exactopt" - options=( "${reply[@]}" \# "${query[@]}" ) - ;; - (update) - _regex_words options "update options" "$helpopt" "$albumopt" "$dontmoveopt" "$pretendopt" "$formatopt" - options=( "${reply[@]}" \# "${query[@]}" ) - ;; - (write) - _regex_words options "write options" "$helpopt" "$pretendopt" - options=( "${reply[@]}" \# "${query[@]}" ) - ;; - (fields|migrate|version) - options=() - ;; - (help) - # The help subcommand is treated separately - continue - ;; - (*) # completions for plugin commands are generated using _beet_subcmd_options - _beet_subcmd_options "$subcmd" - options=( \( "${reply[@]}" \# "${query[@]}" \) ) - ;; - esac - # Create variable for holding option for this subcommand, and assign to it (needs to have a unique name). - typeset -a opts_for_$subcmd - set -A opts_for_$subcmd ${options[@]} # Assignment MUST be done using set (other methods fail). - regex_words_subcmds+=("$subcmd:$cmddesc:\${(@)opts_for_$subcmd}") - # Add to regex_words args for help subcommand - regex_words_help+=("$subcmd:$cmddesc") -done + (config) + _regex_words options "config options" "$helpopt" "$pathopt" "$editopt" "$defaultopt" + options=("${reply[@]}") + ;; + (import) + _regex_words options "import options" "$helpopt" "$writeopt" "$nowriteopt" "$copyopt" "$nocopyopt"\ + "$inferopt" "$noinferopt" "$resumeopt" "$noresumeopt" "$nopromptopt" "$logopt" "$individualopt" "$confirmopt"\ + "$retagopt" "$skipopt" "$noskipopt" "$flatopt" "$groupopt" + options=( "${reply[@]}" \# "${files[@]}" \# ) + ;; + (list) + _regex_words options "list options" "$helpopt" "$pathopt" "$albumopt" "$formatopt" + options=( "$reply[@]" \# "${query[@]}" ) + ;; + (modify) + _regex_words options "modify options" "$helpopt" "$dontmoveopt" "$writeopt" "$nowriteopt" "$albumopt" \ + "$noconfirmopt" "$formatopt" + options=( "${reply[@]}" \# "${query[@]}" "${modify[@]}" ) + ;; + (move) + _regex_words options "move options" "$helpopt" "$albumopt" "$destopt" "$copynomoveopt" + options=( "${reply[@]}" \# "${query[@]}") + ;; + (remove) + _regex_words options "remove options" "$helpopt" "$albumopt" "$removeopt" + options=( "${reply[@]}" \# "${query[@]}" ) + ;; + (stats) + _regex_words options "stats options" "$helpopt" "$exactopt" + options=( "${reply[@]}" \# "${query[@]}" ) + ;; + (update) + _regex_words options "update options" "$helpopt" "$albumopt" "$dontmoveopt" "$pretendopt" "$formatopt" + options=( "${reply[@]}" \# "${query[@]}" ) + ;; + (write) + _regex_words options "write options" "$helpopt" "$pretendopt" + options=( "${reply[@]}" \# "${query[@]}" ) + ;; + (fields|migrate|version) + options=() + ;; + (help) + # The help subcommand is treated separately + continue + ;; + (*) # completions for plugin commands are generated using _beet_subcmd_options + _beet_subcmd_options "$subcmd" + options=( \( "${reply[@]}" \# "${query[@]}" \) ) + ;; + esac + # Create variable for holding option for this subcommand, and assign to it (needs to have a unique name). + typeset -a opts_for_$subcmd + set -A opts_for_$subcmd ${options[@]} # Assignment MUST be done using set (other methods fail). + regex_words_subcmds+=("$subcmd:$cmddesc:\${(@)opts_for_$subcmd}") + # Add to regex_words args for help subcommand + regex_words_help+=("$subcmd:$cmddesc") + done + _store_cache beets regex_words_subcmds regex_words_help BEETS_LIBRARY fields +fi local -a opts_for_help _regex_words subcmds "subcommands" "${regex_words_help[@]}" @@ -253,7 +259,7 @@ _regex_words options "global options" "$configopt" "$debugopt" "$libopt" "$helpo globalopts=("${reply[@]}") # Create main completion function -#local -a subcmds +local -a subcmds _regex_words subcmds "subcommands" "${regex_words_subcmds[@]}" subcmds=("${reply[@]}") _regex_arguments _beet "$matchany" \( "${globalopts[@]}" \# \) "${subcmds[@]}" @@ -267,3 +273,4 @@ _beet "$@" # Local Variables: # mode:shell-script # End: +