src/Tools/VSCode/src/vscode_resources.scala
changeset 75393 87ebf5a50283
parent 75126 da1108a6d249
child 75394 42267c650205
equal deleted inserted replaced
75388:b3ca4a6ed74b 75393:87ebf5a50283
    12 import java.io.{File => JFile}
    12 import java.io.{File => JFile}
    13 
    13 
    14 import scala.util.parsing.input.Reader
    14 import scala.util.parsing.input.Reader
    15 
    15 
    16 
    16 
    17 object VSCode_Resources
    17 object VSCode_Resources {
    18 {
       
    19   /* internal state */
    18   /* internal state */
    20 
    19 
    21   sealed case class State(
    20   sealed case class State(
    22     models: Map[JFile, VSCode_Model] = Map.empty,
    21     models: Map[JFile, VSCode_Model] = Map.empty,
    23     caret: Option[(JFile, Line.Position)] = None,
    22     caret: Option[(JFile, Line.Position)] = None,
    24     overlays: Document.Overlays = Document.Overlays.empty,
    23     overlays: Document.Overlays = Document.Overlays.empty,
    25     pending_input: Set[JFile] = Set.empty,
    24     pending_input: Set[JFile] = Set.empty,
    26     pending_output: Set[JFile] = Set.empty)
    25     pending_output: Set[JFile] = Set.empty
    27   {
    26   ) {
    28     def update_models(changed: Iterable[(JFile, VSCode_Model)]): State =
    27     def update_models(changed: Iterable[(JFile, VSCode_Model)]): State =
    29       copy(
    28       copy(
    30         models = models ++ changed,
    29         models = models ++ changed,
    31         pending_input = changed.foldLeft(pending_input) { case (set, (file, _)) => set + file },
    30         pending_input = changed.foldLeft(pending_input) { case (set, (file, _)) => set + file },
    32         pending_output = changed.foldLeft(pending_output) { case (set, (file, _)) => set + file })
    31         pending_output = changed.foldLeft(pending_output) { case (set, (file, _)) => set + file })
    61   }
    60   }
    62 
    61 
    63 
    62 
    64   /* caret */
    63   /* caret */
    65 
    64 
    66   sealed case class Caret(file: JFile, model: VSCode_Model, offset: Text.Offset)
    65   sealed case class Caret(file: JFile, model: VSCode_Model, offset: Text.Offset) {
    67   {
       
    68     def node_name: Document.Node.Name = model.node_name
    66     def node_name: Document.Node.Name = model.node_name
    69   }
    67   }
    70 }
    68 }
    71 
    69 
    72 class VSCode_Resources(
    70 class VSCode_Resources(
    73     val options: Options,
    71   val options: Options,
    74     session_base_info: Sessions.Base_Info,
    72   session_base_info: Sessions.Base_Info,
    75     log: Logger = No_Logger)
    73   log: Logger = No_Logger)
    76   extends Resources(session_base_info.sessions_structure, session_base_info.check.base, log = log)
    74 extends Resources(session_base_info.sessions_structure, session_base_info.check.base, log = log) {
    77 {
       
    78   resources =>
    75   resources =>
    79 
    76 
    80   private val state = Synchronized(VSCode_Resources.State())
    77   private val state = Synchronized(VSCode_Resources.State())
    81 
    78 
    82 
    79 
   101         val master_dir = file.getParent
    98         val master_dir = file.getParent
   102         Document.Node.Name(node, master_dir, theory)
    99         Document.Node.Name(node, master_dir, theory)
   103       }
   100       }
   104     }
   101     }
   105 
   102 
   106   override def append(dir: String, source_path: Path): String =
   103   override def append(dir: String, source_path: Path): String = {
   107   {
       
   108     val path = source_path.expand
   104     val path = source_path.expand
   109     if (dir == "" || path.is_absolute) File.platform_path(path)
   105     if (dir == "" || path.is_absolute) File.platform_path(path)
   110     else if (path.is_current) dir
   106     else if (path.is_current) dir
   111     else if (path.is_basic && !dir.endsWith("/") && !dir.endsWith(JFile.separator))
   107     else if (path.is_basic && !dir.endsWith("/") && !dir.endsWith(JFile.separator))
   112       dir + JFile.separator + File.platform_path(path)
   108       dir + JFile.separator + File.platform_path(path)
   119   def get_model(name: Document.Node.Name): Option[VSCode_Model] = get_model(node_file(name))
   115   def get_model(name: Document.Node.Name): Option[VSCode_Model] = get_model(node_file(name))
   120 
   116 
   121 
   117 
   122   /* file content */
   118   /* file content */
   123 
   119 
   124   def read_file_content(name: Document.Node.Name): Option[String] =
   120   def read_file_content(name: Document.Node.Name): Option[String] = {
   125   {
       
   126     make_theory_content(name) orElse {
   121     make_theory_content(name) orElse {
   127       try { Some(Line.normalize(File.read(node_file(name)))) }
   122       try { Some(Line.normalize(File.read(node_file(name)))) }
   128       catch { case ERROR(_) => None }
   123       catch { case ERROR(_) => None }
   129     }
   124     }
   130   }
   125   }
   133     get_model(name) match {
   128     get_model(name) match {
   134       case Some(model) => Some(model.content.text)
   129       case Some(model) => Some(model.content.text)
   135       case None => read_file_content(name)
   130       case None => read_file_content(name)
   136     }
   131     }
   137 
   132 
   138   override def with_thy_reader[A](name: Document.Node.Name, f: Reader[Char] => A): A =
   133   override def with_thy_reader[A](name: Document.Node.Name, f: Reader[Char] => A): A = {
   139   {
       
   140     val file = node_file(name)
   134     val file = node_file(name)
   141     get_model(file) match {
   135     get_model(file) match {
   142       case Some(model) => f(Scan.char_reader(model.content.text))
   136       case Some(model) => f(Scan.char_reader(model.content.text))
   143       case None if file.isFile => using(Scan.byte_reader(file))(f)
   137       case None if file.isFile => using(Scan.byte_reader(file))(f)
   144       case None => error("No such file: " + quote(file.toString))
   138       case None => error("No such file: " + quote(file.toString))
   158     session: Session,
   152     session: Session,
   159     editor: Language_Server.Editor,
   153     editor: Language_Server.Editor,
   160     file: JFile,
   154     file: JFile,
   161     version: Long,
   155     version: Long,
   162     text: String,
   156     text: String,
   163     range: Option[Line.Range] = None): Unit =
   157     range: Option[Line.Range] = None
   164   {
   158   ): Unit = {
   165     state.change(st =>
   159     state.change(st => {
   166       {
   160       val model = st.models.getOrElse(file, VSCode_Model.init(session, editor, node_name(file)))
   167         val model = st.models.getOrElse(file, VSCode_Model.init(session, editor, node_name(file)))
   161       val model1 =
   168         val model1 =
   162         (model.change_text(text, range) getOrElse model).set_version(version).external(false)
   169           (model.change_text(text, range) getOrElse model).set_version(version).external(false)
   163       st.update_models(Some(file -> model1))
   170         st.update_models(Some(file -> model1))
   164     })
   171       })
       
   172   }
   165   }
   173 
   166 
   174   def close_model(file: JFile): Boolean =
   167   def close_model(file: JFile): Boolean =
   175     state.change_result(st =>
   168     state.change_result(st =>
   176       st.models.get(file) match {
   169       st.models.get(file) match {
   177         case None => (false, st)
   170         case None => (false, st)
   178         case Some(model) => (true, st.update_models(Some(file -> model.external(true))))
   171         case Some(model) => (true, st.update_models(Some(file -> model.external(true))))
   179       })
   172       })
   180 
   173 
   181   def sync_models(changed_files: Set[JFile]): Unit =
   174   def sync_models(changed_files: Set[JFile]): Unit =
   182     state.change(st =>
   175     state.change(st => {
   183       {
   176       val changed_models =
   184         val changed_models =
   177         (for {
   185           (for {
   178           (file, model) <- st.models.iterator
   186             (file, model) <- st.models.iterator
   179           if changed_files(file) && model.external_file
   187             if changed_files(file) && model.external_file
   180           text <- read_file_content(model.node_name)
   188             text <- read_file_content(model.node_name)
   181           model1 <- model.change_text(text)
   189             model1 <- model.change_text(text)
   182         } yield (file, model1)).toList
   190           } yield (file, model1)).toList
   183       st.update_models(changed_models)
   191         st.update_models(changed_models)
   184     })
   192       })
       
   193 
   185 
   194 
   186 
   195   /* overlays */
   187   /* overlays */
   196 
   188 
   197   def node_overlays(name: Document.Node.Name): Document.Node.Overlays =
   189   def node_overlays(name: Document.Node.Name): Document.Node.Overlays =
   207   /* resolve dependencies */
   199   /* resolve dependencies */
   208 
   200 
   209   def resolve_dependencies(
   201   def resolve_dependencies(
   210     session: Session,
   202     session: Session,
   211     editor: Language_Server.Editor,
   203     editor: Language_Server.Editor,
   212     file_watcher: File_Watcher): (Boolean, Boolean) =
   204     file_watcher: File_Watcher
   213   {
   205   ): (Boolean, Boolean) = {
   214     state.change_result(st =>
   206     state.change_result(st => {
   215       {
   207       /* theory files */
   216         /* theory files */
   208 
   217 
   209       val thys =
   218         val thys =
   210         (for ((_, model) <- st.models.iterator if model.is_theory)
   219           (for ((_, model) <- st.models.iterator if model.is_theory)
   211          yield (model.node_name, Position.none)).toList
   220            yield (model.node_name, Position.none)).toList
   212 
   221 
   213       val thy_files1 = resources.dependencies(thys).theories
   222         val thy_files1 = resources.dependencies(thys).theories
   214 
   223 
   215       val thy_files2 =
   224         val thy_files2 =
   216         (for {
   225           (for {
   217           (_, model) <- st.models.iterator
   226             (_, model) <- st.models.iterator
   218           thy_name <- resources.make_theory_name(model.node_name)
   227             thy_name <- resources.make_theory_name(model.node_name)
   219         } yield thy_name).toList
   228           } yield thy_name).toList
   220 
   229 
   221 
   230 
   222       /* auxiliary files */
   231         /* auxiliary files */
   223 
   232 
   224       val stable_tip_version =
   233         val stable_tip_version =
   225         if (st.models.forall(entry => entry._2.is_stable))
   234           if (st.models.forall(entry => entry._2.is_stable))
   226           session.get_state().stable_tip_version
   235             session.get_state().stable_tip_version
   227         else None
   236           else None
   228 
   237 
   229       val aux_files =
   238         val aux_files =
   230         stable_tip_version match {
   239           stable_tip_version match {
   231           case Some(version) => undefined_blobs(version.nodes)
   240             case Some(version) => undefined_blobs(version.nodes)
   232           case None => Nil
   241             case None => Nil
   233         }
   242           }
   234 
   243 
   235 
   244 
   236       /* loaded models */
   245         /* loaded models */
   237 
   246 
   238       val loaded_models =
   247         val loaded_models =
   239         (for {
   248           (for {
   240           node_name <- thy_files1.iterator ++ thy_files2.iterator ++ aux_files.iterator
   249             node_name <- thy_files1.iterator ++ thy_files2.iterator ++ aux_files.iterator
   241           file = node_file(node_name)
   250             file = node_file(node_name)
   242           if !st.models.isDefinedAt(file)
   251             if !st.models.isDefinedAt(file)
   243           text <- { file_watcher.register_parent(file); read_file_content(node_name) }
   252             text <- { file_watcher.register_parent(file); read_file_content(node_name) }
   244         }
   253           }
   245         yield {
   254           yield {
   246           val model = VSCode_Model.init(session, editor, node_name)
   255             val model = VSCode_Model.init(session, editor, node_name)
   247           val model1 = (model.change_text(text) getOrElse model).external(true)
   256             val model1 = (model.change_text(text) getOrElse model).external(true)
   248           (file, model1)
   257             (file, model1)
   249         }).toList
   258           }).toList
   250 
   259 
   251       val invoke_input = loaded_models.nonEmpty
   260         val invoke_input = loaded_models.nonEmpty
   252       val invoke_load = stable_tip_version.isEmpty
   261         val invoke_load = stable_tip_version.isEmpty
   253 
   262 
   254       ((invoke_input, invoke_load), st.update_models(loaded_models))
   263         ((invoke_input, invoke_load), st.update_models(loaded_models))
   255     })
   264       })
       
   265   }
   256   }
   266 
   257 
   267 
   258 
   268   /* pending input */
   259   /* pending input */
   269 
   260 
   270   def flush_input(session: Session, channel: Channel): Unit =
   261   def flush_input(session: Session, channel: Channel): Unit = {
   271   {
   262     state.change(st => {
   272     state.change(st =>
   263       val changed_models =
   273       {
   264         (for {
   274         val changed_models =
   265           file <- st.pending_input.iterator
   275           (for {
   266           model <- st.models.get(file)
   276             file <- st.pending_input.iterator
   267           (edits, model1) <-
   277             model <- st.models.get(file)
   268             model.flush_edits(false, st.document_blobs, file, st.get_caret(file))
   278             (edits, model1) <-
   269         } yield (edits, (file, model1))).toList
   279               model.flush_edits(false, st.document_blobs, file, st.get_caret(file))
   270 
   280           } yield (edits, (file, model1))).toList
   271       for { ((workspace_edits, _), _) <- changed_models if workspace_edits.nonEmpty }
   281 
   272         channel.write(LSP.WorkspaceEdit(workspace_edits))
   282         for { ((workspace_edits, _), _) <- changed_models if workspace_edits.nonEmpty }
   273 
   283           channel.write(LSP.WorkspaceEdit(workspace_edits))
   274       session.update(st.document_blobs, changed_models.flatMap(res => res._1._2))
   284 
   275 
   285         session.update(st.document_blobs, changed_models.flatMap(res => res._1._2))
   276       st.copy(
   286 
   277         models = st.models ++ changed_models.iterator.map(_._2),
   287         st.copy(
   278         pending_input = Set.empty)
   288           models = st.models ++ changed_models.iterator.map(_._2),
   279     })
   289           pending_input = Set.empty)
       
   290       })
       
   291   }
   280   }
   292 
   281 
   293 
   282 
   294   /* pending output */
   283   /* pending output */
   295 
   284 
   298 
   287 
   299   def update_output_visible(): Unit =
   288   def update_output_visible(): Unit =
   300     state.change(st => st.copy(pending_output = st.pending_output ++
   289     state.change(st => st.copy(pending_output = st.pending_output ++
   301       (for ((file, model) <- st.models.iterator if model.node_visible) yield file)))
   290       (for ((file, model) <- st.models.iterator if model.node_visible) yield file)))
   302 
   291 
   303   def flush_output(channel: Channel): Boolean =
   292   def flush_output(channel: Channel): Boolean = {
   304   {
   293     state.change_result(st => {
   305     state.change_result(st =>
   294       val (postponed, flushed) =
   306       {
   295         (for {
   307         val (postponed, flushed) =
   296           file <- st.pending_output.iterator
   308           (for {
   297           model <- st.models.get(file)
   309             file <- st.pending_output.iterator
   298         } yield (file, model, model.rendering())).toList.partition(_._3.snapshot.is_outdated)
   310             model <- st.models.get(file)
   299 
   311           } yield (file, model, model.rendering())).toList.partition(_._3.snapshot.is_outdated)
   300       val changed_iterator =
   312 
   301         for {
   313         val changed_iterator =
   302           (file, model, rendering) <- flushed.iterator
   314           for {
   303           (changed_diags, changed_decos, model1) = model.publish(rendering)
   315             (file, model, rendering) <- flushed.iterator
   304           if changed_diags.isDefined || changed_decos.isDefined
   316             (changed_diags, changed_decos, model1) = model.publish(rendering)
   305         }
   317             if changed_diags.isDefined || changed_decos.isDefined
   306         yield {
       
   307           for (diags <- changed_diags)
       
   308             channel.write(LSP.PublishDiagnostics(file, rendering.diagnostics_output(diags)))
       
   309           if (pide_extensions) {
       
   310             for (decos <- changed_decos)
       
   311               channel.write(rendering.decoration_output(decos).json(file))
   318           }
   312           }
   319           yield {
   313           (file, model1)
   320             for (diags <- changed_diags)
   314         }
   321               channel.write(LSP.PublishDiagnostics(file, rendering.diagnostics_output(diags)))
   315 
   322             if (pide_extensions) {
   316       (postponed.nonEmpty,
   323               for (decos <- changed_decos)
   317         st.copy(
   324                 channel.write(rendering.decoration_output(decos).json(file))
   318           models = st.models ++ changed_iterator,
   325             }
   319           pending_output = postponed.map(_._1).toSet))
   326             (file, model1)
   320     })
   327           }
       
   328 
       
   329         (postponed.nonEmpty,
       
   330           st.copy(
       
   331             models = st.models ++ changed_iterator,
       
   332             pending_output = postponed.map(_._1).toSet))
       
   333       }
       
   334     )
       
   335   }
   321   }
   336 
   322 
   337 
   323 
   338   /* output text */
   324   /* output text */
   339 
   325 
   352   /* caret handling */
   338   /* caret handling */
   353 
   339 
   354   def update_caret(caret: Option[(JFile, Line.Position)]): Unit =
   340   def update_caret(caret: Option[(JFile, Line.Position)]): Unit =
   355     state.change(_.update_caret(caret))
   341     state.change(_.update_caret(caret))
   356 
   342 
   357   def get_caret(): Option[VSCode_Resources.Caret] =
   343   def get_caret(): Option[VSCode_Resources.Caret] = {
   358   {
       
   359     val st = state.value
   344     val st = state.value
   360     for {
   345     for {
   361       (file, pos) <- st.caret
   346       (file, pos) <- st.caret
   362       model <- st.models.get(file)
   347       model <- st.models.get(file)
   363       offset <- model.content.doc.offset(pos)
   348       offset <- model.content.doc.offset(pos)