more detailed Command_Timings: count actual commands from theory body individually;
authorwenzelm
Mon, 22 Sep 2025 14:01:37 +0200
changeset 83210 9cc5d77d505c
parent 83209 a39fde2f020a
child 83211 1d189ef55adf
more detailed Command_Timings: count actual commands from theory body individually;
src/Pure/PIDE/command.scala
src/Pure/PIDE/document.scala
src/Pure/PIDE/document_status.scala
src/Pure/PIDE/rendering.scala
--- a/src/Pure/PIDE/command.scala	Sun Sep 21 23:48:59 2025 +0200
+++ b/src/Pure/PIDE/command.scala	Mon Sep 22 14:01:37 2025 +0200
@@ -243,7 +243,7 @@
     def consolidating: Boolean = document_status.consolidating
     def consolidated: Boolean = document_status.consolidated
     def maybe_consolidated: Boolean = document_status.maybe_consolidated
-    def timing: Timing = document_status.timing
+    def timings: Document_Status.Command_Timings = document_status.timings
 
     def markup(index: Markup_Index): Markup_Tree = markups(index)
 
--- a/src/Pure/PIDE/document.scala	Sun Sep 21 23:48:59 2025 +0200
+++ b/src/Pure/PIDE/document.scala	Mon Sep 22 14:01:37 2025 +0200
@@ -1273,9 +1273,6 @@
       Document_Status.Command_Status.merge(
         command_states(version, command).iterator.map(_.document_status))
 
-    def command_timing(version: Version, command: Command): Timing =
-      Timing.merge(command_states(version, command).iterator.map(_.timing))
-
     def command_results(version: Version, command: Command): Command.Results =
       Command.State.merge_results(command_states(version, command))
 
--- a/src/Pure/PIDE/document_status.scala	Sun Sep 21 23:48:59 2025 +0200
+++ b/src/Pure/PIDE/document_status.scala	Mon Sep 22 14:01:37 2025 +0200
@@ -7,6 +7,9 @@
 package isabelle
 
 
+import scala.collection.immutable.SortedMap
+
+
 object Document_Status {
   /* theory status: via 'theory' or 'end' commands */
 
@@ -30,6 +33,48 @@
   }
 
 
+  /* command timings: for pro-forma command with actual commands at offset */
+
+  object Command_Timings {
+    type Entry = (Symbol.Offset, Timing)
+    val empty: Command_Timings =
+      new Command_Timings(SortedMap.empty, Timing.zero)
+    def make(args: IterableOnce[Entry]): Command_Timings =
+      args.iterator.foldLeft(empty)(_ + _)
+    def merge(args: IterableOnce[Command_Timings]): Command_Timings =
+      args.iterator.foldLeft(empty)(_ ++ _)
+  }
+
+  final class Command_Timings private(
+    private val rep: SortedMap[Symbol.Offset, Timing],
+    val sum: Timing
+  ) {
+    def is_empty: Boolean = rep.isEmpty
+    def count: Int = rep.size
+    def apply(offset: Symbol.Offset): Timing = rep.getOrElse(offset, Timing.zero)
+    def iterator: Iterator[(Symbol.Offset, Timing)] = rep.iterator
+
+    def + (entry: Command_Timings.Entry): Command_Timings = {
+      val (offset, timing) = entry
+      val rep1 = rep + (offset -> (apply(offset) + timing))
+      val sum1 = sum + timing
+      new Command_Timings(rep1, sum1)
+    }
+
+    def ++ (other: Command_Timings): Command_Timings =
+      if (rep.isEmpty) other
+      else other.rep.foldLeft(this)(_ + _)
+
+    override def hashCode: Int = rep.hashCode
+    override def equals(that: Any): Boolean =
+      that match {
+        case other: Command_Timings => rep == other.rep
+        case _ => false
+      }
+    override def toString: String = rep.mkString("Command_Timings(", ", ", ")")
+  }
+
+
   /* command status */
 
   object Command_Status {
@@ -53,7 +98,7 @@
       var canceled = false
       var forks = 0
       var runs = 0
-      var timing = Timing.zero
+      var timings = Command_Timings.empty
       for (markup <- markups) {
         markup.name match {
           case Markup.INITIALIZED =>
@@ -72,7 +117,11 @@
           case Markup.WARNING | Markup.LEGACY => warned1 = true
           case Markup.FAILED | Markup.ERROR => failed1 = true
           case Markup.CANCELED => canceled = true
-          case Markup.TIMING => timing += Markup.Timing_Properties.get(markup.properties)
+          case Markup.TIMING =>
+            val props = markup.properties
+            val offset = Position.Offset.get(props)
+            val timing = Markup.Timing_Properties.get(props)
+            timings += (offset -> timing)
           case _ =>
         }
       }
@@ -85,7 +134,7 @@
         canceled = canceled,
         forks = forks,
         runs = runs,
-        timing = timing)
+        timings = timings)
     }
 
     val empty: Command_Status = make()
@@ -103,7 +152,7 @@
     private val canceled: Boolean,
     val forks: Int,
     val runs: Int,
-    val timing: Timing
+    val timings: Command_Timings
   ) extends Theory_Status {
     override def toString: String =
       if (is_empty) "Command_Status.empty"
@@ -114,7 +163,7 @@
     def is_empty: Boolean =
       !Theory_Status.initialized(theory_status) &&
       !touched && !accepted && !warned && !failed && !canceled &&
-      forks == 0 && runs == 0 && timing.is_zero
+      forks == 0 && runs == 0 && timings.is_empty
 
     def + (that: Command_Status): Command_Status =
       if (is_empty) that
@@ -129,7 +178,7 @@
           canceled = canceled || that.canceled,
           forks = forks + that.forks,
           runs = runs + that.runs,
-          timing = timing + that.timing)
+          timings = timings ++ that.timings)
       }
 
     def update(
@@ -151,7 +200,7 @@
             canceled = canceled,
             forks = forks,
             runs = runs,
-            timing = timing)
+            timings = timings)
         }
       }
       else this + Command_Status.make(markups = markups, warned = warned, failed = failed)
@@ -204,7 +253,7 @@
         if (status.is_canceled) canceled = true
         if (!status.is_terminated) terminated = false
 
-        val t = state.command_timing(version, command).elapsed
+        val t = status.timings.sum.elapsed
         total_time += t
         if (t > max_time) max_time = t
         if (t.is_notable(threshold)) command_timings += (command -> t)
--- a/src/Pure/PIDE/rendering.scala	Sun Sep 21 23:48:59 2025 +0200
+++ b/src/Pure/PIDE/rendering.scala	Mon Sep 22 14:01:37 2025 +0200
@@ -667,9 +667,9 @@
             val info1 = info.add_info_text(r0, txt, ord = 2)
             val info2 =
               if (kind == Markup.COMMAND) {
-                val timing = Timing.merge(command_states.iterator.map(_.timing))
-                if (timing.is_notable(timing_threshold)) {
-                  info1.add_info(r0, Pretty.string(timing.message))
+                val t = Document_Status.Command_Timings.merge(command_states.map(_.timings)).sum
+                if (t.is_notable(timing_threshold)) {
+                  info1.add_info(r0, Pretty.string(t.message))
                 }
                 else info1
               }