opencode/packages/tui/internal/completions/commands.go
2025-07-16 14:20:28 -05:00

110 lines
2.6 KiB
Go

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
}