Documentation
¶
Overview ¶
Package diff holds the canonical, view-independent representation of a set of changes under review, plus projections of that model into rows for display.
The model here is deliberately decoupled from any terminal concern: rendered screen rows are never canonical comment locations. Comments point at semantic positions in this model (see the review package), which survive terminal resize, context folding, and switching between unified and split layouts.
Index ¶
- Variables
- func NormalizeContent(b []byte) []byte
- func Relocate(files []FileDiff, loc Location) (Location, RelocateResult)
- type AnnotationEdge
- type ContextAnchor
- type DiffLine
- type DisplayCell
- type DisplayRow
- type FileDiff
- type FileStatus
- type Hunk
- type LineKind
- type Location
- type RelocateResult
- type Side
Constants ¶
This section is empty.
Variables ¶
var TabWidth = 4
TabWidth is the number of columns a tab expands to when parsing diff lines. It defaults to 4 and may be overridden from configuration before parsing.
Functions ¶
func NormalizeContent ¶ added in v0.0.5
NormalizeContent expands tabs in each line of raw file content to the display form hunk text uses (TabWidth tab stops). Fetched whole files must pass through this before being compared with — or rendered next to — parsed diff lines: the parser expands tabs at parse time, so raw content would mismatch on every indented line.
func Relocate ¶
func Relocate(files []FileDiff, loc Location) (Location, RelocateResult)
Relocate attempts to map loc onto the given (newer) set of file diffs using the location's captured context anchor. It first checks whether the exact side+line still holds the same content; failing that, it searches the file (following a rename) for a line with the same anchor text and non-conflicting surrounding context, relocating only when the match is unique. When several candidates match — or none do — the location is reported as orphaned and left unchanged so the reviewer can decide.
Types ¶
type AnnotationEdge ¶
type AnnotationEdge uint8
AnnotationEdge distinguishes the border rows of a boxed annotation from its text rows. EdgeDivider separates items of a thread that share one box.
const ( EdgeNone AnnotationEdge = iota EdgeTop EdgeBottom EdgeDivider )
type ContextAnchor ¶
type ContextAnchor struct {
HunkHeader string
Before []string
AnchorText string
After []string
ContentHash string
}
ContextAnchor records enough surrounding content to relocate a comment after the diff changes: the hunk header, a few lines of context around the anchor, and a hash of the anchored content.
func NewContextAnchor ¶
func NewContextAnchor(f *FileDiff, hunkIndex, lineIndex, n int) ContextAnchor
NewContextAnchor builds an anchor for the line at (hunkIndex, lineIndex) in f, capturing up to n lines of context on each side.
type DiffLine ¶
type DiffLine struct {
Kind LineKind
Text string
// Raw is the line's original text when tab expansion changed it, and ""
// when Text is already verbatim. Display uses Text; anything that leaves
// the program as code — change suggestions especially — must use
// RawText, or the patch would silently swap tabs for spaces.
Raw string
// OldLine is the 1-based line number on the old side, or nil for additions.
OldLine *int
// NewLine is the 1-based line number on the new side, or nil for deletions.
NewLine *int
// PatchPosition is the 1-based offset of this line within the file's patch
// (counting from the first hunk header, as GitHub numbers review positions),
// or nil for lines that have no patch position. Preserved to reconcile our
// parsed model with comments returned by the GitHub API.
PatchPosition *int
}
DiffLine is one logical line of a hunk. Exactly one of OldLine/NewLine is nil for pure additions/deletions; both are set for context lines.
func (*DiffLine) LineNumber ¶
LineNumber returns the line number a DiffLine carries on the given side, or (0, false) if the line does not exist on that side (e.g. the new side of a deletion).
type DisplayCell ¶
DisplayCell is one rendered cell: a line number (optional), the kind of line it represents (for styling), and the raw text. A nil cell renders as blank (used for the empty side of an unpaired addition/deletion in split view).
type DisplayRow ¶
type DisplayRow struct {
Left *DisplayCell
Right *DisplayCell
// Source is the semantic location of this row (start line = end line here);
// nil for rows that cannot be commented on (e.g. hunk headers). For split
// rows carrying both sides it points at the right/new side.
Source *Location
// AltSource is the location of the opposite (left/old) side when a split
// row carries both sides — a paired deletion/addition or a context line —
// so the cursor can select either side. Nil elsewhere.
AltSource *Location
// Annotation marks a display-only row injected under a diff line (e.g. an
// inline comment preview). Annotation rows carry no Source, are skipped by
// navigation, and are not hunk headers.
Annotation bool
// Edge marks the horizontal border rows of a boxed annotation; text rows
// keep EdgeNone. Meaningful only when Annotation is set.
Edge AnnotationEdge
// Continuation marks the overflow of a wrapped diff line: it renders with
// the line's own styling but carries no line numbers or Source, so
// navigation and selection treat the logical line as one unit.
Continuation bool
// Separator marks the boundary row drawn between hunks, so the reader can
// see where one excerpt ends and the next begins when the surrounding
// file context is not shown. Display-only: no Source, skipped by
// navigation.
Separator bool
// Pre marks an annotation text row whose content is preformatted terminal
// output (an image rendered to cells): renderers must clip it ANSI-aware
// and never wrap or restyle it. Meaningful only with Annotation set.
Pre bool
}
DisplayRow is one rendered row of a diff view. In unified layout only Left is used to carry the number gutter is folded into a single cell; in split layout Left and Right are the two sides. Source points back at the semantic location this row anchors to, so a comment made on a screen row resolves to a stable Location rather than a terminal coordinate.
func RenderSplit ¶
func RenderSplit(f *FileDiff) []DisplayRow
RenderSplit projects a FileDiff into split-layout rows: a left (old) column and a right (new) column. Context lines occupy both columns. Change blocks are paired with a simple rule — gather consecutive deletions, then the immediately following additions, pair them by index, and render any leftover against a blank cell on the opposite side. Similarity/intraline matching is intentionally out of scope for v1.
func RenderUnified ¶
func RenderUnified(f *FileDiff) []DisplayRow
RenderUnified projects a FileDiff into unified-layout rows: each logical diff line becomes exactly one row. The Left cell carries the old-side number, the Right cell the new-side number, and the text is shared; callers style by Kind. Every content row gets a Source so it can be commented on.
func RenderUnifiedContext ¶ added in v0.0.4
func RenderUnifiedContext(f *FileDiff, content []byte) ([]DisplayRow, error)
RenderUnifiedContext projects a FileDiff over the full new-side file content: every file line becomes a context row, with the hunks' own rows (including deletions) spliced in at their positions — the diff as it reads inside the file, like an unbounded -U. Each hunk is bracketed by boundary rows (a rule plus its @@ header on entry, a rule on exit) so the reader can tell exactly where the reviewed excerpt begins and ends inside the surrounding file. Gap rows carry both line numbers but no Source: hosts anchor review comments to diff positions, so lines outside the hunks are deliberately not commentable. Hunk rows keep their exact Sources, so comments, threads, and navigation behave identically in both views.
Every context/addition line is verified against the file content; a mismatch aborts with an error rather than rendering a lie — it means the content is from the wrong revision.
type FileDiff ¶
type FileDiff struct {
OldPath string
NewPath string
Status FileStatus
Hunks []Hunk
IsBinary bool
IsRenamed bool
// RawPatch is the original unified-diff text for this file, preserved for
// export snippets and (later) GitHub reconciliation.
RawPatch string
}
FileDiff is the full set of changes to a single file.
func FindFileFor ¶ added in v0.0.2
FindFileFor returns the file whose old or new path matches path (so a comment made before a rename still finds its file). Exported because exchange import needs the same rename-tolerant lookup when anchoring foreign comments.
func ParsePatch ¶
ParsePatch reads a unified/Git patch and adapts it into our canonical model.
It computes per-line old/new numbers by walking each fragment from its starting positions, and assigns each line a GitHub-style PatchPosition: the 1-based offset counted down from the file's first "@@" header, where every content line and every subsequent hunk header advances the position.
func ParsePatchBytes ¶
ParsePatchBytes is a convenience wrapper over ParsePatch for in-memory patches.
func (*FileDiff) FindBySideLine ¶
FindBySideLine locates the (hunkIndex, lineIndex) of the line that occupies the given number on the given side. It returns (-1, -1, false) when no such line is present in the diff.
type FileStatus ¶
type FileStatus uint8
FileStatus describes what happened to a file across the diff.
const ( // StatusModified is the default: content changed in place. StatusModified FileStatus = iota StatusAdded StatusDeleted StatusRenamed StatusCopied StatusBinary )
func (FileStatus) String ¶
func (s FileStatus) String() string
String renders a short human label for the status.
type LineKind ¶
type LineKind uint8
LineKind classifies a single logical diff line.
const ( // LineContext is an unchanged line present on both sides. LineContext LineKind = iota // LineAddition is a line added on the new side. LineAddition // LineDeletion is a line removed from the old side. LineDeletion // LineMetadata is a non-content marker line (e.g. "No newline at end of file"). LineMetadata )
type Location ¶
type Location struct {
Path string
// CommitOID is the head commit the location was captured against, when
// known (empty in pure patch-file mode).
CommitOID string
Side Side
// StartLine and EndLine are 1-based line numbers on Side, inclusive.
StartLine int
EndLine int
// HunkIndex and LineIndex point at the anchor line within a specific
// FileDiff (the start of the selection). They are convenient for rendering
// but are always re-derivable from Path+Side+StartLine.
HunkIndex int
LineIndex int
// Anchor captures surrounding content so the comment can be relocated when
// the underlying diff changes (e.g. the PR is updated).
Anchor ContextAnchor
}
Location is a semantic, view-independent position in a diff. It is the canonical anchor for a review comment: it survives terminal resize, context folding, and switching between unified and split layouts, and it maps cleanly onto GitHub's line-oriented review-comment fields (path, side, line, start_line, start_side).
type RelocateResult ¶
type RelocateResult uint8
RelocateResult reports the outcome of trying to map a Location captured against one diff onto a newer diff.
const ( // RelocateExact: the location still resolves at the same place. RelocateExact RelocateResult = iota // RelocateMoved: the location was uniquely re-found elsewhere. RelocateMoved // RelocateOrphaned: no unique match exists; a human must reposition it. RelocateOrphaned )