fpf-cli 1.6.7 → 1.6.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -0
- package/fpf +117 -73
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -38,6 +38,8 @@ On every OS, default auto mode now includes all supported package managers that
|
|
|
38
38
|
|
|
39
39
|
For no-query startup (`fpf`), each manager uses a lighter default query and per-manager result cap to keep startup responsive.
|
|
40
40
|
|
|
41
|
+
Live reload is enabled by default for single-manager mode (`--manager`), and disabled by default in all-manager mode to avoid lag/flicker while navigating.
|
|
42
|
+
|
|
41
43
|
## Supported Managers
|
|
42
44
|
|
|
43
45
|
- Linux: `apt`, `dnf`, `pacman`, `zypper`, `emerge`
|
|
@@ -68,6 +70,7 @@ For no-query startup (`fpf`), each manager uses a lighter default query and per-
|
|
|
68
70
|
- `-l, --list-installed` list installed packages
|
|
69
71
|
- `-R, --remove` remove selected packages
|
|
70
72
|
- `-U, --update` run update/upgrade flow
|
|
73
|
+
- `-v, --version` print version and exit
|
|
71
74
|
- `-h, --help` show help
|
|
72
75
|
|
|
73
76
|
## Keybinds
|
|
@@ -78,8 +81,12 @@ For no-query startup (`fpf`), each manager uses a lighter default query and per-
|
|
|
78
81
|
- `ctrl-n` next selected item
|
|
79
82
|
- `ctrl-b` previous selected item
|
|
80
83
|
|
|
84
|
+
Installed packages are marked with `*` in the result list.
|
|
85
|
+
|
|
81
86
|
## Notes
|
|
82
87
|
|
|
83
88
|
- Requires: `bash` + `fzf`
|
|
84
89
|
- If `fzf` is missing, `fpf` auto-installs it using a compatible detected manager.
|
|
85
90
|
- Root managers (`apt`, `dnf`, `pacman`, `zypper`, `emerge`, `snap`) use `sudo` when needed.
|
|
91
|
+
- `FPF_DYNAMIC_RELOAD`: `single` (default), `always`, or `never`
|
|
92
|
+
- `FPF_DISABLE_INSTALLED_CACHE=1` disables installed-package marker cache
|
package/fpf
CHANGED
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
set -euo pipefail
|
|
4
4
|
|
|
5
5
|
SCRIPT_NAME="fpf"
|
|
6
|
+
SCRIPT_VERSION="1.6.9"
|
|
6
7
|
TMP_ROOT="${TMPDIR:-/tmp}/fpf"
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
SESSION_TMP_ROOT=""
|
|
9
|
+
HELP_FILE=""
|
|
10
|
+
KBINDS_FILE=""
|
|
9
11
|
|
|
10
12
|
ACTION="search"
|
|
11
13
|
MANAGER_OVERRIDE=""
|
|
@@ -28,6 +30,22 @@ ensure_tmp_root() {
|
|
|
28
30
|
mkdir -p "${TMP_ROOT}"
|
|
29
31
|
}
|
|
30
32
|
|
|
33
|
+
initialize_session_tmp_root() {
|
|
34
|
+
if [[ -n "${SESSION_TMP_ROOT}" ]]; then
|
|
35
|
+
return
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
SESSION_TMP_ROOT="$(mktemp -d "${TMP_ROOT}/session.XXXXXX")"
|
|
39
|
+
HELP_FILE="${SESSION_TMP_ROOT}/help"
|
|
40
|
+
KBINDS_FILE="${SESSION_TMP_ROOT}/keybinds"
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
cleanup_session_tmp_root() {
|
|
44
|
+
if [[ -n "${SESSION_TMP_ROOT}" && -d "${SESSION_TMP_ROOT}" ]]; then
|
|
45
|
+
rm -rf "${SESSION_TMP_ROOT}"
|
|
46
|
+
fi
|
|
47
|
+
}
|
|
48
|
+
|
|
31
49
|
run_as_root() {
|
|
32
50
|
if [[ "${EUID}" -eq 0 ]]; then
|
|
33
51
|
"$@"
|
|
@@ -412,7 +430,7 @@ build_help_file() {
|
|
|
412
430
|
local default_managers="$1"
|
|
413
431
|
|
|
414
432
|
cat >"${HELP_FILE}" <<EOF
|
|
415
|
-
${SCRIPT_NAME} -
|
|
433
|
+
${SCRIPT_NAME} - fuzzy package finder
|
|
416
434
|
|
|
417
435
|
Syntax:
|
|
418
436
|
${SCRIPT_NAME} [manager option] [action option] [query]
|
|
@@ -428,6 +446,7 @@ Action options:
|
|
|
428
446
|
-l, --list-installed Fuzzy-search installed packages and show details
|
|
429
447
|
-R, --remove Fuzzy-search installed packages and remove selected
|
|
430
448
|
-U, --update Run manager update/upgrade flow
|
|
449
|
+
-v, --version Print version and exit
|
|
431
450
|
-h, --help Show this help
|
|
432
451
|
|
|
433
452
|
Manager options (one or two-letter style):
|
|
@@ -479,12 +498,19 @@ print_help() {
|
|
|
479
498
|
cat "${HELP_FILE}"
|
|
480
499
|
}
|
|
481
500
|
|
|
501
|
+
print_version() {
|
|
502
|
+
printf "%s %s\n" "${SCRIPT_NAME}" "${SCRIPT_VERSION}"
|
|
503
|
+
}
|
|
504
|
+
|
|
482
505
|
parse_args() {
|
|
483
506
|
while (($#)); do
|
|
484
507
|
case "$1" in
|
|
485
508
|
-h|--help)
|
|
486
509
|
ACTION="help"
|
|
487
510
|
;;
|
|
511
|
+
-v|--version)
|
|
512
|
+
ACTION="version"
|
|
513
|
+
;;
|
|
488
514
|
-l|--list-installed)
|
|
489
515
|
ACTION="list"
|
|
490
516
|
;;
|
|
@@ -582,6 +608,23 @@ join_query() {
|
|
|
582
608
|
printf "%s" "${query}"
|
|
583
609
|
}
|
|
584
610
|
|
|
611
|
+
dynamic_reload_enabled() {
|
|
612
|
+
local manager_count="$1"
|
|
613
|
+
local mode="${FPF_DYNAMIC_RELOAD:-single}"
|
|
614
|
+
|
|
615
|
+
case "${mode}" in
|
|
616
|
+
always|on|1|true|yes)
|
|
617
|
+
return 0
|
|
618
|
+
;;
|
|
619
|
+
never|off|0|false|no)
|
|
620
|
+
return 1
|
|
621
|
+
;;
|
|
622
|
+
single|auto|*)
|
|
623
|
+
[[ "${manager_count}" -eq 1 ]]
|
|
624
|
+
;;
|
|
625
|
+
esac
|
|
626
|
+
}
|
|
627
|
+
|
|
585
628
|
query_is_single_token() {
|
|
586
629
|
local query="$1"
|
|
587
630
|
[[ -n "${query}" && "${query}" != *[[:space:]]* ]]
|
|
@@ -922,14 +965,45 @@ manager_installed_names() {
|
|
|
922
965
|
manager_installed_entries "${manager}" | awk -F'\t' 'NF > 0 { print $1 }'
|
|
923
966
|
}
|
|
924
967
|
|
|
968
|
+
manager_installed_names_cached() {
|
|
969
|
+
local manager="$1"
|
|
970
|
+
local output_file="$2"
|
|
971
|
+
local cache_enabled="${FPF_DISABLE_INSTALLED_CACHE:-0}"
|
|
972
|
+
local cache_dir="${TMP_ROOT}/cache"
|
|
973
|
+
local cache_file="${cache_dir}/installed.${manager}"
|
|
974
|
+
|
|
975
|
+
if [[ "${cache_enabled}" != "1" && -s "${cache_file}" ]]; then
|
|
976
|
+
cp "${cache_file}" "${output_file}"
|
|
977
|
+
return
|
|
978
|
+
fi
|
|
979
|
+
|
|
980
|
+
manager_installed_names "${manager}" >"${output_file}" 2>/dev/null || true
|
|
981
|
+
|
|
982
|
+
if [[ "${cache_enabled}" != "1" && -s "${output_file}" ]]; then
|
|
983
|
+
mkdir -p "${cache_dir}"
|
|
984
|
+
cp "${output_file}" "${cache_file}"
|
|
985
|
+
fi
|
|
986
|
+
}
|
|
987
|
+
|
|
925
988
|
mark_installed_packages() {
|
|
926
989
|
local manager="$1"
|
|
927
990
|
local source_file="$2"
|
|
928
991
|
local output_file="$3"
|
|
929
992
|
local installed_file
|
|
930
993
|
|
|
931
|
-
|
|
932
|
-
|
|
994
|
+
if [[ "${FPF_SKIP_INSTALLED_MARKERS:-0}" == "1" ]]; then
|
|
995
|
+
awk -F'\t' '
|
|
996
|
+
{
|
|
997
|
+
desc = $2
|
|
998
|
+
if (desc == "") desc = "-"
|
|
999
|
+
print $1 "\t " desc
|
|
1000
|
+
}
|
|
1001
|
+
' "${source_file}" >"${output_file}"
|
|
1002
|
+
return
|
|
1003
|
+
fi
|
|
1004
|
+
|
|
1005
|
+
installed_file="$(mktemp "${SESSION_TMP_ROOT}/installed.XXXXXX")"
|
|
1006
|
+
manager_installed_names_cached "${manager}" "${installed_file}"
|
|
933
1007
|
|
|
934
1008
|
awk -F'\t' '
|
|
935
1009
|
FILENAME == ARGV[1] {
|
|
@@ -964,12 +1038,12 @@ collect_search_display_rows() {
|
|
|
964
1038
|
local gather_pid
|
|
965
1039
|
|
|
966
1040
|
for manager in "${managers[@]-}"; do
|
|
967
|
-
part_file="$(mktemp "${
|
|
1041
|
+
part_file="$(mktemp "${SESSION_TMP_ROOT}/part.XXXXXX")"
|
|
968
1042
|
part_files+=("${part_file}")
|
|
969
1043
|
|
|
970
1044
|
(
|
|
971
|
-
local_source_file="$(mktemp "${
|
|
972
|
-
local_marked_file="$(mktemp "${
|
|
1045
|
+
local_source_file="$(mktemp "${SESSION_TMP_ROOT}/source.XXXXXX")"
|
|
1046
|
+
local_marked_file="$(mktemp "${SESSION_TMP_ROOT}/marked.XXXXXX")"
|
|
973
1047
|
manager_search_entries "${manager}" "${query}" >"${local_source_file}" || true
|
|
974
1048
|
if [[ -s "${local_source_file}" ]]; then
|
|
975
1049
|
mark_installed_packages "${manager}" "${local_source_file}" "${local_marked_file}"
|
|
@@ -1011,58 +1085,12 @@ build_dynamic_reload_command() {
|
|
|
1011
1085
|
fi
|
|
1012
1086
|
|
|
1013
1087
|
if [[ -n "${manager_override}" ]]; then
|
|
1014
|
-
printf "%q --feed-search --manager %q -- {q} 2>/dev/null || true" "${script_path}" "${manager_override}"
|
|
1088
|
+
printf "FPF_SKIP_INSTALLED_MARKERS=1 %q --feed-search --manager %q -- {q} 2>/dev/null || true" "${script_path}" "${manager_override}"
|
|
1015
1089
|
else
|
|
1016
|
-
printf "%q --feed-search -- {q} 2>/dev/null || true" "${script_path}"
|
|
1090
|
+
printf "FPF_SKIP_INSTALLED_MARKERS=1 %q --feed-search -- {q} 2>/dev/null || true" "${script_path}"
|
|
1017
1091
|
fi
|
|
1018
1092
|
}
|
|
1019
1093
|
|
|
1020
|
-
manager_preview_command() {
|
|
1021
|
-
local manager="$1"
|
|
1022
|
-
|
|
1023
|
-
case "${manager}" in
|
|
1024
|
-
apt)
|
|
1025
|
-
printf '%s' 'pkg={1}; apt-cache show "$pkg" 2>/dev/null; printf "\n"; dpkg -L "$pkg" 2>/dev/null'
|
|
1026
|
-
;;
|
|
1027
|
-
dnf)
|
|
1028
|
-
printf '%s' 'pkg={1}; dnf info "$pkg" 2>/dev/null; printf "\n"; rpm -ql "$pkg" 2>/dev/null'
|
|
1029
|
-
;;
|
|
1030
|
-
pacman)
|
|
1031
|
-
printf '%s' 'pkg={1}; pacman -Si "$pkg" 2>/dev/null; printf "\n"; pacman -Fl "$pkg" 2>/dev/null | awk "{print \$2}"'
|
|
1032
|
-
;;
|
|
1033
|
-
zypper)
|
|
1034
|
-
printf '%s' 'pkg={1}; zypper --non-interactive info "$pkg" 2>/dev/null'
|
|
1035
|
-
;;
|
|
1036
|
-
emerge)
|
|
1037
|
-
printf '%s' 'pkg={1}; emerge --search --color=n "$pkg" 2>/dev/null'
|
|
1038
|
-
;;
|
|
1039
|
-
brew)
|
|
1040
|
-
printf '%s' 'pkg={1}; brew info "$pkg" 2>/dev/null'
|
|
1041
|
-
;;
|
|
1042
|
-
winget)
|
|
1043
|
-
printf '%s' 'pkg={1}; winget show --id "$pkg" --exact --source winget --accept-source-agreements --disable-interactivity 2>/dev/null'
|
|
1044
|
-
;;
|
|
1045
|
-
choco)
|
|
1046
|
-
printf '%s' 'pkg={1}; choco info "$pkg" 2>/dev/null'
|
|
1047
|
-
;;
|
|
1048
|
-
scoop)
|
|
1049
|
-
printf '%s' 'pkg={1}; scoop info "$pkg" 2>/dev/null'
|
|
1050
|
-
;;
|
|
1051
|
-
snap)
|
|
1052
|
-
printf '%s' 'pkg={1}; snap info "$pkg" 2>/dev/null'
|
|
1053
|
-
;;
|
|
1054
|
-
flatpak)
|
|
1055
|
-
printf '%s' 'pkg={1}; flatpak info "$pkg" 2>/dev/null || flatpak remote-info flathub "$pkg" 2>/dev/null'
|
|
1056
|
-
;;
|
|
1057
|
-
npm)
|
|
1058
|
-
printf '%s' 'pkg={1}; npm view "$pkg" 2>/dev/null'
|
|
1059
|
-
;;
|
|
1060
|
-
bun)
|
|
1061
|
-
printf '%s' 'pkg={1}; bun info "$pkg" 2>/dev/null || npm view "$pkg" 2>/dev/null'
|
|
1062
|
-
;;
|
|
1063
|
-
esac
|
|
1064
|
-
}
|
|
1065
|
-
|
|
1066
1094
|
manager_install() {
|
|
1067
1095
|
local manager="$1"
|
|
1068
1096
|
shift
|
|
@@ -1306,14 +1334,15 @@ run_fuzzy_selector() {
|
|
|
1306
1334
|
--layout=reverse \
|
|
1307
1335
|
--marker='>>' \
|
|
1308
1336
|
--header="${header_line}" \
|
|
1309
|
-
--info=
|
|
1337
|
+
--info=inline-right \
|
|
1310
1338
|
--margin="2%,1%,2%,1%" \
|
|
1311
1339
|
--cycle \
|
|
1340
|
+
--tiebreak=begin,chunk,length \
|
|
1312
1341
|
--bind=ctrl-k:preview:"cat ${KBINDS_FILE}" \
|
|
1313
1342
|
--bind=ctrl-h:preview:"cat ${HELP_FILE}" \
|
|
1314
1343
|
--bind='ctrl-/:change-preview-window(hidden|)' \
|
|
1315
1344
|
--bind=ctrl-n:next-selected,ctrl-b:prev-selected \
|
|
1316
|
-
--bind='focus:transform-preview-label:echo {1}
|
|
1345
|
+
--bind='focus:transform-preview-label:echo [{1}] {2}')
|
|
1317
1346
|
|
|
1318
1347
|
if [[ -n "${reload_cmd}" ]]; then
|
|
1319
1348
|
fzf_args+=(--disabled --bind="start:reload:${reload_cmd}" --bind="change:reload:${reload_cmd}")
|
|
@@ -1326,9 +1355,16 @@ run_fuzzy_selector() {
|
|
|
1326
1355
|
|
|
1327
1356
|
main() {
|
|
1328
1357
|
ensure_tmp_root
|
|
1358
|
+
initialize_session_tmp_root
|
|
1359
|
+
trap cleanup_session_tmp_root EXIT
|
|
1329
1360
|
|
|
1330
1361
|
parse_args "$@"
|
|
1331
1362
|
|
|
1363
|
+
if [[ "${ACTION}" == "version" ]]; then
|
|
1364
|
+
print_version
|
|
1365
|
+
exit 0
|
|
1366
|
+
fi
|
|
1367
|
+
|
|
1332
1368
|
local detected_manager
|
|
1333
1369
|
local detected_managers=()
|
|
1334
1370
|
while IFS= read -r detected_manager; do
|
|
@@ -1374,8 +1410,11 @@ main() {
|
|
|
1374
1410
|
local query
|
|
1375
1411
|
query="$(join_query)"
|
|
1376
1412
|
|
|
1377
|
-
if [[ "${ACTION}"
|
|
1413
|
+
if [[ "${ACTION}" != "feed-search" ]]; then
|
|
1378
1414
|
log "Using manager(s): ${manager_display}"
|
|
1415
|
+
fi
|
|
1416
|
+
|
|
1417
|
+
if [[ "${ACTION}" == "update" ]]; then
|
|
1379
1418
|
if confirm_action "Run update/upgrade for ${manager_display}?"; then
|
|
1380
1419
|
for manager in "${managers[@]-}"; do
|
|
1381
1420
|
log "Updating with $(manager_label "${manager}")"
|
|
@@ -1388,7 +1427,7 @@ main() {
|
|
|
1388
1427
|
fi
|
|
1389
1428
|
|
|
1390
1429
|
local display_file
|
|
1391
|
-
display_file="$(mktemp "${
|
|
1430
|
+
display_file="$(mktemp "${SESSION_TMP_ROOT}/display.XXXXXX")"
|
|
1392
1431
|
: >"${display_file}"
|
|
1393
1432
|
|
|
1394
1433
|
if [[ "${ACTION}" == "feed-search" ]]; then
|
|
@@ -1411,14 +1450,14 @@ main() {
|
|
|
1411
1450
|
local gather_pid
|
|
1412
1451
|
|
|
1413
1452
|
for manager in "${managers[@]-}"; do
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1453
|
+
part_file="$(mktemp "${SESSION_TMP_ROOT}/part.XXXXXX")"
|
|
1454
|
+
part_files+=("${part_file}")
|
|
1455
|
+
|
|
1456
|
+
(
|
|
1457
|
+
local_source_file="$(mktemp "${SESSION_TMP_ROOT}/source.XXXXXX")"
|
|
1458
|
+
manager_installed_entries "${manager}" >"${local_source_file}" || true
|
|
1459
|
+
if [[ -s "${local_source_file}" ]]; then
|
|
1460
|
+
awk -F'\t' -v mgr="${manager}" '
|
|
1422
1461
|
NF >= 1 {
|
|
1423
1462
|
desc = $2
|
|
1424
1463
|
if (desc == "") desc = "-"
|
|
@@ -1449,13 +1488,16 @@ main() {
|
|
|
1449
1488
|
|
|
1450
1489
|
if [[ ! -s "${display_file}" ]]; then
|
|
1451
1490
|
rm -f "${display_file}"
|
|
1452
|
-
|
|
1491
|
+
if [[ -n "${query}" ]]; then
|
|
1492
|
+
die "No packages found for ${manager_display} matching '${query}'. Try a broader query or --manager."
|
|
1493
|
+
fi
|
|
1494
|
+
die "No packages found for ${manager_display}. Try adding a query or using --manager."
|
|
1453
1495
|
fi
|
|
1454
1496
|
|
|
1455
1497
|
local header
|
|
1456
1498
|
case "${ACTION}" in
|
|
1457
1499
|
search)
|
|
1458
|
-
header="Select package(s) to install with ${manager_display} (TAB to multi-select)"
|
|
1500
|
+
header="Select package(s) to install with ${manager_display} (TAB to multi-select, * = installed)"
|
|
1459
1501
|
;;
|
|
1460
1502
|
list)
|
|
1461
1503
|
header="Select installed package(s) to inspect from ${manager_display}"
|
|
@@ -1470,7 +1512,9 @@ main() {
|
|
|
1470
1512
|
|
|
1471
1513
|
local reload_cmd=""
|
|
1472
1514
|
if [[ "${ACTION}" == "search" && -z "${query}" ]]; then
|
|
1473
|
-
|
|
1515
|
+
if dynamic_reload_enabled "${#managers[@]}"; then
|
|
1516
|
+
reload_cmd="$(build_dynamic_reload_command "${MANAGER_OVERRIDE}")"
|
|
1517
|
+
fi
|
|
1474
1518
|
fi
|
|
1475
1519
|
|
|
1476
1520
|
local selected
|
|
@@ -1479,7 +1523,7 @@ main() {
|
|
|
1479
1523
|
rm -f "${display_file}"
|
|
1480
1524
|
|
|
1481
1525
|
if [[ -z "${selected}" ]]; then
|
|
1482
|
-
log "
|
|
1526
|
+
log "Selection canceled"
|
|
1483
1527
|
exit 0
|
|
1484
1528
|
fi
|
|
1485
1529
|
|
|
@@ -1497,7 +1541,7 @@ main() {
|
|
|
1497
1541
|
done <<<"${selected}"
|
|
1498
1542
|
|
|
1499
1543
|
if [[ "${#selected_packages[@]}" -eq 0 ]]; then
|
|
1500
|
-
log "
|
|
1544
|
+
log "Selection canceled"
|
|
1501
1545
|
exit 0
|
|
1502
1546
|
fi
|
|
1503
1547
|
|