mirror of
https://github.com/sst/opencode.git
synced 2025-07-07 16:14:59 +00:00
chore: simplify completions
This commit is contained in:
parent
3e2a0c7281
commit
fce59db94a
5 changed files with 30 additions and 104 deletions
|
@ -46,9 +46,6 @@ type SendMsg struct {
|
||||||
Text string
|
Text string
|
||||||
Attachments []Attachment
|
Attachments []Attachment
|
||||||
}
|
}
|
||||||
type CompletionDialogTriggeredMsg struct {
|
|
||||||
InitialValue string
|
|
||||||
}
|
|
||||||
type OptimisticMessageAddedMsg struct {
|
type OptimisticMessageAddedMsg struct {
|
||||||
Message opencode.Message
|
Message opencode.Message
|
||||||
}
|
}
|
||||||
|
@ -129,7 +126,11 @@ func New(
|
||||||
func (a *App) Key(commandName commands.CommandName) string {
|
func (a *App) Key(commandName commands.CommandName) string {
|
||||||
t := theme.CurrentTheme()
|
t := theme.CurrentTheme()
|
||||||
base := styles.NewStyle().Background(t.Background()).Foreground(t.Text()).Bold(true).Render
|
base := styles.NewStyle().Background(t.Background()).Foreground(t.Text()).Bold(true).Render
|
||||||
muted := styles.NewStyle().Background(t.Background()).Foreground(t.TextMuted()).Faint(true).Render
|
muted := styles.NewStyle().
|
||||||
|
Background(t.Background()).
|
||||||
|
Foreground(t.TextMuted()).
|
||||||
|
Faint(true).
|
||||||
|
Render
|
||||||
command := a.Commands[commandName]
|
command := a.Commands[commandName]
|
||||||
kb := command.Keybindings[0]
|
kb := command.Keybindings[0]
|
||||||
key := kb.Key
|
key := kb.Key
|
||||||
|
@ -201,7 +202,10 @@ func (a *App) InitializeProvider() tea.Cmd {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDefaultModel(response *opencode.ConfigProvidersResponse, provider opencode.Provider) *opencode.Model {
|
func getDefaultModel(
|
||||||
|
response *opencode.ConfigProvidersResponse,
|
||||||
|
provider opencode.Provider,
|
||||||
|
) *opencode.Model {
|
||||||
if match, ok := response.Default[provider.ID]; ok {
|
if match, ok := response.Default[provider.ID]; ok {
|
||||||
model := provider.Models[match]
|
model := provider.Models[match]
|
||||||
return &model
|
return &model
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
package completions
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/sst/opencode/internal/app"
|
|
||||||
"github.com/sst/opencode/internal/components/dialog"
|
|
||||||
)
|
|
||||||
|
|
||||||
type CompletionManager struct {
|
|
||||||
providers map[string]dialog.CompletionProvider
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewCompletionManager(app *app.App) *CompletionManager {
|
|
||||||
return &CompletionManager{
|
|
||||||
providers: map[string]dialog.CompletionProvider{
|
|
||||||
"files": NewFileAndFolderContextGroup(app),
|
|
||||||
"commands": NewCommandCompletionProvider(app),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *CompletionManager) DefaultProvider() dialog.CompletionProvider {
|
|
||||||
return m.providers["commands"]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *CompletionManager) GetProvider(input string) dialog.CompletionProvider {
|
|
||||||
if strings.HasPrefix(input, "/") {
|
|
||||||
return m.providers["commands"]
|
|
||||||
}
|
|
||||||
return m.providers["files"]
|
|
||||||
}
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"github.com/charmbracelet/bubbles/v2/textarea"
|
"github.com/charmbracelet/bubbles/v2/textarea"
|
||||||
tea "github.com/charmbracelet/bubbletea/v2"
|
tea "github.com/charmbracelet/bubbletea/v2"
|
||||||
"github.com/charmbracelet/lipgloss/v2"
|
"github.com/charmbracelet/lipgloss/v2"
|
||||||
"github.com/sst/opencode/internal/app"
|
|
||||||
"github.com/sst/opencode/internal/components/list"
|
"github.com/sst/opencode/internal/components/list"
|
||||||
"github.com/sst/opencode/internal/styles"
|
"github.com/sst/opencode/internal/styles"
|
||||||
"github.com/sst/opencode/internal/theme"
|
"github.com/sst/opencode/internal/theme"
|
||||||
|
@ -79,7 +78,6 @@ type CompletionDialog interface {
|
||||||
tea.ViewModel
|
tea.ViewModel
|
||||||
SetWidth(width int)
|
SetWidth(width int)
|
||||||
IsEmpty() bool
|
IsEmpty() bool
|
||||||
SetProvider(provider CompletionProvider)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type completionDialogComponent struct {
|
type completionDialogComponent struct {
|
||||||
|
@ -114,8 +112,6 @@ func (c *completionDialogComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
switch msg := msg.(type) {
|
switch msg := msg.(type) {
|
||||||
case []CompletionItemI:
|
case []CompletionItemI:
|
||||||
c.list.SetItems(msg)
|
c.list.SetItems(msg)
|
||||||
case app.CompletionDialogTriggeredMsg:
|
|
||||||
c.pseudoSearchTextArea.SetValue(msg.InitialValue)
|
|
||||||
case tea.KeyMsg:
|
case tea.KeyMsg:
|
||||||
if c.pseudoSearchTextArea.Focused() {
|
if c.pseudoSearchTextArea.Focused() {
|
||||||
if !key.Matches(msg, completionDialogKeys.Complete) {
|
if !key.Matches(msg, completionDialogKeys.Complete) {
|
||||||
|
@ -214,19 +210,8 @@ func (c *completionDialogComponent) IsEmpty() bool {
|
||||||
return c.list.IsEmpty()
|
return c.list.IsEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *completionDialogComponent) SetProvider(provider CompletionProvider) {
|
|
||||||
if c.completionProvider.GetId() != provider.GetId() {
|
|
||||||
c.completionProvider = provider
|
|
||||||
c.list.SetEmptyMessage(" " + provider.GetEmptyMessage())
|
|
||||||
c.list.SetItems([]CompletionItemI{})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *completionDialogComponent) complete(item CompletionItemI) tea.Cmd {
|
func (c *completionDialogComponent) complete(item CompletionItemI) tea.Cmd {
|
||||||
value := c.pseudoSearchTextArea.Value()
|
value := c.pseudoSearchTextArea.Value()
|
||||||
if value == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if this is a command completion
|
// Check if this is a command completion
|
||||||
isCommand := c.completionProvider.GetId() == "commands"
|
isCommand := c.completionProvider.GetId() == "commands"
|
||||||
|
|
|
@ -27,7 +27,6 @@ type FindDialog interface {
|
||||||
SetWidth(width int)
|
SetWidth(width int)
|
||||||
SetHeight(height int)
|
SetHeight(height int)
|
||||||
IsEmpty() bool
|
IsEmpty() bool
|
||||||
SetProvider(provider CompletionProvider)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type findDialogComponent struct {
|
type findDialogComponent struct {
|
||||||
|
@ -151,12 +150,6 @@ func (f *findDialogComponent) IsEmpty() bool {
|
||||||
return f.list.IsEmpty()
|
return f.list.IsEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *findDialogComponent) SetProvider(provider CompletionProvider) {
|
|
||||||
f.completionProvider = provider
|
|
||||||
f.list.SetEmptyMessage(" " + provider.GetEmptyMessage())
|
|
||||||
f.list.SetItems([]CompletionItemI{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *findDialogComponent) selectFile(item CompletionItemI) tea.Cmd {
|
func (f *findDialogComponent) selectFile(item CompletionItemI) tea.Cmd {
|
||||||
return tea.Sequence(
|
return tea.Sequence(
|
||||||
f.Close(),
|
f.Close(),
|
||||||
|
@ -184,9 +177,15 @@ func createTextInput(existing *textinput.Model) textinput.Model {
|
||||||
|
|
||||||
ti := textinput.New()
|
ti := textinput.New()
|
||||||
|
|
||||||
ti.Styles.Blurred.Placeholder = styles.NewStyle().Foreground(textMutedColor).Background(bgColor).Lipgloss()
|
ti.Styles.Blurred.Placeholder = styles.NewStyle().
|
||||||
|
Foreground(textMutedColor).
|
||||||
|
Background(bgColor).
|
||||||
|
Lipgloss()
|
||||||
ti.Styles.Blurred.Text = styles.NewStyle().Foreground(textColor).Background(bgColor).Lipgloss()
|
ti.Styles.Blurred.Text = styles.NewStyle().Foreground(textColor).Background(bgColor).Lipgloss()
|
||||||
ti.Styles.Focused.Placeholder = styles.NewStyle().Foreground(textMutedColor).Background(bgColor).Lipgloss()
|
ti.Styles.Focused.Placeholder = styles.NewStyle().
|
||||||
|
Foreground(textMutedColor).
|
||||||
|
Background(bgColor).
|
||||||
|
Lipgloss()
|
||||||
ti.Styles.Focused.Text = styles.NewStyle().Foreground(textColor).Background(bgColor).Lipgloss()
|
ti.Styles.Focused.Text = styles.NewStyle().Foreground(textColor).Background(bgColor).Lipgloss()
|
||||||
ti.Styles.Cursor.Color = t.Primary()
|
ti.Styles.Cursor.Color = t.Primary()
|
||||||
ti.VirtualCursor = true
|
ti.VirtualCursor = true
|
||||||
|
@ -213,7 +212,6 @@ func NewFindDialog(completionProvider CompletionProvider) FindDialog {
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Load initial items
|
|
||||||
go func() {
|
go func() {
|
||||||
items, err := completionProvider.GetChildEntries("")
|
items, err := completionProvider.GetChildEntries("")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -51,7 +51,7 @@ type appModel struct {
|
||||||
editor chat.EditorComponent
|
editor chat.EditorComponent
|
||||||
messages chat.MessagesComponent
|
messages chat.MessagesComponent
|
||||||
completions dialog.CompletionDialog
|
completions dialog.CompletionDialog
|
||||||
completionManager *completions.CompletionManager
|
commandProvider dialog.CompletionProvider
|
||||||
showCompletionDialog bool
|
showCompletionDialog bool
|
||||||
leaderBinding *key.Binding
|
leaderBinding *key.Binding
|
||||||
isLeaderSequence bool
|
isLeaderSequence bool
|
||||||
|
@ -105,6 +105,7 @@ var BUGGED_SCROLL_KEYS = map[string]bool{
|
||||||
"m": true,
|
"m": true,
|
||||||
"[": true,
|
"[": true,
|
||||||
";": true,
|
";": true,
|
||||||
|
"<": true,
|
||||||
}
|
}
|
||||||
|
|
||||||
func isScrollRelatedInput(keyString string) bool {
|
func isScrollRelatedInput(keyString string) bool {
|
||||||
|
@ -134,6 +135,7 @@ func (a appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
switch msg := msg.(type) {
|
switch msg := msg.(type) {
|
||||||
case tea.KeyPressMsg:
|
case tea.KeyPressMsg:
|
||||||
keyString := msg.String()
|
keyString := msg.String()
|
||||||
|
|
||||||
if time.Since(a.lastScroll) < time.Millisecond*100 && (BUGGED_SCROLL_KEYS[keyString] || isScrollRelatedInput(keyString)) {
|
if time.Since(a.lastScroll) < time.Millisecond*100 && (BUGGED_SCROLL_KEYS[keyString] || isScrollRelatedInput(keyString)) {
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
@ -174,37 +176,16 @@ func (a appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Handle completions trigger
|
// 3. Handle completions trigger
|
||||||
if keyString == "/" && !a.showCompletionDialog {
|
if keyString == "/" &&
|
||||||
|
!a.showCompletionDialog &&
|
||||||
|
a.editor.Value() == "" {
|
||||||
a.showCompletionDialog = true
|
a.showCompletionDialog = true
|
||||||
|
|
||||||
initialValue := "/"
|
updated, cmd := a.editor.Update(msg)
|
||||||
currentInput := a.editor.Value()
|
|
||||||
|
|
||||||
// if the input doesn't end with a space,
|
|
||||||
// then we want to include the last word
|
|
||||||
// (ie, `packages/`)
|
|
||||||
if !strings.HasSuffix(currentInput, " ") {
|
|
||||||
words := strings.Split(a.editor.Value(), " ")
|
|
||||||
if len(words) > 0 {
|
|
||||||
lastWord := words[len(words)-1]
|
|
||||||
lastWord = strings.TrimSpace(lastWord)
|
|
||||||
initialValue = lastWord + "/"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updated, cmd := a.completions.Update(
|
|
||||||
app.CompletionDialogTriggeredMsg{
|
|
||||||
InitialValue: initialValue,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
a.completions = updated.(dialog.CompletionDialog)
|
|
||||||
cmds = append(cmds, cmd)
|
|
||||||
|
|
||||||
updated, cmd = a.editor.Update(msg)
|
|
||||||
a.editor = updated.(chat.EditorComponent)
|
a.editor = updated.(chat.EditorComponent)
|
||||||
cmds = append(cmds, cmd)
|
cmds = append(cmds, cmd)
|
||||||
|
|
||||||
updated, cmd = a.updateCompletions(msg)
|
updated, cmd = a.completions.Update(msg)
|
||||||
a.completions = updated.(dialog.CompletionDialog)
|
a.completions = updated.(dialog.CompletionDialog)
|
||||||
cmds = append(cmds, cmd)
|
cmds = append(cmds, cmd)
|
||||||
|
|
||||||
|
@ -214,7 +195,7 @@ func (a appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
if a.showCompletionDialog {
|
if a.showCompletionDialog {
|
||||||
switch keyString {
|
switch keyString {
|
||||||
case "tab", "enter", "esc", "ctrl+c":
|
case "tab", "enter", "esc", "ctrl+c":
|
||||||
updated, cmd := a.updateCompletions(msg)
|
updated, cmd := a.completions.Update(msg)
|
||||||
a.completions = updated.(dialog.CompletionDialog)
|
a.completions = updated.(dialog.CompletionDialog)
|
||||||
cmds = append(cmds, cmd)
|
cmds = append(cmds, cmd)
|
||||||
return a, tea.Batch(cmds...)
|
return a, tea.Batch(cmds...)
|
||||||
|
@ -224,7 +205,7 @@ func (a appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
a.editor = updated.(chat.EditorComponent)
|
a.editor = updated.(chat.EditorComponent)
|
||||||
cmds = append(cmds, cmd)
|
cmds = append(cmds, cmd)
|
||||||
|
|
||||||
updated, cmd = a.updateCompletions(msg)
|
updated, cmd = a.completions.Update(msg)
|
||||||
a.completions = updated.(dialog.CompletionDialog)
|
a.completions = updated.(dialog.CompletionDialog)
|
||||||
cmds = append(cmds, cmd)
|
cmds = append(cmds, cmd)
|
||||||
|
|
||||||
|
@ -971,22 +952,12 @@ func (a appModel) executeCommand(command commands.Command) (tea.Model, tea.Cmd)
|
||||||
return a, tea.Batch(cmds...)
|
return a, tea.Batch(cmds...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a appModel) updateCompletions(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
||||||
currentInput := a.editor.Value()
|
|
||||||
if currentInput != "" {
|
|
||||||
provider := a.completionManager.GetProvider(currentInput)
|
|
||||||
a.completions.SetProvider(provider)
|
|
||||||
}
|
|
||||||
return a.completions.Update(msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewModel(app *app.App) tea.Model {
|
func NewModel(app *app.App) tea.Model {
|
||||||
completionManager := completions.NewCompletionManager(app)
|
commandProvider := completions.NewCommandCompletionProvider(app)
|
||||||
initialProvider := completionManager.DefaultProvider()
|
|
||||||
|
|
||||||
messages := chat.NewMessagesComponent(app)
|
messages := chat.NewMessagesComponent(app)
|
||||||
editor := chat.NewEditorComponent(app)
|
editor := chat.NewEditorComponent(app)
|
||||||
completions := dialog.NewCompletionDialogComponent(initialProvider)
|
completions := dialog.NewCompletionDialogComponent(commandProvider)
|
||||||
|
|
||||||
var leaderBinding *key.Binding
|
var leaderBinding *key.Binding
|
||||||
if app.Config.Keybinds.Leader != "" {
|
if app.Config.Keybinds.Leader != "" {
|
||||||
|
@ -1000,7 +971,7 @@ func NewModel(app *app.App) tea.Model {
|
||||||
editor: editor,
|
editor: editor,
|
||||||
messages: messages,
|
messages: messages,
|
||||||
completions: completions,
|
completions: completions,
|
||||||
completionManager: completionManager,
|
commandProvider: commandProvider,
|
||||||
leaderBinding: leaderBinding,
|
leaderBinding: leaderBinding,
|
||||||
isLeaderSequence: false,
|
isLeaderSequence: false,
|
||||||
showCompletionDialog: false,
|
showCompletionDialog: false,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue