summaryrefslogtreecommitdiffhomepage
path: root/packages/tui/internal/completions/commands.go
blob: 2ffe3ea94b415b0dc6c67120779d83203ee4897f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
package completions

import (
	"sort"
	"strings"

	"github.com/charmbracelet/lipgloss/v2"
	"github.com/lithammer/fuzzysearch/fuzzy"
	"github.com/sst/opencode/internal/app"
	"github.com/sst/opencode/internal/commands"
	"github.com/sst/opencode/internal/styles"
	"github.com/sst/opencode/internal/theme"
)

type CommandCompletionProvider struct {
	app *app.App
}

func NewCommandCompletionProvider(app *app.App) CompletionProvider {
	return &CommandCompletionProvider{app: app}
}

func (c *CommandCompletionProvider) GetId() string {
	return "commands"
}

func (c *CommandCompletionProvider) GetEmptyMessage() string {
	return "no matching commands"
}

func (c *CommandCompletionProvider) getCommandCompletionItem(
	cmd commands.Command,
	space int,
) CompletionSuggestion {
	displayFunc := func(s styles.Style) string {
		t := theme.CurrentTheme()
		spacer := strings.Repeat(" ", space)
		display := "  /" + cmd.PrimaryTrigger() + s.
			Foreground(t.TextMuted()).
			Render(spacer+cmd.Description)
		return display
	}

	value := string(cmd.Name)
	return CompletionSuggestion{
		Display:    displayFunc,
		Value:      value,
		ProviderID: c.GetId(),
		RawData:    cmd,
	}
}

func (c *CommandCompletionProvider) GetChildEntries(
	query string,
) ([]CompletionSuggestion, error) {
	commands := c.app.Commands

	space := 1
	for _, cmd := range c.app.Commands {
		if cmd.HasTrigger() && lipgloss.Width(cmd.PrimaryTrigger()) > space {
			space = lipgloss.Width(cmd.PrimaryTrigger())
		}
	}
	space += 2

	sorted := commands.Sorted()
	if query == "" {
		// If no query, return all commands
		items := []CompletionSuggestion{}
		for _, cmd := range sorted {
			if !cmd.HasTrigger() {
				continue
			}
			space := space - lipgloss.Width(cmd.PrimaryTrigger())
			items = append(items, c.getCommandCompletionItem(cmd, space))
		}
		return items, nil
	}

	var commandNames []string
	commandMap := make(map[string]CompletionSuggestion)

	for _, cmd := range sorted {
		if !cmd.HasTrigger() {
			continue
		}
		space := space - lipgloss.Width(cmd.PrimaryTrigger())
		for _, trigger := range cmd.Trigger {
			commandNames = append(commandNames, trigger)
			commandMap[trigger] = c.getCommandCompletionItem(cmd, space)
		}
	}

	matches := fuzzy.RankFindFold(query, commandNames)
	sort.Sort(matches)

	// Convert matches to completion items, deduplicating by command name
	items := []CompletionSuggestion{}
	seen := make(map[string]bool)
	for _, match := range matches {
		if item, ok := commandMap[match.Target]; ok {
			// Use the command's value (name) as the deduplication key
			if !seen[item.Value] {
				seen[item.Value] = true
				items = append(items, item)
			}
		}
	}
	return items, nil
}