src/Pure/PIDE/document.scala
changeset 56470 8eda56033203
parent 56468 30128d1acfbc
child 56473 5b5c750e9763
--- a/src/Pure/PIDE/document.scala	Tue Apr 08 16:07:02 2014 +0200
+++ b/src/Pure/PIDE/document.scala	Tue Apr 08 19:17:28 2014 +0200
@@ -483,11 +483,19 @@
   }
 
   final case class State private(
+    /*reachable versions*/
     val versions: Map[Document_ID.Version, Version] = Map.empty,
-    val blobs: Set[SHA1.Digest] = Set.empty,   // inlined files
-    val commands: Map[Document_ID.Command, Command.State] = Map.empty,  // static markup from define_command
-    val execs: Map[Document_ID.Exec, Command.State] = Map.empty,  // dynamic markup from execution
+    /*inlined auxiliary files*/
+    val blobs: Set[SHA1.Digest] = Set.empty,
+    /*static markup from define_command*/
+    val commands: Map[Document_ID.Command, Command.State] = Map.empty,
+    /*dynamic markup from execution*/
+    val execs: Map[Document_ID.Exec, Command.State] = Map.empty,
+    /*command-exec assignment for each version*/
     val assignments: Map[Document_ID.Version, State.Assignment] = Map.empty,
+    /*commands with markup produced by other commands (imm_succs)*/
+    val augmented_commands: Graph[Document_ID.Command, Unit] = Graph.long,
+    /*explicit (linear) history*/
     val history: History = History.init)
   {
     private def fail[A]: A = throw new State.Fail(this)
@@ -524,23 +532,35 @@
     def the_dynamic_state(id: Document_ID.Exec): Command.State = execs.getOrElse(id, fail)
     def the_assignment(version: Version): State.Assignment = assignments.getOrElse(version.id, fail)
 
-    def valid_id(st: Command.State)(id: Document_ID.Generic): Boolean =
+    private def self_id(st: Command.State)(id: Document_ID.Generic): Boolean =
       id == st.command.id ||
       (execs.get(id) match { case Some(st1) => st1.command.id == st.command.id case None => false })
 
+    private def other_id(id: Document_ID.Generic): Option[(Text.Chunk.Id, Text.Chunk)] =
+      (execs.get(id) orElse commands.get(id)).map(st =>
+        ((Text.Chunk.Id(st.command.id), st.command.chunk)))
+
+    private def augmented(st: Command.State): Graph[Document_ID.Command, Unit] =
+      (augmented_commands /: st.markups.other_id_iterator)({ case (graph, id) =>
+        graph.default_node(id, ()).default_node(st.command.id, ()).add_edge(id, st.command.id) })
+
     def accumulate(id: Document_ID.Generic, message: XML.Elem): (Command.State, State) =
+    {
       execs.get(id) match {
         case Some(st) =>
-          val new_st = st + (valid_id(st), message)
-          (new_st, copy(execs = execs + (id -> new_st)))
+          val new_st = st.accumulate(self_id(st), other_id _, message)
+          val execs1 = execs + (id -> new_st)
+          (new_st, copy(execs = execs1, augmented_commands = augmented(new_st)))
         case None =>
           commands.get(id) match {
             case Some(st) =>
-              val new_st = st + (valid_id(st), message)
-              (new_st, copy(commands = commands + (id -> new_st)))
+              val new_st = st.accumulate(self_id(st), other_id _, message)
+              val commands1 = commands + (id -> new_st)
+              (new_st, copy(commands = commands1, augmented_commands = augmented(new_st)))
             case None => fail
           }
       }
+    }
 
     def assign(id: Document_ID.Version, update: Assign_Update): (List[Command], State) =
     {
@@ -629,27 +649,48 @@
             execs.get(exec_id).foreach(st => execs1 += (exec_id -> st))
         }
       }
-      copy(versions = versions1, blobs = blobs1, commands = commands1, execs = execs1,
+      copy(
+        versions = versions1,
+        blobs = blobs1,
+        commands = commands1,
+        execs = execs1,
+        augmented_commands = augmented_commands.restrict(commands1.isDefinedAt(_)),
         assignments = assignments1)
     }
 
-    def command_states(version: Version, command: Command): List[Command.State] =
+    private def command_states_self(version: Version, command: Command)
+      : List[(Document_ID.Generic, Command.State)] =
     {
       require(is_assigned(version))
       try {
         the_assignment(version).check_finished.command_execs.getOrElse(command.id, Nil)
-          .map(the_dynamic_state(_)) match {
+          .map(id => id -> the_dynamic_state(id)) match {
             case Nil => fail
-            case states => states
+            case res => res
           }
       }
       catch {
         case _: State.Fail =>
-          try { List(the_static_state(command.id)) }
-          catch { case _: State.Fail => List(command.init_state) }
+          try { List(command.id -> the_static_state(command.id)) }
+          catch { case _: State.Fail => List(command.id -> command.init_state) }
       }
     }
 
+    def command_states(version: Version, command: Command): List[Command.State] =
+    {
+      val self = command_states_self(version, command)
+      val others =
+        if (augmented_commands.defined(command.id)) {
+          (for {
+            command_id <- augmented_commands.imm_succs(command.id).iterator
+            (id, st) <- command_states_self(version, the_static_state(command_id).command)
+            if !self.exists(_._1 == id)
+          } yield (id, st)).toMap.valuesIterator.toList
+        }
+        else Nil
+      self.map(_._2) ::: others.map(_.retarget(command))
+    }
+
     def command_results(version: Version, command: Command): Command.Results =
       Command.State.merge_results(command_states(version, command))