summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorewy <ewy0@protonmail.com>2026-04-17 00:00:40 +0200
committerewy <ewy0@protonmail.com>2026-04-17 00:00:40 +0200
commit022072ce8bac114ea0a7b94132ed3b8630de2f90 (patch)
tree199fb99ab9e242b6eb195c2b1180e58caa4303d6
parentfbdc2b9d849913ccf8dd7a9001012ce2d28cbd2f (diff)
* fix bug which prevented multiple sources from showing up in the tui at the same time
* add padding to bottom of sources * add git status panel to bottom of tui
-rw-r--r--git/git.go99
-rw-r--r--indexers/pikdex/hydrate.go5
-rw-r--r--main.go7
-rw-r--r--menu/menu.go6
-rw-r--r--menu/model.go2
-rw-r--r--menu/source.go47
-rw-r--r--model/git.go6
-rw-r--r--model/init.go2
-rw-r--r--model/mod.go4
-rw-r--r--model/source.go5
-rw-r--r--runner/shell/hydrated.go13
-rw-r--r--runner/shell/shell.go13
12 files changed, 181 insertions, 28 deletions
diff --git a/git/git.go b/git/git.go
new file mode 100644
index 0000000..36b72b1
--- /dev/null
+++ b/git/git.go
@@ -0,0 +1,99 @@
+package git
+
+import (
+ "errors"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "pik/model"
+ "pik/spool"
+ "strconv"
+ "strings"
+)
+
+type gitMod struct {
+ Git string
+ err error
+}
+
+func (g *gitMod) Init() error {
+ p, err := exec.LookPath("git")
+ if err != nil {
+ g.err = err
+ return nil
+ }
+ g.Git = p
+ return nil
+}
+
+var Git = &gitMod{}
+
+func (g *gitMod) Mod(source *model.Source, result *model.HydratedSource) error {
+ gitFolder := filepath.Join(source.Path, ".git")
+ if st, err := os.Stat(gitFolder); err == nil && st.IsDir() {
+ if g.Git == "" {
+ spool.Warn("source %v seems to be a git repository but git is not installed\n", source.Identity.Full)
+ return nil
+ }
+ branch, err := g.Branch(source)
+ if err != nil {
+ spool.Warn("%v", err)
+ return nil
+ }
+ ch, in, de, err := g.Diff(source)
+ if err != nil {
+ spool.Warn("%v", err)
+ return nil
+ }
+ result.Git = &model.GitInfo{
+ Branch: branch,
+ Insertions: in,
+ Deletions: de,
+ Changes: ch,
+ }
+ }
+
+ return nil
+}
+
+func (g *gitMod) Branch(source *model.Source) (string, error) {
+ cmd := exec.Command(g.Git, "branch", "--show-current")
+ cmd.Dir = source.Path
+ b, err := cmd.CombinedOutput()
+ return strings.TrimSpace(string(b)), err
+}
+
+var UnknownResponseError = errors.New("unknown response")
+
+func (g *gitMod) Diff(source *model.Source) (int, int, int, error) {
+ cmd := exec.Command(g.Git, "diff", "--shortstat")
+ cmd.Dir = source.Path
+ b, err := cmd.CombinedOutput()
+ if err != nil {
+ return 0, 0, 0, err
+ }
+ split := strings.Split(string(b), ",")
+ changes := 0
+ insertions := 0
+ deletions := 0
+ for _, s := range split {
+ var e error
+ pt := strings.Split(strings.TrimSpace(s), " ")
+ num, e := strconv.Atoi(pt[0])
+ switch {
+ case strings.Contains(s, "changed"):
+ changes = num
+ case strings.Contains(s, "insertions"):
+ insertions = num
+ case strings.Contains(s, "deletions"):
+ deletions = num
+ default:
+ return changes, insertions, deletions, UnknownResponseError
+ }
+
+ if e != nil {
+ return changes, insertions, deletions, e
+ }
+ }
+ return changes, insertions, deletions, nil
+}
diff --git a/indexers/pikdex/hydrate.go b/indexers/pikdex/hydrate.go
index bc5197d..ec7f71f 100644
--- a/indexers/pikdex/hydrate.go
+++ b/indexers/pikdex/hydrate.go
@@ -2,10 +2,11 @@ package pikdex
import (
"pik/model"
+ "strings"
)
-func (u *pikdex) Hydrate(src *model.Source, result *model.HydratedSource) error {
- mod := u.mods[src.Path]
+func (u *pikdex) Mod(src *model.Source, result *model.HydratedSource) error {
+ mod := u.mods[strings.TrimSuffix(src.Path, "/")]
if mod.Path != "" {
if mod.Aliases != nil {
result.Aliases = append(result.Aliases, mod.Aliases...)
diff --git a/main.go b/main.go
index d12f203..8ea2e14 100644
--- a/main.go
+++ b/main.go
@@ -7,6 +7,7 @@ import (
"pik/cache"
"pik/crawl"
"pik/flags"
+ "pik/git"
"pik/indexers/pikdex"
"pik/menu"
"pik/model"
@@ -20,8 +21,9 @@ import (
"pik/spool"
)
-var initializers = []model.HasInit{
+var initializers = []model.Initializer{
python.Python,
+ git.Git,
}
var indexers = []model.Indexer{
@@ -35,8 +37,9 @@ var runners = []model.Runner{
python.Python,
}
-var hydrators = []model.Hydrator{
+var hydrators = []model.Modder{
pikdex.Indexer,
+ git.Git,
}
var ForceConfirm = false
diff --git a/menu/menu.go b/menu/menu.go
index 7f71e2d..97b4078 100644
--- a/menu/menu.go
+++ b/menu/menu.go
@@ -10,7 +10,7 @@ import (
var WrongModelTypeError = errors.New("wrong model type")
var NoSourcesIndexedError = errors.New("no sources indexed")
-func Show(st *model.State, hydrators []model.Hydrator) (*model.HydratedSource, model.HydratedTarget, error) {
+func Show(st *model.State, hydrators []model.Modder) (*model.HydratedSource, model.HydratedTarget, error) {
if len(st.Sources) == 0 {
return nil, nil, NoSourcesIndexedError
}
@@ -29,7 +29,7 @@ func Show(st *model.State, hydrators []model.Hydrator) (*model.HydratedSource, m
return src, t, nil
}
-func Hydrate(st *model.State, hydrators []model.Hydrator) *model.HydratedState {
+func Hydrate(st *model.State, hydrators []model.Modder) *model.HydratedState {
hyd := &model.HydratedState{
State: st,
HydratedSources: make([]*model.HydratedSource, len(st.Sources)),
@@ -38,7 +38,7 @@ func Hydrate(st *model.State, hydrators []model.Hydrator) *model.HydratedState {
hydSrc := s.Hydrate(hydrators)
for _, h := range hydrators {
- err := h.Hydrate(s, hydSrc)
+ err := h.Mod(s, hydSrc)
if err != nil {
spool.Warn("%v\n", err)
continue
diff --git a/menu/model.go b/menu/model.go
index 6dc4e1c..4eb2577 100644
--- a/menu/model.go
+++ b/menu/model.go
@@ -57,7 +57,7 @@ func (m *Model) Validate() {
}
}
-func NewModel(st *model.State, hydrators []model.Hydrator) *Model {
+func NewModel(st *model.State, hydrators []model.Modder) *Model {
m := &Model{
HydratedState: Hydrate(st, hydrators),
Index: 0,
diff --git a/menu/source.go b/menu/source.go
index 39b3280..efe4e0a 100644
--- a/menu/source.go
+++ b/menu/source.go
@@ -4,11 +4,12 @@ import (
"github.com/charmbracelet/lipgloss"
"pik/menu/style"
"pik/model"
+ "strconv"
)
var (
SourceStyle = style.New(func() lipgloss.Style {
- st := lipgloss.NewStyle()
+ st := lipgloss.NewStyle().PaddingBottom(1)
return st
})
SourceHeaderBackground = lipgloss.Color("5")
@@ -47,11 +48,18 @@ func (m *Model) Source(src *model.HydratedSource) string {
icon := SourceIconStyle.Render(Icon(src.Icon))
- return SourceStyle.Render(lipgloss.JoinVertical(lipgloss.Top,
+ parts := []string{
SourceHeaderStyle.Render(lipgloss.JoinHorizontal(lipgloss.Left, SourceLabelStyle.Render(lipgloss.JoinHorizontal(lipgloss.Left, icon, src.Label()), SourcePathStyle.Render(src.ShortPath())))),
SourceTargetsStyle.Render(targetContent),
- ))
+ }
+
+ if src.Git != nil {
+ parts = append(parts, Git(src.Git))
+ }
+ return SourceStyle.Render(lipgloss.JoinVertical(lipgloss.Top,
+ parts...,
+ ))
}
var (
@@ -68,3 +76,36 @@ func (m *Model) State(st *model.HydratedState) string {
return StateStyle.Render(lipgloss.JoinVertical(lipgloss.Top, sources...))
}
+
+var (
+ GitColor = lipgloss.Color("4")
+ GitInfoStyle = style.New(func() lipgloss.Style {
+ return lipgloss.NewStyle().Background(GitColor).Border(lipgloss.OuterHalfBlockBorder(), false, false, false, true).BorderBackground(GitColor).Padding(0, 1)
+ })
+ GitStatusStyle = style.New(func() lipgloss.Style {
+ return lipgloss.NewStyle().Bold(true).Background(GitColor).PaddingLeft(1)
+ })
+ GitAddColor = lipgloss.Color("2")
+ GitAddStyle = style.New(func() lipgloss.Style {
+ return GitStatusStyle.Get().Foreground(GitAddColor)
+ })
+ GitRemoveColor = lipgloss.Color("1")
+ GitRemoveStyle = style.New(func() lipgloss.Style {
+ return GitStatusStyle.Get().Foreground(GitRemoveColor)
+ })
+ GitChangeColor = lipgloss.Color("5")
+ GitChangeStyle = style.New(func() lipgloss.Style {
+ return GitStatusStyle.Get().Foreground(GitChangeColor)
+ })
+)
+
+func Git(info *model.GitInfo) string {
+
+ return GitInfoStyle.Render(lipgloss.JoinHorizontal(lipgloss.Left,
+ " ",
+ info.Branch,
+ GitAddStyle.Render("+"+strconv.Itoa(info.Insertions)),
+ GitRemoveStyle.Render("-"+strconv.Itoa(info.Deletions)),
+ GitChangeStyle.Render("~"+strconv.Itoa(info.Changes)),
+ ))
+}
diff --git a/model/git.go b/model/git.go
new file mode 100644
index 0000000..35d7557
--- /dev/null
+++ b/model/git.go
@@ -0,0 +1,6 @@
+package model
+
+type GitInfo struct {
+ Branch string
+ Insertions, Deletions, Changes int
+}
diff --git a/model/init.go b/model/init.go
index 503d2b8..13158da 100644
--- a/model/init.go
+++ b/model/init.go
@@ -1,5 +1,5 @@
package model
-type HasInit interface {
+type Initializer interface {
Init() error
}
diff --git a/model/mod.go b/model/mod.go
index f6a7a94..0a680b5 100644
--- a/model/mod.go
+++ b/model/mod.go
@@ -1,5 +1,5 @@
package model
-type Hydrator interface {
- Hydrate(source *Source, result *HydratedSource) error
+type Modder interface {
+ Mod(source *Source, result *HydratedSource) error
}
diff --git a/model/source.go b/model/source.go
index 3a5fe10..29cff05 100644
--- a/model/source.go
+++ b/model/source.go
@@ -18,6 +18,7 @@ type HydratedSource struct {
HydratedTargets []HydratedTarget
Aliases []string
Icon string
+ Git *GitInfo
}
func (s *Source) Label() string {
@@ -31,13 +32,13 @@ func (s *HydratedSource) Label() string {
return s.Identity.Full
}
-func (s *Source) Hydrate(hydrators []Hydrator) *HydratedSource {
+func (s *Source) Hydrate(hydrators []Modder) *HydratedSource {
hs := &HydratedSource{
Source: s,
HydratedTargets: make([]HydratedTarget, 0, len(s.Targets)),
}
for _, h := range hydrators {
- err := h.Hydrate(s, hs)
+ err := h.Mod(s, hs)
if err != nil {
spool.Warn("%v", err)
}
diff --git a/runner/shell/hydrated.go b/runner/shell/hydrated.go
index e1f8892..7ead74c 100644
--- a/runner/shell/hydrated.go
+++ b/runner/shell/hydrated.go
@@ -1,7 +1,9 @@
package shell
import (
+ "errors"
"pik/describe"
+ "pik/model"
"pik/runner"
"pik/spool"
)
@@ -22,3 +24,14 @@ func (h *Hydrated) Description() string {
}
return desc
}
+
+var WrongTargetError = errors.New("wrong target type")
+
+func (s *shell) Hydrate(target model.Target) (model.HydratedTarget, error) {
+ cast, ok := target.(*Target)
+ if !ok {
+ return nil, WrongTargetError
+ }
+ hyd := &Hydrated{BaseHydration: runner.Hydrated(cast)}
+ return hyd, nil
+}
diff --git a/runner/shell/shell.go b/runner/shell/shell.go
index 9d34e0f..c9d7134 100644
--- a/runner/shell/shell.go
+++ b/runner/shell/shell.go
@@ -35,17 +35,6 @@ type shell struct {
Locations map[string]string
}
-var WrongTargetError = errors.New("wrong target type")
-
-func (s *shell) Hydrate(target model.Target) (model.HydratedTarget, error) {
- cast, ok := target.(*Target)
- if !ok {
- return nil, WrongTargetError
- }
- hyd := &Hydrated{BaseHydration: runner.Hydrated(cast)}
- return hyd, nil
-}
-
func (s *shell) Wants(f fs.FS, file string, entry fs.DirEntry) (bool, error) {
if entry != nil && entry.IsDir() {
return false, nil
@@ -108,7 +97,7 @@ func (s *shell) CreateTarget(fs fs.FS, src string, file string, _ fs.DirEntry) (
MyTags: model.TagsFromFilename(filename),
},
Shell: shell,
- Script: file,
+ Script: filepath.Join(src, file),
SubValue: sub,
}, nil
}