access Isabelle theory exports via virtual file-system;
authorwenzelm
Fri, 11 Jan 2019 22:55:02 +0100
changeset 69637 f3b564a13236
parent 69636 dd1e0e1570d2
child 69638 938b28a99863
access Isabelle theory exports via virtual file-system;
src/Tools/jEdit/lib/Tools/jedit
src/Tools/jEdit/src/isabelle_export.scala
src/Tools/jEdit/src/services.xml
--- a/src/Tools/jEdit/lib/Tools/jedit	Fri Jan 11 22:35:41 2019 +0100
+++ b/src/Tools/jEdit/lib/Tools/jedit	Fri Jan 11 22:55:02 2019 +0100
@@ -35,6 +35,7 @@
   "src/info_dockable.scala"
   "src/isabelle.scala"
   "src/isabelle_encoding.scala"
+  "src/isabelle_export.scala"
   "src/isabelle_options.scala"
   "src/isabelle_sidekick.scala"
   "src/jedit_bibtex.scala"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Tools/jEdit/src/isabelle_export.scala	Fri Jan 11 22:55:02 2019 +0100
@@ -0,0 +1,117 @@
+/*  Title:      Tools/jEdit/src/isabelle_export.scala
+    Author:     Makarius
+
+Access Isabelle theory exports via virtual file-system.
+*/
+
+package isabelle.jedit
+
+
+import isabelle._
+
+import java.awt.Component
+import java.io.InputStream
+
+import org.gjt.sp.jedit.io.{VFS => JEditVFS, VFSFile, VFSManager}
+
+
+object Isabelle_Export
+{
+  val vfs_name = "isabelle-export"
+  val vfs_caps =
+    JEditVFS.READ_CAP |
+    JEditVFS.BROWSE_CAP |
+    JEditVFS.LOW_LATENCY_CAP |
+    JEditVFS.NON_AWT_SESSION_CAP
+
+  val vfs_prefix = vfs_name + ":"
+
+  def vfs_error(component: Component, path: String, prop: String, args: AnyRef*): Unit =
+    VFSManager.error(component, path, prop, args.toArray)
+
+  def vfs_file(path: String, is_dir: Boolean = false, size: Long = 0L): VFSFile =
+  {
+    val name = Export.explode_name(path).lastOption getOrElse ""
+    val url = vfs_prefix + path
+    new VFSFile(name, url, url, if (is_dir) VFSFile.DIRECTORY else VFSFile.FILE, size, false)
+  }
+
+  def explode_url(url: String, component: Component = null): Option[List[String]] =
+  {
+    def bad: None.type =
+    {
+      if (component != null) vfs_error(component, url, "ioerror.badurl", url)
+      None
+    }
+    Library.try_unprefix(vfs_prefix, url) match {
+      case Some(path) =>
+        val elems = Export.explode_name(path)
+        if (elems.exists(_.isEmpty)) bad else Some(elems)
+      case None => bad
+    }
+  }
+
+  class VFS extends JEditVFS(vfs_name, vfs_caps)
+  {
+    override def constructPath(parent: String, path: String): String =
+    {
+      if (parent == vfs_prefix || parent.endsWith(Export.sep)) parent + path
+      else parent + Export.sep + path
+    }
+
+    override def _listFiles(session: AnyRef, url: String, component: Component): Array[VFSFile] =
+    {
+      explode_url(url, component = component) match {
+        case None => null
+        case Some(elems) =>
+          val snapshot = PIDE.snapshot()
+          val version = snapshot.version
+          elems match {
+            case Nil =>
+              (for {
+                (node_name, _) <- version.nodes.iterator
+                if !snapshot.state.node_exports(version, node_name).is_empty
+              } yield vfs_file(node_name.theory, is_dir = true)).toArray
+            case theory :: prefix =>
+              val depth = prefix.length + 1
+              val entries: List[(String, Option[Long])] =
+                (for {
+                  (node_name, _) <- version.nodes.iterator if node_name.theory == theory
+                  exports = snapshot.state.node_exports(version, node_name)
+                  (_, entry) <- exports.iterator if entry.name_extends(prefix)
+                } yield {
+                  val is_dir = entry.name_elems.length > depth
+                  val path = Export.implode_name(theory :: entry.name_elems.take(depth))
+                  val file_size = if (is_dir) None else Some(entry.uncompressed().length.toLong)
+                  (path, file_size)
+                }).toSet.toList
+              (for ((path, file_size) <- entries.iterator) yield {
+                file_size match {
+                  case None => vfs_file(path, is_dir = true)
+                  case Some(size) => vfs_file(path, size = size)
+                }
+              }).toArray
+          }
+      }
+    }
+
+    override def _createInputStream(
+      session: AnyRef, url: String, ignore_errors: Boolean, component: Component): InputStream =
+    {
+      explode_url(url, component = if (ignore_errors) null else component) match {
+        case None | Some(Nil) => Bytes.empty.stream()
+        case Some(theory :: name_elems) =>
+          val snapshot = PIDE.snapshot()
+          val version = snapshot.version
+          val bytes =
+            (for {
+              (node_name, _) <- version.nodes.iterator
+              if node_name.theory == theory
+              (_, entry) <- snapshot.state.node_exports(version, node_name).iterator
+              if entry.name_elems == name_elems
+            } yield entry.uncompressed()).find(_ => true).getOrElse(Bytes.empty)
+          bytes.stream()
+      }
+    }
+  }
+}
--- a/src/Tools/jEdit/src/services.xml	Fri Jan 11 22:35:41 2019 +0100
+++ b/src/Tools/jEdit/src/services.xml	Fri Jan 11 22:55:02 2019 +0100
@@ -8,6 +8,9 @@
   <SERVICE CLASS="org.gjt.sp.jedit.gui.DynamicContextMenuService" NAME="Spell_Checker">
     new isabelle.jedit.Context_Menu();
   </SERVICE>
+  <SERVICE NAME="isabelle-export" CLASS="org.gjt.sp.jedit.io.VFS">
+    new isabelle.jedit.Isabelle_Export.VFS();
+  </SERVICE>
   <SERVICE NAME="isabelle" CLASS="sidekick.SideKickParser">
     new isabelle.jedit.Isabelle_Sidekick_Default();
   </SERVICE>