mirror of
https://github.com/usestrix/strix.git
synced 2026-08-16 09:26:39 +02:00
144 lines
3.8 KiB
Go
144 lines
3.8 KiB
Go
package render
|
|
|
|
import (
|
|
"strings"
|
|
)
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Filesystem: apply_patch + view_image (filesystem_renderer.py)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
const (
|
|
addFilePfx = "*** Add File: "
|
|
deleteFilePfx = "*** Delete File: "
|
|
updateFilePfx = "*** Update File: "
|
|
beginPatch = "*** Begin Patch"
|
|
endPatch = "*** End Patch"
|
|
)
|
|
|
|
type patchOp struct {
|
|
kind string
|
|
path string
|
|
old []string
|
|
new []string
|
|
}
|
|
|
|
func extractPatchText(args map[string]any) string {
|
|
if raw, ok := args["patch"].(string); ok {
|
|
return raw
|
|
}
|
|
if raw, ok := args["patch"].(map[string]any); ok {
|
|
if inner, ok := raw["patch"].(string); ok {
|
|
return inner
|
|
}
|
|
}
|
|
if fb, ok := args["input"].(string); ok {
|
|
return fb
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func parsePatchOperations(patch string) []patchOp {
|
|
var ops []patchOp
|
|
var cur *patchOp
|
|
flush := func() {
|
|
if cur != nil && cur.kind != "" {
|
|
ops = append(ops, *cur)
|
|
}
|
|
cur = nil
|
|
}
|
|
for _, line := range strings.Split(patch, "\n") {
|
|
switch {
|
|
case line == beginPatch || line == endPatch:
|
|
continue
|
|
case strings.HasPrefix(line, addFilePfx):
|
|
flush()
|
|
cur = &patchOp{kind: "add", path: strings.TrimSpace(line[len(addFilePfx):])}
|
|
case strings.HasPrefix(line, updateFilePfx):
|
|
flush()
|
|
cur = &patchOp{kind: "update", path: strings.TrimSpace(line[len(updateFilePfx):])}
|
|
case strings.HasPrefix(line, deleteFilePfx):
|
|
flush()
|
|
cur = &patchOp{kind: "delete", path: strings.TrimSpace(line[len(deleteFilePfx):])}
|
|
case cur != nil && cur.kind == "update":
|
|
if strings.HasPrefix(line, "@@") {
|
|
continue
|
|
}
|
|
if strings.HasPrefix(line, "-") && !strings.HasPrefix(line, "---") {
|
|
cur.old = append(cur.old, line[1:])
|
|
} else if strings.HasPrefix(line, "+") && !strings.HasPrefix(line, "+++") {
|
|
cur.new = append(cur.new, line[1:])
|
|
}
|
|
case cur != nil && cur.kind == "add":
|
|
if strings.HasPrefix(line, "+") {
|
|
cur.new = append(cur.new, line[1:])
|
|
} else if strings.TrimSpace(line) != "" {
|
|
cur.new = append(cur.new, line)
|
|
}
|
|
}
|
|
}
|
|
flush()
|
|
return ops
|
|
}
|
|
|
|
var opLabel = map[string]string{"add": "create", "update": "edit", "delete": "delete"}
|
|
|
|
func renderPatchOperation(b *strings.Builder, op patchOp) {
|
|
label := opLabel[op.kind]
|
|
if label == "" {
|
|
label = "file"
|
|
}
|
|
b.WriteString(Col(Emerald).Render("◇ ") + Dim().Render(label))
|
|
if op.path != "" {
|
|
p := op.path
|
|
if len(p) > 60 {
|
|
p = p[len(p)-60:]
|
|
}
|
|
b.WriteString(" " + Dim().Render(p))
|
|
}
|
|
lang := languageForPath(op.path)
|
|
if op.kind == "update" {
|
|
for _, line := range highlightLines(op.old, lang) {
|
|
b.WriteString("\n" + Col(Red).Render("-") + " " + line)
|
|
}
|
|
for _, line := range highlightLines(op.new, lang) {
|
|
b.WriteString("\n" + Col(Green).Render("+") + " " + line)
|
|
}
|
|
} else if op.kind == "add" && len(op.new) > 0 {
|
|
b.WriteString("\n" + HighlightCode(strings.Join(op.new, "\n"), lang))
|
|
}
|
|
}
|
|
|
|
func highlightLines(lines []string, lang string) []string {
|
|
if len(lines) == 0 || lang == "" {
|
|
return lines
|
|
}
|
|
return strings.Split(HighlightCode(strings.Join(lines, "\n"), lang), "\n")
|
|
}
|
|
|
|
func renderApplyPatch(args map[string]any, result any, status string) string {
|
|
ops := parsePatchOperations(extractPatchText(args))
|
|
var b strings.Builder
|
|
if len(ops) == 0 {
|
|
b.WriteString(Col(Emerald).Render("◇ ") + Dim().Render("patch"))
|
|
if s, ok := result.(string); ok && strings.TrimSpace(s) != "" {
|
|
b.WriteString("\n " + Dim().Render(strings.TrimSpace(s)))
|
|
} else if result == nil {
|
|
b.WriteString(" " + Dim().Render("Processing..."))
|
|
}
|
|
return b.String()
|
|
}
|
|
for i, op := range ops {
|
|
if i > 0 {
|
|
b.WriteString("\n")
|
|
}
|
|
renderPatchOperation(&b, op)
|
|
}
|
|
if status == "failed" {
|
|
if s, ok := result.(string); ok && strings.TrimSpace(s) != "" {
|
|
b.WriteString("\n " + Col(Red).Render(strings.TrimSpace(s)))
|
|
}
|
|
}
|
|
return b.String()
|
|
}
|