slightly more general support for one-shot query operations via asynchronous print functions and temporary document overlay;
authorwenzelm
Mon, 05 Aug 2013 17:14:02 +0200
changeset 5400202a7e7180ee5
parent 54001 c2da0d3b974d
child 54003 438f578ef1d9
slightly more general support for one-shot query operations via asynchronous print functions and temporary document overlay;
src/Pure/PIDE/query_operation.ML
src/Pure/ROOT
src/Pure/ROOT.ML
src/Pure/Tools/find_theorems.ML
src/Tools/jEdit/lib/Tools/jedit
src/Tools/jEdit/src/find_dockable.scala
src/Tools/jEdit/src/query_operation.scala
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/Pure/PIDE/query_operation.ML	Mon Aug 05 17:14:02 2013 +0200
     1.3 @@ -0,0 +1,30 @@
     1.4 +(*  Title:      Pure/PIDE/query_operation.ML
     1.5 +    Author:     Makarius
     1.6 +
     1.7 +One-shot query operations via asynchronous print functions and temporary
     1.8 +document overlay.
     1.9 +*)
    1.10 +
    1.11 +signature QUERY_OPERATION =
    1.12 +sig
    1.13 +  val register: string -> (Toplevel.state -> string list -> string) -> unit
    1.14 +end;
    1.15 +
    1.16 +structure Query_Operation: QUERY_OPERATION =
    1.17 +struct
    1.18 +
    1.19 +fun register name f =
    1.20 +  Command.print_function name
    1.21 +    (fn {args = instance :: args, ...} =>
    1.22 +        SOME {delay = NONE, pri = 0, persistent = false,
    1.23 +          print_fn = fn _ => fn state =>
    1.24 +            let
    1.25 +              val msg = XML.Elem ((Markup.writelnN, []), [XML.Text (f state args)])
    1.26 +                handle exn =>
    1.27 +                  if Exn.is_interrupt exn then reraise exn
    1.28 +                  else XML.Elem ((Markup.errorN, []), [XML.Text (ML_Compiler.exn_message exn)]);
    1.29 +            in Output.result [(Markup.instanceN, instance)] (YXML.string_of msg) end}
    1.30 +      | _ => NONE);
    1.31 +
    1.32 +end;
    1.33 +
     2.1 --- a/src/Pure/ROOT	Mon Aug 05 16:12:03 2013 +0200
     2.2 +++ b/src/Pure/ROOT	Mon Aug 05 17:14:02 2013 +0200
     2.3 @@ -158,6 +158,7 @@
     2.4      "PIDE/execution.ML"
     2.5      "PIDE/markup.ML"
     2.6      "PIDE/protocol.ML"
     2.7 +    "PIDE/query_operation.ML"
     2.8      "PIDE/xml.ML"
     2.9      "PIDE/yxml.ML"
    2.10      "Proof/extraction.ML"
     3.1 --- a/src/Pure/ROOT.ML	Mon Aug 05 16:12:03 2013 +0200
     3.2 +++ b/src/Pure/ROOT.ML	Mon Aug 05 17:14:02 2013 +0200
     3.3 @@ -269,6 +269,7 @@
     3.4  use "Thy/present.ML";
     3.5  use "PIDE/execution.ML";
     3.6  use "PIDE/command.ML";
     3.7 +use "PIDE/query_operation.ML";
     3.8  use "Thy/thy_load.ML";
     3.9  use "Thy/thy_info.ML";
    3.10  use "PIDE/document.ML";
     4.1 --- a/src/Pure/Tools/find_theorems.ML	Mon Aug 05 16:12:03 2013 +0200
     4.2 +++ b/src/Pure/Tools/find_theorems.ML	Mon Aug 05 17:14:02 2013 +0200
     4.3 @@ -605,7 +605,7 @@
     4.4  
     4.5  
     4.6  
     4.7 -(** command syntax **)
     4.8 +(** Isar command syntax **)
     4.9  
    4.10  local
    4.11  
    4.12 @@ -647,28 +647,10 @@
    4.13  
    4.14  
    4.15  
    4.16 -(** print function **)
    4.17 +(** PIDE query operation **)
    4.18  
    4.19 -val find_theoremsN = "find_theorems";
    4.20 -
    4.21 -val _ = Command.print_function find_theoremsN
    4.22 -  (fn {args, ...} =>
    4.23 -    (case args of
    4.24 -      [instance, query] =>
    4.25 -      SOME {delay = NONE, pri = 0, persistent = false,
    4.26 -        print_fn = fn _ => fn state =>
    4.27 -          let
    4.28 -            val msg =
    4.29 -              XML.Elem ((Markup.writelnN, []),
    4.30 -                [XML.Text
    4.31 -                  (Pretty.string_of (pretty_theorems_cmd state NONE false (parse_query query)))])
    4.32 -              handle exn =>
    4.33 -                if Exn.is_interrupt exn then reraise exn
    4.34 -                else XML.Elem ((Markup.errorN, []), [XML.Text (ML_Compiler.exn_message exn)]);
    4.35 -          in
    4.36 -            Output.result [(Markup.kindN, find_theoremsN), (Markup.instanceN, instance)]
    4.37 -              (YXML.string_of msg)
    4.38 -          end}
    4.39 -    | _ => NONE));
    4.40 +val _ =
    4.41 +  Query_Operation.register "find_theorems" (fn state => fn query =>
    4.42 +    Pretty.string_of (pretty_theorems_cmd state NONE false (maps parse_query query)));
    4.43  
    4.44  end;
     5.1 --- a/src/Tools/jEdit/lib/Tools/jedit	Mon Aug 05 16:12:03 2013 +0200
     5.2 +++ b/src/Tools/jEdit/lib/Tools/jedit	Mon Aug 05 17:14:02 2013 +0200
     5.3 @@ -35,6 +35,7 @@
     5.4    "src/pretty_text_area.scala"
     5.5    "src/pretty_tooltip.scala"
     5.6    "src/protocol_dockable.scala"
     5.7 +  "src/query_operation.scala"
     5.8    "src/raw_output_dockable.scala"
     5.9    "src/readme_dockable.scala"
    5.10    "src/rendering.scala"
     6.1 --- a/src/Tools/jEdit/src/find_dockable.scala	Mon Aug 05 16:12:03 2013 +0200
     6.2 +++ b/src/Tools/jEdit/src/find_dockable.scala	Mon Aug 05 17:14:02 2013 +0200
     6.3 @@ -25,27 +25,22 @@
     6.4  {
     6.5    Swing_Thread.require()
     6.6  
     6.7 -
     6.8 -  /* component state -- owned by Swing thread */
     6.9 -
    6.10 -  private val FIND_THEOREMS = "find_theorems"
    6.11 -  private val instance = Document_ID.make().toString
    6.12 -
    6.13 -  private var zoom_factor = 100
    6.14 -  private var current_location: Option[Command] = None
    6.15 -  private var current_query: String = ""
    6.16 -  private var current_result = false
    6.17 -  private var current_snapshot = Document.State.init.snapshot()
    6.18 -  private var current_state = Command.empty.init_state
    6.19 -  private var current_output: List[XML.Tree] = Nil
    6.20 -
    6.21 -
    6.22 -  /* pretty text area */
    6.23 -
    6.24    val pretty_text_area = new Pretty_Text_Area(view)
    6.25    set_content(pretty_text_area)
    6.26  
    6.27  
    6.28 +  /* query operation */
    6.29 +
    6.30 +  private val find_theorems =
    6.31 +    Query_Operation(view, "find_theorems",
    6.32 +      (snapshot, results, body) =>
    6.33 +        pretty_text_area.update(snapshot, results, Pretty.separate(body)))
    6.34 +
    6.35 +
    6.36 +  /* resize */
    6.37 +
    6.38 +  private var zoom_factor = 100
    6.39 +
    6.40    private def handle_resize()
    6.41    {
    6.42      Swing_Thread.require()
    6.43 @@ -54,98 +49,12 @@
    6.44        (Rendering.font_size("jedit_font_scale") * zoom_factor / 100).round)
    6.45    }
    6.46  
    6.47 -  private def remove_overlay()
    6.48 -  {
    6.49 -    Swing_Thread.require()
    6.50 +  private val delay_resize =
    6.51 +    Swing_Thread.delay_first(PIDE.options.seconds("editor_update_delay")) { handle_resize() }
    6.52  
    6.53 -    for {
    6.54 -      command <- current_location
    6.55 -      buffer <- JEdit_Lib.jedit_buffer(command.node_name.node)
    6.56 -      model <- PIDE.document_model(buffer)
    6.57 -    } model.remove_overlay(command, FIND_THEOREMS, List(instance, current_query))
    6.58 -  }
    6.59 -
    6.60 -  private def handle_update()
    6.61 -  {
    6.62 -    Swing_Thread.require()
    6.63 -
    6.64 -    val (new_snapshot, new_state) =
    6.65 -      Document_View(view.getTextArea) match {
    6.66 -        case Some(doc_view) =>
    6.67 -          val snapshot = doc_view.model.snapshot()
    6.68 -          if (!snapshot.is_outdated) {
    6.69 -            current_location match {
    6.70 -              case Some(cmd) =>
    6.71 -                (snapshot, snapshot.state.command_state(snapshot.version, cmd))
    6.72 -              case None =>
    6.73 -                (Document.State.init.snapshot(), Command.empty.init_state)
    6.74 -            }
    6.75 -          }
    6.76 -          else (current_snapshot, current_state)
    6.77 -        case None => (current_snapshot, current_state)
    6.78 -      }
    6.79 -
    6.80 -    val new_output =
    6.81 -      (for {
    6.82 -        (_, XML.Elem(Markup(Markup.RESULT, props), List(XML.Elem(markup, body)))) <-
    6.83 -          new_state.results.entries
    6.84 -        if props.contains((Markup.KIND, FIND_THEOREMS))
    6.85 -        if props.contains((Markup.INSTANCE, instance))
    6.86 -      } yield XML.Elem(Markup(Markup.message(markup.name), markup.properties), body)).toList
    6.87 -
    6.88 -    if (new_output != current_output)
    6.89 -      pretty_text_area.update(new_snapshot, new_state.results, Pretty.separate(new_output))
    6.90 -
    6.91 -    if (!new_output.isEmpty) {
    6.92 -      current_result = true
    6.93 -      remove_overlay()
    6.94 -      PIDE.flush_buffers()
    6.95 -    }
    6.96 -
    6.97 -    current_snapshot = new_snapshot
    6.98 -    current_state = new_state
    6.99 -    current_output = new_output
   6.100 -  }
   6.101 -
   6.102 -  private def apply_query(query: String)
   6.103 -  {
   6.104 -    Swing_Thread.require()
   6.105 -
   6.106 -    Document_View(view.getTextArea) match {
   6.107 -      case Some(doc_view) =>
   6.108 -        val snapshot = doc_view.model.snapshot()
   6.109 -        remove_overlay()
   6.110 -        current_location = None
   6.111 -        current_query = ""
   6.112 -        current_result = false
   6.113 -        snapshot.node.command_at(doc_view.text_area.getCaretPosition).map(_._1) match {
   6.114 -          case Some(command) =>
   6.115 -            current_location = Some(command)
   6.116 -            current_query = query
   6.117 -            doc_view.model.insert_overlay(command, FIND_THEOREMS, List(instance, query))
   6.118 -          case None =>
   6.119 -        }
   6.120 -        PIDE.flush_buffers()
   6.121 -      case None =>
   6.122 -    }
   6.123 -  }
   6.124 -
   6.125 -  private def locate_query()
   6.126 -  {
   6.127 -    Swing_Thread.require()
   6.128 -
   6.129 -    current_location match {
   6.130 -      case Some(command) =>
   6.131 -        val snapshot = PIDE.session.snapshot(command.node_name)
   6.132 -        val commands = snapshot.node.commands
   6.133 -        if (commands.contains(command)) {
   6.134 -          val sources = commands.iterator.takeWhile(_ != command).map(_.source)
   6.135 -          val (line, column) = ((1, 1) /: sources)(Symbol.advance_line_column)
   6.136 -          Hyperlink(command.node_name.node, line, column).follow(view)
   6.137 -        }
   6.138 -      case None =>
   6.139 -    }
   6.140 -  }
   6.141 +  addComponentListener(new ComponentAdapter {
   6.142 +    override def componentResized(e: ComponentEvent) { delay_resize.invoke() }
   6.143 +  })
   6.144  
   6.145  
   6.146    /* main actor */
   6.147 @@ -155,12 +64,6 @@
   6.148        react {
   6.149          case _: Session.Global_Options =>
   6.150            Swing_Thread.later { handle_resize() }
   6.151 -        case changed: Session.Commands_Changed =>
   6.152 -          current_location match {
   6.153 -            case Some(command) if !current_result && changed.commands.contains(command) =>
   6.154 -              Swing_Thread.later { handle_update() }
   6.155 -            case _ =>
   6.156 -          }
   6.157          case bad =>
   6.158            java.lang.System.err.println("Find_Dockable: ignoring bad message " + bad)
   6.159        }
   6.160 @@ -172,30 +75,20 @@
   6.161      Swing_Thread.require()
   6.162  
   6.163      PIDE.session.global_options += main_actor
   6.164 -    PIDE.session.commands_changed += main_actor
   6.165      handle_resize()
   6.166 +    find_theorems.activate()
   6.167    }
   6.168  
   6.169    override def exit()
   6.170    {
   6.171      Swing_Thread.require()
   6.172  
   6.173 +    find_theorems.deactivate()
   6.174      PIDE.session.global_options -= main_actor
   6.175 -    PIDE.session.commands_changed -= main_actor
   6.176      delay_resize.revoke()
   6.177    }
   6.178  
   6.179  
   6.180 -  /* resize */
   6.181 -
   6.182 -  private val delay_resize =
   6.183 -    Swing_Thread.delay_first(PIDE.options.seconds("editor_update_delay")) { handle_resize() }
   6.184 -
   6.185 -  addComponentListener(new ComponentAdapter {
   6.186 -    override def componentResized(e: ComponentEvent) { delay_resize.invoke() }
   6.187 -  })
   6.188 -
   6.189 -
   6.190    /* controls */
   6.191  
   6.192    private val query = new HistoryTextArea("isabelle-find-theorems") {
   6.193 @@ -208,12 +101,12 @@
   6.194  
   6.195    private val find = new Button("Find") {
   6.196      tooltip = "Find theorems meeting specified criteria"
   6.197 -    reactions += { case ButtonClicked(_) => apply_query(query.getText) }
   6.198 +    reactions += { case ButtonClicked(_) => find_theorems.apply_query(List(query.getText)) }
   6.199    }
   6.200  
   6.201    private val locate = new Button("Locate") {
   6.202      tooltip = "Locate context of current query within source text"
   6.203 -    reactions += { case ButtonClicked(_) => locate_query() }
   6.204 +    reactions += { case ButtonClicked(_) => find_theorems.locate_query() }
   6.205    }
   6.206  
   6.207    private val zoom = new GUI.Zoom_Box(factor => { zoom_factor = factor; handle_resize() }) {
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/src/Tools/jEdit/src/query_operation.scala	Mon Aug 05 17:14:02 2013 +0200
     7.3 @@ -0,0 +1,153 @@
     7.4 +/*  Title:      Tools/jEdit/src/query_operation.scala
     7.5 +    Author:     Makarius
     7.6 +
     7.7 +One-shot query operations via asynchronous print functions and temporary
     7.8 +document overlay.
     7.9 +*/
    7.10 +
    7.11 +package isabelle.jedit
    7.12 +
    7.13 +
    7.14 +import isabelle._
    7.15 +
    7.16 +import scala.actors.Actor._
    7.17 +
    7.18 +import org.gjt.sp.jedit.View
    7.19 +
    7.20 +
    7.21 +object Query_Operation
    7.22 +{
    7.23 +  def apply(
    7.24 +      view: View,
    7.25 +      name: String,
    7.26 +      consume: (Document.Snapshot, Command.Results, XML.Body) => Unit) =
    7.27 +    new Query_Operation(view, name, consume)
    7.28 +}
    7.29 +
    7.30 +final class Query_Operation private(
    7.31 +  view: View,
    7.32 +  operation_name: String,
    7.33 +  consume_result: (Document.Snapshot, Command.Results, XML.Body) => Unit)
    7.34 +{
    7.35 +  private val instance = Document_ID.make().toString
    7.36 +
    7.37 +
    7.38 +  /* implicit state -- owned by Swing thread */
    7.39 +
    7.40 +  private var current_location: Option[Command] = None
    7.41 +  private var current_query: List[String] = Nil
    7.42 +  private var current_result = false
    7.43 +  private var current_snapshot = Document.State.init.snapshot()
    7.44 +  private var current_state = Command.empty.init_state
    7.45 +  private var current_output: List[XML.Tree] = Nil
    7.46 +
    7.47 +  private def remove_overlay()
    7.48 +  {
    7.49 +    Swing_Thread.require()
    7.50 +
    7.51 +    for {
    7.52 +      command <- current_location
    7.53 +      buffer <- JEdit_Lib.jedit_buffer(command.node_name.node)
    7.54 +      model <- PIDE.document_model(buffer)
    7.55 +    } model.remove_overlay(command, operation_name, instance :: current_query)
    7.56 +  }
    7.57 +
    7.58 +  private def handle_result()
    7.59 +  {
    7.60 +    Swing_Thread.require()
    7.61 +
    7.62 +    val (new_snapshot, new_state) =
    7.63 +      Document_View(view.getTextArea) match {
    7.64 +        case Some(doc_view) =>
    7.65 +          val snapshot = doc_view.model.snapshot()
    7.66 +          current_location match {
    7.67 +            case Some(cmd) =>
    7.68 +              (snapshot, snapshot.state.command_state(snapshot.version, cmd))
    7.69 +            case None =>
    7.70 +              (Document.State.init.snapshot(), Command.empty.init_state)
    7.71 +          }
    7.72 +        case None => (current_snapshot, current_state)
    7.73 +      }
    7.74 +
    7.75 +    val new_output =
    7.76 +      (for {
    7.77 +        (_, XML.Elem(Markup(Markup.RESULT, props), List(XML.Elem(markup, body)))) <-
    7.78 +          new_state.results.entries
    7.79 +        if props.contains((Markup.INSTANCE, instance))
    7.80 +      } yield XML.Elem(Markup(Markup.message(markup.name), markup.properties), body)).toList
    7.81 +
    7.82 +    if (new_output != current_output)
    7.83 +      consume_result(new_snapshot, new_state.results, new_output)
    7.84 +
    7.85 +    if (!new_output.isEmpty) {
    7.86 +      current_result = true
    7.87 +      remove_overlay()
    7.88 +      PIDE.flush_buffers()
    7.89 +    }
    7.90 +
    7.91 +    current_snapshot = new_snapshot
    7.92 +    current_state = new_state
    7.93 +    current_output = new_output
    7.94 +  }
    7.95 +
    7.96 +  def apply_query(query: List[String])
    7.97 +  {
    7.98 +    Swing_Thread.require()
    7.99 +
   7.100 +    Document_View(view.getTextArea) match {
   7.101 +      case Some(doc_view) =>
   7.102 +        val snapshot = doc_view.model.snapshot()
   7.103 +        remove_overlay()
   7.104 +        current_location = None
   7.105 +        current_query = Nil
   7.106 +        current_result = false
   7.107 +        snapshot.node.command_at(doc_view.text_area.getCaretPosition).map(_._1) match {
   7.108 +          case Some(command) =>
   7.109 +            current_location = Some(command)
   7.110 +            current_query = query
   7.111 +            doc_view.model.insert_overlay(command, operation_name, instance :: query)
   7.112 +          case None =>
   7.113 +        }
   7.114 +        PIDE.flush_buffers()
   7.115 +      case None =>
   7.116 +    }
   7.117 +  }
   7.118 +
   7.119 +  def locate_query()
   7.120 +  {
   7.121 +    Swing_Thread.require()
   7.122 +
   7.123 +    current_location match {
   7.124 +      case Some(command) =>
   7.125 +        val snapshot = PIDE.session.snapshot(command.node_name)
   7.126 +        val commands = snapshot.node.commands
   7.127 +        if (commands.contains(command)) {
   7.128 +          val sources = commands.iterator.takeWhile(_ != command).map(_.source)
   7.129 +          val (line, column) = ((1, 1) /: sources)(Symbol.advance_line_column)
   7.130 +          Hyperlink(command.node_name.node, line, column).follow(view)
   7.131 +        }
   7.132 +      case None =>
   7.133 +    }
   7.134 +  }
   7.135 +
   7.136 +
   7.137 +  /* main actor */
   7.138 +
   7.139 +  private val main_actor = actor {
   7.140 +    loop {
   7.141 +      react {
   7.142 +        case changed: Session.Commands_Changed =>
   7.143 +          current_location match {
   7.144 +            case Some(command) if !current_result && changed.commands.contains(command) =>
   7.145 +              Swing_Thread.later { handle_result() }
   7.146 +            case _ =>
   7.147 +          }
   7.148 +        case bad =>
   7.149 +          java.lang.System.err.println("Query_Operation: ignoring bad message " + bad)
   7.150 +      }
   7.151 +    }
   7.152 +  }
   7.153 +
   7.154 +  def activate() { PIDE.session.commands_changed += main_actor }
   7.155 +  def deactivate() { PIDE.session.commands_changed -= main_actor }
   7.156 +}