Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion internal/ui/model/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,14 @@ func (m *UI) loadSessionFiles(sessionID string) ([]SessionFile, error) {
}
}

_, additions, deletions := diff.GenerateDiff(first.Content, last.Content, first.Path)
// Normalize line endings so that a CRLF/LF mismatch between
// historical versions does not inflate the diff stats. Files
// round-tripped through the write tool may end up with different
// line endings than the original (the LLM typically emits LF),
// which previously caused every line to be reported as changed.
firstContent, _ := fsext.ToUnixLineEndings(first.Content)
lastContent, _ := fsext.ToUnixLineEndings(last.Content)
_, additions, deletions := diff.GenerateDiff(firstContent, lastContent, first.Path)

sessionFiles = append(sessionFiles, SessionFile{
FirstVersion: first,
Expand Down
44 changes: 44 additions & 0 deletions internal/ui/model/session_test.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package model

import (
"fmt"
"strings"
"testing"

"charm.land/lipgloss/v2"
"github.com/charmbracelet/crush/internal/diff"
"github.com/charmbracelet/crush/internal/fsext"
"github.com/charmbracelet/crush/internal/history"
"github.com/charmbracelet/crush/internal/ui/styles"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -111,6 +114,47 @@ func TestFileList(t *testing.T) {
})
}

func TestLoadSessionFilesNormalizesLineEndings(t *testing.T) {
t.Parallel()

makeContent := func(sep string) string {
var b strings.Builder
for i := 0; i < 1000; i++ {
b.WriteString(fmt.Sprintf("line %d", i))
b.WriteString(sep)
}
return b.String()
}

t.Run("CRLF vs LF reports the real delta, not the line ending swap", func(t *testing.T) {
t.Parallel()

first := history.File{Path: "main.go", Content: makeContent("\r\n"), Version: 0}
last := history.File{Path: "main.go", Content: makeContent("\n") + "added\n", Version: 1}

firstContent, _ := fsext.ToUnixLineEndings(first.Content)
lastContent, _ := fsext.ToUnixLineEndings(last.Content)
_, additions, removals := diff.GenerateDiff(firstContent, lastContent, first.Path)

require.Equal(t, 1, additions, "expected one addition for the appended line, got %d", additions)
require.Equal(t, 0, removals, "expected zero removals, got %d", removals)
})

t.Run("identical content with mismatched line endings reports no changes", func(t *testing.T) {
t.Parallel()

first := history.File{Path: "main.go", Content: makeContent("\r\n"), Version: 0}
last := history.File{Path: "main.go", Content: makeContent("\n"), Version: 1}

firstContent, _ := fsext.ToUnixLineEndings(first.Content)
lastContent, _ := fsext.ToUnixLineEndings(last.Content)
_, additions, removals := diff.GenerateDiff(firstContent, lastContent, first.Path)

require.Equal(t, 0, additions)
require.Equal(t, 0, removals)
})
}

func minimalFileStyles() *styles.Styles {
st := styles.CharmtonePantera()
st.Files.Path = lipgloss.NewStyle()
Expand Down
Loading