mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-04-21 12:16:54 +02:00
Right-align key labels in menu
I find this makes it look a little nicer
This commit is contained in:
parent
5b933762c2
commit
ec3a28df43
@ -13,6 +13,8 @@ type ListContextTrait struct {
|
|||||||
c *ContextCommon
|
c *ContextCommon
|
||||||
list types.IList
|
list types.IList
|
||||||
getDisplayStrings func(startIdx int, length int) [][]string
|
getDisplayStrings func(startIdx int, length int) [][]string
|
||||||
|
// alignment for each column. If nil, the default is left alignment
|
||||||
|
columnAlignments []utils.Alignment
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ListContextTrait) IsListContext() {}
|
func (self *ListContextTrait) IsListContext() {}
|
||||||
@ -52,7 +54,10 @@ func (self *ListContextTrait) HandleFocusLost(opts types.OnFocusLostOpts) error
|
|||||||
// OnFocus assumes that the content of the context has already been rendered to the view. OnRender is the function which actually renders the content to the view
|
// OnFocus assumes that the content of the context has already been rendered to the view. OnRender is the function which actually renders the content to the view
|
||||||
func (self *ListContextTrait) HandleRender() error {
|
func (self *ListContextTrait) HandleRender() error {
|
||||||
self.list.RefreshSelectedIdx()
|
self.list.RefreshSelectedIdx()
|
||||||
content := utils.RenderDisplayStrings(self.getDisplayStrings(0, self.list.Len()))
|
content := utils.RenderDisplayStrings(
|
||||||
|
self.getDisplayStrings(0, self.list.Len()),
|
||||||
|
self.columnAlignments,
|
||||||
|
)
|
||||||
self.GetViewTrait().SetContent(content)
|
self.GetViewTrait().SetContent(content)
|
||||||
self.c.Render()
|
self.c.Render()
|
||||||
self.setFooter()
|
self.setFooter()
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"github.com/jesseduffield/lazygit/pkg/gui/keybindings"
|
"github.com/jesseduffield/lazygit/pkg/gui/keybindings"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -37,6 +38,7 @@ func NewMenuContext(
|
|||||||
getDisplayStrings: viewModel.GetDisplayStrings,
|
getDisplayStrings: viewModel.GetDisplayStrings,
|
||||||
list: viewModel,
|
list: viewModel,
|
||||||
c: c,
|
c: c,
|
||||||
|
columnAlignments: []utils.Alignment{utils.AlignRight, utils.AlignLeft},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,6 @@ func (self *ViewportListContextTrait) FocusLine() {
|
|||||||
|
|
||||||
startIdx, length := self.GetViewTrait().ViewPortYBounds()
|
startIdx, length := self.GetViewTrait().ViewPortYBounds()
|
||||||
displayStrings := self.ListContextTrait.getDisplayStrings(startIdx, length)
|
displayStrings := self.ListContextTrait.getDisplayStrings(startIdx, length)
|
||||||
content := utils.RenderDisplayStrings(displayStrings)
|
content := utils.RenderDisplayStrings(displayStrings, nil)
|
||||||
self.GetViewTrait().SetViewPortContent(content)
|
self.GetViewTrait().SetViewPortContent(content)
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ func LongAuthor(authorName string) string {
|
|||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
paddedAuthorName := utils.WithPadding(authorName, 17)
|
paddedAuthorName := utils.WithPadding(authorName, 17, utils.AlignLeft)
|
||||||
truncatedName := utils.TruncateWithEllipsis(paddedAuthorName, 17)
|
truncatedName := utils.TruncateWithEllipsis(paddedAuthorName, 17)
|
||||||
value := AuthorStyle(authorName).Sprint(truncatedName)
|
value := AuthorStyle(authorName).Sprint(truncatedName)
|
||||||
authorNameCache[authorName] = value
|
authorNameCache[authorName] = value
|
||||||
|
@ -35,7 +35,7 @@ func getBranchDisplayStrings(b *models.Branch, fullDescription bool, diffed bool
|
|||||||
}
|
}
|
||||||
|
|
||||||
coloredName := nameTextStyle.Sprint(displayName)
|
coloredName := nameTextStyle.Sprint(displayName)
|
||||||
branchStatus := utils.WithPadding(ColoredBranchStatus(b, tr), 2)
|
branchStatus := utils.WithPadding(ColoredBranchStatus(b, tr), 2, utils.AlignLeft)
|
||||||
coloredName = fmt.Sprintf("%s %s", coloredName, branchStatus)
|
coloredName = fmt.Sprintf("%s %s", coloredName, branchStatus)
|
||||||
|
|
||||||
recencyColor := style.FgCyan
|
recencyColor := style.FgCyan
|
||||||
|
@ -283,7 +283,7 @@ func TestGetCommitListDisplayStrings(t *testing.T) {
|
|||||||
s.showYouAreHereLabel,
|
s.showYouAreHereLabel,
|
||||||
)
|
)
|
||||||
|
|
||||||
renderedResult := utils.RenderDisplayStrings(result)
|
renderedResult := utils.RenderDisplayStrings(result, nil)
|
||||||
t.Logf("\n%s", renderedResult)
|
t.Logf("\n%s", renderedResult)
|
||||||
|
|
||||||
assert.EqualValues(t, s.expected, renderedResult)
|
assert.EqualValues(t, s.expected, renderedResult)
|
||||||
|
@ -8,20 +8,52 @@ import (
|
|||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Alignment int
|
||||||
|
|
||||||
|
const (
|
||||||
|
AlignLeft Alignment = iota
|
||||||
|
AlignRight
|
||||||
|
)
|
||||||
|
|
||||||
|
type ColumnConfig struct {
|
||||||
|
Width int
|
||||||
|
Alignment Alignment
|
||||||
|
}
|
||||||
|
|
||||||
// WithPadding pads a string as much as you want
|
// WithPadding pads a string as much as you want
|
||||||
func WithPadding(str string, padding int) string {
|
func WithPadding(str string, padding int, alignment Alignment) string {
|
||||||
uncoloredStr := Decolorise(str)
|
uncoloredStr := Decolorise(str)
|
||||||
width := runewidth.StringWidth(uncoloredStr)
|
width := runewidth.StringWidth(uncoloredStr)
|
||||||
if padding < width {
|
if padding < width {
|
||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
return str + strings.Repeat(" ", padding-width)
|
space := strings.Repeat(" ", padding-width)
|
||||||
|
if alignment == AlignLeft {
|
||||||
|
return str + space
|
||||||
|
} else {
|
||||||
|
return space + str
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func RenderDisplayStrings(displayStringsArr [][]string) string {
|
// defaults to left-aligning each column. If you want to set the alignment of
|
||||||
|
// each column, pass in a slice of Alignment values.
|
||||||
|
func RenderDisplayStrings(displayStringsArr [][]string, columnAlignments []Alignment) string {
|
||||||
displayStringsArr = excludeBlankColumns(displayStringsArr)
|
displayStringsArr = excludeBlankColumns(displayStringsArr)
|
||||||
padWidths := getPadWidths(displayStringsArr)
|
padWidths := getPadWidths(displayStringsArr)
|
||||||
output := getPaddedDisplayStrings(displayStringsArr, padWidths)
|
columnConfigs := make([]ColumnConfig, len(padWidths))
|
||||||
|
for i, padWidth := range padWidths {
|
||||||
|
// gracefully handle when columnAlignments is shorter than padWidths
|
||||||
|
alignment := AlignLeft
|
||||||
|
if len(columnAlignments) > i {
|
||||||
|
alignment = columnAlignments[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
columnConfigs[i] = ColumnConfig{
|
||||||
|
Width: padWidth,
|
||||||
|
Alignment: alignment,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output := getPaddedDisplayStrings(displayStringsArr, columnConfigs)
|
||||||
|
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
@ -59,23 +91,23 @@ outer:
|
|||||||
return displayStringsArr
|
return displayStringsArr
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPaddedDisplayStrings(stringArrays [][]string, padWidths []int) string {
|
func getPaddedDisplayStrings(stringArrays [][]string, columnConfigs []ColumnConfig) string {
|
||||||
builder := strings.Builder{}
|
builder := strings.Builder{}
|
||||||
for i, stringArray := range stringArrays {
|
for i, stringArray := range stringArrays {
|
||||||
if len(stringArray) == 0 {
|
if len(stringArray) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for j, padWidth := range padWidths {
|
for j, columnConfig := range columnConfigs {
|
||||||
if len(stringArray)-1 < j {
|
if len(stringArray)-1 < j {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
builder.WriteString(WithPadding(stringArray[j], padWidth))
|
builder.WriteString(WithPadding(stringArray[j], columnConfig.Width, columnConfig.Alignment))
|
||||||
builder.WriteString(" ")
|
builder.WriteString(" ")
|
||||||
}
|
}
|
||||||
if len(stringArray)-1 < len(padWidths) {
|
if len(stringArray)-1 < len(columnConfigs) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
builder.WriteString(stringArray[len(padWidths)])
|
builder.WriteString(stringArray[len(columnConfigs)])
|
||||||
|
|
||||||
if i < len(stringArrays)-1 {
|
if i < len(stringArrays)-1 {
|
||||||
builder.WriteString("\n")
|
builder.WriteString("\n")
|
||||||
|
@ -6,34 +6,49 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestWithPadding is a function.
|
|
||||||
func TestWithPadding(t *testing.T) {
|
func TestWithPadding(t *testing.T) {
|
||||||
type scenario struct {
|
type scenario struct {
|
||||||
str string
|
str string
|
||||||
padding int
|
padding int
|
||||||
|
alignment Alignment
|
||||||
expected string
|
expected string
|
||||||
}
|
}
|
||||||
|
|
||||||
scenarios := []scenario{
|
scenarios := []scenario{
|
||||||
{
|
{
|
||||||
"hello world !",
|
str: "hello world !",
|
||||||
1,
|
padding: 1,
|
||||||
"hello world !",
|
alignment: AlignLeft,
|
||||||
|
expected: "hello world !",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"hello world !",
|
str: "hello world !",
|
||||||
14,
|
padding: 14,
|
||||||
"hello world ! ",
|
alignment: AlignLeft,
|
||||||
|
expected: "hello world ! ",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Güçlü",
|
str: "hello world !",
|
||||||
7,
|
padding: 14,
|
||||||
"Güçlü ",
|
alignment: AlignRight,
|
||||||
|
expected: " hello world !",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
str: "Güçlü",
|
||||||
|
padding: 7,
|
||||||
|
alignment: AlignLeft,
|
||||||
|
expected: "Güçlü ",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
str: "Güçlü",
|
||||||
|
padding: 7,
|
||||||
|
alignment: AlignRight,
|
||||||
|
expected: " Güçlü",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, s := range scenarios {
|
for _, s := range scenarios {
|
||||||
assert.EqualValues(t, s.expected, WithPadding(s.str, s.padding))
|
assert.EqualValues(t, s.expected, WithPadding(s.str, s.padding, s.alignment))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,38 +160,65 @@ func TestTruncateWithEllipsis(t *testing.T) {
|
|||||||
func TestRenderDisplayStrings(t *testing.T) {
|
func TestRenderDisplayStrings(t *testing.T) {
|
||||||
type scenario struct {
|
type scenario struct {
|
||||||
input [][]string
|
input [][]string
|
||||||
|
columnAlignments []Alignment
|
||||||
expected string
|
expected string
|
||||||
}
|
}
|
||||||
|
|
||||||
tests := []scenario{
|
tests := []scenario{
|
||||||
{
|
{
|
||||||
[][]string{{""}, {""}},
|
input: [][]string{{""}, {""}},
|
||||||
"",
|
columnAlignments: nil,
|
||||||
|
expected: "",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
[][]string{{"a"}, {""}},
|
input: [][]string{{"a"}, {""}},
|
||||||
"a\n",
|
columnAlignments: nil,
|
||||||
|
expected: "a\n",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
[][]string{{"a"}, {"b"}},
|
input: [][]string{{"a"}, {"b"}},
|
||||||
"a\nb",
|
columnAlignments: nil,
|
||||||
|
expected: "a\nb",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
[][]string{{"a", "b"}, {"c", "d"}},
|
input: [][]string{{"a", "b"}, {"c", "d"}},
|
||||||
"a b\nc d",
|
columnAlignments: nil,
|
||||||
|
expected: "a b\nc d",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
[][]string{{"a", "", "c"}, {"d", "", "f"}},
|
input: [][]string{{"a", "", "c"}, {"d", "", "f"}},
|
||||||
"a c\nd f",
|
columnAlignments: nil,
|
||||||
|
expected: "a c\nd f",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
[][]string{{"a", "", "c", ""}, {"d", "", "f", ""}},
|
input: [][]string{{"a", "", "c", ""}, {"d", "", "f", ""}},
|
||||||
"a c\nd f",
|
columnAlignments: nil,
|
||||||
|
expected: "a c\nd f",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: [][]string{{"abc", "", "d", ""}, {"e", "", "f", ""}},
|
||||||
|
columnAlignments: nil,
|
||||||
|
expected: "abc d\ne f",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: [][]string{{"abc", "", "d", ""}, {"e", "", "f", ""}},
|
||||||
|
columnAlignments: []Alignment{AlignLeft, AlignLeft}, // same as nil (default)
|
||||||
|
expected: "abc d\ne f",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: [][]string{{"abc", "", "d", ""}, {"e", "", "f", ""}},
|
||||||
|
columnAlignments: []Alignment{AlignRight, AlignLeft},
|
||||||
|
expected: "abc d\n e f",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: [][]string{{"abc", "", "d", ""}, {"e", "", "f", ""}},
|
||||||
|
columnAlignments: []Alignment{AlignRight}, // gracefully defaults unspecified columns to left-align
|
||||||
|
expected: "abc d\n e f",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
output := RenderDisplayStrings(test.input)
|
output := RenderDisplayStrings(test.input, test.columnAlignments)
|
||||||
if !assert.EqualValues(t, output, test.expected) {
|
if !assert.EqualValues(t, output, test.expected) {
|
||||||
t.Errorf("RenderDisplayStrings(%v) = %v, want %v", test.input, output, test.expected)
|
t.Errorf("RenderDisplayStrings(%v) = %v, want %v", test.input, output, test.expected)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user