Publish shared FXML diagnostics and integrate IntelliJ Problems
Navigate authoritative FXML locations, withdraw stale results and preserve host Undo/Redo. Include editor ownership in native problem identity so equal diagnostics from separate owners survive independent cleanup. Qualify both hosts automatically.
This commit is contained in:
@@ -121,6 +121,11 @@ public final class DesignerPane extends BorderPane implements AutoCloseable {
|
||||
private BindingEditor bindingEditor, computedEditor, repeatEditor;
|
||||
private TableEditor tableEditor;
|
||||
private ReferenceEditor referenceEditor;
|
||||
private DiagnosticsPane diagnosticsPane;
|
||||
private final String diagnosticsOwner = java.util.UUID.randomUUID().toString();
|
||||
private long diagnosticsRevision = -1;
|
||||
private SplitPane sourceSplit;
|
||||
private Integer pendingSourceOffset;
|
||||
private String pendingReferenceValidation;
|
||||
private String validatedSource, validatingSource;
|
||||
private boolean validationPreview;
|
||||
@@ -220,6 +225,8 @@ public final class DesignerPane extends BorderPane implements AutoCloseable {
|
||||
new TableEditor(
|
||||
this::editTable, this::navigateComponent, values -> validateBindings(values, false));
|
||||
referenceEditor = new ReferenceEditor(file.toUri().toString(), this::editStructure);
|
||||
diagnosticsPane =
|
||||
new DiagnosticsPane(() -> validateBindings(null, false), this::navigateDiagnostic);
|
||||
bindingBrowser =
|
||||
new BindingBrowser(
|
||||
this::discoverBindings,
|
||||
@@ -346,6 +353,7 @@ public final class DesignerPane extends BorderPane implements AutoCloseable {
|
||||
}
|
||||
});
|
||||
var split = new SplitPane(source, design);
|
||||
sourceSplit = split;
|
||||
split.setDividerPositions(.3);
|
||||
var modes =
|
||||
new ToolBar(
|
||||
@@ -372,13 +380,21 @@ public final class DesignerPane extends BorderPane implements AutoCloseable {
|
||||
repeatTab.setClosable(false);
|
||||
var tableTab = new Tab("Table / Columns", tableEditor);
|
||||
tableTab.setClosable(false);
|
||||
var problemsTab = new Tab("Problems", diagnosticsPane);
|
||||
problemsTab.setClosable(false);
|
||||
var structureTab = new Tab("Structure", referenceEditor);
|
||||
structureTab.setClosable(false);
|
||||
var bindings =
|
||||
new TitledPane(
|
||||
"Bindings",
|
||||
new TabPane(
|
||||
directBindingTab, computedTab, repeatTab, tableTab, structureTab, discoveryTab));
|
||||
directBindingTab,
|
||||
computedTab,
|
||||
repeatTab,
|
||||
tableTab,
|
||||
structureTab,
|
||||
problemsTab,
|
||||
discoveryTab));
|
||||
bindings.setExpanded(false);
|
||||
center.setBottom(new VBox(properties, bindings));
|
||||
output.setEditable(false);
|
||||
@@ -478,6 +494,7 @@ public final class DesignerPane extends BorderPane implements AutoCloseable {
|
||||
.report(
|
||||
new HostServices.Diagnostic(
|
||||
"Designer", HostServices.Severity.ERROR, String.valueOf(error), Optional.empty()));
|
||||
if (draft != null && diagnosticsPane != null) validationUnavailable(error);
|
||||
status.setText("Designer: " + error.getMessage());
|
||||
log(error.toString());
|
||||
}
|
||||
@@ -518,6 +535,7 @@ public final class DesignerPane extends BorderPane implements AutoCloseable {
|
||||
}
|
||||
}
|
||||
draft = value;
|
||||
publishDiagnostics("Source checked; target validation outdated", java.util.List.of());
|
||||
bindingEditor.load(value.text(), value.writable());
|
||||
computedEditor.load(value.text(), value.writable());
|
||||
repeatEditor.load(value.text(), value.writable());
|
||||
@@ -544,6 +562,7 @@ public final class DesignerPane extends BorderPane implements AutoCloseable {
|
||||
var hit = kit.editor().getSelection().getHitItem();
|
||||
String selectedId = hit == null ? null : hit.getFxId();
|
||||
draft = value;
|
||||
publishDiagnostics("Source checked; target validation outdated", java.util.List.of());
|
||||
bindingEditor.load(value.text(), value.writable());
|
||||
computedEditor.load(value.text(), value.writable());
|
||||
repeatEditor.load(value.text(), value.writable());
|
||||
@@ -614,6 +633,11 @@ public final class DesignerPane extends BorderPane implements AutoCloseable {
|
||||
report(error);
|
||||
} finally {
|
||||
loading = false;
|
||||
if (pendingSourceOffset != null) {
|
||||
int offset = pendingSourceOffset;
|
||||
pendingSourceOffset = null;
|
||||
showSource(offset);
|
||||
}
|
||||
if (value.text().equals(pendingReferenceValidation)) {
|
||||
pendingReferenceValidation = null;
|
||||
Platform.runLater(() -> validateBindings(null, false));
|
||||
@@ -700,6 +724,9 @@ public final class DesignerPane extends BorderPane implements AutoCloseable {
|
||||
|
||||
private void invalidateBindings(String message) {
|
||||
bindingRequest++;
|
||||
host.diagnostics().clear(diagnosticsOwner);
|
||||
if (diagnosticsPane != null) diagnosticsPane.show(java.util.List.of(), message);
|
||||
diagnosticsRevision = -1;
|
||||
validatedSource = null;
|
||||
validatingSource = null;
|
||||
validationPreview = false;
|
||||
@@ -709,6 +736,66 @@ public final class DesignerPane extends BorderPane implements AutoCloseable {
|
||||
tableEditor.invalidateDiscovery();
|
||||
}
|
||||
|
||||
private void publishDiagnostics(String context, java.util.List<HostServices.Diagnostic> target) {
|
||||
if (closed || draft == null) return;
|
||||
var all = new java.util.LinkedHashSet<>(FxmlDiagnostics.local(draft));
|
||||
all.addAll(target);
|
||||
var diagnostics = java.util.List.copyOf(all);
|
||||
diagnosticsRevision = draft.revision();
|
||||
String label = context + " · draft revision " + draft.revision();
|
||||
diagnosticsPane.show(diagnostics, label);
|
||||
host.diagnostics().replace(diagnosticsOwner, draft, label, diagnostics);
|
||||
}
|
||||
|
||||
private void validationUnavailable(Throwable error) {
|
||||
publishDiagnostics(
|
||||
"Validation unavailable; results are not a clean bill of health",
|
||||
java.util.List.of(
|
||||
new HostServices.Diagnostic(
|
||||
"Designer",
|
||||
HostServices.Severity.ERROR,
|
||||
String.valueOf(error.getMessage()),
|
||||
Optional.of(new HostServices.Location(draft.document(), 0)))));
|
||||
}
|
||||
|
||||
private void navigateDiagnostic(HostServices.Diagnostic problem) {
|
||||
long revision = diagnosticsRevision;
|
||||
withDraft(
|
||||
() -> {
|
||||
if (revision != draft.revision()) {
|
||||
publishDiagnostics(
|
||||
"Document changed; validate again before navigating", java.util.List.of());
|
||||
return;
|
||||
}
|
||||
problem
|
||||
.location()
|
||||
.ifPresent(
|
||||
location -> {
|
||||
if (location.document().equals(draft.document())) showSource(location.offset());
|
||||
else
|
||||
host.navigation()
|
||||
.openSource(location)
|
||||
.exceptionally(
|
||||
error -> {
|
||||
fx(() -> report(error));
|
||||
return null;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/** Select authoritative FXML source, preserving the host-owned draft and undo history. */
|
||||
public void showSource(int offset) {
|
||||
if (closed) return;
|
||||
if (draft == null) {
|
||||
pendingSourceOffset = offset;
|
||||
return;
|
||||
}
|
||||
sourceSplit.getItems().setAll(source);
|
||||
source.positionCaret(Math.min(Math.max(0, offset), source.getLength()));
|
||||
source.requestFocus();
|
||||
}
|
||||
|
||||
private void editStructure(ReferenceEditor.Intent intent) {
|
||||
withDraft(
|
||||
() -> {
|
||||
@@ -796,6 +883,7 @@ public final class DesignerPane extends BorderPane implements AutoCloseable {
|
||||
var captured = draft;
|
||||
validatingSource = captured.text();
|
||||
validationPreview |= preview;
|
||||
publishDiagnostics("Validating target types…", java.util.List.of());
|
||||
status.setText("Validating binding types in target runtime…");
|
||||
bindingWorkflow
|
||||
.bindingDetails(
|
||||
@@ -820,12 +908,18 @@ public final class DesignerPane extends BorderPane implements AutoCloseable {
|
||||
boolean resumePreview = validationPreview;
|
||||
validationPreview = false;
|
||||
if (error != null) {
|
||||
validationUnavailable(error);
|
||||
status.setText(
|
||||
"Binding validation unavailable: " + error.getMessage());
|
||||
return;
|
||||
}
|
||||
validatedSource = captured.text();
|
||||
bindingProblems = metadata.problems();
|
||||
publishDiagnostics(
|
||||
last
|
||||
? "Explicit retained build (outdated)"
|
||||
: "Current successful build",
|
||||
FxmlDiagnostics.target(captured, metadata.problems()));
|
||||
bindingEditor.metadata(
|
||||
metadata,
|
||||
values,
|
||||
@@ -888,6 +982,7 @@ public final class DesignerPane extends BorderPane implements AutoCloseable {
|
||||
boolean resumePreview = validationPreview;
|
||||
validationPreview = false;
|
||||
if (error != null) {
|
||||
validationUnavailable(error);
|
||||
bindingBrowser.invalidate(
|
||||
"Discovery unavailable: "
|
||||
+ error.getMessage()
|
||||
@@ -896,6 +991,11 @@ public final class DesignerPane extends BorderPane implements AutoCloseable {
|
||||
}
|
||||
validatedSource = captured.text();
|
||||
bindingProblems = metadata.problems();
|
||||
publishDiagnostics(
|
||||
last
|
||||
? "Explicit retained build (outdated)"
|
||||
: "Current successful build",
|
||||
FxmlDiagnostics.target(captured, metadata.problems()));
|
||||
bindingBrowser.show(
|
||||
metadata,
|
||||
(last
|
||||
@@ -1278,6 +1378,7 @@ public final class DesignerPane extends BorderPane implements AutoCloseable {
|
||||
public void close() {
|
||||
if (closed) return;
|
||||
closed = true;
|
||||
host.diagnostics().clear(diagnosticsOwner);
|
||||
try {
|
||||
changes.close();
|
||||
saves.close();
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package de.neitzel.fxdesigner.kit;
|
||||
|
||||
import de.neitzel.fxdesigner.host.HostServices.Diagnostic;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
/** One shared, navigable diagnostic list for both hosts. */
|
||||
final class DiagnosticsPane extends VBox {
|
||||
private final ListView<Diagnostic> problems = new ListView<>();
|
||||
private final Label state = new Label("Target validation not run");
|
||||
|
||||
DiagnosticsPane(Runnable validate, Consumer<Diagnostic> navigate) {
|
||||
super(5);
|
||||
problems.setId("diagnostics-list");
|
||||
problems.setPrefHeight(110);
|
||||
state.setId("diagnostics-state");
|
||||
state.setWrapText(true);
|
||||
problems.setCellFactory(
|
||||
v ->
|
||||
new ListCell<>() {
|
||||
@Override
|
||||
protected void updateItem(Diagnostic d, boolean empty) {
|
||||
super.updateItem(d, empty);
|
||||
setText(
|
||||
empty || d == null
|
||||
? null
|
||||
: d.severity() + " · " + d.operation() + " · " + d.message());
|
||||
}
|
||||
});
|
||||
Runnable open =
|
||||
() -> {
|
||||
var d = problems.getSelectionModel().getSelectedItem();
|
||||
if (d != null) navigate.accept(d);
|
||||
};
|
||||
problems.setOnMouseClicked(
|
||||
e -> {
|
||||
if (e.getClickCount() == 2) open.run();
|
||||
});
|
||||
problems.setOnKeyPressed(
|
||||
e -> {
|
||||
if (e.getCode() == KeyCode.ENTER) {
|
||||
open.run();
|
||||
e.consume();
|
||||
}
|
||||
});
|
||||
var check = new Button("Validate View");
|
||||
check.setOnAction(e -> validate.run());
|
||||
var go = new Button("Go to FXML");
|
||||
go.setOnAction(e -> open.run());
|
||||
go.disableProperty().bind(problems.getSelectionModel().selectedItemProperty().isNull());
|
||||
getChildren().addAll(new javafx.scene.layout.HBox(6, check, go), state, problems);
|
||||
}
|
||||
|
||||
void show(List<Diagnostic> diagnostics, String context) {
|
||||
problems.getItems().setAll(diagnostics);
|
||||
state.setText(context + " · " + diagnostics.size() + " diagnostics");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package de.neitzel.fxdesigner.kit;
|
||||
|
||||
import de.neitzel.fxdesigner.contracts.DocumentSnapshot;
|
||||
import de.neitzel.fxdesigner.host.HostServices.*;
|
||||
import de.neitzel.fxdesigner.protocol.BindingMetadata.Problem;
|
||||
import java.util.*;
|
||||
import org.w3c.dom.Element;
|
||||
import org.xml.sax.SAXParseException;
|
||||
|
||||
/** Maps shared validation evidence to the authoritative FXML, never to the Kit projection. */
|
||||
final class FxmlDiagnostics {
|
||||
static List<Diagnostic> local(DocumentSnapshot source) {
|
||||
var messages = new LinkedHashSet<String>();
|
||||
int offset = 0;
|
||||
try {
|
||||
var xml = new BindingDocument(source.text());
|
||||
offset = xml.source.tags.getFirst().start();
|
||||
messages.addAll(xml.errors());
|
||||
messages.addAll(new TableDocument(source.text()).errors());
|
||||
messages.addAll(new ReferenceDocument(source.text()).referenceErrors());
|
||||
} catch (Exception e) {
|
||||
if (e instanceof SAXParseException parse)
|
||||
offset = offset(source.text(), parse.getLineNumber() - 1, parse.getColumnNumber() - 1);
|
||||
messages.add(String.valueOf(e.getMessage()));
|
||||
}
|
||||
int origin = offset;
|
||||
return messages.stream().map(m -> diagnostic(source, "FXML", "ERROR", m, origin)).toList();
|
||||
}
|
||||
|
||||
static List<Diagnostic> target(DocumentSnapshot source, List<Problem> problems) {
|
||||
var result = new LinkedHashSet<Diagnostic>();
|
||||
try {
|
||||
var xml = new BindingDocument(source.text());
|
||||
for (var p : problems) {
|
||||
var matches = new ArrayList<Integer>();
|
||||
for (int i = 0; i < xml.elements.size(); i++) {
|
||||
var e = xml.elements.get(i);
|
||||
if ((xml.named(e, "DirectBinding") || xml.named(e, "ComputedBinding"))
|
||||
&& property(e, "control").equals(p.control())
|
||||
&& property(e, "property").equals(p.property())) matches.add(i);
|
||||
else if (xml.named(e, "Repeat")
|
||||
&& p.property().equals("children")
|
||||
&& property(e, "pane").equals(p.control())) matches.add(i);
|
||||
}
|
||||
if (matches.isEmpty()) {
|
||||
for (int i = 0; i < xml.elements.size(); i++) {
|
||||
var e = xml.elements.get(i);
|
||||
if ((!p.control().isEmpty()
|
||||
&& e.getAttributeNS(BindingDocument.FX, "id").equals(p.control()))
|
||||
|| xml.named(e, "Table") && p.control().equals("Table #" + i)) matches.add(i);
|
||||
}
|
||||
}
|
||||
// Unknown or aggregate origins remain at the document root, not a guessed Java/Row
|
||||
// location.
|
||||
if (matches.isEmpty()) matches.add(0);
|
||||
for (int i : matches)
|
||||
result.add(
|
||||
diagnostic(
|
||||
source, "Bindings", p.severity(), p.message(), xml.source.tags.get(i).start()));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return local(source);
|
||||
}
|
||||
return List.copyOf(result);
|
||||
}
|
||||
|
||||
private static String property(Element e, String name) {
|
||||
if (e.hasAttribute(name)) return e.getAttribute(name);
|
||||
for (var n = e.getFirstChild(); n != null; n = n.getNextSibling())
|
||||
if (n instanceof Element child && child.getTagName().equals(name))
|
||||
return child.getTextContent().trim();
|
||||
return "";
|
||||
}
|
||||
|
||||
private static Diagnostic diagnostic(
|
||||
DocumentSnapshot source, String operation, String severity, String message, int offset) {
|
||||
return new Diagnostic(
|
||||
operation,
|
||||
Severity.valueOf(severity),
|
||||
message,
|
||||
Optional.of(new Location(source.document(), offset)));
|
||||
}
|
||||
|
||||
static int offset(String text, int line, int column) {
|
||||
int at = 0;
|
||||
for (int n = 0; n < line && at < text.length(); n++) {
|
||||
int next = text.indexOf('\n', at);
|
||||
at = next < 0 ? text.length() : next + 1;
|
||||
}
|
||||
return Math.min(text.length(), at + Math.max(0, column));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package de.neitzel.fxdesigner.kit;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import de.neitzel.fxdesigner.contracts.DocumentSnapshot;
|
||||
import de.neitzel.fxdesigner.protocol.BindingMetadata.Problem;
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class FxmlDiagnosticsTest {
|
||||
private DocumentSnapshot source(String xml) {
|
||||
return new DocumentSnapshot(URI.create("file:/View.fxml"), 3, xml, true, true);
|
||||
}
|
||||
|
||||
private static final String XML =
|
||||
"<?import de.neitzel.fx.view.*?><VBox xmlns:fx='http://javafx.com/fxml/1'>\n"
|
||||
+ "<fx:define><BindingList><!-- untouched --><DirectBinding control='field'"
|
||||
+ " property='text' path='missing' direction='view'/><Repeat pane='rows'"
|
||||
+ " collection='items' component='app.Row'/></BindingList></fx:define>\n"
|
||||
+ "<Label fx:id='field'/><VBox fx:id='rows'/><Table fx:id='table' collection='items'"
|
||||
+ " row='app.Row'><columns><Column header='N'"
|
||||
+ " attribute='name'/></columns></Table></VBox>";
|
||||
|
||||
@Test
|
||||
void locatesBindingsRepeatAndTableWithoutProjectionIdentities() {
|
||||
var d = source(XML);
|
||||
var result =
|
||||
FxmlDiagnostics.target(
|
||||
d,
|
||||
List.of(
|
||||
new Problem("field", "text", "ERROR", "bad path"),
|
||||
new Problem("rows", "children", "WARNING", "unknown"),
|
||||
new Problem("table", "columns", "ERROR", "bad column")));
|
||||
assertEquals(
|
||||
List.of(XML.indexOf("<DirectBinding"), XML.indexOf("<Repeat"), XML.indexOf("<Table")),
|
||||
result.stream().map(p -> p.location().orElseThrow().offset()).toList());
|
||||
assertTrue(
|
||||
result.stream().allMatch(p -> p.location().orElseThrow().document().equals(d.document())));
|
||||
}
|
||||
|
||||
@Test
|
||||
void ambiguousTargetsIdentifyEveryDeclarationAndDeduplicateEvidence() {
|
||||
String xml =
|
||||
XML.replace(
|
||||
"<!-- untouched -->",
|
||||
"<ComputedBinding control='field' property='text' method='#value'/>");
|
||||
var p = new Problem("field", "text", "ERROR", "duplicate");
|
||||
var result = FxmlDiagnostics.target(source(xml), List.of(p, p));
|
||||
assertEquals(2, result.size());
|
||||
assertEquals(
|
||||
xml.indexOf("<ComputedBinding"), result.getFirst().location().orElseThrow().offset());
|
||||
}
|
||||
|
||||
@Test
|
||||
void unknownOriginsFallBackToRootAndNestedTargetsAreLocated() {
|
||||
String xml =
|
||||
XML.replace(
|
||||
"control='field' property='text' path='missing' direction='view'/>",
|
||||
"path='missing'"
|
||||
+ " direction='view'><control>field</control><property>text</property></DirectBinding>");
|
||||
var result =
|
||||
FxmlDiagnostics.target(
|
||||
source(xml),
|
||||
List.of(
|
||||
new Problem("field", "text", "ERROR", "bad"),
|
||||
new Problem("", "", "WARNING", "context unavailable")));
|
||||
assertEquals(
|
||||
xml.indexOf("<DirectBinding"), result.getFirst().location().orElseThrow().offset());
|
||||
assertEquals(xml.indexOf("<VBox"), result.getLast().location().orElseThrow().offset());
|
||||
}
|
||||
|
||||
@Test
|
||||
void structuralErrorsAndMalformedXmlRemainNavigable() {
|
||||
assertFalse(
|
||||
FxmlDiagnostics.local(source(XML.replace("direction='view'", "direction='wrong'")))
|
||||
.isEmpty());
|
||||
var malformed = FxmlDiagnostics.local(source("<VBox>\n<Label>\n</VBox>"));
|
||||
assertEquals(1, malformed.size());
|
||||
assertTrue(malformed.getFirst().location().orElseThrow().offset() > 0);
|
||||
assertTrue(FxmlDiagnostics.local(source(XML)).isEmpty());
|
||||
}
|
||||
}
|
||||
@@ -691,6 +691,75 @@ public final class ProjectionHostChecks {
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static java.util.List<de.neitzel.fxdesigner.host.HostServices.Diagnostic> diagnostics(
|
||||
DesignerPane pane) throws Exception {
|
||||
return fx(
|
||||
() ->
|
||||
java.util.List.copyOf(
|
||||
((ListView<de.neitzel.fxdesigner.host.HostServices.Diagnostic>)
|
||||
pane.lookup("#diagnostics-list"))
|
||||
.getItems()));
|
||||
}
|
||||
|
||||
private static void diagnosticsEdits(DesignerPane pane, DocumentSession document, String full)
|
||||
throws Exception {
|
||||
String invalid = full.replace("path=\"rank\"", "path=\"diagnosticMissing\"");
|
||||
set(document, invalid);
|
||||
await(() -> fx(() -> source(pane).getText().equals(invalid)));
|
||||
bindingTab(pane, "Problems");
|
||||
click(pane, "Validate View");
|
||||
await(
|
||||
() -> diagnostics(pane).stream().anyMatch(d -> d.message().contains("diagnosticMissing")));
|
||||
var problem =
|
||||
diagnostics(pane).stream()
|
||||
.filter(d -> d.message().contains("diagnosticMissing"))
|
||||
.findFirst()
|
||||
.orElseThrow();
|
||||
int offset = problem.location().orElseThrow().offset();
|
||||
check(
|
||||
invalid.substring(offset).startsWith("<DirectBinding"),
|
||||
"Diagnostic did not locate authoritative binding declaration");
|
||||
fx(
|
||||
() -> {
|
||||
((ListView<de.neitzel.fxdesigner.host.HostServices.Diagnostic>)
|
||||
pane.lookup("#diagnostics-list"))
|
||||
.getSelectionModel()
|
||||
.select(problem);
|
||||
return null;
|
||||
});
|
||||
click(pane, "Go to FXML");
|
||||
await(() -> fx(() -> source(pane).getCaretPosition() == offset));
|
||||
check(documentText(document).equals(invalid), "Diagnostic navigation mutated source");
|
||||
set(document, full);
|
||||
await(() -> fx(() -> source(pane).getText().equals(full)));
|
||||
await(
|
||||
() -> diagnostics(pane).stream().noneMatch(d -> d.message().contains("diagnosticMissing")));
|
||||
repeatHistory(pane, document, invalid, full);
|
||||
bindingTab(pane, "Problems");
|
||||
click(pane, "Validate View");
|
||||
await(
|
||||
() ->
|
||||
fx(
|
||||
() ->
|
||||
((Label) pane.lookup("#diagnostics-state"))
|
||||
.getText()
|
||||
.startsWith("Current successful build")));
|
||||
check(
|
||||
diagnostics(pane).stream()
|
||||
.noneMatch(d -> d.severity() == de.neitzel.fxdesigner.host.HostServices.Severity.ERROR),
|
||||
"Corrected source retained error diagnostics");
|
||||
set(document, "<VBox>\n<Label>\n</VBox>");
|
||||
await(() -> diagnostics(pane).stream().anyMatch(d -> d.operation().equals("FXML")));
|
||||
set(document, full);
|
||||
await(() -> fx(() -> source(pane).getText().equals(full)));
|
||||
await(() -> diagnostics(pane).isEmpty());
|
||||
click(pane, "Split");
|
||||
System.out.println(
|
||||
"PASS shared diagnostics: authoritative binding location, source navigation, stale"
|
||||
+ " withdrawal, correction, exact undo/redo and malformed FXML recovery");
|
||||
}
|
||||
|
||||
private static void structureEdits(DesignerPane pane, DocumentSession document, String full)
|
||||
throws Exception {
|
||||
String base =
|
||||
@@ -1708,6 +1777,7 @@ public final class ProjectionHostChecks {
|
||||
tableDiscovery(pane, document, full);
|
||||
tableEdits(pane, document, full);
|
||||
structureEdits(pane, document, full);
|
||||
diagnosticsEdits(pane, document, full);
|
||||
document
|
||||
.save(document.snapshot().toCompletableFuture().get().revision())
|
||||
.toCompletableFuture()
|
||||
|
||||
Reference in New Issue
Block a user