diffview

package
v0.1.22 Latest Latest
Warning

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

Go to latest
Published: May 15, 2026 License: MIT Imports: 21 Imported by: 0

Documentation

Overview

Package diffview renders unified diffs from `gh pr diff` in a TUI.

Index

Constants

View Source
const PlanNudgeMessage = `This PR is too large for me to review without a clearer implementation plan. ` +
	`Can you break it into smaller scoped units, or add a summary of what each part does and ` +
	`why it's structured this way? Happy to review after that.

(via [chainrail](https://github.com/brayschurman/chainrail))`

PlanNudgeMessage is the templated comment chainrail posts when the reviewer presses P on a PR with no plan. Copy-paste from the GitHub agent-PR review article.

Variables

This section is empty.

Functions

func IsCIRelated added in v0.1.17

func IsCIRelated(path string) bool

IsCIRelated reports whether a path is one of the file kinds whose changes affect CI behavior (workflows, test/build configs, coverage).

Types

type CIRisk added in v0.1.17

type CIRisk int

CIRisk classifies how a file affects CI confidence. Used to pin risky files to the top of the sidebar and to gate 100% progress.

const (
	CIRiskNone      CIRisk = iota
	CIRiskConfig           // CI/test/coverage config touched but no obvious weakening
	CIRiskWeakening        // explicit weakening signal in the diff
)

type CISignal added in v0.1.17

type CISignal struct {
	Risk    CIRisk
	Reasons []string // human-readable summary, e.g. "test marked .skip" or "continue-on-error added"
}

CISignal is a per-file detection result.

func DetectCIRisk added in v0.1.17

func DetectCIRisk(path string, lines []Line) CISignal

DetectCIRisk inspects a file's path and diff body for CI-related changes and returns a CISignal. Returns CIRiskNone for files that don't match any CI-relevant path pattern.

Content-level weakening signals are only inspected on workflow files (where they're unambiguous). For other config files we conservatively report CIRiskConfig — the reviewer's judgment is the gate.

type File

type File struct {
	Path  string
	Adds  int
	Dels  int
	Lines []Line
}

File is one file's worth of changes from a unified diff.

func Parse

func Parse(diff string) []File

Parse splits a unified diff (as produced by `gh pr diff --patch` or `git diff`) into per-file blocks. Adds/Dels are counted only for content lines (not file/hunk headers). The parser is intentionally lenient: any content that doesn't match an expected prefix is treated as context.

type Highlighter

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

Highlighter colorizes diff content lines using chroma based on file extension. Lexers and the formatter are looked up once per extension and reused. When NO_COLOR is set or the lexer is unknown, Highlight returns the input unchanged.

func NewHighlighter

func NewHighlighter() *Highlighter

func (*Highlighter) Highlight

func (h *Highlighter) Highlight(path, src string) string

Highlight returns the colorized form of src for the lexer matching path's extension. Falls back to src on any error or when disabled.

func (*Highlighter) HighlightWithBg added in v0.1.14

func (h *Highlighter) HighlightWithBg(path, src string, bg lipgloss.Color) string

HighlightWithBg tokenises src with the lexer for path and renders each token with both its syntax foreground AND the given diff background. This gives the hunk/diffs.com look where chroma's colors stay vivid on top of a soft +/- tint, without relying on fragile mid-stream ANSI rewriting.

Returns src wrapped in a single bg style if no lexer matches or highlighting is disabled. The caller is responsible for any extra padding needed to reach a target pane width.

type Line

type Line struct {
	Kind LineKind
	Text string
}

Line is one rendered line of the diff (file header, hunk header, or content). Kind drives coloring; Text is the raw text without trailing newline.

type LineKind

type LineKind int
const (
	LineContext   LineKind = iota // " foo"
	LineAdd                       // "+foo"
	LineDel                       // "-foo"
	LineFile                      // "diff --git ..." / "--- a/" / "+++ b/"
	LineHunk                      // "@@ -1,3 +1,4 @@"
	LineNoNewLine                 // "\ No newline at end of file"
)

type Model

type Model struct {
	Title string // e.g. "#687 wall drawing UX"
	Files []File

	// BlobByPath maps file path -> blob SHA for the PR's current head, used
	// to detect "changed since you checked" against persisted review state.
	BlobByPath map[string]string

	// CISignals records the per-file CI risk classification computed once
	// at load time. The signal still drives the 100%-progress hard-block
	// (a typed waiver via shift+W is required when CI files are unreviewed),
	// but is no longer surfaced as a sidebar marker — that was too noisy
	// without an AI-based discriminator. Kept available for a future
	// `cn lint-pr` CI-mode subcommand.
	CISignals map[string]CISignal

	// PRBody is the PR description text — used by the plan detector.
	PRBody string
	// PlanSignal is the verdict from DetectPlan on PRBody.
	PlanSignal PlanSignal
	// PRNumber for posting the nudge comment.
	PRNumber int
	// PlanNudger sends the nudge comment when the reviewer presses P.
	// Optional; nil disables the nudge.
	PlanNudger func(number int, body string) error

	// ReviewState is the loaded per-file checklist for this PR. Optional;
	// when nil, review-tracking UI is hidden entirely. The owner+repo+number
	// fields are kept here so Save can write back to the right key.
	ReviewState *reviewstate.PRState
	ReviewStore *reviewstate.Store
	RepoOwner   string
	RepoName    string
	// contains filtered or unexported fields
}

Model is the bubbletea model for the PR diff viewer.

func New

func New(title string, files []File) Model

func (Model) Init

func (m Model) Init() tea.Cmd

func (Model) Update

func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd)

func (Model) View

func (m Model) View() string

type PlanSeverity added in v0.1.20

type PlanSeverity int

PlanSeverity classifies how much structure / scope the PR description has. "Plan" here means: does the reviewer have enough context to scope a review, or is the PR a "just look at the diff" wall of agent output?

const (
	PlanPresent PlanSeverity = iota // structured body, multiple sections / bullets / >200 chars
	PlanThin                        // 50–200 chars, no structure — usable but minimal
	PlanMissing                     // empty or <50 chars
)

type PlanSignal added in v0.1.20

type PlanSignal struct {
	Severity   PlanSeverity
	Chars      int
	HasHeading bool
	HasBullets bool
	HasCode    bool
}

PlanSignal captures the verdict on a PR body.

func DetectPlan added in v0.1.20

func DetectPlan(body string) PlanSignal

DetectPlan inspects a PR body and returns a structured verdict.

Jump to

Keyboard shortcuts

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