Something went wrong. Try again.
changelog generator & diff tool stormlightlabs.github.io/git-storm
changelog changeset markdown golang git
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190package diff
import "io"
// DiffViewKind enumerates the supported diff views.type DiffViewKind int
const ( ViewUnified DiffViewKind = iota ViewSplit ViewHunk ViewInline ViewRich ViewSource)
// String returns a human-readable name for the view kind.func (k DiffViewKind) String() string { switch k { case ViewUnified: return "Unified" case ViewSplit: return "Split" case ViewHunk: return "Hunk" case ViewInline: return "Inline" case ViewRich: return "Rich" case ViewSource: return "Source" default: return "Unknown" }}
// DiffResult holds the output of a diff tool.type DiffResult struct { // Content is the rendered diff output (text, ANSI, HTML, etc.) Content string
// View is the kind of view used to render the diff. View DiffViewKind
// Metadata like file paths, commit hashes, timestamps (optional). // You may extend as needed. OldPath string NewPath string FromHash string ToHash string}
// DiffTool is the interface for generating diffs between two versions.type DiffTool interface { // Diff takes two blobs (old and new versions) and returns a DiffResult. // The reader parameters may be full file contents or other abstraction. // The viewKind parameter selects which view implementation (Unified/Split/...). Diff(oldContent io.Reader, newContent io.Reader, viewKind DiffViewKind) (DiffResult, error)}
// UnifiedDiff implements unified view (single linear view with additions & deletions).//// TODO: Support pluggable diff algorithms beyond Myers.type UnifiedDiff struct { // TerminalWidth is the total available width for rendering TerminalWidth int // ShowLineNumbers controls whether line numbers are displayed ShowLineNumbers bool // Expanded controls whether to show all unchanged lines or compress them Expanded bool // EnableWordWrap enables word wrapping for long lines EnableWordWrap bool}
// Diff generates a unified diff view from two content readers.func (u *UnifiedDiff) Diff(oldContent io.Reader, newContent io.Reader, viewKind DiffViewKind) (DiffResult, error) { oldBytes, err := io.ReadAll(oldContent) if err != nil { return DiffResult{}, err } newBytes, err := io.ReadAll(newContent) if err != nil { return DiffResult{}, err }
oldLines := splitLines(string(oldBytes)) newLines := splitLines(string(newBytes))
myers := &Myers{} edits, err := myers.Compute(oldLines, newLines) if err != nil { return DiffResult{}, err }
formatter := &UnifiedFormatter{ TerminalWidth: u.TerminalWidth, ShowLineNumbers: u.ShowLineNumbers, Expanded: u.Expanded, EnableWordWrap: u.EnableWordWrap, }
content := formatter.Format(edits)
return DiffResult{ Content: content, View: ViewUnified, }, nil}
// SplitDiff implements side-by-side view (old on left, new on right).//// TODO: Support pluggable diff algorithms beyond Myers.type SplitDiff struct { // TerminalWidth is the total available width for rendering TerminalWidth int // ShowLineNumbers controls whether line numbers are displayed ShowLineNumbers bool // Expanded controls whether to show all unchanged lines or compress them Expanded bool // EnableWordWrap enables word wrapping for long lines EnableWordWrap bool}
// Diff generates a side-by-side diff view from two content readers.func (s *SplitDiff) Diff(oldContent io.Reader, newContent io.Reader, viewKind DiffViewKind) (DiffResult, error) { oldBytes, err := io.ReadAll(oldContent) if err != nil { return DiffResult{}, err } newBytes, err := io.ReadAll(newContent) if err != nil { return DiffResult{}, err }
oldLines := splitLines(string(oldBytes)) newLines := splitLines(string(newBytes))
myers := &Myers{} edits, err := myers.Compute(oldLines, newLines) if err != nil { return DiffResult{}, err }
formatter := &SideBySideFormatter{ TerminalWidth: s.TerminalWidth, ShowLineNumbers: s.ShowLineNumbers, Expanded: s.Expanded, EnableWordWrap: s.EnableWordWrap, }
content := formatter.Format(edits)
return DiffResult{ Content: content, View: ViewSplit, }, nil}
// splitLines splits a string into lines, preserving empty lines.func splitLines(s string) []string { if s == "" { return []string{} } lines := make([]string, 0) start := 0 for i := 0; i < len(s); i++ { if s[i] == '\n' { lines = append(lines, s[start:i]) start = i + 1 } }
if start < len(s) { lines = append(lines, s[start:]) } return lines}
// HunkDiff focuses on changed blocks, minimal context.type HunkDiff struct{}
// InlineDiff renders changes inline in full file flow.type InlineDiff struct{}
// RichDiff renders changes for formatted preview (for example, markdown or html previews).type RichDiff struct{}
// SourceDiff simply returns raw diff data or patch format without special formatting.type SourceDiff struct{}