feat(tui): @symbol attachments

This commit is contained in:
adamdottv
2025-07-10 05:51:47 -05:00
parent 085c0e4e2b
commit 85dbfeb314
9 changed files with 449 additions and 117 deletions

View File

@@ -29,17 +29,26 @@ func (c *CommandCompletionProvider) GetEmptyMessage() string {
return "no matching commands"
}
func getCommandCompletionItem(cmd commands.Command, space int, t theme.Theme) dialog.CompletionItemI {
func (c *CommandCompletionProvider) getCommandCompletionItem(
cmd commands.Command,
space int,
t theme.Theme,
) dialog.CompletionItemI {
spacer := strings.Repeat(" ", space)
title := " /" + cmd.PrimaryTrigger() + styles.NewStyle().Foreground(t.TextMuted()).Render(spacer+cmd.Description)
title := " /" + cmd.PrimaryTrigger() + styles.NewStyle().
Foreground(t.TextMuted()).
Render(spacer+cmd.Description)
value := string(cmd.Name)
return dialog.NewCompletionItem(dialog.CompletionItem{
Title: title,
Value: value,
Title: title,
Value: value,
ProviderID: c.GetId(),
})
}
func (c *CommandCompletionProvider) GetChildEntries(query string) ([]dialog.CompletionItemI, error) {
func (c *CommandCompletionProvider) GetChildEntries(
query string,
) ([]dialog.CompletionItemI, error) {
t := theme.CurrentTheme()
commands := c.app.Commands
@@ -60,7 +69,7 @@ func (c *CommandCompletionProvider) GetChildEntries(query string) ([]dialog.Comp
continue
}
space := space - lipgloss.Width(cmd.PrimaryTrigger())
items = append(items, getCommandCompletionItem(cmd, space, t))
items = append(items, c.getCommandCompletionItem(cmd, space, t))
}
return items, nil
}
@@ -77,7 +86,7 @@ func (c *CommandCompletionProvider) GetChildEntries(query string) ([]dialog.Comp
// Add all triggers as searchable options
for _, trigger := range cmd.Trigger {
commandNames = append(commandNames, trigger)
commandMap[trigger] = getCommandCompletionItem(cmd, space, t)
commandMap[trigger] = c.getCommandCompletionItem(cmd, space, t)
}
}

View File

@@ -14,20 +14,20 @@ import (
"github.com/sst/opencode/internal/theme"
)
type filesAndFoldersContextGroup struct {
type filesContextGroup struct {
app *app.App
gitFiles []dialog.CompletionItemI
}
func (cg *filesAndFoldersContextGroup) GetId() string {
func (cg *filesContextGroup) GetId() string {
return "files"
}
func (cg *filesAndFoldersContextGroup) GetEmptyMessage() string {
func (cg *filesContextGroup) GetEmptyMessage() string {
return "no matching files"
}
func (cg *filesAndFoldersContextGroup) getGitFiles() []dialog.CompletionItemI {
func (cg *filesContextGroup) getGitFiles() []dialog.CompletionItemI {
t := theme.CurrentTheme()
items := make([]dialog.CompletionItemI, 0)
base := styles.NewStyle().Background(t.BackgroundElement())
@@ -50,8 +50,10 @@ func (cg *filesAndFoldersContextGroup) getGitFiles() []dialog.CompletionItemI {
title += red(" -" + strconv.Itoa(int(file.Removed)))
}
item := dialog.NewCompletionItem(dialog.CompletionItem{
Title: title,
Value: file.Path,
Title: title,
Value: file.Path,
ProviderID: cg.GetId(),
Raw: file,
})
items = append(items, item)
}
@@ -60,7 +62,7 @@ func (cg *filesAndFoldersContextGroup) getGitFiles() []dialog.CompletionItemI {
return items
}
func (cg *filesAndFoldersContextGroup) GetChildEntries(
func (cg *filesContextGroup) GetChildEntries(
query string,
) ([]dialog.CompletionItemI, error) {
items := make([]dialog.CompletionItemI, 0)
@@ -94,8 +96,10 @@ func (cg *filesAndFoldersContextGroup) GetChildEntries(
}
if !exists {
item := dialog.NewCompletionItem(dialog.CompletionItem{
Title: file,
Value: file,
Title: file,
Value: file,
ProviderID: cg.GetId(),
Raw: file,
})
items = append(items, item)
}
@@ -104,8 +108,8 @@ func (cg *filesAndFoldersContextGroup) GetChildEntries(
return items, nil
}
func NewFileAndFolderContextGroup(app *app.App) dialog.CompletionProvider {
cg := &filesAndFoldersContextGroup{
func NewFileContextGroup(app *app.App) dialog.CompletionProvider {
cg := &filesContextGroup{
app: app,
}
go func() {

View File

@@ -0,0 +1,118 @@
package completions
import (
"context"
"fmt"
"log/slog"
"strings"
"github.com/sst/opencode-sdk-go"
"github.com/sst/opencode/internal/app"
"github.com/sst/opencode/internal/components/dialog"
"github.com/sst/opencode/internal/styles"
"github.com/sst/opencode/internal/theme"
)
type symbolsContextGroup struct {
app *app.App
}
func (cg *symbolsContextGroup) GetId() string {
return "symbols"
}
func (cg *symbolsContextGroup) GetEmptyMessage() string {
return "no matching symbols"
}
type SymbolKind int
const (
SymbolKindFile SymbolKind = 1
SymbolKindModule SymbolKind = 2
SymbolKindNamespace SymbolKind = 3
SymbolKindPackage SymbolKind = 4
SymbolKindClass SymbolKind = 5
SymbolKindMethod SymbolKind = 6
SymbolKindProperty SymbolKind = 7
SymbolKindField SymbolKind = 8
SymbolKindConstructor SymbolKind = 9
SymbolKindEnum SymbolKind = 10
SymbolKindInterface SymbolKind = 11
SymbolKindFunction SymbolKind = 12
SymbolKindVariable SymbolKind = 13
SymbolKindConstant SymbolKind = 14
SymbolKindString SymbolKind = 15
SymbolKindNumber SymbolKind = 16
SymbolKindBoolean SymbolKind = 17
SymbolKindArray SymbolKind = 18
SymbolKindObject SymbolKind = 19
SymbolKindKey SymbolKind = 20
SymbolKindNull SymbolKind = 21
SymbolKindEnumMember SymbolKind = 22
SymbolKindStruct SymbolKind = 23
SymbolKindEvent SymbolKind = 24
SymbolKindOperator SymbolKind = 25
SymbolKindTypeParameter SymbolKind = 26
)
func (cg *symbolsContextGroup) GetChildEntries(
query string,
) ([]dialog.CompletionItemI, error) {
items := make([]dialog.CompletionItemI, 0)
query = strings.TrimSpace(query)
if query == "" {
return items, nil
}
symbols, err := cg.app.Client.Find.Symbols(
context.Background(),
opencode.FindSymbolsParams{Query: opencode.F(query)},
)
if err != nil {
slog.Error("Failed to get symbol completion items", "error", err)
return items, err
}
if symbols == nil {
return items, nil
}
t := theme.CurrentTheme()
baseStyle := styles.NewStyle().Background(t.BackgroundElement())
base := baseStyle.Render
muted := baseStyle.Foreground(t.TextMuted()).Render
for _, sym := range *symbols {
parts := strings.Split(sym.Name, ".")
lastPart := parts[len(parts)-1]
title := base(lastPart)
uriParts := strings.Split(sym.Location.Uri, "/")
lastTwoParts := uriParts[len(uriParts)-2:]
joined := strings.Join(lastTwoParts, "/")
title += muted(fmt.Sprintf(" %s", joined))
start := int(sym.Location.Range.Start.Line)
end := int(sym.Location.Range.End.Line)
title += muted(fmt.Sprintf(":L%d-%d", start, end))
value := fmt.Sprintf("%s?start=%d&end=%d", sym.Location.Uri, start, end)
item := dialog.NewCompletionItem(dialog.CompletionItem{
Title: title,
Value: value,
ProviderID: cg.GetId(),
Raw: sym,
})
items = append(items, item)
}
return items, nil
}
func NewSymbolsContextGroup(app *app.App) dialog.CompletionProvider {
return &symbolsContextGroup{
app: app,
}
}