src/Pure/Thy/thy_syntax.scala
changeset 48754 c2c1e5944536
parent 48748 89b4e7d83d6f
child 48755 393a37003851
--- a/src/Pure/Thy/thy_syntax.scala	Fri Aug 10 13:15:00 2012 +0200
+++ b/src/Pure/Thy/thy_syntax.scala	Fri Aug 10 13:33:07 2012 +0200
@@ -194,7 +194,7 @@
   }
 
 
-  /* phase 2: recover command spans */
+  /* phase 2a: reparse range of command spans */
 
   @tailrec private def chop_common(
       cmds: List[Command], spans: List[Command.Span]): (List[Command], List[Command.Span]) =
@@ -203,70 +203,95 @@
       case _ => (cmds, spans)
     }
 
-  private def trim_common(
-      cmds: List[Command], spans: List[Command.Span]): (List[Command], List[Command.Span]) =
+  private def reparse_spans(
+    syntax: Outer_Syntax,
+    name: Document.Node.Name,
+    commands: Linear_Set[Command],
+    first: Command, last: Command): Linear_Set[Command] =
   {
-    val (cmds1, spans1) = chop_common(cmds, spans)
+    val cmds0 = commands.iterator(first, last).toList
+    val spans0 = parse_spans(syntax.scan(cmds0.iterator.map(_.source).mkString))
+
+    val (cmds1, spans1) = chop_common(cmds0, spans0)
+
     val (rev_cmds2, rev_spans2) = chop_common(cmds1.reverse, spans1.reverse)
-    (rev_cmds2.reverse, rev_spans2.reverse)
+    val cmds2 = rev_cmds2.reverse
+    val spans2 = rev_spans2.reverse
+
+    cmds2 match {
+      case Nil =>
+        assert(spans2.isEmpty)
+        commands
+      case cmd :: _ =>
+        val hook = commands.prev(cmd)
+        val inserted = spans2.map(span => Command(Document.new_id(), name, span))
+        (commands /: cmds2)(_ - _).append_after(hook, inserted)
+    }
   }
 
+
+  /* phase 2b: recover command spans after edits */
+
   private def recover_spans(
     syntax: Outer_Syntax,
-    node_name: Document.Node.Name,
+    name: Document.Node.Name,
     perspective: Command.Perspective,
-    old_commands: Linear_Set[Command]): Linear_Set[Command] =
+    commands: Linear_Set[Command]): Linear_Set[Command] =
   {
-    val visible = perspective.commands.iterator.filter(_.is_defined).toSet
+    val visible = perspective.commands.toSet
 
-    def next_invisible_command(commands: Linear_Set[Command], from: Command): Command =
-      commands.iterator(from).dropWhile(cmd => !cmd.is_command || visible(cmd))
-        .find(_.is_command) getOrElse commands.last
-
-    @tailrec def recover(commands: Linear_Set[Command]): Linear_Set[Command] =
-      commands.iterator.find(cmd => !cmd.is_defined) match {
-        case Some(first_undefined) =>
-          val first = next_invisible_command(commands.reverse, first_undefined)
-          val last = next_invisible_command(commands, first_undefined)
+    def next_invisible_command(cmds: Linear_Set[Command], from: Command): Command =
+      cmds.iterator(from).dropWhile(cmd => !cmd.is_command || visible(cmd))
+        .find(_.is_command) getOrElse cmds.last
 
-          val cmds0 = commands.iterator(first, last).toList
-          val spans0 = parse_spans(syntax.scan(cmds0.iterator.map(_.source).mkString))
-
-          val (cmds, spans) = trim_common(cmds0, spans0)
-          val new_commands =
-            cmds match {
-              case Nil =>
-                assert(spans.isEmpty)
-                commands
-              case cmd :: _ =>
-                val hook = commands.prev(cmd)
-                val inserted = spans.map(span => Command(Document.new_id(), node_name, span))
-                (commands /: cmds)(_ - _).append_after(hook, inserted)
-            }
-          recover(new_commands)
-
-        case None => commands
+    @tailrec def recover(cmds: Linear_Set[Command]): Linear_Set[Command] =
+      cmds.find(_.is_unparsed) match {
+        case Some(first_unparsed) =>
+          val first = next_invisible_command(cmds.reverse, first_unparsed)
+          val last = next_invisible_command(cmds, first_unparsed)
+          recover(reparse_spans(syntax, name, cmds, first, last))
+        case None => cmds
       }
-    recover(old_commands)
+    recover(commands)
   }
 
 
-  /* phase 3: full reparsing after syntax change */
+  /* phase 2c: consolidate unfinished spans */
 
-  private def reparse_spans(
+  private def consolidate_spans(
     syntax: Outer_Syntax,
-    node_name: Document.Node.Name,
+    name: Document.Node.Name,
+    perspective: Command.Perspective,
     commands: Linear_Set[Command]): Linear_Set[Command] =
   {
-    val cmds = commands.toList
-    val spans1 = parse_spans(syntax.scan(cmds.map(_.source).mkString))
-    if (cmds.map(_.span) == spans1) commands
-    else Linear_Set(spans1.map(span => Command(Document.new_id(), node_name, span)): _*)
+    if (perspective.commands.isEmpty) commands
+    else {
+      commands.find(_.is_unfinished) match {
+        case Some(first_unfinished) =>
+          val visible = perspective.commands.toSet
+          commands.reverse.find(visible) match {
+            case Some(last_visible) =>
+              reparse_spans(syntax, name, commands, first_unfinished, last_visible)
+            case None => commands
+          }
+        case None => commands
+      }
+    }
   }
 
 
   /* main phase */
 
+  private def diff_commands(old_cmds: Linear_Set[Command], new_cmds: Linear_Set[Command])
+    : List[(Option[Command], Option[Command])] =
+  {
+    val removed = old_cmds.iterator.filter(!new_cmds.contains(_)).toList
+    val inserted = new_cmds.iterator.filter(!old_cmds.contains(_)).toList
+
+    removed.reverse.map(cmd => (old_cmds.prev(cmd), None)) :::
+    inserted.map(cmd => (new_cmds.prev(cmd), Some(cmd)))
+  }
+
   def text_edits(
       base_syntax: Outer_Syntax,
       previous: Document.Version,
@@ -286,21 +311,16 @@
 
       case (name, Document.Node.Edits(text_edits)) =>
         val node = nodes(name)
+
         val commands0 = node.commands
         val commands1 = edit_text(text_edits, commands0)
         val commands2 = recover_spans(syntax, name, node.perspective, commands1)   // FIXME somewhat slow
         val commands3 =
-          if (reparse_set.contains(name)) reparse_spans(syntax, name, commands2)  // slow
+          if (reparse_set.contains(name) && !commands2.isEmpty)
+            reparse_spans(syntax, name, commands2, commands2.head, commands2.last)  // FIXME somewhat slow
           else commands2
 
-        val removed_commands = commands0.iterator.filter(!commands3.contains(_)).toList
-        val inserted_commands = commands3.iterator.filter(!commands0.contains(_)).toList
-
-        val cmd_edits =
-          removed_commands.reverse.map(cmd => (commands0.prev(cmd), None)) :::
-          inserted_commands.map(cmd => (commands3.prev(cmd), Some(cmd)))
-
-        doc_edits += (name -> Document.Node.Edits(cmd_edits))
+        doc_edits += (name -> Document.Node.Edits(diff_commands(commands0, commands3)))
         nodes += (name -> node.update_commands(commands3))
 
       case (name, Document.Node.Deps(_)) =>
@@ -309,10 +329,15 @@
         val node = nodes(name)
         val perspective = command_perspective(node, text_perspective)
         if (!(node.perspective same perspective)) {
+/* FIXME
+          val commands1 = consolidate_spans(syntax, name, perspective, node.commands)
+          doc_edits += (name -> Document.Node.Edits(diff_commands(node.commands, commands1)))
+*/
           doc_edits += (name -> Document.Node.Perspective(perspective))
           nodes += (name -> node.update_perspective(perspective))
         }
     }
+
     (doc_edits.toList, Document.Version.make(syntax, nodes))
   }
 }