Documentation
¶
Overview ¶
Package diff parses unified diff text into files, hunks, and lines, and turns a user's hunk and line selections back into a patch or into reconstructed files on disk.
Index ¶
- Constants
- Variables
- func CompareDirectories(leftDir, rightDir string) (string, error)
- func ComputeHunkWordDiffs(hunk *Hunk) map[int]WordDiffResult
- func CountTrailingWhitespace(content string) int
- func GeneratePatch(files []FileChange, selection SelectionState) string
- func GeneratePatchForTag(files []FileChange, selection SelectionState) string
- func GetLeadingWhitespace(content string) string
- func GetSelectedHunksMap(files []FileChange, selectionState interface{ ... }) map[string]map[int]bool
- func GetUnselectedFiles(files []FileChange, selection SelectionState) []string
- func HasTrailingWhitespace(content string) bool
- func IsWhitespaceOnlyChange(oldContent, newContent string) bool
- func RenderWhitespaceSimple(content string, tabWidth int) string
- func SelectAll(files []FileChange, selection interface{ ... })
- type Applier
- type ChangeType
- type DirectorySource
- type FileChange
- type Hunk
- type IntraLineSpan
- type Line
- type LinePair
- type LineType
- type RevisionSource
- type SelectionState
- type SidePair
- type Source
- type SpanType
- type WordDiffResult
Constants ¶
const ( TabChar = '→' SpaceChar = '·' )
Glyphs standing in for whitespace when it is rendered visibly.
Variables ¶
var ErrPathEscapesBase = errors.New("path escapes its base directory")
ErrPathEscapesBase reports a diff path that resolves outside the directory it was joined to.
Functions ¶
func CompareDirectories ¶
CompareDirectories generates a unified diff comparing two directories. Returns git-format diff text suitable for parsing by diff.Parse().
func ComputeHunkWordDiffs ¶
func ComputeHunkWordDiffs(hunk *Hunk) map[int]WordDiffResult
ComputeHunkWordDiffs keys word diffs by hunk line index, storing one pair's result under both of its line indices. A line with no counterpart is absent, so a renderer treats a missing key as "no intra-line highlighting" instead of an error.
func CountTrailingWhitespace ¶
CountTrailingWhitespace counts the trailing spaces and tabs as bytes, so a tab counts once whatever tab width the renderer expands it to.
func GeneratePatch ¶
func GeneratePatch(files []FileChange, selection SelectionState) string
GeneratePatch renders the selected hunks as a unified diff that git apply accepts. A fully selected hunk is copied verbatim, and a partially selected one keeps the selected lines plus three lines of context on each side with recalculated header counts. A file with nothing selected is left out, so an empty selection returns the empty string.
func GeneratePatchForTag ¶
func GeneratePatchForTag(files []FileChange, selection SelectionState) string
GeneratePatchForTag renders the patch for one split tag. The tag is already baked into the selection the caller passes, so the output matches GeneratePatch over that same selection.
func GetLeadingWhitespace ¶
GetLeadingWhitespace extracts the leading whitespace from a string.
func GetSelectedHunksMap ¶
func GetSelectedHunksMap(files []FileChange, selectionState interface { IsHunkSelected(filePath string, hunkIdx int) bool }, ) map[string]map[int]bool
GetSelectedHunksMap is deprecated but kept for backward compatibility.
func GetUnselectedFiles ¶
func GetUnselectedFiles(files []FileChange, selection SelectionState) []string
GetUnselectedFiles returns paths of files with no selections. These files should be restored to their left state.
func HasTrailingWhitespace ¶
HasTrailingWhitespace reports whether content ends in a space or a tab. An empty string does not, and a line that is entirely whitespace does.
func IsWhitespaceOnlyChange ¶
IsWhitespaceOnlyChange returns true if the two strings differ only in whitespace.
func RenderWhitespaceSimple ¶
RenderWhitespaceSimple substitutes whitespace glyphs without any styling, treating trailing whitespace the same as interior whitespace. The result carries no ANSI codes, which suits a caller that needs to slice or measure the output by index.
func SelectAll ¶
func SelectAll(files []FileChange, selection interface { ToggleHunk(filePath string, hunkIdx int) }, )
SelectAll toggles every hunk in every file, which diff-editor mode calls once on load because starting with nothing selected would make a no-op edit discard the whole working copy. It toggles rather than sets, so calling it on an already-full selection clears it.
Types ¶
type Applier ¶
Applier handles writing user selections back to the right directory. For diff-editor mode, jj expects the right directory to contain only the changes the user wants to keep.
func NewApplier ¶
NewApplier reconstructs files in rightDir from the originals in leftDir. Pass the two directories jj hands the diff editor: every path out of the diff is resolved under them and rejected if it escapes, and files are written 0600 under 0750 parents because both trees are temporary.
func (*Applier) ApplySelections ¶
func (a *Applier) ApplySelections(files []FileChange, selection SelectionState) error
ApplySelections reconstructs files in rightDir based on user selections. - Selected hunks: keep the change (content from right) - Unselected hunks: revert the change (content from left).
type ChangeType ¶
type ChangeType int
ChangeType is what a diff header says happened to a file. String returns the one-letter status jj and git print.
const ( ChangeTypeModified ChangeType = iota ChangeTypeAdded ChangeTypeDeleted ChangeTypeRenamed )
Change kinds a file header can describe. ChangeTypeModified is the zero value and the fallback when the header carries no "new file", "deleted file", or "rename from" marker.
func (ChangeType) String ¶
func (ct ChangeType) String() string
type DirectorySource ¶
DirectorySource generates diffs by comparing two directories. Used for diff-editor mode where jj passes $left and $right directories.
func NewDirectorySource ¶
func NewDirectorySource(leftPath, rightPath string) *DirectorySource
NewDirectorySource compares two directory trees, which is how jj invokes a diff editor. Pass jj's $left and $right in that order, because the diff reads left as the old side.
func (*DirectorySource) GetDiff ¶
func (s *DirectorySource) GetDiff() (string, error)
GetDiff walks both trees and diffs every file that differs, so it blocks for as long as the trees are large and belongs in a tea.Cmd rather than in Update.
func (*DirectorySource) GetSourceLabel ¶
func (*DirectorySource) GetSourceLabel() string
GetSourceLabel returns a fixed label, because the directories jj passes are temporary paths that mean nothing to the user.
func (*DirectorySource) SupportsRevisions ¶
func (*DirectorySource) SupportsRevisions() bool
SupportsRevisions reports false, because a diff editor sees two trees and has no revset to resolve against.
type FileChange ¶
type FileChange struct {
Path string
Hunks []Hunk
ChangeType ChangeType
}
FileChange holds one file's hunks in the order the diff lists them. Path is the "b/" side of the diff header, so a renamed file carries its new path.
func Parse ¶
func Parse(diffText string) []FileChange
Parse splits unified diff text into one FileChange per "diff --git" header, dropping any section whose header does not parse. Empty input returns an empty slice rather than nil.
func (*FileChange) AddedLines ¶
func (fc *FileChange) AddedLines() int
AddedLines counts added lines across every hunk, which with DeletedLines gives the +/- pair shown beside a file. A whitespace-only edit still counts on both sides.
func (*FileChange) DeletedLines ¶
func (fc *FileChange) DeletedLines() int
DeletedLines counts deleted lines across every hunk, counting the parsed diff rather than the file, so a line replaced by an edit counts here and in AddedLines.
func (*FileChange) TotalLines ¶
func (fc *FileChange) TotalLines() int
TotalLines counts every line across the file's hunks, context included, so it is the height the file's hunks occupy rather than the size of the change.
type Hunk ¶
Hunk is one @@ section. OldStart and NewStart are 1-based line numbers in the old and new file, and Lines interleaves context, additions, and deletions in diff order.
type IntraLineSpan ¶
IntraLineSpan is a run of one line's content. Start and End are byte offsets into that line's Content with End exclusive, so a span's byte width is not its column width.
type Line ¶
Line is one diff line with the leading +, -, or space stripped from Content. OldLineNum and NewLineNum are 1-based and both are always set, so an addition still records where it falls on the old side.
func ProcessHunkHideWhitespace ¶
ProcessHunkHideWhitespace transforms a hunk to hide whitespace-only changes Returns a new set of lines with whitespace changes handled.
type LinePair ¶
LinePair links a deletion to the addition it is compared against. Both indices are positions in the hunk's Lines slice, and OldLine and NewLine point into that same slice.
func FindLinePairs ¶
FindLinePairs pairs each deletion in a hunk with the addition at the same position in the addition run that follows it, leaving the surplus on the longer side unpaired. The returned pointers alias hunk.Lines, so the hunk must outlive the pairs.
type LineType ¶
type LineType int
LineType marks a diff line as context, an addition, or a deletion. String returns the diff marker, which patch rendering writes back in front of the content.
Line kinds inside a hunk. LineContext is the zero value, so a line the parser cannot classify from its first byte is treated as context and its content is kept whole.
type RevisionSource ¶
RevisionSource generates diffs from jj revisions.
func NewRevisionSource ¶
func NewRevisionSource(client *jj.Client, revision string) *RevisionSource
NewRevisionSource reads diffs from a jj revision through client. The revision is resolved on each GetDiff call, so a revset such as @ follows the working copy as it moves.
func (*RevisionSource) GetDiff ¶
func (s *RevisionSource) GetDiff() (string, error)
GetDiff shells out to jj for the revision's diff, so it blocks and belongs in a tea.Cmd rather than in Update.
func (*RevisionSource) GetSourceLabel ¶
func (s *RevisionSource) GetSourceLabel() string
GetSourceLabel returns the revset the caller asked for, unresolved, which is what the header shows.
func (*RevisionSource) SupportsRevisions ¶
func (*RevisionSource) SupportsRevisions() bool
SupportsRevisions reports true, so interactive mode opens the revision picker.
type SelectionState ¶
type SelectionState interface {
IsHunkSelected(filePath string, hunkIdx int) bool
HasPartialSelection(filePath string, hunkIdx int) bool
IsLineSelected(filePath string, hunkIdx, lineIdx int) bool
}
SelectionState interface for accessing selection state.
type SidePair ¶ added in v0.4.0
SidePair is one row of a two-column diff. Left or Right is nil where that side has no line, which is how an added line sits opposite blank space. LeftIdx and RightIdx index the hunk's Lines and are -1 for the nil side, so a caller can still reach anything keyed by line index.
type Source ¶ added in v0.1.2
type Source interface {
GetDiff() (string, error)
GetSourceLabel() string
SupportsRevisions() bool
}
Source is where a diff comes from, either a jj revision or a pair of directories. GetDiff blocks on external work, so the UI calls it from a tea.Cmd rather than from Update.
type SpanType ¶
type SpanType int
SpanType marks a run inside a line as unchanged, added, or deleted.
type WordDiffResult ¶
type WordDiffResult struct {
OldSpans []IntraLineSpan
NewSpans []IntraLineSpan
}
WordDiffResult holds the spans for one deleted and added line pair. Each side's spans are contiguous and in order, so concatenating their Text rebuilds that side's line.
func ComputeWordDiff ¶
func ComputeWordDiff(oldLine, newLine string) WordDiffResult
ComputeWordDiff diffs two single lines into highlight spans. Pass content only, without the diff marker, because offsets are relative to the start of the content the renderer draws.