src/Pure/PIDE/isar_document.scala
author wenzelm
Fri, 12 Aug 2011 11:41:26 +0200
changeset 45037 fe6d1ae7a065
parent 45028 a21d3e1e64fd
child 45038 9a35e88d9dc9
permissions -rw-r--r--
clarified Exn.message;
     1 /*  Title:      Pure/PIDE/isar_document.scala
     2     Author:     Makarius
     3 
     4 Protocol message formats for interactive Isar documents.
     5 */
     6 
     7 package isabelle
     8 
     9 
    10 object Isar_Document
    11 {
    12   /* document editing */
    13 
    14   object Assign {
    15     def unapply(msg: XML.Tree)
    16         : Option[(Document.Version_ID, List[(Document.Command_ID, Document.Exec_ID)])] =
    17       msg match {
    18         case XML.Elem(Markup(Markup.ASSIGN, List((Markup.VERSION, Document.ID(id)))), edits) =>
    19           val id_edits = edits.map(Edit.unapply)
    20           if (id_edits.forall(_.isDefined)) Some((id, id_edits.map(_.get)))
    21           else None
    22         case _ => None
    23       }
    24   }
    25 
    26   object Edit {
    27     def unapply(msg: XML.Tree): Option[(Document.Command_ID, Document.Exec_ID)] =
    28       msg match {
    29         case XML.Elem(
    30           Markup(Markup.EDIT,
    31             List((Markup.ID, Document.ID(i)), (Markup.EXEC, Document.ID(j)))), Nil) => Some((i, j))
    32         case _ => None
    33       }
    34   }
    35 
    36 
    37   /* toplevel transactions */
    38 
    39   sealed abstract class Status
    40   case class Forked(forks: Int) extends Status
    41   case object Unprocessed extends Status
    42   case object Finished extends Status
    43   case object Failed extends Status
    44 
    45   def command_status(markup: List[Markup]): Status =
    46   {
    47     val forks = (0 /: markup) {
    48       case (i, Markup(Markup.FORKED, _)) => i + 1
    49       case (i, Markup(Markup.JOINED, _)) => i - 1
    50       case (i, _) => i
    51     }
    52     if (forks != 0) Forked(forks)
    53     else if (markup.exists(_.name == Markup.FAILED)) Failed
    54     else if (markup.exists(_.name == Markup.FINISHED)) Finished
    55     else Unprocessed
    56   }
    57 
    58 
    59   /* result messages */
    60 
    61   def clean_message(body: XML.Body): XML.Body =
    62     body filter { case XML.Elem(Markup(Markup.NO_REPORT, _), _) => false case _ => true } map
    63       { case XML.Elem(markup, ts) => XML.Elem(markup, clean_message(ts)) case t => t }
    64 
    65   def message_reports(msg: XML.Tree): List[XML.Elem] =
    66     msg match {
    67       case elem @ XML.Elem(Markup(Markup.REPORT, _), _) => List(elem)
    68       case XML.Elem(_, body) => body.flatMap(message_reports)
    69       case XML.Text(_) => Nil
    70     }
    71 
    72 
    73   /* specific messages */
    74 
    75   def is_ready(msg: XML.Tree): Boolean =
    76     msg match {
    77       case XML.Elem(Markup(Markup.STATUS, _),
    78         List(XML.Elem(Markup(Markup.READY, _), _))) => true
    79       case _ => false
    80     }
    81 
    82  def is_tracing(msg: XML.Tree): Boolean =
    83     msg match {
    84       case XML.Elem(Markup(Markup.TRACING, _), _) => true
    85       case _ => false
    86     }
    87 
    88   def is_warning(msg: XML.Tree): Boolean =
    89     msg match {
    90       case XML.Elem(Markup(Markup.WARNING, _), _) => true
    91       case _ => false
    92     }
    93 
    94   def is_error(msg: XML.Tree): Boolean =
    95     msg match {
    96       case XML.Elem(Markup(Markup.ERROR, _), _) => true
    97       case _ => false
    98     }
    99 
   100   def is_state(msg: XML.Tree): Boolean =
   101     msg match {
   102       case XML.Elem(Markup(Markup.WRITELN, _),
   103         List(XML.Elem(Markup(Markup.STATE, _), _))) => true
   104       case _ => false
   105     }
   106 
   107 
   108   /* reported positions */
   109 
   110   private val include_pos =
   111     Set(Markup.BINDING, Markup.ENTITY, Markup.REPORT, Markup.POSITION)
   112 
   113   def message_positions(command: Command, message: XML.Elem): Set[Text.Range] =
   114   {
   115     def positions(set: Set[Text.Range], tree: XML.Tree): Set[Text.Range] =
   116       tree match {
   117         case XML.Elem(Markup(name, Position.Id_Range(id, raw_range)), body)
   118         if include_pos(name) && id == command.id =>
   119           val range = command.decode(raw_range).restrict(command.range)
   120           body.foldLeft(if (range.is_singularity) set else set + range)(positions)
   121         case XML.Elem(Markup(name, _), body) => body.foldLeft(set)(positions)
   122         case _ => set
   123       }
   124     val set = positions(Set.empty, message)
   125     if (set.isEmpty && !is_state(message))
   126       set ++ Position.Range.unapply(message.markup.properties).map(command.decode(_))
   127     else set
   128   }
   129 }
   130 
   131 
   132 trait Isar_Document extends Isabelle_Process
   133 {
   134   /* commands */
   135 
   136   def define_command(id: Document.Command_ID, text: String): Unit =
   137     input("Isar_Document.define_command", Document.ID(id), text)
   138 
   139 
   140   /* document versions */
   141 
   142   def edit_version(old_id: Document.Version_ID, new_id: Document.Version_ID,
   143     edits: List[Document.Edit_Command_ID])
   144   {
   145     val edits_yxml =
   146     { import XML.Encode._
   147       def encode: T[List[Document.Edit_Command_ID]] =
   148         list(pair(string,
   149           variant(List(
   150             { case Document.Node.Remove() => (Nil, Nil) },
   151             { case Document.Node.Edits(a) => (Nil, list(pair(option(long), option(long)))(a)) },
   152             { case Document.Node.Update_Header(
   153                   Document.Node.Header(_, Exn.Res(Thy_Header.Header(a, b, c)))) =>
   154                 (Nil, triple(string, list(string), list(string))(a, b, c)) },
   155             { case Document.Node.Update_Header(
   156                   Document.Node.Header(_, Exn.Exn(e))) => (List(Exn.message(e)), Nil) }))))
   157       YXML.string_of_body(encode(edits)) }
   158 
   159     input("Isar_Document.edit_version", Document.ID(old_id), Document.ID(new_id), edits_yxml)
   160   }
   161 
   162 
   163   /* method invocation service */
   164 
   165   def invoke_scala(id: String, tag: Invoke_Scala.Tag.Value, res: String)
   166   {
   167     input("Isar_Document.invoke_scala", id, tag.toString, res)
   168   }
   169 }