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.
Files changed (3) hide show
  1. package/README.md +7 -1
  2. package/fpf +144 -17
  3. 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 now includes all supported package managers that are detected on your system in one combined list.
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.8"
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 bun; do
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:-200}"
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 }' | sort -u | {
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
- manager_installed_names "${manager}" >"${installed_file}" 2>/dev/null || true
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] ? "\033[32m*\033[0m " : " ")
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 "%q --feed-search --manager %q -- {q} 2>/dev/null || true" "${script_path}" "${manager_override}"
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 "%q --feed-search -- {q} 2>/dev/null || true" "${script_path}"
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+=(--disabled --bind="start:reload:${reload_cmd}" --bind="change:reload:${reload_cmd}")
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
- reload_cmd="$(build_dynamic_reload_command "${MANAGER_OVERRIDE}")"
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fpf-cli",
3
- "version": "1.6.8",
3
+ "version": "1.6.10",
4
4
  "description": "Cross-platform fuzzy package finder powered by fzf",
5
5
  "bin": {
6
6
  "fpf": "fpf"