aboutsummaryrefslogtreecommitdiff
path: root/gui/internal/model/plan.go
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-09-16 09:39:59 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-09-16 09:39:59 +0200
commit6ef83d6bdfb6120f9e1fbd145e0bc463196103d1 (patch)
tree8eee1f2c29dcbef4ed96b23b08273d42ba0b5d49 /gui/internal/model/plan.go
parent27eb6353030953e91a45b0104bd0567e53622090 (diff)
downloadkrino-6ef83d6bdfb6120f9e1fbd145e0bc463196103d1.tar.gz
krino-6ef83d6bdfb6120f9e1fbd145e0bc463196103d1.zip
gui: History and undo tab; each plan and undo is its own run
Diffstat (limited to 'gui/internal/model/plan.go')
-rw-r--r--gui/internal/model/plan.go41
1 files changed, 25 insertions, 16 deletions
diff --git a/gui/internal/model/plan.go b/gui/internal/model/plan.go
index 4066eae..86acab7 100644
--- a/gui/internal/model/plan.go
+++ b/gui/internal/model/plan.go
@@ -54,18 +54,26 @@ type Counts struct {
// chooses, and no other krino moves the files under them (GUI design §3).
// A directory another run is already working in is not planned at all: the
// error is lock.ErrHeld, naming the directory.
-func Plan(ctx context.Context, sess *engine.Session, d *engine.Dir) (*PlanTab, error) {
+//
+// Each plan gets its own session, so applying it is one run of krino with
+// its own run id in the log - the same shape a command line invocation
+// writes. A window is not a run: one that sorted a directory and later
+// undid something must not log both under one id, which would make the run
+// the undo of itself.
+func Plan(ctx context.Context, e *engine.Engine, d *engine.Dir) (*PlanTab, error) {
+ sess, err := e.NewSession(false)
+ if err != nil {
+ return nil, err
+ }
l, err := sess.Lock(ctx, d, false)
if err != nil {
+ sess.Close()
return nil, fmt.Errorf("%s: %w", d.Name, err)
}
- // Start from what this run has actually landed: a name a plan the user
- // closed had reserved is free again, so looking twice never creeps up
- // through name-1, name-2 (spec §7.4).
- sess.FinishDirectory()
dp, err := sess.Plan(ctx, d)
if err != nil {
l.Release()
+ sess.Close()
return nil, err
}
t := &PlanTab{Dir: d, sess: sess, dp: dp, lock: l}
@@ -73,17 +81,22 @@ func Plan(ctx context.Context, sess *engine.Session, d *engine.Dir) (*PlanTab, e
return t, nil
}
-// Close releases the directory's lock, which Apply also does once the plan
-// is history. The session outlives the tab, so closing one plan to open
-// another keeps the run - and its claims - going. Closing twice is not an
-// error.
+// Run is the run id this plan was applied under, "" until Apply.
+func (t *PlanTab) Run() string { return t.sess.Run() }
+
+// Close releases the directory's lock and ends the run, which Apply also
+// does once the plan is history. Closing twice is not an error.
func (t *PlanTab) Close() error {
if t.lock == nil {
return nil
}
l := t.lock
t.lock = nil
- return l.Release()
+ err := l.Release()
+ if cerr := t.sess.Close(); err == nil {
+ err = cerr
+ }
+ return err
}
// fill turns the engine's plan into rows and counts.
@@ -218,12 +231,8 @@ func (t *PlanTab) Apply(ctx context.Context) (*engine.ApplyResult, error) {
}
res, err := t.sess.Apply(ctx, t.dp, approved)
t.Applied = true
- // The disk is now the truth: a destination this plan reserved but never
- // used is free again, while one it did use stays protected for the rest
- // of the run (spec §7.4).
- t.sess.FinishDirectory()
- // An applied plan is history, so the directory is free again without
- // closing the window (GUI design §3).
+ // An applied plan is history: the run is over and the directory free
+ // again, without closing the window (GUI design §3).
t.Close()
if res != nil {
t.record(res)