14 import java.nio.file.StandardWatchEventKinds.{ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY} |
14 import java.nio.file.StandardWatchEventKinds.{ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY} |
15 |
15 |
16 import scala.jdk.CollectionConverters._ |
16 import scala.jdk.CollectionConverters._ |
17 |
17 |
18 |
18 |
19 class File_Watcher private[File_Watcher] // dummy template |
19 class File_Watcher private[File_Watcher] { |
20 { |
20 // dummy template |
21 def register(dir: JFile): Unit = {} |
21 def register(dir: JFile): Unit = {} |
22 def register_parent(file: JFile): Unit = {} |
22 def register_parent(file: JFile): Unit = {} |
23 def deregister(dir: JFile): Unit = {} |
23 def deregister(dir: JFile): Unit = {} |
24 def purge(retain: Set[JFile]): Unit = {} |
24 def purge(retain: Set[JFile]): Unit = {} |
25 def shutdown(): Unit = {} |
25 def shutdown(): Unit = {} |
26 } |
26 } |
27 |
27 |
28 object File_Watcher |
28 object File_Watcher { |
29 { |
29 val none: File_Watcher = new File_Watcher { |
30 val none: File_Watcher = new File_Watcher |
|
31 { |
|
32 override def toString: String = "File_Watcher.none" |
30 override def toString: String = "File_Watcher.none" |
33 } |
31 } |
34 |
32 |
35 def apply(handle: Set[JFile] => Unit, delay: => Time = Time.seconds(0.5)): File_Watcher = |
33 def apply(handle: Set[JFile] => Unit, delay: => Time = Time.seconds(0.5)): File_Watcher = |
36 if (Platform.is_windows) none else new Impl(handle, delay) |
34 if (Platform.is_windows) none else new Impl(handle, delay) |
40 |
38 |
41 sealed case class State( |
39 sealed case class State( |
42 dirs: Map[JFile, WatchKey] = Map.empty, |
40 dirs: Map[JFile, WatchKey] = Map.empty, |
43 changed: Set[JFile] = Set.empty) |
41 changed: Set[JFile] = Set.empty) |
44 |
42 |
45 class Impl private[File_Watcher](handle: Set[JFile] => Unit, delay: Time) extends File_Watcher |
43 class Impl private[File_Watcher](handle: Set[JFile] => Unit, delay: Time) extends File_Watcher { |
46 { |
|
47 private val state = Synchronized(File_Watcher.State()) |
44 private val state = Synchronized(File_Watcher.State()) |
48 private val watcher = FileSystems.getDefault.newWatchService() |
45 private val watcher = FileSystems.getDefault.newWatchService() |
49 |
46 |
50 override def toString: String = |
47 override def toString: String = |
51 state.value.dirs.keySet.mkString("File_Watcher(", ", ", ")") |
48 state.value.dirs.keySet.mkString("File_Watcher(", ", ", ")") |
60 case _ => |
57 case _ => |
61 val key = dir.toPath.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY) |
58 val key = dir.toPath.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY) |
62 st.copy(dirs = st.dirs + (dir -> key)) |
59 st.copy(dirs = st.dirs + (dir -> key)) |
63 }) |
60 }) |
64 |
61 |
65 override def register_parent(file: JFile): Unit = |
62 override def register_parent(file: JFile): Unit = { |
66 { |
|
67 val dir = file.getParentFile |
63 val dir = file.getParentFile |
68 if (dir != null && dir.isDirectory) register(dir) |
64 if (dir != null && dir.isDirectory) register(dir) |
69 } |
65 } |
70 |
66 |
71 override def deregister(dir: JFile): Unit = |
67 override def deregister(dir: JFile): Unit = |
83 (for ((dir, key) <- st.dirs.iterator if !retain(dir)) yield { key.cancel(); dir }))) |
79 (for ((dir, key) <- st.dirs.iterator if !retain(dir)) yield { key.cancel(); dir }))) |
84 |
80 |
85 |
81 |
86 /* changed directory entries */ |
82 /* changed directory entries */ |
87 |
83 |
88 private val delay_changed = Delay.last(delay) |
84 private val delay_changed = Delay.last(delay) { |
89 { |
|
90 val changed = state.change_result(st => (st.changed, st.copy(changed = Set.empty))) |
85 val changed = state.change_result(st => (st.changed, st.copy(changed = Set.empty))) |
91 handle(changed) |
86 handle(changed) |
92 } |
87 } |
93 |
88 |
94 private val watcher_thread = Isabelle_Thread.fork(name = "file_watcher", daemon = true) |
89 private val watcher_thread = Isabelle_Thread.fork(name = "file_watcher", daemon = true) { |
95 { |
|
96 try { |
90 try { |
97 while (true) { |
91 while (true) { |
98 val key = watcher.take |
92 val key = watcher.take |
99 val has_changed = |
93 val has_changed = |
100 state.change_result(st => |
94 state.change_result(st => { |
101 { |
|
102 val (remove, changed) = |
95 val (remove, changed) = |
103 st.dirs.collectFirst({ case (dir, key1) if key == key1 => dir }) match { |
96 st.dirs.collectFirst({ case (dir, key1) if key == key1 => dir }) match { |
104 case Some(dir) => |
97 case Some(dir) => |
105 val events: Iterable[WatchEvent[JPath]] = |
98 val events: Iterable[WatchEvent[JPath]] = |
106 key.pollEvents.asInstanceOf[JList[WatchEvent[JPath]]].asScala |
99 key.pollEvents.asInstanceOf[JList[WatchEvent[JPath]]].asScala |