fpf-cli 1.6.10 → 1.6.12
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/fpf +151 -42
- 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.
|
|
6
|
+
SCRIPT_VERSION="1.6.12"
|
|
7
7
|
TMP_ROOT="${TMPDIR:-/tmp}/fpf"
|
|
8
8
|
SESSION_TMP_ROOT=""
|
|
9
9
|
HELP_FILE=""
|
|
@@ -642,80 +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 =
|
|
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(
|
|
713
|
+
} else if (q_norm != "" && index(pkg_norm, q_norm) == 1) {
|
|
668
714
|
score = 1
|
|
669
|
-
} else if (
|
|
715
|
+
} else if (query_token_count > 1 && pkg_token_hits == query_token_count) {
|
|
670
716
|
score = 2
|
|
671
|
-
} else if (index(
|
|
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
|
-
|
|
677
|
-
|
|
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
|
-
|
|
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,
|
|
689
|
-
awk -F'\t' '{ print $
|
|
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
|
-
|
|
752
|
+
exact_query_candidates() {
|
|
695
753
|
local query="$1"
|
|
696
|
-
|
|
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
|
|
779
|
+
if [[ -z "${query}" ]]; then
|
|
704
780
|
return 0
|
|
705
781
|
fi
|
|
706
782
|
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
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]++')
|
|
719
807
|
}
|
|
720
808
|
|
|
721
809
|
manager_search_entries() {
|
|
@@ -724,10 +812,14 @@ manager_search_entries() {
|
|
|
724
812
|
local effective_query="${query}"
|
|
725
813
|
local npm_search_limit=500
|
|
726
814
|
local line_limit=0
|
|
815
|
+
local query_line_limit=40
|
|
816
|
+
local effective_limit=0
|
|
727
817
|
|
|
728
818
|
if [[ -z "${query}" ]]; then
|
|
729
819
|
npm_search_limit="${FPF_NO_QUERY_NPM_LIMIT:-120}"
|
|
730
820
|
line_limit="${FPF_NO_QUERY_RESULT_LIMIT:-120}"
|
|
821
|
+
else
|
|
822
|
+
query_line_limit="${FPF_QUERY_PER_MANAGER_LIMIT:-40}"
|
|
731
823
|
fi
|
|
732
824
|
|
|
733
825
|
if ! [[ "${npm_search_limit}" =~ ^[0-9]+$ ]] || [[ "${npm_search_limit}" -eq 0 ]]; then
|
|
@@ -738,9 +830,19 @@ manager_search_entries() {
|
|
|
738
830
|
line_limit=0
|
|
739
831
|
fi
|
|
740
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
|
+
|
|
741
843
|
if [[ -z "${effective_query}" ]]; then
|
|
742
844
|
case "${manager}" in
|
|
743
|
-
apt|dnf|pacman|zypper|emerge|choco|scoop|snap
|
|
845
|
+
apt|dnf|pacman|zypper|emerge|choco|scoop|snap)
|
|
744
846
|
effective_query="a"
|
|
745
847
|
;;
|
|
746
848
|
brew|npm|bun)
|
|
@@ -894,19 +996,21 @@ manager_search_entries() {
|
|
|
894
996
|
;;
|
|
895
997
|
bun)
|
|
896
998
|
{
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
999
|
+
local bun_search_file
|
|
1000
|
+
bun_search_file="$(mktemp "${SESSION_TMP_ROOT}/bun-search.XXXXXX")"
|
|
1001
|
+
if bun search "${effective_query}" >"${bun_search_file}" 2>/dev/null; then
|
|
1002
|
+
awk 'NR > 1 && NF > 0 { name=$1; $1=""; sub(/^[[:space:]]+/, "", $0); if ($0 == "") $0="-"; print name "\t" $0 }' "${bun_search_file}"
|
|
900
1003
|
elif command_exists npm; then
|
|
901
1004
|
npm search "${effective_query}" --searchlimit="${npm_search_limit}" --parseable 2>/dev/null |
|
|
902
1005
|
awk -F'\t' 'NF >= 2 { print $1 "\t" $2 }'
|
|
903
1006
|
fi
|
|
1007
|
+
rm -f "${bun_search_file}"
|
|
904
1008
|
exact_match_entry "${manager}" "${query}"
|
|
905
1009
|
} || true
|
|
906
1010
|
;;
|
|
907
1011
|
esac | awk -F'\t' 'NF >= 1 { if ($2 == "") $2 = "-"; print $1 "\t" $2 }' | awk -F'\t' '!seen[$1]++' | {
|
|
908
|
-
if [[ "${
|
|
909
|
-
awk -v limit="${
|
|
1012
|
+
if [[ "${effective_limit}" -gt 0 ]]; then
|
|
1013
|
+
awk -v limit="${effective_limit}" 'NR <= limit'
|
|
910
1014
|
else
|
|
911
1015
|
cat
|
|
912
1016
|
fi
|
|
@@ -1094,6 +1198,7 @@ collect_search_display_rows() {
|
|
|
1094
1198
|
local output_file="$2"
|
|
1095
1199
|
shift 2
|
|
1096
1200
|
local managers=("$@")
|
|
1201
|
+
local query_limit="${FPF_QUERY_RESULT_LIMIT:-80}"
|
|
1097
1202
|
|
|
1098
1203
|
: >"${output_file}"
|
|
1099
1204
|
|
|
@@ -1140,6 +1245,11 @@ collect_search_display_rows() {
|
|
|
1140
1245
|
if [[ -s "${output_file}" ]]; then
|
|
1141
1246
|
sort -u "${output_file}" -o "${output_file}"
|
|
1142
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
|
|
1143
1253
|
fi
|
|
1144
1254
|
}
|
|
1145
1255
|
|
|
@@ -1405,10 +1515,11 @@ run_fuzzy_selector() {
|
|
|
1405
1515
|
|
|
1406
1516
|
local -a fzf_args=()
|
|
1407
1517
|
fzf_args=(-q "${query}" -m \
|
|
1518
|
+
-e \
|
|
1408
1519
|
--delimiter=$'\t' \
|
|
1409
1520
|
--with-nth=1,2,3 \
|
|
1410
1521
|
--preview="${preview_cmd}" \
|
|
1411
|
-
--preview-window=55%:wrap:border-sharp
|
|
1522
|
+
--preview-window=55%:wrap:border-sharp \
|
|
1412
1523
|
--layout=reverse \
|
|
1413
1524
|
--marker='>>' \
|
|
1414
1525
|
--prompt='Search> ' \
|
|
@@ -1424,8 +1535,6 @@ run_fuzzy_selector() {
|
|
|
1424
1535
|
|
|
1425
1536
|
if [[ -n "${reload_cmd}" ]]; then
|
|
1426
1537
|
fzf_args+=(--bind="change:reload:${reload_cmd}")
|
|
1427
|
-
else
|
|
1428
|
-
fzf_args+=(-e)
|
|
1429
1538
|
fi
|
|
1430
1539
|
|
|
1431
1540
|
fzf "${fzf_args[@]}" <"${input_file}"
|