summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGal Schlezinger <[email protected]>2025-06-28 14:01:10 +0300
committerGitHub <[email protected]>2025-06-28 06:01:10 -0500
commitf618e569ab478920022a93a8a3deab2520326d09 (patch)
treee10c47e314891cf8b2ba5818dec0b5d05b14e7a2
parent7b394b91e2b40d526b36b3d468445ed1726bb297 (diff)
downloadopencode-f618e569ab478920022a93a8a3deab2520326d09.tar.gz
opencode-f618e569ab478920022a93a8a3deab2520326d09.zip
optimize edit-tool rendering (#463)
Co-authored-by: opencode <[email protected]> Co-authored-by: Adam <[email protected]>
-rw-r--r--packages/tui/internal/components/chat/messages.go5
-rw-r--r--packages/tui/internal/components/diff/diff.go32
-rw-r--r--packages/tui/internal/util/concurrency.go50
-rw-r--r--packages/tui/internal/util/util.go10
4 files changed, 88 insertions, 9 deletions
diff --git a/packages/tui/internal/components/chat/messages.go b/packages/tui/internal/components/chat/messages.go
index e1c7a9d14..356eba9f6 100644
--- a/packages/tui/internal/components/chat/messages.go
+++ b/packages/tui/internal/components/chat/messages.go
@@ -16,6 +16,7 @@ import (
"github.com/sst/opencode/internal/layout"
"github.com/sst/opencode/internal/styles"
"github.com/sst/opencode/internal/theme"
+ "github.com/sst/opencode/internal/util"
)
type MessagesComponent interface {
@@ -121,9 +122,13 @@ func (m *messagesComponent) renderView() {
return
}
+ measure := util.Measure("messages.renderView")
+ defer measure("messageCount", len(m.app.Messages))
+
t := theme.CurrentTheme()
blocks := make([]string, 0)
previousBlockType := none
+
for _, message := range m.app.Messages {
var content string
var cached bool
diff --git a/packages/tui/internal/components/diff/diff.go b/packages/tui/internal/components/diff/diff.go
index 9475c1f19..4b5c62008 100644
--- a/packages/tui/internal/components/diff/diff.go
+++ b/packages/tui/internal/components/diff/diff.go
@@ -8,6 +8,7 @@ import (
"regexp"
"strconv"
"strings"
+ "sync"
"github.com/alecthomas/chroma/v2"
"github.com/alecthomas/chroma/v2/formatters"
@@ -19,6 +20,7 @@ import (
"github.com/sergi/go-diff/diffmatchpatch"
stylesi "github.com/sst/opencode/internal/styles"
"github.com/sst/opencode/internal/theme"
+ "github.com/sst/opencode/internal/util"
)
// -------------------------------------------------------------------------
@@ -939,11 +941,22 @@ func RenderSideBySideHunk(fileName string, h Hunk, opts ...SideBySideOption) str
leftWidth := colWidth
rightWidth := config.TotalWidth - colWidth
var sb strings.Builder
- for _, p := range pairs {
- leftStr := renderLeftColumn(fileName, p.left, leftWidth)
- rightStr := renderRightColumn(fileName, p.right, rightWidth)
- sb.WriteString(leftStr + rightStr + "\n")
- }
+
+ util.WriteStringsPar(&sb, pairs, func(p linePair) string {
+ wg := &sync.WaitGroup{}
+ var leftStr, rightStr string
+ wg.Add(2)
+ go func() {
+ defer wg.Done()
+ leftStr = renderLeftColumn(fileName, p.left, leftWidth)
+ }()
+ go func() {
+ defer wg.Done()
+ rightStr = renderRightColumn(fileName, p.right, rightWidth)
+ }()
+ wg.Wait()
+ return leftStr + rightStr + "\n"
+ })
return sb.String()
}
@@ -957,7 +970,8 @@ func FormatUnifiedDiff(filename string, diffText string, opts ...UnifiedOption)
var sb strings.Builder
for _, h := range diffResult.Hunks {
- sb.WriteString(RenderUnifiedHunk(filename, h, opts...))
+ unifiedDiff := RenderUnifiedHunk(filename, h, opts...)
+ sb.WriteString(unifiedDiff)
}
return sb.String(), nil
@@ -973,7 +987,7 @@ func FormatDiff(filename string, diffText string, opts ...SideBySideOption) (str
var sb strings.Builder
// config := NewSideBySideConfig(opts...)
- for _, h := range diffResult.Hunks {
+ util.WriteStringsPar(&sb, diffResult.Hunks, func(h Hunk) string {
// sb.WriteString(
// lipgloss.NewStyle().
// Background(t.DiffHunkHeader()).
@@ -981,8 +995,8 @@ func FormatDiff(filename string, diffText string, opts ...SideBySideOption) (str
// Width(config.TotalWidth).
// Render(h.Header) + "\n",
// )
- sb.WriteString(RenderSideBySideHunk(filename, h, opts...))
- }
+ return RenderSideBySideHunk(filename, h, opts...)
+ })
return sb.String(), nil
}
diff --git a/packages/tui/internal/util/concurrency.go b/packages/tui/internal/util/concurrency.go
new file mode 100644
index 000000000..fb6eecec8
--- /dev/null
+++ b/packages/tui/internal/util/concurrency.go
@@ -0,0 +1,50 @@
+package util
+
+import (
+ "strings"
+ "sync"
+)
+
+// MapReducePar performs a parallel map-reduce operation on a slice of items.
+// It applies a function to each item in the slice concurrently,
+// and combines the results serially using a reducer returned from
+// each one of the functions, allowing the use of closures.
+func MapReducePar[a, b any](items []a, init b, fn func(a) func(b) b) b {
+ itemCount := len(items)
+ locks := make([]*sync.Mutex, itemCount)
+ mapped := make([]func(b) b, itemCount)
+
+ for i, value := range items {
+ lock := &sync.Mutex{}
+ lock.Lock()
+ locks[i] = lock
+ go func() {
+ defer lock.Unlock()
+ mapped[i] = fn(value)
+ }()
+ }
+
+ result := init
+ for i := range itemCount {
+ locks[i].Lock()
+ defer locks[i].Unlock()
+ f := mapped[i]
+ if f != nil {
+ result = f(result)
+ }
+ }
+
+ return result
+}
+
+// WriteStringsPar allows to iterate over a list and compute strings in parallel,
+// yet write them in order.
+func WriteStringsPar[a any](sb *strings.Builder, items []a, fn func(a) string) {
+ MapReducePar(items, sb, func(item a) func(*strings.Builder) *strings.Builder {
+ str := fn(item)
+ return func(sbdr *strings.Builder) *strings.Builder {
+ sbdr.WriteString(str)
+ return sbdr
+ }
+ })
+}
diff --git a/packages/tui/internal/util/util.go b/packages/tui/internal/util/util.go
index c7fd98a8c..da12cc5b3 100644
--- a/packages/tui/internal/util/util.go
+++ b/packages/tui/internal/util/util.go
@@ -1,8 +1,10 @@
package util
import (
+ "log/slog"
"os"
"strings"
+ "time"
tea "github.com/charmbracelet/bubbletea/v2"
)
@@ -35,3 +37,11 @@ func IsWsl() bool {
return false
}
+
+func Measure(tag string) func(...any) {
+ startTime := time.Now()
+ return func(tags ...any) {
+ args := append([]any{"timeTakenMs", time.Since(startTime).Milliseconds()}, tags...)
+ slog.Info(tag, args...)
+ }
+}