diff options
| -rw-r--r-- | git/git.go | 99 | ||||
| -rw-r--r-- | indexers/pikdex/hydrate.go | 5 | ||||
| -rw-r--r-- | main.go | 7 | ||||
| -rw-r--r-- | menu/menu.go | 6 | ||||
| -rw-r--r-- | menu/model.go | 2 | ||||
| -rw-r--r-- | menu/source.go | 47 | ||||
| -rw-r--r-- | model/git.go | 6 | ||||
| -rw-r--r-- | model/init.go | 2 | ||||
| -rw-r--r-- | model/mod.go | 4 | ||||
| -rw-r--r-- | model/source.go | 5 | ||||
| -rw-r--r-- | runner/shell/hydrated.go | 13 | ||||
| -rw-r--r-- | runner/shell/shell.go | 13 |
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...) @@ -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 } |
