1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-08-08 22:36:49 +02:00

Add user config to use hunk mode by default when entering staging view (#4685)

- **PR Description**

As of #4684, hunk mode has become so useful that I prefer it over line
mode now. This PR adds a config that lets you use hunk mode by default
in the staging view.

I'm not enabling this by default yet, although I do think it's the more
useful mode for most people. The biggest issue that I still have with
this is that _if_ you need to switch to line mode for some reason, then
it's very non-obvious how to do that. New users might not find out at
all, and think that lazygit doesn't allow staging individual lines.
This commit is contained in:
Stefan Haller
2025-07-04 19:46:36 +02:00
committed by GitHub
8 changed files with 52 additions and 19 deletions

View File

@ -116,6 +116,9 @@ gui:
# paragraphs of markdown text. # paragraphs of markdown text.
wrapLinesInStagingView: true wrapLinesInStagingView: true
# If true, hunk selection mode will be enabled by default when entering the staging view.
useHunkModeInStagingView: false
# One of 'auto' (default) | 'en' | 'zh-CN' | 'zh-TW' | 'pl' | 'nl' | 'ja' | 'ko' | 'ru' # One of 'auto' (default) | 'en' | 'zh-CN' | 'zh-TW' | 'pl' | 'nl' | 'ja' | 'ko' | 'ru'
language: auto language: auto

View File

@ -107,6 +107,8 @@ type GuiConfig struct {
// makes it much easier to work with diffs that have long lines, e.g. // makes it much easier to work with diffs that have long lines, e.g.
// paragraphs of markdown text. // paragraphs of markdown text.
WrapLinesInStagingView bool `yaml:"wrapLinesInStagingView"` WrapLinesInStagingView bool `yaml:"wrapLinesInStagingView"`
// If true, hunk selection mode will be enabled by default when entering the staging view.
UseHunkModeInStagingView bool `yaml:"useHunkModeInStagingView"`
// One of 'auto' (default) | 'en' | 'zh-CN' | 'zh-TW' | 'pl' | 'nl' | 'ja' | 'ko' | 'ru' // One of 'auto' (default) | 'en' | 'zh-CN' | 'zh-TW' | 'pl' | 'nl' | 'ja' | 'ko' | 'ru'
Language string `yaml:"language" jsonschema:"enum=auto,enum=en,enum=zh-TW,enum=zh-CN,enum=pl,enum=nl,enum=ja,enum=ko,enum=ru"` Language string `yaml:"language" jsonschema:"enum=auto,enum=en,enum=zh-TW,enum=zh-CN,enum=pl,enum=nl,enum=ja,enum=ko,enum=ru"`
// Format used when displaying time e.g. commit time. // Format used when displaying time e.g. commit time.
@ -745,6 +747,7 @@ func GetDefaultConfig() *UserConfig {
MainPanelSplitMode: "flexible", MainPanelSplitMode: "flexible",
EnlargedSideViewLocation: "left", EnlargedSideViewLocation: "left",
WrapLinesInStagingView: true, WrapLinesInStagingView: true,
UseHunkModeInStagingView: false,
Language: "auto", Language: "auto",
TimeFormat: "02 Jan 06", TimeFormat: "02 Jan 06",
ShortTimeFormat: time.Kitchen, ShortTimeFormat: time.Kitchen,

View File

@ -84,7 +84,7 @@ func (self *PatchBuildingHelper) RefreshPatchBuildingPanel(opts types.OnFocusOpt
oldState := context.GetState() oldState := context.GetState()
state := patch_exploring.NewState(diff, selectedLineIdx, context.GetView(), oldState) state := patch_exploring.NewState(diff, selectedLineIdx, context.GetView(), oldState, self.c.UserConfig().Gui.UseHunkModeInStagingView)
context.SetState(state) context.SetState(state)
if state == nil { if state == nil {
self.Escape() self.Escape()

View File

@ -62,12 +62,13 @@ func (self *StagingHelper) RefreshStagingPanel(focusOpts types.OnFocusOpts) {
mainContext.GetMutex().Lock() mainContext.GetMutex().Lock()
secondaryContext.GetMutex().Lock() secondaryContext.GetMutex().Lock()
hunkMode := self.c.UserConfig().Gui.UseHunkModeInStagingView
mainContext.SetState( mainContext.SetState(
patch_exploring.NewState(mainDiff, mainSelectedLineIdx, mainContext.GetView(), mainContext.GetState()), patch_exploring.NewState(mainDiff, mainSelectedLineIdx, mainContext.GetView(), mainContext.GetState(), hunkMode),
) )
secondaryContext.SetState( secondaryContext.SetState(
patch_exploring.NewState(secondaryDiff, secondarySelectedLineIdx, secondaryContext.GetView(), secondaryContext.GetState()), patch_exploring.NewState(secondaryDiff, secondarySelectedLineIdx, secondaryContext.GetView(), secondaryContext.GetState(), hunkMode),
) )
mainState := mainContext.GetState() mainState := mainContext.GetState()

View File

@ -134,7 +134,7 @@ func (self *PatchBuildingController) toggleSelection() error {
state := self.context().GetState() state := self.context().GetState()
// Get added/deleted lines in the selected patch range // Get added/deleted lines in the selected patch range
lineIndicesToToggle := state.ChangeLinesInSelectedPatchRange() lineIndicesToToggle := state.LineIndicesOfAddedOrDeletedLinesInSelectedPatchRange()
if len(lineIndicesToToggle) == 0 { if len(lineIndicesToToggle) == 0 {
// Only context lines or header lines selected, so nothing to do // Only context lines or header lines selected, so nothing to do
return nil return nil
@ -170,7 +170,7 @@ func (self *PatchBuildingController) Escape() error {
context := self.c.Contexts().CustomPatchBuilder context := self.c.Contexts().CustomPatchBuilder
state := context.GetState() state := context.GetState()
if state.SelectingRange() || state.SelectingHunk() { if state.SelectingRange() || state.SelectingHunkEnabledByUser() {
state.SetLineSelectMode() state.SetLineSelectMode()
self.c.PostRefreshUpdate(context) self.c.PostRefreshUpdate(context)
return nil return nil

View File

@ -168,7 +168,7 @@ func (self *StagingController) EditFile() error {
} }
func (self *StagingController) Escape() error { func (self *StagingController) Escape() error {
if self.context.GetState().SelectingRange() || self.context.GetState().SelectingHunk() { if self.context.GetState().SelectingRange() || self.context.GetState().SelectingHunkEnabledByUser() {
self.context.GetState().SetLineSelectMode() self.context.GetState().SetLineSelectMode()
self.c.PostRefreshUpdate(self.context) self.c.PostRefreshUpdate(self.context)
return nil return nil

View File

@ -28,6 +28,12 @@ type State struct {
viewLineIndices []int viewLineIndices []int
// Array of indices of the original patch lines indexed by a wrapped view line index // Array of indices of the original patch lines indexed by a wrapped view line index
patchLineIndices []int patchLineIndices []int
// whether the user has switched to hunk mode manually; if hunk mode is on
// but this is false, then hunk mode was enabled because the config makes it
// on by default.
// this makes a difference for whether we want to escape out of hunk mode
userEnabledHunkMode bool
} }
// these represent what select mode we're in // these represent what select mode we're in
@ -39,7 +45,7 @@ const (
HUNK HUNK
) )
func NewState(diff string, selectedLineIdx int, view *gocui.View, oldState *State) *State { func NewState(diff string, selectedLineIdx int, view *gocui.View, oldState *State, useHunkModeByDefault bool) *State {
if oldState != nil && diff == oldState.diff && selectedLineIdx == -1 { if oldState != nil && diff == oldState.diff && selectedLineIdx == -1 {
// if we're here then we can return the old state. If selectedLineIdx was not -1 // if we're here then we can return the old state. If selectedLineIdx was not -1
// then that would mean we were trying to click and potentially drag a range, which // then that would mean we were trying to click and potentially drag a range, which
@ -61,6 +67,15 @@ func NewState(diff string, selectedLineIdx int, view *gocui.View, oldState *Stat
} }
selectMode := LINE selectMode := LINE
if useHunkModeByDefault {
selectMode = HUNK
}
userEnabledHunkMode := false
if oldState != nil {
userEnabledHunkMode = oldState.userEnabledHunkMode
}
// if we have clicked from the outside to focus the main view we'll pass in a non-negative line index so that we can instantly select that line // if we have clicked from the outside to focus the main view we'll pass in a non-negative line index so that we can instantly select that line
if selectedLineIdx >= 0 { if selectedLineIdx >= 0 {
// Clamp to the number of wrapped view lines; index might be out of // Clamp to the number of wrapped view lines; index might be out of
@ -70,9 +85,9 @@ func NewState(diff string, selectedLineIdx int, view *gocui.View, oldState *Stat
selectMode = RANGE selectMode = RANGE
rangeStartLineIdx = selectedLineIdx rangeStartLineIdx = selectedLineIdx
} else if oldState != nil { } else if oldState != nil {
// if we previously had a selectMode of RANGE, we want that to now be line again // if we previously had a selectMode of RANGE, we want that to now be line again (or hunk, if that's the default)
if oldState.selectMode == HUNK { if oldState.selectMode != RANGE {
selectMode = HUNK selectMode = oldState.selectMode
} }
selectedLineIdx = viewLineIndices[patch.GetNextChangeIdx(oldState.patchLineIndices[oldState.selectedLineIdx])] selectedLineIdx = viewLineIndices[patch.GetNextChangeIdx(oldState.patchLineIndices[oldState.selectedLineIdx])]
} else { } else {
@ -80,14 +95,15 @@ func NewState(diff string, selectedLineIdx int, view *gocui.View, oldState *Stat
} }
return &State{ return &State{
patch: patch, patch: patch,
selectedLineIdx: selectedLineIdx, selectedLineIdx: selectedLineIdx,
selectMode: selectMode, selectMode: selectMode,
rangeStartLineIdx: rangeStartLineIdx, rangeStartLineIdx: rangeStartLineIdx,
rangeIsSticky: false, rangeIsSticky: false,
diff: diff, diff: diff,
viewLineIndices: viewLineIndices, viewLineIndices: viewLineIndices,
patchLineIndices: patchLineIndices, patchLineIndices: patchLineIndices,
userEnabledHunkMode: userEnabledHunkMode,
} }
} }
@ -125,6 +141,7 @@ func (s *State) ToggleSelectHunk() {
s.selectMode = LINE s.selectMode = LINE
} else { } else {
s.selectMode = HUNK s.selectMode = HUNK
s.userEnabledHunkMode = true
// If we are not currently on a change line, select the next one (or the // If we are not currently on a change line, select the next one (or the
// previous one if there is no next one): // previous one if there is no next one):
@ -155,6 +172,10 @@ func (s *State) SelectingHunk() bool {
return s.selectMode == HUNK return s.selectMode == HUNK
} }
func (s *State) SelectingHunkEnabledByUser() bool {
return s.selectMode == HUNK && s.userEnabledHunkMode
}
func (s *State) SelectingRange() bool { func (s *State) SelectingRange() bool {
return s.selectMode == RANGE && (s.rangeIsSticky || s.rangeStartLineIdx != s.selectedLineIdx) return s.selectMode == RANGE && (s.rangeIsSticky || s.rangeStartLineIdx != s.selectedLineIdx)
} }
@ -335,7 +356,7 @@ func (s *State) SelectedPatchRange() (int, int) {
} }
// Returns the line indices of the selected patch range that are changes (i.e. additions or deletions) // Returns the line indices of the selected patch range that are changes (i.e. additions or deletions)
func (s *State) ChangeLinesInSelectedPatchRange() []int { func (s *State) LineIndicesOfAddedOrDeletedLinesInSelectedPatchRange() []int {
viewStart, viewEnd := s.SelectedViewRange() viewStart, viewEnd := s.SelectedViewRange()
patchStart, patchEnd := s.patchLineIndices[viewStart], s.patchLineIndices[viewEnd] patchStart, patchEnd := s.patchLineIndices[viewStart], s.patchLineIndices[viewEnd]
lines := s.patch.Lines() lines := s.patch.Lines()

View File

@ -530,6 +530,11 @@
"description": "If true, wrap lines in the staging view to the width of the view. This\nmakes it much easier to work with diffs that have long lines, e.g.\nparagraphs of markdown text.", "description": "If true, wrap lines in the staging view to the width of the view. This\nmakes it much easier to work with diffs that have long lines, e.g.\nparagraphs of markdown text.",
"default": true "default": true
}, },
"useHunkModeInStagingView": {
"type": "boolean",
"description": "If true, hunk selection mode will be enabled by default when entering the staging view.",
"default": false
},
"language": { "language": {
"type": "string", "type": "string",
"enum": [ "enum": [