Archived
Split hidden issue counts by level
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
+5
-3
@@ -53,9 +53,11 @@ 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
|
||||||
|
|
||||||
|
|||||||
@@ -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).
|
||||||
|
|||||||
+3
-2
@@ -96,8 +96,9 @@ 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.
|
A descendant whose load failed (`error`) counts as one danger.
|
||||||
|
|
||||||
## Resolving a pin to a ref
|
## Resolving a pin to a ref
|
||||||
|
|||||||
+1
-1
@@ -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` |
|
||||||
|
|
||||||
|
|||||||
@@ -60,21 +60,31 @@ func (s *Status) Issues() []Issue {
|
|||||||
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. A descendant whose load failed counts as one
|
type Hidden struct{ Warn, Danger int }
|
||||||
// danger, since its state is unknown.
|
|
||||||
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 {
|
if c.Status == nil && c.Err != nil {
|
||||||
count++
|
h.Danger++
|
||||||
worst = max(worst, LevelDanger)
|
|
||||||
}
|
}
|
||||||
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.
|
||||||
|
|||||||
@@ -46,9 +46,8 @@ 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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,7 +74,7 @@ func TestHiddenIssuesCountLoadErrors(t *testing.T) {
|
|||||||
bad := &Submodule{Err: errors.New("boom")}
|
bad := &Submodule{Err: errors.New("boom")}
|
||||||
loading := &Submodule{}
|
loading := &Submodule{}
|
||||||
parent := &Submodule{Children: []*Submodule{bad, loading}}
|
parent := &Submodule{Children: []*Submodule{bad, loading}}
|
||||||
if n, w := parent.HiddenIssues(); n != 1 || w != LevelDanger {
|
if got := parent.HiddenIssues(); got != (Hidden{Danger: 1}) {
|
||||||
t.Errorf("got %d, %v; want 1, danger", n, w)
|
t.Errorf("got %+v; want 1 danger", got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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