src/Tools/jEdit/src/proofdocument/document.scala
author wenzelm
Mon, 11 Jan 2010 13:58:51 +0100
changeset 34865 a1d394600a92
parent 34863 847c00f5535a
child 34866 3e655d0ff936
permissions -rw-r--r--
updated header;
     1 /*
     2  * Document as editable list of commands
     3  *
     4  * @author Makarius
     5  */
     6 
     7 package isabelle.proofdocument
     8 
     9 
    10 object Document
    11 {
    12   /* command start positions */
    13 
    14   def command_starts(commands: Linear_Set[Command]): Iterator[(Command, Int)] =
    15   {
    16     var offset = 0
    17     for (cmd <- commands.elements) yield {
    18       val start = offset
    19       offset += cmd.length
    20       (cmd, start)
    21     }
    22   }
    23 
    24 
    25   /* empty document */
    26 
    27   def empty(id: Isar_Document.Document_ID): Document =
    28   {
    29     val doc = new Document(id, Linear_Set(), Map())
    30     doc.assign_states(Nil)
    31     doc
    32   }
    33 
    34 
    35   // FIXME
    36   var phase0: List[Text_Edit] = null
    37   var phase1: Linear_Set[Command] = null
    38   var phase2: Linear_Set[Command] = null
    39   var phase3: List[Edit] = null
    40   var raw_source: String = null
    41 
    42 
    43 
    44   /** document edits **/
    45 
    46   type Edit = (Option[Command], Option[Command])
    47 
    48   def text_edits(session: Session, old_doc: Document, new_id: Isar_Document.Document_ID,
    49     edits: List[Text_Edit]): (List[Edit], Document) =
    50   {
    51     require(old_doc.assignment.is_finished)
    52 
    53     phase0 = edits
    54 
    55 
    56     /* unparsed dummy commands */
    57 
    58     def unparsed(source: String) =
    59       new Command(null, List(Outer_Lex.Token(Outer_Lex.Token_Kind.UNPARSED, source)))
    60 
    61     def is_unparsed(command: Command) = command.id == null
    62 
    63     assert(!old_doc.commands.exists(is_unparsed))
    64 
    65 
    66     /* phase 1: edit individual command source */
    67 
    68     var commands = old_doc.commands
    69 
    70     def edit_text(eds: List[Text_Edit]): Unit =
    71     {
    72       eds match {
    73         case e :: es =>
    74           command_starts(commands).find {   // FIXME relative search!
    75             case (cmd, cmd_start) => e.can_edit(cmd.length, cmd_start)
    76           } match {  // FIXME try to append after command
    77             case Some((cmd, cmd_start)) =>
    78               val (rest, source) = e.edit(cmd.source, cmd_start)
    79               // FIXME Linear_Set.edit (?)
    80               commands = commands.insert_after(Some(cmd), unparsed(source))
    81               commands -= cmd
    82               edit_text(rest.toList ::: es)
    83 
    84             case None =>
    85               require(e.isInstanceOf[Text_Edit.Insert] && e.start == 0)
    86               commands = commands.insert_after(None, unparsed(e.text))
    87               edit_text(es)
    88           }
    89         case Nil =>
    90       }
    91     }
    92     edit_text(edits)
    93 
    94     phase1 = commands
    95 
    96 
    97     /* phase 2: command range edits */
    98 
    99     def edit_commands(): Unit =
   100     {
   101       // FIXME relative search!
   102       commands.elements.find(is_unparsed) match {
   103         case Some(first_unparsed) =>
   104           val prefix = commands.prev(first_unparsed)
   105           val body = commands.elements(first_unparsed).takeWhile(is_unparsed).toList
   106           val suffix = commands.next(body.last)
   107 
   108           val source =
   109             (prefix.toList ::: body ::: suffix.toList).flatMap(_.span.map(_.source)).mkString
   110           assert(source != "")
   111           raw_source = source
   112           
   113           val spans0 = Thy_Syntax.parse_spans(session.current_syntax.scan(source))
   114 
   115           val (before_edit, spans1) =
   116             if (!spans0.isEmpty && Some(spans0.first) == prefix.map(_.span))
   117               (prefix, spans0.tail)
   118             else (if (prefix.isDefined) commands.prev(prefix.get) else None, spans0)
   119 
   120           val (after_edit, spans2) =
   121             if (!spans1.isEmpty && Some(spans1.last) == suffix.map(_.span))
   122               (suffix, spans1.take(spans1.length - 1))
   123             else (if (suffix.isDefined) commands.next(suffix.get) else None, spans1)
   124 
   125           val new_commands = spans2.map(span => new Command(session.create_id(), span))
   126 
   127           commands = commands.delete_between(before_edit, after_edit)
   128           commands = commands.append_after(before_edit, new_commands)
   129 
   130           edit_commands()
   131 
   132         case None =>
   133       }
   134     }
   135     edit_commands()
   136     
   137     phase2 = commands
   138 
   139 
   140     /* phase 3: resulting document edits */
   141 
   142     val removed_commands = old_doc.commands.elements.filter(!commands.contains(_)).toList
   143     val inserted_commands = commands.elements.filter(!old_doc.commands.contains(_)).toList
   144 
   145     // FIXME proper order
   146     val doc_edits =
   147       removed_commands.reverse.map(cmd => (commands.prev(cmd), None)) :::
   148       inserted_commands.map(cmd => (commands.prev(cmd), Some(cmd)))
   149 
   150     val former_states = old_doc.assignment.join -- removed_commands
   151 
   152     phase3 = doc_edits
   153     (doc_edits, new Document(new_id, commands, former_states))
   154   }
   155 }
   156 
   157 
   158 class Document(
   159     val id: Isar_Document.Document_ID,
   160     val commands: Linear_Set[Command],
   161     former_states: Map[Command, Command])
   162 {
   163   /* command ranges */
   164 
   165   def command_starts: Iterator[(Command, Int)] = Document.command_starts(commands)
   166 
   167   def command_start(cmd: Command): Option[Int] =
   168     command_starts.find(_._1 == cmd).map(_._2)
   169 
   170   def command_range(i: Int): Iterator[(Command, Int)] =
   171     command_starts dropWhile { case (cmd, start) => start + cmd.length <= i }
   172 
   173   def command_range(i: Int, j: Int): Iterator[(Command, Int)] =
   174     command_range(i) takeWhile { case (_, start) => start < j }
   175 
   176   def command_at(i: Int): Option[(Command, Int)] =
   177   {
   178     val range = command_range(i)
   179     if (range.hasNext) Some(range.next) else None
   180   }
   181 
   182 
   183   /* command state assignment */
   184 
   185   val assignment = Future.promise[Map[Command, Command]]
   186   def await_assignment { assignment.join }
   187 
   188   @volatile private var tmp_states = former_states
   189 
   190   def assign_states(new_states: List[(Command, Command)])
   191   {
   192     assignment.fulfill(tmp_states ++ new_states)
   193     tmp_states = Map()
   194   }
   195 
   196   def current_state(cmd: Command): State =
   197   {
   198     require(assignment.is_finished)
   199     (assignment.join)(cmd).current_state
   200   }
   201 }