mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-04-27 12:32:37 +02:00
Add gui.tabWidth config (#4291)
- **PR Description** This adds a config `gui.tabWidth` that can be used to change the tab width from the default of 4 to a different number. It affects everything that's shown in the main view, but is probably mostly relevant for diffs. When using a pager, the pager needs to be configured separately for the same tab width. This may often be different for different repos, so it may make sense to put it in a repo-local config file. In some cases this may still not be flexible enough, e.g. in multi-language projects that use different tab widths for the different file types; but it's better than before and was easy to do... Addresses #4290.
This commit is contained in:
commit
52b1c42d38
@ -47,6 +47,10 @@ gui:
|
||||
# One of: 'margin' (default) | 'jump'
|
||||
scrollOffBehavior: margin
|
||||
|
||||
# The number of spaces per tab; used for everything that's shown in the main view, but probably mostly relevant for diffs.
|
||||
# Note that when using a pager, the pager has its own tab width setting, so you need to pass it separately in the pager command.
|
||||
tabWidth: 4
|
||||
|
||||
# If true, capture mouse events.
|
||||
# When mouse events are captured, it's a little harder to select text: e.g. requiring you to hold the option key when on macOS.
|
||||
mouseEvents: true
|
||||
|
2
go.mod
2
go.mod
@ -16,7 +16,7 @@ require (
|
||||
github.com/integrii/flaggy v1.4.0
|
||||
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68
|
||||
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20250210123912-aba68ae65951
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20250220081214-b376cb0857ac
|
||||
github.com/jesseduffield/kill v0.0.0-20250101124109-e216ddbe133a
|
||||
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5
|
||||
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e
|
||||
|
4
go.sum
4
go.sum
@ -188,8 +188,8 @@ github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68 h1:EQP2Tv8T
|
||||
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68/go.mod h1:+LLj9/WUPAP8LqCchs7P+7X0R98HiFujVFANdNaxhGk=
|
||||
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d h1:bO+OmbreIv91rCe8NmscRwhFSqkDJtzWCPV4Y+SQuXE=
|
||||
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d/go.mod h1:nGNEErzf+NRznT+N2SWqmHnDnF9aLgANB1CUNEan09o=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20250210123912-aba68ae65951 h1:7/3M0yosAM9/aLAjTfzSJWhsWjT860ZVe4T76RPwE2k=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20250210123912-aba68ae65951/go.mod h1:sLIyZ2J42R6idGdtemZzsiR3xY5EF0KsvYEGh3dQv3s=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20250220081214-b376cb0857ac h1:vUNTiVEB9Bz16pTJ5kNgb/1HhnWdSA1P0GfFLUJeITI=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20250220081214-b376cb0857ac/go.mod h1:sLIyZ2J42R6idGdtemZzsiR3xY5EF0KsvYEGh3dQv3s=
|
||||
github.com/jesseduffield/kill v0.0.0-20250101124109-e216ddbe133a h1:UDeJ3EBk04bXDLOPvuqM3on8HvyJfISw0+UMqW+0a4g=
|
||||
github.com/jesseduffield/kill v0.0.0-20250101124109-e216ddbe133a/go.mod h1:FSWDLKT0NQpntbDd1H3lbz51fhCVlMzy/J0S6nM727Q=
|
||||
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5 h1:CDuQmfOjAtb1Gms6a1p5L2P8RhbLUq5t8aL7PiQd2uY=
|
||||
|
@ -64,6 +64,9 @@ type GuiConfig struct {
|
||||
ScrollOffMargin int `yaml:"scrollOffMargin"`
|
||||
// One of: 'margin' (default) | 'jump'
|
||||
ScrollOffBehavior string `yaml:"scrollOffBehavior"`
|
||||
// The number of spaces per tab; used for everything that's shown in the main view, but probably mostly relevant for diffs.
|
||||
// Note that when using a pager, the pager has its own tab width setting, so you need to pass it separately in the pager command.
|
||||
TabWidth int `yaml:"tabWidth" jsonschema:"minimum=1"`
|
||||
// If true, capture mouse events.
|
||||
// When mouse events are captured, it's a little harder to select text: e.g. requiring you to hold the option key when on macOS.
|
||||
MouseEvents bool `yaml:"mouseEvents"`
|
||||
@ -693,6 +696,7 @@ func GetDefaultConfig() *UserConfig {
|
||||
ScrollPastBottom: true,
|
||||
ScrollOffMargin: 2,
|
||||
ScrollOffBehavior: "margin",
|
||||
TabWidth: 4,
|
||||
MouseEvents: true,
|
||||
SkipDiscardChangeWarning: false,
|
||||
SkipStashWarning: false,
|
||||
|
@ -56,8 +56,8 @@ func (self *ConfirmationHelper) DeactivateConfirmationPrompt() {
|
||||
self.clearConfirmationViewKeyBindings()
|
||||
}
|
||||
|
||||
func getMessageHeight(wrap bool, editable bool, message string, width int) int {
|
||||
wrappedLines, _, _ := utils.WrapViewLinesToWidth(wrap, editable, message, width)
|
||||
func getMessageHeight(wrap bool, editable bool, message string, width int, tabWidth int) int {
|
||||
wrappedLines, _, _ := utils.WrapViewLinesToWidth(wrap, editable, message, width, tabWidth)
|
||||
return len(wrappedLines)
|
||||
}
|
||||
|
||||
@ -265,7 +265,7 @@ func (self *ConfirmationHelper) resizeMenu(parentPopupContext types.Context) {
|
||||
if selectedItem != nil {
|
||||
tooltip = self.TooltipForMenuItem(selectedItem)
|
||||
}
|
||||
tooltipHeight := getMessageHeight(true, false, tooltip, contentWidth) + 2 // plus 2 for the frame
|
||||
tooltipHeight := getMessageHeight(true, false, tooltip, contentWidth, self.c.Views().Menu.TabWidth) + 2 // plus 2 for the frame
|
||||
_, _ = self.c.GocuiGui().SetView(self.c.Views().Tooltip.Name(), x0, tooltipTop, x1, tooltipTop+tooltipHeight-1, 0)
|
||||
}
|
||||
|
||||
@ -276,7 +276,7 @@ func (self *ConfirmationHelper) layoutMenuPrompt(contentWidth int) int {
|
||||
var promptLines []string
|
||||
prompt := self.c.Contexts().Menu.GetPrompt()
|
||||
if len(prompt) > 0 {
|
||||
promptLines, _, _ = utils.WrapViewLinesToWidth(true, false, prompt, contentWidth)
|
||||
promptLines, _, _ = utils.WrapViewLinesToWidth(true, false, prompt, contentWidth, self.c.Views().Menu.TabWidth)
|
||||
promptLines = append(promptLines, "")
|
||||
}
|
||||
self.c.Contexts().Menu.SetPromptLines(promptLines)
|
||||
@ -305,17 +305,18 @@ func (self *ConfirmationHelper) resizeConfirmationPanel(parentPopupContext types
|
||||
}
|
||||
panelWidth := self.getPopupPanelWidth()
|
||||
contentWidth := panelWidth - 2 // minus 2 for the frame
|
||||
prompt := self.c.Views().Confirmation.Buffer()
|
||||
confirmationView := self.c.Views().Confirmation
|
||||
prompt := confirmationView.Buffer()
|
||||
wrap := true
|
||||
editable := self.c.Views().Confirmation.Editable
|
||||
editable := confirmationView.Editable
|
||||
if editable {
|
||||
prompt = self.c.Views().Confirmation.TextArea.GetContent()
|
||||
prompt = confirmationView.TextArea.GetContent()
|
||||
wrap = false
|
||||
}
|
||||
panelHeight := getMessageHeight(wrap, editable, prompt, contentWidth) + suggestionsViewHeight
|
||||
panelHeight := getMessageHeight(wrap, editable, prompt, contentWidth, confirmationView.TabWidth) + suggestionsViewHeight
|
||||
x0, y0, x1, y1 := self.getPopupPanelDimensionsAux(panelWidth, panelHeight, parentPopupContext)
|
||||
confirmationViewBottom := y1 - suggestionsViewHeight
|
||||
_, _ = self.c.GocuiGui().SetView(self.c.Views().Confirmation.Name(), x0, y0, x1, confirmationViewBottom, 0)
|
||||
_, _ = self.c.GocuiGui().SetView(confirmationView.Name(), x0, y0, x1, confirmationViewBottom, 0)
|
||||
|
||||
suggestionsViewTop := confirmationViewBottom + 1
|
||||
_, _ = self.c.GocuiGui().SetView(self.c.Views().Suggestions.Name(), x0, suggestionsViewTop, x1, suggestionsViewTop+suggestionsViewHeight, 0)
|
||||
@ -325,7 +326,7 @@ func (self *ConfirmationHelper) ResizeCommitMessagePanels(parentPopupContext typ
|
||||
panelWidth := self.getPopupPanelWidth()
|
||||
content := self.c.Views().CommitDescription.TextArea.GetContent()
|
||||
summaryViewHeight := 3
|
||||
panelHeight := getMessageHeight(false, true, content, panelWidth)
|
||||
panelHeight := getMessageHeight(false, true, content, panelWidth, self.c.Views().CommitDescription.TabWidth)
|
||||
minHeight := 7
|
||||
if panelHeight < minHeight {
|
||||
panelHeight = minHeight
|
||||
|
@ -323,6 +323,6 @@ func (s *State) CalculateOrigin(currentOrigin int, bufferHeight int, numLines in
|
||||
|
||||
func wrapPatchLines(diff string, view *gocui.View) ([]int, []int) {
|
||||
_, viewLineIndices, patchLineIndices := utils.WrapViewLinesToWidth(
|
||||
view.Wrap, view.Editable, strings.TrimSuffix(diff, "\n"), view.InnerWidth())
|
||||
view.Wrap, view.Editable, strings.TrimSuffix(diff, "\n"), view.InnerWidth(), view.TabWidth)
|
||||
return viewLineIndices, patchLineIndices
|
||||
}
|
||||
|
@ -203,6 +203,7 @@ func (gui *Gui) configureViewProperties() {
|
||||
|
||||
for _, view := range []*gocui.View{gui.Views.Main, gui.Views.Secondary, gui.Views.Staging, gui.Views.StagingSecondary, gui.Views.PatchBuilding, gui.Views.PatchBuildingSecondary, gui.Views.MergeConflicts} {
|
||||
view.CanScrollPastBottom = gui.c.UserConfig().Gui.ScrollPastBottom
|
||||
view.TabWidth = gui.c.UserConfig().Gui.TabWidth
|
||||
}
|
||||
|
||||
gui.Views.CommitDescription.FgColor = theme.GocuiDefaultTextColor
|
||||
|
@ -109,7 +109,7 @@ func ScanLinesAndTruncateWhenLongerThanBuffer(maxBufferSize int) func(data []byt
|
||||
// - the line indices of the original lines, indexed by the wrapped line indices
|
||||
// If wrap is false, the text is returned as is.
|
||||
// This code needs to behave the same as `gocui.lineWrap` does.
|
||||
func WrapViewLinesToWidth(wrap bool, editable bool, text string, width int) ([]string, []int, []int) {
|
||||
func WrapViewLinesToWidth(wrap bool, editable bool, text string, width int, tabWidth int) ([]string, []int, []int) {
|
||||
if !editable {
|
||||
text = strings.TrimSuffix(text, "\n")
|
||||
}
|
||||
@ -126,14 +126,18 @@ func WrapViewLinesToWidth(wrap bool, editable bool, text string, width int) ([]s
|
||||
wrappedLineIndices := make([]int, 0, len(lines))
|
||||
originalLineIndices := make([]int, 0, len(lines))
|
||||
|
||||
if tabWidth < 1 {
|
||||
tabWidth = 4
|
||||
}
|
||||
|
||||
for originalLineIdx, line := range lines {
|
||||
wrappedLineIndices = append(wrappedLineIndices, len(wrappedLines))
|
||||
|
||||
// convert tabs to spaces
|
||||
for i := 0; i < len(line); i++ {
|
||||
if line[i] == '\t' {
|
||||
numSpaces := 4 - (i % 4)
|
||||
line = line[:i] + " "[:numSpaces] + line[i+1:]
|
||||
numSpaces := tabWidth - (i % tabWidth)
|
||||
line = line[:i] + strings.Repeat(" ", numSpaces) + line[i+1:]
|
||||
i += numSpaces - 1
|
||||
}
|
||||
}
|
||||
|
@ -173,6 +173,7 @@ func TestWrapViewLinesToWidth(t *testing.T) {
|
||||
editable bool
|
||||
text string
|
||||
width int
|
||||
tabWidth int
|
||||
expectedWrappedLines []string
|
||||
expectedWrappedLinesIndices []int
|
||||
expectedOriginalLinesIndices []int
|
||||
@ -353,14 +354,25 @@ func TestWrapViewLinesToWidth(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Tabs",
|
||||
wrap: true,
|
||||
text: "\ta\tbb\tccc\tdddd\teeeee",
|
||||
width: 50,
|
||||
name: "Tabs, width 4",
|
||||
wrap: true,
|
||||
text: "\ta\tbb\tccc\tdddd\teeeee",
|
||||
width: 50,
|
||||
tabWidth: 4,
|
||||
expectedWrappedLines: []string{
|
||||
" a bb ccc dddd eeeee",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Tabs, width 8",
|
||||
wrap: true,
|
||||
text: "\ta\tbb\tccc\tdddddddd\teeeee",
|
||||
width: 100,
|
||||
tabWidth: 8,
|
||||
expectedWrappedLines: []string{
|
||||
" a bb ccc dddddddd eeeee",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Multiple lines",
|
||||
wrap: true,
|
||||
@ -425,7 +437,11 @@ func TestWrapViewLinesToWidth(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
wrappedLines, wrappedLinesIndices, originalLinesIndices := WrapViewLinesToWidth(tt.wrap, tt.editable, tt.text, tt.width)
|
||||
tabWidth := tt.tabWidth
|
||||
if tabWidth == 0 {
|
||||
tabWidth = 4
|
||||
}
|
||||
wrappedLines, wrappedLinesIndices, originalLinesIndices := WrapViewLinesToWidth(tt.wrap, tt.editable, tt.text, tt.width, tabWidth)
|
||||
assert.Equal(t, tt.expectedWrappedLines, wrappedLines)
|
||||
if tt.expectedWrappedLinesIndices != nil {
|
||||
assert.Equal(t, tt.expectedWrappedLinesIndices, wrappedLinesIndices)
|
||||
@ -436,6 +452,7 @@ func TestWrapViewLinesToWidth(t *testing.T) {
|
||||
|
||||
// As a sanity check, also test that gocui's line wrapping behaves the same way
|
||||
view := gocui.NewView("", 0, 0, tt.width+1, 1000, gocui.OutputNormal)
|
||||
view.TabWidth = tabWidth
|
||||
assert.Equal(t, tt.width, view.InnerWidth())
|
||||
view.Wrap = tt.wrap
|
||||
view.Editable = tt.editable
|
||||
|
@ -46,6 +46,12 @@
|
||||
"description": "One of: 'margin' (default) | 'jump'",
|
||||
"default": "margin"
|
||||
},
|
||||
"tabWidth": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"description": "The number of spaces per tab; used for everything that's shown in the main view, but probably mostly relevant for diffs.\nNote that when using a pager, the pager has its own tab width setting, so you need to pass it separately in the pager command.",
|
||||
"default": 4
|
||||
},
|
||||
"mouseEvents": {
|
||||
"type": "boolean",
|
||||
"description": "If true, capture mouse events.\nWhen mouse events are captured, it's a little harder to select text: e.g. requiring you to hold the option key when on macOS.",
|
||||
|
11
vendor/github.com/jesseduffield/gocui/view.go
generated
vendored
11
vendor/github.com/jesseduffield/gocui/view.go
generated
vendored
@ -195,6 +195,9 @@ type View struct {
|
||||
// if true, the view will underline hyperlinks only when the cursor is on
|
||||
// them; otherwise, they will always be underlined
|
||||
UnderlineHyperLinksOnlyOnHover bool
|
||||
|
||||
// number of spaces per \t character, defaults to 4
|
||||
TabWidth int
|
||||
}
|
||||
|
||||
type pos struct {
|
||||
@ -424,6 +427,7 @@ func NewView(name string, x0, y0, x1, y1 int, mode OutputMode) *View {
|
||||
searcher: &searcher{},
|
||||
TextArea: &TextArea{},
|
||||
rangeSelectStartY: -1,
|
||||
TabWidth: 4,
|
||||
}
|
||||
|
||||
v.FgColor, v.BgColor = ColorDefault, ColorDefault
|
||||
@ -923,9 +927,12 @@ func (v *View) parseInput(ch rune, x int, _ int) (bool, []cell) {
|
||||
return truncateLine, nil
|
||||
} else if ch == '\t' {
|
||||
// fill tab-sized space
|
||||
const tabStop = 4
|
||||
tabWidth := v.TabWidth
|
||||
if tabWidth < 1 {
|
||||
tabWidth = 4
|
||||
}
|
||||
ch = ' '
|
||||
repeatCount = tabStop - (x % tabStop)
|
||||
repeatCount = tabWidth - (x % tabWidth)
|
||||
}
|
||||
c := cell{
|
||||
fgColor: v.ei.curFgColor,
|
||||
|
2
vendor/modules.txt
vendored
2
vendor/modules.txt
vendored
@ -171,7 +171,7 @@ github.com/jesseduffield/go-git/v5/utils/merkletrie/filesystem
|
||||
github.com/jesseduffield/go-git/v5/utils/merkletrie/index
|
||||
github.com/jesseduffield/go-git/v5/utils/merkletrie/internal/frame
|
||||
github.com/jesseduffield/go-git/v5/utils/merkletrie/noder
|
||||
# github.com/jesseduffield/gocui v0.3.1-0.20250210123912-aba68ae65951
|
||||
# github.com/jesseduffield/gocui v0.3.1-0.20250220081214-b376cb0857ac
|
||||
## explicit; go 1.12
|
||||
github.com/jesseduffield/gocui
|
||||
# github.com/jesseduffield/kill v0.0.0-20250101124109-e216ddbe133a
|
||||
|
Loading…
x
Reference in New Issue
Block a user