1package completions
2
3import (
4 "testing"
5
6 "charm.land/lipgloss/v2"
7 "github.com/sahilm/fuzzy"
8 "github.com/stretchr/testify/require"
9)
10
11func TestRankPrefersStrongBasenameMatch(t *testing.T) {
12 t.Parallel()
13
14 c := &Completions{
15 items: []*CompletionItem{
16 NewCompletionItem("internal/ui/chat/search.go", FileCompletionValue{Path: "internal/ui/chat/search.go"}, lipgloss.NewStyle(), lipgloss.NewStyle(), lipgloss.NewStyle()),
17 NewCompletionItem("user.go", FileCompletionValue{Path: "user.go"}, lipgloss.NewStyle(), lipgloss.NewStyle(), lipgloss.NewStyle()),
18 NewCompletionItem("internal/user_service.go", FileCompletionValue{Path: "internal/user_service.go"}, lipgloss.NewStyle(), lipgloss.NewStyle(), lipgloss.NewStyle()),
19 },
20 paths: []string{"internal/ui/chat/search.go", "user.go", "internal/user_service.go"},
21 bases: []string{"search.go", "user.go", "user_service.go"},
22 }
23
24 ranked := c.rank(queryContext{query: "user"})
25 require.NotEmpty(t, ranked)
26 require.Equal(t, "user.go", ranked[0].Text())
27}
28
29func TestRankReturnsOriginalOrderForEmptyQuery(t *testing.T) {
30 t.Parallel()
31
32 c := &Completions{
33 items: []*CompletionItem{
34 NewCompletionItem("b/user.go", FileCompletionValue{Path: "b/user.go"}, lipgloss.NewStyle(), lipgloss.NewStyle(), lipgloss.NewStyle()),
35 NewCompletionItem("a/user.go", FileCompletionValue{Path: "a/user.go"}, lipgloss.NewStyle(), lipgloss.NewStyle(), lipgloss.NewStyle()),
36 },
37 paths: []string{"b/user.go", "a/user.go"},
38 bases: []string{"user.go", "user.go"},
39 }
40
41 ranked := c.rank(queryContext{query: ""})
42 require.Len(t, ranked, 2)
43 require.Equal(t, "b/user.go", ranked[0].Text())
44 require.Equal(t, "a/user.go", ranked[1].Text())
45}
46
47func TestRankPrefersPathMatchesWhenPathHintPresent(t *testing.T) {
48 t.Parallel()
49
50 c := &Completions{
51 items: []*CompletionItem{
52 NewCompletionItem("user.go", FileCompletionValue{Path: "user.go"}, lipgloss.NewStyle(), lipgloss.NewStyle(), lipgloss.NewStyle()),
53 NewCompletionItem("internal/user.go", FileCompletionValue{Path: "internal/user.go"}, lipgloss.NewStyle(), lipgloss.NewStyle(), lipgloss.NewStyle()),
54 NewCompletionItem("internal/ui/chat/search.go", FileCompletionValue{Path: "internal/ui/chat/search.go"}, lipgloss.NewStyle(), lipgloss.NewStyle(), lipgloss.NewStyle()),
55 },
56 paths: []string{"user.go", "internal/user.go", "internal/ui/chat/search.go"},
57 bases: []string{"user.go", "user.go", "search.go"},
58 }
59
60 ranked := c.rank(queryContext{query: "internal/u"})
61 require.NotEmpty(t, ranked)
62 require.Equal(t, "internal/user.go", ranked[0].Text())
63}
64
65func TestRankDotHintPrefersSuffixPathMatch(t *testing.T) {
66 t.Parallel()
67
68 c := &Completions{
69 items: []*CompletionItem{
70 NewCompletionItem("docs/go-guide.md", FileCompletionValue{Path: "docs/go-guide.md"}, lipgloss.NewStyle(), lipgloss.NewStyle(), lipgloss.NewStyle()),
71 NewCompletionItem("src/user.go", FileCompletionValue{Path: "src/user.go"}, lipgloss.NewStyle(), lipgloss.NewStyle(), lipgloss.NewStyle()),
72 },
73 paths: []string{"docs/go-guide.md", "src/user.go"},
74 bases: []string{"go-guide.md", "user.go"},
75 }
76
77 ranked := c.rank(queryContext{query: ".go"})
78 require.NotEmpty(t, ranked)
79 require.Equal(t, "src/user.go", ranked[0].Text())
80}
81
82func TestRemapMatchToPath(t *testing.T) {
83 t.Parallel()
84
85 match := remapMatchToPath(
86 fuzzy.Match{MatchedIndexes: []int{0, 1, 2}},
87 "internal/user.go",
88 )
89 require.Equal(t, []int{9, 10, 11}, match.MatchedIndexes)
90}
91
92func TestHasPathHint(t *testing.T) {
93 t.Parallel()
94
95 require.True(t, hasPathHint("internal/u"))
96 require.True(t, hasPathHint("main.go"))
97 require.False(t, hasPathHint("v0.1"))
98 require.False(t, hasPathHint("main"))
99}