-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathlog.cljs
More file actions
119 lines (100 loc) · 4.04 KB
/
Copy pathlog.cljs
File metadata and controls
119 lines (100 loc) · 4.04 KB
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
(ns shell.log
"Shell-level op log storage.
Owns the `!log` atom. The current db is a pure function of the log
(see `kernel.log/head-db`), but for performance we maintain a live
`!db` atom alongside: forward progress updates it directly without
a full refold, and undo/redo refold from the log's root.
This namespace also mints the entry identity (op-id, timestamp) on
behalf of the kernel, honoring the invariant that the kernel never
reads a clock or calls random-uuid."
(:require [kernel.log :as L]
[kernel.transaction :as tx]
[shell.view-state :as vs]))
(defonce !log (atom L/empty-log))
(defn- now-ms [] (.now js/Date))
(defn- mint-entry
"Build a log entry from a dispatch result, minting identity."
[intent ops session session-after prev-op-id]
(L/make-entry
{:op-id (random-uuid)
:prev-op-id prev-op-id
:timestamp (now-ms)
:intent intent
:ops ops
:session session
:session-after session-after}))
(defn append-and-advance!
"Record a successful structural dispatch into the log.
Takes the post-dispatch `db-after`, the intent, the kernel ops, and the
pre-dispatch session snapshot. Updates !log (and returns the new log).
`!db` is not touched here — the executor already has db-after in hand
and resets !db itself. Keeping db-update out of this function avoids
double reset and matches the current intent-apply shape."
[intent ops session session-after]
(let [prev-head (:head @!log)
prev-entry (L/entry-at-head @!log)
prev-op-id (:op-id prev-entry)
_ (assert (>= prev-head -1))
entry (mint-entry intent ops session session-after prev-op-id)]
(swap! !log L/append entry)))
(defn undo!
"Perform undo: rewrite !log and !db, restore session from the entry
being stepped past.
Returns true if an undo happened, false if nothing to undo."
[!db]
(let [log @!log]
(if-let [new-log (L/undo log)]
(let [entry-undone (L/entry-at-head log) ; the entry we're stepping off of
session-before (:session-before entry-undone)
new-db (L/head-db new-log)]
(clojure.core/reset! !log new-log)
(clojure.core/reset! !db new-db)
(when session-before
(vs/merge-view-state-updates! session-before))
true)
false)))
(defn redo!
"Perform redo. Returns true if a redo happened, false otherwise."
[!db]
(let [log @!log]
(if-let [new-log (L/redo log)]
(let [new-db (L/head-db new-log)
entry-redone (L/entry-at-head new-log)
session-after (:session-after entry-redone)]
(clojure.core/reset! !log new-log)
(clojure.core/reset! !db new-db)
(when session-after
(vs/merge-view-state-updates! session-after))
true)
false)))
(defn can-undo? [] (L/can-undo? @!log))
(defn can-redo? [] (L/can-redo? @!log))
(defn undo-count [] (L/undo-count @!log))
(defn redo-count [] (L/redo-count @!log))
(defn reset-with-db!
"Discard history and set :root-db to `db`. Used when loading a folder
or resetting in test mode: the loaded state is the new baseline, not
part of the undo stack."
[db]
(clojure.core/reset! !log (L/reset-root db (:limit @!log))))
(defn clear!
"Discard ops, keep :root-db. Typically callers reset-with-db! instead."
[]
(swap! !log L/clear))
(defn get-log
"Read the current log value (for devtools/debug inspection)."
[]
@!log)
;; ── Bulk ops (load-from-folder, fixtures) ────────────────────────────────────
(defn apply-ops!
"Apply a batch of kernel ops to the current db, bypassing the log.
Used by the storage layer on cold-start (loading the folder) and by
test fixtures. These ops are baseline state, not user actions, so they
do not enter the undo stack — they update :root-db instead.
Returns the new db."
[!db ops]
(let [db-before @!db
{new-db :db} (tx/interpret db-before ops {:tx/now-ms (now-ms)})]
(clojure.core/reset! !db new-db)
(reset-with-db! new-db)
new-db))