src/Tools/jEdit/src/document_model.scala
changeset 75393 87ebf5a50283
parent 75380 2cb2606ce075
child 75394 42267c650205
equal deleted inserted replaced
75388:b3ca4a6ed74b 75393:87ebf5a50283
    19 import org.gjt.sp.jedit.View
    19 import org.gjt.sp.jedit.View
    20 import org.gjt.sp.jedit.Buffer
    20 import org.gjt.sp.jedit.Buffer
    21 import org.gjt.sp.jedit.buffer.{BufferAdapter, BufferListener, JEditBuffer}
    21 import org.gjt.sp.jedit.buffer.{BufferAdapter, BufferListener, JEditBuffer}
    22 
    22 
    23 
    23 
    24 object Document_Model
    24 object Document_Model {
    25 {
       
    26   /* document models */
    25   /* document models */
    27 
    26 
    28   sealed case class State(
    27   sealed case class State(
    29     models: Map[Document.Node.Name, Document_Model] = Map.empty,
    28     models: Map[Document.Node.Name, Document_Model] = Map.empty,
    30     buffer_models: Map[JEditBuffer, Buffer_Model] = Map.empty,
    29     buffer_models: Map[JEditBuffer, Buffer_Model] = Map.empty,
    31     overlays: Document.Overlays = Document.Overlays.empty)
    30     overlays: Document.Overlays = Document.Overlays.empty
    32   {
    31   ) {
    33     def file_models_iterator: Iterator[(Document.Node.Name, File_Model)] =
    32     def file_models_iterator: Iterator[(Document.Node.Name, File_Model)] =
    34       for {
    33       for {
    35         (node_name, model) <- models.iterator
    34         (node_name, model) <- models.iterator
    36         if model.isInstanceOf[File_Model]
    35         if model.isInstanceOf[File_Model]
    37       } yield (node_name, model.asInstanceOf[File_Model])
    36       } yield (node_name, model.asInstanceOf[File_Model])
    41         (for {
    40         (for {
    42           (node_name, model) <- models.iterator
    41           (node_name, model) <- models.iterator
    43           blob <- model.get_blob
    42           blob <- model.get_blob
    44         } yield (node_name -> blob)).toMap)
    43         } yield (node_name -> blob)).toMap)
    45 
    44 
    46     def open_buffer(session: Session, node_name: Document.Node.Name, buffer: Buffer)
    45     def open_buffer(
    47       : (Buffer_Model, State) =
    46       session: Session,
    48     {
    47       node_name: Document.Node.Name,
       
    48       buffer: Buffer
       
    49     ) : (Buffer_Model, State) = {
    49       val old_model =
    50       val old_model =
    50         models.get(node_name) match {
    51         models.get(node_name) match {
    51           case Some(file_model: File_Model) => Some(file_model)
    52           case Some(file_model: File_Model) => Some(file_model)
    52           case Some(buffer_model: Buffer_Model) => Some(buffer_model.exit())
    53           case Some(buffer_model: Buffer_Model) => Some(buffer_model.exit())
    53           case _ => None
    54           case _ => None
    56       (buffer_model,
    57       (buffer_model,
    57         copy(models = models + (node_name -> buffer_model),
    58         copy(models = models + (node_name -> buffer_model),
    58           buffer_models = buffer_models + (buffer -> buffer_model)))
    59           buffer_models = buffer_models + (buffer -> buffer_model)))
    59     }
    60     }
    60 
    61 
    61     def close_buffer(buffer: JEditBuffer): State =
    62     def close_buffer(buffer: JEditBuffer): State = {
    62     {
       
    63       buffer_models.get(buffer) match {
    63       buffer_models.get(buffer) match {
    64         case None => this
    64         case None => this
    65         case Some(buffer_model) =>
    65         case Some(buffer_model) =>
    66           val file_model = buffer_model.exit()
    66           val file_model = buffer_model.exit()
    67           copy(models = models + (file_model.node_name -> file_model),
    67           copy(models = models + (file_model.node_name -> file_model),
   111     state.change(st => st.copy(overlays = st.overlays.remove(command, fn, args)))
   111     state.change(st => st.copy(overlays = st.overlays.remove(command, fn, args)))
   112 
   112 
   113 
   113 
   114   /* sync external files */
   114   /* sync external files */
   115 
   115 
   116   def sync_files(changed_files: Set[JFile]): Boolean =
   116   def sync_files(changed_files: Set[JFile]): Boolean = {
   117   {
   117     state.change_result(st => {
   118     state.change_result(st =>
   118       val changed_models =
   119       {
   119         (for {
   120         val changed_models =
   120           (node_name, model) <- st.file_models_iterator
   121           (for {
   121           file <- model.file if changed_files(file)
   122             (node_name, model) <- st.file_models_iterator
   122           text <- PIDE.resources.read_file_content(node_name)
   123             file <- model.file if changed_files(file)
   123           if model.content.text != text
   124             text <- PIDE.resources.read_file_content(node_name)
   124         } yield {
   125             if model.content.text != text
   125           val content = Document_Model.File_Content(text)
   126           } yield {
   126           val edits = Text.Edit.replace(0, model.content.text, text)
   127             val content = Document_Model.File_Content(text)
   127           (node_name, model.copy(content = content, pending_edits = model.pending_edits ::: edits))
   128             val edits = Text.Edit.replace(0, model.content.text, text)
   128         }).toList
   129             (node_name, model.copy(content = content, pending_edits = model.pending_edits ::: edits))
   129       if (changed_models.isEmpty) (false, st)
   130           }).toList
   130       else (true, st.copy(models = changed_models.foldLeft(st.models)(_ + _)))
   131         if (changed_models.isEmpty) (false, st)
   131     })
   132         else (true, st.copy(models = changed_models.foldLeft(st.models)(_ + _)))
       
   133       })
       
   134   }
   132   }
   135 
   133 
   136 
   134 
   137   /* syntax */
   135   /* syntax */
   138 
   136 
   139   def syntax_changed(names: List[Document.Node.Name]): Unit =
   137   def syntax_changed(names: List[Document.Node.Name]): Unit = {
   140   {
       
   141     GUI_Thread.require {}
   138     GUI_Thread.require {}
   142 
   139 
   143     val models = state.value.models
   140     val models = state.value.models
   144     for (name <- names.iterator; model <- models.get(name)) {
   141     for (name <- names.iterator; model <- models.get(name)) {
   145       model match { case buffer_model: Buffer_Model => buffer_model.syntax_changed() case _ => }
   142       model match { case buffer_model: Buffer_Model => buffer_model.syntax_changed() case _ => }
   147   }
   144   }
   148 
   145 
   149 
   146 
   150   /* init and exit */
   147   /* init and exit */
   151 
   148 
   152   def init(session: Session, node_name: Document.Node.Name, buffer: Buffer): Buffer_Model =
   149   def init(session: Session, node_name: Document.Node.Name, buffer: Buffer): Buffer_Model = {
   153   {
       
   154     GUI_Thread.require {}
   150     GUI_Thread.require {}
   155     state.change_result(st =>
   151     state.change_result(st =>
   156       st.buffer_models.get(buffer) match {
   152       st.buffer_models.get(buffer) match {
   157         case Some(buffer_model) if buffer_model.node_name == node_name =>
   153         case Some(buffer_model) if buffer_model.node_name == node_name =>
   158           buffer_model.init_token_marker()
   154           buffer_model.init_token_marker()
   162           buffer.propertiesChanged()
   158           buffer.propertiesChanged()
   163           res
   159           res
   164       })
   160       })
   165   }
   161   }
   166 
   162 
   167   def exit(buffer: Buffer): Unit =
   163   def exit(buffer: Buffer): Unit = {
   168   {
       
   169     GUI_Thread.require {}
   164     GUI_Thread.require {}
   170     state.change(st =>
   165     state.change(st =>
   171       if (st.buffer_models.isDefinedAt(buffer)) {
   166       if (st.buffer_models.isDefinedAt(buffer)) {
   172         val res = st.close_buffer(buffer)
   167         val res = st.close_buffer(buffer)
   173         buffer.propertiesChanged()
   168         buffer.propertiesChanged()
   174         res
   169         res
   175       }
   170       }
   176       else st)
   171       else st)
   177   }
   172   }
   178 
   173 
   179   def provide_files(session: Session, files: List[(Document.Node.Name, String)]): Unit =
   174   def provide_files(session: Session, files: List[(Document.Node.Name, String)]): Unit = {
   180   {
       
   181     GUI_Thread.require {}
   175     GUI_Thread.require {}
   182     state.change(st =>
   176     state.change(st =>
   183       files.foldLeft(st) {
   177       files.foldLeft(st) {
   184         case (st1, (node_name, text)) => st1.provide_file(session, node_name, text)
   178         case (st1, (node_name, text)) => st1.provide_file(session, node_name, text)
   185       })
   179       })
   193       (node_name, model) <- state.value.models.iterator
   187       (node_name, model) <- state.value.models.iterator
   194       if model.node_required
   188       if model.node_required
   195     } yield node_name).toSet
   189     } yield node_name).toSet
   196 
   190 
   197   def node_required(
   191   def node_required(
   198     name: Document.Node.Name, toggle: Boolean = false, set: Boolean = false): Unit =
   192     name: Document.Node.Name,
   199   {
   193     toggle: Boolean = false,
       
   194     set: Boolean = false
       
   195   ) : Unit = {
   200     GUI_Thread.require {}
   196     GUI_Thread.require {}
   201 
   197 
   202     val changed =
   198     val changed =
   203       state.change_result(st =>
   199       state.change_result(st =>
   204         st.models.get(name) match {
   200         st.models.get(name) match {
   224       node_required(model.node_name, toggle = toggle, set = set))
   220       node_required(model.node_name, toggle = toggle, set = set))
   225 
   221 
   226 
   222 
   227   /* flushed edits */
   223   /* flushed edits */
   228 
   224 
   229   def flush_edits(hidden: Boolean, purge: Boolean): (Document.Blobs, List[Document.Edit_Text]) =
   225   def flush_edits(hidden: Boolean, purge: Boolean): (Document.Blobs, List[Document.Edit_Text]) = {
   230   {
   226     GUI_Thread.require {}
   231     GUI_Thread.require {}
   227 
   232 
   228     state.change_result(st => {
   233     state.change_result(st =>
   229       val doc_blobs = st.document_blobs
   234       {
   230 
   235         val doc_blobs = st.document_blobs
   231       val buffer_edits =
   236 
   232         (for {
   237         val buffer_edits =
   233           (_, model) <- st.buffer_models.iterator
   238           (for {
   234           edit <- model.flush_edits(doc_blobs, hidden).iterator
   239             (_, model) <- st.buffer_models.iterator
   235         } yield edit).toList
   240             edit <- model.flush_edits(doc_blobs, hidden).iterator
   236 
   241           } yield edit).toList
   237       val file_edits =
   242 
   238         (for {
   243         val file_edits =
   239           (node_name, model) <- st.file_models_iterator
   244           (for {
   240           (edits, model1) <- model.flush_edits(doc_blobs, hidden)
   245             (node_name, model) <- st.file_models_iterator
   241         } yield (edits, node_name -> model1)).toList
   246             (edits, model1) <- model.flush_edits(doc_blobs, hidden)
   242 
   247           } yield (edits, node_name -> model1)).toList
   243       val model_edits = buffer_edits ::: file_edits.flatMap(_._1)
   248 
   244 
   249         val model_edits = buffer_edits ::: file_edits.flatMap(_._1)
   245       val purge_edits =
   250 
   246         if (purge) {
   251         val purge_edits =
   247           val purged =
   252           if (purge) {
   248             (for ((node_name, model) <- st.file_models_iterator)
   253             val purged =
   249              yield (node_name -> model.purge_edits(doc_blobs))).toList
   254               (for ((node_name, model) <- st.file_models_iterator)
   250 
   255                yield (node_name -> model.purge_edits(doc_blobs))).toList
   251           val imports = {
   256 
   252             val open_nodes =
   257             val imports =
   253               (for ((_, model) <- st.buffer_models.iterator) yield model.node_name).toList
   258             {
   254             val touched_nodes = model_edits.map(_._1)
   259               val open_nodes =
   255             val pending_nodes = for ((node_name, None) <- purged) yield node_name
   260                 (for ((_, model) <- st.buffer_models.iterator) yield model.node_name).toList
   256             (open_nodes ::: touched_nodes ::: pending_nodes).map((_, Position.none))
   261               val touched_nodes = model_edits.map(_._1)
       
   262               val pending_nodes = for ((node_name, None) <- purged) yield node_name
       
   263               (open_nodes ::: touched_nodes ::: pending_nodes).map((_, Position.none))
       
   264             }
       
   265             val retain = PIDE.resources.dependencies(imports).theories.toSet
       
   266 
       
   267             for ((node_name, Some(edits)) <- purged if !retain(node_name); edit <- edits)
       
   268               yield edit
       
   269           }
   257           }
   270           else Nil
   258           val retain = PIDE.resources.dependencies(imports).theories.toSet
   271 
   259 
   272         val st1 = st.copy(models = st.models ++ file_edits.map(_._2) -- purge_edits.map(_._1))
   260           for ((node_name, Some(edits)) <- purged if !retain(node_name); edit <- edits)
   273         PIDE.plugin.file_watcher.purge(
   261             yield edit
   274           (for {
   262         }
   275             (_, model) <- st1.file_models_iterator
   263         else Nil
   276             file <- model.file
   264 
   277           } yield file.getParentFile).toSet)
   265       val st1 = st.copy(models = st.models ++ file_edits.map(_._2) -- purge_edits.map(_._1))
   278 
   266       PIDE.plugin.file_watcher.purge(
   279         ((doc_blobs, model_edits ::: purge_edits), st1)
   267         (for {
   280       })
   268           (_, model) <- st1.file_models_iterator
       
   269           file <- model.file
       
   270         } yield file.getParentFile).toSet)
       
   271 
       
   272       ((doc_blobs, model_edits ::: purge_edits), st1)
       
   273     })
   281   }
   274   }
   282 
   275 
   283 
   276 
   284   /* file content */
   277   /* file content */
   285 
   278 
   286   sealed case class File_Content(text: String)
   279   sealed case class File_Content(text: String) {
   287   {
       
   288     lazy val bytes: Bytes = Bytes(Symbol.encode(text))
   280     lazy val bytes: Bytes = Bytes(Symbol.encode(text))
   289     lazy val chunk: Symbol.Text_Chunk = Symbol.Text_Chunk(text)
   281     lazy val chunk: Symbol.Text_Chunk = Symbol.Text_Chunk(text)
   290     lazy val bibtex_entries: List[Text.Info[String]] =
   282     lazy val bibtex_entries: List[Text.Info[String]] =
   291       try { Bibtex.entries(text) }
   283       try { Bibtex.entries(text) }
   292       catch { case ERROR(_) => Nil }
   284       catch { case ERROR(_) => Nil }
   293   }
   285   }
   294 
   286 
   295 
   287 
   296   /* HTTP preview */
   288   /* HTTP preview */
   297 
   289 
   298   def open_preview(view: View, plain_text: Boolean): Unit =
   290   def open_preview(view: View, plain_text: Boolean): Unit = {
   299   {
       
   300     Document_Model.get(view.getBuffer) match {
   291     Document_Model.get(view.getBuffer) match {
   301       case Some(model) =>
   292       case Some(model) =>
   302         val url = Preview_Service.server_url(plain_text, model.node_name)
   293         val url = Preview_Service.server_url(plain_text, model.node_name)
   303         PIDE.editor.hyperlink_url(url).follow(view)
   294         PIDE.editor.hyperlink_url(url).follow(view)
   304       case _ =>
   295       case _ =>
   305     }
   296     }
   306   }
   297   }
   307 
   298 
   308   object Preview_Service extends HTTP.Service("preview")
   299   object Preview_Service extends HTTP.Service("preview") {
   309   {
       
   310     service =>
   300     service =>
   311 
   301 
   312     private val plain_text_prefix = "plain_text="
   302     private val plain_text_prefix = "plain_text="
   313 
   303 
   314     def server_url(plain_text: Boolean, node_name: Document.Node.Name): String =
   304     def server_url(plain_text: Boolean, node_name: Document.Node.Name): String =
   339         HTTP.Response.html(document.content)
   329         HTTP.Response.html(document.content)
   340       }
   330       }
   341   }
   331   }
   342 }
   332 }
   343 
   333 
   344 sealed abstract class Document_Model extends Document.Model
   334 sealed abstract class Document_Model extends Document.Model {
   345 {
       
   346   /* perspective */
   335   /* perspective */
   347 
   336 
   348   def document_view_ranges(snapshot: Document.Snapshot): List[Text.Range] = Nil
   337   def document_view_ranges(snapshot: Document.Snapshot): List[Text.Range] = Nil
   349 
   338 
   350   def node_perspective(
   339   def node_perspective(
   351     doc_blobs: Document.Blobs, hidden: Boolean): (Boolean, Document.Node.Perspective_Text) =
   340     doc_blobs: Document.Blobs,
   352   {
   341     hidden: Boolean
       
   342   ): (Boolean, Document.Node.Perspective_Text) = {
   353     GUI_Thread.require {}
   343     GUI_Thread.require {}
   354 
   344 
   355     if (Isabelle.continuous_checking && is_theory) {
   345     if (Isabelle.continuous_checking && is_theory) {
   356       val snapshot = this.snapshot()
   346       val snapshot = this.snapshot()
   357 
   347 
   371   }
   361   }
   372 
   362 
   373 
   363 
   374   /* snapshot */
   364   /* snapshot */
   375 
   365 
   376   @tailrec final def await_stable_snapshot(): Document.Snapshot =
   366   @tailrec final def await_stable_snapshot(): Document.Snapshot = {
   377   {
       
   378     val snapshot = this.snapshot()
   367     val snapshot = this.snapshot()
   379     if (snapshot.is_outdated) {
   368     if (snapshot.is_outdated) {
   380       PIDE.options.seconds("editor_output_delay").sleep()
   369       PIDE.options.seconds("editor_output_delay").sleep()
   381       await_stable_snapshot()
   370       await_stable_snapshot()
   382     }
   371     }
   383     else snapshot
   372     else snapshot
   384   }
   373   }
   385 }
   374 }
   386 
   375 
   387 object File_Model
   376 object File_Model {
   388 {
       
   389   def empty(session: Session): File_Model =
   377   def empty(session: Session): File_Model =
   390     File_Model(session, Document.Node.Name.empty, None, Document_Model.File_Content(""),
   378     File_Model(session, Document.Node.Name.empty, None, Document_Model.File_Content(""),
   391       false, Document.Node.no_perspective_text, Nil)
   379       false, Document.Node.no_perspective_text, Nil)
   392 
   380 
   393   def init(session: Session,
   381   def init(session: Session,
   394     node_name: Document.Node.Name,
   382     node_name: Document.Node.Name,
   395     text: String,
   383     text: String,
   396     node_required: Boolean = false,
   384     node_required: Boolean = false,
   397     last_perspective: Document.Node.Perspective_Text = Document.Node.no_perspective_text,
   385     last_perspective: Document.Node.Perspective_Text = Document.Node.no_perspective_text,
   398     pending_edits: List[Text.Edit] = Nil): File_Model =
   386     pending_edits: List[Text.Edit] = Nil
   399   {
   387   ): File_Model = {
   400     val file = JEdit_Lib.check_file(node_name.node)
   388     val file = JEdit_Lib.check_file(node_name.node)
   401     file.foreach(PIDE.plugin.file_watcher.register_parent(_))
   389     file.foreach(PIDE.plugin.file_watcher.register_parent(_))
   402 
   390 
   403     val content = Document_Model.File_Content(text)
   391     val content = Document_Model.File_Content(text)
   404     val node_required1 = node_required || File_Format.registry.is_theory(node_name)
   392     val node_required1 = node_required || File_Format.registry.is_theory(node_name)
   411   node_name: Document.Node.Name,
   399   node_name: Document.Node.Name,
   412   file: Option[JFile],
   400   file: Option[JFile],
   413   content: Document_Model.File_Content,
   401   content: Document_Model.File_Content,
   414   node_required: Boolean,
   402   node_required: Boolean,
   415   last_perspective: Document.Node.Perspective_Text,
   403   last_perspective: Document.Node.Perspective_Text,
   416   pending_edits: List[Text.Edit]) extends Document_Model
   404   pending_edits: List[Text.Edit]
   417 {
   405 ) extends Document_Model {
   418   /* text */
   406   /* text */
   419 
   407 
   420   def get_text(range: Text.Range): Option[String] =
   408   def get_text(range: Text.Range): Option[String] =
   421     range.try_substring(content.text)
   409     range.try_substring(content.text)
   422 
   410 
   451         val content1 = Document_Model.File_Content(text)
   439         val content1 = Document_Model.File_Content(text)
   452         val pending_edits1 = pending_edits ::: edits
   440         val pending_edits1 = pending_edits ::: edits
   453         Some(copy(content = content1, pending_edits = pending_edits1))
   441         Some(copy(content = content1, pending_edits = pending_edits1))
   454     }
   442     }
   455 
   443 
   456   def flush_edits(doc_blobs: Document.Blobs, hidden: Boolean)
   444   def flush_edits(
   457     : Option[(List[Document.Edit_Text], File_Model)] =
   445     doc_blobs: Document.Blobs,
   458   {
   446     hidden: Boolean
       
   447   ) : Option[(List[Document.Edit_Text], File_Model)] = {
   459     val (reparse, perspective) = node_perspective(doc_blobs, hidden)
   448     val (reparse, perspective) = node_perspective(doc_blobs, hidden)
   460     if (reparse || pending_edits.nonEmpty || last_perspective != perspective) {
   449     if (reparse || pending_edits.nonEmpty || last_perspective != perspective) {
   461       val edits = node_edits(node_header, pending_edits, perspective)
   450       val edits = node_edits(node_header, pending_edits, perspective)
   462       Some((edits, copy(last_perspective = perspective, pending_edits = Nil)))
   451       Some((edits, copy(last_perspective = perspective, pending_edits = Nil)))
   463     }
   452     }
   479   def is_stable: Boolean = pending_edits.isEmpty
   468   def is_stable: Boolean = pending_edits.isEmpty
   480   def snapshot(): Document.Snapshot = session.snapshot(node_name, pending_edits)
   469   def snapshot(): Document.Snapshot = session.snapshot(node_name, pending_edits)
   481 }
   470 }
   482 
   471 
   483 case class Buffer_Model(session: Session, node_name: Document.Node.Name, buffer: Buffer)
   472 case class Buffer_Model(session: Session, node_name: Document.Node.Name, buffer: Buffer)
   484   extends Document_Model
   473 extends Document_Model {
   485 {
       
   486   /* text */
   474   /* text */
   487 
   475 
   488   def get_text(range: Text.Range): Option[String] =
   476   def get_text(range: Text.Range): Option[String] =
   489     JEdit_Lib.get_text(buffer, range)
   477     JEdit_Lib.get_text(buffer, range)
   490 
   478 
   491 
   479 
   492   /* header */
   480   /* header */
   493 
   481 
   494   def node_header(): Document.Node.Header =
   482   def node_header(): Document.Node.Header = {
   495   {
       
   496     GUI_Thread.require {}
   483     GUI_Thread.require {}
   497 
   484 
   498     PIDE.resources.special_header(node_name) getOrElse
   485     PIDE.resources.special_header(node_name) getOrElse
   499       JEdit_Lib.buffer_lock(buffer) {
   486       JEdit_Lib.buffer_lock(buffer) {
   500         PIDE.resources.check_thy(node_name, JEdit_Lib.buffer_reader(buffer), strict = false)
   487         PIDE.resources.check_thy(node_name, JEdit_Lib.buffer_reader(buffer), strict = false)
   513     for {
   500     for {
   514       text_area <- JEdit_Lib.jedit_text_areas(buffer)
   501       text_area <- JEdit_Lib.jedit_text_areas(buffer)
   515       doc_view <- Document_View.get(text_area)
   502       doc_view <- Document_View.get(text_area)
   516     } yield doc_view
   503     } yield doc_view
   517 
   504 
   518   override def document_view_ranges(snapshot: Document.Snapshot): List[Text.Range] =
   505   override def document_view_ranges(snapshot: Document.Snapshot): List[Text.Range] = {
   519   {
       
   520     GUI_Thread.require {}
   506     GUI_Thread.require {}
   521 
   507 
   522     (for {
   508     (for {
   523       doc_view <- document_view_iterator
   509       doc_view <- document_view_iterator
   524       range <- doc_view.perspective(snapshot).ranges.iterator
   510       range <- doc_view.perspective(snapshot).ranges.iterator
   579     }
   565     }
   580 
   566 
   581 
   567 
   582   /* pending edits */
   568   /* pending edits */
   583 
   569 
   584   private object pending_edits
   570   private object pending_edits {
   585   {
       
   586     private val pending = new mutable.ListBuffer[Text.Edit]
   571     private val pending = new mutable.ListBuffer[Text.Edit]
   587     private var last_perspective = Document.Node.no_perspective_text
   572     private var last_perspective = Document.Node.no_perspective_text
   588 
   573 
   589     def nonEmpty: Boolean = synchronized { pending.nonEmpty }
   574     def nonEmpty: Boolean = synchronized { pending.nonEmpty }
   590     def get_edits: List[Text.Edit] = synchronized { pending.toList }
   575     def get_edits: List[Text.Edit] = synchronized { pending.toList }
   627     pending_edits.flush_edits(doc_blobs, hidden)
   612     pending_edits.flush_edits(doc_blobs, hidden)
   628 
   613 
   629 
   614 
   630   /* buffer listener */
   615   /* buffer listener */
   631 
   616 
   632   private val buffer_listener: BufferListener = new BufferAdapter
   617   private val buffer_listener: BufferListener = new BufferAdapter {
   633   {
   618     override def contentInserted(
   634     override def contentInserted(buffer: JEditBuffer,
   619       buffer: JEditBuffer,
   635       start_line: Int, offset: Int, num_lines: Int, length: Int): Unit =
   620       start_line: Int,
   636     {
   621       offset: Int,
       
   622       num_lines: Int,
       
   623       length: Int
       
   624     ): Unit = {
   637       pending_edits.edit(List(Text.Edit.insert(offset, buffer.getText(offset, length))))
   625       pending_edits.edit(List(Text.Edit.insert(offset, buffer.getText(offset, length))))
   638     }
   626     }
   639 
   627 
   640     override def preContentRemoved(buffer: JEditBuffer,
   628     override def preContentRemoved(
   641       start_line: Int, offset: Int, num_lines: Int, removed_length: Int): Unit =
   629       buffer: JEditBuffer,
   642     {
   630       start_line: Int,
       
   631       offset: Int,
       
   632       num_lines: Int,
       
   633       removed_length: Int
       
   634     ): Unit = {
   643       pending_edits.edit(List(Text.Edit.remove(offset, buffer.getText(offset, removed_length))))
   635       pending_edits.edit(List(Text.Edit.remove(offset, buffer.getText(offset, removed_length))))
   644     }
   636     }
   645   }
   637   }
   646 
   638 
   647 
   639 
   648   /* syntax */
   640   /* syntax */
   649 
   641 
   650   def syntax_changed(): Unit =
   642   def syntax_changed(): Unit = {
   651   {
       
   652     JEdit_Lib.buffer_line_manager(buffer).setFirstInvalidLineContext(0)
   643     JEdit_Lib.buffer_line_manager(buffer).setFirstInvalidLineContext(0)
   653     for (text_area <- JEdit_Lib.jedit_text_areas(buffer))
   644     for (text_area <- JEdit_Lib.jedit_text_areas(buffer))
   654       Untyped.method(Class.forName("org.gjt.sp.jedit.textarea.TextArea"), "foldStructureChanged").
   645       Untyped.method(Class.forName("org.gjt.sp.jedit.textarea.TextArea"), "foldStructureChanged").
   655         invoke(text_area)
   646         invoke(text_area)
   656     buffer.invalidateCachedFoldLevels()
   647     buffer.invalidateCachedFoldLevels()
   657   }
   648   }
   658 
   649 
   659   def init_token_marker(): Unit =
   650   def init_token_marker(): Unit = {
   660   {
       
   661     Isabelle.buffer_token_marker(buffer) match {
   651     Isabelle.buffer_token_marker(buffer) match {
   662       case Some(marker) if marker != buffer.getTokenMarker =>
   652       case Some(marker) if marker != buffer.getTokenMarker =>
   663         buffer.setTokenMarker(marker)
   653         buffer.setTokenMarker(marker)
   664         syntax_changed()
   654         syntax_changed()
   665       case _ =>
   655       case _ =>
   667   }
   657   }
   668 
   658 
   669 
   659 
   670   /* init */
   660   /* init */
   671 
   661 
   672   def init(old_model: Option[File_Model]): Buffer_Model =
   662   def init(old_model: Option[File_Model]): Buffer_Model = {
   673   {
       
   674     GUI_Thread.require {}
   663     GUI_Thread.require {}
   675 
   664 
   676     old_model match {
   665     old_model match {
   677       case None =>
   666       case None =>
   678         pending_edits.edit(List(Text.Edit.insert(0, JEdit_Lib.buffer_text(buffer))))
   667         pending_edits.edit(List(Text.Edit.insert(0, JEdit_Lib.buffer_text(buffer))))
   691   }
   680   }
   692 
   681 
   693 
   682 
   694   /* exit */
   683   /* exit */
   695 
   684 
   696   def exit(): File_Model =
   685   def exit(): File_Model = {
   697   {
       
   698     GUI_Thread.require {}
   686     GUI_Thread.require {}
   699 
   687 
   700     buffer.removeBufferListener(buffer_listener)
   688     buffer.removeBufferListener(buffer_listener)
   701     init_token_marker()
   689     init_token_marker()
   702 
   690