Skip to content

Commit 251f602

Browse files
committed
feat(context-dialog): init
1 parent 603a3e3 commit 251f602

File tree

8 files changed

+657
-11
lines changed

8 files changed

+657
-11
lines changed

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ require (
100100
github.com/rivo/uniseg v0.4.7 // indirect
101101
github.com/rogpeppe/go-internal v1.14.1 // indirect
102102
github.com/sagikazarmark/locafero v0.7.0 // indirect
103+
github.com/sahilm/fuzzy v0.1.1 // indirect
103104
github.com/sethvargo/go-retry v0.3.0 // indirect
104105
github.com/sourcegraph/conc v0.3.0 // indirect
105106
github.com/spf13/afero v1.12.0 // indirect

go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,8 @@ github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7
207207
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
208208
github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
209209
github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
210+
github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA=
211+
github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
210212
github.com/sebdah/goldie/v2 v2.5.3 h1:9ES/mNN+HNUbNWpVAlrzuZ7jE+Nrczbj8uFRjM7624Y=
211213
github.com/sebdah/goldie/v2 v2.5.3/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI=
212214
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=

internal/completions/files-folders.go

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package completions
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"os/exec"
7+
"path/filepath"
8+
"sort"
9+
10+
"github.com/opencode-ai/opencode/internal/tui/components/dialog"
11+
)
12+
13+
type filesAndFoldersContextGroup struct {
14+
prefix string
15+
}
16+
17+
func (cg *filesAndFoldersContextGroup) GetId() string {
18+
return cg.prefix
19+
}
20+
21+
func (cg *filesAndFoldersContextGroup) GetEntry() dialog.CompletionItemI {
22+
return dialog.NewCompletionItem(dialog.CompletionItem{
23+
Title: "Files & Folders",
24+
Value: "files",
25+
})
26+
}
27+
28+
func getFilesRg() ([]string, error) {
29+
searchRoot := "."
30+
31+
rgBin, err := exec.LookPath("rg")
32+
if err != nil {
33+
return nil, fmt.Errorf("ripgrep not found in $PATH: %w", err)
34+
}
35+
36+
args := []string{
37+
"--files",
38+
"-L",
39+
"--null",
40+
}
41+
42+
cmd := exec.Command(rgBin, args...)
43+
cmd.Dir = "."
44+
45+
out, err := cmd.CombinedOutput()
46+
if err != nil {
47+
if ee, ok := err.(*exec.ExitError); ok && ee.ExitCode() == 1 {
48+
return nil, nil
49+
}
50+
return nil, fmt.Errorf("ripgrep: %w\n%s", err, out)
51+
}
52+
53+
var matches []string
54+
for _, p := range bytes.Split(out, []byte{0}) {
55+
if len(p) == 0 {
56+
continue
57+
}
58+
abs := filepath.Join(searchRoot, string(p))
59+
matches = append(matches, abs)
60+
}
61+
62+
sort.SliceStable(matches, func(i, j int) bool {
63+
return len(matches[i]) < len(matches[j])
64+
})
65+
66+
return matches, nil
67+
}
68+
69+
func (cg *filesAndFoldersContextGroup) GetChildEntries() ([]dialog.CompletionItemI, error) {
70+
matches, err := getFilesRg()
71+
if err != nil {
72+
return nil, err
73+
}
74+
75+
items := make([]dialog.CompletionItemI, 0)
76+
77+
for _, file := range matches {
78+
item := dialog.NewCompletionItem(dialog.CompletionItem{
79+
Title: file,
80+
Value: file,
81+
})
82+
items = append(items, item)
83+
}
84+
85+
return items, nil
86+
}
87+
88+
func NewFileAndFolderContextGroup() dialog.CompletionProvider {
89+
return &filesAndFoldersContextGroup{prefix: "file"}
90+
}

internal/llm/models/gemini.go

+13
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const (
88
Gemini25 ModelID = "gemini-2.5"
99
Gemini20Flash ModelID = "gemini-2.0-flash"
1010
Gemini20FlashLite ModelID = "gemini-2.0-flash-lite"
11+
Gemini25ProExp ModelID = "gemini-2.5-pro-exp-03-25"
1112
)
1213

1314
var GeminiModels = map[ModelID]Model{
@@ -60,4 +61,16 @@ var GeminiModels = map[ModelID]Model{
6061
ContextWindow: 1000000,
6162
DefaultMaxTokens: 6000,
6263
},
64+
Gemini25ProExp: {
65+
ID: Gemini20FlashLite,
66+
Name: "Gemini 2.5 Pro Exp",
67+
Provider: ProviderGemini,
68+
APIModel: "gemini-2.5-pro-exp-03-25",
69+
CostPer1MIn: 1.25,
70+
CostPer1MInCached: 0,
71+
CostPer1MOutCached: 0,
72+
CostPer1MOut: 10,
73+
ContextWindow: 1000000,
74+
DefaultMaxTokens: 6000,
75+
},
6376
}

internal/tui/components/chat/editor.go

+6
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package chat
33
import (
44
"os"
55
"os/exec"
6+
"strings"
67

78
"github.com/charmbracelet/bubbles/key"
89
"github.com/charmbracelet/bubbles/textarea"
@@ -104,6 +105,11 @@ func (m *editorCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
104105
switch msg := msg.(type) {
105106
case dialog.ThemeChangedMsg:
106107
m.textarea = CreateTextArea(&m.textarea)
108+
case dialog.CompletionSelectedMsg:
109+
existingValue := m.textarea.Value()
110+
modifiedValue := strings.Replace(existingValue, msg.SearchString, msg.CompletionValue, 1)
111+
112+
m.textarea.SetValue(modifiedValue)
107113
return m, nil
108114
case SessionSelectedMsg:
109115
if msg.ID != m.session.ID {

0 commit comments

Comments
 (0)