Skip to content

Commit

Permalink
better commit lines in fullscreen mode
Browse files Browse the repository at this point in the history
  • Loading branch information
jesseduffield committed Feb 25, 2020
1 parent b8717d7 commit 9fd9fd6
Show file tree
Hide file tree
Showing 7 changed files with 204 additions and 104 deletions.
60 changes: 3 additions & 57 deletions pkg/commands/commit.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
package commands

import (
"strings"

"github.com/fatih/color"
"github.com/jesseduffield/lazygit/pkg/theme"
"github.com/jesseduffield/lazygit/pkg/utils"
)

// Commit : A git commit
type Commit struct {
Sha string
Expand All @@ -17,53 +9,7 @@ type Commit struct {
Action string // one of "", "pick", "edit", "squash", "reword", "drop", "fixup"
Copied bool // to know if this commit is ready to be cherry-picked somewhere
Tags []string
}

// GetDisplayStrings is a function.
func (c *Commit) GetDisplayStrings(isFocused bool) []string {
red := color.New(color.FgRed)
yellow := color.New(color.FgYellow)
green := color.New(color.FgGreen)
blue := color.New(color.FgBlue)
cyan := color.New(color.FgCyan)
defaultColor := color.New(theme.DefaultTextColor)
magenta := color.New(color.FgMagenta)

// for some reason, setting the background to blue pads out the other commits
// horizontally. For the sake of accessibility I'm considering this a feature,
// not a bug
copied := color.New(color.FgCyan, color.BgBlue)

var shaColor *color.Color
switch c.Status {
case "unpushed":
shaColor = red
case "pushed":
shaColor = yellow
case "merged":
shaColor = green
case "rebasing":
shaColor = blue
case "reflog":
shaColor = blue
case "selected":
shaColor = magenta
default:
shaColor = defaultColor
}

if c.Copied {
shaColor = copied
}

actionString := ""
tagString := ""
if c.Action != "" {
actionString = cyan.Sprint(utils.WithPadding(c.Action, 7)) + " "
} else if len(c.Tags) > 0 {
tagColor := color.New(color.FgMagenta, color.Bold)
tagString = utils.ColoredStringDirect(strings.Join(c.Tags, " "), tagColor) + " "
}

return []string{shaColor.Sprint(c.Sha[:8]), actionString + tagString + defaultColor.Sprint(c.Name)}
ExtraInfo string // something like 'HEAD -> master, tag: v0.15.2'
Author string
Date string
}
80 changes: 36 additions & 44 deletions pkg/commands/commit_list_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import (
// if we find out we need to use one of these functions in the git.go file, we
// can just pull them out of here and put them there and then call them from in here

const SEPARATION_CHAR = "|"

// CommitListBuilder returns a list of Branch objects for the current repo
type CommitListBuilder struct {
Log *logrus.Entry
Expand All @@ -45,42 +47,37 @@ func NewCommitListBuilder(log *logrus.Entry, gitCommand *GitCommand, osCommand *
}, nil
}

// nameAndTag takes a line from a git log and extracts the sha, message and tag (if present)
// example inputs:
// 66e6369c284e96ed5af5 (tag: v0.14.4) allow fastforwarding the current branch
// 32e650e0bb3f4327749f (HEAD -> show-tags) this is my commit
// 32e650e0bb3f4327749e this is my other commit
func (c *CommitListBuilder) commitLineParts(line string) (string, string, []string) {
re := regexp.MustCompile(`(\w+) (.*)`)
shaMatch := re.FindStringSubmatch(line)

if len(shaMatch) <= 1 {
return line, "", nil
}
sha := shaMatch[1]
rest := shaMatch[2]

if !strings.HasPrefix(rest, "(") {
return sha, rest, nil
}

re = regexp.MustCompile(`\((.*)\) (.*)`)

parensMatch := re.FindStringSubmatch(rest)
if len(parensMatch) <= 1 {
return sha, rest, nil
// extractCommitFromLine takes a line from a git log and extracts the sha, message, date, and tag if present
// then puts them into a commit object
// example input:
// 8ad01fe32fcc20f07bc6693f87aa4977c327f1e1|10 hours ago|Jesse Duffield| (HEAD -> master, tag: v0.15.2)|refresh commits when adding a tag
func (c *CommitListBuilder) extractCommitFromLine(line string) *Commit {
split := strings.Split(line, SEPARATION_CHAR)

sha := split[0]
date := split[1]
author := split[2]
extraInfo := strings.TrimSpace(split[3])
message := strings.Join(split[4:len(split)], SEPARATION_CHAR)
tags := []string{}

if extraInfo != "" {
re := regexp.MustCompile(`tag: ([^,\)]+)`)
tagMatch := re.FindStringSubmatch(extraInfo)
if len(tagMatch) > 1 {
tags = append(tags, tagMatch[1])
}
}

notes := parensMatch[1]
message := parensMatch[2]
re = regexp.MustCompile(`tag: ([^,]+)`)
tagMatch := re.FindStringSubmatch(notes)
if len(tagMatch) <= 1 {
return sha, message, nil
return &Commit{
Sha: sha,
Name: message,
DisplayString: line,
Tags: tags,
ExtraInfo: extraInfo,
Date: date,
Author: author,
}

tag := tagMatch[1]
return sha, message, []string{tag}
}

// GetCommits obtains the commits of the current branch
Expand All @@ -107,16 +104,10 @@ func (c *CommitListBuilder) GetCommits(limit bool) ([]*Commit, error) {

// now we can split it up and turn it into commits
for _, line := range utils.SplitLines(log) {
sha, name, tags := c.commitLineParts(line)
_, unpushed := unpushedCommits[sha]
status := map[bool]string{true: "unpushed", false: "pushed"}[unpushed]
commits = append(commits, &Commit{
Sha: sha,
Name: name,
Status: status,
DisplayString: line,
Tags: tags,
})
commit := c.extractCommitFromLine(line)
_, unpushed := unpushedCommits[commit.Sha]
commit.Status = map[bool]string{true: "unpushed", false: "pushed"}[unpushed]
commits = append(commits, commit)
}
if rebaseMode != "" {
currentCommit := commits[len(rebasingCommits)]
Expand Down Expand Up @@ -325,7 +316,8 @@ func (c *CommitListBuilder) getLog(limit bool) string {
limitFlag = "-30"
}

result, err := c.OSCommand.RunCommandWithOutput(fmt.Sprintf("git log --decorate --oneline %s --abbrev=%d", limitFlag, 20))
result, err := c.OSCommand.RunCommandWithOutput(fmt.Sprintf("git log --oneline --pretty=format:\"%%H%s%%ar%s%%aN%s%%d%s%%s\" %s --abbrev=%d", SEPARATION_CHAR, SEPARATION_CHAR, SEPARATION_CHAR, SEPARATION_CHAR, limitFlag, 20))

if err != nil {
// assume if there is an error there are no commits yet for this branch
return ""
Expand Down
6 changes: 3 additions & 3 deletions pkg/gui/commits_panel.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
"github.com/jesseduffield/lazygit/pkg/utils"
)

Expand Down Expand Up @@ -600,9 +601,8 @@ func (gui *Gui) renderBranchCommitsWithSelection() error {
commitsView := gui.getCommitsView()

gui.refreshSelectedLine(&gui.State.Panels.Commits.SelectedLine, len(gui.State.Commits))
if err := gui.renderListPanel(commitsView, gui.State.Commits); err != nil {
return err
}
displayStrings := presentation.GetCommitListDisplayStrings(gui.State.Commits, gui.State.ScreenMode != SCREEN_NORMAL)
gui.renderDisplayStrings(commitsView, displayStrings)
if gui.g.CurrentView() == commitsView && commitsView.Context == "branch-commits" {
if err := gui.handleCommitSelect(gui.g, commitsView); err != nil {
return err
Expand Down
8 changes: 8 additions & 0 deletions pkg/gui/gui.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,12 +269,20 @@ func NewGui(log *logrus.Entry, gitCommand *commands.GitCommand, oSCommand *comma

func (gui *Gui) nextScreenMode(g *gocui.Gui, v *gocui.View) error {
gui.State.ScreenMode = utils.NextIntInCycle([]int{SCREEN_NORMAL, SCREEN_HALF, SCREEN_FULL}, gui.State.ScreenMode)
// commits render differently depending on whether we're in fullscreen more or not
if err := gui.renderBranchCommitsWithSelection(); err != nil {
return err
}

return nil
}

func (gui *Gui) prevScreenMode(g *gocui.Gui, v *gocui.View) error {
gui.State.ScreenMode = utils.PrevIntInCycle([]int{SCREEN_NORMAL, SCREEN_HALF, SCREEN_FULL}, gui.State.ScreenMode)
// commits render differently depending on whether we're in fullscreen more or not
if err := gui.renderBranchCommitsWithSelection(); err != nil {
return err
}

return nil
}
Expand Down
126 changes: 126 additions & 0 deletions pkg/gui/presentation/commits.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package presentation

import (
"strings"

"github.com/fatih/color"
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/theme"
"github.com/jesseduffield/lazygit/pkg/utils"
)

func GetCommitListDisplayStrings(commits []*commands.Commit, fullDescription bool) [][]string {
lines := make([][]string, len(commits))

var displayFunc func(*commands.Commit) []string
if fullDescription {
displayFunc = getFullDescriptionDisplayStringsForCommit
} else {
displayFunc = getDisplayStringsForCommit
}

for i := range commits {
lines[i] = displayFunc(commits[i])
}

return lines
}

func getFullDescriptionDisplayStringsForCommit(c *commands.Commit) []string {
red := color.New(color.FgRed)
yellow := color.New(color.FgYellow)
green := color.New(color.FgGreen)
blue := color.New(color.FgBlue)
cyan := color.New(color.FgCyan)
defaultColor := color.New(theme.DefaultTextColor)
magenta := color.New(color.FgMagenta)

// for some reason, setting the background to blue pads out the other commits
// horizontally. For the sake of accessibility I'm considering this a feature,
// not a bug
copied := color.New(color.FgCyan, color.BgBlue)

var shaColor *color.Color
switch c.Status {
case "unpushed":
shaColor = red
case "pushed":
shaColor = yellow
case "merged":
shaColor = green
case "rebasing":
shaColor = blue
case "reflog":
shaColor = blue
case "selected":
shaColor = magenta
default:
shaColor = defaultColor
}

if c.Copied {
shaColor = copied
}

tagString := ""
truncatedDate := utils.TruncateWithEllipsis(c.Date, 15)
secondColumnString := blue.Sprint(truncatedDate)
if c.Action != "" {
secondColumnString = cyan.Sprint(c.Action)
} else if len(c.Tags) > 0 {
tagColor := color.New(color.FgMagenta, color.Bold)
tagString = utils.ColoredStringDirect(c.ExtraInfo, tagColor) + " "
}

truncatedAuthor := utils.TruncateWithEllipsis(c.Author, 17)

return []string{shaColor.Sprint(c.Sha[:8]), secondColumnString, yellow.Sprint(truncatedAuthor), tagString + defaultColor.Sprint(c.Name)}
}

func getDisplayStringsForCommit(c *commands.Commit) []string {
red := color.New(color.FgRed)
yellow := color.New(color.FgYellow)
green := color.New(color.FgGreen)
blue := color.New(color.FgBlue)
cyan := color.New(color.FgCyan)
defaultColor := color.New(theme.DefaultTextColor)
magenta := color.New(color.FgMagenta)

// for some reason, setting the background to blue pads out the other commits
// horizontally. For the sake of accessibility I'm considering this a feature,
// not a bug
copied := color.New(color.FgCyan, color.BgBlue)

var shaColor *color.Color
switch c.Status {
case "unpushed":
shaColor = red
case "pushed":
shaColor = yellow
case "merged":
shaColor = green
case "rebasing":
shaColor = blue
case "reflog":
shaColor = blue
case "selected":
shaColor = magenta
default:
shaColor = defaultColor
}

if c.Copied {
shaColor = copied
}

actionString := ""
tagString := ""
if c.Action != "" {
actionString = cyan.Sprint(utils.WithPadding(c.Action, 7)) + " "
} else if len(c.Tags) > 0 {
tagColor := color.New(color.FgMagenta, color.Bold)
tagString = utils.ColoredStringDirect(strings.Join(c.Tags, " "), tagColor) + " "
}

return []string{shaColor.Sprint(c.Sha[:8]), actionString + tagString + defaultColor.Sprint(c.Name)}
}
9 changes: 9 additions & 0 deletions pkg/gui/view_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,15 @@ func (gui *Gui) refreshSelectedLine(line *int, total int) {
}
}

func (gui *Gui) renderDisplayStrings(v *gocui.View, displayStrings [][]string) {
gui.g.Update(func(g *gocui.Gui) error {
list := utils.RenderDisplayStrings(displayStrings)
v.Clear()
fmt.Fprint(v, list)
return nil
})
}

func (gui *Gui) renderListPanel(v *gocui.View, items interface{}) error {
gui.g.Update(func(g *gocui.Gui) error {
isFocused := gui.g.CurrentView().Name() == v.Name()
Expand Down
19 changes: 19 additions & 0 deletions pkg/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -348,3 +348,22 @@ func PrevIntInCycle(sl []int, current int) int {
}
return sl[len(sl)-1]
}

// TruncateWithEllipsis returns a string, truncated to a certain length, with an ellipsis
func TruncateWithEllipsis(str string, limit int) string {
if limit == 1 && len(str) > 1 {
return "."
}

if limit == 2 && len(str) > 2 {
return ".."
}

ellipsis := "..."
if len(str) <= limit {
return str
}

remainingLength := limit - len(ellipsis)
return str[0:remainingLength] + "..."
}

0 comments on commit 9fd9fd6

Please sign in to comment.