diff options
| author | adamdottv <[email protected]> | 2025-04-28 08:46:09 -0500 |
|---|---|---|
| committer | adamdottv <[email protected]> | 2025-04-30 07:46:34 -0500 |
| commit | 61b605e724eb4cc50ab831534fcdd18e031d68eb (patch) | |
| tree | b15b074a8fed1931e179a8d3df08d05b753c7aa3 /internal/diff/diff.go | |
| parent | 61d9dc95111d2645a49816f6d9d6cc1014be1a22 (diff) | |
| download | opencode-61b605e724eb4cc50ab831534fcdd18e031d68eb.tar.gz opencode-61b605e724eb4cc50ab831534fcdd18e031d68eb.zip | |
feat: themes
Diffstat (limited to 'internal/diff/diff.go')
| -rw-r--r-- | internal/diff/diff.go | 532 |
1 files changed, 224 insertions, 308 deletions
diff --git a/internal/diff/diff.go b/internal/diff/diff.go index a2edb7e74..8f5e669d3 100644 --- a/internal/diff/diff.go +++ b/internal/diff/diff.go @@ -16,6 +16,7 @@ import ( "github.com/charmbracelet/lipgloss" "github.com/charmbracelet/x/ansi" "github.com/opencode-ai/opencode/internal/config" + "github.com/opencode-ai/opencode/internal/tui/theme" "github.com/sergi/go-diff/diffmatchpatch" ) @@ -69,143 +70,6 @@ type linePair struct { } // ------------------------------------------------------------------------- -// Style Configuration -// ------------------------------------------------------------------------- - -// StyleConfig defines styling for diff rendering -type StyleConfig struct { - ShowHeader bool - ShowHunkHeader bool - FileNameFg lipgloss.Color - // Background colors - RemovedLineBg lipgloss.Color - AddedLineBg lipgloss.Color - ContextLineBg lipgloss.Color - HunkLineBg lipgloss.Color - RemovedLineNumberBg lipgloss.Color - AddedLineNamerBg lipgloss.Color - - // Foreground colors - HunkLineFg lipgloss.Color - RemovedFg lipgloss.Color - AddedFg lipgloss.Color - LineNumberFg lipgloss.Color - RemovedHighlightFg lipgloss.Color - AddedHighlightFg lipgloss.Color - - // Highlight settings - HighlightStyle string - RemovedHighlightBg lipgloss.Color - AddedHighlightBg lipgloss.Color -} - -// StyleOption is a function that modifies a StyleConfig -type StyleOption func(*StyleConfig) - -// NewStyleConfig creates a StyleConfig with default values -func NewStyleConfig(opts ...StyleOption) StyleConfig { - // Default color scheme - config := StyleConfig{ - ShowHeader: true, - ShowHunkHeader: true, - FileNameFg: lipgloss.Color("#a0a0a0"), - RemovedLineBg: lipgloss.Color("#3A3030"), - AddedLineBg: lipgloss.Color("#303A30"), - ContextLineBg: lipgloss.Color("#212121"), - HunkLineBg: lipgloss.Color("#212121"), - HunkLineFg: lipgloss.Color("#a0a0a0"), - RemovedFg: lipgloss.Color("#7C4444"), - AddedFg: lipgloss.Color("#478247"), - LineNumberFg: lipgloss.Color("#888888"), - HighlightStyle: "dracula", - RemovedHighlightBg: lipgloss.Color("#612726"), - AddedHighlightBg: lipgloss.Color("#256125"), - RemovedLineNumberBg: lipgloss.Color("#332929"), - AddedLineNamerBg: lipgloss.Color("#293229"), - RemovedHighlightFg: lipgloss.Color("#FADADD"), - AddedHighlightFg: lipgloss.Color("#DAFADA"), - } - - // Apply all provided options - for _, opt := range opts { - opt(&config) - } - - return config -} - -// Style option functions -func WithFileNameFg(color lipgloss.Color) StyleOption { - return func(s *StyleConfig) { s.FileNameFg = color } -} - -func WithRemovedLineBg(color lipgloss.Color) StyleOption { - return func(s *StyleConfig) { s.RemovedLineBg = color } -} - -func WithAddedLineBg(color lipgloss.Color) StyleOption { - return func(s *StyleConfig) { s.AddedLineBg = color } -} - -func WithContextLineBg(color lipgloss.Color) StyleOption { - return func(s *StyleConfig) { s.ContextLineBg = color } -} - -func WithRemovedFg(color lipgloss.Color) StyleOption { - return func(s *StyleConfig) { s.RemovedFg = color } -} - -func WithAddedFg(color lipgloss.Color) StyleOption { - return func(s *StyleConfig) { s.AddedFg = color } -} - -func WithLineNumberFg(color lipgloss.Color) StyleOption { - return func(s *StyleConfig) { s.LineNumberFg = color } -} - -func WithHighlightStyle(style string) StyleOption { - return func(s *StyleConfig) { s.HighlightStyle = style } -} - -func WithRemovedHighlightColors(bg, fg lipgloss.Color) StyleOption { - return func(s *StyleConfig) { - s.RemovedHighlightBg = bg - s.RemovedHighlightFg = fg - } -} - -func WithAddedHighlightColors(bg, fg lipgloss.Color) StyleOption { - return func(s *StyleConfig) { - s.AddedHighlightBg = bg - s.AddedHighlightFg = fg - } -} - -func WithRemovedLineNumberBg(color lipgloss.Color) StyleOption { - return func(s *StyleConfig) { s.RemovedLineNumberBg = color } -} - -func WithAddedLineNumberBg(color lipgloss.Color) StyleOption { - return func(s *StyleConfig) { s.AddedLineNamerBg = color } -} - -func WithHunkLineBg(color lipgloss.Color) StyleOption { - return func(s *StyleConfig) { s.HunkLineBg = color } -} - -func WithHunkLineFg(color lipgloss.Color) StyleOption { - return func(s *StyleConfig) { s.HunkLineFg = color } -} - -func WithShowHeader(show bool) StyleOption { - return func(s *StyleConfig) { s.ShowHeader = show } -} - -func WithShowHunkHeader(show bool) StyleOption { - return func(s *StyleConfig) { s.ShowHunkHeader = show } -} - -// ------------------------------------------------------------------------- // Parse Configuration // ------------------------------------------------------------------------- @@ -233,7 +97,6 @@ func WithContextSize(size int) ParseOption { // SideBySideConfig configures the rendering of side-by-side diffs type SideBySideConfig struct { TotalWidth int - Style StyleConfig } // SideBySideOption modifies a SideBySideConfig @@ -243,7 +106,6 @@ type SideBySideOption func(*SideBySideConfig) func NewSideBySideConfig(opts ...SideBySideOption) SideBySideConfig { config := SideBySideConfig{ TotalWidth: 160, // Default width for side-by-side view - Style: NewStyleConfig(), } for _, opt := range opts { @@ -262,20 +124,6 @@ func WithTotalWidth(width int) SideBySideOption { } } -// WithStyle sets the styling configuration -func WithStyle(style StyleConfig) SideBySideOption { - return func(s *SideBySideConfig) { - s.Style = style - } -} - -// WithStyleOptions applies the specified style options -func WithStyleOptions(opts ...StyleOption) SideBySideOption { - return func(s *SideBySideConfig) { - s.Style = NewStyleConfig(opts...) - } -} - // ------------------------------------------------------------------------- // Diff Parsing // ------------------------------------------------------------------------- @@ -382,7 +230,7 @@ func ParseUnifiedDiff(diff string) (DiffResult, error) { } // HighlightIntralineChanges updates lines in a hunk to show character-level differences -func HighlightIntralineChanges(h *Hunk, style StyleConfig) { +func HighlightIntralineChanges(h *Hunk) { var updated []DiffLine dmp := diffmatchpatch.New() @@ -476,6 +324,8 @@ func pairLines(lines []DiffLine) []linePair { // SyntaxHighlight applies syntax highlighting to text based on file extension func SyntaxHighlight(w io.Writer, source, fileName, formatter string, bg lipgloss.TerminalColor) error { + t := theme.CurrentTheme() + // Determine the language lexer to use l := lexers.Match(fileName) if l == nil { @@ -491,93 +341,175 @@ func SyntaxHighlight(w io.Writer, source, fileName, formatter string, bg lipglos if f == nil { f = formatters.Fallback } - theme := ` - <style name="vscode-dark-plus"> + + // Dynamic theme based on current theme values + syntaxThemeXml := fmt.Sprintf(` + <style name="opencode-theme"> <!-- Base colors --> - <entry type="Background" style="bg:#1E1E1E"/> - <entry type="Text" style="#D4D4D4"/> - <entry type="Other" style="#D4D4D4"/> - <entry type="Error" style="#F44747"/> - <!-- Keywords - using the Control flow / Special keywords color --> - <entry type="Keyword" style="#C586C0"/> - <entry type="KeywordConstant" style="#4FC1FF"/> - <entry type="KeywordDeclaration" style="#C586C0"/> - <entry type="KeywordNamespace" style="#C586C0"/> - <entry type="KeywordPseudo" style="#C586C0"/> - <entry type="KeywordReserved" style="#C586C0"/> - <entry type="KeywordType" style="#4EC9B0"/> + <entry type="Background" style="bg:%s"/> + <entry type="Text" style="%s"/> + <entry type="Other" style="%s"/> + <entry type="Error" style="%s"/> + <!-- Keywords --> + <entry type="Keyword" style="%s"/> + <entry type="KeywordConstant" style="%s"/> + <entry type="KeywordDeclaration" style="%s"/> + <entry type="KeywordNamespace" style="%s"/> + <entry type="KeywordPseudo" style="%s"/> + <entry type="KeywordReserved" style="%s"/> + <entry type="KeywordType" style="%s"/> <!-- Names --> - <entry type="Name" style="#D4D4D4"/> - <entry type="NameAttribute" style="#9CDCFE"/> - <entry type="NameBuiltin" style="#4EC9B0"/> - <entry type="NameBuiltinPseudo" style="#9CDCFE"/> - <entry type="NameClass" style="#4EC9B0"/> - <entry type="NameConstant" style="#4FC1FF"/> - <entry type="NameDecorator" style="#DCDCAA"/> - <entry type="NameEntity" style="#9CDCFE"/> - <entry type="NameException" style="#4EC9B0"/> - <entry type="NameFunction" style="#DCDCAA"/> - <entry type="NameLabel" style="#C8C8C8"/> - <entry type="NameNamespace" style="#4EC9B0"/> - <entry type="NameOther" style="#9CDCFE"/> - <entry type="NameTag" style="#569CD6"/> - <entry type="NameVariable" style="#9CDCFE"/> - <entry type="NameVariableClass" style="#9CDCFE"/> - <entry type="NameVariableGlobal" style="#9CDCFE"/> - <entry type="NameVariableInstance" style="#9CDCFE"/> + <entry type="Name" style="%s"/> + <entry type="NameAttribute" style="%s"/> + <entry type="NameBuiltin" style="%s"/> + <entry type="NameBuiltinPseudo" style="%s"/> + <entry type="NameClass" style="%s"/> + <entry type="NameConstant" style="%s"/> + <entry type="NameDecorator" style="%s"/> + <entry type="NameEntity" style="%s"/> + <entry type="NameException" style="%s"/> + <entry type="NameFunction" style="%s"/> + <entry type="NameLabel" style="%s"/> + <entry type="NameNamespace" style="%s"/> + <entry type="NameOther" style="%s"/> + <entry type="NameTag" style="%s"/> + <entry type="NameVariable" style="%s"/> + <entry type="NameVariableClass" style="%s"/> + <entry type="NameVariableGlobal" style="%s"/> + <entry type="NameVariableInstance" style="%s"/> <!-- Literals --> - <entry type="Literal" style="#CE9178"/> - <entry type="LiteralDate" style="#CE9178"/> - <entry type="LiteralString" style="#CE9178"/> - <entry type="LiteralStringBacktick" style="#CE9178"/> - <entry type="LiteralStringChar" style="#CE9178"/> - <entry type="LiteralStringDoc" style="#CE9178"/> - <entry type="LiteralStringDouble" style="#CE9178"/> - <entry type="LiteralStringEscape" style="#d7ba7d"/> - <entry type="LiteralStringHeredoc" style="#CE9178"/> - <entry type="LiteralStringInterpol" style="#CE9178"/> - <entry type="LiteralStringOther" style="#CE9178"/> - <entry type="LiteralStringRegex" style="#d16969"/> - <entry type="LiteralStringSingle" style="#CE9178"/> - <entry type="LiteralStringSymbol" style="#CE9178"/> - <!-- Numbers - using the numberLiteral color --> - <entry type="LiteralNumber" style="#b5cea8"/> - <entry type="LiteralNumberBin" style="#b5cea8"/> - <entry type="LiteralNumberFloat" style="#b5cea8"/> - <entry type="LiteralNumberHex" style="#b5cea8"/> - <entry type="LiteralNumberInteger" style="#b5cea8"/> - <entry type="LiteralNumberIntegerLong" style="#b5cea8"/> - <entry type="LiteralNumberOct" style="#b5cea8"/> + <entry type="Literal" style="%s"/> + <entry type="LiteralDate" style="%s"/> + <entry type="LiteralString" style="%s"/> + <entry type="LiteralStringBacktick" style="%s"/> + <entry type="LiteralStringChar" style="%s"/> + <entry type="LiteralStringDoc" style="%s"/> + <entry type="LiteralStringDouble" style="%s"/> + <entry type="LiteralStringEscape" style="%s"/> + <entry type="LiteralStringHeredoc" style="%s"/> + <entry type="LiteralStringInterpol" style="%s"/> + <entry type="LiteralStringOther" style="%s"/> + <entry type="LiteralStringRegex" style="%s"/> + <entry type="LiteralStringSingle" style="%s"/> + <entry type="LiteralStringSymbol" style="%s"/> + <!-- Numbers --> + <entry type="LiteralNumber" style="%s"/> + <entry type="LiteralNumberBin" style="%s"/> + <entry type="LiteralNumberFloat" style="%s"/> + <entry type="LiteralNumberHex" style="%s"/> + <entry type="LiteralNumberInteger" style="%s"/> + <entry type="LiteralNumberIntegerLong" style="%s"/> + <entry type="LiteralNumberOct" style="%s"/> <!-- Operators --> - <entry type="Operator" style="#D4D4D4"/> - <entry type="OperatorWord" style="#C586C0"/> - <entry type="Punctuation" style="#D4D4D4"/> - <!-- Comments - standard VSCode Dark+ comment color --> - <entry type="Comment" style="#6A9955"/> - <entry type="CommentHashbang" style="#6A9955"/> - <entry type="CommentMultiline" style="#6A9955"/> - <entry type="CommentSingle" style="#6A9955"/> - <entry type="CommentSpecial" style="#6A9955"/> - <entry type="CommentPreproc" style="#C586C0"/> + <entry type="Operator" style="%s"/> + <entry type="OperatorWord" style="%s"/> + <entry type="Punctuation" style="%s"/> + <!-- Comments --> + <entry type="Comment" style="%s"/> + <entry type="CommentHashbang" style="%s"/> + <entry type="CommentMultiline" style="%s"/> + <entry type="CommentSingle" style="%s"/> + <entry type="CommentSpecial" style="%s"/> + <entry type="CommentPreproc" style="%s"/> <!-- Generic styles --> - <entry type="Generic" style="#D4D4D4"/> - <entry type="GenericDeleted" style="#F44747"/> - <entry type="GenericEmph" style="italic #D4D4D4"/> - <entry type="GenericError" style="#F44747"/> - <entry type="GenericHeading" style="bold #D4D4D4"/> - <entry type="GenericInserted" style="#b5cea8"/> - <entry type="GenericOutput" style="#808080"/> - <entry type="GenericPrompt" style="#D4D4D4"/> - <entry type="GenericStrong" style="bold #D4D4D4"/> - <entry type="GenericSubheading" style="bold #D4D4D4"/> - <entry type="GenericTraceback" style="#F44747"/> + <entry type="Generic" style="%s"/> + <entry type="GenericDeleted" style="%s"/> + <entry type="GenericEmph" style="italic %s"/> + <entry type="GenericError" style="%s"/> + <entry type="GenericHeading" style="bold %s"/> + <entry type="GenericInserted" style="%s"/> + <entry type="GenericOutput" style="%s"/> + <entry type="GenericPrompt" style="%s"/> + <entry type="GenericStrong" style="bold %s"/> + <entry type="GenericSubheading" style="bold %s"/> + <entry type="GenericTraceback" style="%s"/> <entry type="GenericUnderline" style="underline"/> - <entry type="TextWhitespace" style="#D4D4D4"/> + <entry type="TextWhitespace" style="%s"/> </style> -` +`, + getColor(t.Background()), // Background + getColor(t.Text()), // Text + getColor(t.Text()), // Other + getColor(t.Error()), // Error + + getColor(t.SyntaxKeyword()), // Keyword + getColor(t.SyntaxKeyword()), // KeywordConstant + getColor(t.SyntaxKeyword()), // KeywordDeclaration + getColor(t.SyntaxKeyword()), // KeywordNamespace + getColor(t.SyntaxKeyword()), // KeywordPseudo + getColor(t.SyntaxKeyword()), // KeywordReserved + getColor(t.SyntaxType()), // KeywordType + + getColor(t.Text()), // Name + getColor(t.SyntaxVariable()), // NameAttribute + getColor(t.SyntaxType()), // NameBuiltin + getColor(t.SyntaxVariable()), // NameBuiltinPseudo + getColor(t.SyntaxType()), // NameClass + getColor(t.SyntaxVariable()), // NameConstant + getColor(t.SyntaxFunction()), // NameDecorator + getColor(t.SyntaxVariable()), // NameEntity + getColor(t.SyntaxType()), // NameException + getColor(t.SyntaxFunction()), // NameFunction + getColor(t.Text()), // NameLabel + getColor(t.SyntaxType()), // NameNamespace + getColor(t.SyntaxVariable()), // NameOther + getColor(t.SyntaxKeyword()), // NameTag + getColor(t.SyntaxVariable()), // NameVariable + getColor(t.SyntaxVariable()), // NameVariableClass + getColor(t.SyntaxVariable()), // NameVariableGlobal + getColor(t.SyntaxVariable()), // NameVariableInstance + + getColor(t.SyntaxString()), // Literal + getColor(t.SyntaxString()), // LiteralDate + getColor(t.SyntaxString()), // LiteralString + getColor(t.SyntaxString()), // LiteralStringBacktick + getColor(t.SyntaxString()), // LiteralStringChar + getColor(t.SyntaxString()), // LiteralStringDoc + getColor(t.SyntaxString()), // LiteralStringDouble + getColor(t.SyntaxString()), // LiteralStringEscape + getColor(t.SyntaxString()), // LiteralStringHeredoc + getColor(t.SyntaxString()), // LiteralStringInterpol + getColor(t.SyntaxString()), // LiteralStringOther + getColor(t.SyntaxString()), // LiteralStringRegex + getColor(t.SyntaxString()), // LiteralStringSingle + getColor(t.SyntaxString()), // LiteralStringSymbol + + getColor(t.SyntaxNumber()), // LiteralNumber + getColor(t.SyntaxNumber()), // LiteralNumberBin + getColor(t.SyntaxNumber()), // LiteralNumberFloat + getColor(t.SyntaxNumber()), // LiteralNumberHex + getColor(t.SyntaxNumber()), // LiteralNumberInteger + getColor(t.SyntaxNumber()), // LiteralNumberIntegerLong + getColor(t.SyntaxNumber()), // LiteralNumberOct + + getColor(t.SyntaxOperator()), // Operator + getColor(t.SyntaxKeyword()), // OperatorWord + getColor(t.SyntaxPunctuation()), // Punctuation + + getColor(t.SyntaxComment()), // Comment + getColor(t.SyntaxComment()), // CommentHashbang + getColor(t.SyntaxComment()), // CommentMultiline + getColor(t.SyntaxComment()), // CommentSingle + getColor(t.SyntaxComment()), // CommentSpecial + getColor(t.SyntaxKeyword()), // CommentPreproc + + getColor(t.Text()), // Generic + getColor(t.Error()), // GenericDeleted + getColor(t.Text()), // GenericEmph + getColor(t.Error()), // GenericError + getColor(t.Text()), // GenericHeading + getColor(t.Success()), // GenericInserted + getColor(t.TextMuted()), // GenericOutput + getColor(t.Text()), // GenericPrompt + getColor(t.Text()), // GenericStrong + getColor(t.Text()), // GenericSubheading + getColor(t.Error()), // GenericTraceback + getColor(t.Text()), // TextWhitespace + ) - r := strings.NewReader(theme) + r := strings.NewReader(syntaxThemeXml) style := chroma.MustNewXMLStyle(r) + // Modify the style to use the provided background s, err := style.Builder().Transform( func(t chroma.StyleEntry) chroma.StyleEntry { @@ -599,6 +531,14 @@ func SyntaxHighlight(w io.Writer, source, fileName, formatter string, bg lipglos return f.Format(w, s, it) } +// getColor returns the appropriate hex color string based on terminal background +func getColor(adaptiveColor lipgloss.AdaptiveColor) string { + if lipgloss.HasDarkBackground() { + return adaptiveColor.Dark + } + return adaptiveColor.Light +} + // highlightLine applies syntax highlighting to a single line func highlightLine(fileName string, line string, bg lipgloss.TerminalColor) string { var buf bytes.Buffer @@ -610,11 +550,11 @@ func highlightLine(fileName string, line string, bg lipgloss.TerminalColor) stri } // createStyles generates the lipgloss styles needed for rendering diffs -func createStyles(config StyleConfig) (removedLineStyle, addedLineStyle, contextLineStyle, lineNumberStyle lipgloss.Style) { - removedLineStyle = lipgloss.NewStyle().Background(config.RemovedLineBg) - addedLineStyle = lipgloss.NewStyle().Background(config.AddedLineBg) - contextLineStyle = lipgloss.NewStyle().Background(config.ContextLineBg) - lineNumberStyle = lipgloss.NewStyle().Foreground(config.LineNumberFg) +func createStyles(t theme.Theme) (removedLineStyle, addedLineStyle, contextLineStyle, lineNumberStyle lipgloss.Style) { + removedLineStyle = lipgloss.NewStyle().Background(t.DiffRemovedBg()) + addedLineStyle = lipgloss.NewStyle().Background(t.DiffAddedBg()) + contextLineStyle = lipgloss.NewStyle().Background(t.DiffContextBg()) + lineNumberStyle = lipgloss.NewStyle().Foreground(t.DiffLineNumber()) return } @@ -623,9 +563,20 @@ func createStyles(config StyleConfig) (removedLineStyle, addedLineStyle, context // Rendering Functions // ------------------------------------------------------------------------- +func lipglossToHex(color lipgloss.Color) string { + r, g, b, a := color.RGBA() + + // Scale uint32 values (0-65535) to uint8 (0-255). + r8 := uint8(r >> 8) + g8 := uint8(g >> 8) + b8 := uint8(b >> 8) + a8 := uint8(a >> 8) + + return fmt.Sprintf("#%02x%02x%02x%02x", r8, g8, b8, a8) +} + // applyHighlighting applies intra-line highlighting to a piece of text -func applyHighlighting(content string, segments []Segment, segmentType LineType, highlightBg lipgloss.Color, -) string { +func applyHighlighting(content string, segments []Segment, segmentType LineType, highlightBg lipgloss.AdaptiveColor) string { // Find all ANSI sequences in the content ansiRegex := regexp.MustCompile(`\x1b(?:[@-Z\\-_]|\[[0-9?]*(?:;[0-9?]*)*[@-~])`) ansiMatches := ansiRegex.FindAllStringIndex(content, -1) @@ -663,6 +614,10 @@ func applyHighlighting(content string, segments []Segment, segmentType LineType, inSelection := false currentPos := 0 + // Get the appropriate color based on terminal background + bgColor := lipgloss.Color(getColor(highlightBg)) + fgColor := lipgloss.Color(getColor(theme.CurrentTheme().Background())) + for i := 0; i < len(content); { // Check if we're at an ANSI sequence isAnsi := false @@ -697,12 +652,16 @@ func applyHighlighting(content string, segments []Segment, segmentType LineType, // Get the current styling currentStyle := ansiSequences[currentPos] - // Apply background highlight + // Apply foreground and background highlight + sb.WriteString("\x1b[38;2;") + r, g, b, _ := fgColor.RGBA() + sb.WriteString(fmt.Sprintf("%d;%d;%dm", r>>8, g>>8, b>>8)) sb.WriteString("\x1b[48;2;") - r, g, b, _ := highlightBg.RGBA() + r, g, b, _ = bgColor.RGBA() sb.WriteString(fmt.Sprintf("%d;%d;%dm", r>>8, g>>8, b>>8)) sb.WriteString(char) - sb.WriteString("\x1b[49m") // Reset only background + // Reset foreground and background + sb.WriteString("\x1b[39m") // Reapply the original ANSI sequence sb.WriteString(currentStyle) @@ -719,22 +678,24 @@ func applyHighlighting(content string, segments []Segment, segmentType LineType, } // renderLeftColumn formats the left side of a side-by-side diff -func renderLeftColumn(fileName string, dl *DiffLine, colWidth int, styles StyleConfig) string { +func renderLeftColumn(fileName string, dl *DiffLine, colWidth int) string { + t := theme.CurrentTheme() + if dl == nil { - contextLineStyle := lipgloss.NewStyle().Background(styles.ContextLineBg) + contextLineStyle := lipgloss.NewStyle().Background(t.DiffContextBg()) return contextLineStyle.Width(colWidth).Render("") } - removedLineStyle, _, contextLineStyle, lineNumberStyle := createStyles(styles) + removedLineStyle, _, contextLineStyle, lineNumberStyle := createStyles(t) // Determine line style based on line type var marker string var bgStyle lipgloss.Style switch dl.Kind { case LineRemoved: - marker = removedLineStyle.Foreground(styles.RemovedFg).Render("-") + marker = removedLineStyle.Foreground(t.DiffRemoved()).Render("-") bgStyle = removedLineStyle - lineNumberStyle = lineNumberStyle.Foreground(styles.RemovedFg).Background(styles.RemovedLineNumberBg) + lineNumberStyle = lineNumberStyle.Foreground(t.DiffRemoved()).Background(t.DiffRemovedLineNumberBg()) case LineAdded: marker = "?" bgStyle = contextLineStyle @@ -757,7 +718,7 @@ func renderLeftColumn(fileName string, dl *DiffLine, colWidth int, styles StyleC // Apply intra-line highlighting for removed lines if dl.Kind == LineRemoved && len(dl.Segments) > 0 { - content = applyHighlighting(content, dl.Segments, LineRemoved, styles.RemovedHighlightBg) + content = applyHighlighting(content, dl.Segments, LineRemoved, t.DiffHighlightRemoved()) } // Add a padding space for removed lines @@ -771,28 +732,30 @@ func renderLeftColumn(fileName string, dl *DiffLine, colWidth int, styles StyleC ansi.Truncate( lineText, colWidth, - lipgloss.NewStyle().Background(styles.HunkLineBg).Foreground(styles.HunkLineFg).Render("..."), + lipgloss.NewStyle().Background(bgStyle.GetBackground()).Foreground(t.TextMuted()).Render("..."), ), ) } // renderRightColumn formats the right side of a side-by-side diff -func renderRightColumn(fileName string, dl *DiffLine, colWidth int, styles StyleConfig) string { +func renderRightColumn(fileName string, dl *DiffLine, colWidth int) string { + t := theme.CurrentTheme() + if dl == nil { - contextLineStyle := lipgloss.NewStyle().Background(styles.ContextLineBg) + contextLineStyle := lipgloss.NewStyle().Background(t.DiffContextBg()) return contextLineStyle.Width(colWidth).Render("") } - _, addedLineStyle, contextLineStyle, lineNumberStyle := createStyles(styles) + _, addedLineStyle, contextLineStyle, lineNumberStyle := createStyles(t) // Determine line style based on line type var marker string var bgStyle lipgloss.Style switch dl.Kind { case LineAdded: - marker = addedLineStyle.Foreground(styles.AddedFg).Render("+") + marker = addedLineStyle.Foreground(t.DiffAdded()).Render("+") bgStyle = addedLineStyle - lineNumberStyle = lineNumberStyle.Foreground(styles.AddedFg).Background(styles.AddedLineNamerBg) + lineNumberStyle = lineNumberStyle.Foreground(t.DiffAdded()).Background(t.DiffAddedLineNumberBg()) case LineRemoved: marker = "?" bgStyle = contextLineStyle @@ -815,7 +778,7 @@ func renderRightColumn(fileName string, dl *DiffLine, colWidth int, styles Style // Apply intra-line highlighting for added lines if dl.Kind == LineAdded && len(dl.Segments) > 0 { - content = applyHighlighting(content, dl.Segments, LineAdded, styles.AddedHighlightBg) + content = applyHighlighting(content, dl.Segments, LineAdded, t.DiffHighlightAdded()) } // Add a padding space for added lines @@ -829,7 +792,7 @@ func renderRightColumn(fileName string, dl *DiffLine, colWidth int, styles Style ansi.Truncate( lineText, colWidth, - lipgloss.NewStyle().Background(styles.HunkLineBg).Foreground(styles.HunkLineFg).Render("..."), + lipgloss.NewStyle().Background(bgStyle.GetBackground()).Foreground(t.TextMuted()).Render("..."), ), ) } @@ -848,7 +811,7 @@ func RenderSideBySideHunk(fileName string, h Hunk, opts ...SideBySideOption) str copy(hunkCopy.Lines, h.Lines) // Highlight changes within lines - HighlightIntralineChanges(&hunkCopy, config.Style) + HighlightIntralineChanges(&hunkCopy) // Pair lines for side-by-side display pairs := pairLines(hunkCopy.Lines) @@ -860,8 +823,8 @@ func RenderSideBySideHunk(fileName string, h Hunk, opts ...SideBySideOption) str rightWidth := config.TotalWidth - colWidth var sb strings.Builder for _, p := range pairs { - leftStr := renderLeftColumn(fileName, p.left, leftWidth, config.Style) - rightStr := renderRightColumn(fileName, p.right, rightWidth, config.Style) + leftStr := renderLeftColumn(fileName, p.left, leftWidth) + rightStr := renderRightColumn(fileName, p.right, rightWidth) sb.WriteString(leftStr + rightStr + "\n") } @@ -876,54 +839,7 @@ func FormatDiff(diffText string, opts ...SideBySideOption) (string, error) { } var sb strings.Builder - config := NewSideBySideConfig(opts...) - - if config.Style.ShowHeader { - removeIcon := lipgloss.NewStyle(). - Background(config.Style.RemovedLineBg). - Foreground(config.Style.RemovedFg). - Render("⏹") - addIcon := lipgloss.NewStyle(). - Background(config.Style.AddedLineBg). - Foreground(config.Style.AddedFg). - Render("⏹") - - fileName := lipgloss.NewStyle(). - Background(config.Style.ContextLineBg). - Foreground(config.Style.FileNameFg). - Render(" " + diffResult.OldFile) - sb.WriteString( - lipgloss.NewStyle(). - Background(config.Style.ContextLineBg). - Padding(0, 1, 0, 1). - Foreground(config.Style.FileNameFg). - BorderStyle(lipgloss.NormalBorder()). - BorderTop(true). - BorderBottom(true). - BorderForeground(config.Style.FileNameFg). - BorderBackground(config.Style.ContextLineBg). - Width(config.TotalWidth). - Render( - lipgloss.JoinHorizontal(lipgloss.Top, - removeIcon, - addIcon, - fileName, - ), - ) + "\n", - ) - } - for _, h := range diffResult.Hunks { - // Render hunk header - if config.Style.ShowHunkHeader { - sb.WriteString( - lipgloss.NewStyle(). - Background(config.Style.HunkLineBg). - Foreground(config.Style.HunkLineFg). - Width(config.TotalWidth). - Render(h.Header) + "\n", - ) - } sb.WriteString(RenderSideBySideHunk(diffResult.OldFile, h, opts...)) } @@ -944,8 +860,8 @@ func GenerateDiff(beforeContent, afterContent, fileName string) (string, int, in removals = 0 ) - lines := strings.Split(unified, "\n") - for _, line := range lines { + lines := strings.SplitSeq(unified, "\n") + for line := range lines { if strings.HasPrefix(line, "+") && !strings.HasPrefix(line, "+++") { additions++ } else if strings.HasPrefix(line, "-") && !strings.HasPrefix(line, "---") { |
