feat: status bar shows repo name (or parent folder) + filename (TASK-010)
0b2e184dc6993c3b4e665958d97fa7c9e256a860
Kevin Kortum <kevinkortum@me.com> · 2026-06-29 16:11
parent 466af178
feat: status bar shows repo name (or parent folder) + filename (TASK-010) The left segment now resolves the file's git repository root name (the folder holding .git), falling back to the immediate parent folder when not in a repo. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KjNrdAWUdkaxFyGdrPHaBj
2 files changed
internal/app/app.go +23 −7
@@ -725,17 +725,15 @@ dirty := ""
if a.editor.Dirty {
dirty = " ●"
}
- // Left: a transient message takes over; otherwise dir (+ filename).
+ // Left: a transient message takes over; otherwise the repo (or parent
+ // folder) and filename.
var left string
if a.status != "" {
left = a.status + dirty
+ } else if a.path != "" {
+ left = repoOrParent(a.path) + "/" + filepath.Base(a.path) + dirty
} else {
- dir := filepath.Base(a.currentDir())
- if a.path != "" {
- left = dir + "/" + filepath.Base(a.path) + dirty
- } else {
- left = dir + "/" + dirty
- }
+ left = filepath.Base(a.currentDir()) + "/" + dirty
}
// Right: [sel stats ·] Ln r:c · N words · theme · ? (high→low priority).
pos := a.editor.Cursor
@@ -783,6 +781,24 @@ for len(r) > 0 && lipgloss.Width(string(r))+1 > width {
r = r[:len(r)-1]
}
return string(r) + "…"
+}
+
+// repoOrParent returns the name of the git repository containing path (the
+// folder holding .git), or the file's immediate parent folder when path is not
+// inside a repo.
+func repoOrParent(path string) string {
+ parent := filepath.Dir(path)
+ for dir := parent; ; {
+ if _, err := os.Stat(filepath.Join(dir, ".git")); err == nil {
+ return filepath.Base(dir)
+ }
+ up := filepath.Dir(dir)
+ if up == dir { // reached the filesystem root
+ break
+ }
+ dir = up
+ }
+ return filepath.Base(parent)
}
func maxInt(a, b int) int {
internal/app/status_test.go +30 −0
@@ -1,6 +1,8 @@
package app
import (
+ "os"
+ "path/filepath"
"strings"
"testing"
@@ -56,6 +58,34 @@ }
got := a.statusBar()
if !strings.Contains(got, "7 chars") || !strings.Contains(got, "2 words selected") {
t.Fatalf("status bar missing selection stats:\n%s", got)
+ }
+}
+
+func TestRepoOrParentReturnsRepoName(t *testing.T) {
+ root := t.TempDir()
+ repo := filepath.Join(root, "myrepo")
+ sub := filepath.Join(repo, "notes")
+ if err := os.MkdirAll(filepath.Join(repo, ".git"), 0o755); err != nil {
+ t.Fatal(err)
+ }
+ if err := os.MkdirAll(sub, 0o755); err != nil {
+ t.Fatal(err)
+ }
+ got := repoOrParent(filepath.Join(sub, "n.md"))
+ if got != "myrepo" {
+ t.Fatalf("repoOrParent = %q, want %q", got, "myrepo")
+ }
+}
+
+func TestRepoOrParentFallsBackToParent(t *testing.T) {
+ root := t.TempDir()
+ dir := filepath.Join(root, "plainfolder")
+ if err := os.MkdirAll(dir, 0o755); err != nil {
+ t.Fatal(err)
+ }
+ got := repoOrParent(filepath.Join(dir, "n.md"))
+ if got != "plainfolder" {
+ t.Fatalf("repoOrParent = %q, want %q", got, "plainfolder")
}
}