@@ -1,22 +1,17 @@
1package diff
1package diff
6 "image/color"
5 "image/color"
12− "github.com/alecthomas/chroma/v2"
13− "github.com/alecthomas/chroma/v2/formatters"
14− "github.com/alecthomas/chroma/v2/lexers"
15− "github.com/alecthomas/chroma/v2/styles"
16 "github.com/aymanbagabas/go-udiff"
10 "github.com/aymanbagabas/go-udiff"
17 "github.com/charmbracelet/lipgloss/v2"
11 "github.com/charmbracelet/lipgloss/v2"
18 "github.com/charmbracelet/x/ansi"
12 "github.com/charmbracelet/x/ansi"
19 "github.com/opencode-ai/opencode/internal/config"
13 "github.com/opencode-ai/opencode/internal/config"
14+ "github.com/opencode-ai/opencode/internal/highlight"
20 "github.com/opencode-ai/opencode/internal/tui/theme"
15 "github.com/opencode-ai/opencode/internal/tui/theme"
21 "github.com/sergi/go-diff/diffmatchpatch"
16 "github.com/sergi/go-diff/diffmatchpatch"
@@ -322,216 +317,6 @@ func pairLines(lines []DiffLine) []linePair {
322// -------------------------------------------------------------------------
317// -------------------------------------------------------------------------
323// Syntax Highlighting
318// Syntax Highlighting
324// -------------------------------------------------------------------------
319// -------------------------------------------------------------------------
326−// SyntaxHighlight applies syntax highlighting to text based on file extension
327−func SyntaxHighlight(w io.Writer, source, fileName, formatter string, bg color.Color) error {
328− t := theme.CurrentTheme()
330− // Determine the language lexer to use
331− l := lexers.Match(fileName)
333− l = lexers.Analyse(source)
338− l = chroma.Coalesce(l)
340− // Get the formatter
341− f := formatters.Get(formatter)
343− f = formatters.Fallback
346− // Dynamic theme based on current theme values
347− syntaxThemeXml := fmt.Sprintf(`
348− <style name="opencode-theme">
349− <!-- Base colors -->
350− <entry type="Background" style="bg:%s"/>
351− <entry type="Text" style="%s"/>
352− <entry type="Other" style="%s"/>
353− <entry type="Error" style="%s"/>
355− <entry type="Keyword" style="%s"/>
356− <entry type="KeywordConstant" style="%s"/>
357− <entry type="KeywordDeclaration" style="%s"/>
358− <entry type="KeywordNamespace" style="%s"/>
359− <entry type="KeywordPseudo" style="%s"/>
360− <entry type="KeywordReserved" style="%s"/>
361− <entry type="KeywordType" style="%s"/>
363− <entry type="Name" style="%s"/>
364− <entry type="NameAttribute" style="%s"/>
365− <entry type="NameBuiltin" style="%s"/>
366− <entry type="NameBuiltinPseudo" style="%s"/>
367− <entry type="NameClass" style="%s"/>
368− <entry type="NameConstant" style="%s"/>
369− <entry type="NameDecorator" style="%s"/>
370− <entry type="NameEntity" style="%s"/>
371− <entry type="NameException" style="%s"/>
372− <entry type="NameFunction" style="%s"/>
373− <entry type="NameLabel" style="%s"/>
374− <entry type="NameNamespace" style="%s"/>
375− <entry type="NameOther" style="%s"/>
376− <entry type="NameTag" style="%s"/>
377− <entry type="NameVariable" style="%s"/>
378− <entry type="NameVariableClass" style="%s"/>
379− <entry type="NameVariableGlobal" style="%s"/>
380− <entry type="NameVariableInstance" style="%s"/>
382− <entry type="Literal" style="%s"/>
383− <entry type="LiteralDate" style="%s"/>
384− <entry type="LiteralString" style="%s"/>
385− <entry type="LiteralStringBacktick" style="%s"/>
386− <entry type="LiteralStringChar" style="%s"/>
387− <entry type="LiteralStringDoc" style="%s"/>
388− <entry type="LiteralStringDouble" style="%s"/>
389− <entry type="LiteralStringEscape" style="%s"/>
390− <entry type="LiteralStringHeredoc" style="%s"/>
391− <entry type="LiteralStringInterpol" style="%s"/>
392− <entry type="LiteralStringOther" style="%s"/>
393− <entry type="LiteralStringRegex" style="%s"/>
394− <entry type="LiteralStringSingle" style="%s"/>
395− <entry type="LiteralStringSymbol" style="%s"/>
397− <entry type="LiteralNumber" style="%s"/>
398− <entry type="LiteralNumberBin" style="%s"/>
399− <entry type="LiteralNumberFloat" style="%s"/>
400− <entry type="LiteralNumberHex" style="%s"/>
401− <entry type="LiteralNumberInteger" style="%s"/>
402− <entry type="LiteralNumberIntegerLong" style="%s"/>
403− <entry type="LiteralNumberOct" style="%s"/>
405− <entry type="Operator" style="%s"/>
406− <entry type="OperatorWord" style="%s"/>
407− <entry type="Punctuation" style="%s"/>
409− <entry type="Comment" style="%s"/>
410− <entry type="CommentHashbang" style="%s"/>
411− <entry type="CommentMultiline" style="%s"/>
412− <entry type="CommentSingle" style="%s"/>
413− <entry type="CommentSpecial" style="%s"/>
414− <entry type="CommentPreproc" style="%s"/>
415− <!-- Generic styles -->
416− <entry type="Generic" style="%s"/>
417− <entry type="GenericDeleted" style="%s"/>
418− <entry type="GenericEmph" style="italic %s"/>
419− <entry type="GenericError" style="%s"/>
420− <entry type="GenericHeading" style="bold %s"/>
421− <entry type="GenericInserted" style="%s"/>
422− <entry type="GenericOutput" style="%s"/>
423− <entry type="GenericPrompt" style="%s"/>
424− <entry type="GenericStrong" style="bold %s"/>
425− <entry type="GenericSubheading" style="bold %s"/>
426− <entry type="GenericTraceback" style="%s"/>
427− <entry type="GenericUnderline" style="underline"/>
428− <entry type="TextWhitespace" style="%s"/>
431− getColor(t.Background()), // Background
432− getColor(t.Text()), // Text
433− getColor(t.Text()), // Other
434− getColor(t.Error()), // Error
436− getColor(t.SyntaxKeyword()), // Keyword
437− getColor(t.SyntaxKeyword()), // KeywordConstant
438− getColor(t.SyntaxKeyword()), // KeywordDeclaration
439− getColor(t.SyntaxKeyword()), // KeywordNamespace
440− getColor(t.SyntaxKeyword()), // KeywordPseudo
441− getColor(t.SyntaxKeyword()), // KeywordReserved
442− getColor(t.SyntaxType()), // KeywordType
444− getColor(t.Text()), // Name
445− getColor(t.SyntaxVariable()), // NameAttribute
446− getColor(t.SyntaxType()), // NameBuiltin
447− getColor(t.SyntaxVariable()), // NameBuiltinPseudo
448− getColor(t.SyntaxType()), // NameClass
449− getColor(t.SyntaxVariable()), // NameConstant
450− getColor(t.SyntaxFunction()), // NameDecorator
451− getColor(t.SyntaxVariable()), // NameEntity
452− getColor(t.SyntaxType()), // NameException
453− getColor(t.SyntaxFunction()), // NameFunction
454− getColor(t.Text()), // NameLabel
455− getColor(t.SyntaxType()), // NameNamespace
456− getColor(t.SyntaxVariable()), // NameOther
457− getColor(t.SyntaxKeyword()), // NameTag
458− getColor(t.SyntaxVariable()), // NameVariable
459− getColor(t.SyntaxVariable()), // NameVariableClass
460− getColor(t.SyntaxVariable()), // NameVariableGlobal
461− getColor(t.SyntaxVariable()), // NameVariableInstance
463− getColor(t.SyntaxString()), // Literal
464− getColor(t.SyntaxString()), // LiteralDate
465− getColor(t.SyntaxString()), // LiteralString
466− getColor(t.SyntaxString()), // LiteralStringBacktick
467− getColor(t.SyntaxString()), // LiteralStringChar
468− getColor(t.SyntaxString()), // LiteralStringDoc
469− getColor(t.SyntaxString()), // LiteralStringDouble
470− getColor(t.SyntaxString()), // LiteralStringEscape
471− getColor(t.SyntaxString()), // LiteralStringHeredoc
472− getColor(t.SyntaxString()), // LiteralStringInterpol
473− getColor(t.SyntaxString()), // LiteralStringOther
474− getColor(t.SyntaxString()), // LiteralStringRegex
475− getColor(t.SyntaxString()), // LiteralStringSingle
476− getColor(t.SyntaxString()), // LiteralStringSymbol
478− getColor(t.SyntaxNumber()), // LiteralNumber
479− getColor(t.SyntaxNumber()), // LiteralNumberBin
480− getColor(t.SyntaxNumber()), // LiteralNumberFloat
481− getColor(t.SyntaxNumber()), // LiteralNumberHex
482− getColor(t.SyntaxNumber()), // LiteralNumberInteger
483− getColor(t.SyntaxNumber()), // LiteralNumberIntegerLong
484− getColor(t.SyntaxNumber()), // LiteralNumberOct
486− getColor(t.SyntaxOperator()), // Operator
487− getColor(t.SyntaxKeyword()), // OperatorWord
488− getColor(t.SyntaxPunctuation()), // Punctuation
490− getColor(t.SyntaxComment()), // Comment
491− getColor(t.SyntaxComment()), // CommentHashbang
492− getColor(t.SyntaxComment()), // CommentMultiline
493− getColor(t.SyntaxComment()), // CommentSingle
494− getColor(t.SyntaxComment()), // CommentSpecial
495− getColor(t.SyntaxKeyword()), // CommentPreproc
497− getColor(t.Text()), // Generic
498− getColor(t.Error()), // GenericDeleted
499− getColor(t.Text()), // GenericEmph
500− getColor(t.Error()), // GenericError
501− getColor(t.Text()), // GenericHeading
502− getColor(t.Success()), // GenericInserted
503− getColor(t.TextMuted()), // GenericOutput
504− getColor(t.Text()), // GenericPrompt
505− getColor(t.Text()), // GenericStrong
506− getColor(t.Text()), // GenericSubheading
507− getColor(t.Error()), // GenericTraceback
508− getColor(t.Text()), // TextWhitespace
511− r := strings.NewReader(syntaxThemeXml)
512− style := chroma.MustNewXMLStyle(r)
514− // Modify the style to use the provided background
515− s, err := style.Builder().Transform(
516− func(t chroma.StyleEntry) chroma.StyleEntry {
517− r, g, b, _ := bg.RGBA()
518− t.Background = chroma.NewColour(uint8(r>>8), uint8(g>>8), uint8(b>>8))
526− // Tokenize and format
527− it, err := l.Tokenise(nil, source)
532− return f.Format(w, s, it)
535func getColor(c color.Color) string {
320func getColor(c color.Color) string {
536 rgba := color.RGBAModel.Convert(c).(color.RGBA)
321 rgba := color.RGBAModel.Convert(c).(color.RGBA)
537 return fmt.Sprintf("#%02x%02x%02x", rgba.R, rgba.G, rgba.B)
322 return fmt.Sprintf("#%02x%02x%02x", rgba.R, rgba.G, rgba.B)
@@ -539,12 +324,11 @@ func getColor(c color.Color) string {
540// highlightLine applies syntax highlighting to a single line
325// highlightLine applies syntax highlighting to a single line
541func highlightLine(fileName string, line string, bg color.Color) string {
326func highlightLine(fileName string, line string, bg color.Color) string {
542− var buf bytes.Buffer
543− err := SyntaxHighlight(&buf, line, fileName, "terminal16m", bg)
327+ highlighted, err := highlight.SyntaxHighlight(line, fileName, bg)
544 if err != nil {
328 if err != nil {
545 return line
329 return line
547− return buf.String()
331+ return highlighted
550// createStyles generates the lipgloss styles needed for rendering diffs
334// createStyles generates the lipgloss styles needed for rendering diffs
@@ -561,18 +345,6 @@ func createStyles(t theme.Theme) (removedLineStyle, addedLineStyle, contextLineS
561// Rendering Functions
345// Rendering Functions
562// -------------------------------------------------------------------------
346// -------------------------------------------------------------------------
564−func lipglossToHex(color color.Color) string {
565− r, g, b, a := color.RGBA()
567− // Scale uint32 values (0-65535) to uint8 (0-255).
573− return fmt.Sprintf("#%02x%02x%02x%02x", r8, g8, b8, a8)
576// applyHighlighting applies intra-line highlighting to a piece of text
348// applyHighlighting applies intra-line highlighting to a piece of text
577func applyHighlighting(content string, segments []Segment, segmentType LineType, highlightBg color.Color) string {
349func applyHighlighting(content string, segments []Segment, segmentType LineType, highlightBg color.Color) string {
578 // Find all ANSI sequences in the content
350 // Find all ANSI sequences in the content
@@ -614,7 +386,7 @@ func applyHighlighting(content string, segments []Segment, segmentType LineType,
615 // Get the appropriate color based on terminal background
387 // Get the appropriate color based on terminal background
616 bgColor := lipgloss.Color(getColor(highlightBg))
388 bgColor := lipgloss.Color(getColor(highlightBg))
617− fgColor := lipgloss.Color(getColor(theme.CurrentTheme().Background()))
389+ // fgColor := lipgloss.Color(getColor(theme.CurrentTheme().Background()))
619 for i := 0; i < len(content); {
391 for i := 0; i < len(content); {
620 // Check if we're at an ANSI sequence
392 // Check if we're at an ANSI sequence
@@ -651,15 +423,15 @@ func applyHighlighting(content string, segments []Segment, segmentType LineType,
651 currentStyle := ansiSequences[currentPos]
423 currentStyle := ansiSequences[currentPos]
653 // Apply foreground and background highlight
425 // Apply foreground and background highlight
654− sb.WriteString("\x1b[38;2;")
426+ // sb.WriteString("\x1b[38;2;")
655− r, g, b, _ := fgColor.RGBA()
427+ // r, g, b, _ := fgColor.RGBA()
656− sb.WriteString(fmt.Sprintf("%d;%d;%dm", r>>8, g>>8, b>>8))
428+ // sb.WriteString(fmt.Sprintf("%d;%d;%dm", r>>8, g>>8, b>>8))
657 sb.WriteString("\x1b[48;2;")
429 sb.WriteString("\x1b[48;2;")
658− r, g, b, _ = bgColor.RGBA()
430+ r, g, b, _ := bgColor.RGBA()
659 sb.WriteString(fmt.Sprintf("%d;%d;%dm", r>>8, g>>8, b>>8))
431 sb.WriteString(fmt.Sprintf("%d;%d;%dm", r>>8, g>>8, b>>8))
660 sb.WriteString(char)
432 sb.WriteString(char)
661 // Reset foreground and background
433 // Reset foreground and background
662− sb.WriteString("\x1b[39m")
434+ // sb.WriteString("\x1b[39m")
664 // Reapply the original ANSI sequence
436 // Reapply the original ANSI sequence
665 sb.WriteString(currentStyle)
437 sb.WriteString(currentStyle)