166 |
166 |
167 /* changes of plain text, eventually resulting in document edits */ |
167 /* changes of plain text, eventually resulting in document edits */ |
168 |
168 |
169 object Change |
169 object Change |
170 { |
170 { |
171 val init: Change = Change(Future.value(Version.init), Nil, Future.value(Version.init)) |
171 val init: Change = Change(Some(Future.value(Version.init)), Nil, Future.value(Version.init)) |
172 } |
172 } |
173 |
173 |
174 sealed case class Change( |
174 sealed case class Change( |
175 val previous: Future[Version], |
175 val previous: Option[Future[Version]], |
176 val edits: List[Edit_Text], |
176 val edits: List[Edit_Text], |
177 val version: Future[Version]) |
177 val version: Future[Version]) |
178 { |
178 { |
179 def is_finished: Boolean = previous.is_finished && version.is_finished |
179 def is_finished: Boolean = |
|
180 (previous match { case None => true case Some(future) => future.is_finished }) && |
|
181 version.is_finished |
|
182 |
|
183 def truncate: Change = copy(previous = None, edits = Nil) |
180 } |
184 } |
181 |
185 |
182 |
186 |
183 /* history navigation */ |
187 /* history navigation */ |
184 |
188 |
185 object History |
189 object History |
186 { |
190 { |
187 val init: History = new History(List(Change.init)) |
191 val init: History = History(List(Change.init)) |
188 } |
192 } |
189 |
193 |
190 // FIXME pruning, purging of state |
194 // FIXME pruning, purging of state |
191 class History(val undo_list: List[Change]) |
195 sealed case class History(val undo_list: List[Change]) |
192 { |
196 { |
193 require(!undo_list.isEmpty) |
197 require(!undo_list.isEmpty) |
194 |
198 |
195 def tip: Change = undo_list.head |
199 def tip: Change = undo_list.head |
196 def +(change: Change): History = new History(change :: undo_list) |
200 def +(change: Change): History = copy(undo_list = change :: undo_list) |
197 } |
201 } |
198 |
202 |
199 |
203 |
200 |
204 |
201 /** global state -- document structure, execution process, editing history **/ |
205 /** global state -- document structure, execution process, editing history **/ |
340 } |
344 } |
341 |
345 |
342 def is_stable(change: Change): Boolean = |
346 def is_stable(change: Change): Boolean = |
343 change.is_finished && is_assigned(change.version.get_finished) |
347 change.is_finished && is_assigned(change.version.get_finished) |
344 |
348 |
345 def recent_stable: Option[Change] = history.undo_list.find(is_stable) |
349 def recent_stable: Change = history.undo_list.find(is_stable) getOrElse fail |
346 def tip_stable: Boolean = is_stable(history.tip) |
350 def tip_stable: Boolean = is_stable(history.tip) |
347 def tip_version: Version = history.tip.version.get_finished |
351 def tip_version: Version = history.tip.version.get_finished |
348 |
352 |
349 def last_exec_offset(name: Node.Name): Text.Offset = |
353 def last_exec_offset(name: Node.Name): Text.Offset = |
350 { |
354 { |
364 def continue_history( |
368 def continue_history( |
365 previous: Future[Version], |
369 previous: Future[Version], |
366 edits: List[Edit_Text], |
370 edits: List[Edit_Text], |
367 version: Future[Version]): (Change, State) = |
371 version: Future[Version]): (Change, State) = |
368 { |
372 { |
369 val change = Change(previous, edits, version) |
373 val change = Change(Some(previous), edits, version) |
370 (change, copy(history = history + change)) |
374 (change, copy(history = history + change)) |
|
375 } |
|
376 |
|
377 def prune_history(retain: Int = 0): (List[Version], State) = |
|
378 { |
|
379 val undo_list = history.undo_list |
|
380 val n = undo_list.iterator.zipWithIndex.find(p => is_stable(p._1)).get._2 + 1 |
|
381 val (retained, dropped) = undo_list.splitAt(n max retain) |
|
382 |
|
383 retained.splitAt(retained.length - 1) match { |
|
384 case (prefix, List(last)) => |
|
385 val dropped_versions = dropped.map(change => change.version.get_finished) |
|
386 val state1 = copy(history = History(prefix ::: List(last.truncate))) |
|
387 (dropped_versions, state1) |
|
388 case _ => fail |
|
389 } |
371 } |
390 } |
372 |
391 |
373 def command_state(version: Version, command: Command): Command.State = |
392 def command_state(version: Version, command: Command): Command.State = |
374 { |
393 { |
375 require(is_assigned(version)) |
394 require(is_assigned(version)) |
382 |
401 |
383 |
402 |
384 // persistent user-view |
403 // persistent user-view |
385 def snapshot(name: Node.Name, pending_edits: List[Text.Edit]): Snapshot = |
404 def snapshot(name: Node.Name, pending_edits: List[Text.Edit]): Snapshot = |
386 { |
405 { |
387 val stable = recent_stable.get |
406 val stable = recent_stable |
388 val latest = history.tip |
407 val latest = history.tip |
389 |
408 |
390 // FIXME proper treatment of removed nodes |
409 // FIXME proper treatment of removed nodes |
391 val edits = |
410 val edits = |
392 (pending_edits /: history.undo_list.takeWhile(_ != stable))((edits, change) => |
411 (pending_edits /: history.undo_list.takeWhile(_ != stable))((edits, change) => |