fpf-cli 1.6.11 → 1.6.13

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 (2) hide show
  1. package/fpf +147 -44
  2. package/package.json +1 -1
package/fpf CHANGED
@@ -3,7 +3,7 @@
3
3
  set -euo pipefail
4
4
 
5
5
  SCRIPT_NAME="fpf"
6
- SCRIPT_VERSION="1.6.11"
6
+ SCRIPT_VERSION="1.6.13"
7
7
  TMP_ROOT="${TMPDIR:-/tmp}/fpf"
8
8
  SESSION_TMP_ROOT=""
9
9
  HELP_FILE=""
@@ -642,85 +642,168 @@ rank_display_rows_by_query() {
642
642
  local query="$1"
643
643
  local input_file="$2"
644
644
  local ranked_file
645
+ local has_exact=0
646
+ local candidate
645
647
 
646
648
  [[ -n "${query}" ]] || return 0
647
649
 
650
+ while IFS= read -r candidate; do
651
+ [[ -n "${candidate}" ]] || continue
652
+ if awk -F'\t' -v cand="${candidate}" 'tolower($2) == tolower(cand) { found=1; exit } END { exit (found ? 0 : 1) }' "${input_file}"; then
653
+ has_exact=1
654
+ break
655
+ fi
656
+ done < <(exact_query_candidates "${query}" | awk '!seen[$0]++')
657
+
648
658
  ranked_file="$(mktemp "${SESSION_TMP_ROOT}/ranked.XXXXXX")"
649
659
 
650
- awk -F'\t' -v query="${query}" '
660
+ awk -F'\t' -v query="${query}" -v has_exact="${has_exact}" '
651
661
  function lower(s) { return tolower(s) }
662
+ function normalize(s, t) {
663
+ t = lower(s)
664
+ gsub(/[^[:alnum:]]+/, "", t)
665
+ return t
666
+ }
652
667
  BEGIN {
653
668
  q = lower(query)
669
+ gsub(/^[[:space:]]+|[[:space:]]+$/, "", q)
670
+ q_norm = normalize(q)
671
+
672
+ token_total = split(q, raw_tokens, /[^[:alnum:]]+/)
673
+ query_token_count = 0
674
+ for (i = 1; i <= token_total; i++) {
675
+ if (raw_tokens[i] != "") {
676
+ query_tokens[++query_token_count] = raw_tokens[i]
677
+ }
678
+ }
654
679
  }
655
680
  {
656
- mgr = $1
657
681
  pkg = $2
658
682
  desc = $3
659
683
 
660
684
  pkg_l = lower(pkg)
661
685
  desc_l = lower(desc)
686
+ pkg_norm = normalize(pkg_l)
687
+ desc_norm = normalize(desc_l)
688
+
689
+ pkg_token_hits = 0
690
+ desc_token_hits = 0
691
+ for (i = 1; i <= query_token_count; i++) {
692
+ token = query_tokens[i]
693
+ if (index(pkg_l, token) > 0 || index(pkg_norm, token) > 0) {
694
+ pkg_token_hits++
695
+ }
696
+ if (index(desc_l, token) > 0 || index(desc_norm, token) > 0) {
697
+ desc_token_hits++
698
+ }
699
+ }
700
+
701
+ pkg_token_count = 0
702
+ split(pkg_l, pkg_parts, /[^[:alnum:]]+/)
703
+ for (i in pkg_parts) {
704
+ if (pkg_parts[i] != "") {
705
+ pkg_token_count++
706
+ }
707
+ }
662
708
 
663
- score = 8
709
+ score = 12
664
710
  if (q != "") {
665
- if (pkg_l == q) {
711
+ if (pkg_l == q || (q_norm != "" && pkg_norm == q_norm)) {
666
712
  score = 0
667
- } else if (index(pkg_l, q) == 1) {
713
+ } else if (q_norm != "" && index(pkg_norm, q_norm) == 1) {
668
714
  score = 1
669
- } else if (index(pkg_l, q) > 0) {
715
+ } else if (query_token_count > 1 && pkg_token_hits == query_token_count) {
670
716
  score = 2
671
- } else if (index(desc_l, q) > 0) {
717
+ } else if (q_norm != "" && index(pkg_norm, q_norm) > 0) {
672
718
  score = 3
719
+ } else if (query_token_count > 0 && pkg_token_hits > 0) {
720
+ score = 4
721
+ } else if (query_token_count > 0 && desc_token_hits == query_token_count) {
722
+ score = 5
723
+ } else if (query_token_count > 0 && desc_token_hits > 0) {
724
+ score = 6
673
725
  }
674
726
  }
675
727
 
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
728
+ if (q != "" && has_exact == 1 && pkg_token_hits == query_token_count && query_token_count > 0 && pkg_token_count > query_token_count) {
729
+ score += 5
683
730
  }
684
731
 
685
- print score "\t" mgr_score "\t" length(pkg) "\t" pkg_l "\t" $0
732
+ if (q != "" && desc_l ~ /(plugin|template|starter|boilerplate|router|hooks?|mcp|integration)/) {
733
+ score += 2
734
+ }
735
+
736
+ mgr_bias = 0
737
+ if (mgr == "npm") {
738
+ mgr_bias = 4
739
+ } else if (mgr == "bun") {
740
+ mgr_bias = 3
741
+ }
742
+
743
+ print score "\t" mgr_bias "\t" (99 - pkg_token_hits) "\t" (99 - desc_token_hits) "\t" length(pkg) "\t" pkg_l "\t" $0
686
744
  }
687
745
  ' "${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}"
746
+ sort -t $'\t' -k1,1n -k2,2n -k3,3n -k4,4n -k5,5n -k6,6 |
747
+ awk -F'\t' '{ print $7 "\t" $8 "\t" $9 }' >"${ranked_file}"
690
748
 
691
749
  mv "${ranked_file}" "${input_file}"
692
750
  }
693
751
 
694
- query_is_single_token() {
752
+ exact_query_candidates() {
695
753
  local query="$1"
696
- [[ -n "${query}" && "${query}" != *[[:space:]]* ]]
754
+ local compact_query=""
755
+ local dashed_query=""
756
+ local underscored_query=""
757
+ local nospace_query=""
758
+
759
+ compact_query="$(printf "%s" "${query}" | awk '{$1=$1; print}')"
760
+ [[ -n "${compact_query}" ]] || return 0
761
+
762
+ printf "%s\n" "${compact_query}"
763
+
764
+ if [[ "${compact_query}" == *[[:space:]]* ]]; then
765
+ dashed_query="${compact_query// /-}"
766
+ underscored_query="${compact_query// /_}"
767
+ nospace_query="${compact_query// /}"
768
+ printf "%s\n" "${dashed_query}"
769
+ printf "%s\n" "${underscored_query}"
770
+ printf "%s\n" "${nospace_query}"
771
+ fi
697
772
  }
698
773
 
699
774
  exact_match_entry() {
700
775
  local manager="$1"
701
776
  local query="$2"
777
+ local candidate
702
778
 
703
- if ! query_is_single_token "${query}"; then
779
+ if [[ -z "${query}" ]]; then
704
780
  return 0
705
781
  fi
706
782
 
707
- case "${manager}" in
708
- brew)
709
- if brew info --formula "${query}" >/dev/null 2>&1 || brew info --cask "${query}" >/dev/null 2>&1; then
710
- printf "%s\t-\n" "${query}"
711
- fi
712
- ;;
713
- npm)
714
- if command_exists npm && npm view "${query}" name >/dev/null 2>&1; then
715
- printf "%s\t-\n" "${query}"
716
- fi
717
- ;;
718
- bun)
719
- if bun info "${query}" >/dev/null 2>&1 || (command_exists npm && npm view "${query}" name >/dev/null 2>&1); then
720
- printf "%s\t-\n" "${query}"
721
- fi
722
- ;;
723
- esac
783
+ while IFS= read -r candidate; do
784
+ [[ -n "${candidate}" ]] || continue
785
+
786
+ case "${manager}" in
787
+ brew)
788
+ if brew info --formula "${candidate}" >/dev/null 2>&1 || brew info --cask "${candidate}" >/dev/null 2>&1; then
789
+ printf "%s\t-\n" "${candidate}"
790
+ return 0
791
+ fi
792
+ ;;
793
+ npm)
794
+ if command_exists npm && npm view "${candidate}" name >/dev/null 2>&1; then
795
+ printf "%s\t-\n" "${candidate}"
796
+ return 0
797
+ fi
798
+ ;;
799
+ bun)
800
+ if bun info "${candidate}" >/dev/null 2>&1 || (command_exists npm && npm view "${candidate}" name >/dev/null 2>&1); then
801
+ printf "%s\t-\n" "${candidate}"
802
+ return 0
803
+ fi
804
+ ;;
805
+ esac
806
+ done < <(exact_query_candidates "${query}" | awk '!seen[$0]++')
724
807
  }
725
808
 
726
809
  manager_search_entries() {
@@ -729,10 +812,14 @@ manager_search_entries() {
729
812
  local effective_query="${query}"
730
813
  local npm_search_limit=500
731
814
  local line_limit=0
815
+ local query_line_limit=40
816
+ local effective_limit=0
732
817
 
733
818
  if [[ -z "${query}" ]]; then
734
819
  npm_search_limit="${FPF_NO_QUERY_NPM_LIMIT:-120}"
735
820
  line_limit="${FPF_NO_QUERY_RESULT_LIMIT:-120}"
821
+ else
822
+ query_line_limit="${FPF_QUERY_PER_MANAGER_LIMIT:-40}"
736
823
  fi
737
824
 
738
825
  if ! [[ "${npm_search_limit}" =~ ^[0-9]+$ ]] || [[ "${npm_search_limit}" -eq 0 ]]; then
@@ -743,6 +830,16 @@ manager_search_entries() {
743
830
  line_limit=0
744
831
  fi
745
832
 
833
+ if ! [[ "${query_line_limit}" =~ ^[0-9]+$ ]]; then
834
+ query_line_limit=40
835
+ fi
836
+
837
+ if [[ "${line_limit}" -gt 0 ]]; then
838
+ effective_limit="${line_limit}"
839
+ else
840
+ effective_limit="${query_line_limit}"
841
+ fi
842
+
746
843
  if [[ -z "${effective_query}" ]]; then
747
844
  case "${manager}" in
748
845
  apt|dnf|pacman|zypper|emerge|choco|scoop|snap)
@@ -912,8 +1009,8 @@ manager_search_entries() {
912
1009
  } || true
913
1010
  ;;
914
1011
  esac | awk -F'\t' 'NF >= 1 { if ($2 == "") $2 = "-"; print $1 "\t" $2 }' | awk -F'\t' '!seen[$1]++' | {
915
- if [[ "${line_limit}" -gt 0 ]]; then
916
- awk -v limit="${line_limit}" 'NR <= limit'
1012
+ if [[ "${effective_limit}" -gt 0 ]]; then
1013
+ awk -v limit="${effective_limit}" 'NR <= limit'
917
1014
  else
918
1015
  cat
919
1016
  fi
@@ -1101,6 +1198,7 @@ collect_search_display_rows() {
1101
1198
  local output_file="$2"
1102
1199
  shift 2
1103
1200
  local managers=("$@")
1201
+ local query_limit="${FPF_QUERY_RESULT_LIMIT:-0}"
1104
1202
 
1105
1203
  : >"${output_file}"
1106
1204
 
@@ -1147,6 +1245,11 @@ collect_search_display_rows() {
1147
1245
  if [[ -s "${output_file}" ]]; then
1148
1246
  sort -u "${output_file}" -o "${output_file}"
1149
1247
  rank_display_rows_by_query "${query}" "${output_file}"
1248
+
1249
+ if [[ -n "${query}" && "${query_limit}" =~ ^[0-9]+$ && "${query_limit}" -gt 0 ]]; then
1250
+ awk -v limit="${query_limit}" 'NR <= limit' "${output_file}" >"${output_file}.limited"
1251
+ mv "${output_file}.limited" "${output_file}"
1252
+ fi
1150
1253
  fi
1151
1254
  }
1152
1255
 
@@ -1412,10 +1515,11 @@ run_fuzzy_selector() {
1412
1515
 
1413
1516
  local -a fzf_args=()
1414
1517
  fzf_args=(-q "${query}" -m \
1518
+ -e \
1415
1519
  --delimiter=$'\t' \
1416
1520
  --with-nth=1,2,3 \
1417
1521
  --preview="${preview_cmd}" \
1418
- --preview-window=55%:wrap:border-sharp:hidden \
1522
+ --preview-window=55%:wrap:border-sharp \
1419
1523
  --layout=reverse \
1420
1524
  --marker='>>' \
1421
1525
  --prompt='Search> ' \
@@ -1427,12 +1531,11 @@ run_fuzzy_selector() {
1427
1531
  --bind=ctrl-k:preview:"cat ${KBINDS_FILE}" \
1428
1532
  --bind=ctrl-h:preview:"cat ${HELP_FILE}" \
1429
1533
  --bind='ctrl-/:change-preview-window(hidden|)' \
1430
- --bind=ctrl-n:next-selected,ctrl-b:prev-selected)
1534
+ --bind=ctrl-n:next-selected,ctrl-b:prev-selected \
1535
+ --bind='focus:transform-preview-label:echo [{1}] {2}')
1431
1536
 
1432
1537
  if [[ -n "${reload_cmd}" ]]; then
1433
1538
  fzf_args+=(--bind="change:reload:${reload_cmd}")
1434
- else
1435
- fzf_args+=(-e)
1436
1539
  fi
1437
1540
 
1438
1541
  fzf "${fzf_args[@]}" <"${input_file}"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fpf-cli",
3
- "version": "1.6.11",
3
+ "version": "1.6.13",
4
4
  "description": "Cross-platform fuzzy package finder powered by fzf",
5
5
  "bin": {
6
6
  "fpf": "fpf"