Help us shape the future of Go! Take the 2026 Go Developer Survey

diffview

package
v1.0.0-rc.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 1, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Overview

Package diffview draws the lines of a diff: syntax highlighting by file type, line numbers in a gutter, added and removed lines on tinted grounds, the changed part of a changed line marked, and a side by side layout.

The design follows the diff view of Crush (github.com/charmbracelet/crush, internal/ui/diffview): chroma picks a lexer from the file name and tokenises the code, every token is drawn with its colour on the ground of the line it sits on so no cell is left without a background, and a changed line is paired with the line it replaced for the split layout. No code is copied from Crush, whose licence (FSL-1.1-MIT) is not yet MIT for any version of that view. What differs here: the colours come from the active tuios theme rather than a fixed style, the lines of one side of a hunk are tokenised together so a comment or string that spans lines keeps its colour, and the part of a line that changed is marked.

The package draws; it does not lay out. The caller decides which rows are on screen and asks for those only, so a file of five thousand lines costs the rows that show. Tokenising is the expensive part, so the caller asks for a chunk of lines at a time and keeps what Highlighter.Highlight returned for as long as the diff does not change.

The browser build (GOOS=js) leaves chroma out and draws every line plain: it never shows a review, and the lexers would add megabytes to the page.

Index

Constants

View Source
const (
	ContrastFloor = 4.5
	MarkFloor     = 3.0
)

Contrast floors in WCAG contrast ratio: text, and a mark such as a comment or a line number.

View Source
const Enabled = true

Enabled reports whether this build highlights at all. The browser build does not: it never shows a review, and the lexers would add megabytes to the page.

View Source
const NoColor noColor = 0

NoColor stands for the terminal's default colour: it paints nothing.

View Source
const SignWidth = 2

SignWidth is how many cells Sign draws.

Variables

This section is empty.

Functions

func Changed

func Changed(old, new string) (Range, Range)

Changed finds the part of a line that changed between old and new: what is left once the prefix and suffix they share are taken off, widened to whole words so a renamed identifier is marked as a whole. Both ranges are empty when the lines have too little in common for the mark to mean anything, which is when a line was rewritten rather than edited: the line's own ground already says it changed.

func ClassNames

func ClassNames() [NumClasses]string

ClassNames names each Class, in the order of their numbers, for a client that is sent the numbers.

func ContrastRatio

func ContrastRatio(a, b color.Color) float64

ContrastRatio is the WCAG contrast ratio of a and b, from 1 to 21.

func ContrastText

func ContrastText(bg color.Color) color.Color

ContrastText is black or white, whichever reads better on bg.

func Darker

func Darker(c color.Color, factor float64) color.Color

Darker is c with its lightness divided by factor (factor > 1).

func DefaultSyntax

func DefaultSyntax() [NumClasses]color.Color

DefaultSyntax is the classes' colours when no terminal theme is set, from the charmtone palette the rest of tuios's chrome is drawn in.

func Ellipsis

func Ellipsis() string

Ellipsis is the mark a cut line ends in.

func IsNoColor

func IsNoColor(c color.Color) bool

IsNoColor reports whether c is NoColor.

func Lighter

func Lighter(c color.Color, factor float64) color.Color

Lighter is c with its lightness multiplied by factor (factor > 1).

func MixColors

func MixColors(a, b color.Color, t float64) color.Color

MixColors is the share t of b mixed into a, interpolated in OKLab.

func Readable

func Readable(fg, bg color.Color) color.Color

Readable is fg moved just far enough from bg to clear ContrastFloor.

func ReadableAt

func ReadableAt(fg, bg color.Color, floor float64) color.Color

ReadableAt is fg, or the nearest colour of its hue that has at least floor contrast against bg, found by moving its lightness away from bg.

func Shown

func Shown(c color.Color) color.Color

Shown is c as the terminal shows it. AIM draws truecolor and Bubble Tea downsamples, so it is c itself.

func Slot

func Slot(i int) color.Color

Slot is the terminal's i-th basic colour (0-15), painted from the user's own palette.

func SyntaxFromANSI

func SyntaxFromANSI(pal [16]color.Color) [NumClasses]color.Color

SyntaxFromANSI maps a terminal theme's sixteen colours onto the classes, the way most editor themes built on a terminal palette do: keywords in magenta, strings in green, numbers in yellow, functions in blue, types in cyan and comments in the dim grey. A nil slot leaves its class plain.

Types

type Class

type Class uint8

Class is what a run of code is, as far as its colour goes. It is a small set on purpose: a theme has sixteen colours to say it with, and a class that has no colour of its own is drawn as Plain.

const (
	// Plain is text with no class: names, and anything the lexer does not
	// know.
	Plain Class = iota
	// Keyword is a keyword of the language.
	Keyword
	// Type is a type name or a keyword that names one.
	Type
	// Func is the name of a function being declared or called.
	Func
	// Builtin is a name the language provides: len, print, self.
	Builtin
	// String is a string or character literal.
	String
	// Number is a number, and a constant such as true or nil.
	Number
	// Comment is a comment.
	Comment
	// Operator is an operator.
	Operator
	// Punct is punctuation: brackets, commas, semicolons.
	Punct
	// Preproc is a preprocessor line, a decorator or an annotation.
	Preproc
	// Tag is a markup tag name.
	Tag
	// Attr is a markup attribute name, or a key in a data file.
	Attr
	// Heading is a heading in prose markup.
	Heading
	// Meta is text tuios adds to a line, such as the mark for a missing
	// newline at the end of a file.
	Meta
	// NumClasses is how many classes there are.
	NumClasses
)

type Depth

type Depth int

Depth is the colour depth a diff is drawn for.

const (
	DepthTrue Depth = iota
	Depth256
	Depth16
)

The depths, richest first so the zero value draws truecolor.

type Highlighter

type Highlighter struct {
	// contains filtered or unexported fields
}

Highlighter tokenises code and caches the lexers it finds. The zero value is ready to use; use it through a pointer. It is safe for concurrent use.

func (*Highlighter) Highlight

func (h *Highlighter) Highlight(path string, lines []string) [][]Span

Highlight tokenises lines of the file at path as one text, and returns the spans of each line. It returns nil when the file type is not known, the text is past the limits, or the lexer fails: the caller draws those lines plain.

The lines are one side of one hunk, in order. Tokenising them together rather than one by one is what keeps a block comment or a multi-line string coloured on its later lines. A hunk starts wherever the diff does, so a hunk that opens inside a comment is read as code until the comment ends; that is the most a view of part of a file can do.

func (*Highlighter) HighlightLang

func (h *Highlighter) HighlightLang(lang string, lines []string) [][]Span

HighlightLang is Highlight for a code block whose language is named, as a Markdown fence names it, rather than known by its file name. It returns nil for an empty or unknown name.

func (*Highlighter) Tokens

func (h *Highlighter) Tokens(path string, lines []string, skipLine int) [][]Token

Tokens is Highlight for a client that colours the code itself. It picks the lexer the same way and keeps the same limits, and gives each run chroma's CSS class name rather than a Class. Plain text and whitespace get no token. A line longer than skipLine characters is tokenised as empty and gets no tokens, so one long line costs the others nothing; 0 keeps every line.

type Kind

type Kind uint8

Kind is what a line of a diff is, which picks its ground.

const (
	// Context is a line both sides have.
	Context Kind = iota
	// Add is a line only the new side has.
	Add
	// Delete is a line only the old side has.
	Delete
	// Missing is the empty half of a side by side row whose line is on the
	// other side only.
	Missing
)

type Pair

type Pair struct {
	Left, Right int
}

Pair is one row of the side by side layout: the index of the line on the left (old) side and on the right (new) side, -1 for a side with no line. A line both sides have is on both.

func Pairs

func Pairs(kinds []Kind) []Pair

Pairs lays a hunk's lines out side by side. kinds holds each line's kind, Context, Add or Delete. A run of removed lines followed by a run of added ones is read as the removed lines replaced, and the n-th removed line sits beside the n-th added one; what is left of the longer run sits beside nothing.

type Palette

type Palette struct {
	// Depth is the colour depth the diff is drawn for. It has to be the depth
	// overlay is set to, because the inks are measured as that depth shows
	// them.
	Depth Depth
	// Ground is the background of everything the view draws.
	Ground color.Color
	// Fg is the colour of plain text.
	Fg color.Color
	// Accent tints the row under the cursor.
	Accent color.Color
	// Add and Delete tint added and removed lines.
	Add, Delete color.Color
	// Syntax is each class's colour. A nil entry is drawn in Fg. At 16
	// colours it is not read: the classes take the terminal's own slots.
	Syntax [NumClasses]color.Color
}

Palette is what a Theme is built from: the ground the diff is drawn on, the text colour, and the colours that mean something.

type Range

type Range struct {
	Start, End int
}

Range is a run of bytes in a line, empty when Start == End.

func (Range) Empty

func (r Range) Empty() bool

Empty reports whether the range covers nothing.

type Span

type Span struct {
	Start, End int
	Class      Class
}

Span is a run of one class in a line, in bytes of the line's text.

type Theme

type Theme struct {
	// contains filtered or unexported fields
}

Theme is a Palette worked out into the sequences every cell is drawn with: a ground per kind of line, the cursor's version of each, and each class's ink measured against the ground it sits on so it stays legible on a tinted line in any theme. It is built once per palette; drawing a row only looks sequences up.

What it draws depends on the depth. In truecolor the grounds are tints of the theme's colours. At 256 colours they are fixed palette entries. At 16 colours there are no grounds at all: an added line's number and sign are green and a removed line's red, the changed words are bold and underlined, and the cursor is left to the caller, which reverses the gutter.

func NewTheme

func NewTheme(p Palette) *Theme

NewTheme works a palette out into a Theme. A nil colour in the palette falls back to a readable default, so a theme missing a slot still draws.

func (*Theme) Bg

func (t *Theme) Bg(kind Kind, cursor, word bool) color.Color

Bg is the ground of a line's code: of the part that changed with word.

func (*Theme) Blank

func (t *Theme) Blank(kind Kind, cursor bool, width int) string

Blank draws width empty cells on a line's code ground.

func (*Theme) BlankGutter

func (t *Theme) BlankGutter(kind Kind, cursor bool, width int) string

BlankGutter draws width empty cells on a line's number column ground.

func (*Theme) Code

func (t *Theme) Code(text string, spans []Span, changed Range, kind Kind, cursor bool, xOff, width int) string

Code draws a line's text in exactly width cells, from column xOff of the text on. spans colour it and changed marks the part that changed; both are in bytes of text. A line longer than the room ends in an ellipsis. That a line is scrolled sideways is Sign's to show, so no cell of code is given up for it.

text has to be printable already: the view draws it as it is.

func (*Theme) CodeBg

func (t *Theme) CodeBg(kind Kind, cursor bool) color.Color

CodeBg is the ground of a line's code, without the word tint.

func (*Theme) CodePart

func (t *Theme) CodePart(text string, spans []Span, changed []Range, kind Kind, cursor bool, from, to, width int) string

CodePart draws the cells [from, to) of text as Code styles them, padded on the code ground to width cells and never ending in an ellipsis. A part wider than width is cut at the edge.

func (*Theme) CodeRanges

func (t *Theme) CodeRanges(text string, spans []Span, changed []Range, kind Kind, cursor bool, xOff, width int) string

CodeRanges is Code with more than one changed part, in order and apart: each is lit, and the text between them is not.

func (*Theme) Fg

func (t *Theme) Fg() color.Color

Fg is the colour of plain text, as the theme draws it on its ground.

func (*Theme) Ground

func (t *Theme) Ground() color.Color

Ground is the ground the theme was built on.

func (*Theme) Gutter

func (t *Theme) Gutter(n, width int, kind Kind, cursor bool) string

Gutter draws a line number right-aligned in width cells, with a space on its right. n of zero or less leaves the cells blank, for the side of a line that has no number there.

func (*Theme) GutterBg

func (t *Theme) GutterBg(kind Kind, cursor bool) color.Color

GutterBg is the ground of a line's number column.

func (*Theme) Sign

func (t *Theme) Sign(kind Kind, cursor, scrolled bool) string

Sign draws the mark before a line's code: "+" for an added line, "-" for a removed one, blank otherwise, then an ellipsis when the code is scrolled sideways and there is code to its left that does not show.

type Token

type Token struct {
	Start, End int
	Class      string
}

Token is a run of one token class in a line, in bytes of the line's text. Class is chroma's short CSS class name for the token type: "k" for a keyword, "s2" for a double-quoted string, "c1" for a line comment.

Directories

Path Synopsis
Package lexers holds the syntax definitions the review highlights with, and the registry that serves them.
Package lexers holds the syntax definitions the review highlights with, and the registry that serves them.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL