1 (* Title: Pure/PIDE/document.ML
4 Document as collection of named nodes, each consisting of an editable
5 list of commands, associated with asynchronous execution process.
15 val create_id: unit -> id
16 val parse_id: string -> id
17 val print_id: id -> string
18 type edit = string * ((command_id * command_id option) list) option
21 val define_command: command_id -> string -> state -> state
22 val edit: version_id -> version_id -> edit list -> state -> (command_id * exec_id) list * state
23 val execute: version_id -> state -> state
26 structure Document: DOCUMENT =
29 (* unique identifiers *)
39 val id_count = Synchronized.var "id" 0;
42 Synchronized.change_result id_count
48 val parse_id = Markup.parse_int;
49 val print_id = Markup.print_int;
51 fun err_dup kind id = error ("Duplicate " ^ kind ^ ": " ^ print_id id);
52 fun err_undef kind id = error ("Undefined " ^ kind ^ ": " ^ print_id id);
56 (** document structure **)
58 abstype entry = Entry of {next: command_id option, exec: exec_id option}
59 and node = Node of entry Inttab.table (*unique entries indexed by command_id, start with no_id*)
60 and version = Version of node Graph.T (*development graph wrt. static imports*)
66 fun make_entry next exec = Entry {next = next, exec = exec};
68 fun the_entry (Node entries) (id: command_id) =
69 (case Inttab.lookup entries id of
70 NONE => err_undef "command entry" id
71 | SOME (Entry entry) => entry);
73 fun put_entry (id: command_id, entry: entry) (Node entries) =
74 Node (Inttab.update (id, entry) entries);
76 fun put_entry_exec (id: command_id) exec node =
77 let val {next, ...} = the_entry node id
78 in put_entry (id, make_entry next exec) node end;
80 fun reset_entry_exec id = put_entry_exec id NONE;
81 fun set_entry_exec (id, exec_id) = put_entry_exec id (SOME exec_id);
86 fun fold_entries id0 f (node as Node entries) =
90 let val entry = the_entry node id
91 in apply (#next entry) (f (id, entry) x) end;
92 in if Inttab.defined entries id0 then apply (SOME id0) else I end;
94 fun first_entry P node =
96 fun first _ NONE = NONE
97 | first prev (SOME id) =
98 let val entry = the_entry node id
99 in if P (id, entry) then SOME (prev, id, entry) else first (SOME id) (#next entry) end;
100 in first NONE (SOME no_id) end;
105 fun insert_after (id: command_id) (id2: command_id) node =
106 let val {next, exec} = the_entry node id in
108 |> put_entry (id, make_entry (SOME id2) exec)
109 |> put_entry (id2, make_entry next NONE)
112 fun delete_after (id: command_id) node =
113 let val {next, exec} = the_entry node id in
115 NONE => error ("No next entry to delete: " ^ print_id id)
118 (case #next (the_entry node id2) of
119 NONE => put_entry (id, make_entry NONE exec)
120 | SOME id3 => put_entry (id, make_entry (SOME id3) exec) #> reset_entry_exec id3))
127 string * (*node name*)
128 ((command_id * command_id option) list) option; (*NONE: remove, SOME: insert/remove commands*)
130 val empty_node = Node (Inttab.make [(no_id, make_entry NONE (SOME no_id))]);
132 fun edit_node (id, SOME id2) = insert_after id id2
133 | edit_node (id, NONE) = delete_after id;
136 (* version operations *)
138 fun nodes_of (Version nodes) = nodes;
139 val node_names_of = Graph.keys o nodes_of;
141 fun edit_nodes (name, SOME edits) (Version nodes) =
143 |> Graph.default_node (name, empty_node)
144 |> Graph.map_node name (fold edit_node edits))
145 | edit_nodes (name, NONE) (Version nodes) = Version (Graph.del_node name nodes);
147 val empty_version = Version Graph.empty;
149 fun the_node version name =
150 Graph.get_node (nodes_of version) name handle Graph.UNDEF _ => empty_node;
152 fun put_node name node (Version nodes) =
153 Version (Graph.map_node name (K node) nodes); (* FIXME Graph.UNDEF !? *)
159 (** global state -- document structure and execution process **)
161 abstype state = State of
162 {versions: version Inttab.table, (*version_id -> document content*)
163 commands: Toplevel.transition Inttab.table, (*command_id -> transition function*)
164 execs: Toplevel.state option lazy Inttab.table, (*exec_id -> execution process*)
165 execution: unit future list} (*global execution process*)
168 fun make_state (versions, commands, execs, execution) =
169 State {versions = versions, commands = commands, execs = execs, execution = execution};
171 fun map_state f (State {versions, commands, execs, execution}) =
172 make_state (f (versions, commands, execs, execution));
175 make_state (Inttab.make [(no_id, empty_version)],
176 Inttab.make [(no_id, Toplevel.empty)],
177 Inttab.make [(no_id, Lazy.value (SOME Toplevel.toplevel))],
181 (* document versions *)
183 fun define_version (id: version_id) version =
184 map_state (fn (versions, commands, execs, execution) =>
185 let val versions' = Inttab.update_new (id, version) versions
186 handle Inttab.DUP dup => err_dup "document version" dup
187 in (versions', commands, execs, execution) end);
189 fun the_version (State {versions, ...}) (id: version_id) =
190 (case Inttab.lookup versions id of
191 NONE => err_undef "document version" id
192 | SOME version => version);
197 fun define_command (id: command_id) text =
198 map_state (fn (versions, commands, execs, execution) =>
200 val id_string = print_id id;
202 Position.setmp_thread_data (Position.id_only id_string)
203 (fn () => Outer_Syntax.prepare_command (Position.id id_string) text) ();
205 Inttab.update_new (id, Toplevel.put_id id_string tr) commands
206 handle Inttab.DUP dup => err_dup "command" dup;
207 in (versions, commands', execs, execution) end);
209 fun the_command (State {commands, ...}) (id: command_id) =
210 (case Inttab.lookup commands id of
211 NONE => err_undef "command" id
215 (* command executions *)
217 fun define_exec (id: exec_id) exec =
218 map_state (fn (versions, commands, execs, execution) =>
219 let val execs' = Inttab.update_new (id, exec) execs
220 handle Inttab.DUP dup => err_dup "command execution" dup
221 in (versions, commands, execs', execution) end);
223 fun the_exec (State {execs, ...}) (id: exec_id) =
224 (case Inttab.lookup execs id of
225 NONE => err_undef "command execution" id
226 | SOME exec => exec);
238 fun is_changed node' (id, {next = _, exec}) =
239 (case try (the_entry node') id of
241 | SOME {next = _, exec = exec'} => exec' <> exec);
243 fun new_exec name (id: command_id) (exec_id, updates, state) =
245 val exec = the_exec state exec_id;
246 val exec_id' = create_id ();
247 val tr = Toplevel.put_id (print_id exec_id') (the_command state id);
250 (case Lazy.force exec of
252 | SOME st => Toplevel.run_command name tr st));
253 val state' = define_exec exec_id' exec' state;
254 in (exec_id', (id, exec_id') :: updates, state') end;
258 fun edit (old_id: version_id) (new_id: version_id) edits state =
260 val old_version = the_version state old_id;
261 val new_version = fold edit_nodes edits old_version;
263 fun update_node name (rev_updates, version, st) =
264 let val node = the_node version name in
265 (case first_entry (is_changed (the_node old_version name)) node of
266 NONE => (rev_updates, version, st)
267 | SOME (prev, id, _) =>
269 val prev_exec = the (#exec (the_entry node (the prev)));
270 val (_, rev_upds, st') =
271 fold_entries id (new_exec name o #1) node (prev_exec, [], st);
272 val node' = fold set_entry_exec rev_upds node;
273 in (rev_upds @ rev_updates, put_node name node' version, st') end)
276 (* FIXME proper node deps *)
277 val (rev_updates, new_version', state') =
278 fold update_node (node_names_of new_version) ([], new_version, state);
279 val state'' = define_version new_id new_version' state';
280 in (rev rev_updates, state'') end;
287 fun execute version_id state =
288 state |> map_state (fn (versions, commands, execs, execution) =>
290 val version = the_version state version_id;
292 fun force_exec NONE = ()
293 | force_exec (SOME exec_id) = ignore (Lazy.force (the_exec state exec_id));
295 val _ = List.app Future.cancel execution;
296 fun await_cancellation () = uninterruptible (fn _ => Future.join_results) execution;
298 val execution' = (* FIXME proper node deps *)
299 node_names_of version |> map (fn name =>
300 Future.fork_pri 1 (fn () =>
301 (await_cancellation ();
302 fold_entries no_id (fn (_, {exec, ...}) => fn () => force_exec exec)
303 (the_node version name) ())));
304 in (versions, commands, execs, execution') end);