src/Pure/GUI/rich_text.scala
changeset 81433 c3793899b880
parent 81432 85fc3b482924
child 82229 ac7c09c6ff2f
--- a/src/Pure/GUI/rich_text.scala	Tue Nov 12 11:32:07 2024 +0100
+++ b/src/Pure/GUI/rich_text.scala	Tue Nov 12 22:30:45 2024 +0100
@@ -7,6 +7,8 @@
 package isabelle
 
 
+import java.lang.ref.WeakReference
+
 import javax.swing.JComponent
 
 import scala.collection.mutable
@@ -33,15 +35,16 @@
       Command.unparsed(text, id = id, results = results, markups = markups)
   }
 
-  def format(msgs: List[XML.Elem], margin: Double, metric: Font_Metric): List[Formatted] = {
+  def format(
+    msgs: List[XML.Elem],
+    margin: Double,
+    metric: Font_Metric,
+    cache: Cache = Cache.none
+  ): List[Formatted] = {
     val result = new mutable.ListBuffer[Formatted]
     for (msg <- msgs) {
       if (result.nonEmpty) result += Formatted(Document_ID.make(), Pretty.Separator)
-
-      val id = Protocol_Message.get_serial(msg)
-      val body = Pretty.formatted(List(msg), margin = margin, metric = metric)
-      result += Formatted(id, body)
-
+      result += cache.format(msg, margin, metric)
       Exn.Interrupt.expose()
     }
     result.toList
@@ -57,4 +60,44 @@
   def formatted_margin(metric: Font_Metric, formatted: List[Formatted]): Double =
     split_lines(formatted.map(_.text).mkString)
       .foldLeft(0.0) { case (m, line) => m max metric(line) }
+
+
+  /* cache */
+
+  object Cache {
+    def make(
+        compress: Compress.Cache = Compress.Cache.make(),
+        max_string: Int = isabelle.Cache.default_max_string,
+        initial_size: Int = isabelle.Cache.default_initial_size): Cache =
+      new Cache(compress, initial_size, max_string)
+
+    val none: Cache = make(max_string = 0)
+
+    sealed case class Args(msg: XML.Elem, margin: Double, metric: Font_Metric)
+  }
+
+  class Cache(compress: Compress.Cache, max_string: Int, initial_size: Int)
+  extends Term.Cache(compress, max_string, initial_size) {
+    cache =>
+
+    def format(msg: XML.Elem, margin: Double, metric: Font_Metric): Formatted = {
+      def run: Formatted =
+        Formatted(Protocol_Message.get_serial(msg),
+          cache.body(Pretty.formatted(List(msg), margin = margin, metric = metric)))
+
+      if (cache.table == null) run
+      else {
+        val x = Cache.Args(msg, margin, metric)
+        cache.table.get(x) match {
+          case ref: WeakReference[Any] => ref.get.asInstanceOf[Formatted]
+          case null =>
+            val y = run
+            cache.table.synchronized {
+              if (cache.table.get(x) == null) cache.table.put(x, new WeakReference[Any](y))
+            }
+            y
+        }
+      }
+    }
+  }
 }