Skip to content

Commit

Permalink
feat(ui): add stash view
Browse files Browse the repository at this point in the history
Display repository stashed items
  • Loading branch information
aymanbagabas committed Oct 25, 2023
1 parent 7517d55 commit 2e007a9
Show file tree
Hide file tree
Showing 11 changed files with 531 additions and 95 deletions.
127 changes: 65 additions & 62 deletions cmd/soft/browse.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"fmt"
"path/filepath"
"time"

Expand Down Expand Up @@ -31,24 +32,32 @@ var browseCmd = &cobra.Command{
return err
}

r, err := git.Open(abs)
if err != nil {
return fmt.Errorf("failed to open repository: %w", err)
}

// Bubble Tea uses Termenv default output so we have to use the same
// thing here.
output := termenv.DefaultOutput()
ctx := cmd.Context()
c := common.NewCommon(ctx, output, 0, 0)
comps := []common.TabComponent{
repo.NewReadme(c),
repo.NewFiles(c),
repo.NewLog(c),
}
if !r.IsBare {
comps = append(comps, repo.NewStash(c))
}
comps = append(comps, repo.NewRefs(c, git.RefsHeads), repo.NewRefs(c, git.RefsTags))
m := &model{
m: repo.New(c,
repo.NewReadme(c),
repo.NewFiles(c),
repo.NewLog(c),
repo.NewRefs(c, git.RefsHeads),
repo.NewRefs(c, git.RefsTags),
),
repoPath: abs,
c: c,
model: repo.New(c, comps...),
repo: repository{r},
common: c,
}

m.f = footer.New(c, m)
m.footer = footer.New(c, m)
p := tea.NewProgram(m,
tea.WithAltScreen(),
tea.WithMouseCellMotion(),
Expand All @@ -74,10 +83,10 @@ const (
)

type model struct {
m *repo.Repo
f *footer.Footer
repoPath string
c common.Common
model *repo.Repo
footer *footer.Footer
repo proto.Repository
common common.Common
state state
showFooter bool
error error
Expand All @@ -86,29 +95,29 @@ type model struct {
var _ tea.Model = &model{}

func (m *model) SetSize(w, h int) {
m.c.SetSize(w, h)
style := m.c.Styles.App.Copy()
m.common.SetSize(w, h)
style := m.common.Styles.App.Copy()
wm := style.GetHorizontalFrameSize()
hm := style.GetVerticalFrameSize()
if m.showFooter {
hm += m.f.Height()
hm += m.footer.Height()
}

m.f.SetSize(w-wm, h-hm)
m.m.SetSize(w-wm, h-hm)
m.footer.SetSize(w-wm, h-hm)
m.model.SetSize(w-wm, h-hm)
}

// ShortHelp implements help.KeyMap.
func (m model) ShortHelp() []key.Binding {
switch m.state {
case errorState:
return []key.Binding{
m.c.KeyMap.Back,
m.c.KeyMap.Quit,
m.c.KeyMap.Help,
m.common.KeyMap.Back,
m.common.KeyMap.Quit,
m.common.KeyMap.Help,
}
default:
return m.m.ShortHelp()
return m.model.ShortHelp()
}
}

Expand All @@ -118,122 +127,116 @@ func (m model) FullHelp() [][]key.Binding {
case errorState:
return [][]key.Binding{
{
m.c.KeyMap.Back,
m.common.KeyMap.Back,
},
{
m.c.KeyMap.Quit,
m.c.KeyMap.Help,
m.common.KeyMap.Quit,
m.common.KeyMap.Help,
},
}
default:
return m.m.FullHelp()
return m.model.FullHelp()
}
}

// Init implements tea.Model.
func (m *model) Init() tea.Cmd {
rr, err := git.Open(m.repoPath)
if err != nil {
return common.ErrorCmd(err)
}

r := repository{rr}
return tea.Batch(
m.m.Init(),
m.f.Init(),
m.model.Init(),
m.footer.Init(),
func() tea.Msg {
return repo.RepoMsg(r)
return repo.RepoMsg(m.repo)
},
repo.UpdateRefCmd(r),
repo.UpdateRefCmd(m.repo),
)
}

// Update implements tea.Model.
func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.c.Logger.Debugf("msg received: %T", msg)
m.common.Logger.Debugf("msg received: %T", msg)
cmds := make([]tea.Cmd, 0)
switch msg := msg.(type) {
case tea.WindowSizeMsg:
m.SetSize(msg.Width, msg.Height)
case tea.KeyMsg:
switch {
case key.Matches(msg, m.c.KeyMap.Back) && m.error != nil:
case key.Matches(msg, m.common.KeyMap.Back) && m.error != nil:
m.error = nil
m.state = startState
// Always show the footer on error.
m.showFooter = m.f.ShowAll()
case key.Matches(msg, m.c.KeyMap.Help):
m.showFooter = m.footer.ShowAll()
case key.Matches(msg, m.common.KeyMap.Help):
cmds = append(cmds, footer.ToggleFooterCmd)
case key.Matches(msg, m.c.KeyMap.Quit):
case key.Matches(msg, m.common.KeyMap.Quit):
// Stop bubblezone background workers.
m.c.Zone.Close()
m.common.Zone.Close()
return m, tea.Quit
}
case tea.MouseMsg:
switch msg.Type {
case tea.MouseLeft:
switch {
case m.c.Zone.Get("footer").InBounds(msg):
case m.common.Zone.Get("footer").InBounds(msg):
cmds = append(cmds, footer.ToggleFooterCmd)
}
}
case footer.ToggleFooterMsg:
m.f.SetShowAll(!m.f.ShowAll())
m.footer.SetShowAll(!m.footer.ShowAll())
m.showFooter = !m.showFooter
case common.ErrorMsg:
m.error = msg
m.state = errorState
m.showFooter = true
}

f, cmd := m.f.Update(msg)
m.f = f.(*footer.Footer)
f, cmd := m.footer.Update(msg)
m.footer = f.(*footer.Footer)
if cmd != nil {
cmds = append(cmds, cmd)
}

r, cmd := m.m.Update(msg)
m.m = r.(*repo.Repo)
r, cmd := m.model.Update(msg)
m.model = r.(*repo.Repo)
if cmd != nil {
cmds = append(cmds, cmd)
}

// This fixes determining the height margin of the footer.
m.SetSize(m.c.Width, m.c.Height)
m.SetSize(m.common.Width, m.common.Height)

return m, tea.Batch(cmds...)
}

// View implements tea.Model.
func (m *model) View() string {
style := m.c.Styles.App.Copy()
style := m.common.Styles.App.Copy()
wm, hm := style.GetHorizontalFrameSize(), style.GetVerticalFrameSize()
if m.showFooter {
hm += m.f.Height()
hm += m.footer.Height()
}

var view string
switch m.state {
case startState:
view = m.m.View()
view = m.model.View()
case errorState:
err := m.c.Styles.ErrorTitle.Render("Bummer")
err += m.c.Styles.ErrorBody.Render(m.error.Error())
view = m.c.Styles.Error.Copy().
Width(m.c.Width -
err := m.common.Styles.ErrorTitle.Render("Bummer")
err += m.common.Styles.ErrorBody.Render(m.error.Error())
view = m.common.Styles.Error.Copy().
Width(m.common.Width -
wm -
m.c.Styles.ErrorBody.GetHorizontalFrameSize()).
Height(m.c.Height -
m.common.Styles.ErrorBody.GetHorizontalFrameSize()).
Height(m.common.Height -
hm -
m.c.Styles.Error.GetVerticalFrameSize()).
m.common.Styles.Error.GetVerticalFrameSize()).
Render(err)
}

if m.showFooter {
view = lipgloss.JoinVertical(lipgloss.Top, view, m.f.View())
view = lipgloss.JoinVertical(lipgloss.Top, view, m.footer.View())
}

return m.c.Zone.Scan(style.Render(view))
return m.common.Zone.Scan(style.Render(view))
}

type repository struct {
Expand Down
21 changes: 21 additions & 0 deletions git/patch.go
Original file line number Diff line number Diff line change
Expand Up @@ -332,3 +332,24 @@ func (d *Diff) Patch() string {
}
return p.String()
}

func toDiff(ddiff *git.Diff) *Diff {
files := make([]*DiffFile, 0, len(ddiff.Files))
for _, df := range ddiff.Files {
sections := make([]*DiffSection, 0, len(df.Sections))
for _, ds := range df.Sections {
sections = append(sections, &DiffSection{
DiffSection: ds,
})
}
files = append(files, &DiffFile{
DiffFile: df,
Sections: sections,
})
}
diff := &Diff{
Diff: ddiff,
Files: files,
}
return diff
}
21 changes: 2 additions & 19 deletions git/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,32 +140,15 @@ func (r *Repository) TreePath(ref *Reference, path string) (*Tree, error) {

// Diff returns the diff for the given commit.
func (r *Repository) Diff(commit *Commit) (*Diff, error) {
ddiff, err := r.Repository.Diff(commit.ID.String(), DiffMaxFiles, DiffMaxFileLines, DiffMaxLineChars, git.DiffOptions{
diff, err := r.Repository.Diff(commit.ID.String(), DiffMaxFiles, DiffMaxFileLines, DiffMaxLineChars, git.DiffOptions{
CommandOptions: git.CommandOptions{
Envs: []string{"GIT_CONFIG_GLOBAL=/dev/null"},
},
})
if err != nil {
return nil, err
}
files := make([]*DiffFile, 0, len(ddiff.Files))
for _, df := range ddiff.Files {
sections := make([]*DiffSection, 0, len(df.Sections))
for _, ds := range df.Sections {
sections = append(sections, &DiffSection{
DiffSection: ds,
})
}
files = append(files, &DiffFile{
DiffFile: df,
Sections: sections,
})
}
diff := &Diff{
Diff: ddiff,
Files: files,
}
return diff, nil
return toDiff(diff), nil
}

// Patch returns the patch for the given reference.
Expand Down
16 changes: 16 additions & 0 deletions git/stash.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package git

import "github.com/gogs/git-module"

// StashDiff returns the diff of the given stash index.
func (r *Repository) StashDiff(index int) (*Diff, error) {
diff, err := r.Repository.StashDiff(index, DiffMaxFiles, DiffMaxFileLines, DiffMaxLineChars, git.DiffOptions{
CommandOptions: git.CommandOptions{
Envs: []string{"GIT_CONFIG_GLOBAL=/dev/null"},
},
})
if err != nil {
return nil, err
}
return toDiff(diff), nil
}
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ module github.com/charmbracelet/soft-serve

go 1.20

replace github.com/gogs/git-module => github.com/aymanbagabas/git-module v1.4.1-0.20231025145308-5e8facf7a213

require (
github.com/alecthomas/chroma v0.10.0
github.com/charmbracelet/bubbles v0.16.1
Expand Down
5 changes: 2 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFI
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/aymanbagabas/git-module v1.4.1-0.20231025145308-5e8facf7a213 h1:/tUfPeV5T/tn2UjvQedq1incFa9B9WkFHTv0fdt5Ah0=
github.com/aymanbagabas/git-module v1.4.1-0.20231025145308-5e8facf7a213/go.mod h1:3OBxY2gWeblk83u6BlGMO1TYDEbV4bspATMP/S2Kfsk=
github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
Expand Down Expand Up @@ -63,8 +65,6 @@ github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfC
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/gogs/git-module v1.8.3 h1:4N9HOLzkmSfb5y4Go4f/gdt1/Z60/aQaAKr8lbsfFps=
github.com/gogs/git-module v1.8.3/go.mod h1:yAn6ZMwh8x0u3fMotXqMP7Ct1XNNOZWNdBSBx6IFGCY=
github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE=
github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
Expand Down Expand Up @@ -209,7 +209,6 @@ golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfS
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
Expand Down
Loading

0 comments on commit 2e007a9

Please sign in to comment.