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) |
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 |