From 8de3e1ad8fa73229893db15141b7a5d89d2e99c9 Mon Sep 17 00:00:00 2001 From: Dolev Hadar Date: Fri, 23 Aug 2024 18:28:34 +0300 Subject: [PATCH 1/6] WIP --- git/git.go | 6 +++ ui/components/reposection/checkout.go | 45 ---------------- ui/components/reposection/commands.go | 68 ++++++++++++++++++++++++ ui/components/reposection/reposection.go | 6 +++ ui/keys/branchKeys.go | 5 ++ 5 files changed, 85 insertions(+), 45 deletions(-) delete mode 100644 ui/components/reposection/checkout.go diff --git a/git/git.go b/git/git.go index 7df4e805..756f5678 100644 --- a/git/git.go +++ b/git/git.go @@ -25,6 +25,7 @@ type Branch struct { CommitsAhead int CommitsBehind int IsCheckedOut bool + Remotes []string } func GetOriginUrl(dir string) (string, error) { @@ -87,10 +88,15 @@ func GetRepo(dir string) (*Repo, error) { if err != nil { commitsBehind = 0 } + remotes, err := repo.RemoteGetURL(b) + if err != nil { + commitsBehind = 0 + } branches[i] = Branch{ Name: b, LastUpdatedAt: updatedAt, IsCheckedOut: isHead, + Remotes: remotes, LastCommitMsg: lastCommitMsg, CommitsAhead: int(commitsAhead), CommitsBehind: int(commitsBehind), diff --git a/ui/components/reposection/checkout.go b/ui/components/reposection/checkout.go deleted file mode 100644 index 083a685b..00000000 --- a/ui/components/reposection/checkout.go +++ /dev/null @@ -1,45 +0,0 @@ -package reposection - -import ( - "fmt" - "time" - - gitm "github.com/aymanbagabas/git-module" - tea "github.com/charmbracelet/bubbletea" - - "github.com/dlvhdr/gh-dash/v4/git" - "github.com/dlvhdr/gh-dash/v4/ui/constants" - "github.com/dlvhdr/gh-dash/v4/ui/context" -) - -func (m *Model) checkout() (tea.Cmd, error) { - b := m.getCurrBranch() - - taskId := fmt.Sprintf("checkout_%s_%d", b.Data.Name, time.Now().Unix()) - task := context.Task{ - Id: taskId, - StartText: fmt.Sprintf("Checking out branch %s", b.Data.Name), - FinishedText: fmt.Sprintf("Branch %s has been checked out", b.Data.Name), - State: context.TaskStart, - Error: nil, - } - startCmd := m.Ctx.StartTask(task) - return tea.Batch(startCmd, func() tea.Msg { - err := gitm.Checkout(*m.Ctx.RepoPath, b.Data.Name) - if err != nil { - return constants.TaskFinishedMsg{TaskId: taskId, Err: err} - } - repo, err := git.GetRepo(*m.Ctx.RepoPath) - if err != nil { - return constants.TaskFinishedMsg{TaskId: taskId, Err: err} - } - - return constants.TaskFinishedMsg{ - SectionId: 0, - SectionType: SectionType, - TaskId: taskId, - Msg: repoMsg{repo: repo}, - Err: err, - } - }), nil -} diff --git a/ui/components/reposection/commands.go b/ui/components/reposection/commands.go index 245c1037..9cba54ce 100644 --- a/ui/components/reposection/commands.go +++ b/ui/components/reposection/commands.go @@ -4,6 +4,7 @@ import ( "fmt" "time" + gitm "github.com/aymanbagabas/git-module" tea "github.com/charmbracelet/bubbletea" "github.com/dlvhdr/gh-dash/v4/data" @@ -22,6 +23,73 @@ type UpdatePRMsg struct { RemovedAssignees *data.Assignees } +func (m *Model) push() (tea.Cmd, error) { + b := m.getCurrBranch() + + taskId := fmt.Sprintf("push_%s_%d", b.Data.Name, time.Now().Unix()) + task := context.Task{ + Id: taskId, + StartText: fmt.Sprintf("Pushing branch %s", b.Data.Name), + FinishedText: fmt.Sprintf("Branch %s has been pushed", b.Data.Name), + State: context.TaskStart, + Error: nil, + } + startCmd := m.Ctx.StartTask(task) + return tea.Batch(startCmd, func() tea.Msg { + if len(b.Data.Remotes) == 0 { + return constants.TaskFinishedMsg{TaskId: taskId, Err: fmt.Errorf("No remotes found for branch %s", b.Data.Name)} + } + err := gitm.Push(*m.Ctx.RepoPath, b.Data.Remotes[0], b.Data.Name) + if err != nil { + return constants.TaskFinishedMsg{TaskId: taskId, Err: err} + } + repo, err := git.GetRepo(*m.Ctx.RepoPath) + if err != nil { + return constants.TaskFinishedMsg{TaskId: taskId, Err: err} + } + + return constants.TaskFinishedMsg{ + SectionId: 0, + SectionType: SectionType, + TaskId: taskId, + Msg: repoMsg{repo: repo}, + Err: err, + } + }), nil +} + +func (m *Model) checkout() (tea.Cmd, error) { + b := m.getCurrBranch() + + taskId := fmt.Sprintf("checkout_%s_%d", b.Data.Name, time.Now().Unix()) + task := context.Task{ + Id: taskId, + StartText: fmt.Sprintf("Checking out branch %s", b.Data.Name), + FinishedText: fmt.Sprintf("Branch %s has been checked out", b.Data.Name), + State: context.TaskStart, + Error: nil, + } + startCmd := m.Ctx.StartTask(task) + return tea.Batch(startCmd, func() tea.Msg { + err := gitm.Checkout(*m.Ctx.RepoPath, b.Data.Name) + if err != nil { + return constants.TaskFinishedMsg{TaskId: taskId, Err: err} + } + repo, err := git.GetRepo(*m.Ctx.RepoPath) + if err != nil { + return constants.TaskFinishedMsg{TaskId: taskId, Err: err} + } + + return constants.TaskFinishedMsg{ + SectionId: 0, + SectionType: SectionType, + TaskId: taskId, + Msg: repoMsg{repo: repo}, + Err: err, + } + }), nil +} + type repoMsg struct { repo *git.Repo err error diff --git a/ui/components/reposection/reposection.go b/ui/components/reposection/reposection.go index cc0ab9ba..de134570 100644 --- a/ui/components/reposection/reposection.go +++ b/ui/components/reposection/reposection.go @@ -72,6 +72,12 @@ func (m *Model) Update(msg tea.Msg) (section.Section, tea.Cmd) { m.Ctx.Error = err } + case key.Matches(msg, keys.BranchKeys.Push): + cmd, err = m.push() + if err != nil { + m.Ctx.Error = err + } + } case repoMsg: diff --git a/ui/keys/branchKeys.go b/ui/keys/branchKeys.go index e471f557..4f9c0429 100644 --- a/ui/keys/branchKeys.go +++ b/ui/keys/branchKeys.go @@ -11,6 +11,7 @@ import ( type BranchKeyMap struct { Checkout key.Binding + Push key.Binding Delete key.Binding ViewPRs key.Binding } @@ -20,6 +21,10 @@ var BranchKeys = BranchKeyMap{ key.WithKeys("C", " "), key.WithHelp("C/space", "checkout"), ), + Push: key.NewBinding( + key.WithKeys("P"), + key.WithHelp("P", "push"), + ), Delete: key.NewBinding( key.WithKeys("d", "backspace"), key.WithHelp("d/backspace", "checkout"), From 864a779ba452bea2bc3261ff4c5d962ac73bad0a Mon Sep 17 00:00:00 2001 From: Dolev Hadar Date: Fri, 23 Aug 2024 18:32:51 +0300 Subject: [PATCH 2/6] WIP --- ui/components/reposection/commands.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ui/components/reposection/commands.go b/ui/components/reposection/commands.go index 9cba54ce..7f63aa0e 100644 --- a/ui/components/reposection/commands.go +++ b/ui/components/reposection/commands.go @@ -36,10 +36,12 @@ func (m *Model) push() (tea.Cmd, error) { } startCmd := m.Ctx.StartTask(task) return tea.Batch(startCmd, func() tea.Msg { + var err error if len(b.Data.Remotes) == 0 { - return constants.TaskFinishedMsg{TaskId: taskId, Err: fmt.Errorf("No remotes found for branch %s", b.Data.Name)} + err = gitm.Push(*m.Ctx.RepoPath, "", b.Data.Name, gitm.PushOptions{CommandOptions: gitm.CommandOptions{Args: []string{"--set-upstream"}}}) + } else { + err = gitm.Push(*m.Ctx.RepoPath, b.Data.Remotes[0], b.Data.Name, gitm.PushOptions{CommandOptions: gitm.CommandOptions{Args: []string{"--set-upstream"}}}) } - err := gitm.Push(*m.Ctx.RepoPath, b.Data.Remotes[0], b.Data.Name) if err != nil { return constants.TaskFinishedMsg{TaskId: taskId, Err: err} } From 4d9ed22f5022ebc1696c0b9a8a08dd8b28e89346 Mon Sep 17 00:00:00 2001 From: Dolev Hadar Date: Fri, 23 Aug 2024 18:51:33 +0300 Subject: [PATCH 3/6] WIP --- ui/components/reposection/commands.go | 6 ++++-- ui/ui.go | 7 +++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/ui/components/reposection/commands.go b/ui/components/reposection/commands.go index 7f63aa0e..744f7748 100644 --- a/ui/components/reposection/commands.go +++ b/ui/components/reposection/commands.go @@ -6,6 +6,7 @@ import ( gitm "github.com/aymanbagabas/git-module" tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/log" "github.com/dlvhdr/gh-dash/v4/data" "github.com/dlvhdr/gh-dash/v4/git" @@ -37,10 +38,11 @@ func (m *Model) push() (tea.Cmd, error) { startCmd := m.Ctx.StartTask(task) return tea.Batch(startCmd, func() tea.Msg { var err error + log.Debug("Pushing branch", "branch", b.Data.Name, "remotes", len(b.Data.Remotes), "repoPath", *m.Ctx.RepoPath) if len(b.Data.Remotes) == 0 { - err = gitm.Push(*m.Ctx.RepoPath, "", b.Data.Name, gitm.PushOptions{CommandOptions: gitm.CommandOptions{Args: []string{"--set-upstream"}}}) + err = gitm.Push(*m.Ctx.RepoPath, "origin", b.Data.Name, gitm.PushOptions{CommandOptions: gitm.CommandOptions{Args: []string{"--set-upstream"}}}) } else { - err = gitm.Push(*m.Ctx.RepoPath, b.Data.Remotes[0], b.Data.Name, gitm.PushOptions{CommandOptions: gitm.CommandOptions{Args: []string{"--set-upstream"}}}) + err = gitm.Push(*m.Ctx.RepoPath, b.Data.Remotes[0], b.Data.Name) } if err != nil { return constants.TaskFinishedMsg{TaskId: taskId, Err: err} diff --git a/ui/ui.go b/ui/ui.go index 9ecaf12d..ff4eaf3a 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -71,7 +71,6 @@ func NewModel(repoPath *string, configPath string) Model { }, } - m.currSectionId = m.getCurrentViewDefaultSection() m.taskSpinner.Style = lipgloss.NewStyle(). Background(m.ctx.Theme.SelectedBackground) @@ -270,6 +269,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { cmd = tea.Quit case m.ctx.View == config.RepoView: + log.Debug("RepoView") switch { case key.Matches(msg, keys.BranchKeys.Delete): if currSection != nil { @@ -291,9 +291,11 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { cmd = fetchSectionsCmds } m.onViewedRowChanged() + default: + log.Debug("Not a top-level binding for RepoView") } - case m.ctx.View == config.PRsView, m.ctx.View == config.RepoView: + case m.ctx.View == config.PRsView: switch { case key.Matches(msg, keys.PRKeys.Approve): m.sidebar.IsOpen = true @@ -432,6 +434,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.ctx.Styles = context.InitStyles(m.ctx.Theme) log.Debug("Config loaded", "default view", m.ctx.Config.Defaults.View) m.ctx.View = m.ctx.Config.Defaults.View + m.currSectionId = m.getCurrentViewDefaultSection() m.sidebar.IsOpen = msg.Config.Defaults.Preview.Open m.tabs.UpdateSectionsConfigs(&m.ctx) m.syncMainContentWidth() From 2b1c27db1607e31354d63134b9bbbb9631e4aa65 Mon Sep 17 00:00:00 2001 From: Dolev Hadar Date: Sat, 24 Aug 2024 14:15:56 +0300 Subject: [PATCH 4/6] refactor: cleanup branch rendering code --- ui/components/branch/branch.go | 101 +++++++++++++++++++++------------ ui/components/pr/pr.go | 23 +------- 2 files changed, 68 insertions(+), 56 deletions(-) diff --git a/ui/components/branch/branch.go b/ui/components/branch/branch.go index 8fbb43bc..555336b8 100644 --- a/ui/components/branch/branch.go +++ b/ui/components/branch/branch.go @@ -175,42 +175,12 @@ func (b *Branch) renderTitle() string { } func (b *Branch) renderExtendedTitle(isSelected bool) string { - baseStyle := lipgloss.NewStyle() - if isSelected { - baseStyle = baseStyle.Background(b.Ctx.Theme.SelectedBackground) - } + baseStyle := b.getBaseStyle(isSelected) + width := b.getMaxWidth() - title := "-" - if b.PR != nil { - title = fmt.Sprintf("#%d %s", b.PR.Number, b.PR.Title) - } else if b.Data.LastCommitMsg != nil { - title = *b.Data.LastCommitMsg - } - var titleColumn table.Column - for _, column := range b.Columns { - if column.Title == "Title" { - titleColumn = column - } - } - width := titleColumn.ComputedWidth - 2 - title = baseStyle.Foreground(b.Ctx.Theme.SecondaryText).Width(width).MaxWidth(width).Render(title) - name := b.Data.Name - if b.Data.IsCheckedOut { - name = baseStyle.Foreground(b.Ctx.Theme.SuccessText).Render(name) - } else { - name = baseStyle.Foreground(b.Ctx.Theme.PrimaryText).Render(name) - } - commitsAhead := "" - commitsBehind := "" - if b.Data.CommitsAhead > 0 { - commitsAhead = baseStyle.Render(fmt.Sprintf(" ↑%d", b.Data.CommitsAhead)) - } - if b.Data.CommitsBehind > 0 { - commitsBehind = baseStyle.Render(fmt.Sprintf(" ↓%d", b.Data.CommitsBehind)) - } - top := baseStyle.Width(width).MaxWidth(width).Render(lipgloss.JoinHorizontal(lipgloss.Left, name, commitsAhead, commitsBehind)) - - return baseStyle.Render(lipgloss.JoinVertical(lipgloss.Left, top, title)) + title := b.renderPRTitleOrCommigMsg(isSelected, width) + branch := b.renderBranch(isSelected, width) + return baseStyle.Render(lipgloss.JoinVertical(lipgloss.Left, branch, title)) } func (pr *Branch) renderAuthor() string { @@ -311,3 +281,64 @@ func (b *Branch) ToTableRow(isSelected bool) table.Row { b.renderUpdateAt(), } } + +func (b *Branch) renderBranch(isSelected bool, width int) string { + baseStyle := b.getBaseStyle(isSelected) + name := b.Data.Name + if b.Data.IsCheckedOut { + name = baseStyle.Foreground(b.Ctx.Theme.SuccessText).Render(name) + } else { + name = baseStyle.Foreground(b.Ctx.Theme.PrimaryText).Render(name) + } + return baseStyle.MaxHeight(1).Width(width).MaxWidth(width).Render(lipgloss.JoinHorizontal( + lipgloss.Top, + name, + b.renderCommitsAheadBehind(isSelected), + )) + +} + +func (b *Branch) getBaseStyle(isSelected bool) lipgloss.Style { + baseStyle := lipgloss.NewStyle() + if isSelected { + baseStyle = baseStyle.Background(b.Ctx.Theme.SelectedBackground) + } + + return baseStyle +} + +func (b *Branch) getMaxWidth() int { + var titleColumn table.Column + for _, column := range b.Columns { + if column.Grow != nil && *column.Grow { + titleColumn = column + } + } + return titleColumn.ComputedWidth - 2 +} + +func (b *Branch) renderCommitsAheadBehind(isSelected bool) string { + baseStyle := b.getBaseStyle(isSelected) + + commitsAhead := "" + commitsBehind := "" + if b.Data.CommitsAhead > 0 { + commitsAhead = baseStyle.Foreground(b.Ctx.Theme.WarningText).Render(fmt.Sprintf(" ↑%d", b.Data.CommitsAhead)) + } + if b.Data.CommitsBehind > 0 { + commitsBehind = baseStyle.Foreground(b.Ctx.Theme.WarningText).Render(fmt.Sprintf(" ↓%d", b.Data.CommitsBehind)) + } + + return lipgloss.JoinHorizontal(lipgloss.Top, commitsAhead, commitsBehind) +} + +func (b *Branch) renderPRTitleOrCommigMsg(isSelected bool, width int) string { + baseStyle := b.getBaseStyle(isSelected) + title := "-" + if b.PR != nil { + title = fmt.Sprintf("#%d %s", b.PR.Number, b.PR.Title) + } else if b.Data.LastCommitMsg != nil { + title = *b.Data.LastCommitMsg + } + return baseStyle.Foreground(b.Ctx.Theme.SecondaryText).Width(width).MaxWidth(width).Render(title) +} diff --git a/ui/components/pr/pr.go b/ui/components/pr/pr.go index 81e81cc7..084b2802 100644 --- a/ui/components/pr/pr.go +++ b/ui/components/pr/pr.go @@ -191,9 +191,6 @@ func (pr *PullRequest) renderExtendedTitle(isSelected bool) string { baseStyle = baseStyle.Foreground(pr.Ctx.Theme.SecondaryText).Background(pr.Ctx.Theme.SelectedBackground) } - if pr.Data == nil { - return pr.renderBranch(isSelected) - } repoName := baseStyle.Render(lipgloss.JoinHorizontal(lipgloss.Top, pr.Data.Repository.NameWithOwner, fmt.Sprintf(" #%d", pr.Data.Number))) author := baseStyle.Render(fmt.Sprintf("@%s", pr.Data.Author.Login)) branch := baseStyle.Render(pr.Data.HeadRefName) @@ -201,30 +198,14 @@ func (pr *PullRequest) renderExtendedTitle(isSelected bool) string { title := pr.Data.Title var titleColumn table.Column for _, column := range pr.Columns { - if column.Title == "Title" { + if column.Grow != nil && *column.Grow { titleColumn = column } } width := titleColumn.ComputedWidth - 2 top = baseStyle.Foreground(pr.Ctx.Theme.SecondaryText).Width(width).MaxWidth(width).Height(1).MaxHeight(1).Render(top) - title = baseStyle.Foreground(pr.Ctx.Theme.PrimaryText).Width(width).MaxWidth(width).Render(title) - - return baseStyle.Render(lipgloss.JoinVertical(lipgloss.Left, top, title)) -} - -func (pr *PullRequest) renderBranch(isSelected bool) string { - baseStyle := lipgloss.NewStyle() - if isSelected { - baseStyle = baseStyle.Foreground(pr.Ctx.Theme.SecondaryText).Background(pr.Ctx.Theme.SelectedBackground) - } + title = baseStyle.Foreground(pr.Ctx.Theme.PrimaryText).Width(width).MaxWidth(width).Height(1).MaxHeight(1).Render(title) - // repoName := strings.TrimPrefix(pr.Ctx.Repo.Origin, "https://github.com/") - // repoName = strings.TrimSuffix(repoName, ".git") - top := lipgloss.JoinHorizontal(lipgloss.Top, baseStyle.Render("TODO"), baseStyle.Render(" · "), baseStyle.Render("UNPUBLISHED")) - branch := pr.Branch.Name - width := max(lipgloss.Width(top), lipgloss.Width(branch)) - top = baseStyle.Foreground(pr.Ctx.Theme.SecondaryText).Width(width).Render(top) - title := baseStyle.Foreground(pr.Ctx.Theme.PrimaryText).Width(width).Render(branch) return baseStyle.Render(lipgloss.JoinVertical(lipgloss.Left, top, title)) } From 0bd8eae2e7e9b83b3d610451c2ce8071c27822dc Mon Sep 17 00:00:00 2001 From: Dolev Hadar Date: Sat, 24 Aug 2024 14:25:56 +0300 Subject: [PATCH 5/6] fix: warning color to yellow --- ui/theme/theme.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/theme/theme.go b/ui/theme/theme.go index 730717b4..91584db9 100644 --- a/ui/theme/theme.go +++ b/ui/theme/theme.go @@ -29,7 +29,7 @@ var DefaultTheme = &Theme{ FaintText: lipgloss.AdaptiveColor{Light: "007", Dark: "245"}, InvertedText: lipgloss.AdaptiveColor{Light: "015", Dark: "236"}, SuccessText: lipgloss.AdaptiveColor{Light: "002", Dark: "002"}, - WarningText: lipgloss.AdaptiveColor{Light: "001", Dark: "001"}, + WarningText: lipgloss.AdaptiveColor{Light: "003", Dark: "003"}, } func ParseTheme(cfg *config.Config) Theme { From 8564d0bfcf9f3e97be285eac2c7611a22766b15b Mon Sep 17 00:00:00 2001 From: Dolev Hadar Date: Sat, 24 Aug 2024 14:28:11 +0300 Subject: [PATCH 6/6] chore: remove logs --- ui/components/reposection/commands.go | 2 -- ui/ui.go | 4 ---- 2 files changed, 6 deletions(-) diff --git a/ui/components/reposection/commands.go b/ui/components/reposection/commands.go index 744f7748..0c8cb2c1 100644 --- a/ui/components/reposection/commands.go +++ b/ui/components/reposection/commands.go @@ -6,7 +6,6 @@ import ( gitm "github.com/aymanbagabas/git-module" tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/log" "github.com/dlvhdr/gh-dash/v4/data" "github.com/dlvhdr/gh-dash/v4/git" @@ -38,7 +37,6 @@ func (m *Model) push() (tea.Cmd, error) { startCmd := m.Ctx.StartTask(task) return tea.Batch(startCmd, func() tea.Msg { var err error - log.Debug("Pushing branch", "branch", b.Data.Name, "remotes", len(b.Data.Remotes), "repoPath", *m.Ctx.RepoPath) if len(b.Data.Remotes) == 0 { err = gitm.Push(*m.Ctx.RepoPath, "origin", b.Data.Name, gitm.PushOptions{CommandOptions: gitm.CommandOptions{Args: []string{"--set-upstream"}}}) } else { diff --git a/ui/ui.go b/ui/ui.go index ff4eaf3a..22a694e6 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -269,7 +269,6 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { cmd = tea.Quit case m.ctx.View == config.RepoView: - log.Debug("RepoView") switch { case key.Matches(msg, keys.BranchKeys.Delete): if currSection != nil { @@ -291,8 +290,6 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { cmd = fetchSectionsCmds } m.onViewedRowChanged() - default: - log.Debug("Not a top-level binding for RepoView") } case m.ctx.View == config.PRsView: @@ -432,7 +429,6 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.ctx.Config = &msg.Config m.ctx.Theme = theme.ParseTheme(m.ctx.Config) m.ctx.Styles = context.InitStyles(m.ctx.Theme) - log.Debug("Config loaded", "default view", m.ctx.Config.Defaults.View) m.ctx.View = m.ctx.Config.Defaults.View m.currSectionId = m.getCurrentViewDefaultSection() m.sidebar.IsOpen = msg.Config.Defaults.Preview.Open