Highlight error nodes from the event log panel
This commit is contained in:
1 parent
5241ebf3cd
commit
0fd7f282f5
11 files changed
+233
-96
No files matched your search
@@ -56,7 +56,7 @@ public class Designer extends Application {
|
||||
NodeInfoPanelController nodeInfoPanelController = new NodeInfoPanelController(mainController);
|
||||
XPathPanelController xpathPanelController = new XPathPanelController(owner, mainController);
|
||||
SourceEditorController sourceEditorController = new SourceEditorController(owner, mainController);
|
||||
EventLogController eventLogController = new EventLogController(owner);
|
||||
EventLogController eventLogController = new EventLogController(owner, mainController);
|
||||
|
||||
loader.setControllerFactory(type -> {
|
||||
if (type == MainDesignerController.class) {
|
||||
|
||||
@@ -8,16 +8,22 @@ import java.net.URL;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.Duration;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import org.reactfx.EventStream;
|
||||
import org.reactfx.EventStreams;
|
||||
import org.reactfx.value.Var;
|
||||
|
||||
import net.sourceforge.pmd.lang.ast.Node;
|
||||
import net.sourceforge.pmd.util.fxdesigner.model.LogEntry;
|
||||
import net.sourceforge.pmd.util.fxdesigner.model.LogEntry.Category;
|
||||
import net.sourceforge.pmd.util.fxdesigner.util.DesignerUtil;
|
||||
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.control.TableCell;
|
||||
@@ -41,6 +47,7 @@ public class EventLogController implements Initializable {
|
||||
private static final Duration PARSE_EXCEPTION_DELAY = Duration.ofMillis(3000);
|
||||
|
||||
private final DesignerRoot designerRoot;
|
||||
private final MainDesignerController mediator;
|
||||
|
||||
@FXML
|
||||
private TableView<LogEntry> eventLogTableView;
|
||||
@@ -53,9 +60,12 @@ public class EventLogController implements Initializable {
|
||||
@FXML
|
||||
private TextArea logDetailsTextArea;
|
||||
|
||||
private Var<List<Node>> selectedErrorNodes = Var.newSimpleVar(Collections.emptyList());
|
||||
|
||||
public EventLogController(DesignerRoot owner) {
|
||||
|
||||
public EventLogController(DesignerRoot owner, MainDesignerController mediator) {
|
||||
this.designerRoot = owner;
|
||||
this.mediator = mediator;
|
||||
}
|
||||
|
||||
|
||||
@@ -64,8 +74,7 @@ public class EventLogController implements Initializable {
|
||||
logCategoryColumn.setCellValueFactory(new PropertyValueFactory<>("category"));
|
||||
logMessageColumn.setCellValueFactory(new PropertyValueFactory<>("message"));
|
||||
final DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
|
||||
logDateColumn.setCellValueFactory(
|
||||
entry -> new SimpleObjectProperty<>(entry.getValue().getTimestamp()));
|
||||
logDateColumn.setCellValueFactory(entry -> new SimpleObjectProperty<>(entry.getValue().getTimestamp()));
|
||||
logDateColumn.setCellFactory(column -> new TableCell<LogEntry, Date>() {
|
||||
@Override
|
||||
protected void updateItem(Date item, boolean empty) {
|
||||
@@ -96,12 +105,25 @@ public class EventLogController implements Initializable {
|
||||
|
||||
eventLogTableView.getSelectionModel()
|
||||
.selectedItemProperty()
|
||||
.addListener((obs, oldVal, newVal) -> logDetailsTextArea.setText(
|
||||
newVal == null ? "" : newVal.getStackTrace()));
|
||||
.addListener(this::onExceptionSelectionChanges);
|
||||
|
||||
EventStreams.combine(EventStreams.changesOf(eventLogTableView.focusedProperty()),
|
||||
EventStreams.changesOf(selectedErrorNodes));
|
||||
|
||||
EventStreams.valuesOf(eventLogTableView.focusedProperty())
|
||||
.successionEnds(Duration.ofMillis(100))
|
||||
.subscribe(b -> {
|
||||
if (b) {
|
||||
mediator.handleSelectedNodeInError(selectedErrorNodes.getValue());
|
||||
} else {
|
||||
mediator.resetSelectedErrorNodes();
|
||||
}
|
||||
});
|
||||
|
||||
selectedErrorNodes.values().subscribe(mediator::handleSelectedNodeInError);
|
||||
|
||||
eventLogTableView.resizeColumn(logMessageColumn, -1);
|
||||
|
||||
|
||||
logMessageColumn.prefWidthProperty()
|
||||
.bind(eventLogTableView.widthProperty()
|
||||
.subtract(logCategoryColumn.getPrefWidth())
|
||||
@@ -110,4 +132,37 @@ public class EventLogController implements Initializable {
|
||||
logDateColumn.setSortType(SortType.DESCENDING);
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void handleSelectedEntry(LogEntry entry) {
|
||||
if (entry == null) {
|
||||
selectedErrorNodes.setValue(Collections.emptyList());
|
||||
return;
|
||||
}
|
||||
switch (entry.getCategory()) {
|
||||
case OTHER:
|
||||
break;
|
||||
case PARSE_EXCEPTION:
|
||||
// TODO
|
||||
break;
|
||||
case TYPERESOLUTION_EXCEPTION:
|
||||
case SYMBOL_FACADE_EXCEPTION:
|
||||
DesignerUtil.stackTraceToXPath(entry.getThrown()).map(mediator::runXPathQuery).ifPresent(selectedErrorNodes::setValue);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void onExceptionSelectionChanges(ObservableValue<? extends LogEntry> obs, LogEntry oldVal, LogEntry newVal) {
|
||||
if (newVal != null) {
|
||||
logDetailsTextArea.setText(newVal.getStackTrace());
|
||||
} else {
|
||||
logDetailsTextArea.clear();
|
||||
}
|
||||
if (newVal != oldVal) {
|
||||
handleSelectedEntry(newVal);
|
||||
}
|
||||
}
|
||||
}
|
||||
+39
-14
@@ -12,9 +12,9 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.Stack;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.reactfx.value.Val;
|
||||
@@ -22,7 +22,7 @@ import org.reactfx.value.Val;
|
||||
import net.sourceforge.pmd.lang.LanguageVersion;
|
||||
import net.sourceforge.pmd.lang.ast.Node;
|
||||
import net.sourceforge.pmd.lang.symboltable.NameDeclaration;
|
||||
import net.sourceforge.pmd.lang.symboltable.NameOccurrence;
|
||||
import net.sourceforge.pmd.util.fxdesigner.model.XPathEvaluationException;
|
||||
import net.sourceforge.pmd.util.fxdesigner.util.DesignerUtil;
|
||||
import net.sourceforge.pmd.util.fxdesigner.util.LimitedSizeStack;
|
||||
import net.sourceforge.pmd.util.fxdesigner.util.beans.SettingsOwner;
|
||||
@@ -245,24 +245,49 @@ public class MainDesignerController implements Initializable, SettingsOwner {
|
||||
*/
|
||||
public void onNodeItemSelected(Node selectedValue) {
|
||||
nodeInfoPanelController.displayInfo(selectedValue);
|
||||
// The following line causes problems, since it wipes out the name occurrence highlighting,
|
||||
// but it's already fixed in a PR to come soon
|
||||
sourceEditorController.clearNodeHighlight();
|
||||
sourceEditorController.highlightNodePrimary(selectedValue);
|
||||
sourceEditorController.setFocusNode(selectedValue);
|
||||
sourceEditorController.focusNodeInTreeView(selectedValue);
|
||||
}
|
||||
|
||||
|
||||
public void onNameDeclarationSelected(NameDeclaration declaration) {
|
||||
Platform.runLater(() -> onNodeItemSelected(declaration.getNode()));
|
||||
sourceEditorController.clearSecondaryHighlight();
|
||||
|
||||
List<NameOccurrence> occ = declaration.getNode().getScope().getDeclarations().get(declaration);
|
||||
if (occ != null) {
|
||||
sourceEditorController.highlightNodesSecondary(occ.stream()
|
||||
.map(NameOccurrence::getLocation)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
Optional.ofNullable(declaration.getNode().getScope().getDeclarations().get(declaration))
|
||||
.ifPresent(sourceEditorController::highlightNameOccurences);
|
||||
|
||||
sourceEditorController.setFocusNode(declaration.getNode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs an XPath (2.0) query on the current AST.
|
||||
* Performs no side effects.
|
||||
*
|
||||
* @param query the query
|
||||
* @return the matched nodes
|
||||
* @throws XPathEvaluationException if the query fails
|
||||
*/
|
||||
public List<Node> runXPathQuery(String query) throws XPathEvaluationException {
|
||||
return xpathPanelController.runXPathQuery(sourceEditorController.getCompilationUnit(),
|
||||
getLanguageVersion(), query);
|
||||
}
|
||||
|
||||
// TODO consider using a messenger pattern instead of mediator
|
||||
|
||||
/**
|
||||
* Handles nodes that potentially caused an error.
|
||||
* This can for example highlight nodes on the
|
||||
* editor. Effects can be reset with {@link #resetSelectedErrorNodes()}.
|
||||
*
|
||||
* @param n Node
|
||||
*/
|
||||
public void handleSelectedNodeInError(List<Node> n) {
|
||||
resetSelectedErrorNodes();
|
||||
sourceEditorController.highlightErrorNodes(n);
|
||||
}
|
||||
|
||||
public void resetSelectedErrorNodes() {
|
||||
sourceEditorController.clearSecondaryHighlight();
|
||||
}
|
||||
|
||||
|
||||
@@ -355,7 +380,7 @@ public class MainDesignerController implements Initializable, SettingsOwner {
|
||||
public void invalidateAst() {
|
||||
nodeInfoPanelController.invalidateInfo();
|
||||
xpathPanelController.invalidateResults(false);
|
||||
sourceEditorController.clearNodeHighlight();
|
||||
sourceEditorController.clearFocusHighlight();
|
||||
}
|
||||
|
||||
|
||||
|
||||
+31
-14
@@ -15,7 +15,6 @@ import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@@ -27,6 +26,7 @@ import org.reactfx.value.Var;
|
||||
import net.sourceforge.pmd.lang.Language;
|
||||
import net.sourceforge.pmd.lang.LanguageVersion;
|
||||
import net.sourceforge.pmd.lang.ast.Node;
|
||||
import net.sourceforge.pmd.lang.symboltable.NameOccurrence;
|
||||
import net.sourceforge.pmd.util.ClasspathClassLoader;
|
||||
import net.sourceforge.pmd.util.fxdesigner.model.ASTManager;
|
||||
import net.sourceforge.pmd.util.fxdesigner.model.ParseAbortedException;
|
||||
@@ -35,6 +35,7 @@ import net.sourceforge.pmd.util.fxdesigner.util.beans.SettingsOwner;
|
||||
import net.sourceforge.pmd.util.fxdesigner.util.beans.SettingsPersistenceUtil.PersistentProperty;
|
||||
import net.sourceforge.pmd.util.fxdesigner.util.codearea.AvailableSyntaxHighlighters;
|
||||
import net.sourceforge.pmd.util.fxdesigner.util.codearea.CustomCodeArea;
|
||||
import net.sourceforge.pmd.util.fxdesigner.util.codearea.CustomCodeArea.LayerId;
|
||||
import net.sourceforge.pmd.util.fxdesigner.util.codearea.SyntaxHighlighter;
|
||||
import net.sourceforge.pmd.util.fxdesigner.util.controls.ASTTreeCell;
|
||||
import net.sourceforge.pmd.util.fxdesigner.util.controls.ASTTreeItem;
|
||||
@@ -76,8 +77,8 @@ public class SourceEditorController implements Initializable, SettingsOwner {
|
||||
return new ClasspathClassLoader(fileList, SourceEditorController.class.getClassLoader());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return SourceEditorController.class.getClassLoader();
|
||||
}
|
||||
return SourceEditorController.class.getClassLoader();
|
||||
});
|
||||
|
||||
public SourceEditorController(DesignerRoot owner, MainDesignerController mainController) {
|
||||
@@ -185,34 +186,50 @@ public class SourceEditorController implements Initializable, SettingsOwner {
|
||||
}
|
||||
|
||||
|
||||
public void clearNodeHighlight() {
|
||||
codeEditorArea.clearPrimaryStyleLayer();
|
||||
/** Clears the focus node highlight. */
|
||||
public void clearFocusHighlight() {
|
||||
codeEditorArea.clearStyleLayer(LayerId.FOCUS);
|
||||
}
|
||||
|
||||
|
||||
public void highlightNodePrimary(Node node) {
|
||||
highlightNodes(Collections.singleton(node), Collections.singleton("primary-highlight"));
|
||||
/** Clears the secondary highlight. Doesn't clear the primary focus.. */
|
||||
public void clearSecondaryHighlight() {
|
||||
codeEditorArea.clearStyleLayer(LayerId.SECONDARY);
|
||||
}
|
||||
|
||||
|
||||
private void highlightNodes(Collection<? extends Node> nodes, Set<String> cssClasses) {
|
||||
/** Highlights the given node. Removes highlighting on the previously highlighted node. */
|
||||
public void setFocusNode(Node node) {
|
||||
clearFocusHighlight();
|
||||
highlightNodes(Collections.singleton(node), LayerId.FOCUS);
|
||||
}
|
||||
|
||||
|
||||
/** Highlights name occurences (secondary highlight). */
|
||||
public void highlightNameOccurences(Collection<? extends NameOccurrence> occs) {
|
||||
clearSecondaryHighlight();
|
||||
highlightNodes(occs.stream().map(NameOccurrence::getLocation).collect(Collectors.toList()), LayerId.SECONDARY, "name-occurence");
|
||||
}
|
||||
|
||||
|
||||
/** Highlights nodes that are in error (secondary highlight). */
|
||||
public void highlightErrorNodes(Collection<? extends Node> nodes) {
|
||||
clearSecondaryHighlight();
|
||||
highlightNodes(nodes, LayerId.SECONDARY, "error-highlight");
|
||||
}
|
||||
|
||||
private void highlightNodes(Collection<? extends Node> nodes, LayerId layer, String... cssClasses) {
|
||||
for (Node node : nodes) {
|
||||
if (codeEditorArea.isInRange(node)) {
|
||||
codeEditorArea.styleCss(node, cssClasses);
|
||||
codeEditorArea.styleCss(node, layer, cssClasses);
|
||||
codeEditorArea.paintCss();
|
||||
codeEditorArea.moveTo(node.getBeginLine() - 1, 0);
|
||||
codeEditorArea.requestFollowCaret();
|
||||
} else {
|
||||
codeEditorArea.clearPrimaryStyleLayer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void highlightNodesSecondary(Collection<? extends Node> nodes) {
|
||||
highlightNodes(nodes, Collections.singleton("secondary-highlight"));
|
||||
}
|
||||
|
||||
public void focusNodeInTreeView(Node node) {
|
||||
SelectionModel<TreeItem<Node>> selectionModel = astTreeView.getSelectionModel();
|
||||
|
||||
|
||||
@@ -186,7 +186,10 @@ public class XPathPanelController implements Initializable, SettingsOwner {
|
||||
|
||||
|
||||
/**
|
||||
* Evaluate XPath on the given compilation unit.
|
||||
* Evaluate the contents of the XPath expression area
|
||||
* on the given compilation unit. This updates the xpath
|
||||
* result panel, and can log XPath exceptions to the
|
||||
* event log panel.
|
||||
*
|
||||
* @param compilationUnit The AST root
|
||||
* @param version The language version
|
||||
@@ -220,6 +223,11 @@ public class XPathPanelController implements Initializable, SettingsOwner {
|
||||
}
|
||||
|
||||
|
||||
public List<Node> runXPathQuery(Node compilationUnit, LanguageVersion version, String query) throws XPathEvaluationException {
|
||||
return xpathEvaluator.evaluateQuery(compilationUnit, version, "2.0", query, ruleBuilder.getRuleProperties());
|
||||
}
|
||||
|
||||
|
||||
public void invalidateResults(boolean error) {
|
||||
xpathResultListView.getItems().clear();
|
||||
violationsTitledPane.setText("Matched nodes" + (error ? "\t(error)" : ""));
|
||||
|
||||
@@ -29,6 +29,10 @@ public class LogEntry {
|
||||
}
|
||||
|
||||
|
||||
public Throwable getThrown() {
|
||||
return throwable;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return throwable.getMessage();
|
||||
}
|
||||
|
||||
@@ -30,7 +30,8 @@ public class XPathEvaluator {
|
||||
|
||||
|
||||
/**
|
||||
* Evaluates an XPath query on the compilation unit.
|
||||
* Evaluates an XPath query on the compilation unit. Performs
|
||||
* no side effects.
|
||||
*
|
||||
* @param compilationUnit AST root
|
||||
* @param languageVersion language version
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
package net.sourceforge.pmd.util.fxdesigner.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
@@ -214,4 +216,21 @@ public final class DesignerUtil {
|
||||
return lines.isEmpty() ? Optional.empty() : Optional.of("//" + String.join("/", lines));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Works out an xpath query that matches the node
|
||||
* which was being visited during the failure.
|
||||
*
|
||||
* @param e Exception
|
||||
*
|
||||
* @return A query, if possible.
|
||||
*
|
||||
* @see #stackTraceToXPath(String)
|
||||
*/
|
||||
public static Optional<String> stackTraceToXPath(Throwable e) {
|
||||
|
||||
StringWriter writer = new StringWriter();
|
||||
e.printStackTrace(new PrintWriter(writer));
|
||||
return stackTraceToXPath(writer.toString());
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -46,7 +46,7 @@ public class RestorePropertyVisitor extends BeanNodeVisitor<SettingsOwner> {
|
||||
}
|
||||
|
||||
Map<String, PropertyDescriptor> descriptors = Arrays.stream(PropertyUtils.getPropertyDescriptors(target))
|
||||
.filter(d -> d.getReadMethod().isAnnotationPresent(PersistentProperty.class))
|
||||
.filter(d -> d.getReadMethod() != null && d.getReadMethod().isAnnotationPresent(PersistentProperty.class))
|
||||
.collect(Collectors.toMap(PropertyDescriptor::getName, d -> d));
|
||||
|
||||
for (Entry<String, Object> saved : model.getSettingsValues().entrySet()) {
|
||||
|
||||
+51
-56
@@ -4,7 +4,12 @@
|
||||
|
||||
package net.sourceforge.pmd.util.fxdesigner.util.codearea;
|
||||
|
||||
import static net.sourceforge.pmd.util.fxdesigner.util.codearea.CustomCodeArea.LayerId.FOCUS;
|
||||
import static net.sourceforge.pmd.util.fxdesigner.util.codearea.CustomCodeArea.LayerId.SECONDARY;
|
||||
import static net.sourceforge.pmd.util.fxdesigner.util.codearea.CustomCodeArea.LayerId.XPATH_RESULTS;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
@@ -27,10 +32,12 @@ import javafx.concurrent.Task;
|
||||
|
||||
/**
|
||||
* Code area that can handle syntax highlighting as well as regular node highlighting. Regular node highlighting is
|
||||
* handled in the "primary" {@link StyleLayer}, which you can affect with {@link #styleCss(Node, Set)}, {@link
|
||||
* #clearPrimaryStyleLayer()} and the like. Syntax highlighting uses another internal style layer. Syntax highlighting
|
||||
* is performed asynchronously by another thread. You must shut down the executor gracefully by calling {@link
|
||||
* #disableSyntaxHighlighting()} before exiting the application.
|
||||
* handled in several {@link StyleLayer}s, which you can affect with {@link #styleCss(Node, LayerId, String...)},
|
||||
* {@link #clearStyleLayer(LayerId)} and the like. Highlighting
|
||||
*
|
||||
* <p>Syntax highlighting uses another internal style layer. Syntax highlighting
|
||||
* is performed asynchronously by another thread. You must shut down the executor
|
||||
* gracefully by calling {@link #disableSyntaxHighlighting()} before exiting the application.
|
||||
*
|
||||
* @author Clément Fournier
|
||||
* @since 6.0.0
|
||||
@@ -38,7 +45,7 @@ import javafx.concurrent.Task;
|
||||
public class CustomCodeArea extends CodeArea {
|
||||
|
||||
private static final String SYNTAX_HIGHLIGHT_LAYER_ID = "syntax";
|
||||
private static final String PRIMARY_HIGHLIGHT_LAYER_ID = "primary";
|
||||
|
||||
private ExecutorService executorService;
|
||||
private Subscription syntaxAutoRefresh;
|
||||
private BooleanProperty isSyntaxHighlightingEnabled = new SimpleBooleanProperty(false);
|
||||
@@ -49,54 +56,40 @@ public class CustomCodeArea extends CodeArea {
|
||||
public CustomCodeArea() {
|
||||
super();
|
||||
styleContext = new StyleContext(this);
|
||||
styleContext.addLayer(PRIMARY_HIGHLIGHT_LAYER_ID, new StyleLayer(PRIMARY_HIGHLIGHT_LAYER_ID, this));
|
||||
styleContext.addLayer(XPATH_RESULTS.id, new StyleLayer(XPATH_RESULTS.id, this));
|
||||
styleContext.addLayer(SYNTAX_HIGHLIGHT_LAYER_ID, new StyleLayer(SYNTAX_HIGHLIGHT_LAYER_ID, this));
|
||||
styleContext.addLayer(FOCUS.id, new StyleLayer(FOCUS.id, this));
|
||||
styleContext.addLayer(SECONDARY.id, new StyleLayer(SECONDARY.id, this));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Styles the region delimited by the coordinates with the given css classes. Should be followed by a call to {@link
|
||||
* #paintCss()} to update the visual appearance.
|
||||
* Styles the node in the given layer.
|
||||
*
|
||||
* @param beginLine Begin line
|
||||
* @param beginColumn Begin column
|
||||
* @param endLine End line
|
||||
* @param endColumn End column
|
||||
* @param cssClasses The css classes to apply
|
||||
*
|
||||
* @throws IllegalArgumentException if the region identified by the coordinates is out of bounds
|
||||
* <p>The focus layer is meant for the node in primary focus, in contrast
|
||||
* with the secondary layer, which highlights some nodes that are related
|
||||
* to a specific selection (eg error nodes correspond to an error, name
|
||||
* occurrences correspond to a name declaration). The XPath result layer
|
||||
* highlights nodes that are independent from any selection (they depend
|
||||
* on the xpath results).
|
||||
* @param node node to style
|
||||
* @param layerId Layer id
|
||||
* @param cssClasses css classes to apply
|
||||
*/
|
||||
public void styleCss(int beginLine, int beginColumn, int endLine, int endColumn, Set<String> cssClasses) {
|
||||
Set<String> fullClasses = new HashSet<>(cssClasses);
|
||||
public void styleCss(Node node, LayerId layerId, String... cssClasses) {
|
||||
Set<String> fullClasses = new HashSet<>(Arrays.asList(cssClasses));
|
||||
fullClasses.add("text");
|
||||
fullClasses.add("styled-text-area");
|
||||
styleContext.getLayer(PRIMARY_HIGHLIGHT_LAYER_ID).style(beginLine, beginColumn, endLine, endColumn, fullClasses);
|
||||
fullClasses.add(layerId.id + "-highlight"); // focus-highlight, xpath-highlight, secondary-highlight
|
||||
styleContext.getLayer(layerId.id).style(node.getBeginLine(), node.getBeginColumn(), node.getEndLine(), node.getEndColumn(), fullClasses);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Styles the node's position with the given css classes.
|
||||
* Clears a style layer.
|
||||
*
|
||||
* @param node The node to style
|
||||
* @param cssClasses The css classes to apply
|
||||
*
|
||||
* @throws IllegalArgumentException if the node's coordinates are out of bounds
|
||||
* @param id layer id.
|
||||
*/
|
||||
public void styleCss(Node node, Set<String> cssClasses) {
|
||||
this.styleCss(node.getBeginLine(), node.getBeginColumn(), node.getEndLine(), node.getEndColumn(), cssClasses);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Replaces the styling of the primary layer by styling the node's position with the given css classes.
|
||||
*
|
||||
* @param node The node to style
|
||||
* @param cssClasses The css classes to apply
|
||||
*
|
||||
* @throws IllegalArgumentException if the node's coordinates are out of bounds
|
||||
*/
|
||||
public void restylePrimaryStyleLayer(Node node, Set<String> cssClasses) {
|
||||
clearPrimaryStyleLayer();
|
||||
styleCss(node, cssClasses);
|
||||
public void clearStyleLayer(LayerId id) {
|
||||
styleContext.getLayer(id.id).clearStyles();
|
||||
}
|
||||
|
||||
|
||||
@@ -109,18 +102,11 @@ public class CustomCodeArea extends CodeArea {
|
||||
*/
|
||||
public boolean isInRange(Node n) {
|
||||
return n.getEndLine() <= getParagraphs().size()
|
||||
&& (n.getEndLine() != getParagraphs().size()
|
||||
|| n.getEndColumn() <= getParagraph(n.getEndLine() - 1).length());
|
||||
&& (n.getEndLine() != getParagraphs().size()
|
||||
|| n.getEndColumn() <= getParagraph(n.getEndLine() - 1).length());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clears the primary style layer from its contents.
|
||||
*/
|
||||
public void clearPrimaryStyleLayer() {
|
||||
styleContext.getLayer(PRIMARY_HIGHLIGHT_LAYER_ID).clearStyles();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clears all style layers from their contents.
|
||||
@@ -198,12 +184,6 @@ public class CustomCodeArea extends CodeArea {
|
||||
isSyntaxHighlightingEnabled.set(true);
|
||||
Objects.requireNonNull(newHighlighter, "The syntax highlighting highlighter cannot be null");
|
||||
|
||||
StyleLayer syntaxHighlightLayer = styleContext.getLayer(SYNTAX_HIGHLIGHT_LAYER_ID);
|
||||
if (syntaxHighlightLayer == null) {
|
||||
styleContext.addLayer(SYNTAX_HIGHLIGHT_LAYER_ID, new StyleLayer(SYNTAX_HIGHLIGHT_LAYER_ID, this));
|
||||
}
|
||||
|
||||
|
||||
ObservableList<String> styleClasses = this.getStyleClass();
|
||||
if (syntaxHighlighter != null) {
|
||||
styleClasses.remove("." + syntaxHighlighter.getLanguageTerseName());
|
||||
@@ -261,4 +241,19 @@ public class CustomCodeArea extends CodeArea {
|
||||
}
|
||||
|
||||
|
||||
/** Public style layers of the code area. */
|
||||
public enum LayerId {
|
||||
/** For the currently selected node. */
|
||||
FOCUS("focus"),
|
||||
/** For nodes in error, declaration usages. */
|
||||
SECONDARY("secondary"),
|
||||
/** For xpath results. */
|
||||
XPATH_RESULTS("xpath");
|
||||
|
||||
private final String id;
|
||||
|
||||
LayerId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,28 @@
|
||||
/* Presets for the editor theme and syntax highlighting. */
|
||||
|
||||
.styled-text-area .primary-highlight {
|
||||
/* CSS reference of the code area:
|
||||
https://github.com/FXMisc/RichTextFX/wiki/RichTextFX-CSS-Reference-Guide
|
||||
*/
|
||||
|
||||
|
||||
|
||||
.styled-text-area .focus-highlight {
|
||||
-fx-font-weight: bolder !important;
|
||||
-fx-fill: royalblue !important;
|
||||
}
|
||||
|
||||
.styled-text-area .secondary-highlight {
|
||||
.styled-text-area .name-occurence {
|
||||
-fx-font-weight: bolder !important;
|
||||
-fx-fill: lightgreen !important;
|
||||
}
|
||||
|
||||
/* Highlights nodes in error */
|
||||
.styled-text-area .error-highlight {
|
||||
-rtfx-background-color: lightcoral;
|
||||
-fx-font-weight: bolder !important;
|
||||
-fx-fill: darkred !important;
|
||||
}
|
||||
|
||||
/********/
|
||||
/* Base */
|
||||
/*******/
|
||||
|
||||
Reference in new issue
Block a user