fpf-cli 1.6.8 → 1.6.10
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 -1
- package/fpf +144 -17
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -34,10 +34,12 @@ fpf -U
|
|
|
34
34
|
|
|
35
35
|
By default, `fpf` auto-detects your package manager.
|
|
36
36
|
|
|
37
|
-
On every OS, default auto mode
|
|
37
|
+
On every OS, default auto mode includes all supported detected managers. If both `bun` and `npm` are available, auto mode keeps `bun` and skips `npm`.
|
|
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, with a minimum query length and debounce to reduce lag while typing.
|
|
42
|
+
|
|
41
43
|
## Supported Managers
|
|
42
44
|
|
|
43
45
|
- Linux: `apt`, `dnf`, `pacman`, `zypper`, `emerge`
|
|
@@ -86,3 +88,7 @@ Installed packages are marked with `*` in the result list.
|
|
|
86
88
|
- Requires: `bash` + `fzf`
|
|
87
89
|
- If `fzf` is missing, `fpf` auto-installs it using a compatible detected manager.
|
|
88
90
|
- Root managers (`apt`, `dnf`, `pacman`, `zypper`, `emerge`, `snap`) use `sudo` when needed.
|
|
91
|
+
- `FPF_DYNAMIC_RELOAD`: `always` (default), `single`, or `never`
|
|
92
|
+
- `FPF_RELOAD_MIN_CHARS`: minimum query length before live reload (default `2`)
|
|
93
|
+
- `FPF_RELOAD_DEBOUNCE`: reload debounce seconds (default `0.12`)
|
|
94
|
+
- `FPF_DISABLE_INSTALLED_CACHE=1` disables installed-package marker cache
|
package/fpf
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
set -euo pipefail
|
|
4
4
|
|
|
5
5
|
SCRIPT_NAME="fpf"
|
|
6
|
-
SCRIPT_VERSION="1.6.
|
|
6
|
+
SCRIPT_VERSION="1.6.10"
|
|
7
7
|
TMP_ROOT="${TMPDIR:-/tmp}/fpf"
|
|
8
8
|
SESSION_TMP_ROOT=""
|
|
9
9
|
HELP_FILE=""
|
|
@@ -300,8 +300,8 @@ detect_default_manager() {
|
|
|
300
300
|
if command_exists winget; then printf "winget"; return; fi
|
|
301
301
|
if command_exists choco; then printf "choco"; return; fi
|
|
302
302
|
if command_exists scoop; then printf "scoop"; return; fi
|
|
303
|
-
if command_exists npm; then printf "npm"; return; fi
|
|
304
303
|
if command_exists bun; then printf "bun"; return; fi
|
|
304
|
+
if command_exists npm; then printf "npm"; return; fi
|
|
305
305
|
fi
|
|
306
306
|
|
|
307
307
|
if [[ "${os}" == "Linux" ]]; then
|
|
@@ -349,16 +349,16 @@ detect_default_manager() {
|
|
|
349
349
|
if command_exists emerge; then printf "emerge"; return; fi
|
|
350
350
|
if command_exists snap; then printf "snap"; return; fi
|
|
351
351
|
if command_exists flatpak; then printf "flatpak"; return; fi
|
|
352
|
-
if command_exists npm; then printf "npm"; return; fi
|
|
353
352
|
if command_exists bun; then printf "bun"; return; fi
|
|
353
|
+
if command_exists npm; then printf "npm"; return; fi
|
|
354
354
|
fi
|
|
355
355
|
|
|
356
356
|
if command_exists brew; then printf "brew"; return; fi
|
|
357
357
|
if command_exists winget; then printf "winget"; return; fi
|
|
358
358
|
if command_exists choco; then printf "choco"; return; fi
|
|
359
359
|
if command_exists scoop; then printf "scoop"; return; fi
|
|
360
|
-
if command_exists npm; then printf "npm"; return; fi
|
|
361
360
|
if command_exists bun; then printf "bun"; return; fi
|
|
361
|
+
if command_exists npm; then printf "npm"; return; fi
|
|
362
362
|
|
|
363
363
|
die "Unable to auto-detect a supported package manager. Use --manager."
|
|
364
364
|
}
|
|
@@ -367,10 +367,20 @@ detect_default_managers() {
|
|
|
367
367
|
local emitted=""
|
|
368
368
|
local primary_manager=""
|
|
369
369
|
local manager
|
|
370
|
+
local prefer_bun=0
|
|
371
|
+
|
|
372
|
+
if manager_command_ready bun; then
|
|
373
|
+
prefer_bun=1
|
|
374
|
+
fi
|
|
370
375
|
|
|
371
376
|
add_detected_manager() {
|
|
372
377
|
local manager_name="$1"
|
|
373
378
|
[[ -n "${manager_name}" ]] || return
|
|
379
|
+
|
|
380
|
+
if [[ "${prefer_bun}" -eq 1 && "${manager_name}" == "npm" ]]; then
|
|
381
|
+
return
|
|
382
|
+
fi
|
|
383
|
+
|
|
374
384
|
case " ${emitted} " in
|
|
375
385
|
*" ${manager_name} "*)
|
|
376
386
|
return
|
|
@@ -385,7 +395,7 @@ detect_default_managers() {
|
|
|
385
395
|
primary_manager="$(detect_default_manager)"
|
|
386
396
|
add_detected_manager "${primary_manager}"
|
|
387
397
|
|
|
388
|
-
for manager in apt dnf pacman zypper emerge brew winget choco scoop snap flatpak npm
|
|
398
|
+
for manager in apt dnf pacman zypper emerge brew winget choco scoop snap flatpak bun npm; do
|
|
389
399
|
add_detected_manager "${manager}"
|
|
390
400
|
done
|
|
391
401
|
|
|
@@ -608,6 +618,79 @@ join_query() {
|
|
|
608
618
|
printf "%s" "${query}"
|
|
609
619
|
}
|
|
610
620
|
|
|
621
|
+
dynamic_reload_enabled() {
|
|
622
|
+
local manager_count="$1"
|
|
623
|
+
local mode="${FPF_DYNAMIC_RELOAD:-always}"
|
|
624
|
+
|
|
625
|
+
case "${mode}" in
|
|
626
|
+
always|auto|on|1|true|yes)
|
|
627
|
+
return 0
|
|
628
|
+
;;
|
|
629
|
+
never|off|0|false|no)
|
|
630
|
+
return 1
|
|
631
|
+
;;
|
|
632
|
+
single)
|
|
633
|
+
[[ "${manager_count}" -eq 1 ]]
|
|
634
|
+
;;
|
|
635
|
+
*)
|
|
636
|
+
return 0
|
|
637
|
+
;;
|
|
638
|
+
esac
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
rank_display_rows_by_query() {
|
|
642
|
+
local query="$1"
|
|
643
|
+
local input_file="$2"
|
|
644
|
+
local ranked_file
|
|
645
|
+
|
|
646
|
+
[[ -n "${query}" ]] || return 0
|
|
647
|
+
|
|
648
|
+
ranked_file="$(mktemp "${SESSION_TMP_ROOT}/ranked.XXXXXX")"
|
|
649
|
+
|
|
650
|
+
awk -F'\t' -v query="${query}" '
|
|
651
|
+
function lower(s) { return tolower(s) }
|
|
652
|
+
BEGIN {
|
|
653
|
+
q = lower(query)
|
|
654
|
+
}
|
|
655
|
+
{
|
|
656
|
+
mgr = $1
|
|
657
|
+
pkg = $2
|
|
658
|
+
desc = $3
|
|
659
|
+
|
|
660
|
+
pkg_l = lower(pkg)
|
|
661
|
+
desc_l = lower(desc)
|
|
662
|
+
|
|
663
|
+
score = 8
|
|
664
|
+
if (q != "") {
|
|
665
|
+
if (pkg_l == q) {
|
|
666
|
+
score = 0
|
|
667
|
+
} else if (index(pkg_l, q) == 1) {
|
|
668
|
+
score = 1
|
|
669
|
+
} else if (index(pkg_l, q) > 0) {
|
|
670
|
+
score = 2
|
|
671
|
+
} else if (index(desc_l, q) > 0) {
|
|
672
|
+
score = 3
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
mgr_score = 9
|
|
677
|
+
if (mgr == "bun") {
|
|
678
|
+
mgr_score = 0
|
|
679
|
+
} else if (mgr == "npm") {
|
|
680
|
+
mgr_score = 1
|
|
681
|
+
} else if (mgr == "brew") {
|
|
682
|
+
mgr_score = 2
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
print score "\t" mgr_score "\t" length(pkg) "\t" pkg_l "\t" $0
|
|
686
|
+
}
|
|
687
|
+
' "${input_file}" |
|
|
688
|
+
sort -t $'\t' -k1,1n -k2,2n -k3,3n -k4,4 |
|
|
689
|
+
awk -F'\t' '{ print $5 "\t" $6 "\t" $7 }' >"${ranked_file}"
|
|
690
|
+
|
|
691
|
+
mv "${ranked_file}" "${input_file}"
|
|
692
|
+
}
|
|
693
|
+
|
|
611
694
|
query_is_single_token() {
|
|
612
695
|
local query="$1"
|
|
613
696
|
[[ -n "${query}" && "${query}" != *[[:space:]]* ]]
|
|
@@ -644,7 +727,7 @@ manager_search_entries() {
|
|
|
644
727
|
|
|
645
728
|
if [[ -z "${query}" ]]; then
|
|
646
729
|
npm_search_limit="${FPF_NO_QUERY_NPM_LIMIT:-120}"
|
|
647
|
-
line_limit="${FPF_NO_QUERY_RESULT_LIMIT:-
|
|
730
|
+
line_limit="${FPF_NO_QUERY_RESULT_LIMIT:-120}"
|
|
648
731
|
fi
|
|
649
732
|
|
|
650
733
|
if ! [[ "${npm_search_limit}" =~ ^[0-9]+$ ]] || [[ "${npm_search_limit}" -eq 0 ]]; then
|
|
@@ -821,7 +904,7 @@ manager_search_entries() {
|
|
|
821
904
|
exact_match_entry "${manager}" "${query}"
|
|
822
905
|
} || true
|
|
823
906
|
;;
|
|
824
|
-
esac | awk -F'\t' 'NF >= 1 { if ($2 == "") $2 = "-"; print $1 "\t" $2 }' |
|
|
907
|
+
esac | awk -F'\t' 'NF >= 1 { if ($2 == "") $2 = "-"; print $1 "\t" $2 }' | awk -F'\t' '!seen[$1]++' | {
|
|
825
908
|
if [[ "${line_limit}" -gt 0 ]]; then
|
|
826
909
|
awk -v limit="${line_limit}" 'NR <= limit'
|
|
827
910
|
else
|
|
@@ -948,14 +1031,45 @@ manager_installed_names() {
|
|
|
948
1031
|
manager_installed_entries "${manager}" | awk -F'\t' 'NF > 0 { print $1 }'
|
|
949
1032
|
}
|
|
950
1033
|
|
|
1034
|
+
manager_installed_names_cached() {
|
|
1035
|
+
local manager="$1"
|
|
1036
|
+
local output_file="$2"
|
|
1037
|
+
local cache_enabled="${FPF_DISABLE_INSTALLED_CACHE:-0}"
|
|
1038
|
+
local cache_dir="${TMP_ROOT}/cache"
|
|
1039
|
+
local cache_file="${cache_dir}/installed.${manager}"
|
|
1040
|
+
|
|
1041
|
+
if [[ "${cache_enabled}" != "1" && -s "${cache_file}" ]]; then
|
|
1042
|
+
cp "${cache_file}" "${output_file}"
|
|
1043
|
+
return
|
|
1044
|
+
fi
|
|
1045
|
+
|
|
1046
|
+
manager_installed_names "${manager}" >"${output_file}" 2>/dev/null || true
|
|
1047
|
+
|
|
1048
|
+
if [[ "${cache_enabled}" != "1" && -s "${output_file}" ]]; then
|
|
1049
|
+
mkdir -p "${cache_dir}"
|
|
1050
|
+
cp "${output_file}" "${cache_file}"
|
|
1051
|
+
fi
|
|
1052
|
+
}
|
|
1053
|
+
|
|
951
1054
|
mark_installed_packages() {
|
|
952
1055
|
local manager="$1"
|
|
953
1056
|
local source_file="$2"
|
|
954
1057
|
local output_file="$3"
|
|
955
1058
|
local installed_file
|
|
956
1059
|
|
|
1060
|
+
if [[ "${FPF_SKIP_INSTALLED_MARKERS:-0}" == "1" ]]; then
|
|
1061
|
+
awk -F'\t' '
|
|
1062
|
+
{
|
|
1063
|
+
desc = $2
|
|
1064
|
+
if (desc == "") desc = "-"
|
|
1065
|
+
print $1 "\t " desc
|
|
1066
|
+
}
|
|
1067
|
+
' "${source_file}" >"${output_file}"
|
|
1068
|
+
return
|
|
1069
|
+
fi
|
|
1070
|
+
|
|
957
1071
|
installed_file="$(mktemp "${SESSION_TMP_ROOT}/installed.XXXXXX")"
|
|
958
|
-
|
|
1072
|
+
manager_installed_names_cached "${manager}" "${installed_file}"
|
|
959
1073
|
|
|
960
1074
|
awk -F'\t' '
|
|
961
1075
|
FILENAME == ARGV[1] {
|
|
@@ -965,7 +1079,7 @@ mark_installed_packages() {
|
|
|
965
1079
|
next
|
|
966
1080
|
}
|
|
967
1081
|
{
|
|
968
|
-
mark = (installed[$1] ? "
|
|
1082
|
+
mark = (installed[$1] ? "* " : " ")
|
|
969
1083
|
desc = $2
|
|
970
1084
|
if (desc == "") desc = "-"
|
|
971
1085
|
print $1 "\t" mark desc
|
|
@@ -1025,21 +1139,33 @@ collect_search_display_rows() {
|
|
|
1025
1139
|
|
|
1026
1140
|
if [[ -s "${output_file}" ]]; then
|
|
1027
1141
|
sort -u "${output_file}" -o "${output_file}"
|
|
1142
|
+
rank_display_rows_by_query "${query}" "${output_file}"
|
|
1028
1143
|
fi
|
|
1029
1144
|
}
|
|
1030
1145
|
|
|
1031
1146
|
build_dynamic_reload_command() {
|
|
1032
1147
|
local manager_override="$1"
|
|
1148
|
+
local fallback_file="$2"
|
|
1033
1149
|
local script_path="${BASH_SOURCE[0]}"
|
|
1150
|
+
local min_chars="${FPF_RELOAD_MIN_CHARS:-2}"
|
|
1151
|
+
local reload_debounce="${FPF_RELOAD_DEBOUNCE:-0.12}"
|
|
1152
|
+
|
|
1153
|
+
if ! [[ "${min_chars}" =~ ^[0-9]+$ ]]; then
|
|
1154
|
+
min_chars=2
|
|
1155
|
+
fi
|
|
1156
|
+
|
|
1157
|
+
if ! [[ "${reload_debounce}" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
|
|
1158
|
+
reload_debounce=0.12
|
|
1159
|
+
fi
|
|
1034
1160
|
|
|
1035
1161
|
if [[ "${script_path}" != /* ]]; then
|
|
1036
1162
|
script_path="$(pwd)/${script_path}"
|
|
1037
1163
|
fi
|
|
1038
1164
|
|
|
1039
1165
|
if [[ -n "${manager_override}" ]]; then
|
|
1040
|
-
printf
|
|
1166
|
+
printf 'q={q}; if [ ${#q} -lt %s ]; then cat %q; else sleep %s; FPF_SKIP_INSTALLED_MARKERS=1 %q --feed-search --manager %q -- "$q" 2>/dev/null || cat %q; fi' "${min_chars}" "${fallback_file}" "${reload_debounce}" "${script_path}" "${manager_override}" "${fallback_file}"
|
|
1041
1167
|
else
|
|
1042
|
-
printf
|
|
1168
|
+
printf 'q={q}; if [ ${#q} -lt %s ]; then cat %q; else sleep %s; FPF_SKIP_INSTALLED_MARKERS=1 %q --feed-search -- "$q" 2>/dev/null || cat %q; fi' "${min_chars}" "${fallback_file}" "${reload_debounce}" "${script_path}" "${fallback_file}"
|
|
1043
1169
|
fi
|
|
1044
1170
|
}
|
|
1045
1171
|
|
|
@@ -1282,23 +1408,22 @@ run_fuzzy_selector() {
|
|
|
1282
1408
|
--delimiter=$'\t' \
|
|
1283
1409
|
--with-nth=1,2,3 \
|
|
1284
1410
|
--preview="${preview_cmd}" \
|
|
1285
|
-
--preview-window=55%:wrap:border-sharp \
|
|
1411
|
+
--preview-window=55%:wrap:border-sharp:hidden \
|
|
1286
1412
|
--layout=reverse \
|
|
1287
1413
|
--marker='>>' \
|
|
1414
|
+
--prompt='Search> ' \
|
|
1288
1415
|
--header="${header_line}" \
|
|
1289
1416
|
--info=inline-right \
|
|
1290
|
-
--ansi \
|
|
1291
1417
|
--margin="2%,1%,2%,1%" \
|
|
1292
1418
|
--cycle \
|
|
1293
1419
|
--tiebreak=begin,chunk,length \
|
|
1294
1420
|
--bind=ctrl-k:preview:"cat ${KBINDS_FILE}" \
|
|
1295
1421
|
--bind=ctrl-h:preview:"cat ${HELP_FILE}" \
|
|
1296
1422
|
--bind='ctrl-/:change-preview-window(hidden|)' \
|
|
1297
|
-
--bind=ctrl-n:next-selected,ctrl-b:prev-selected
|
|
1298
|
-
--bind='focus:transform-preview-label:echo [{1}] {2}')
|
|
1423
|
+
--bind=ctrl-n:next-selected,ctrl-b:prev-selected)
|
|
1299
1424
|
|
|
1300
1425
|
if [[ -n "${reload_cmd}" ]]; then
|
|
1301
|
-
fzf_args+=(--
|
|
1426
|
+
fzf_args+=(--bind="change:reload:${reload_cmd}")
|
|
1302
1427
|
else
|
|
1303
1428
|
fzf_args+=(-e)
|
|
1304
1429
|
fi
|
|
@@ -1465,7 +1590,9 @@ main() {
|
|
|
1465
1590
|
|
|
1466
1591
|
local reload_cmd=""
|
|
1467
1592
|
if [[ "${ACTION}" == "search" && -z "${query}" ]]; then
|
|
1468
|
-
|
|
1593
|
+
if dynamic_reload_enabled "${#managers[@]}"; then
|
|
1594
|
+
reload_cmd="$(build_dynamic_reload_command "${MANAGER_OVERRIDE}" "${display_file}")"
|
|
1595
|
+
fi
|
|
1469
1596
|
fi
|
|
1470
1597
|
|
|
1471
1598
|
local selected
|