mirror of
https://github.com/charmbracelet/crush.git
synced 2025-08-02 05:20:46 +03:00
chore: improve diff view performance
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package diffview
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"image/color"
|
||||
"strconv"
|
||||
@@ -59,6 +60,13 @@ type DiffView struct {
|
||||
extraColOnAfter bool // add extra column on after panel
|
||||
beforeNumDigits int
|
||||
afterNumDigits int
|
||||
|
||||
// Cache lexer to avoid expensive file pattern matching on every line
|
||||
cachedLexer chroma.Lexer
|
||||
|
||||
// Cache highlighted lines to avoid re-highlighting the same content
|
||||
// Key: hash of (content + background color), Value: highlighted string
|
||||
syntaxCache map[string]string
|
||||
}
|
||||
|
||||
// New creates a new DiffView with default settings.
|
||||
@@ -68,6 +76,7 @@ func New() *DiffView {
|
||||
contextLines: udiff.DefaultContextLines,
|
||||
lineNumbers: true,
|
||||
tabWidth: 8,
|
||||
syntaxCache: make(map[string]string),
|
||||
}
|
||||
dv.style = DefaultDarkStyle()
|
||||
return dv
|
||||
@@ -88,15 +97,26 @@ func (dv *DiffView) Split() *DiffView {
|
||||
// Before sets the "before" file for the DiffView.
|
||||
func (dv *DiffView) Before(path, content string) *DiffView {
|
||||
dv.before = file{path: path, content: content}
|
||||
// Clear caches when content changes
|
||||
dv.clearCaches()
|
||||
return dv
|
||||
}
|
||||
|
||||
// After sets the "after" file for the DiffView.
|
||||
func (dv *DiffView) After(path, content string) *DiffView {
|
||||
dv.after = file{path: path, content: content}
|
||||
// Clear caches when content changes
|
||||
dv.clearCaches()
|
||||
return dv
|
||||
}
|
||||
|
||||
// clearCaches clears all caches when content or major settings change.
|
||||
func (dv *DiffView) clearCaches() {
|
||||
dv.cachedLexer = nil
|
||||
dv.clearSyntaxCache()
|
||||
dv.isComputed = false
|
||||
}
|
||||
|
||||
// ContextLines sets the number of context lines for the DiffView.
|
||||
func (dv *DiffView) ContextLines(contextLines int) *DiffView {
|
||||
dv.contextLines = contextLines
|
||||
@@ -156,9 +176,21 @@ func (dv *DiffView) TabWidth(tabWidth int) *DiffView {
|
||||
// If nil, no syntax highlighting will be applied.
|
||||
func (dv *DiffView) ChromaStyle(style *chroma.Style) *DiffView {
|
||||
dv.chromaStyle = style
|
||||
// Clear syntax cache when style changes since highlighting will be different
|
||||
dv.clearSyntaxCache()
|
||||
return dv
|
||||
}
|
||||
|
||||
// clearSyntaxCache clears the syntax highlighting cache.
|
||||
func (dv *DiffView) clearSyntaxCache() {
|
||||
if dv.syntaxCache != nil {
|
||||
// Clear the map but keep it allocated
|
||||
for k := range dv.syntaxCache {
|
||||
delete(dv.syntaxCache, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// String returns the string representation of the DiffView.
|
||||
func (dv *DiffView) String() string {
|
||||
dv.replaceTabs()
|
||||
@@ -700,7 +732,15 @@ func (dv *DiffView) hightlightCode(source string, bgColor color.Color) string {
|
||||
return source
|
||||
}
|
||||
|
||||
l := dv.getChromaLexer(source)
|
||||
// Create cache key from content and background color
|
||||
cacheKey := dv.createSyntaxCacheKey(source, bgColor)
|
||||
|
||||
// Check if we already have this highlighted
|
||||
if cached, exists := dv.syntaxCache[cacheKey]; exists {
|
||||
return cached
|
||||
}
|
||||
|
||||
l := dv.getChromaLexer()
|
||||
f := dv.getChromaFormatter(bgColor)
|
||||
|
||||
it, err := l.Tokenise(nil, source)
|
||||
@@ -712,22 +752,47 @@ func (dv *DiffView) hightlightCode(source string, bgColor color.Color) string {
|
||||
if err := f.Format(&b, dv.chromaStyle, it); err != nil {
|
||||
return source
|
||||
}
|
||||
return b.String()
|
||||
|
||||
result := b.String()
|
||||
|
||||
// Cache the result for future use
|
||||
dv.syntaxCache[cacheKey] = result
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (dv *DiffView) getChromaLexer(source string) chroma.Lexer {
|
||||
// createSyntaxCacheKey creates a cache key from source content and background color.
|
||||
// We use a simple hash to keep memory usage reasonable.
|
||||
func (dv *DiffView) createSyntaxCacheKey(source string, bgColor color.Color) string {
|
||||
// Convert color to string representation
|
||||
r, g, b, a := bgColor.RGBA()
|
||||
colorStr := fmt.Sprintf("%d,%d,%d,%d", r, g, b, a)
|
||||
|
||||
// Create a hash of the content + color to use as cache key
|
||||
h := sha256.New()
|
||||
h.Write([]byte(source))
|
||||
h.Write([]byte(colorStr))
|
||||
return fmt.Sprintf("%x", h.Sum(nil))
|
||||
}
|
||||
|
||||
func (dv *DiffView) getChromaLexer() chroma.Lexer {
|
||||
if dv.cachedLexer != nil {
|
||||
return dv.cachedLexer
|
||||
}
|
||||
|
||||
l := lexers.Match(dv.before.path)
|
||||
if l == nil {
|
||||
l = lexers.Analyse(source)
|
||||
l = lexers.Analyse(dv.before.content)
|
||||
}
|
||||
if l == nil {
|
||||
l = lexers.Fallback
|
||||
}
|
||||
return chroma.Coalesce(l)
|
||||
dv.cachedLexer = chroma.Coalesce(l)
|
||||
return dv.cachedLexer
|
||||
}
|
||||
|
||||
func (dv *DiffView) getChromaFormatter(gbColor color.Color) chroma.Formatter {
|
||||
func (dv *DiffView) getChromaFormatter(bgColor color.Color) chroma.Formatter {
|
||||
return chromaFormatter{
|
||||
bgColor: gbColor,
|
||||
bgColor: bgColor,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user