src/Pure/PIDE/document.scala
changeset 38424 940a404e45e2
parent 38418 9a7af64d71bb
child 38425 e467db701d78
--- a/src/Pure/PIDE/document.scala	Sun Aug 15 20:27:29 2010 +0200
+++ b/src/Pure/PIDE/document.scala	Sun Aug 15 21:03:13 2010 +0200
@@ -30,12 +30,21 @@
 
 
 
-  /** named document nodes **/
+  /** document structure **/
+
+  /* named nodes -- development graph */
+
+  type Node_Text_Edit = (String, List[Text_Edit])  // FIXME None: remove
+
+  type Edit[C] =
+   (String,  // node name
+    Option[List[(Option[C], Option[C])]])  // None: remove, Some: insert/remove commands
 
   object Node
   {
     val empty: Node = new Node(Linear_Set())
 
+    // FIXME not scalable
     def command_starts(commands: Iterator[Command], offset: Int = 0): Iterator[(Command, Int)] =
     {
       var i = offset
@@ -49,8 +58,6 @@
 
   class Node(val commands: Linear_Set[Command])
   {
-    /* command ranges */
-
     def command_starts: Iterator[(Command, Int)] =
       Node.command_starts(commands.iterator)
 
@@ -78,7 +85,10 @@
   }
 
 
-  /* document versions */
+
+  /** versioning **/
+
+  /* particular document versions */
 
   object Version
   {
@@ -88,25 +98,7 @@
   class Version(val id: Version_ID, val nodes: Map[String, Node])
 
 
-
-  /** changes of plain text, eventually resulting in document edits **/
-
-  type Node_Text_Edit = (String, List[Text_Edit])  // FIXME None: remove
-
-  type Edit[C] =
-   (String,  // node name
-    Option[List[(Option[C], Option[C])]])  // None: remove, Some: insert/remove commands
-
-  abstract class Snapshot
-  {
-    val version: Version
-    val node: Node
-    val is_outdated: Boolean
-    def convert(offset: Int): Int
-    def revert(offset: Int): Int
-    def lookup_command(id: Command_ID): Option[Command]
-    def state(command: Command): Command.State
-  }
+  /* changes of plain text, eventually resulting in document edits */
 
   object Change
   {
@@ -123,6 +115,61 @@
   }
 
 
+  /* history navigation and persistent snapshots */
+
+  abstract class Snapshot
+  {
+    val version: Version
+    val node: Node
+    val is_outdated: Boolean
+    def convert(offset: Int): Int
+    def revert(offset: Int): Int
+    def lookup_command(id: Command_ID): Option[Command]
+    def state(command: Command): Command.State
+  }
+
+  object History
+  {
+    val init = new History(List(Change.init))
+  }
+
+  // FIXME pruning, purging of state
+  class History(undo_list: List[Change])
+  {
+    require(!undo_list.isEmpty)
+
+    def tip: Change = undo_list.head
+    def +(ch: Change): History = new History(ch :: undo_list)
+
+    def snapshot(name: String, pending_edits: List[Text_Edit], state_snapshot: State): Snapshot =
+    {
+      val found_stable = undo_list.find(change =>
+        change.is_finished && state_snapshot.is_assigned(change.current.join))
+      require(found_stable.isDefined)
+      val stable = found_stable.get
+      val latest = undo_list.head
+
+      val edits =
+        (pending_edits /: undo_list.takeWhile(_ != stable))((edits, change) =>
+            (for ((a, eds) <- change.edits if a == name) yield eds).flatten ::: edits)
+      lazy val reverse_edits = edits.reverse
+
+      new Snapshot
+      {
+        val version = stable.current.join
+        val node = version.nodes(name)
+        val is_outdated = !(pending_edits.isEmpty && latest == stable)
+        def convert(offset: Int): Int = (offset /: edits)((i, edit) => edit.convert(i))
+        def revert(offset: Int): Int = (offset /: reverse_edits)((i, edit) => edit.revert(i))
+        def lookup_command(id: Command_ID): Option[Command] = state_snapshot.lookup_command(id)
+        def state(command: Command): Command.State =
+          try { state_snapshot.command_state(version, command) }
+          catch { case _: State.Fail => command.empty_state }
+      }
+    }
+  }
+
+
 
   /** global state -- document structure and execution process **/