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

model

package
v0.1.3 Latest Latest
Warning

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

Go to latest
Published: Jul 30, 2026 License: MIT Imports: 20 Imported by: 0

Documentation

Overview

Package model is the Bubble Tea root: it owns the diff, the selection, and every child component, and routes each key to the handler for the current mode.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func KeyPress

func KeyPress(key rune) tea.KeyMsg

KeyPress builds the message Bubble Tea sends for a printable key.

func SpecialKey

func SpecialKey(keyType tea.KeyType) tea.KeyMsg

SpecialKey builds the message Bubble Tea sends for a named key such as tea.KeyEsc.

func TestChanges

func TestChanges() []diff.FileChange

TestChanges returns three file changes covering the modified, added, and deleted paths.

Types

type Assertion added in v0.1.2

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

Assertion reads unexported Model state, which is why it lives in the package rather than a test file. Every check reports through t.Errorf and returns the receiver, so a chain runs to the end and reports every mismatch instead of stopping at the first.

func Assert

func Assert(t *testing.T, m Model) *Assertion

Assert opens a chain of assertions over a snapshot of m. Later mutations of m are not observed.

func (*Assertion) FileListFilterModeDisabled added in v0.1.2

func (a *Assertion) FileListFilterModeDisabled() *Assertion

FileListFilterModeDisabled checks that the file list has handed key handling back to the model.

func (*Assertion) FileListFilterModeEnabled added in v0.1.2

func (a *Assertion) FileListFilterModeEnabled() *Assertion

FileListFilterModeEnabled checks that the file list is capturing keys for its inline filter.

func (*Assertion) FocusedPanelIs added in v0.1.2

func (a *Assertion) FocusedPanelIs(panel FocusedPanel) *Assertion

FocusedPanelIs checks which panel receives navigation keys.

func (*Assertion) HasChanges added in v0.1.2

func (a *Assertion) HasChanges(count int) *Assertion

HasChanges checks how many file changes are loaded, ignoring any file-list filter.

func (*Assertion) HasDestination added in v0.1.2

func (a *Assertion) HasDestination(dest string) *Assertion

HasDestination checks the revset the model will move changes to, which is empty until one is picked.

func (*Assertion) HasError added in v0.1.2

func (a *Assertion) HasError() *Assertion

HasError checks that the model is holding an error for the view to render.

func (*Assertion) HasHunkNotSelected added in v0.1.2

func (a *Assertion) HasHunkNotSelected(filePath string, hunkIdx int) *Assertion

HasHunkNotSelected checks that no part of the hunk is marked for apply.

func (*Assertion) HasHunkSelected added in v0.1.2

func (a *Assertion) HasHunkSelected(filePath string, hunkIdx int) *Assertion

HasHunkSelected checks that the hunk is marked for apply. A partially selected hunk still counts.

func (*Assertion) HasLineCursor added in v0.1.2

func (a *Assertion) HasLineCursor(position int) *Assertion

HasLineCursor checks the line cursor as an index into the rendered rows of the selected hunk.

func (*Assertion) HasNoError added in v0.1.2

func (a *Assertion) HasNoError() *Assertion

HasNoError checks that no error is pending, and prints the error it found when one is.

func (*Assertion) HasSelectedFile added in v0.1.2

func (a *Assertion) HasSelectedFile(idx int) *Assertion

HasSelectedFile checks the file-list cursor, which is an index into the unfiltered change list.

func (*Assertion) HasSelectedHunk added in v0.1.2

func (a *Assertion) HasSelectedHunk(idx int) *Assertion

HasSelectedHunk checks the hunk cursor, which is an index within the currently selected file only.

func (*Assertion) HelpIsNotVisible added in v0.1.2

func (a *Assertion) HelpIsNotVisible() *Assertion

HelpIsNotVisible checks that the help overlay is down.

func (*Assertion) HelpIsVisible added in v0.1.2

func (a *Assertion) HelpIsVisible() *Assertion

HelpIsVisible checks that the help overlay is up.

func (*Assertion) IsInVisualMode added in v0.1.2

func (a *Assertion) IsInVisualMode() *Assertion

IsInVisualMode checks that line-range selection is active.

func (*Assertion) IsNotInVisualMode added in v0.1.2

func (a *Assertion) IsNotInVisualMode() *Assertion

IsNotInVisualMode checks that line-range selection is off, so space acts on the whole hunk.

func (*Assertion) ModeIs added in v0.1.2

func (a *Assertion) ModeIs(mode OperatingMode) *Assertion

ModeIs checks the operating mode, which is fixed at construction and never changes at runtime.

func (*Assertion) NoModalsVisible added in v0.1.2

func (a *Assertion) NoModalsVisible() *Assertion

NoModalsVisible checks every overlay and the file-list filter at once, reporting each one that is up.

func (*Assertion) SearchIsNotVisible added in v0.1.2

func (a *Assertion) SearchIsNotVisible() *Assertion

SearchIsNotVisible checks that the search prompt is down.

func (*Assertion) SearchIsVisible added in v0.1.2

func (a *Assertion) SearchIsVisible() *Assertion

SearchIsVisible checks that the search prompt is up, which is separate from the file-list filter.

type DestinationSpec

type DestinationSpec struct {
	Type        DestinationType
	ChangeID    string
	Description string
}

DestinationSpec is where one tag's hunks land. ChangeID is empty for DestNewCommit, where Description becomes the message of the commit that gets created.

type DestinationType

type DestinationType int

DestinationType separates a split target that already exists from one that will be created.

const (
	DestExistingRevision DestinationType = iota
	DestNewCommit
)

Destinations a tag can be sent to. DestExistingRevision is the zero value, so a DestinationSpec that was never filled in reads as targeting an existing revision.

type FileSelection

type FileSelection struct {
	Hunks map[int]*HunkSelection
}

FileSelection holds one file's selected hunks, keyed by the hunk's index in the parsed file. The indices go stale when the diff is reloaded.

type FocusedPanel

type FocusedPanel int

FocusedPanel is which of the two panes takes navigation keys.

const (
	PanelFileList FocusedPanel = iota
	PanelDiffView
)

The two panes Tab switches between. PanelFileList is the zero value, so a fresh model starts on the file list.

type HunkSelection

type HunkSelection struct {
	WholeHunk     bool
	SelectedLines map[int]bool
}

HunkSelection is one hunk's selection. WholeHunk wins over SelectedLines, and selecting the whole hunk discards the per-line set, so the two are never both meaningful.

type Model

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

Model is the whole application state. Bubble Tea passes it by value, so Update returns the updated copy and mutating a Model a handler received has no effect unless that copy is returned.

func NewModel

func NewModel(
	client *jj.Client,
	source, destination string,
	mode OperatingMode,
	cfg config.Config,
) (Model, error)

NewModel builds a model reading its diff from a jj revision.

func NewModelWithSource

func NewModelWithSource(
	source diff.Source,
	client *jj.Client,
	destination string,
	mode OperatingMode,
	cfg config.Config,
) (Model, error)

NewModelWithSource builds a model over any diff source, which is how diff-editor mode supplies two directories instead of a revision. The client is still needed for the write paths and may only be nil when nothing will be applied.

func NewTestModel

func NewTestModel(t *testing.T, mode OperatingMode) Model

NewTestModel builds a Model backed by a jj client rooted at a fresh temp dir.

func Update

func Update(t *testing.T, m Model, msg tea.Msg) Model

Update runs one Update round and asserts the result is still a Model, discarding the command.

func (Model) Init

func (m Model) Init() tea.Cmd

Init starts the first diff load. Nothing is rendered until it returns and a window size arrives.

func (Model) Update

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

Update handles one message and returns the model to use next. The concrete type is always Model, so callers chaining updates can assert it.

func (Model) View

func (m Model) View() string

func (Model) WithChanges

func (m Model) WithChanges(changes []diff.FileChange) Model

WithChanges loads changes and selects the first file, mirroring what a real diff load does.

func (Model) WithDestination

func (m Model) WithDestination(dest string) Model

WithDestination sets the move destination without going through the picker.

type MultiSplitState

type MultiSplitState struct {
	Active       bool
	Selections   map[SplitTag]*SelectionState
	Destinations map[SplitTag]*DestinationSpec
	CurrentTag   SplitTag
}

MultiSplitState is an in-progress multi-way split: one selection and one destination per tag. A tag with a selection but no destination is incomplete and blocks the split from being applied.

func NewMultiSplitState

func NewMultiSplitState() *MultiSplitState

NewMultiSplitState returns an inactive split with 'A' as the current tag.

type OperatingMode

type OperatingMode int

OperatingMode is which of the three entry points the app was started for, which decides what the selection means and what applying it does.

const (
	ModeBrowse OperatingMode = iota
	ModeInteractive
	ModeDiffEditor
)

The three ways the app is launched. ModeBrowse is read-only, ModeInteractive moves selected hunks into another revision, and ModeDiffEditor writes the selection back into the trees jj passed.

type SelectionState

type SelectionState struct {
	Files map[string]*FileSelection
}

SelectionState is what the user has picked across every file, keyed by the diff's path. Build it with NewSelectionState, because the mutators assume the map exists.

func NewSelectionState

func NewSelectionState() *SelectionState

NewSelectionState returns an empty selection.

func (*SelectionState) HasPartialSelection

func (s *SelectionState) HasPartialSelection(filePath string, hunkIdx int) bool

HasPartialSelection reports whether a hunk has lines picked without being selected as a whole, which is what the renderer draws the partial marker for.

func (*SelectionState) IsHunkSelected

func (s *SelectionState) IsHunkSelected(filePath string, hunkIdx int) bool

IsHunkSelected reports whether the whole hunk is selected, which is false for a hunk that only has individual lines picked.

func (*SelectionState) IsLineSelected

func (s *SelectionState) IsLineSelected(filePath string, hunkIdx, lineIdx int) bool

IsLineSelected reports whether one line is selected, which is true for every line of a hunk selected as a whole.

func (*SelectionState) SelectLineRange

func (s *SelectionState) SelectLineRange(filePath string, hunkIdx, startLine, endLine int)

SelectLineRange selects an inclusive range of lines, accepting the bounds in either order. It clears whole-hunk selection, and it only adds, so lines already selected outside the range stay.

func (*SelectionState) ToggleHunk

func (s *SelectionState) ToggleHunk(filePath string, hunkIdx int)

ToggleHunk flips whole-hunk selection, creating the file and hunk entries as needed. Selecting a hunk discards any lines picked inside it, so a toggle out and back in loses the line selection.

func (*SelectionState) ToggleLine

func (s *SelectionState) ToggleLine(filePath string, hunkIdx, lineIdx int)

ToggleLine flips one line's selection. It does nothing while the hunk is selected as a whole, because that state has no per-line detail to change.

type SplitTag

type SplitTag rune

SplitTag is the single character a hunk carries while a multi-way split is being assembled. Tags are handed out from 'A' upward.

Jump to

Keyboard shortcuts

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