committed by
Tres Finocchiaro
parent
9fe74c2730
commit
93dc557c56
92
cmake/modules/BashCompletion.cmake
Normal file
92
cmake/modules/BashCompletion.cmake
Normal file
@@ -0,0 +1,92 @@
|
||||
# A wrapper around pkg-config-provided and cmake-provided bash completion that
|
||||
# will have dynamic behavior at INSTALL() time to allow both root-level
|
||||
# INSTALL() as well as user-level INSTALL().
|
||||
#
|
||||
# See also https://github.com/scop/bash-completion
|
||||
#
|
||||
# Copyright (c) 2018, Tres Finocchiaro, <tres.finocchiaro@gmail.com>
|
||||
# Redistribution and use is allowed according to the terms of the BSD license.
|
||||
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||
#
|
||||
# Usage:
|
||||
# INCLUDE(BashCompletion)
|
||||
# BASHCOMP_INSTALL(foo)
|
||||
# ... where "foo" is a shell script adjacent to the CMakeLists.txt
|
||||
#
|
||||
# How it determines BASHCOMP_PKG_PATH, in order:
|
||||
# 1. Uses BASHCOMP_PKG_PATH if already set (e.g. -DBASHCOMP_PKG_PATH=...)
|
||||
# a. If not, uses pkg-config's PKG_CHECK_MODULES to determine path
|
||||
# b. Fallback to cmake's FIND_PACKAGE(bash-completion) path
|
||||
# c. Fallback to hard-coded /usr/share/bash-completion/completions
|
||||
# 2. Final fallback to ${CMAKE_INSTALL_PREFIX}/share/bash-completion/completions if
|
||||
# detected path is unwritable.
|
||||
|
||||
# - Windows does not support bash completion
|
||||
# - macOS support should eventually be added for Homebrew (TODO)
|
||||
IF(WIN32)
|
||||
MESSAGE(STATUS "Bash competion is not supported on this platform.")
|
||||
ELSEIF(APPLE)
|
||||
MESSAGE(STATUS "Bash completion is not yet implemented for this platform.")
|
||||
ELSE()
|
||||
INCLUDE(FindUnixCommands)
|
||||
# Honor manual override if provided
|
||||
IF(NOT BASHCOMP_PKG_PATH)
|
||||
# First, use pkg-config, which is the most reliable
|
||||
FIND_PACKAGE(PkgConfig QUIET)
|
||||
IF(PKGCONFIG_FOUND)
|
||||
PKG_CHECK_MODULES(BASH_COMPLETION bash-completion)
|
||||
PKG_GET_VARIABLE(BASHCOMP_PKG_PATH bash-completion completionsdir)
|
||||
ELSE()
|
||||
# Second, use cmake (preferred but less common)
|
||||
FIND_PACKAGE(bash-completion QUIET)
|
||||
IF(BASH_COMPLETION_FOUND)
|
||||
SET(BASHCOMP_PKG_PATH "${BASH_COMPLETION_COMPLETIONSDIR}")
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
|
||||
# Third, use a hard-coded fallback value
|
||||
IF("${BASHCOMP_PKG_PATH}" STREQUAL "")
|
||||
SET(BASHCOMP_PKG_PATH "/usr/share/bash-completion/completions")
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
|
||||
# Always provide a fallback for non-root INSTALL()
|
||||
SET(BASHCOMP_USER_PATH "${CMAKE_INSTALL_PREFIX}/share/bash-completion/completions")
|
||||
|
||||
# Cmake doesn't allow easy use of conditional logic at INSTALL() time
|
||||
# this is a problem because ${BASHCOMP_PKG_PATH} may not be writable and we
|
||||
# need sane fallback behavior for bundled INSTALL() (e.g. .AppImage, etc).
|
||||
#
|
||||
# The reason this can't be detected by cmake is that it's fairly common to
|
||||
# run "cmake" as a one user (i.e. non-root) and "make install" as another user
|
||||
# (i.e. root).
|
||||
#
|
||||
# - Creates a script called "install_${SCRIPT_NAME}_completion.sh" into the
|
||||
# working binary directory and invokes this script at install.
|
||||
# - Script handles INSTALL()-time conditional logic for sane ballback behavior
|
||||
# when ${BASHCOMP_PKG_PATH} is unwritable (i.e. non-root); Something cmake
|
||||
# can't handle on its own at INSTALL() time)
|
||||
MACRO(BASHCOMP_INSTALL SCRIPT_NAME)
|
||||
# A shell script for wrapping conditionl logic
|
||||
SET(BASHCOMP_SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/install_${SCRIPT_NAME}_completion.sh")
|
||||
|
||||
FILE(WRITE ${BASHCOMP_SCRIPT} "\
|
||||
#!${BASH}\n\
|
||||
set -e\n\
|
||||
BASHCOMP_PKG_PATH=\"${BASHCOMP_USER_PATH}\"\n\
|
||||
if [ -w \"${BASHCOMP_PKG_PATH}\" ]; then\n\
|
||||
BASHCOMP_PKG_PATH=\"${BASHCOMP_PKG_PATH}\"\n\
|
||||
fi\n\
|
||||
echo -e \"\\nInstalling bash completion...\\n\"\n\
|
||||
mkdir -p \"\$BASHCOMP_PKG_PATH\"\n\
|
||||
cp \"${CMAKE_CURRENT_SOURCE_DIR}/${SCRIPT_NAME}\" \"\$BASHCOMP_PKG_PATH\"\n\
|
||||
chmod a+r \"\$BASHCOMP_PKG_PATH/${SCRIPT_NAME}\"\n\
|
||||
echo -e \"Bash completion for ${SCRIPT_NAME} has been installed to \$BASHCOMP_PKG_PATH/${SCRIPT_NAME}\"\n\
|
||||
")
|
||||
INSTALL(CODE "EXECUTE_PROCESS(COMMAND chmod u+x \"install_${SCRIPT_NAME}_completion.sh\" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} )")
|
||||
INSTALL(CODE "EXECUTE_PROCESS(COMMAND \"./install_${SCRIPT_NAME}_completion.sh\" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} )")
|
||||
|
||||
MESSAGE(STATUS "Bash completion script for ${SCRIPT_NAME} will be installed to ${BASHCOMP_PKG_PATH} or fallback to ${BASHCOMP_USER_PATH} if unwritable.")
|
||||
ENDMACRO()
|
||||
ENDIF()
|
||||
|
||||
@@ -15,3 +15,6 @@ if(DOXYGEN_FOUND)
|
||||
COMMENT "Generating API documentation with Doxygen"
|
||||
SOURCES Doxyfile.in)
|
||||
endif(DOXYGEN_FOUND)
|
||||
|
||||
ADD_SUBDIRECTORY(bash-completion)
|
||||
|
||||
|
||||
4
doc/bash-completion/CMakeLists.txt
Normal file
4
doc/bash-completion/CMakeLists.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
INCLUDE(BashCompletion)
|
||||
IF(COMMAND BASHCOMP_INSTALL)
|
||||
BASHCOMP_INSTALL(lmms)
|
||||
ENDIF()
|
||||
334
doc/bash-completion/lmms
Normal file
334
doc/bash-completion/lmms
Normal file
@@ -0,0 +1,334 @@
|
||||
# lmms(1) completion -*- shell-script -*-
|
||||
# use shellcheck: "shellcheck -e bash <filename>"
|
||||
|
||||
_lmms_array_contains ()
|
||||
{
|
||||
local e match="$1"
|
||||
shift
|
||||
for e; do [[ "$e" == "$match" ]] && return 0; done
|
||||
return 1
|
||||
}
|
||||
|
||||
_lmms_long_param_of()
|
||||
{
|
||||
case "$1" in
|
||||
-a)
|
||||
echo "float"
|
||||
;;
|
||||
-b)
|
||||
echo "bitrate"
|
||||
;;
|
||||
-c)
|
||||
echo "config"
|
||||
;;
|
||||
-f)
|
||||
echo "format"
|
||||
;;
|
||||
-i)
|
||||
echo "interpolation"
|
||||
;;
|
||||
-l)
|
||||
echo "loop"
|
||||
;;
|
||||
-m)
|
||||
echo "mode"
|
||||
;;
|
||||
-o)
|
||||
echo "output"
|
||||
;;
|
||||
-p)
|
||||
echo "profile"
|
||||
;;
|
||||
-s)
|
||||
echo "samplerate"
|
||||
;;
|
||||
-x)
|
||||
echo "oversampling"
|
||||
;;
|
||||
*)
|
||||
echo ""
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_lmms_conv_old_action ()
|
||||
{
|
||||
case "$1" in
|
||||
-d|--dump)
|
||||
echo "dump"
|
||||
;;
|
||||
-r|--render)
|
||||
echo "render"
|
||||
;;
|
||||
--rendertracks)
|
||||
echo "rendertracks"
|
||||
;;
|
||||
-u|--upgrade)
|
||||
echo "upgrade"
|
||||
;;
|
||||
*)
|
||||
echo ""
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_lmms()
|
||||
{
|
||||
local cword=$COMP_CWORD
|
||||
local cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
|
||||
# call routine provided by bash-completion
|
||||
_init_completion || return
|
||||
|
||||
local params filemode filetypes
|
||||
local i # counter variable
|
||||
local pars_global pars_noaction pars_render actions shortargs
|
||||
pars_global=(--allowroot --config --help --version)
|
||||
pars_noaction=(--geometry --import)
|
||||
pars_render=(--float --bitrate --format --interpolation)
|
||||
pars_render+=(--loop --mode --output --profile)
|
||||
pars_render+=(--samplerate --oversampling)
|
||||
actions=(dump render rendertracks upgrade)
|
||||
actions_old=(-d --dump -r --render --rendertracks -u --upgrade)
|
||||
shortargs+=(-a -b -c -f -h -i -l -m -o -p -s -v -x)
|
||||
|
||||
local prev prev2
|
||||
if [ "$cword" -gt 1 ]
|
||||
then
|
||||
prev=${COMP_WORDS[cword-1]}
|
||||
fi
|
||||
if [ "$cword" -gt 2 ]
|
||||
then
|
||||
prev2=${COMP_WORDS[cword-2]}
|
||||
fi
|
||||
|
||||
# don't show shortargs, but complete them when entered
|
||||
if [[ $cur =~ ^-[^-]$ ]]
|
||||
then
|
||||
if _lmms_array_contains "$cur" "${shortargs[@]}"
|
||||
then
|
||||
COMPREPLY=( "$cur" )
|
||||
fi
|
||||
return
|
||||
fi
|
||||
|
||||
#
|
||||
# please keep those in order like def_pars_args above
|
||||
#
|
||||
case $prev in
|
||||
--bitrate|-b)
|
||||
params="64 96 128 160 192 256 320"
|
||||
;;
|
||||
--config|-c)
|
||||
filetypes='xml'
|
||||
filemode='existing_files'
|
||||
;;
|
||||
--format|-f)
|
||||
params='wav ogg mp3'
|
||||
;;
|
||||
--geometry)
|
||||
# we can not name all possibilities, but this helps the user
|
||||
# by showing them how the format is
|
||||
params='0x0+0+0'
|
||||
;;
|
||||
--interpolation|-i)
|
||||
params='linear sincfastest sincmedium sincbest'
|
||||
;;
|
||||
--import)
|
||||
filetypes='mid|midi|MID|MIDI|rmi|RMI|h2song|H2SONG'
|
||||
filemode='existing_files'
|
||||
;;
|
||||
--mode|-m)
|
||||
params='s j m'
|
||||
;;
|
||||
--output|-o)
|
||||
# default assumption: could be both
|
||||
local render=1 rendertracks=1
|
||||
for i in "${!COMP_WORDS[@]}"
|
||||
do
|
||||
if [[ ${COMP_WORDS[i]} =~ ^(render|-r|--render)$ ]]
|
||||
then
|
||||
rendertracks=
|
||||
elif [[ ${COMP_WORDS[i]} =~ ^(rendertracks|--rendertracks)$ ]]
|
||||
then
|
||||
render=
|
||||
fi
|
||||
done
|
||||
if [ "$rendertracks" ]
|
||||
then
|
||||
filemode='existing_directories'
|
||||
fi
|
||||
if [ "$render" ]
|
||||
then
|
||||
# filemode files is a superset of "existing directories"
|
||||
# so it's OK to overwrite the filemode='existing_directories'
|
||||
# from above
|
||||
filetypes='wav|ogg|mp3'
|
||||
filemode='files'
|
||||
fi
|
||||
;;
|
||||
--profile|-p)
|
||||
filemode='files'
|
||||
;;
|
||||
--samplerate|-s)
|
||||
# these are the ones suggested for zyn
|
||||
# if you think more are required,
|
||||
# remove this comment and write a justification
|
||||
params='44100 48000 96000 192000'
|
||||
;;
|
||||
--oversampling|-x)
|
||||
params='1 2 4 8'
|
||||
;;
|
||||
*)
|
||||
local action_found
|
||||
|
||||
# Is an action specified?
|
||||
if [ "$cword" -gt 1 ]
|
||||
then
|
||||
local wrd
|
||||
for wrd in "${COMP_WORDS[@]}"
|
||||
do
|
||||
# action named explicitly?
|
||||
if _lmms_array_contains "$wrd" "${actions[@]}"
|
||||
then
|
||||
action_found=$wrd
|
||||
break
|
||||
# deprecated action name?
|
||||
elif _lmms_array_contains "$wrd" "${actions_old[@]}"
|
||||
then
|
||||
action_found="$(_lmms_conv_old_action "$wrd")"
|
||||
break
|
||||
# no-action params found?
|
||||
elif _lmms_array_contains "$wrd" "${pars_noaction[@]}"
|
||||
then
|
||||
action_found=none
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if [[ $prev =~ -e|--help|-h|-version|-v ]]
|
||||
then
|
||||
# the -e flag (from --import) and help/version
|
||||
# always mark the end of arguments
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ "$action_found" =~ dump|none|^$ ]] && [[ $prev =~ \.mmpz? ]]
|
||||
then
|
||||
# mmp(z) mark the end of arguments for those actions
|
||||
return
|
||||
fi
|
||||
|
||||
local savefiletypes='mmpz|mmp'
|
||||
local params_array
|
||||
|
||||
# find parameters/filetypes/dirtypes depending on actions
|
||||
if ! [ "$action_found" ]
|
||||
then
|
||||
params_array=( "${actions[@]}" "${pars_global[@]}" "${pars_noaction[@]}")
|
||||
filemode="existing_files"
|
||||
filetypes="$savefiletypes"
|
||||
elif [ "$action_found" == "none" ]
|
||||
then
|
||||
params_array=( "${pars_noaction[@]}" )
|
||||
filemode="existing_files"
|
||||
filetypes="$savefiletypes"
|
||||
elif [ "$action_found" == "dump" ]
|
||||
then
|
||||
filemode="existing_files"
|
||||
filetypes="mmpz"
|
||||
elif [ "$action_found" == "upgrade" ]
|
||||
then
|
||||
if [ "$prev" == "upgrade" ]
|
||||
then
|
||||
filemode="existing_files"
|
||||
filetypes="$savefiletypes"
|
||||
elif [ "$prev2" == "upgrade" ]
|
||||
then
|
||||
filemode="files"
|
||||
filetypes="$savefiletypes"
|
||||
fi
|
||||
elif [[ "$action_found" =~ render(tracks)? ]]
|
||||
then
|
||||
if [[ "$prev" =~ render(tracks)? ]]
|
||||
then
|
||||
filemode="existing_files"
|
||||
filetypes="$savefiletypes"
|
||||
else
|
||||
params_array=( "${pars_render[@]}" )
|
||||
fi
|
||||
fi
|
||||
|
||||
# add params_array to params, but also check the history of comp words
|
||||
local param
|
||||
for param in "${params_array[@]}"
|
||||
do
|
||||
local do_append=1
|
||||
for i in "${!COMP_WORDS[@]}"
|
||||
do
|
||||
if [ "$i" -ne 0 ] && [ "$i" -ne "$cword" ]
|
||||
then
|
||||
# disallow double long parameters
|
||||
if [ "${COMP_WORDS[$i]}" == "$param" ]
|
||||
then
|
||||
do_append=
|
||||
# disallow double short parameters
|
||||
elif [ "--$(_lmms_long_param_of "${COMP_WORDS[$i]}")" == "$param" ]
|
||||
then
|
||||
do_append=
|
||||
# --help or --version must be the first parameters
|
||||
elif [ "$cword" -gt 1 ] && [[ $param =~ --help|--version ]]
|
||||
then
|
||||
do_append=
|
||||
fi
|
||||
fi
|
||||
done
|
||||
if [ "$do_append" ]
|
||||
then
|
||||
params+="$param "
|
||||
fi
|
||||
done
|
||||
;;
|
||||
esac
|
||||
|
||||
case $filemode in
|
||||
|
||||
# use completion routine provided by bash-completion
|
||||
# to fill $COMPREPLY
|
||||
|
||||
existing_files)
|
||||
_filedir "@($filetypes)"
|
||||
;;
|
||||
|
||||
existing_directories)
|
||||
_filedir -d
|
||||
;;
|
||||
|
||||
files)
|
||||
|
||||
# non existing files complete like directories...
|
||||
_filedir -d
|
||||
|
||||
# ...except for non-completing files with the right file type
|
||||
if [ ${#COMPREPLY[@]} -eq 0 ]
|
||||
then
|
||||
if ! [[ "$cur" =~ /$ ]] && [ "$filetypes" ] && [[ "$cur" =~ \.($filetypes)$ ]]
|
||||
then
|
||||
# file ending fits, we seem to be done
|
||||
COMPREPLY=( "$cur" )
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
|
||||
esac
|
||||
|
||||
if [ "$params" ]
|
||||
then
|
||||
# none of our parameters contain spaces, so deactivate shellcheck's warning
|
||||
# shellcheck disable=SC2207
|
||||
COMPREPLY+=( $(compgen -W "${params}" -- "${cur}") )
|
||||
fi
|
||||
}
|
||||
|
||||
complete -F _lmms lmms
|
||||
Reference in New Issue
Block a user