on main help sign in
feat: use the new diff formatter
ed06ea2Kujtim Hoxha
internal/llm/tools/edit.go+31−23
@@ -24,14 +24,16 @@ type EditParams struct {
24}
24}
25
25
26type EditPermissionsParams struct {
26type EditPermissionsParams struct {
27− FilePath string `json:"file_path"`
27+ FilePath string `json:"file_path"`
28− Diff string `json:"diff"`
28+ OldContent string `json:"old_content,omitempty"`
29+ NewContent string `json:"new_content,omitempty"`
29}
30}
30
31
31type EditResponseMetadata struct {
32type EditResponseMetadata struct {
32− Diff string `json:"diff"`
33− Additions int `json:"additions"`
33+ Additions int `json:"additions"`
34− Removals int `json:"removals"`
34+ Removals int `json:"removals"`
35+ OldContent string `json:"old_content,omitempty"`
36+ NewContent string `json:"new_content,omitempty"`
35}
37}
36
38
37type editTool struct {
39type editTool struct {
@@ -191,7 +193,7 @@ func (e *editTool) createNewFile(ctx context.Context, filePath, content string)
191 return ToolResponse{}, fmt.Errorf("session ID and message ID are required for creating a new file")
193 return ToolResponse{}, fmt.Errorf("session ID and message ID are required for creating a new file")
192 }
194 }
193
195
194− diff, additions, removals := diff.GenerateDiff(
196+ _, additions, removals := diff.GenerateDiff(
195 "",
197 "",
196 content,
198 content,
197 filePath,
199 filePath,
@@ -209,8 +211,9 @@ func (e *editTool) createNewFile(ctx context.Context, filePath, content string)
209 Action: "write",
211 Action: "write",
210 Description: fmt.Sprintf("Create file %s", filePath),
212 Description: fmt.Sprintf("Create file %s", filePath),
211 Params: EditPermissionsParams{
213 Params: EditPermissionsParams{
212− FilePath: filePath,
214+ FilePath: filePath,
213− Diff: diff,
215+ OldContent: "",
216+ NewContent: content,
214 },
217 },
215 },
218 },
216 )
219 )
@@ -243,9 +246,10 @@ func (e *editTool) createNewFile(ctx context.Context, filePath, content string)
243 return WithResponseMetadata(
246 return WithResponseMetadata(
244 NewTextResponse("File created: "+filePath),
247 NewTextResponse("File created: "+filePath),
245 EditResponseMetadata{
248 EditResponseMetadata{
246− Diff: diff,
249+ OldContent: "",
250+ NewContent: content,
247− Additions: additions,
251+ Additions: additions,
248− Removals: removals,
252+ Removals: removals,
249 },
253 },
250 ), nil
254 ), nil
251}
255}
@@ -301,7 +305,7 @@ func (e *editTool) deleteContent(ctx context.Context, filePath, oldString string
301 return ToolResponse{}, fmt.Errorf("session ID and message ID are required for creating a new file")
305 return ToolResponse{}, fmt.Errorf("session ID and message ID are required for creating a new file")
302 }
306 }
303
307
304− diff, additions, removals := diff.GenerateDiff(
308+ _, additions, removals := diff.GenerateDiff(
305 oldContent,
309 oldContent,
306 newContent,
310 newContent,
307 filePath,
311 filePath,
@@ -320,8 +324,9 @@ func (e *editTool) deleteContent(ctx context.Context, filePath, oldString string
320 Action: "write",
324 Action: "write",
321 Description: fmt.Sprintf("Delete content from file %s", filePath),
325 Description: fmt.Sprintf("Delete content from file %s", filePath),
322 Params: EditPermissionsParams{
326 Params: EditPermissionsParams{
323− FilePath: filePath,
327+ FilePath: filePath,
324− Diff: diff,
328+ OldContent: oldContent,
329+ NewContent: newContent,
325 },
330 },
326 },
331 },
327 )
332 )
@@ -362,9 +367,10 @@ func (e *editTool) deleteContent(ctx context.Context, filePath, oldString string
362 return WithResponseMetadata(
367 return WithResponseMetadata(
363 NewTextResponse("Content deleted from file: "+filePath),
368 NewTextResponse("Content deleted from file: "+filePath),
364 EditResponseMetadata{
369 EditResponseMetadata{
365− Diff: diff,
370+ OldContent: oldContent,
371+ NewContent: newContent,
366− Additions: additions,
372+ Additions: additions,
367− Removals: removals,
373+ Removals: removals,
368 },
374 },
369 ), nil
375 ), nil
370}
376}
@@ -422,7 +428,7 @@ func (e *editTool) replaceContent(ctx context.Context, filePath, oldString, newS
422 if sessionID == "" || messageID == "" {
428 if sessionID == "" || messageID == "" {
423 return ToolResponse{}, fmt.Errorf("session ID and message ID are required for creating a new file")
429 return ToolResponse{}, fmt.Errorf("session ID and message ID are required for creating a new file")
424 }
430 }
425− diff, additions, removals := diff.GenerateDiff(
431+ _, additions, removals := diff.GenerateDiff(
426 oldContent,
432 oldContent,
427 newContent,
433 newContent,
428 filePath,
434 filePath,
@@ -440,8 +446,9 @@ func (e *editTool) replaceContent(ctx context.Context, filePath, oldString, newS
440 Action: "write",
446 Action: "write",
441 Description: fmt.Sprintf("Replace content in file %s", filePath),
447 Description: fmt.Sprintf("Replace content in file %s", filePath),
442 Params: EditPermissionsParams{
448 Params: EditPermissionsParams{
443− FilePath: filePath,
449+ FilePath: filePath,
444− Diff: diff,
450+ OldContent: oldContent,
451+ NewContent: newContent,
445 },
452 },
446 },
453 },
447 )
454 )
@@ -482,8 +489,9 @@ func (e *editTool) replaceContent(ctx context.Context, filePath, oldString, newS
482 return WithResponseMetadata(
489 return WithResponseMetadata(
483 NewTextResponse("Content replaced in file: "+filePath),
490 NewTextResponse("Content replaced in file: "+filePath),
484 EditResponseMetadata{
491 EditResponseMetadata{
485− Diff: diff,
492+ OldContent: oldContent,
493+ NewContent: newContent,
486− Additions: additions,
494+ Additions: additions,
487− Removals: removals,
495+ Removals: removals,
488 }), nil
496 }), nil
489}
497}
internal/tui/components/chat/messages/renderer.go+7−4
@@ -6,11 +6,11 @@ import (
6 "strings"
6 "strings"
7 "time"
7 "time"
8
8
9− "github.com/charmbracelet/crush/internal/diff"
10 "github.com/charmbracelet/crush/internal/fileutil"
9 "github.com/charmbracelet/crush/internal/fileutil"
11 "github.com/charmbracelet/crush/internal/highlight"
10 "github.com/charmbracelet/crush/internal/highlight"
12 "github.com/charmbracelet/crush/internal/llm/agent"
11 "github.com/charmbracelet/crush/internal/llm/agent"
13 "github.com/charmbracelet/crush/internal/llm/tools"
12 "github.com/charmbracelet/crush/internal/llm/tools"
13+ "github.com/charmbracelet/crush/internal/tui/components/core"
14 "github.com/charmbracelet/crush/internal/tui/styles"
14 "github.com/charmbracelet/crush/internal/tui/styles"
15 "github.com/charmbracelet/lipgloss/v2"
15 "github.com/charmbracelet/lipgloss/v2"
16 "github.com/charmbracelet/lipgloss/v2/tree"
16 "github.com/charmbracelet/lipgloss/v2/tree"
@@ -257,9 +257,12 @@ func (er editRenderer) Render(v *toolCallCmp) string {
257 return renderPlainContent(v, v.result.Content)
257 return renderPlainContent(v, v.result.Content)
258 }
258 }
259
259
260− trunc := truncateHeight(meta.Diff, responseContextHeight)
260+ formatter := core.DiffFormatter().
261− diffView, _ := diff.FormatDiff(trunc, diff.WithTotalWidth(v.textWidth()-2))
261+ Before(fileutil.PrettyPath(params.FilePath), meta.OldContent).
262− return diffView
262+ After(fileutil.PrettyPath(params.FilePath), meta.NewContent).
263+ Split().
264+ Width(v.textWidth() - 2) // -2 for padding
265+ return formatter.String()
263 })
266 })
264}
267}
265
268
internal/tui/components/core/helpers.go+11
@@ -4,6 +4,8 @@ import (
4 "image/color"
4 "image/color"
5 "strings"
5 "strings"
6
6
7+ "github.com/alecthomas/chroma/v2"
8+ "github.com/charmbracelet/crush/internal/exp/diffview"
7 "github.com/charmbracelet/crush/internal/tui/styles"
9 "github.com/charmbracelet/crush/internal/tui/styles"
8 "github.com/charmbracelet/lipgloss/v2"
10 "github.com/charmbracelet/lipgloss/v2"
9 "github.com/charmbracelet/x/ansi"
11 "github.com/charmbracelet/x/ansi"
@@ -131,3 +133,12 @@ func SelectableButtons(buttons []ButtonOpts, spacing string) string {
131
133
132 return lipgloss.JoinHorizontal(lipgloss.Left, parts...)
134 return lipgloss.JoinHorizontal(lipgloss.Left, parts...)
133}
135}
136+
137+func DiffFormatter() *diffview.DiffView {
138+ formatDiff := diffview.New()
139+ style := chroma.MustNewStyle("crush", styles.GetChromaTheme())
140+ diff := formatDiff.
141+ SyntaxHightlight(true).
142+ ChromaStyle(style)
143+ return diff
144+}
internal/tui/components/dialogs/permissions/permissions.go+6−3
@@ -267,10 +267,13 @@ func (p *permissionDialogCmp) renderBashContent() string {
267
267
268func (p *permissionDialogCmp) renderEditContent() string {
268func (p *permissionDialogCmp) renderEditContent() string {
269 if pr, ok := p.permission.Params.(tools.EditPermissionsParams); ok {
269 if pr, ok := p.permission.Params.(tools.EditPermissionsParams); ok {
270− diff := p.GetOrSetDiff(p.permission.ID, func() (string, error) {
270+ formatter := core.DiffFormatter().
271+ Before(fileutil.PrettyPath(pr.FilePath), pr.OldContent).
272+ After(fileutil.PrettyPath(pr.FilePath), pr.NewContent).
271− return diff.FormatDiff(pr.Diff, diff.WithTotalWidth(p.contentViewPort.Width()))
273+ Width(p.contentViewPort.Width()).
272− })
274+ Split()
273
275
276+ diff := formatter.String()
274 contentHeight := min(p.height-9, lipgloss.Height(diff))
277 contentHeight := min(p.height-9, lipgloss.Height(diff))
275 p.contentViewPort.SetHeight(contentHeight)
278 p.contentViewPort.SetHeight(contentHeight)
276 p.contentViewPort.SetContent(diff)
279 p.contentViewPort.SetContent(diff)