diff --git a/contrib/osc.spec b/contrib/osc.spec index a7264ea5..3e9239a1 100644 --- a/contrib/osc.spec +++ b/contrib/osc.spec @@ -10,6 +10,7 @@ %define completion_dir_bash %{_datadir}/bash-completion/completions %define completion_dir_csh %{_sysconfdir}/profile.d %define completion_dir_fish %{_datadir}/fish/vendor_completions.d +%define completion_dir_zsh %{_datadir}/zsh/functions/Completion %define osc_plugin_dir %{_prefix}/lib/osc-plugins # need to override python_sitelib because it is not set as we would expect on many distros %define python_sitelib %(RPM_BUILD_ROOT= %{use_python} -Ic "import sysconfig; print(sysconfig.get_path('purelib'))") @@ -156,6 +157,7 @@ install -Dm0755 contrib/osc.complete %{buildroot}%{_datadir}/osc/complete install -Dm0644 contrib/complete.csh %{buildroot}%{completion_dir_csh}/osc.csh install -Dm0644 contrib/complete.sh %{buildroot}%{completion_dir_bash}/osc.sh install -Dm0644 contrib/osc.fish %{buildroot}%{completion_dir_fish}/osc.fish +install -Dm0644 contrib/osc.zsh %{buildroot}%{completion_dir_zsh}/osc.zsh # install rpm macros install -Dm0644 macros.osc %{buildroot}%{_rpmmacrodir}/macros.osc @@ -198,6 +200,7 @@ install -Dm0644 oscrc.5 %{buildroot}%{_mandir}/man5/oscrc.5 %{completion_dir_bash}/* %{completion_dir_csh}/* %{completion_dir_fish}/* +%{completion_dir_zsh}/* # osc owns the dirs to avoid the "directories not owned by a package" build error %dir %{_datadir}/fish diff --git a/contrib/osc.zsh b/contrib/osc.zsh new file mode 100644 index 00000000..10bfc32e --- /dev/null +++ b/contrib/osc.zsh @@ -0,0 +1,286 @@ +#compdef osc +# +# Copyright (C) 2009,2010 Holger Macht +# Copyright (C) 2023 Björn Bidar +# +# This file is released under the GPLv2. +# +# Based on the zsh guide from http://zsh.dotsrc.org/Guide/zshguide06.html +# +# Toggle verbose completions: zstyle ':completion:*:osc:*' verbose no +# zstyle ':completion:*:osc-subcommand:*' verbose no +# +# version 0.2 +# + +# Main dispatcher + +_osc() { + # Variables shared by all internal functions + local osc_projects osc_rc osc_cmd osc_alias + _osc_complete_prepare + osc_projects="${XDG_CACHE_HOME}/osc.projects" + osc_rc="${XDG_CONFIG_HOME}/osc/oscrc" + osc_cmd=osc + + if [[ "${words[0]}" = "isc" ]] ; then + osc_alias=internal + fi + + if [ -s "${PWD}/.osc/_apiurl" -a -s "${osc_rc}" ]; then + local osc_apiurl + read osc_apiurl < "${PWD}/.osc/_apiurl" + # We prefer to match an apiurl with an alias so that the project list + # cache would match also when -A was passed with said alias. + # If there's no alias for that api url match to use the plain apiurl instead. + osc_alias=$(sed -rn '\@^\['${apiurl}'@,\@=@{\@^aliases=@{s@[^=]+=([^,]+),.*@\1@p};}' < "${osc_rc}" 2> /dev/null) + if [ -z $osc_alias ] ; then + osc_alias=${osc_apiurl} + fi + fi + + if (( CURRENT > 2 )) && [[ ${words[2]} != "help" ]]; then + # Remember the subcommand name + local cmd=${words[2]} + # Set the context for the subcommand. + curcontext="${curcontext%:*:*}:osc-subcommand" + # Narrow the range of words we are looking at to exclude `osc' + (( CURRENT-- )) + shift words + # Run the completion for the subcommand + if [ $cmd = -A -o $cmd = --apiurl ] ; then + if [[ -s "${osc_rc}" ]] ; then + local hints=($(sed -rn '/^(aliases=|\[http)/{s/,/ /g;s/(aliases=|\[|\])//gp}' < "${osc_rc}" 2> /dev/null)) + if [[ -n "${words[2]}" ]]; then + for h in ${hints[@]} ; do + case "$h" in + http*) + local tmp=$(sed -rn '\@^\['${h}'@,\@=@{\@^aliases=@{s@[^=]+=([^,]+),.*@\1@p};}' < "${osc_rc}" 2> /dev/null) + if [[ "${words[2]}" = "$h" ]]; then + osc_alias=$tmp + break + fi + ;; + *) + if [[ "${words[2]}" = "$h" ]]; then + osc_alias=$h + break + fi + esac + done + else + _arguments '1:ALIAS:( `echo $hints`)' + return + fi + fi + fi + + if [[ -n "$osc_alias" ]] ; then + osc_projects="${osc_projects}.${osc_alias//\//_}" + osc_command="$osc_command -A ${osc_alias}" + fi + + _osc_update_project_list + + case $cmd in + submitrequest|submitreq|sr) _osc_cmd_submitreq ;; + getbinaries) _osc_cmd_getbinaries ;; + build) _osc_cmd_build ;; + checkout|co|branch|getpac|bco|branchco) _osc_cmd_checkout ;; + buildlog|buildinfo|bl|blt|buildlogtail) _osc_cmd_buildlog ;; + *) _osc_cmd_do $cmd + esac + else + local hline + local -a cmdlist + local tag=0 + _call_program help-commands osc help | while read -A hline; do + # start parsing with "commands:" + [[ $hline[1] = "commands:" ]] && tag=1 + # stop parsing at the line starting with "For" + [[ $hline[1] = "For" ]] && tag=0 + [[ $tag = 0 ]] && continue + # all commands have to start with lower case letters + [[ $hline[1] =~ ^[A-Z] ]] && continue + (( ${#hline} < 2 )) && continue + + # ${hline[1]%,} truncates the last ',' + cmdlist=($cmdlist "${hline[1]%,}:${hline[2,-1]}") + done + _describe -t osc-commands 'osc command' cmdlist + fi +} + +_osc_call_me_maybe() +{ + typeset -i ctime=$(command date -d "$(command stat -c '%z' ${1})" +'%s') + typeset -i now=$(command date -d now +'%s') + if ((now - ctime < 86400)) ; then + return 1 + fi + return 0 +} + +_osc_complete_prepare() { + local xdg_dir + for xdg_dir in "${XDG_CACHE_HOME:=$HOME/.cache}" "${XDG_CONFIG_HOME:=$HOME/.config}"; do + if [[ ! -d "${xdg_dir}" ]]; then + mkdir -p "${xdg_dir}" + fi + done + + if [[ -f ~/.osc.projects ]]; then + rm ~/.osc.projects -f + fi +} + +_osc_update_project_list() { + if [[ -s "${osc_projects}" ]] ; then + if _osc_call_me_maybe "$osc_projects" ; then + if tmp=$(mktemp ${osc_projects}.XXXXXX) ; then + command ${osc_cmd} ls / >| $tmp + mv -uf $tmp ${osc_projects} + fi + fi + else + command ${osc_cmd} ls / >| "${osc_projects}" + fi +} + +_osc_project_repositories() { + if [ ! -s $PWD/.osc/_build_repositories ] || \ + _osc_call_me_maybe $PWD/.osc/_build_repositories ; then + osc repositories > /dev/null + fi + # Just check if file exist in case the call to the api failed + if [ -s $PWD/.osc/_build_repositories ] ; then + cat $PWD/.osc/_build_repositories | while read build_repository ; do + # Only output first word of each line + echo ${build_repository%\ *} + done | sort -u + fi +} + +_osc_project_repositories_arches() { + if [ ! -s $PWD/.osc/_build_repositories ] || \ + _osc_call_me_maybe $PWD/.osc/_build_repositories ; then + osc repositories > /dev/null + fi + # Just check if file exist in case the call to the api failed + if [ -s $PWD/.osc/_build_repositories ] ; then + grep -- $1 $PWD/.osc/_build_repositories | while read build_repository ; do + # Only output second word of each line + echo ${build_repository#*\ } + done | sort -u + fi +} + + +_osc_cmd_getbinaries() { + if [ "$words[2]" = "-" ]; then + _osc_complete_help_commands 'options' 'option' + return + else + if [ -n "$words[2]" ] ; then + local osc_project_repository_arch=$(_osc_project_repositories_arches \ + "${words[2]}") + fi + _arguments \ + '1:PROJECT:( `cat $osc_projects` )' \ + '2:PACKAGE:(PACKAGE)' \ + '3:REPOSITORY:( `_osc_project_repositories`' \ + '4:ARCHITECTURE:(`echo $osc_project_repository_arch`)' + fi +} + +_osc_cmd_checkout() { + if [ "$words[2]" = "-" ]; then + _osc_complete_help_commands 'options' 'option' + return + else + _arguments \ + '1:PROJECT:( `cat $osc_projects` )' \ + '2:PACKAGE:(PACKAGE)' + fi +} + +_osc_cmd_buildlog() { + if [ "$words[2]" = "-" ]; then + _osc_complete_help_commands 'options' 'option' + return + else + if [ -n "$words[2]" ] ; then + local osc_project_repository_arch=$(_osc_project_repositories_arches \ + "${words[2]}") + fi + _arguments \ + '1:REPOSITORY:( `_osc_project_repositories` )' \ + '2:ARCHITECTURE:(`echo $osc_project_repository_arch`)' + fi +} + +_osc_cmd_build() { + if [ "$words[2]" = "-" ]; then + _osc_complete_help_commands 'options' 'option' + return + else + if [ -n "$words[2]" ] ; then + local osc_project_repository_arch=$(_osc_project_repositories_arches \ + "${words[2]}") + fi + _arguments \ + '1:REPOSITORY:( `_osc_project_repositories` )' \ + '2:ARCHITECTURE:(`echo $osc_project_repository_arch`)' \ + '3:Build Description:_files' + fi +} + +_osc_cmd_submitreq() { + _osc_complete_help_commands 'options' 'option' +} + +_osc_complete_help_commands() { + local hline + local -a cmdlist + local tag=0 + _call_program help-commands osc help $cmd | while read -A hline; do + # start parsing from "usage:" + [[ $hline[1] = "${1}:" ]] && tag=1 + [[ $tag = 0 ]] && continue + + if [[ $hline[1] =~ ^osc ]]; then + shift hline; shift hline + elif ! [[ $hline[1] =~ ^- ]]; then + # Option has to start with a '-' or 'osc submitrequest' + continue + fi + + (( ${#hline} < 2 )) && continue + + cmdlist=($cmdlist "${hline[1]%,}:${hline[2,-1]}") + + done + + if [ -n "$cmdlist" ] ; then + _describe -t osc-commands "osc $2" cmdlist + else + return 1 + fi +} + +_osc_cmd_do() { + # only start completion if there's some '-' on the line + if ! [ "$words[2]" = "-" ]; then + _complete + return + fi + + if ! _osc_complete_help_commands 'options' 'option'; then + _complete + fi +} + +# Code to make sure _osc is run when we load it +_osc "$@" + +