src/Pure/PIDE/document.scala
changeset 72821 13275ae9e209
parent 72820 af1bd8f2760f
child 72822 8d166825265e
equal deleted inserted replaced
72820:af1bd8f2760f 72821:13275ae9e209
   536   object Snapshot
   536   object Snapshot
   537   {
   537   {
   538     val init: Snapshot = State.init.snapshot()
   538     val init: Snapshot = State.init.snapshot()
   539   }
   539   }
   540 
   540 
   541   abstract class Snapshot private[Document](
   541   class Snapshot private[Document](
   542     val state: State,
   542     val state: State,
   543     val version: Version,
   543     val version: Version,
   544     val node_name: Node.Name,
   544     val node_name: Node.Name,
   545     edits: List[Text.Edit],
   545     edits: List[Text.Edit],
   546     snippet_command: Option[Command])
   546     snippet_command: Option[Command])
   547   {
   547   {
       
   548     override def toString: String =
       
   549       "Snapshot(node = " + node_name.node + ", version = " + version.id +
       
   550         (if (is_outdated) ", outdated" else "") + ")"
       
   551 
       
   552 
   548     /* edits */
   553     /* edits */
   549 
   554 
   550     def is_outdated: Boolean = edits.nonEmpty
   555     def is_outdated: Boolean = edits.nonEmpty
   551 
   556 
   552     private lazy val reverse_edits = edits.reverse
   557     private lazy val reverse_edits = edits.reverse
   558 
   563 
   559     def convert(range: Text.Range): Text.Range = range.map(convert)
   564     def convert(range: Text.Range): Text.Range = range.map(convert)
   560     def revert(range: Text.Range): Text.Range = range.map(revert)
   565     def revert(range: Text.Range): Text.Range = range.map(revert)
   561 
   566 
   562 
   567 
   563     def node: Node
   568     /* local node content */
   564     def nodes: List[(Node.Name, Node)]
   569 
   565 
   570     val node: Node = version.nodes(node_name)
   566     def commands_loading: List[Command]
   571 
   567     def commands_loading_ranges(pred: Node.Name => Boolean): List[Text.Range]
   572     def nodes: List[(Node.Name, Node)] =
   568     def current_command(other_node_name: Node.Name, offset: Text.Offset): Option[Command]
   573       (node_name :: node.load_commands.flatMap(_.blobs_names)).
       
   574         map(name => (name, version.nodes(name)))
   569 
   575 
   570     def node_files: List[Node.Name] =
   576     def node_files: List[Node.Name] =
   571       snippet_command match {
   577       snippet_command match {
   572         case None => List(node_name)
   578         case None => List(node_name)
   573         case Some(command) => node_name :: command.blobs_names
   579         case Some(command) => node_name :: command.blobs_names
   574       }
   580       }
       
   581 
       
   582 
       
   583     /* theory load commands */
       
   584 
       
   585     val commands_loading: List[Command] =
       
   586       if (node_name.is_theory) Nil
       
   587       else version.nodes.commands_loading(node_name)
       
   588 
       
   589     def commands_loading_ranges(pred: Node.Name => Boolean): List[Text.Range] =
       
   590       (for {
       
   591         cmd <- node.load_commands.iterator
       
   592         blob_name <- cmd.blobs_names.iterator
       
   593         if pred(blob_name)
       
   594         start <- node.command_start(cmd)
       
   595       } yield convert(cmd.core_range + start)).toList
       
   596 
       
   597 
       
   598     /* command as add-on snippet */
   575 
   599 
   576     def command_snippet(command: Command): Snapshot =
   600     def command_snippet(command: Command): Snapshot =
   577     {
   601     {
   578       val node_name = command.node_name
   602       val node_name = command.node_name
   579 
   603 
   591           .assign(version1.id, Nil, List(command.id -> List(Document_ID.make())))._2
   615           .assign(version1.id, Nil, List(command.id -> List(Document_ID.make())))._2
   592 
   616 
   593       state1.snapshot(node_name = node_name, snippet_command = Some(command))
   617       state1.snapshot(node_name = node_name, snippet_command = Some(command))
   594     }
   618     }
   595 
   619 
       
   620 
       
   621     /* XML markup */
       
   622 
   596     def xml_markup(
   623     def xml_markup(
   597       range: Text.Range = Text.Range.full,
   624         range: Text.Range = Text.Range.full,
   598       elements: Markup.Elements = Markup.Elements.full): XML.Body
   625         elements: Markup.Elements = Markup.Elements.full): XML.Body =
       
   626       state.xml_markup(version, node_name, range = range, elements = elements)
   599 
   627 
   600     def xml_markup_blobs(elements: Markup.Elements = Markup.Elements.full): List[XML.Body] =
   628     def xml_markup_blobs(elements: Markup.Elements = Markup.Elements.full): List[XML.Body] =
   601     {
   629     {
   602       snippet_command match {
   630       snippet_command match {
   603         case None => Nil
   631         case None => Nil
   609             markup.to_XML(Text.Range(0, text.length), text, elements)
   637             markup.to_XML(Text.Range(0, text.length), text, elements)
   610           }
   638           }
   611       }
   639       }
   612     }
   640     }
   613 
   641 
   614     def messages: List[(XML.Tree, Position.T)]
   642 
   615     def exports: List[Export.Entry]
   643     /* messages */
   616     def exports_map: Map[String, Export.Entry]
   644 
   617 
   645     lazy val messages: List[(XML.Tree, Position.T)] =
   618     def find_command(id: Document_ID.Generic): Option[(Node, Command)]
   646       (for {
       
   647         (command, start) <-
       
   648           Document.Node.Commands.starts_pos(
       
   649             node.commands.iterator, Token.Pos.file(node_name.node))
       
   650         pos = command.span.keyword_pos(start).position(command.span.name)
       
   651         (_, tree) <- state.command_results(version, command).iterator
       
   652        } yield (tree, pos)).toList
       
   653 
       
   654 
       
   655     /* exports */
       
   656 
       
   657     lazy val exports: List[Export.Entry] =
       
   658       state.node_exports(version, node_name).iterator.map(_._2).toList
       
   659 
       
   660     lazy val exports_map: Map[String, Export.Entry] =
       
   661       (for (entry <- exports.iterator) yield (entry.name, entry)).toMap
       
   662 
       
   663 
       
   664     /* find command */
       
   665 
       
   666     def find_command(id: Document_ID.Generic): Option[(Node, Command)] =
       
   667       state.lookup_id(id) match {
       
   668         case None => None
       
   669         case Some(st) =>
       
   670           val command = st.command
       
   671           val node = version.nodes(command.node_name)
       
   672           if (node.commands.contains(command)) Some((node, command)) else None
       
   673       }
       
   674 
   619     def find_command_position(id: Document_ID.Generic, offset: Symbol.Offset)
   675     def find_command_position(id: Document_ID.Generic, offset: Symbol.Offset)
   620       : Option[Line.Node_Position]
   676         : Option[Line.Node_Position] =
       
   677       for ((node, command) <- find_command(id))
       
   678       yield {
       
   679         val name = command.node_name.node
       
   680         val sources_iterator =
       
   681           node.commands.iterator.takeWhile(_ != command).map(_.source) ++
       
   682             (if (offset == 0) Iterator.empty
       
   683              else Iterator.single(command.source(Text.Range(0, command.chunk.decode(offset)))))
       
   684         val pos = (Line.Position.zero /: sources_iterator)(_.advance(_))
       
   685         Line.Node_Position(name, pos)
       
   686       }
       
   687 
       
   688     def current_command(other_node_name: Node.Name, offset: Text.Offset): Option[Command] =
       
   689       if (other_node_name.is_theory) {
       
   690         val other_node = version.nodes(other_node_name)
       
   691         val iterator = other_node.command_iterator(revert(offset) max 0)
       
   692         if (iterator.hasNext) {
       
   693           val (command0, _) = iterator.next
       
   694           other_node.commands.reverse.iterator(command0).find(command => !command.is_ignored)
       
   695         }
       
   696         else other_node.commands.reverse.iterator.find(command => !command.is_ignored)
       
   697       }
       
   698       else version.nodes.commands_loading(other_node_name).headOption
       
   699 
       
   700 
       
   701     /* command results */
       
   702 
       
   703     def command_results(range: Text.Range): Command.Results =
       
   704       Command.State.merge_results(
       
   705         select[List[Command.State]](range, Markup.Elements.full, command_states =>
       
   706           { case _ => Some(command_states) }).flatMap(_.info))
       
   707 
       
   708     def command_results(command: Command): Command.Results =
       
   709       state.command_results(version, command)
       
   710 
       
   711 
       
   712     /* command ids: static and dynamic */
       
   713 
       
   714     def command_id_map: Map[Document_ID.Generic, Command] =
       
   715       state.command_id_map(version, version.nodes(node_name).commands)
       
   716 
       
   717 
       
   718     /* cumulate markup */
   621 
   719 
   622     def cumulate[A](
   720     def cumulate[A](
   623       range: Text.Range,
   721       range: Text.Range,
   624       info: A,
   722       info: A,
   625       elements: Markup.Elements,
   723       elements: Markup.Elements,
   626       result: List[Command.State] => (A, Text.Markup) => Option[A],
   724       result: List[Command.State] => (A, Text.Markup) => Option[A],
   627       status: Boolean = false): List[Text.Info[A]]
   725       status: Boolean = false): List[Text.Info[A]] =
       
   726     {
       
   727       val former_range = revert(range).inflate_singularity
       
   728       val (chunk_name, command_iterator) =
       
   729         commands_loading.headOption match {
       
   730           case None => (Symbol.Text_Chunk.Default, node.command_iterator(former_range))
       
   731           case Some(command) => (Symbol.Text_Chunk.File(node_name.node), Iterator((command, 0)))
       
   732         }
       
   733       val markup_index = Command.Markup_Index(status, chunk_name)
       
   734       (for {
       
   735         (command, command_start) <- command_iterator
       
   736         chunk <- command.chunks.get(chunk_name).iterator
       
   737         states = state.command_states(version, command)
       
   738         res = result(states)
       
   739         markup_range <- (former_range - command_start).try_restrict(chunk.range).iterator
       
   740         markup = Command.State.merge_markup(states, markup_index, markup_range, elements)
       
   741         Text.Info(r0, a) <- markup.cumulate[A](markup_range, info, elements,
       
   742           {
       
   743             case (a, Text.Info(r0, b)) => res(a, Text.Info(convert(r0 + command_start), b))
       
   744           }).iterator
       
   745         r1 <- convert(r0 + command_start).try_restrict(range).iterator
       
   746       } yield Text.Info(r1, a)).toList
       
   747     }
   628 
   748 
   629     def select[A](
   749     def select[A](
   630       range: Text.Range,
   750       range: Text.Range,
   631       elements: Markup.Elements,
   751       elements: Markup.Elements,
   632       result: List[Command.State] => Text.Markup => Option[A],
   752       result: List[Command.State] => Text.Markup => Option[A],
   633       status: Boolean = false): List[Text.Info[A]]
   753       status: Boolean = false): List[Text.Info[A]] =
   634 
   754     {
   635     def command_results(range: Text.Range): Command.Results
   755       def result1(states: List[Command.State]): (Option[A], Text.Markup) => Option[Option[A]] =
   636     def command_results(command: Command): Command.Results
   756       {
   637 
   757         val res = result(states)
   638     def command_id_map: Map[Document_ID.Generic, Command]
   758         (_: Option[A], x: Text.Markup) =>
       
   759           res(x) match {
       
   760             case None => None
       
   761             case some => Some(some)
       
   762           }
       
   763       }
       
   764       for (Text.Info(r, Some(x)) <- cumulate(range, None, elements, result1, status))
       
   765         yield Text.Info(r, x)
       
   766     }
   639   }
   767   }
   640 
   768 
   641 
   769 
   642   /* model */
   770   /* model */
   643 
   771 
  1127           case (edits, Node.Edits(es)) => es ::: edits
  1255           case (edits, Node.Edits(es)) => es ::: edits
  1128           case (edits, _) => edits
  1256           case (edits, _) => edits
  1129         })
  1257         })
  1130 
  1258 
  1131       new Snapshot(this, version, node_name, edits, snippet_command)
  1259       new Snapshot(this, version, node_name, edits, snippet_command)
  1132       {
       
  1133         /* local node content */
       
  1134 
       
  1135         val node: Node = version.nodes(node_name)
       
  1136 
       
  1137         def nodes: List[(Node.Name, Node)] =
       
  1138           (node_name :: node.load_commands.flatMap(_.blobs_names)).
       
  1139             map(name => (name, version.nodes(name)))
       
  1140 
       
  1141         val commands_loading: List[Command] =
       
  1142           if (node_name.is_theory) Nil
       
  1143           else version.nodes.commands_loading(node_name)
       
  1144 
       
  1145         def commands_loading_ranges(pred: Node.Name => Boolean): List[Text.Range] =
       
  1146           (for {
       
  1147             cmd <- node.load_commands.iterator
       
  1148             blob_name <- cmd.blobs_names.iterator
       
  1149             if pred(blob_name)
       
  1150             start <- node.command_start(cmd)
       
  1151           } yield convert(cmd.core_range + start)).toList
       
  1152 
       
  1153         def current_command(other_node_name: Node.Name, offset: Text.Offset): Option[Command] =
       
  1154           if (other_node_name.is_theory) {
       
  1155             val other_node = version.nodes(other_node_name)
       
  1156             val iterator = other_node.command_iterator(revert(offset) max 0)
       
  1157             if (iterator.hasNext) {
       
  1158               val (command0, _) = iterator.next
       
  1159               other_node.commands.reverse.iterator(command0).find(command => !command.is_ignored)
       
  1160             }
       
  1161             else other_node.commands.reverse.iterator.find(command => !command.is_ignored)
       
  1162           }
       
  1163           else version.nodes.commands_loading(other_node_name).headOption
       
  1164 
       
  1165         def xml_markup(
       
  1166             range: Text.Range = Text.Range.full,
       
  1167             elements: Markup.Elements = Markup.Elements.full): XML.Body =
       
  1168           state.xml_markup(version, node_name, range = range, elements = elements)
       
  1169 
       
  1170         lazy val messages: List[(XML.Tree, Position.T)] =
       
  1171           (for {
       
  1172             (command, start) <-
       
  1173               Document.Node.Commands.starts_pos(
       
  1174                 node.commands.iterator, Token.Pos.file(node_name.node))
       
  1175             pos = command.span.keyword_pos(start).position(command.span.name)
       
  1176             (_, tree) <- state.command_results(version, command).iterator
       
  1177            } yield (tree, pos)).toList
       
  1178 
       
  1179         lazy val exports: List[Export.Entry] =
       
  1180           state.node_exports(version, node_name).iterator.map(_._2).toList
       
  1181 
       
  1182         lazy val exports_map: Map[String, Export.Entry] =
       
  1183           (for (entry <- exports.iterator) yield (entry.name, entry)).toMap
       
  1184 
       
  1185 
       
  1186         /* find command */
       
  1187 
       
  1188         def find_command(id: Document_ID.Generic): Option[(Node, Command)] =
       
  1189           state.lookup_id(id) match {
       
  1190             case None => None
       
  1191             case Some(st) =>
       
  1192               val command = st.command
       
  1193               val node = version.nodes(command.node_name)
       
  1194               if (node.commands.contains(command)) Some((node, command)) else None
       
  1195           }
       
  1196 
       
  1197         def find_command_position(id: Document_ID.Generic, offset: Symbol.Offset)
       
  1198             : Option[Line.Node_Position] =
       
  1199           for ((node, command) <- find_command(id))
       
  1200           yield {
       
  1201             val name = command.node_name.node
       
  1202             val sources_iterator =
       
  1203               node.commands.iterator.takeWhile(_ != command).map(_.source) ++
       
  1204                 (if (offset == 0) Iterator.empty
       
  1205                  else Iterator.single(command.source(Text.Range(0, command.chunk.decode(offset)))))
       
  1206             val pos = (Line.Position.zero /: sources_iterator)(_.advance(_))
       
  1207             Line.Node_Position(name, pos)
       
  1208           }
       
  1209 
       
  1210 
       
  1211         /* cumulate markup */
       
  1212 
       
  1213         def cumulate[A](
       
  1214           range: Text.Range,
       
  1215           info: A,
       
  1216           elements: Markup.Elements,
       
  1217           result: List[Command.State] => (A, Text.Markup) => Option[A],
       
  1218           status: Boolean = false): List[Text.Info[A]] =
       
  1219         {
       
  1220           val former_range = revert(range).inflate_singularity
       
  1221           val (chunk_name, command_iterator) =
       
  1222             commands_loading.headOption match {
       
  1223               case None => (Symbol.Text_Chunk.Default, node.command_iterator(former_range))
       
  1224               case Some(command) => (Symbol.Text_Chunk.File(node_name.node), Iterator((command, 0)))
       
  1225             }
       
  1226           val markup_index = Command.Markup_Index(status, chunk_name)
       
  1227           (for {
       
  1228             (command, command_start) <- command_iterator
       
  1229             chunk <- command.chunks.get(chunk_name).iterator
       
  1230             states = state.command_states(version, command)
       
  1231             res = result(states)
       
  1232             markup_range <- (former_range - command_start).try_restrict(chunk.range).iterator
       
  1233             markup = Command.State.merge_markup(states, markup_index, markup_range, elements)
       
  1234             Text.Info(r0, a) <- markup.cumulate[A](markup_range, info, elements,
       
  1235               {
       
  1236                 case (a, Text.Info(r0, b)) => res(a, Text.Info(convert(r0 + command_start), b))
       
  1237               }).iterator
       
  1238             r1 <- convert(r0 + command_start).try_restrict(range).iterator
       
  1239           } yield Text.Info(r1, a)).toList
       
  1240         }
       
  1241 
       
  1242         def select[A](
       
  1243           range: Text.Range,
       
  1244           elements: Markup.Elements,
       
  1245           result: List[Command.State] => Text.Markup => Option[A],
       
  1246           status: Boolean = false): List[Text.Info[A]] =
       
  1247         {
       
  1248           def result1(states: List[Command.State]): (Option[A], Text.Markup) => Option[Option[A]] =
       
  1249           {
       
  1250             val res = result(states)
       
  1251             (_: Option[A], x: Text.Markup) =>
       
  1252               res(x) match {
       
  1253                 case None => None
       
  1254                 case some => Some(some)
       
  1255               }
       
  1256           }
       
  1257           for (Text.Info(r, Some(x)) <- cumulate(range, None, elements, result1, status))
       
  1258             yield Text.Info(r, x)
       
  1259         }
       
  1260 
       
  1261 
       
  1262         /* command results */
       
  1263 
       
  1264         def command_results(range: Text.Range): Command.Results =
       
  1265           Command.State.merge_results(
       
  1266             select[List[Command.State]](range, Markup.Elements.full, command_states =>
       
  1267               { case _ => Some(command_states) }).flatMap(_.info))
       
  1268 
       
  1269         def command_results(command: Command): Command.Results =
       
  1270           state.command_results(version, command)
       
  1271 
       
  1272 
       
  1273         /* command ids: static and dynamic */
       
  1274 
       
  1275         def command_id_map: Map[Document_ID.Generic, Command] =
       
  1276           state.command_id_map(version, version.nodes(node_name).commands)
       
  1277 
       
  1278 
       
  1279         /* output */
       
  1280 
       
  1281         override def toString: String =
       
  1282           "Snapshot(node = " + node_name.node + ", version = " + version.id +
       
  1283             (if (is_outdated) ", outdated" else "") + ")"
       
  1284       }
       
  1285     }
  1260     }
  1286   }
  1261   }
  1287 }
  1262 }