diff --git a/engine/common/src/main/java/org/enso/common/ContextInsightSetup.java b/engine/common/src/main/java/org/enso/common/ContextInsightSetup.java
index 375240b878d..563745a1f6a 100644
--- a/engine/common/src/main/java/org/enso/common/ContextInsightSetup.java
+++ b/engine/common/src/main/java/org/enso/common/ContextInsightSetup.java
@@ -2,8 +2,13 @@ package org.enso.common;
import java.io.File;
import java.io.IOException;
+import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.nio.file.StandardWatchEventKinds;
+import java.nio.file.WatchKey;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.attribute.FileTime;
import java.util.function.Function;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Source;
@@ -53,6 +58,78 @@ final class ContextInsightSetup {
this.insightFile = file;
}
+ private static final class InsightFileWatcher implements Runnable {
+
+ private final Path insightFilePath;
+ private final Runnable onChange;
+
+ public InsightFileWatcher(Path insightFilePath, Runnable onChange) {
+ this.insightFilePath = insightFilePath;
+ this.onChange = onChange;
+ }
+
+ @Override
+ public void run() {
+ try {
+ watch();
+ } catch (IOException e) {
+ new IOException("Error watching the insight file", e).printStackTrace();
+ }
+ }
+
+ private void watch() throws IOException {
+ final Path insightDirectory = insightFilePath.getParent();
+ final Path insightFileName = insightFilePath.getFileName();
+ FileTime lastRecordedModifiedTime = FileTime.fromMillis(0);
+
+ try (final var watchService = FileSystems.getDefault().newWatchService()) {
+ insightDirectory.register(
+ watchService,
+ StandardWatchEventKinds.ENTRY_CREATE,
+ StandardWatchEventKinds.ENTRY_MODIFY);
+ while (true) {
+ WatchKey watchKey;
+ try {
+ watchKey = watchService.take();
+ } catch (InterruptedException ignored) {
+ return;
+ }
+
+ boolean isModified = false;
+ for (var watchEvent : watchKey.pollEvents()) {
+ if (watchEvent.kind() == StandardWatchEventKinds.OVERFLOW) {
+ continue;
+ }
+
+ final Path eventPath = (Path) watchEvent.context();
+ if (eventPath.endsWith(insightFileName)) {
+ final Path insightFilePath = insightDirectory.resolve(eventPath);
+ try {
+ final BasicFileAttributes attributes =
+ Files.readAttributes(insightFilePath, BasicFileAttributes.class);
+ if (attributes.lastModifiedTime().compareTo(lastRecordedModifiedTime) > 0) {
+ lastRecordedModifiedTime = attributes.lastModifiedTime();
+ isModified = true;
+ }
+ } catch (IOException ignored) {
+ continue;
+ }
+ }
+ }
+
+ if (isModified) {
+ onChange.run();
+ }
+
+ final boolean isValid = watchKey.reset();
+ if (!isValid) {
+ break;
+ }
+ }
+ }
+ }
+ }
+
/**
* Configures the context if {@link #INSIGHT_PROP} property is specified. This support is
* development only. It can be (and will be) removed in the future.
@@ -72,6 +149,7 @@ final class ContextInsightSetup {
: "Cannot find " + insightFile + " specified via " + INSIGHT_PROP + " property";
ACTIVE = new ContextInsightSetup(ctx, insightFile.toPath());
ACTIVE.initialize();
+ ACTIVE.startFileWatcherThread();
} catch (Error | Exception ex) {
var ae =
new AssertionError(
@@ -99,4 +177,22 @@ final class ContextInsightSetup {
var insight = (Function