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
- func Changed(old, new string) (Range, Range)
- func ClassNames() [NumClasses]string
- func ContrastRatio(a, b color.Color) float64
- func ContrastText(bg color.Color) color.Color
- func Darker(c color.Color, factor float64) color.Color
- func DefaultSyntax() [NumClasses]color.Color
- func Ellipsis() string
- func IsNoColor(c color.Color) bool
- func Lighter(c color.Color, factor float64) color.Color
- func MixColors(a, b color.Color, t float64) color.Color
- func Readable(fg, bg color.Color) color.Color
- func ReadableAt(fg, bg color.Color, floor float64) color.Color
- func Shown(c color.Color) color.Color
- func Slot(i int) color.Color
- func SyntaxFromANSI(pal [16]color.Color) [NumClasses]color.Color
- type Class
- type Depth
- type Highlighter
- type Kind
- type Pair
- type Palette
- type Range
- type Span
- type Theme
- func (t *Theme) Bg(kind Kind, cursor, word bool) color.Color
- func (t *Theme) Blank(kind Kind, cursor bool, width int) string
- func (t *Theme) BlankGutter(kind Kind, cursor bool, width int) string
- func (t *Theme) Code(text string, spans []Span, changed Range, kind Kind, cursor bool, ...) string
- func (t *Theme) CodeBg(kind Kind, cursor bool) color.Color
- func (t *Theme) CodePart(text string, spans []Span, changed []Range, kind Kind, cursor bool, ...) string
- func (t *Theme) CodeRanges(text string, spans []Span, changed []Range, kind Kind, cursor bool, ...) string
- func (t *Theme) Fg() color.Color
- func (t *Theme) Ground() color.Color
- func (t *Theme) Gutter(n, width int, kind Kind, cursor bool) string
- func (t *Theme) GutterBg(kind Kind, cursor bool) color.Color
- func (t *Theme) Sign(kind Kind, cursor, scrolled bool) string
- type Token
Constants ¶
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.
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.
const NoColor noColor = 0
NoColor stands for the terminal's default colour: it paints nothing.
const SignWidth = 2
SignWidth is how many cells Sign draws.
Variables ¶
This section is empty.
Functions ¶
func Changed ¶
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 ¶
ContrastRatio is the WCAG contrast ratio of a and b, from 1 to 21.
func ContrastText ¶
ContrastText is black or white, whichever reads better on bg.
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 ReadableAt ¶
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 ¶
Shown is c as the terminal shows it. AIM draws truecolor and Bubble Tea downsamples, so it is c itself.
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 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 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 ¶
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.
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 ¶
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) BlankGutter ¶
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) 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) Gutter ¶
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.