Archived
Compare commits
8
Commits
633de5aab1
...
v0.2.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
90ea9cc241 | ||
|
|
e4340fb35e | ||
|
|
6cb7bd6967 | ||
|
|
17151a578d | ||
|
|
aec4ec6e3b | ||
|
|
e5fdc875af | ||
|
|
af6d03a5b1 | ||
|
|
69d0f9b704 |
+6
-4
@@ -53,15 +53,17 @@ everyone else's checkout.
|
|||||||
|
|
||||||
**Context.** Only the first level is shown by default, so nested problems
|
**Context.** Only the first level is shown by default, so nested problems
|
||||||
would be invisible.
|
would be invisible.
|
||||||
**Decision.** Collapsed parents show `·k` (issues in all descendants), colored
|
**Decision.** Collapsed parents show `·k` (issues in all descendants), one
|
||||||
by the worst one. Problems in a child do **not** mark the parent itself as
|
count per level: red for danger, then yellow for warn (amended: a single
|
||||||
`changed`.
|
count in the worst color made 1 error + 1 warning look like 2 errors; the
|
||||||
|
status column grew from 14 to 16 for the second count). Problems in a child
|
||||||
|
do **not** mark the parent itself as `changed`.
|
||||||
|
|
||||||
## D8 — Collapsed by default, branch-first navigation
|
## D8 — Collapsed by default, branch-first navigation
|
||||||
|
|
||||||
**Decision.** Only the first level is visible. `→` expands and enters the
|
**Decision.** Only the first level is visible. `→` expands and enters the
|
||||||
first child, `←` collapses and goes to the parent, `space` toggles,
|
first child, `←` collapses and goes to the parent, `space` toggles,
|
||||||
`ctrl+→/←` expand/collapse all (see [ux-guide.md](ux-guide.md#navigation)).
|
`shift+→/←`, `L`/`H` (or `ctrl+→/←`) expand/collapse all (see [ux-guide.md](ux-guide.md#navigation)).
|
||||||
`←` on an expanded node collapses it in place, so a node can be closed
|
`←` on an expanded node collapses it in place, so a node can be closed
|
||||||
without leaving it. Remembered expansion is kept on collapse, but reset by
|
without leaving it. Remembered expansion is kept on collapse, but reset by
|
||||||
"collapse all".
|
"collapse all".
|
||||||
|
|||||||
@@ -34,7 +34,8 @@ commit hashes. It doesn't answer the questions users actually have:
|
|||||||
- **F5** A collapsed submodule shows the number of its **direct** children,
|
- **F5** A collapsed submodule shows the number of its **direct** children,
|
||||||
e.g. `[2]`.
|
e.g. `[2]`.
|
||||||
- **F6** A collapsed submodule shows the number of issues **hidden** in all
|
- **F6** A collapsed submodule shows the number of issues **hidden** in all
|
||||||
its descendants (`·2`), colored by the most severe hidden issue.
|
its descendants (`·1 ·2`), one count per level: errors in red, then
|
||||||
|
warnings in yellow.
|
||||||
- **F7** Expansion state of descendants is remembered when a parent is
|
- **F7** Expansion state of descendants is remembered when a parent is
|
||||||
collapsed and restored when it is expanded again (except after
|
collapsed and restored when it is expanded again (except after
|
||||||
"collapse all", which resets it).
|
"collapse all", which resets it).
|
||||||
|
|||||||
+5
-3
@@ -72,7 +72,7 @@ truth for what counts as a problem.
|
|||||||
| `conflict` | Merge conflict in the worktree | Danger |
|
| `conflict` | Merge conflict in the worktree | Danger |
|
||||||
| `missing` | Not initialized; directory missing | Warn |
|
| `missing` | Not initialized; directory missing | Warn |
|
||||||
| `off pin` | Checkout differs from pin | Warn |
|
| `off pin` | Checkout differs from pin | Warn |
|
||||||
| `changed` | Uncommitted changes; untracked files | Warn |
|
| `changed` | Uncommitted changes; untracked files (worded "untracked repository" if all of them are git repositories) | Warn |
|
||||||
|
|
||||||
Rules:
|
Rules:
|
||||||
|
|
||||||
@@ -96,8 +96,10 @@ Rules:
|
|||||||
### Hidden issues
|
### Hidden issues
|
||||||
|
|
||||||
`Submodule.HiddenIssues()` counts the issues of **all** loaded descendants
|
`Submodule.HiddenIssues()` counts the issues of **all** loaded descendants
|
||||||
and returns the most severe level. A collapsed submodule shows this as `·k`
|
per level (`Hidden{Warn, Danger}`). A collapsed submodule shows them as `·k`
|
||||||
in that level's color, so a danger can never hide inside a collapsed subtree.
|
per level, red first, then yellow, so a danger can never hide inside a
|
||||||
|
collapsed subtree and a warning is never mistaken for one.
|
||||||
|
A descendant whose load failed (`error`) counts as one danger.
|
||||||
|
|
||||||
## Resolving a pin to a ref
|
## Resolving a pin to a ref
|
||||||
|
|
||||||
|
|||||||
+14
-9
@@ -26,7 +26,7 @@
|
|||||||
my_submodule_3 unpushed origin/main ↑1 │ Pinned b7c8d9e Hotfix null pointer
|
my_submodule_3 unpushed origin/main ↑1 │ Pinned b7c8d9e Hotfix null pointer
|
||||||
libs/ │ Checkout b7c8d9e Hotfix null pointer
|
libs/ │ Checkout b7c8d9e Hotfix null pointer
|
||||||
logging origin/main ↓3 │ Tip 6f5e4d3 Release 2.1.0
|
logging origin/main ↓3 │ Tip 6f5e4d3 Release 2.1.0
|
||||||
↑/↓ move • ←/→ collapse/expand • space toggle • u checkout pin • … • ? help • q quit
|
↑↓/jk move • ←→/hl fold • ⇧←→/HL fold all • space toggle • ? help • q quit
|
||||||
```
|
```
|
||||||
|
|
||||||
| Area | Height / width |
|
| Area | Height / width |
|
||||||
@@ -47,7 +47,7 @@ indent ▸ name [n] status ref delta
|
|||||||
| indent | 2 per depth | |
|
| indent | 2 per depth | |
|
||||||
| toggle | 3 | `▸` collapsed, `▾` expanded, blank for leaves |
|
| toggle | 3 | `▸` collapsed, `▾` expanded, blank for leaves |
|
||||||
| name | fits the widest visible name | name, `[n]` = direct children (collapsed only) |
|
| name | fits the widest visible name | name, `[n]` = direct children (collapsed only) |
|
||||||
| status | 14 | issue word, `+n`, `·k` hidden issues; muted `in sync` without issues; spinner while loading |
|
| status | 16 | issue word, `+n`, `·k` hidden issues per level (red, then yellow); muted `in sync` without issues; spinner while loading |
|
||||||
| ref | widest ref, max 22 | resolved ref; muted short SHA if unresolved |
|
| ref | widest ref, max 22 | resolved ref; muted short SHA if unresolved |
|
||||||
| delta | 6 | `↑n`, `↓n`, `↑n↓m` |
|
| delta | 6 | `↑n`, `↓n`, `↑n↓m` |
|
||||||
|
|
||||||
@@ -91,7 +91,9 @@ Changes 5 files · 2M 1A 1D 1R · +37 −12
|
|||||||
meaning, the color only supports it (N4).
|
meaning, the color only supports it (N4).
|
||||||
- Letters and colors: `!` red bold, `M` yellow, `A` green, `D` red, `R`
|
- Letters and colors: `!` red bold, `M` yellow, `A` green, `D` red, `R`
|
||||||
cyan, `?` muted. Line counts: green `+n`, red `−n`; `bin` for binary
|
cyan, `?` muted. Line counts: green `+n`, red `−n`; `bin` for binary
|
||||||
files, `submodule` for a moved nested pin, nothing for untracked files.
|
files, `submodule` for a moved nested pin, `repository` for an untracked
|
||||||
|
directory that is a git repository itself (cloned, never added as a
|
||||||
|
submodule), nothing for other untracked files.
|
||||||
- Order: kind (`!`, `M`, `A`, `D`, `R`, `?`), then changed lines, largest
|
- Order: kind (`!`, `M`, `A`, `D`, `R`, `?`), then changed lines, largest
|
||||||
first, because the biggest edits are the ones people remember.
|
first, because the biggest edits are the ones people remember.
|
||||||
- Budget: 10 files. Every kind that occurs gets at least one line, the
|
- Budget: 10 files. Every kind that occurs gets at least one line, the
|
||||||
@@ -118,10 +120,12 @@ Changes 5 files · 2M 1A 1D 1R · +37 −12
|
|||||||
- Untracked directories are shown as one entry (`tmp/`).
|
- Untracked directories are shown as one entry (`tmp/`).
|
||||||
### Header
|
### Header
|
||||||
|
|
||||||
`path branch ↑↓ · n submodules · <count> <issue>… · n pin staged · n loading · <fetch>`
|
`path branch ↑↓ · n submodules · n error · <count> <issue>… · n pin staged · n loading · <fetch>`
|
||||||
|
|
||||||
Issue counts are ordered by severity (unpushed, conflict, missing, off pin,
|
Issue counts are ordered by severity (unpushed, conflict, missing, off pin,
|
||||||
changed), each in its level color. Zero counts are omitted. A detached
|
changed), each in its level color. Zero counts are omitted. `n loading`
|
||||||
|
counts only submodules whose status is still being loaded; ones whose load
|
||||||
|
failed are counted separately as `n error` (red, before the issue counts). A detached
|
||||||
superproject shows `(detached)` as its branch.
|
superproject shows `(detached)` as its branch.
|
||||||
|
|
||||||
`<fetch>` on the right:
|
`<fetch>` on the right:
|
||||||
@@ -179,8 +183,8 @@ colors in components.
|
|||||||
| `space` | Toggle expand/collapse of the selected submodule |
|
| `space` | Toggle expand/collapse of the selected submodule |
|
||||||
| `→` / `l` | Collapsed with children: expand and select the first child. Expanded: select the first child. Leaf: nothing |
|
| `→` / `l` | Collapsed with children: expand and select the first child. Expanded: select the first child. Leaf: nothing |
|
||||||
| `←` / `h` | Expanded: collapse it (stays selected). Otherwise: collapse the parent and select it. Top level: nothing |
|
| `←` / `h` | Expanded: collapse it (stays selected). Otherwise: collapse the parent and select it. Top level: nothing |
|
||||||
| `ctrl+→` | Expand all; then follow the first child from the selection down to a submodule without children |
|
| `L` / `shift+→` / `ctrl+→` | Expand all; then follow the first child from the selection down to a submodule without children |
|
||||||
| `ctrl+←` | Collapse all (resets remembered expansion); select the top-level ancestor |
|
| `H` / `shift+←` / `ctrl+←` | Collapse all (resets remembered expansion); select the top-level ancestor |
|
||||||
| `home` / `g`, `end` / `G` | First / last submodule |
|
| `home` / `g`, `end` / `G` | First / last submodule |
|
||||||
| `pgup`, `pgdn` | Move by one screen |
|
| `pgup`, `pgdn` | Move by one screen |
|
||||||
| `?` | Toggle full help |
|
| `?` | Toggle full help |
|
||||||
@@ -205,5 +209,6 @@ is selected, headers above it are shown too.
|
|||||||
## Accessibility and terminals
|
## Accessibility and terminals
|
||||||
|
|
||||||
- `SUBMARINE_ASCII=1`: ASCII symbols (`^ v > v . *`) instead of Unicode.
|
- `SUBMARINE_ASCII=1`: ASCII symbols (`^ v > v . *`) instead of Unicode.
|
||||||
- Some terminals or multiplexers capture `ctrl+←/→`; letter aliases can be
|
- Many setups capture `ctrl+←/→` (macOS Mission Control, some terminals and
|
||||||
added if needed.
|
multiplexers), so expand/collapse all also work as `shift+→/←` and as `L` / `H`, the
|
||||||
|
shifted vim keys for `→` / `←` (for terminals that don't report shift+arrows).
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ func status(ctx context.Context, in info) (*submodule.Status, error) {
|
|||||||
}
|
}
|
||||||
pin := parseGitlink(out)
|
pin := parseGitlink(out)
|
||||||
if pin == "" {
|
if pin == "" {
|
||||||
return nil, errors.New("no gitlink in the superproject index")
|
return nil, errors.New("listed in .gitmodules, but has no gitlink in the superproject index (stale entry?)")
|
||||||
}
|
}
|
||||||
out, _, err = try(ctx, in.superDir, "ls-tree", "HEAD", "--", in.relPath)
|
out, _, err = try(ctx, in.superDir, "ls-tree", "HEAD", "--", in.relPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -387,6 +387,9 @@ func changes(ctx context.Context, dir string, entries []entry) (*submodule.Chang
|
|||||||
c.Added += n.added
|
c.Added += n.added
|
||||||
c.Removed += n.removed
|
c.Removed += n.removed
|
||||||
}
|
}
|
||||||
|
if e.kind == submodule.ChangeUntracked && strings.HasSuffix(e.path, "/") {
|
||||||
|
f.Repo = initialized(filepath.Join(dir, filepath.FromSlash(e.path)))
|
||||||
|
}
|
||||||
if e.kind != submodule.ChangeRemoved {
|
if e.kind != submodule.ChangeRemoved {
|
||||||
if fi, err := os.Stat(filepath.Join(dir, filepath.FromSlash(e.path))); err == nil {
|
if fi, err := os.Stat(filepath.Join(dir, filepath.FromSlash(e.path))); err == nil {
|
||||||
f.Modified = fi.ModTime()
|
f.Modified = fi.ModTime()
|
||||||
|
|||||||
@@ -132,6 +132,7 @@ type FileChange struct {
|
|||||||
Removed int // removed lines
|
Removed int // removed lines
|
||||||
Binary bool // line counts are unknown
|
Binary bool // line counts are unknown
|
||||||
Submodule bool // a nested submodule pointer changed
|
Submodule bool // a nested submodule pointer changed
|
||||||
|
Repo bool // an untracked directory that is a git repository itself
|
||||||
Modified time.Time // modification time of the file; zero if it doesn't exist
|
Modified time.Time // modification time of the file; zero if it doesn't exist
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,21 +55,36 @@ func (s *Status) Issues() []Issue {
|
|||||||
case WorktreeModified:
|
case WorktreeModified:
|
||||||
out = append(out, Issue{IssueChanged, LevelWarn, "uncommitted changes"})
|
out = append(out, Issue{IssueChanged, LevelWarn, "uncommitted changes"})
|
||||||
case WorktreeUntracked:
|
case WorktreeUntracked:
|
||||||
out = append(out, Issue{IssueChanged, LevelWarn, "untracked files"})
|
out = append(out, Issue{IssueChanged, LevelWarn, s.untrackedText()})
|
||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// HiddenIssues counts the issues of all loaded descendants and returns the
|
// Hidden counts issues below a submodule per level.
|
||||||
// most severe level among them.
|
type Hidden struct{ Warn, Danger int }
|
||||||
func (m *Submodule) HiddenIssues() (count int, worst Level) {
|
|
||||||
|
// Total is the number of issues of all levels.
|
||||||
|
func (h Hidden) Total() int { return h.Warn + h.Danger }
|
||||||
|
|
||||||
|
// HiddenIssues counts the issues of all loaded descendants per level. A
|
||||||
|
// descendant whose load failed counts as one danger, since its state is
|
||||||
|
// unknown.
|
||||||
|
func (m *Submodule) HiddenIssues() Hidden {
|
||||||
|
var h Hidden
|
||||||
Walk(m.Children, func(c *Submodule) {
|
Walk(m.Children, func(c *Submodule) {
|
||||||
|
if c.Status == nil && c.Err != nil {
|
||||||
|
h.Danger++
|
||||||
|
}
|
||||||
for _, is := range c.Status.Issues() {
|
for _, is := range c.Status.Issues() {
|
||||||
count++
|
switch is.Level {
|
||||||
worst = max(worst, is.Level)
|
case LevelDanger:
|
||||||
|
h.Danger++
|
||||||
|
case LevelWarn:
|
||||||
|
h.Warn++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return count, worst
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
// Walk calls fn for every submodule in depth-first order.
|
// Walk calls fn for every submodule in depth-first order.
|
||||||
@@ -79,3 +94,18 @@ func Walk(mods []*Submodule, fn func(*Submodule)) {
|
|||||||
Walk(m.Children, fn)
|
Walk(m.Children, fn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// untrackedText words the untracked-files issue: "untracked repository" if
|
||||||
|
// everything untracked is a git repository of its own, e.g. a clone that was
|
||||||
|
// never added as a submodule.
|
||||||
|
func (s *Status) untrackedText() string {
|
||||||
|
if s.Changes == nil || s.Changes.Total != len(s.Changes.Files) {
|
||||||
|
return "untracked files"
|
||||||
|
}
|
||||||
|
for _, f := range s.Changes.Files {
|
||||||
|
if f.Kind != ChangeUntracked || !f.Repo {
|
||||||
|
return "untracked files"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "untracked repository"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package submodule
|
package submodule
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"slices"
|
"slices"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@@ -45,8 +46,35 @@ func TestHiddenIssues(t *testing.T) {
|
|||||||
loading := &Submodule{}
|
loading := &Submodule{}
|
||||||
root := &Submodule{Status: ok, Children: []*Submodule{child, {Status: ok}, loading}}
|
root := &Submodule{Status: ok, Children: []*Submodule{child, {Status: ok}, loading}}
|
||||||
|
|
||||||
count, worst := root.HiddenIssues()
|
if got := root.HiddenIssues(); got != (Hidden{Warn: 1, Danger: 1}) {
|
||||||
if count != 2 || worst != LevelDanger {
|
t.Errorf("HiddenIssues() = %+v; want 1 warn, 1 danger", got)
|
||||||
t.Errorf("HiddenIssues() = %d, %v; want 2, LevelDanger", count, worst)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUntrackedRepositoryWording(t *testing.T) {
|
||||||
|
repo := FileChange{Path: "lib/x/", Kind: ChangeUntracked, Repo: true}
|
||||||
|
file := FileChange{Path: "a.txt", Kind: ChangeUntracked}
|
||||||
|
for _, c := range []struct {
|
||||||
|
files []FileChange
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{[]FileChange{repo}, "untracked repository"},
|
||||||
|
{[]FileChange{repo, file}, "untracked files"},
|
||||||
|
{[]FileChange{file}, "untracked files"},
|
||||||
|
} {
|
||||||
|
s := &Status{Worktree: WorktreeUntracked, Changes: &Changes{Total: len(c.files), Files: c.files}}
|
||||||
|
got := s.Issues()
|
||||||
|
if len(got) == 0 || got[len(got)-1].Detail != c.want {
|
||||||
|
t.Errorf("%v: got %v, want %q", c.files, got, c.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHiddenIssuesCountLoadErrors(t *testing.T) {
|
||||||
|
bad := &Submodule{Err: errors.New("boom")}
|
||||||
|
loading := &Submodule{}
|
||||||
|
parent := &Submodule{Children: []*Submodule{bad, loading}}
|
||||||
|
if got := parent.HiddenIssues(); got != (Hidden{Danger: 1}) {
|
||||||
|
t.Errorf("got %+v; want 1 danger", got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -163,6 +163,8 @@ func (m Model) fileLines(files []submodule.FileChange) []string {
|
|||||||
switch {
|
switch {
|
||||||
case f.Submodule:
|
case f.Submodule:
|
||||||
r.counts = muted.Render("submodule")
|
r.counts = muted.Render("submodule")
|
||||||
|
case f.Repo:
|
||||||
|
r.counts = muted.Render("repository")
|
||||||
case f.Binary:
|
case f.Binary:
|
||||||
r.counts = muted.Render("bin")
|
r.counts = muted.Render("bin")
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -138,8 +138,16 @@ func (m Model) lines(s *submodule.Submodule) []string {
|
|||||||
out = append(out, "")
|
out = append(out, "")
|
||||||
if n := len(s.Children); n > 0 {
|
if n := len(s.Children); n > 0 {
|
||||||
children := fmt.Sprintf("%d direct", n)
|
children := fmt.Sprintf("%d direct", n)
|
||||||
if k, worst := s.HiddenIssues(); k > 0 {
|
h := s.HiddenIssues()
|
||||||
children += t.LevelStyle(worst, lipgloss.NewStyle()).Render(fmt.Sprintf(" · %d issue(s) below", k))
|
var parts []string
|
||||||
|
if h.Danger > 0 {
|
||||||
|
parts = append(parts, t.LevelStyle(submodule.LevelDanger, lipgloss.NewStyle()).Render(fmt.Sprintf("%d error(s)", h.Danger)))
|
||||||
|
}
|
||||||
|
if h.Warn > 0 {
|
||||||
|
parts = append(parts, t.LevelStyle(submodule.LevelWarn, lipgloss.NewStyle()).Render(fmt.Sprintf("%d warning(s)", h.Warn)))
|
||||||
|
}
|
||||||
|
if len(parts) > 0 {
|
||||||
|
children += muted.Render(" · ") + strings.Join(parts, muted.Render(", ")) + muted.Render(" below")
|
||||||
}
|
}
|
||||||
out = append(out, label("Children")+children)
|
out = append(out, label("Children")+children)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,13 +36,17 @@ func View(t theme.Theme, repo *submodule.Repo, fetch Fetch, spinner string, widt
|
|||||||
}
|
}
|
||||||
|
|
||||||
// count submodules per issue kind (most severe first), plus staged pins
|
// count submodules per issue kind (most severe first), plus staged pins
|
||||||
var total, loading, staged int
|
var total, loading, errored, staged int
|
||||||
perKind := map[submodule.IssueKind]int{}
|
perKind := map[submodule.IssueKind]int{}
|
||||||
level := map[submodule.IssueKind]submodule.Level{}
|
level := map[submodule.IssueKind]submodule.Level{}
|
||||||
submodule.Walk(repo.Submodules, func(s *submodule.Submodule) {
|
submodule.Walk(repo.Submodules, func(s *submodule.Submodule) {
|
||||||
total++
|
total++
|
||||||
if s.Status == nil {
|
if s.Status == nil {
|
||||||
loading++
|
if s.Err == nil {
|
||||||
|
loading++
|
||||||
|
} else {
|
||||||
|
errored++
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if s.Status.PinStaged {
|
if s.Status.PinStaged {
|
||||||
@@ -56,6 +60,9 @@ func View(t theme.Theme, repo *submodule.Repo, fetch Fetch, spinner string, widt
|
|||||||
|
|
||||||
sep := seg(t.Muted, " · ")
|
sep := seg(t.Muted, " · ")
|
||||||
mid := seg(t.Muted, fmt.Sprintf("%d submodules", total))
|
mid := seg(t.Muted, fmt.Sprintf("%d submodules", total))
|
||||||
|
if errored > 0 {
|
||||||
|
mid += sep + t.LevelStyle(submodule.LevelDanger, bar).Render(fmt.Sprintf("%d error", errored))
|
||||||
|
}
|
||||||
for _, k := range []submodule.IssueKind{
|
for _, k := range []submodule.IssueKind{
|
||||||
submodule.IssueUnpushed, submodule.IssueConflict, submodule.IssueMissing,
|
submodule.IssueUnpushed, submodule.IssueConflict, submodule.IssueMissing,
|
||||||
submodule.IssueOffPin, submodule.IssueChanged,
|
submodule.IssueOffPin, submodule.IssueChanged,
|
||||||
|
|||||||
@@ -12,13 +12,13 @@ type keyMap struct {
|
|||||||
|
|
||||||
func defaultKeys() keyMap {
|
func defaultKeys() keyMap {
|
||||||
return keyMap{
|
return keyMap{
|
||||||
Up: key.NewBinding(key.WithKeys("up", "k"), key.WithHelp("↑/↓", "move")),
|
Up: key.NewBinding(key.WithKeys("up", "k"), key.WithHelp("↑/k", "up")),
|
||||||
Down: key.NewBinding(key.WithKeys("down", "j"), key.WithHelp("↓/j", "down")),
|
Down: key.NewBinding(key.WithKeys("down", "j"), key.WithHelp("↓/j", "down")),
|
||||||
Left: key.NewBinding(key.WithKeys("left", "h"), key.WithHelp("←/→", "collapse/expand")),
|
Left: key.NewBinding(key.WithKeys("left", "h"), key.WithHelp("←/h", "collapse, parent")),
|
||||||
Right: key.NewBinding(key.WithKeys("right", "l"), key.WithHelp("→/l", "expand, first child")),
|
Right: key.NewBinding(key.WithKeys("right", "l"), key.WithHelp("→/l", "expand, first child")),
|
||||||
Toggle: key.NewBinding(key.WithKeys(" "), key.WithHelp("space", "toggle")),
|
Toggle: key.NewBinding(key.WithKeys(" "), key.WithHelp("space", "toggle")),
|
||||||
CollapseAll: key.NewBinding(key.WithKeys("ctrl+left"), key.WithHelp("ctrl+←", "collapse all")),
|
CollapseAll: key.NewBinding(key.WithKeys("shift+left", "ctrl+left", "H"), key.WithHelp("H/⇧←", "collapse all")),
|
||||||
ExpandAll: key.NewBinding(key.WithKeys("ctrl+right"), key.WithHelp("ctrl+→", "expand all")),
|
ExpandAll: key.NewBinding(key.WithKeys("shift+right", "ctrl+right", "L"), key.WithHelp("L/⇧→", "expand all")),
|
||||||
Home: key.NewBinding(key.WithKeys("home", "g"), key.WithHelp("home/g", "first")),
|
Home: key.NewBinding(key.WithKeys("home", "g"), key.WithHelp("home/g", "first")),
|
||||||
End: key.NewBinding(key.WithKeys("end", "G"), key.WithHelp("end/G", "last")),
|
End: key.NewBinding(key.WithKeys("end", "G"), key.WithHelp("end/G", "last")),
|
||||||
PageUp: key.NewBinding(key.WithKeys("pgup"), key.WithHelp("pgup", "page up")),
|
PageUp: key.NewBinding(key.WithKeys("pgup"), key.WithHelp("pgup", "page up")),
|
||||||
@@ -35,7 +35,10 @@ func defaultKeys() keyMap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (k keyMap) ShortHelp() []key.Binding {
|
func (k keyMap) ShortHelp() []key.Binding {
|
||||||
return []key.Binding{k.Up, k.Left, k.Toggle, k.Update, k.Bump, k.Fetch, k.Help, k.Quit}
|
move := key.NewBinding(key.WithKeys("up"), key.WithHelp("↑↓/jk", "move"))
|
||||||
|
fold := key.NewBinding(key.WithKeys("left"), key.WithHelp("←→/hl", "fold"))
|
||||||
|
all := key.NewBinding(key.WithKeys("H"), key.WithHelp("⇧←→/HL", "fold all"))
|
||||||
|
return []key.Binding{move, fold, all, k.Toggle, k.Help, k.Quit}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k keyMap) FullHelp() [][]key.Binding {
|
func (k keyMap) FullHelp() [][]key.Binding {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
statusWidth = 14 // "unpushed +1 ·2"
|
statusWidth = 16 // "unpushed +1 ·2 ·3"
|
||||||
deltaWidth = 6
|
deltaWidth = 6
|
||||||
maxRefWidth = 22
|
maxRefWidth = 22
|
||||||
)
|
)
|
||||||
@@ -414,11 +414,19 @@ func (m Model) nodeLine(r row, nameColW, refW int) string {
|
|||||||
default:
|
default:
|
||||||
status = seg(t.Muted, theme.InSync)
|
status = seg(t.Muted, theme.InSync)
|
||||||
}
|
}
|
||||||
if k, worst := s.HiddenIssues(); collapsed && k > 0 {
|
if h := s.HiddenIssues(); collapsed && h.Total() > 0 {
|
||||||
if status != "" {
|
for _, c := range []struct {
|
||||||
status += pad(1)
|
level submodule.Level
|
||||||
|
n int
|
||||||
|
}{{submodule.LevelDanger, h.Danger}, {submodule.LevelWarn, h.Warn}} {
|
||||||
|
if c.n == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if status != "" {
|
||||||
|
status += pad(1)
|
||||||
|
}
|
||||||
|
status += t.LevelStyle(c.level, base).Render(fmt.Sprintf("%s%d", t.Glyphs.Problems, c.n))
|
||||||
}
|
}
|
||||||
status += t.LevelStyle(worst, base).Render(fmt.Sprintf("%s%d", t.Glyphs.Problems, k))
|
|
||||||
}
|
}
|
||||||
status = ansi.Truncate(status, statusWidth, "")
|
status = ansi.Truncate(status, statusWidth, "")
|
||||||
status += pad(statusWidth - lipgloss.Width(status))
|
status += pad(statusWidth - lipgloss.Width(status))
|
||||||
|
|||||||
Reference in New Issue
Block a user