2036620528a4

mark the replayed-away originals in the history view and refresh it after actions (3.0.8)

time
2026-09-23 21:43 UTC · 2 weeks ago
machine
koh2
agent
claude-opus-5-5
session
koh 3 review fixes
parent
1af9dc58a448
4 files VERSION+1 −1src/view/view.v+23 −12src/view/view_test.v+25 −7v.mod+1 −1
VERSION+1 −1
@@ -1 +1 @@1-3.0.71+3.0.8

view file

src/view/view.v+23 −12
@@ -79,6 +79,8 @@7979 	mode   Mode8080 	target history.Save8181 	typed  string82+	// set when an action changed history, so the lists are read again83+	stale bool8284 }8385 8486 // run shows the view until the user leaves it. `act` performs a chosen action@@ -89,17 +91,22 @@8991 	defer {9092 		leave_screen()9193 	}92-	every := history.all_saves(store)!93-	on_line := on_line_set(store)94+	mut every := []history.Save{}9495 	mut session := Session{9596 		screen: Screen{96-			tab:     start97-			on_line: on_line98-			flags:   events.latest_flags(store)!99-			counts:  count_tabs(every, on_line)97+			tab: start10098 		}99+		stale:  true101100 	}102101 	for {102+		if session.stale {103+			// A load appends a save and a flag changes a mark: read both again.104+			every = history.all_saves(store)!105+			session.screen.on_line = on_line_set(store)106+			session.screen.flags = events.latest_flags(store)!107+			session.screen.counts = count_tabs(every, session.screen.on_line)108+			session.stale = false109+		}103110 		session.screen.rows, session.screen.cols = terminal_size()104111 		session.screen.items = filter_saves(every, session.screen.tab, session.screen.on_line,105112 			session.screen.query)@@ -235,6 +242,7 @@235242 		}236243 		.enter {237244 			if session.typed != '' {245+				session.stale = true238246 				session.screen.status = act(store, Action{239247 					kind:    .flag240248 					save_id: session.target.id@@ -259,6 +267,7 @@259267 			match key.text {260268 				'l' {261269 					if ask_confirm('load [${session.target.id}]') {270+						session.stale = true262271 						session.screen.status = act(store, Action{263272 							kind:    .load264273 							save_id: session.target.id@@ -273,6 +282,7 @@273282 						}274283 					}275284 					if state.flagged {285+						session.stale = true276286 						session.screen.status = act(store, Action{277287 							kind:    .clear_flag278288 							save_id: session.target.id@@ -299,7 +309,7 @@299309 	mut out := []history.Save{}300310 	needle := query.to_lower()301311 	for save in items {302-		live := save.id in on_line && save.replayed_from == ''312+		live := save.id in on_line303313 		if tab == .saves && !live {304314 			continue305315 		}@@ -466,8 +476,8 @@466476 	if save.restores != '' {467477 		tags << 'back to ${save.restores}'468478 	}469-	if save.replayed_from != '' {470-		tags << 'replayed'479+	if save.id !in screen.on_line {480+		tags << 'replayed away'471481 	}472482 	if tags.len > 0 {473483 		line += '   ${fmt.dim(tags.join('  '))}'@@ -497,6 +507,10 @@497507 }498508 499509 fn save_mark(save history.Save, screen Screen) string {510+	// Replayed away: still in history, no longer on the line.511+	if save.id !in screen.on_line {512+		return fmt.dim('↪')513+	}500514 	if save.is_checkpoint {501515 		return fmt.amber('◐')502516 	}@@ -506,9 +520,6 @@506520 		}507521 	}).flagged {508522 		return fmt.red('⚑')509-	}510-	if save.replayed_from != '' {511-		return fmt.dim('↪')512523 	}513524 	return ' '514525 }

view file

src/view/view_test.v+25 −7
@@ -1,6 +1,7 @@11 module view22 33 import src.events4+import src.fmt45 import src.history56 import src.snapshot67 @@ -23,15 +24,32 @@2324 }2425 2526 fn test_tabs_filter_by_what_is_on_the_line() {26-	on_the_line := line_map(['aaa', 'ccc'])27-	mut replayed := save('bbb', 'replayed away', false)28-	replayed.replayed_from = 'aaa'29-	items := [save('aaa', 'first', false), replayed, save('ccc', 'before things', true)]27+	// A combine replays the local save `bbb` as `ddd`: the copy is on the line,28+	// the original is not.29+	on_the_line := line_map(['aaa', 'ccc', 'ddd'])30+	mut copy := save('ddd', 'my work, replayed', false)31+	copy.replayed_from = 'bbb'32+	items := [save('aaa', 'first', false), save('bbb', 'my work', false),33+		save('ccc', 'before things', true), copy]3034 31-	assert filter_saves(items, .all, on_the_line, '').len == 332-	// A replayed-away save exists in history but is on neither live list.33-	assert filter_saves(items, .saves, on_the_line, '').map(it.id) == ['aaa', 'ccc']35+	assert filter_saves(items, .all, on_the_line, '').len == 436+	// The replayed-away original exists in history but is on neither live list;37+	// the copy that replaced it is an ordinary save on the line.38+	assert filter_saves(items, .saves, on_the_line, '').map(it.id) == ['aaa', 'ccc', 'ddd']3439 	assert filter_saves(items, .checkpoints, on_the_line, '').map(it.id) == ['ccc']40+}41+42+fn test_only_the_replayed_away_original_is_marked() {43+	on_the_line := line_map(['ddd'])44+	mut copy := save('ddd', 'my work, replayed', false)45+	copy.replayed_from = 'bbb'46+	screen := Screen{47+		on_line: on_the_line48+	}49+	assert fmt.strip_ansi(save_mark(save('bbb', 'my work', false), screen)) == '↪'50+	assert fmt.strip_ansi(save_mark(copy, screen)) == ' '51+	assert entry_line(save('bbb', 'my work', false), false, screen).contains('replayed away')52+	assert !entry_line(copy, false, screen).contains('replayed away')3553 }3654 3755 fn test_search_is_case_insensitive_across_the_fields() {

view file

v.mod+1 −1
@@ -1,6 +1,6 @@11 Module {22 	name: 'koh3'3-	version: '3.0.7'3+	version: '3.0.8'44 	description: 'One SQLite file per repo, one line of history.'55 	license: 'MIT'66 }

view file