Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion internal/ui/app/launch.go → internal/app/launch.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package app

import (
tea "github.com/charmbracelet/bubbletea"
"github.com/michaelhass/gitglance/internal/ui/logger"
"github.com/michaelhass/gitglance/internal/core/logger"
)

type Option func(opts *options)
Expand Down
10 changes: 5 additions & 5 deletions internal/ui/app/model.go → internal/app/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import (
"time"

tea "github.com/charmbracelet/bubbletea"
"github.com/michaelhass/gitglance/internal/ui/dialog"
"github.com/michaelhass/gitglance/internal/ui/exit"
"github.com/michaelhass/gitglance/internal/ui/logger"
"github.com/michaelhass/gitglance/internal/ui/refresh"
"github.com/michaelhass/gitglance/internal/ui/status"
"github.com/michaelhass/gitglance/internal/core/exit"
"github.com/michaelhass/gitglance/internal/core/logger"
"github.com/michaelhass/gitglance/internal/core/refresh"
"github.com/michaelhass/gitglance/internal/core/ui/components/dialog"
"github.com/michaelhass/gitglance/internal/page/status"
)

// refreshInterval is the duration when a refresh.Msg is send.
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion internal/git/cmd.go → internal/core/git/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ func newDiffCmd(opts DiffOptions) *gitCommand {
}

if opts.IsUntracked {
args = append(args, untrackedFileDiffArgs[:3]...)
args = append(args, untrackedFileDiffArgs[:]...)
}

if len(opts.FilePath) > 0 {
Expand Down
9 changes: 9 additions & 0 deletions internal/git/git.go → internal/core/git/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,12 @@ func IsInWorkTree() bool {
func StashAll() error {
return newGitCommand("stash", "-u").run()
}

func GetStash() (Stash, error) {
out, err := newGitCommand("stash", "list").output()
if err != nil {
return Stash([]StashEntry{}), err
}
return newDefaultStashBuilder().
makeStashFromMultilineText(out)
}
94 changes: 94 additions & 0 deletions internal/core/git/stash.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package git

import (
"errors"
"regexp"
"strconv"
"strings"
)

const (
stashEntryIdxRegexPattern = `stash@{([0-9]+)}`
stashEntryComponentSeparator = ": "
)

var (
missingStashIdxErr = errors.New("No stash index found")
missingStashMsgErr = errors.New("No stash message found")
defaultStashIdxRegex *regexp.Regexp = regexp.MustCompile(stashEntryIdxRegexPattern)
)

type stashBuilder struct {
stashEntryIdxRegex *regexp.Regexp
}

func newDefaultStashBuilder() stashBuilder {
return stashBuilder{
stashEntryIdxRegex: defaultStashIdxRegex,
}
}

func (b stashBuilder) makeStashFromMultilineText(text string) (Stash, error) {
lines := strings.Split(text, "\n")
return b.makeStashFromLines(lines...)
}

func (b stashBuilder) makeStashFromLines(lines ...string) (Stash, error) {
var (
entries []StashEntry
err error
)

for _, line := range lines {
idx, idxErr := b.getStashEntryIdxFromLine(line)
if idxErr != nil {
err = idxErr
break
}
msg, msgErr := b.getStashEntryMsgFromLine(line)
if msgErr != nil {
err = msgErr
break
}

entries = append(entries, StashEntry{idx: idx, msg: msg})
}

return entries, err
}

func (b stashBuilder) getStashEntryIdxFromLine(line string) (int, error) {
matches := b.stashEntryIdxRegex.FindStringSubmatch(line)
if len(matches) < 2 {
return -1, missingStashIdxErr
}
idx, err := strconv.Atoi(matches[1])
if err != nil {
return -1, missingStashIdxErr
}
return idx, nil
}

func (b stashBuilder) getStashEntryMsgFromLine(line string) (string, error) {
idx := strings.Index(line, stashEntryComponentSeparator)
if idx == -1 {
return "", missingStashMsgErr
}
msgStartIdx := idx + len(stashEntryComponentSeparator)
return line[msgStartIdx:], nil
}

type StashEntry struct {
idx int
msg string
}

func (s StashEntry) Message() string {
return s.msg
}

func (s StashEntry) Index() int {
return s.idx
}

type Stash []StashEntry
63 changes: 63 additions & 0 deletions internal/core/git/stash_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package git

import (
"fmt"
"testing"
)

func TestGetStashIdx(t *testing.T) {
tests := []struct {
input string
expectIdx int
hasError bool
}{
{"stash@{12}", 12, false},
{"prefix stash@{12}", 12, false},
{"stash@{12} stash@{3} multiple", 12, false},
{"stash@{} empty", -1, true},
{"25", -1, true},
}

builder := newDefaultStashBuilder()
for _, tt := range tests {
testName := fmt.Sprintf("Reading idx from: `%s`", tt.input)
t.Run(testName, func(t *testing.T) {
gotIdx, gotErr := builder.getStashEntryIdxFromLine(tt.input)
if (tt.hasError && gotErr == nil) ||
(!tt.hasError && gotErr != nil) {
t.Error("Unexpected error result:", gotErr)
}
if gotIdx != tt.expectIdx {
t.Errorf("Got idx `%d` but expected `%d.`", gotIdx, tt.expectIdx)
}
})
}
}

func TestGetStashEntryMsg(t *testing.T) {
tests := []struct {
input string
expectMsg string
hasError bool
}{
{"stash@{12}", "", true},
{": ", "", false},
{"stash@{12}: this is the message", "this is the message", false},
{"stash@{12}: this is the message: more message", "this is the message: more message", false},
}

builder := newDefaultStashBuilder()
for _, tt := range tests {
testName := fmt.Sprintf("Reading message from: `%s`", tt.input)
t.Run(testName, func(t *testing.T) {
gotMsg, gotErr := builder.getStashEntryMsgFromLine(tt.input)
if (tt.hasError && gotErr == nil) ||
(!tt.hasError && gotErr != nil) {
t.Error("Unexpected error result:", gotErr)
}
if gotMsg != tt.expectMsg {
t.Errorf("Got msg`%s` but expected `%s`.", gotMsg, tt.expectMsg)
}
})
}
}
11 changes: 2 additions & 9 deletions internal/git/status.go → internal/core/git/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,15 +110,8 @@ func readFileStatusFromOutputComponent(component string) (FileStatus, error) {
}

func cleanedPathString(path string) string {
path = strings.TrimSuffix(path, " ")
path = strings.TrimPrefix(path, " ")

if strings.Contains(path, " ") &&
strings.HasPrefix(path, "\"") &&
strings.HasSuffix(path, "\"") {
path = path[1 : len(path)-1]
}

path = strings.Trim(path, " ")
path = strings.Trim(path, "\"")
return path
}

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ package container
import (
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
styles "github.com/michaelhass/gitglance/internal/ui/style"
"github.com/michaelhass/gitglance/internal/core/ui/style"
)

const (
Expand All @@ -17,11 +17,11 @@ const (
)

var (
inactiveTitleStyle = styles.InactiveTitle.Height(titleHeight)
focusTitleStyle = styles.Title.Height(titleHeight)
inactiveTitleStyle = style.InactiveTitle.Height(titleHeight)
focusTitleStyle = style.Title.Height(titleHeight)

inactiveBorderStyle = styles.InactiveBorder.PaddingLeft(paddingLeft)
focusBorderStyle = styles.FocusBorder.PaddingLeft(paddingLeft)
inactiveBorderStyle = style.InactiveBorder.PaddingLeft(paddingLeft)
focusBorderStyle = style.FocusBorder.PaddingLeft(paddingLeft)
)

type Model struct {
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import (
"github.com/charmbracelet/bubbles/key"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/michaelhass/gitglance/internal/ui/dialog"
styles "github.com/michaelhass/gitglance/internal/ui/style"
"github.com/michaelhass/gitglance/internal/core/ui/components/dialog"
styles "github.com/michaelhass/gitglance/internal/core/ui/style"
)

const (
Expand Down Expand Up @@ -43,7 +43,7 @@ func (m Model) Init() tea.Cmd {
return nil
}

func (m Model) Update(msg tea.Msg) (dialog.Content, tea.Cmd) {
func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
if keyMsg, ok := msg.(tea.KeyMsg); ok && key.Matches(keyMsg, m.keys.confirm) {
return m, tea.Sequence(m.confirmCmd, dialog.Close())
}
Expand All @@ -67,7 +67,7 @@ func (m Model) Help() []key.Binding {
}
}

func (m Model) SetSize(width, height int) dialog.Content {
func (m Model) SetSize(width, height int) Model {
m.width = width
m.height = height
return m
Expand Down
33 changes: 33 additions & 0 deletions internal/core/ui/components/dialog/confirm/dialog.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package confirm

import (
tea "github.com/charmbracelet/bubbletea"
"github.com/michaelhass/gitglance/internal/core/ui/components/dialog"
)

type DialogContent struct {
Model
}

func NewDialogConent(confirm Model) DialogContent {
return DialogContent{Model: confirm}
}

func (dc DialogContent) Init() tea.Cmd {
return dc.Model.Init()
}

func (dc DialogContent) Update(msg tea.Msg) (dialog.Content, tea.Cmd) {
model, cmd := dc.Model.Update(msg)
dc.Model = model
return dc, cmd
}

func (dc DialogContent) View() string {
return dc.Model.View()
}

func (dc DialogContent) SetSize(width, height int) dialog.Content {
dc.Model = dc.Model.SetSize(width, height)
return dc
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"github.com/charmbracelet/bubbles/key"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/michaelhass/gitglance/internal/ui/style"
"github.com/michaelhass/gitglance/internal/core/ui/style"
)

type DisplayMode byte
Expand Down
37 changes: 37 additions & 0 deletions internal/core/ui/components/list/container.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package list

import (
tea "github.com/charmbracelet/bubbletea"
"github.com/michaelhass/gitglance/internal/core/ui/components/container"
)

// ContainerContent is a wrapper to use the filelist ui as container.ContainerContent.
type ContainerContent struct {
Model
}

func NewContainerContent(model Model) ContainerContent {
return ContainerContent{Model: model}
}

func NewContainer(model Model) container.Model {
containerContent := NewContainerContent(model)
return container.New(containerContent)
}

func (c ContainerContent) Update(msg tea.Msg) (container.Content, tea.Cmd) {
model, cmd := c.Model.Update(msg)
c.Model = model
return c, cmd
}

func (c ContainerContent) UpdateFocus(isFocused bool) (container.Content, tea.Cmd) {
model, cmd := c.Model.UpdateFocus(isFocused)
c.Model = model
return c, cmd
}

func (c ContainerContent) SetSize(width, height int) container.Content {
c.Model = c.Model.SetSize(width, height)
return c
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package file
import (
"fmt"

"github.com/michaelhass/gitglance/internal/git"
"github.com/michaelhass/gitglance/internal/core/git"
)

type Item struct {
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"github.com/charmbracelet/bubbles/key"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/michaelhass/gitglance/internal/ui/style"
"github.com/michaelhass/gitglance/internal/core/ui/style"
)

var (
Expand Down
File renamed without changes.
Loading