Implement retained target sessions, runtime sizing and Component navigation

This commit is contained in:
2026-11-12 14:20:26 +01:00
parent e41b276b13
commit 6e25cf3ac6
71 changed files with 2538 additions and 120 deletions
@@ -36,14 +36,17 @@ public final class DesignerFileEditor extends UserDataHolderBase implements File
try {
ToolkitGate.check(project, file);
Path settings =
Path.of(
System.getProperty(
"neitzel.designer.launch",
Path.of(
project.getBasePath(),
".neitzel-fx-designer",
"host.properties")
.toString()));
Path.of(project.getBasePath(), ".neitzel-fx-designer", "host.properties");
String explicit = System.getProperty("neitzel.designer.launch");
if (explicit != null) {
var configured = HostLaunch.load(Path.of(explicit));
if (configured
.configuration()
.getParent()
.toRealPath()
.equals(Path.of(project.getBasePath()).toRealPath()))
settings = Path.of(explicit);
}
var launch = HostLaunch.load(settings);
SwingUtilities.invokeLater(
() -> {
@@ -26,6 +26,14 @@ public final class IntellijDocuments implements HostServices.Documents, AutoClos
private final java.util.concurrent.CopyOnWriteArrayList<java.util.function.Consumer<URI>>
changes = new java.util.concurrent.CopyOnWriteArrayList<>();
private final java.util.List<java.util.function.Consumer<URI>> saves =
new java.util.concurrent.CopyOnWriteArrayList<>();
public AutoCloseable onSave(java.util.function.Consumer<URI> listener) {
saves.add(listener);
return () -> saves.remove(listener);
}
public AutoCloseable onChange(java.util.function.Consumer<URI> listener) {
changes.add(listener);
return () -> changes.remove(listener);
@@ -48,6 +56,18 @@ public final class IntellijDocuments implements HostServices.Documents, AutoClos
this.project = Objects.requireNonNull(project);
lifetime = this::disposeResources;
Disposer.register(project, lifetime);
ApplicationManager.getApplication()
.getMessageBus()
.connect(lifetime)
.subscribe(
com.intellij.openapi.fileEditor.FileDocumentManagerListener.TOPIC,
new com.intellij.openapi.fileEditor.FileDocumentManagerListener() {
public void afterDocumentSaved(Document document) {
var file = FileDocumentManager.getInstance().getFile(document);
if (file != null && file.isInLocalFileSystem())
saves.forEach(listener -> listener.accept(canonicalUri(file)));
}
});
EditorFactory.getInstance()
.getEventMulticaster()
.addDocumentListener(
@@ -27,31 +27,85 @@ public final class IntellijHost implements AutoCloseable {
docs,
(request, output) -> scope.own(builds.start(request, output)),
runtimes,
location -> {
var result = new CompletableFuture<Void>();
ApplicationManager.getApplication()
.invokeLater(
() ->
WriteIntentReadAction.run(
() -> {
try {
if (project.isDisposed())
throw new IllegalStateException("Project disposed");
var file =
LocalFileSystem.getInstance()
.refreshAndFindFileByNioFile(
Path.of(location.document()));
if (file == null)
throw new IllegalArgumentException(
"Navigation file unavailable");
new OpenFileDescriptor(project, file, location.offset())
.navigate(true);
result.complete(null);
} catch (Throwable e) {
result.completeExceptionally(e);
}
}));
return result;
new HostServices.Navigation() {
public CompletionStage<java.util.List<HostServices.Location>> componentSources(
String type) {
if (!type.matches("[A-Za-z_$][A-Za-z0-9_$]*(\\.[A-Za-z_$][A-Za-z0-9_$]*)*"))
return CompletableFuture.failedFuture(
new IllegalArgumentException("Invalid type"));
return CompletableFuture.supplyAsync(
() ->
com.intellij.openapi.application.ReadAction.compute(
() -> {
var result = new java.util.ArrayList<HostServices.Location>();
String path = type.replace('.', '/');
for (var owner :
com.intellij.openapi.project.ProjectManager.getInstance()
.getOpenProjects()) {
var roots =
com.intellij.openapi.roots.ProjectRootManager.getInstance(owner)
.getContentSourceRoots();
boolean declared =
java.util.Arrays.stream(roots)
.anyMatch(
r -> r.findFileByRelativePath(path + ".java") != null);
if (declared)
for (var root : roots) {
var file = root.findFileByRelativePath(path + ".fxml");
if (file != null && file.isInLocalFileSystem())
result.add(
new HostServices.Location(
Path.of(file.getPath()).toUri(), 0));
}
}
return java.util.List.copyOf(result);
}));
}
public CompletionStage<Void> open(HostServices.Location location) {
var result = new CompletableFuture<Void>();
ApplicationManager.getApplication()
.invokeLater(
() ->
WriteIntentReadAction.run(
() -> {
try {
if (project.isDisposed())
throw new IllegalStateException("Project disposed");
var file =
LocalFileSystem.getInstance()
.refreshAndFindFileByNioFile(
Path.of(location.document()));
if (file == null)
throw new IllegalArgumentException(
"Navigation file unavailable");
Project owner = project;
for (var candidate :
com.intellij.openapi.project.ProjectManager.getInstance()
.getOpenProjects())
if (!candidate.isDisposed()
&& com.intellij.openapi.roots.ProjectFileIndex
.getInstance(candidate)
.isInSourceContent(file)) {
owner = candidate;
break;
}
if ("fxml".equalsIgnoreCase(file.getExtension())) {
var manager =
com.intellij.openapi.fileEditor.FileEditorManager
.getInstance(owner);
manager.openFile(file, true);
manager.setSelectedEditor(file, "neitzel-fx-designer");
} else
new OpenFileDescriptor(owner, file, location.offset())
.navigate(true);
result.complete(null);
} catch (Throwable e) {
result.completeExceptionally(e);
}
}));
return result;
}
},
diagnostics,
scope);
@@ -46,7 +46,7 @@ public final class ConnectedWorkflowTest extends BasePlatformTestCase {
UIUtil.dispatchAllInvocationEvents();
Thread.sleep(10);
}
assertEquals(32, (int) future.get(1, TimeUnit.SECONDS));
assertEquals(50, (int) future.get(1, TimeUnit.SECONDS));
}
}
}