diff --git a/internal/llm/tools/edit.go b/internal/llm/tools/edit.go index 2411187c1b5e6b93cc9f7fff4cdfa4b2014bbca8..fdbf4042528cfe8e2e213860203a16cb5d82ecf5 100644 --- a/internal/llm/tools/edit.go +++ b/internal/llm/tools/edit.go @@ -24,14 +24,16 @@ type EditParams struct { } type EditPermissionsParams struct { - FilePath string `json:"file_path"` - Diff string `json:"diff"` + FilePath string `json:"file_path"` + OldContent string `json:"old_content,omitempty"` + NewContent string `json:"new_content,omitempty"` } type EditResponseMetadata struct { - Diff string `json:"diff"` - Additions int `json:"additions"` - Removals int `json:"removals"` + Additions int `json:"additions"` + Removals int `json:"removals"` + OldContent string `json:"old_content,omitempty"` + NewContent string `json:"new_content,omitempty"` } type editTool struct { @@ -191,7 +193,7 @@ func (e *editTool) createNewFile(ctx context.Context, filePath, content string) return ToolResponse{}, fmt.Errorf("session ID and message ID are required for creating a new file") } - diff, additions, removals := diff.GenerateDiff( + _, additions, removals := diff.GenerateDiff( "", content, filePath, @@ -209,8 +211,9 @@ func (e *editTool) createNewFile(ctx context.Context, filePath, content string) Action: "write", Description: fmt.Sprintf("Create file %s", filePath), Params: EditPermissionsParams{ - FilePath: filePath, - Diff: diff, + FilePath: filePath, + OldContent: "", + NewContent: content, }, }, ) @@ -243,9 +246,10 @@ func (e *editTool) createNewFile(ctx context.Context, filePath, content string) return WithResponseMetadata( NewTextResponse("File created: "+filePath), EditResponseMetadata{ - Diff: diff, - Additions: additions, - Removals: removals, + OldContent: "", + NewContent: content, + Additions: additions, + Removals: removals, }, ), nil } @@ -301,7 +305,7 @@ func (e *editTool) deleteContent(ctx context.Context, filePath, oldString string return ToolResponse{}, fmt.Errorf("session ID and message ID are required for creating a new file") } - diff, additions, removals := diff.GenerateDiff( + _, additions, removals := diff.GenerateDiff( oldContent, newContent, filePath, @@ -320,8 +324,9 @@ func (e *editTool) deleteContent(ctx context.Context, filePath, oldString string Action: "write", Description: fmt.Sprintf("Delete content from file %s", filePath), Params: EditPermissionsParams{ - FilePath: filePath, - Diff: diff, + FilePath: filePath, + OldContent: oldContent, + NewContent: newContent, }, }, ) @@ -362,9 +367,10 @@ func (e *editTool) deleteContent(ctx context.Context, filePath, oldString string return WithResponseMetadata( NewTextResponse("Content deleted from file: "+filePath), EditResponseMetadata{ - Diff: diff, - Additions: additions, - Removals: removals, + OldContent: oldContent, + NewContent: newContent, + Additions: additions, + Removals: removals, }, ), nil } @@ -422,7 +428,7 @@ func (e *editTool) replaceContent(ctx context.Context, filePath, oldString, newS if sessionID == "" || messageID == "" { return ToolResponse{}, fmt.Errorf("session ID and message ID are required for creating a new file") } - diff, additions, removals := diff.GenerateDiff( + _, additions, removals := diff.GenerateDiff( oldContent, newContent, filePath, @@ -440,8 +446,9 @@ func (e *editTool) replaceContent(ctx context.Context, filePath, oldString, newS Action: "write", Description: fmt.Sprintf("Replace content in file %s", filePath), Params: EditPermissionsParams{ - FilePath: filePath, - Diff: diff, + FilePath: filePath, + OldContent: oldContent, + NewContent: newContent, }, }, ) @@ -482,8 +489,9 @@ func (e *editTool) replaceContent(ctx context.Context, filePath, oldString, newS return WithResponseMetadata( NewTextResponse("Content replaced in file: "+filePath), EditResponseMetadata{ - Diff: diff, - Additions: additions, - Removals: removals, + OldContent: oldContent, + NewContent: newContent, + Additions: additions, + Removals: removals, }), nil } diff --git a/internal/tui/components/chat/messages/renderer.go b/internal/tui/components/chat/messages/renderer.go index 12e34c5d11486859df5d22cfe23d646fff456c91..d63201fe17fe2841b3d2ee77c63edc8bbef0e34f 100644 --- a/internal/tui/components/chat/messages/renderer.go +++ b/internal/tui/components/chat/messages/renderer.go @@ -6,11 +6,11 @@ import ( "strings" "time" - "github.com/charmbracelet/crush/internal/diff" "github.com/charmbracelet/crush/internal/fileutil" "github.com/charmbracelet/crush/internal/highlight" "github.com/charmbracelet/crush/internal/llm/agent" "github.com/charmbracelet/crush/internal/llm/tools" + "github.com/charmbracelet/crush/internal/tui/components/core" "github.com/charmbracelet/crush/internal/tui/styles" "github.com/charmbracelet/lipgloss/v2" "github.com/charmbracelet/lipgloss/v2/tree" @@ -257,9 +257,12 @@ func (er editRenderer) Render(v *toolCallCmp) string { return renderPlainContent(v, v.result.Content) } - trunc := truncateHeight(meta.Diff, responseContextHeight) - diffView, _ := diff.FormatDiff(trunc, diff.WithTotalWidth(v.textWidth()-2)) - return diffView + formatter := core.DiffFormatter(). + Before(fileutil.PrettyPath(params.FilePath), meta.OldContent). + After(fileutil.PrettyPath(params.FilePath), meta.NewContent). + Split(). + Width(v.textWidth() - 2) // -2 for padding + return formatter.String() }) } diff --git a/internal/tui/components/core/helpers.go b/internal/tui/components/core/helpers.go index e586b0563278080eb85c7e0bbaa4dbee86e670e9..f147e074dab550b4315481f35b8c5d1fdf39f153 100644 --- a/internal/tui/components/core/helpers.go +++ b/internal/tui/components/core/helpers.go @@ -4,6 +4,8 @@ import ( "image/color" "strings" + "github.com/alecthomas/chroma/v2" + "github.com/charmbracelet/crush/internal/exp/diffview" "github.com/charmbracelet/crush/internal/tui/styles" "github.com/charmbracelet/lipgloss/v2" "github.com/charmbracelet/x/ansi" @@ -131,3 +133,12 @@ func SelectableButtons(buttons []ButtonOpts, spacing string) string { return lipgloss.JoinHorizontal(lipgloss.Left, parts...) } + +func DiffFormatter() *diffview.DiffView { + formatDiff := diffview.New() + style := chroma.MustNewStyle("crush", styles.GetChromaTheme()) + diff := formatDiff. + SyntaxHightlight(true). + ChromaStyle(style) + return diff +} diff --git a/internal/tui/components/dialogs/permissions/permissions.go b/internal/tui/components/dialogs/permissions/permissions.go index 5a4680d9409310c089965efa6801a70633ef5317..f36c9ec085c0627174078ec589659b28c912e327 100644 --- a/internal/tui/components/dialogs/permissions/permissions.go +++ b/internal/tui/components/dialogs/permissions/permissions.go @@ -267,10 +267,13 @@ func (p *permissionDialogCmp) renderBashContent() string { func (p *permissionDialogCmp) renderEditContent() string { if pr, ok := p.permission.Params.(tools.EditPermissionsParams); ok { - diff := p.GetOrSetDiff(p.permission.ID, func() (string, error) { - return diff.FormatDiff(pr.Diff, diff.WithTotalWidth(p.contentViewPort.Width())) - }) + formatter := core.DiffFormatter(). + Before(fileutil.PrettyPath(pr.FilePath), pr.OldContent). + After(fileutil.PrettyPath(pr.FilePath), pr.NewContent). + Width(p.contentViewPort.Width()). + Split() + diff := formatter.String() contentHeight := min(p.height-9, lipgloss.Height(diff)) p.contentViewPort.SetHeight(contentHeight) p.contentViewPort.SetContent(diff)