1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
//! The statusline consumer (issue #309): the first real client of the
//! `gwm daemon` JSON-RPC surface (issue #38).
//!
//! This module is the **pure render core** — it turns a slice of
//! [`JsonWorktree`] (exactly what the daemon's `list` / `subscribe` stream
//! hands back) into a compact, single-line summary for tmux / starship /
//! zsh prompts. No socket, no git I/O, no clock: deterministic and unit
//! tested in `tests/statusline_tests.rs`. The socket transport lives in
//! [`crate::daemon`] (client side) behind the `daemon` feature.
//!
//! Scope note (MVP): the summary is built **only** from fields present in
//! [`JsonWorktree`] — active branch, worktree count, dirty / ahead /
//! behind, and the linked issue / PR numbers. A CI rollup (the
//! `CI passing 9/9` half of issue #309's example) is deliberately omitted:
//! it is not part of the stable daemon schema, and fetching it per update
//! would mean a `gh` call on every prompt redraw. That is left as a
//! follow-up rather than smuggled into the daemon. `age_seconds` is also
//! skipped — [`crate::daemon::worktrees_differ`] ignores it, so it never
//! moves a `--watch` stream.
use crateJsonWorktree;
use ;
/// Resolve which worktree in `worktrees` owns `cwd`, passing every path
/// (the `cwd` and each worktree `path`) through `canonicalize` first.
///
/// A worktree owns `cwd` when its canonicalised `path` is an ancestor of
/// (or equal to) the canonicalised `cwd`. When several match — a worktree
/// nested inside another — the one with the longest canonical path wins
/// (the most specific enclosing worktree). Returns `None` when `cwd` is
/// outside every worktree.
///
/// The canonicaliser is injected so the core stays free of filesystem
/// access (and unit-testable): callers that need symlink equivalence
/// (e.g. macOS `/var` ↔ `/private/var`, or a worktree added under a
/// symlink) pass `std::fs::canonicalize`; tests pass a deterministic stub.
/// Canonicalising **both** sides is the point — resolving only the `cwd`
/// while the daemon's paths stay raw would defeat the match.
/// Resolve the enclosing worktree by raw path comparison — [`active_index_with`]
/// under the identity canonicaliser. No filesystem access, so symlink
/// equivalence is the caller's responsibility (see [`active_index_with`]).
/// Render the compact statusline for `worktrees`, highlighting the
/// `active` worktree's branch and local state. Returns an empty string for
/// an empty set so a prompt substitution collapses to nothing.
/// Convenience: resolve the active worktree from `cwd`, then [`render`].
/// Drive a `--watch` render loop: hand each pushed snapshot to `emit`, then
/// emit exactly **one** final empty render once the stream ends — for any
/// reason. A `subscribe` stream ends only when the daemon goes away (it was
/// unreachable, or stopped / restarted after pushing snapshots). Emitting a
/// trailing blank then clears the now-stale line so a long-running consumer
/// (a tmux tail) doesn't freeze on the last render instead of degrading to
/// nothing (issue #309).
///
/// Generic over the subscribe transport so it stays socket-free and
/// unit-testable: callers pass a closure that wires the real
/// `daemon::client::subscribe` to the supplied snapshot callback. The
/// stream's `Result` is intentionally ignored — both the error path (never
/// connected) and the clean-close path (daemon stopped after ≥1 snapshot)
/// degrade identically to the trailing blank.