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

diffview

package
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Sep 17, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Overview

Package diffview renders one file's hunks as a scrollable pane, either unified or side by side. It draws the hunk and line cursors, selection markers, split tags, search matches, and syntax or word-level highlighting, and owns no keybindings of its own.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type HunkPosition added in v0.1.5

type HunkPosition struct {
	HunkIdx    int
	LineInHunk int
}

HunkPosition locates a rendered row. HunkIdx is the hunk the row belongs to and LineInHunk is the row within it, where row 0 is the hunk header.

type LineIndex

type LineIndex struct {
	HunkOffsets    []int
	HunkLineCounts []int
	TotalLines     int
}

LineIndex maps a scroll offset to a hunk without walking every hunk. Offsets and counts are in rendered rows, and each hunk's first row is its header, so HunkLineCounts is one more than the hunk's line count. Hiding whitespace changes the row counts, so the index is rebuilt on that toggle.

func (*LineIndex) FindHunkForOffset

func (idx *LineIndex) FindHunkForOffset(offset int) HunkPosition

FindHunkForOffset maps a rendered row to its hunk and the row within that hunk. An offset past the last row clamps to the end of the final hunk, and an empty index returns the zero position.

type MatchRange

type MatchRange struct {
	Start int
	End   int
}

MatchRange is a half-open byte range within one line's raw content, used to underlay search hits. Offsets are byte offsets rather than rune or column positions.

type Model

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

Model is the diff pane. It holds no cursor authority of its own: the parent model pushes the selected hunk, line cursor, search state, and tag state in before each render.

func New

func New(cfg config.Config) Model

New builds a pane with no file loaded, so View renders a placeholder until SetFileChange is called. Everything the user can toggle at runtime takes its starting value from cfg.

func (*Model) IsSideBySide

func (m *Model) IsSideBySide() bool

IsSideBySide reports the current layout, which the status bar reads to label the toggle.

func (*Model) Scroll

func (m *Model) Scroll(delta int)

Scroll moves the viewport by delta lines, clamped to the file's rendered height. It does nothing when no file is loaded, so callers need not check first.

func (*Model) ScrollFullPageDown

func (m *Model) ScrollFullPageDown(viewHeight int)

ScrollFullPageDown scrolls down viewHeight lines. Pass the pane's height, not the terminal's.

func (*Model) ScrollFullPageUp

func (m *Model) ScrollFullPageUp(viewHeight int)

ScrollFullPageUp scrolls up viewHeight lines. Pass the pane's height, not the terminal's.

func (*Model) ScrollHalfPageDown

func (m *Model) ScrollHalfPageDown(viewHeight int)

ScrollHalfPageDown scrolls down half of viewHeight. Pass the pane's height, not the terminal's.

func (*Model) ScrollHalfPageUp

func (m *Model) ScrollHalfPageUp(viewHeight int)

ScrollHalfPageUp scrolls up half of viewHeight. Pass the pane's height, not the terminal's.

func (*Model) SetFileChange

func (m *Model) SetFileChange(file diff.FileChange)

SetFileChange loads a file, scrolls back to the top, and rebuilds the word-diff cache and line index. The pane keeps its own copy of file, so later edits to the caller's value are not picked up.

func (*Model) SetSearchState

func (m *Model) SetSearchState(
	isSearching bool,
	getMatches func(hunkIdx, lineIdx int) []MatchRange,
)

SetSearchState turns match highlighting on or off. Highlighting needs both isSearching and a non-nil getMatches, so passing false with nil is the way to clear a finished search.

func (*Model) SetSelection

func (m *Model) SetSelection(selectedHunk int, isSelected func(hunkIdx int) bool)

SetSelection points the hunk cursor at selectedHunk and installs the predicate that decides which hunks draw as selected. A nil isSelected draws no hunk as selected, which is the browse-mode case.

func (*Model) SetTagState

func (m *Model) SetTagState(getHunkTags func(hunkIdx int) []SplitTag)

SetTagState installs the lookup for split tags drawn beside each hunk header. The callback is asked once per visible hunk per render, so it must be cheap. A nil callback draws no tags.

func (*Model) SetVisualState

func (m *Model) SetVisualState(
	lineCursor int,
	isVisualMode bool,
	visualAnchor int,
	isLineSelected func(hunkIdx, lineIdx int) bool,
)

SetVisualState installs the line cursor and visual-mode range. Both lineCursor and visualAnchor are line indexes within the hunk the cursor is on, and the range they bound is inclusive at both ends.

func (*Model) ShowLineNumbers

func (m *Model) ShowLineNumbers() bool

ShowLineNumbers reports whether the line-number gutter is drawn.

func (*Model) ShowWhitespace

func (m *Model) ShowWhitespace() bool

ShowWhitespace reports whether whitespace glyphs are drawn.

func (*Model) ToggleLineNumbers

func (m *Model) ToggleLineNumbers()

ToggleLineNumbers flips the old/new line-number gutter.

func (*Model) ToggleSideBySide

func (m *Model) ToggleSideBySide()

ToggleSideBySide switches between the unified and side-by-side layouts.

func (*Model) ToggleWhitespace

func (m *Model) ToggleWhitespace()

ToggleWhitespace flips visible whitespace glyphs and rebuilds the line index, because the glyphs change how wide a rendered line is.

func (*Model) ToggleWordDiff

func (m *Model) ToggleWordDiff()

ToggleWordDiff flips intra-line highlighting and recomputes the word diffs for the current file, which costs a pass over every changed line pair.

func (*Model) View

func (m *Model) View(width, height int, focused bool) string

View renders the pane at the given size, padding out to it when the content is shorter. The focused flag only changes cursor styling, so an unfocused pane still shows where the cursor sits.

func (*Model) WordLevelDiff

func (m *Model) WordLevelDiff() bool

WordLevelDiff reports whether intra-line highlighting is on.

type RenderContext

type RenderContext struct {
	IsSelected      func(hunkIdx int) bool
	WordDiffCache   *WordDiffCache
	IsLineSelected  func(hunkIdx, lineIdx int) bool
	GetMatches      func(hunkIdx, lineIdx int) []MatchRange
	SelectedHunk    int
	LineCursor      int
	TabWidth        int
	VisualAnchor    int
	Width           int
	Height          int
	IsVisualMode    bool
	IsSearching     bool
	Focused         bool
	ShowLineNumbers bool
	ShowWhitespace  bool
	WordLevelDiff   bool
}

RenderContext is everything a ViewMode needs for one render pass. The callbacks are invoked per visible hunk or line, so they must be cheap, and a nil callback means that decoration is off.

type SideBySideView

type SideBySideView struct{}

SideBySideView renders old and new content in two columns. It is stateless, so one instance can serve every file.

func NewSideBySideView

func NewSideBySideView() *SideBySideView

NewSideBySideView returns the two-column renderer.

func (*SideBySideView) Render

func (*SideBySideView) Render(file *diff.FileChange, ctx *RenderContext) string

Render draws the file into ctx.Width by ctx.Height, splitting the width between the two columns and padding out when the content is shorter. A nil file renders the empty-state placeholder.

func (*SideBySideView) SupportsSelection

func (*SideBySideView) SupportsSelection() bool

SupportsSelection reports false, because the two-column layout draws no selection markers.

type SplitTag

type SplitTag rune

SplitTag is the single character a hunk is tagged with during a multi-way split, drawn as "[a]" beside the hunk header.

type ViewMode

type ViewMode interface {
	Render(file *diff.FileChange, ctx *RenderContext) string
	SupportsSelection() bool
}

ViewMode is one diff layout. Render must pad its output to ctx.Width by ctx.Height, because the caller composites it against a fixed pane.

type ViewModeType

type ViewModeType string

ViewModeType names the layout the pane renders in.

const (
	ViewModeUnified    ViewModeType = "unified"
	ViewModeSideBySide ViewModeType = "side-by-side"
)

The two layouts a pane can render in. The values match the strings accepted by config.ViewMode.

type WordDiffCache

type WordDiffCache struct {
	HunkDiffs map[int]map[int]diff.WordDiffResult
}

WordDiffCache holds precomputed intra-line diffs keyed by hunk index, then by line index within that hunk. Only added and deleted lines get an entry, so a lookup miss means render the line plain.

Jump to

Keyboard shortcuts

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