src/Pure/PIDE/protocol.scala
author wenzelm
Fri Sep 29 20:49:42 2017 +0200 (24 months ago)
changeset 66717 67dbf5cdc056
parent 66712 4c98c929a12a
child 66873 9953ae603a23
permissions -rw-r--r--
more informative loaded_theories: dependencies and syntax;
wenzelm@45709
     1
/*  Title:      Pure/PIDE/protocol.scala
wenzelm@38412
     2
    Author:     Makarius
wenzelm@38412
     3
wenzelm@45709
     4
Protocol message formats for interactive proof documents.
wenzelm@38412
     5
*/
wenzelm@38412
     6
wenzelm@38412
     7
package isabelle
wenzelm@38412
     8
wenzelm@38412
     9
wenzelm@45709
    10
object Protocol
wenzelm@38412
    11
{
wenzelm@38567
    12
  /* document editing */
wenzelm@38412
    13
wenzelm@52563
    14
  object Assign_Update
wenzelm@44476
    15
  {
wenzelm@52563
    16
    def unapply(text: String): Option[(Document_ID.Version, Document.Assign_Update)] =
wenzelm@44661
    17
      try {
wenzelm@44661
    18
        import XML.Decode._
wenzelm@65345
    19
        Some(pair(long, list(pair(long, list(long))))(Symbol.decode_yxml(text)))
wenzelm@44661
    20
      }
wenzelm@44661
    21
      catch {
wenzelm@44661
    22
        case ERROR(_) => None
wenzelm@51987
    23
        case _: XML.Error => None
wenzelm@38412
    24
      }
wenzelm@38412
    25
  }
wenzelm@38567
    26
wenzelm@44676
    27
  object Removed
wenzelm@44676
    28
  {
wenzelm@52530
    29
    def unapply(text: String): Option[List[Document_ID.Version]] =
wenzelm@44676
    30
      try {
wenzelm@44676
    31
        import XML.Decode._
wenzelm@65345
    32
        Some(list(long)(Symbol.decode_yxml(text)))
wenzelm@44676
    33
      }
wenzelm@44676
    34
      catch {
wenzelm@44676
    35
        case ERROR(_) => None
wenzelm@51987
    36
        case _: XML.Error => None
wenzelm@44676
    37
      }
wenzelm@44676
    38
  }
wenzelm@44676
    39
wenzelm@38567
    40
wenzelm@46209
    41
  /* command status */
wenzelm@38567
    42
wenzelm@56359
    43
  object Status
wenzelm@56359
    44
  {
wenzelm@56359
    45
    def make(markup_iterator: Iterator[Markup]): Status =
wenzelm@56359
    46
    {
wenzelm@56359
    47
      var touched = false
wenzelm@56359
    48
      var accepted = false
wenzelm@56395
    49
      var warned = false
wenzelm@56359
    50
      var failed = false
wenzelm@56359
    51
      var forks = 0
wenzelm@56359
    52
      var runs = 0
wenzelm@56359
    53
      for (markup <- markup_iterator) {
wenzelm@56359
    54
        markup.name match {
wenzelm@56359
    55
          case Markup.ACCEPTED => accepted = true
wenzelm@56359
    56
          case Markup.FORKED => touched = true; forks += 1
wenzelm@56359
    57
          case Markup.JOINED => forks -= 1
wenzelm@56359
    58
          case Markup.RUNNING => touched = true; runs += 1
wenzelm@56359
    59
          case Markup.FINISHED => runs -= 1
wenzelm@59203
    60
          case Markup.WARNING | Markup.LEGACY => warned = true
wenzelm@56395
    61
          case Markup.FAILED | Markup.ERROR => failed = true
wenzelm@56359
    62
          case _ =>
wenzelm@56359
    63
        }
wenzelm@56359
    64
      }
wenzelm@56395
    65
      Status(touched, accepted, warned, failed, forks, runs)
wenzelm@56359
    66
    }
wenzelm@56359
    67
wenzelm@56359
    68
    val empty = make(Iterator.empty)
wenzelm@56359
    69
wenzelm@56359
    70
    def merge(status_iterator: Iterator[Status]): Status =
wenzelm@56359
    71
      if (status_iterator.hasNext) {
wenzelm@56359
    72
        val status0 = status_iterator.next
wenzelm@56359
    73
        (status0 /: status_iterator)(_ + _)
wenzelm@56359
    74
      }
wenzelm@56359
    75
      else empty
wenzelm@56359
    76
  }
wenzelm@56359
    77
wenzelm@46166
    78
  sealed case class Status(
wenzelm@56352
    79
    private val touched: Boolean,
wenzelm@56352
    80
    private val accepted: Boolean,
wenzelm@56395
    81
    private val warned: Boolean,
wenzelm@56352
    82
    private val failed: Boolean,
wenzelm@56352
    83
    forks: Int,
wenzelm@56352
    84
    runs: Int)
wenzelm@38567
    85
  {
wenzelm@56359
    86
    def + (that: Status): Status =
wenzelm@56395
    87
      Status(
wenzelm@56395
    88
        touched || that.touched,
wenzelm@56395
    89
        accepted || that.accepted,
wenzelm@56395
    90
        warned || that.warned,
wenzelm@56395
    91
        failed || that.failed,
wenzelm@56395
    92
        forks + that.forks,
wenzelm@56395
    93
        runs + that.runs)
wenzelm@56359
    94
wenzelm@49036
    95
    def is_unprocessed: Boolean = accepted && !failed && (!touched || (forks != 0 && runs == 0))
wenzelm@49036
    96
    def is_running: Boolean = runs != 0
wenzelm@56474
    97
    def is_warned: Boolean = warned
wenzelm@56474
    98
    def is_failed: Boolean = failed
wenzelm@49039
    99
    def is_finished: Boolean = !failed && touched && forks == 0 && runs == 0
wenzelm@46166
   100
  }
wenzelm@46166
   101
wenzelm@56395
   102
  val proper_status_elements =
wenzelm@56743
   103
    Markup.Elements(Markup.ACCEPTED, Markup.FORKED, Markup.JOINED, Markup.RUNNING,
wenzelm@55646
   104
      Markup.FINISHED, Markup.FAILED)
wenzelm@55646
   105
wenzelm@56395
   106
  val liberal_status_elements =
wenzelm@59203
   107
    proper_status_elements + Markup.WARNING + Markup.LEGACY + Markup.ERROR
wenzelm@55646
   108
wenzelm@46209
   109
wenzelm@51818
   110
  /* command timing */
wenzelm@51818
   111
wenzelm@51818
   112
  object Command_Timing
wenzelm@51818
   113
  {
wenzelm@52531
   114
    def unapply(props: Properties.T): Option[(Document_ID.Generic, isabelle.Timing)] =
wenzelm@51818
   115
      props match {
wenzelm@51818
   116
        case (Markup.FUNCTION, Markup.COMMAND_TIMING) :: args =>
wenzelm@51818
   117
          (args, args) match {
wenzelm@51818
   118
            case (Position.Id(id), Markup.Timing_Properties(timing)) => Some((id, timing))
wenzelm@51818
   119
            case _ => None
wenzelm@51818
   120
          }
wenzelm@51818
   121
        case _ => None
wenzelm@51818
   122
      }
wenzelm@51818
   123
  }
wenzelm@51818
   124
wenzelm@51818
   125
wenzelm@46209
   126
  /* node status */
wenzelm@46209
   127
wenzelm@46688
   128
  sealed case class Node_Status(
wenzelm@66410
   129
    unprocessed: Int, running: Int, warned: Int, failed: Int, finished: Int, consolidated: Boolean)
wenzelm@44866
   130
  {
wenzelm@56474
   131
    def total: Int = unprocessed + running + warned + failed + finished
wenzelm@66411
   132
    def ok: Boolean = failed == 0
wenzelm@44866
   133
  }
wenzelm@44613
   134
wenzelm@44613
   135
  def node_status(
wenzelm@66410
   136
    state: Document.State,
wenzelm@66410
   137
    version: Document.Version,
wenzelm@66410
   138
    name: Document.Node.Name,
wenzelm@66410
   139
    node: Document.Node): Node_Status =
wenzelm@44613
   140
  {
wenzelm@44613
   141
    var unprocessed = 0
wenzelm@44613
   142
    var running = 0
wenzelm@46688
   143
    var warned = 0
wenzelm@44613
   144
    var failed = 0
wenzelm@56474
   145
    var finished = 0
wenzelm@56356
   146
    for (command <- node.commands.iterator) {
wenzelm@56355
   147
      val states = state.command_states(version, command)
wenzelm@56359
   148
      val status = Status.merge(states.iterator.map(_.protocol_status))
wenzelm@56355
   149
wenzelm@56299
   150
      if (status.is_running) running += 1
wenzelm@57843
   151
      else if (status.is_failed) failed += 1
wenzelm@56395
   152
      else if (status.is_warned) warned += 1
wenzelm@56395
   153
      else if (status.is_finished) finished += 1
wenzelm@56299
   154
      else unprocessed += 1
wenzelm@56299
   155
    }
wenzelm@66410
   156
    val consolidated = state.node_consolidated(version, name)
wenzelm@66410
   157
wenzelm@66410
   158
    Node_Status(unprocessed, running, warned, failed, finished, consolidated)
wenzelm@44613
   159
  }
wenzelm@44613
   160
wenzelm@38887
   161
wenzelm@51533
   162
  /* node timing */
wenzelm@51533
   163
wenzelm@51533
   164
  sealed case class Node_Timing(total: Double, commands: Map[Command, Double])
wenzelm@51533
   165
wenzelm@51533
   166
  val empty_node_timing = Node_Timing(0.0, Map.empty)
wenzelm@51533
   167
wenzelm@51533
   168
  def node_timing(
wenzelm@51533
   169
    state: Document.State,
wenzelm@51533
   170
    version: Document.Version,
wenzelm@51533
   171
    node: Document.Node,
wenzelm@51533
   172
    threshold: Double): Node_Timing =
wenzelm@51533
   173
  {
wenzelm@51533
   174
    var total = 0.0
wenzelm@51533
   175
    var commands = Map.empty[Command, Double]
wenzelm@51533
   176
    for {
wenzelm@51533
   177
      command <- node.commands.iterator
wenzelm@56299
   178
      st <- state.command_states(version, command)
wenzelm@56356
   179
    } {
wenzelm@56356
   180
      val command_timing =
wenzelm@51533
   181
        (0.0 /: st.status)({
wenzelm@51533
   182
          case (timing, Markup.Timing(t)) => timing + t.elapsed.seconds
wenzelm@51533
   183
          case (timing, _) => timing
wenzelm@51533
   184
        })
wenzelm@51533
   185
      total += command_timing
wenzelm@51533
   186
      if (command_timing >= threshold) commands += (command -> command_timing)
wenzelm@51533
   187
    }
wenzelm@51533
   188
    Node_Timing(total, commands)
wenzelm@51533
   189
  }
wenzelm@51533
   190
wenzelm@51533
   191
wenzelm@39441
   192
  /* result messages */
wenzelm@39439
   193
wenzelm@50500
   194
  def is_result(msg: XML.Tree): Boolean =
wenzelm@50500
   195
    msg match {
wenzelm@50500
   196
      case XML.Elem(Markup(Markup.RESULT, _), _) => true
wenzelm@50500
   197
      case _ => false
wenzelm@50500
   198
    }
wenzelm@50500
   199
wenzelm@50157
   200
  def is_tracing(msg: XML.Tree): Boolean =
wenzelm@39622
   201
    msg match {
wenzelm@50201
   202
      case XML.Elem(Markup(Markup.TRACING, _), _) => true
wenzelm@50201
   203
      case XML.Elem(Markup(Markup.TRACING_MESSAGE, _), _) => true
wenzelm@39622
   204
      case _ => false
wenzelm@39622
   205
    }
wenzelm@39622
   206
wenzelm@59184
   207
  def is_state(msg: XML.Tree): Boolean =
wenzelm@50500
   208
    msg match {
wenzelm@59184
   209
      case XML.Elem(Markup(Markup.STATE, _), _) => true
wenzelm@59184
   210
      case XML.Elem(Markup(Markup.STATE_MESSAGE, _), _) => true
wenzelm@59184
   211
      case _ => false
wenzelm@59184
   212
    }
wenzelm@59184
   213
wenzelm@59184
   214
  def is_information(msg: XML.Tree): Boolean =
wenzelm@59184
   215
    msg match {
wenzelm@59184
   216
      case XML.Elem(Markup(Markup.INFORMATION, _), _) => true
wenzelm@59184
   217
      case XML.Elem(Markup(Markup.INFORMATION_MESSAGE, _), _) => true
wenzelm@50500
   218
      case _ => false
wenzelm@50500
   219
    }
wenzelm@50500
   220
wenzelm@39511
   221
  def is_warning(msg: XML.Tree): Boolean =
wenzelm@39511
   222
    msg match {
wenzelm@50201
   223
      case XML.Elem(Markup(Markup.WARNING, _), _) => true
wenzelm@50201
   224
      case XML.Elem(Markup(Markup.WARNING_MESSAGE, _), _) => true
wenzelm@39511
   225
      case _ => false
wenzelm@39511
   226
    }
wenzelm@39511
   227
wenzelm@59203
   228
  def is_legacy(msg: XML.Tree): Boolean =
wenzelm@59203
   229
    msg match {
wenzelm@59203
   230
      case XML.Elem(Markup(Markup.LEGACY, _), _) => true
wenzelm@59203
   231
      case XML.Elem(Markup(Markup.LEGACY_MESSAGE, _), _) => true
wenzelm@59203
   232
      case _ => false
wenzelm@59203
   233
    }
wenzelm@59203
   234
wenzelm@39511
   235
  def is_error(msg: XML.Tree): Boolean =
wenzelm@39511
   236
    msg match {
wenzelm@50201
   237
      case XML.Elem(Markup(Markup.ERROR, _), _) => true
wenzelm@50201
   238
      case XML.Elem(Markup(Markup.ERROR_MESSAGE, _), _) => true
wenzelm@39511
   239
      case _ => false
wenzelm@39511
   240
    }
wenzelm@38887
   241
wenzelm@56495
   242
  def is_inlined(msg: XML.Tree): Boolean =
wenzelm@56495
   243
    !(is_result(msg) || is_tracing(msg) || is_state(msg))
wenzelm@56495
   244
wenzelm@50500
   245
wenzelm@60882
   246
  /* breakpoints */
wenzelm@60882
   247
wenzelm@60882
   248
  object ML_Breakpoint
wenzelm@60882
   249
  {
wenzelm@60882
   250
    def unapply(tree: XML.Tree): Option[Long] =
wenzelm@60882
   251
    tree match {
wenzelm@60882
   252
      case XML.Elem(Markup(Markup.ML_BREAKPOINT, Markup.Serial(breakpoint)), _) => Some(breakpoint)
wenzelm@60882
   253
      case _ => None
wenzelm@60882
   254
    }
wenzelm@60882
   255
  }
wenzelm@60882
   256
wenzelm@60882
   257
wenzelm@50500
   258
  /* dialogs */
wenzelm@50500
   259
wenzelm@50500
   260
  object Dialog_Args
wenzelm@50500
   261
  {
wenzelm@52531
   262
    def unapply(props: Properties.T): Option[(Document_ID.Generic, Long, String)] =
wenzelm@50500
   263
      (props, props, props) match {
wenzelm@50500
   264
        case (Position.Id(id), Markup.Serial(serial), Markup.Result(result)) =>
wenzelm@50500
   265
          Some((id, serial, result))
wenzelm@50500
   266
        case _ => None
wenzelm@50500
   267
      }
wenzelm@50500
   268
  }
wenzelm@50500
   269
wenzelm@50500
   270
  object Dialog
wenzelm@50500
   271
  {
wenzelm@52531
   272
    def unapply(tree: XML.Tree): Option[(Document_ID.Generic, Long, String)] =
wenzelm@50500
   273
      tree match {
wenzelm@50500
   274
        case XML.Elem(Markup(Markup.DIALOG, Dialog_Args(id, serial, result)), _) =>
wenzelm@50500
   275
          Some((id, serial, result))
wenzelm@50500
   276
        case _ => None
wenzelm@50500
   277
      }
wenzelm@50500
   278
  }
wenzelm@50500
   279
wenzelm@50500
   280
  object Dialog_Result
wenzelm@50500
   281
  {
wenzelm@52531
   282
    def apply(id: Document_ID.Generic, serial: Long, result: String): XML.Elem =
wenzelm@50501
   283
    {
wenzelm@50501
   284
      val props = Position.Id(id) ::: Markup.Serial(serial)
wenzelm@50501
   285
      XML.Elem(Markup(Markup.RESULT, props), List(XML.Text(result)))
wenzelm@50501
   286
    }
wenzelm@50500
   287
wenzelm@50501
   288
    def unapply(tree: XML.Tree): Option[String] =
wenzelm@50500
   289
      tree match {
wenzelm@50501
   290
        case XML.Elem(Markup(Markup.RESULT, _), List(XML.Text(result))) => Some(result)
wenzelm@50500
   291
        case _ => None
wenzelm@50500
   292
      }
wenzelm@50500
   293
  }
wenzelm@38412
   294
}
wenzelm@38412
   295
wenzelm@38412
   296
wenzelm@57916
   297
trait Protocol
wenzelm@38412
   298
{
wenzelm@57916
   299
  /* protocol commands */
wenzelm@57916
   300
wenzelm@57916
   301
  def protocol_command_bytes(name: String, args: Bytes*): Unit
wenzelm@57916
   302
  def protocol_command(name: String, args: String*): Unit
wenzelm@57916
   303
wenzelm@57916
   304
wenzelm@56387
   305
  /* options */
wenzelm@56387
   306
wenzelm@56387
   307
  def options(opts: Options): Unit =
wenzelm@65345
   308
    protocol_command("Prover.options", Symbol.encode_yxml(opts.encode))
wenzelm@56387
   309
wenzelm@56387
   310
wenzelm@65470
   311
  /* session base */
wenzelm@65470
   312
wenzelm@65470
   313
  private def encode_table(table: List[(String, String)]): String =
wenzelm@65470
   314
  {
wenzelm@65470
   315
    import XML.Encode._
wenzelm@65470
   316
    Symbol.encode_yxml(list(pair(string, string))(table))
wenzelm@65470
   317
  }
wenzelm@65470
   318
wenzelm@66712
   319
  private def encode_list(lst: List[String]): String =
wenzelm@66712
   320
  {
wenzelm@66712
   321
    import XML.Encode._
wenzelm@66712
   322
    Symbol.encode_yxml(list(string)(lst))
wenzelm@66712
   323
  }
wenzelm@66712
   324
wenzelm@66668
   325
  def session_base(resources: Resources)
wenzelm@66668
   326
  {
wenzelm@66668
   327
    val base = resources.session_base.standard_path
wenzelm@65470
   328
    protocol_command("Prover.session_base",
wenzelm@66668
   329
      encode_table(base.global_theories.toList),
wenzelm@66717
   330
      encode_list(base.loaded_theories.keys),
wenzelm@66668
   331
      encode_table(base.dest_known_theories))
wenzelm@66668
   332
  }
wenzelm@65470
   333
wenzelm@65470
   334
wenzelm@56387
   335
  /* interned items */
wenzelm@54519
   336
wenzelm@56335
   337
  def define_blob(digest: SHA1.Digest, bytes: Bytes): Unit =
wenzelm@56387
   338
    protocol_command_bytes("Document.define_blob", Bytes(digest.toString), bytes)
wenzelm@38412
   339
wenzelm@59671
   340
  def define_command(command: Command)
wenzelm@54519
   341
  {
wenzelm@54519
   342
    val blobs_yxml =
wenzelm@61376
   343
    {
wenzelm@61376
   344
      import XML.Encode._
wenzelm@54519
   345
      val encode_blob: T[Command.Blob] =
wenzelm@54519
   346
        variant(List(
wenzelm@54526
   347
          { case Exn.Res((a, b)) =>
wenzelm@65345
   348
              (Nil, pair(string, option(string))((a.node, b.map(p => p._1.toString)))) },
wenzelm@65345
   349
          { case Exn.Exn(e) => (Nil, string(Exn.message(e))) }))
wenzelm@59085
   350
wenzelm@65345
   351
      Symbol.encode_yxml(pair(list(encode_blob), int)(command.blobs, command.blobs_index))
wenzelm@54519
   352
    }
wenzelm@59085
   353
wenzelm@59085
   354
    val toks = command.span.content
wenzelm@59085
   355
    val toks_yxml =
wenzelm@61376
   356
    {
wenzelm@61376
   357
      import XML.Encode._
wenzelm@64616
   358
      val encode_tok: T[Token] = (tok => pair(int, int)((tok.kind.id, Symbol.length(tok.source))))
wenzelm@65345
   359
      Symbol.encode_yxml(list(encode_tok)(toks))
wenzelm@59085
   360
    }
wenzelm@59085
   361
wenzelm@52582
   362
    protocol_command("Document.define_command",
wenzelm@65345
   363
      (Document_ID(command.id) :: Symbol.encode(command.span.name) :: blobs_yxml :: toks_yxml ::
wenzelm@65345
   364
        toks.map(tok => Symbol.encode(tok.source))): _*)
wenzelm@54519
   365
  }
wenzelm@38412
   366
wenzelm@38412
   367
wenzelm@52931
   368
  /* execution */
wenzelm@52931
   369
wenzelm@66379
   370
  def consolidate_execution(): Unit =
wenzelm@66379
   371
    protocol_command("Document.consolidate_execution")
wenzelm@66379
   372
wenzelm@52931
   373
  def discontinue_execution(): Unit =
wenzelm@52931
   374
    protocol_command("Document.discontinue_execution")
wenzelm@38412
   375
wenzelm@52931
   376
  def cancel_exec(id: Document_ID.Exec): Unit =
wenzelm@52931
   377
    protocol_command("Document.cancel_exec", Document_ID(id))
wenzelm@52931
   378
wenzelm@52931
   379
wenzelm@52931
   380
  /* document versions */
wenzelm@47343
   381
wenzelm@52530
   382
  def update(old_id: Document_ID.Version, new_id: Document_ID.Version,
wenzelm@44383
   383
    edits: List[Document.Edit_Command])
wenzelm@38412
   384
  {
wenzelm@44157
   385
    val edits_yxml =
wenzelm@61376
   386
    {
wenzelm@61376
   387
      import XML.Encode._
wenzelm@44383
   388
      def id: T[Command] = (cmd => long(cmd.id))
wenzelm@46737
   389
      def encode_edit(name: Document.Node.Name)
wenzelm@52849
   390
          : T[Document.Node.Edit[Command.Edit, Command.Perspective]] =
wenzelm@44979
   391
        variant(List(
wenzelm@44979
   392
          { case Document.Node.Edits(a) => (Nil, list(pair(option(id), option(id)))(a)) },
wenzelm@48707
   393
          { case Document.Node.Deps(header) =>
wenzelm@60992
   394
              val master_dir = File.standard_url(name.master_dir)
wenzelm@59695
   395
              val imports = header.imports.map({ case (a, _) => a.node })
wenzelm@65384
   396
              val keywords =
wenzelm@65384
   397
                header.keywords.map({ case (a, Keyword.Spec(b, c, d)) => (a, ((b, c), d)) })
wenzelm@44979
   398
              (Nil,
wenzelm@65345
   399
                pair(string, pair(string, pair(list(string), pair(list(pair(string,
wenzelm@65345
   400
                    pair(pair(string, list(string)), list(string)))), list(string)))))(
wenzelm@65445
   401
                (master_dir, (name.theory, (imports, (keywords, header.errors)))))) },
wenzelm@52849
   402
          { case Document.Node.Perspective(a, b, c) =>
wenzelm@52849
   403
              (bool_atom(a) :: b.commands.map(cmd => long_atom(cmd.id)),
wenzelm@65345
   404
                list(pair(id, pair(string, list(string))))(c.dest)) }))
wenzelm@48705
   405
      def encode_edits: T[List[Document.Edit_Command]] = list((node_edit: Document.Edit_Command) =>
wenzelm@44979
   406
      {
wenzelm@44979
   407
        val (name, edit) = node_edit
wenzelm@65345
   408
        pair(string, encode_edit(name))(name.node, edit)
wenzelm@44979
   409
      })
wenzelm@65345
   410
      Symbol.encode_yxml(encode_edits(edits)) }
wenzelm@52582
   411
    protocol_command("Document.update", Document_ID(old_id), Document_ID(new_id), edits_yxml)
wenzelm@38412
   412
  }
wenzelm@43748
   413
wenzelm@44673
   414
  def remove_versions(versions: List[Document.Version])
wenzelm@44673
   415
  {
wenzelm@44673
   416
    val versions_yxml =
wenzelm@59364
   417
    { import XML.Encode._
wenzelm@65345
   418
      Symbol.encode_yxml(list(long)(versions.map(_.id))) }
wenzelm@52582
   419
    protocol_command("Document.remove_versions", versions_yxml)
wenzelm@44673
   420
  }
wenzelm@44673
   421
wenzelm@43748
   422
wenzelm@50498
   423
  /* dialog via document content */
wenzelm@50498
   424
wenzelm@52931
   425
  def dialog_result(serial: Long, result: String): Unit =
wenzelm@63805
   426
    protocol_command("Document.dialog_result", Value.Long(serial), result)
wenzelm@38412
   427
}