Detailed changes
@@ -238,7 +238,7 @@ func readTextFile(filePath string, offset, limit int) (string, int, error) {
lineCount := 0
- scanner := bufio.NewScanner(file)
+ scanner := NewLineScanner(file)
if offset > 0 {
for lineCount < offset && scanner.Scan() {
lineCount++
@@ -298,3 +298,25 @@ func isImageFile(filePath string) (bool, string) {
return false, ""
}
}
+
+type LineScanner struct {
+ scanner *bufio.Scanner
+}
+
+func NewLineScanner(r io.Reader) *LineScanner {
+ return &LineScanner{
+ scanner: bufio.NewScanner(r),
+ }
+}
+
+func (s *LineScanner) Scan() bool {
+ return s.scanner.Scan()
+}
+
+func (s *LineScanner) Text() string {
+ return s.scanner.Text()
+}
+
+func (s *LineScanner) Err() error {
+ return s.scanner.Err()
+}
@@ -3,7 +3,6 @@ package messages
import (
"encoding/json"
"fmt"
- "strconv"
"strings"
"time"
@@ -657,7 +656,6 @@ func joinHeaderBody(header, body string) string {
func renderPlainContent(v *toolCallCmp, content string) string {
t := styles.CurrentTheme()
content = strings.TrimSpace(content)
- content = escapeContent(t, content)
lines := strings.Split(content, "\n")
width := v.textWidth() - 2 // -2 for left padding
@@ -696,7 +694,6 @@ func pad(v any, width int) string {
func renderCodeContent(v *toolCallCmp, path, content string, offset int) string {
t := styles.CurrentTheme()
- content = escapeContent(t, content)
truncated := truncateHeight(content, responseContextHeight)
highlighted, _ := highlight.SyntaxHighlight(truncated, path, t.BgBase)
@@ -769,45 +766,3 @@ func prettifyToolName(name string) string {
return name
}
}
-
-// escapeContent escapes ANSI escape sequences and control characters in the
-// content and styles it for display in the terminal.
-func escapeContent(t *styles.Theme, content string) string {
- content = strings.ReplaceAll(content, "\r\n", "\n")
- lines := strings.Split(content, "\n")
- for i, line := range lines {
- lines[i] = escapeLine(t, line)
- }
-
- content = strings.Join(lines, "\n")
- return content
-}
-
-// escapeLine escapes ANSI escape sequences and control characters and styles
-// them for display in the terminal.
-func escapeLine(t *styles.Theme, text string) string {
- var (
- sb strings.Builder
- state byte
- seq string
- n int
- w int
- )
- var faint lipgloss.Style
- if t != nil {
- faint = t.S().Muted.Faint(true)
- }
- for len(text) > 0 {
- seq, w, n, state = ansi.DecodeSequence(text, state, nil)
- if w > 0 {
- sb.WriteString(seq)
- } else {
- quote := strconv.Quote(seq)
- quote = strings.TrimPrefix(quote, "\"")
- quote = strings.TrimSuffix(quote, "\"")
- sb.WriteString(faint.Render(quote))
- }
- text = text[n:]
- }
- return sb.String()
-}
@@ -1,52 +0,0 @@
-package messages
-
-import (
- "testing"
-)
-
-func TestEscapeContent(t *testing.T) {
- cases := []struct {
- name string
- input string
- expected string
- }{
- {
- name: "nothing to escape",
- input: "Hello, World!",
- expected: "Hello, World!",
- },
- {
- name: "escape csi sequences",
- input: "\x1b[31mRed Text\x1b[0m",
- expected: "\\x1b[31mRed Text\\x1b[0m",
- },
- {
- name: "escape control characters",
- input: "Hello\x00World\x7f!",
- expected: "Hello\\x00World\\x7f!",
- },
- {
- name: "escape csi sequences with control characters",
- input: "\x1b[31mHello\x00World\x7f!\x1b[0m",
- expected: "\\x1b[31mHello\\x00World\\x7f!\\x1b[0m",
- },
- {
- name: "just unicode",
- input: "ใใใซใกใฏ", // "Hello" in Japanese
- expected: "ใใใซใกใฏ",
- },
- {
- name: "unicode with csi sequences and control characters",
- input: "\x1b[31mใใใซใกใฏ\x00World\x7f!\x1b[0m",
- expected: "\\x1b[31mใใใซใกใฏ\\x00World\\x7f!\\x1b[0m",
- },
- }
- for i, c := range cases {
- t.Run(c.name, func(t *testing.T) {
- result := escapeContent(nil, c.input)
- if result != c.expected {
- t.Errorf("case %d, expected %q, got %q", i+1, c.expected, result)
- }
- })
- }
-}