From 24d1d6fc0b8e780ff330dfa3c327cab75f6964bc Mon Sep 17 00:00:00 2001 From: Danny Ben Shitrit Date: Sun, 5 Jul 2026 09:28:51 +0300 Subject: [PATCH 1/4] - Avoid showing packages without `main` in `rush list` --- features/list.feature | 1 - rush | 3 ++- src/lib/list_show_repo.sh | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/features/list.feature b/features/list.feature index 6a785e5..eb48587 100644 --- a/features/list.feature +++ b/features/list.feature @@ -24,7 +24,6 @@ Scenario: Show package names only bootstrap download hello - nested """ Scenario: Show nested packages for a package name diff --git a/rush b/rush index 3b699e3..b2e3dd1 100755 --- a/rush +++ b/rush @@ -1135,7 +1135,7 @@ list_show_repo() { if [[ ${glob[0]} =~ .*\*.* ]]; then infofile="$repo_path/$package/info" - if [[ -f "$infofile" ]]; then + if [[ -f "$infofile" && -f "$repo_path/$package/main" ]]; then list_display_item "$package" "$infofile" "$repo" "$simple" elif [[ ! $simple ]]; then red "nothing in $repo repo" @@ -1147,6 +1147,7 @@ list_show_repo() { if [[ $infofile =~ $regex ]]; then package_name="${BASH_REMATCH[1]}" + [[ -f "$repo_path/$package_name/main" ]] || continue list_display_item "$package_name" "$infofile" "$repo" "$simple" fi done diff --git a/src/lib/list_show_repo.sh b/src/lib/list_show_repo.sh index 5032c67..a9055fb 100644 --- a/src/lib/list_show_repo.sh +++ b/src/lib/list_show_repo.sh @@ -37,7 +37,7 @@ list_show_repo() { if [[ ${glob[0]} =~ .*\*.* ]]; then infofile="$repo_path/$package/info" - if [[ -f "$infofile" ]]; then + if [[ -f "$infofile" && -f "$repo_path/$package/main" ]]; then list_display_item "$package" "$infofile" "$repo" "$simple" elif [[ ! $simple ]]; then red "nothing in $repo repo" @@ -49,6 +49,7 @@ list_show_repo() { if [[ $infofile =~ $regex ]]; then package_name="${BASH_REMATCH[1]}" + [[ -f "$repo_path/$package_name/main" ]] || continue list_display_item "$package_name" "$infofile" "$repo" "$simple" fi done From 5db5370182e9d26da431f81c7a566c184e06e404 Mon Sep 17 00:00:00 2001 From: Danny Ben Shitrit Date: Sun, 5 Jul 2026 09:40:24 +0300 Subject: [PATCH 2/4] - Add interactive `rush menu` command for viewing and installing with fzf --- doc/rush-menu.1 | 35 +++++++ doc/rush-menu.md | 69 +++++++++++++ doc/rush.1 | 8 +- doc/rush.md | 7 +- rush | 193 ++++++++++++++++++++++++++++++++++-- src/bashly.yml | 26 +++++ src/commands/menu.sh | 24 +++++ src/lib/send_completions.sh | 38 +++++-- 8 files changed, 374 insertions(+), 26 deletions(-) create mode 100644 doc/rush-menu.1 create mode 100644 doc/rush-menu.md create mode 100644 src/commands/menu.sh diff --git a/doc/rush-menu.1 b/doc/rush-menu.1 new file mode 100644 index 0000000..6a3c13c --- /dev/null +++ b/doc/rush-menu.1 @@ -0,0 +1,35 @@ +.\" Automatically generated by Pandoc 3.9 +.\" +.TH "rush\-menu" "1" "July 2026" "" "Select a package from an interactive menu." +.SH NAME +\f[B]rush menu\f[R] \- Select a package from an interactive menu. +.SH SYNOPSIS +\f[B]rush menu\f[R] REPO_OR_PACKAGE OPTIONS +.SH DESCRIPTION +Select a package from an interactive menu. +.SH ARGUMENTS +.SS REPO_OR_PACKAGE +Repository name or package name. +.PP +Leave blank to show all packages in all repositories. +.PP +Provide a repository name to show only packages in this repository. +.PP +Provide a package name (may be in the form of \f[B]repo:package\f[R]) to +show nested packages. +.SH OPTIONS +.SS \-\-install, \-i +Install the selected package instead of showing its info. +.SH DEPENDENCIES +.SS fzf +.SH EXAMPLES +.IP +.EX +rush menu + +rush menu \-\-install + +rush menu personal +.EE +.SH SEE ALSO +\f[B]rush\f[R](1) diff --git a/doc/rush-menu.md b/doc/rush-menu.md new file mode 100644 index 0000000..0a36078 --- /dev/null +++ b/doc/rush-menu.md @@ -0,0 +1,69 @@ +% rush-menu(1) | Select a package from an interactive menu. +% +% July 2026 + +NAME +================================================== + +**rush menu** - Select a package from an interactive menu. + +SYNOPSIS +================================================== + +**rush menu** [REPO_OR_PACKAGE] [OPTIONS] + +DESCRIPTION +================================================== + +Select a package from an interactive menu. + + +ARGUMENTS +================================================== + +REPO_OR_PACKAGE +-------------------------------------------------- + +Repository name or package name. + +Leave blank to show all packages in all repositories. + +Provide a repository name to show only packages in this repository. + +Provide a package name (may be in the form of **repo:package**) to show nested packages. + + +OPTIONS +================================================== + +--install, -i +-------------------------------------------------- + +Install the selected package instead of showing its info. + + +DEPENDENCIES +================================================== + +fzf +-------------------------------------------------- + + +EXAMPLES +================================================== + +~~~ +rush menu + +rush menu --install + +rush menu personal + +~~~ + +SEE ALSO +================================================== + +**rush**(1) + + diff --git a/doc/rush.1 b/doc/rush.1 index 7a97c25..76efe1b 100644 --- a/doc/rush.1 +++ b/doc/rush.1 @@ -37,6 +37,8 @@ Copy a package between local repositories. Show information about a package. .SS rush list Show packages in one or all repositories. +.SS rush menu +Select a package from an interactive menu. .SS rush search Search in package names and info files. .SS rush edit @@ -62,9 +64,9 @@ Default: \(ti/rush\-repos \f[B]rush\-default\f[R](1), \f[B]rush\-get\f[R](1), \f[B]rush\-undo\f[R](1), \f[B]rush\-snatch\f[R](1), \f[B]rush\-copy\f[R](1), \f[B]rush\-info\f[R](1), -\f[B]rush\-list\f[R](1), \f[B]rush\-search\f[R](1), -\f[B]rush\-edit\f[R](1), \f[B]rush\-show\f[R](1), -\f[B]rush\-completions\f[R](1) +\f[B]rush\-list\f[R](1), \f[B]rush\-menu\f[R](1), +\f[B]rush\-search\f[R](1), \f[B]rush\-edit\f[R](1), +\f[B]rush\-show\f[R](1), \f[B]rush\-completions\f[R](1) .SH SOURCE CODE https://github.com/dannyben/rush .SH ISSUE TRACKER diff --git a/doc/rush.md b/doc/rush.md index 1b03528..98b0861 100644 --- a/doc/rush.md +++ b/doc/rush.md @@ -98,6 +98,11 @@ rush list Show packages in one or all repositories. +rush menu +-------------------------------------------------- + +Select a package from an interactive menu. + rush search -------------------------------------------------- @@ -145,7 +150,7 @@ Default: ~/rush-repos SEE ALSO ================================================== -**rush-add**(1), **rush-remove**(1), **rush-clone**(1), **rush-pull**(1), **rush-push**(1), **rush-config**(1), **rush-default**(1), **rush-get**(1), **rush-undo**(1), **rush-snatch**(1), **rush-copy**(1), **rush-info**(1), **rush-list**(1), **rush-search**(1), **rush-edit**(1), **rush-show**(1), **rush-completions**(1) +**rush-add**(1), **rush-remove**(1), **rush-clone**(1), **rush-pull**(1), **rush-push**(1), **rush-config**(1), **rush-default**(1), **rush-get**(1), **rush-undo**(1), **rush-snatch**(1), **rush-copy**(1), **rush-info**(1), **rush-list**(1), **rush-menu**(1), **rush-search**(1), **rush-edit**(1), **rush-show**(1), **rush-completions**(1) # SOURCE CODE diff --git a/rush b/rush index b2e3dd1..c5270a3 100755 --- a/rush +++ b/rush @@ -49,6 +49,7 @@ rush_usage() { printf " %s Copy a package between local repositories.\n" "$(green "copy") " printf " %s Show information about a package.\n" "$(green "info") " printf " %s Show packages in one or all repositories.\n" "$(green "list") " + printf " %s Select a package from an interactive menu.\n" "$(green "menu") " printf " %s Search in package names and info files.\n" "$(green "search") " printf " %s Edit package files.\n" "$(green "edit") " printf " %s Show package files.\n" "$(green "show") " @@ -718,6 +719,49 @@ rush_list_usage() { fi } +# :command.usage +rush_menu_usage() { + printf "rush menu - Select a package from an interactive menu.\n\n" + + printf "%s\n" "$(bold "Usage:")" + printf " rush menu [REPO_OR_PACKAGE] [OPTIONS]\n" + printf " rush menu --help | -h\n" + echo + + # :command.long_usage + if [[ -n "$long_usage" ]]; then + # :command.usage_options + printf "%s\n" "$(bold "Options:")" + + # :command.usage_flags + # :flag.usage + printf " %s\n" "$(green "--install, -i")" + printf " Install the selected package instead of showing its info.\n" + echo + + # :command.usage_fixed_flags + printf " %s\n" "$(green "--help, -h")" + printf " Show this help\n" + echo + + # :command.usage_args + printf "%s\n" "$(bold "Arguments:")" + + # :argument.usage + printf " %s\n" "$(green "REPO_OR_PACKAGE")" + printf " Repository name or package name.\n \n Leave blank to show all packages in all repositories.\n \n Provide a repository name to show only packages in this repository.\n \n Provide a package name (may be in the form of \`repo:package\`) to show nested\n packages.\n" + echo + + # :command.usage_examples + printf "%s\n" "$(bold "Examples:")" + printf " rush menu\n" + printf " rush menu --install\n" + printf " rush menu personal\n" + echo + + fi +} + # :command.usage rush_search_usage() { printf "rush search - Search in package names and info files.\n\n" @@ -1296,9 +1340,11 @@ send_completions() { echo $' 13:--simple|13:-s) return 0 ;;' echo $' 13:--all|13:-a) return 0 ;;' echo $' 14:--help|14:-h) return 0 ;;' + echo $' 14:--install|14:-i) return 0 ;;' echo $' 15:--help|15:-h) return 0 ;;' echo $' 16:--help|16:-h) return 0 ;;' echo $' 17:--help|17:-h) return 0 ;;' + echo $' 18:--help|18:-h) return 0 ;;' echo $' esac' echo $'' echo $' return 1' @@ -1423,28 +1469,32 @@ send_completions() { echo $' node_id=13' echo $' node_word_count=1' echo $' ;;' - echo $' 0:search)' + echo $' 0:menu)' echo $' node_id=14' echo $' node_word_count=1' echo $' ;;' + echo $' 0:search)' + echo $' node_id=15' + echo $' node_word_count=1' + echo $' ;;' echo $' 0:s)' - echo $' node_id=14' + echo $' node_id=15' echo $' node_word_count=1' echo $' ;;' echo $' 0:edit)' - echo $' node_id=15' + echo $' node_id=16' echo $' node_word_count=1' echo $' ;;' echo $' 0:e)' - echo $' node_id=15' + echo $' node_id=16' echo $' node_word_count=1' echo $' ;;' echo $' 0:show)' - echo $' node_id=16' + echo $' node_id=17' echo $' node_word_count=1' echo $' ;;' echo $' 0:completions)' - echo $' node_id=17' + echo $' node_id=18' echo $' node_word_count=1' echo $' ;;' echo $' *)' @@ -1518,7 +1568,7 @@ send_completions() { echo $' if [[ "${cur:0:1}" != "-" ]] && (( positional_index == 0 )); then' echo $' case "$node_id" in' echo $' 0)' - echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "add a remove r clone pull p download update push upload config c default get g undo u snatch copy info i list l ls search s edit e show completions $(rush list -s -a)" -- "$cur")' + echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "add a remove r clone pull p download update push upload config c default get g undo u snatch copy info i list l ls menu search s edit e show completions $(rush list -s -a)" -- "$cur")' echo $' return' echo $' ;;' echo $' esac' @@ -1636,6 +1686,7 @@ send_completions() { echo $' 14)' echo $' local words=()' echo $' _rush_completions_option_seen "--help" "-h" || words+=("--help" "-h")' + echo $' _rush_completions_option_seen "--install" "-i" || words+=("--install" "-i")' echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "${words[*]}" -- "$cur")' echo $' return' echo $' ;;' @@ -1657,12 +1708,18 @@ send_completions() { echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "${words[*]}" -- "$cur")' echo $' return' echo $' ;;' + echo $' 18)' + echo $' local words=()' + echo $' _rush_completions_option_seen "--help" "-h" || words+=("--help" "-h")' + echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "${words[*]}" -- "$cur")' + echo $' return' + echo $' ;;' echo $' esac' echo $' fi' echo $'' echo $' case "$node_id:$positional_index" in' echo $' 0:0)' - echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "add a remove r clone pull p download update push upload config c default get g undo u snatch copy info i list l ls search s edit e show completions $(rush list -s -a)" -- "$cur")' + echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "add a remove r clone pull p download update push upload config c default get g undo u snatch copy info i list l ls menu search s edit e show completions $(rush list -s -a)" -- "$cur")' echo $' return' echo $' ;;' echo $' 1:0)' @@ -1723,17 +1780,20 @@ send_completions() { echo $' return' echo $' ;;' echo $' 15:0)' + echo $' return' + echo $' ;;' + echo $' 16:0)' echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(rush list -s -a)" -- "$cur")' echo $' return' echo $' ;;' - echo $' 15:1)' + echo $' 16:1)' echo $' return' echo $' ;;' - echo $' 16:0)' + echo $' 17:0)' echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(rush list -s -a)" -- "$cur")' echo $' return' echo $' ;;' - echo $' 16:1)' + echo $' 17:1)' echo $' return' echo $' ;;' echo $' esac' @@ -2153,6 +2213,37 @@ rush_list_command() { } +# :command.function +rush_menu_command() { + + # src/commands/menu.sh + repo_or_package=${args[repo_or_package]} + install=${args[--install]} + list_args=(--all) + + [[ $repo_or_package ]] && list_args+=("$repo_or_package") + + items=$(rush list "${list_args[@]}") + + if ! selected=$(printf "%s\n" "$items" | fzf --ansi); then + exit 0 + fi + + [[ $selected ]] || exit 0 + + plain=$(printf "%s" "$selected" | sed -E $'s/\x1B\\[[0-9;]*[[:alpha:]]//g') + package=${plain%%[[:space:]]*} + + [[ $package ]] || exit 0 + + if [[ $install ]]; then + rush get "$package" + else + rush info "$package" + fi + +} + # :command.function rush_search_command() { @@ -2360,6 +2451,13 @@ parse_requirements() { shift $# ;; + menu) + action="menu" + shift + rush_menu_parse_requirements "$@" + shift $# + ;; + search | s) action="search" shift @@ -3441,6 +3539,78 @@ rush_list_parse_requirements() { } +# :command.parse_requirements +rush_menu_parse_requirements() { + local key + + # :command.fixed_flags_filter + while [[ $# -gt 0 ]]; do + key="$1" + case "$key" in + --help | -h) + long_usage=yes + rush_menu_usage + exit + ;; + + *) + break + ;; + + esac + done + + # :command.dependencies_filter + missing_deps= + # :dependency.filter + if ! command -v fzf >/dev/null 2>&1; then + printf "missing dependency: fzf\n" >&2 + missing_deps=1 + fi + + if [[ -n $missing_deps ]]; then + exit 1 + fi + + # :command.command_filter + action="menu" + + # :command.parse_requirements_while + while [[ $# -gt 0 ]]; do + key="$1" + case "$key" in + # :flag.case + --install | -i) + + # :flag.case_no_arg + args['--install']=1 + shift + ;; + + -?*) + printf "invalid option: %s\n" "$key" >&2 + exit 1 + ;; + + *) + # :command.parse_requirements_case + # :command.parse_requirements_case_simple + # :argument.case + if [[ -z ${args['repo_or_package']+x} ]]; then + args['repo_or_package']=$1 + shift + else + printf "invalid argument: %s\n" "$key" >&2 + exit 1 + fi + + ;; + + esac + done + +} + # :command.parse_requirements rush_search_parse_requirements() { local key @@ -3717,6 +3887,7 @@ run() { "copy") rush_copy_command ;; "info") rush_info_command ;; "list") rush_list_command ;; + "menu") rush_menu_command ;; "search") rush_search_command ;; "edit") rush_edit_command ;; "show") rush_show_command ;; diff --git a/src/bashly.yml b/src/bashly.yml index 360d080..4b110c1 100644 --- a/src/bashly.yml +++ b/src/bashly.yml @@ -372,6 +372,32 @@ commands: - rush list apps - rush list apps --simple --all +- name: menu + group: Package + dependencies: [fzf] + help: Select a package from an interactive menu. + + args: + - name: repo_or_package + help: |- + Repository name or package name. + + Leave blank to show all packages in all repositories. + + Provide a repository name to show only packages in this repository. + + Provide a package name (may be in the form of `repo:package`) to show nested packages. + + flags: + - long: --install + short: -i + help: Install the selected package instead of showing its info. + + examples: + - rush menu + - rush menu --install + - rush menu personal + - name: search alias: s group: Package diff --git a/src/commands/menu.sh b/src/commands/menu.sh new file mode 100644 index 0000000..b2a95c5 --- /dev/null +++ b/src/commands/menu.sh @@ -0,0 +1,24 @@ +repo_or_package=${args[repo_or_package]} +install=${args[--install]} +list_args=(--all) + +[[ $repo_or_package ]] && list_args+=("$repo_or_package") + +items=$(rush list "${list_args[@]}") + +if ! selected=$(printf "%s\n" "$items" | fzf --ansi); then + exit 0 +fi + +[[ $selected ]] || exit 0 + +plain=$(printf "%s" "$selected" | sed -E $'s/\x1B\\[[0-9;]*[[:alpha:]]//g') +package=${plain%%[[:space:]]*} + +[[ $package ]] || exit 0 + +if [[ $install ]]; then + rush get "$package" +else + rush info "$package" +fi diff --git a/src/lib/send_completions.sh b/src/lib/send_completions.sh index 9caaba8..077ba93 100644 --- a/src/lib/send_completions.sh +++ b/src/lib/send_completions.sh @@ -46,9 +46,11 @@ send_completions() { echo $' 13:--simple|13:-s) return 0 ;;' echo $' 13:--all|13:-a) return 0 ;;' echo $' 14:--help|14:-h) return 0 ;;' + echo $' 14:--install|14:-i) return 0 ;;' echo $' 15:--help|15:-h) return 0 ;;' echo $' 16:--help|16:-h) return 0 ;;' echo $' 17:--help|17:-h) return 0 ;;' + echo $' 18:--help|18:-h) return 0 ;;' echo $' esac' echo $'' echo $' return 1' @@ -173,28 +175,32 @@ send_completions() { echo $' node_id=13' echo $' node_word_count=1' echo $' ;;' - echo $' 0:search)' + echo $' 0:menu)' echo $' node_id=14' echo $' node_word_count=1' echo $' ;;' + echo $' 0:search)' + echo $' node_id=15' + echo $' node_word_count=1' + echo $' ;;' echo $' 0:s)' - echo $' node_id=14' + echo $' node_id=15' echo $' node_word_count=1' echo $' ;;' echo $' 0:edit)' - echo $' node_id=15' + echo $' node_id=16' echo $' node_word_count=1' echo $' ;;' echo $' 0:e)' - echo $' node_id=15' + echo $' node_id=16' echo $' node_word_count=1' echo $' ;;' echo $' 0:show)' - echo $' node_id=16' + echo $' node_id=17' echo $' node_word_count=1' echo $' ;;' echo $' 0:completions)' - echo $' node_id=17' + echo $' node_id=18' echo $' node_word_count=1' echo $' ;;' echo $' *)' @@ -268,7 +274,7 @@ send_completions() { echo $' if [[ "${cur:0:1}" != "-" ]] && (( positional_index == 0 )); then' echo $' case "$node_id" in' echo $' 0)' - echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "add a remove r clone pull p download update push upload config c default get g undo u snatch copy info i list l ls search s edit e show completions $(rush list -s -a)" -- "$cur")' + echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "add a remove r clone pull p download update push upload config c default get g undo u snatch copy info i list l ls menu search s edit e show completions $(rush list -s -a)" -- "$cur")' echo $' return' echo $' ;;' echo $' esac' @@ -386,6 +392,7 @@ send_completions() { echo $' 14)' echo $' local words=()' echo $' _rush_completions_option_seen "--help" "-h" || words+=("--help" "-h")' + echo $' _rush_completions_option_seen "--install" "-i" || words+=("--install" "-i")' echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "${words[*]}" -- "$cur")' echo $' return' echo $' ;;' @@ -407,12 +414,18 @@ send_completions() { echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "${words[*]}" -- "$cur")' echo $' return' echo $' ;;' + echo $' 18)' + echo $' local words=()' + echo $' _rush_completions_option_seen "--help" "-h" || words+=("--help" "-h")' + echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "${words[*]}" -- "$cur")' + echo $' return' + echo $' ;;' echo $' esac' echo $' fi' echo $'' echo $' case "$node_id:$positional_index" in' echo $' 0:0)' - echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "add a remove r clone pull p download update push upload config c default get g undo u snatch copy info i list l ls search s edit e show completions $(rush list -s -a)" -- "$cur")' + echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "add a remove r clone pull p download update push upload config c default get g undo u snatch copy info i list l ls menu search s edit e show completions $(rush list -s -a)" -- "$cur")' echo $' return' echo $' ;;' echo $' 1:0)' @@ -473,17 +486,20 @@ send_completions() { echo $' return' echo $' ;;' echo $' 15:0)' + echo $' return' + echo $' ;;' + echo $' 16:0)' echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(rush list -s -a)" -- "$cur")' echo $' return' echo $' ;;' - echo $' 15:1)' + echo $' 16:1)' echo $' return' echo $' ;;' - echo $' 16:0)' + echo $' 17:0)' echo $' while read -r; do COMPREPLY+=("$REPLY"); done < <(compgen -W "$(rush list -s -a)" -- "$cur")' echo $' return' echo $' ;;' - echo $' 16:1)' + echo $' 17:1)' echo $' return' echo $' ;;' echo $' esac' From f54f4cf58ba1584a24b4d1a1d7da40e37ed5d54a Mon Sep 17 00:00:00 2001 From: Danny Ben Shitrit Date: Sun, 5 Jul 2026 10:37:39 +0300 Subject: [PATCH 3/4] add tests for rush menu --- features/fixtures/bin/fzf | 19 +++++++++++ features/menu.feature | 46 +++++++++++++++++++++++++++ features/step_definitions/stubbing.sh | 13 ++++++++ features/support.sh | 24 +++++++++++++- 4 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 features/fixtures/bin/fzf create mode 100644 features/menu.feature diff --git a/features/fixtures/bin/fzf b/features/fixtures/bin/fzf new file mode 100644 index 0000000..4f25930 --- /dev/null +++ b/features/fixtures/bin/fzf @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +selection=${STUB_FZF_SELECTION:-hello} +input=$(cat) + +if [[ ${STUB_FZF_INPUT-} ]]; then + printf "%s\n" "$input" >"$STUB_FZF_INPUT" +fi + +while IFS= read -r line; do + plain=$(printf "%s" "$line" | sed -E $'s/\x1B\\[[0-9;]*[[:alpha:]]//g') + package=${plain%%[[:space:]]*} + + if [[ $package == "$selection" ]]; then + printf "%s\n" "$line" + exit 0 + fi +done <<<"$input" + +exit 1 diff --git a/features/menu.feature b/features/menu.feature new file mode 100644 index 0000000..3ed0650 --- /dev/null +++ b/features/menu.feature @@ -0,0 +1,46 @@ +Feature: menu + Show packages in one or all repositories using fzf interactive menu + +Scenario: Show --help + When I run 'rush menu --help' + Then the exit code should mean success + And stdout should include 'Select a package from an interactive menu' + And stdout should include '--install, -i' + +Scenario: Show packages in the default repository + Given rush is properly configured + And the command 'fzf' is stubbed + And my fzf selection will be 'hello' + When I run 'rush menu' + Then the fzf menu should show + """ + ▌ package-in-package/two Package two + ▌ package-in-package/one Package one + ▌ nested/hi Shows how to use nested folders + ▌ hello This is a simple example. + ▌ download Shows how a script can copy files from its own folder + ▌ bootstrap Shows how to run another command from the same reposi· + """ + And the exit code should mean success + And stdout should include 'This info file can be accessed by running' + +Scenario: Show packages in the a nested package + Given rush is properly configured + And the command 'fzf' is stubbed + And my fzf selection will be 'nested/hi' + When I run 'rush menu nested' + Then the fzf menu should show + """ + ▌ nested/hi Shows how to use nested folders + """ + Then the exit code should mean success + And stdout should include '$ rush info nested/hi' + +Scenario: Install package + Given rush is properly configured + And the command 'fzf' is stubbed + And my fzf selection will be 'hello' + When I run 'rush menu --install' + Then the exit code should mean success + And stdout should include 'What's the rush' + And stdout should include 'REPO: default' diff --git a/features/step_definitions/stubbing.sh b/features/step_definitions/stubbing.sh index d38e5b3..e5c1cd2 100644 --- a/features/step_definitions/stubbing.sh +++ b/features/step_definitions/stubbing.sh @@ -1,2 +1,15 @@ @Given the command '{command}' is stubbed install_stubbed_command "$command" + +@Given my fzf selection will be '{selection}' + export STUB_FZF_INPUT="$TMPDIR/fzf-input" + export STUB_FZF_SELECTION="$selection" + defer unset STUB_FZF_INPUT + defer unset STUB_FZF_SELECTION + +@Then the fzf menu should show + [[ -f "$STUB_FZF_INPUT" ]] || fail "fzf input was not captured" + + actual=$(fzf_menu_display "$STUB_FZF_INPUT") + + [[ "$actual" == "$DOC_STRING" ]] || fail "expected fzf menu to show:\n$DOC_STRING\n\ngot:\n$actual" diff --git a/features/support.sh b/features/support.sh index daa40c5..8e125bc 100644 --- a/features/support.sh +++ b/features/support.sh @@ -19,6 +19,24 @@ stub_fixture_root() { printf '%s\n' "$SUPPORT_PROJECT_ROOT/features/fixtures/bin" } +strip_ansi() { + local escape + escape=$(printf '\033') + sed -E "s/${escape}\\[[0-9;]*[[:alpha:]]//g" "$@" +} + +fzf_menu_display() { + strip_ansi "$1" | + tac | + while IFS= read -r line; do + if ((${#line} > 75)); then + line="${line:0:75}·" + fi + + printf "▌ %s\n" "$line" + done +} + prepare_isolated_environment() { local old_pwd="$PWD" local old_home="${HOME-}" @@ -75,7 +93,11 @@ install_stubbed_command() { stub_root=$(mktemp -d) stub_path="$stub_root/$command" - [[ -f "$fixture_root/$command" ]] || fail "missing stub fixture: $fixture_root/$command" + if [[ ! -f "$fixture_root/$command" ]]; then + fail "missing stub fixture: $fixture_root/$command" + return + fi + cp "$fixture_root/$command" "$stub_path" chmod +x "$stub_path" export PATH="$stub_root:$PATH" From c50cd771c5cb07cd701064840847d0a02397d1f6 Mon Sep 17 00:00:00 2001 From: Danny Ben Shitrit Date: Sun, 5 Jul 2026 10:50:58 +0300 Subject: [PATCH 4/4] remove ansi colors from fzf menu input --- features/fixtures/bin/fzf | 3 +-- features/support.sh | 9 +-------- rush | 7 +++---- src/commands/menu.sh | 7 +++---- 4 files changed, 8 insertions(+), 18 deletions(-) diff --git a/features/fixtures/bin/fzf b/features/fixtures/bin/fzf index 4f25930..09b0069 100644 --- a/features/fixtures/bin/fzf +++ b/features/fixtures/bin/fzf @@ -7,8 +7,7 @@ if [[ ${STUB_FZF_INPUT-} ]]; then fi while IFS= read -r line; do - plain=$(printf "%s" "$line" | sed -E $'s/\x1B\\[[0-9;]*[[:alpha:]]//g') - package=${plain%%[[:space:]]*} + package=${line%%[[:space:]]*} if [[ $package == "$selection" ]]; then printf "%s\n" "$line" diff --git a/features/support.sh b/features/support.sh index 8e125bc..8709fc0 100644 --- a/features/support.sh +++ b/features/support.sh @@ -19,15 +19,8 @@ stub_fixture_root() { printf '%s\n' "$SUPPORT_PROJECT_ROOT/features/fixtures/bin" } -strip_ansi() { - local escape - escape=$(printf '\033') - sed -E "s/${escape}\\[[0-9;]*[[:alpha:]]//g" "$@" -} - fzf_menu_display() { - strip_ansi "$1" | - tac | + tac "$1" | while IFS= read -r line; do if ((${#line} > 75)); then line="${line:0:75}·" diff --git a/rush b/rush index c5270a3..e4dc066 100755 --- a/rush +++ b/rush @@ -2223,16 +2223,15 @@ rush_menu_command() { [[ $repo_or_package ]] && list_args+=("$repo_or_package") - items=$(rush list "${list_args[@]}") + items=$(NO_COLOR=1 rush list "${list_args[@]}") - if ! selected=$(printf "%s\n" "$items" | fzf --ansi); then + if ! selected=$(printf "%s\n" "$items" | fzf --color='fg+:green:bold,bg+:-1,pointer:green:bold'); then exit 0 fi [[ $selected ]] || exit 0 - plain=$(printf "%s" "$selected" | sed -E $'s/\x1B\\[[0-9;]*[[:alpha:]]//g') - package=${plain%%[[:space:]]*} + package=${selected%%[[:space:]]*} [[ $package ]] || exit 0 diff --git a/src/commands/menu.sh b/src/commands/menu.sh index b2a95c5..1371701 100644 --- a/src/commands/menu.sh +++ b/src/commands/menu.sh @@ -4,16 +4,15 @@ list_args=(--all) [[ $repo_or_package ]] && list_args+=("$repo_or_package") -items=$(rush list "${list_args[@]}") +items=$(NO_COLOR=1 rush list "${list_args[@]}") -if ! selected=$(printf "%s\n" "$items" | fzf --ansi); then +if ! selected=$(printf "%s\n" "$items" | fzf --color='fg+:green:bold,bg+:-1,pointer:green:bold'); then exit 0 fi [[ $selected ]] || exit 0 -plain=$(printf "%s" "$selected" | sed -E $'s/\x1B\\[[0-9;]*[[:alpha:]]//g') -package=${plain%%[[:space:]]*} +package=${selected%%[[:space:]]*} [[ $package ]] || exit 0