diff --git a/internal/llm/tools/view.go b/internal/llm/tools/view.go index ff3749dd8424639b8c8700ce46ebaeb61a3d0394..27bbc237209e64637cfefb0f4ff1097f96641c2e 100644 --- a/internal/llm/tools/view.go +++ b/internal/llm/tools/view.go @@ -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() +} diff --git a/internal/tui/components/chat/messages/renderer.go b/internal/tui/components/chat/messages/renderer.go index 6bf3fbc3fa0c4fe6a6ba44d930bf3418696ea1d3..87eb2c8476655fe7d11fc8c787e73b32d4584de4 100644 --- a/internal/tui/components/chat/messages/renderer.go +++ b/internal/tui/components/chat/messages/renderer.go @@ -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() -} diff --git a/internal/tui/components/chat/messages/renderer_test.go b/internal/tui/components/chat/messages/renderer_test.go deleted file mode 100644 index cc20ed711aa16ab1bd2ab8f462cd9ee33e58b005..0000000000000000000000000000000000000000 --- a/internal/tui/components/chat/messages/renderer_test.go +++ /dev/null @@ -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) - } - }) - } -}