completions_test.go

 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}