Reorganise style context
This commit is contained in:
@ -4,8 +4,13 @@
|
||||
|
||||
package net.sourceforge.pmd.util.fxdesigner.util.codearea;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.fxmisc.richtext.CodeArea;
|
||||
|
||||
@ -15,15 +20,21 @@ import net.sourceforge.pmd.util.fxdesigner.util.DesignerUtil;
|
||||
/**
|
||||
* 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.
|
||||
* #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.
|
||||
*
|
||||
* @author Clément Fournier
|
||||
* @since 6.0.0
|
||||
*/
|
||||
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 boolean isSyntaxHighlightingEnabled;
|
||||
private StyleContext styleContext;
|
||||
private SyntaxHighlighter highlightingComputer;
|
||||
|
||||
|
||||
public CustomCodeArea() {
|
||||
@ -70,20 +81,14 @@ public class CustomCodeArea extends CodeArea {
|
||||
|
||||
|
||||
public void setSyntaxHighlightingEnabled(SyntaxHighlighter computer) {
|
||||
styleContext.setSyntaxHighlighting(computer);
|
||||
this.setSyntaxHighlighting(computer);
|
||||
this.replaceText(0, 0, " ");
|
||||
this.undo();
|
||||
}
|
||||
|
||||
|
||||
public boolean isSyntaxHighlightingEnabled() {
|
||||
return styleContext.isSyntaxHighlightingEnabled();
|
||||
}
|
||||
|
||||
|
||||
public void disableSyntaxHighlighting() {
|
||||
styleContext.disableSyntaxHighlighting();
|
||||
paintCss();
|
||||
return isSyntaxHighlightingEnabled;
|
||||
}
|
||||
|
||||
|
||||
@ -92,4 +97,71 @@ public class CustomCodeArea extends CodeArea {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Disables syntax highlighting if enabled.
|
||||
*/
|
||||
public void disableSyntaxHighlighting() {
|
||||
if (isSyntaxHighlightingEnabled) {
|
||||
isSyntaxHighlightingEnabled = false;
|
||||
if (executorService != null) {
|
||||
executorService.shutdown();
|
||||
}
|
||||
StyleLayer syntaxHighlightLayer = styleContext.getLayer(SYNTAX_HIGHLIGHT_LAYER_ID);
|
||||
if (syntaxHighlightLayer != null) {
|
||||
syntaxHighlightLayer.clearStyles();
|
||||
}
|
||||
}
|
||||
paintCss();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enables syntax highlighting if disabled and sets it to use the given computer.
|
||||
*
|
||||
* @param computer The computer to use
|
||||
*/
|
||||
public void setSyntaxHighlighting(SyntaxHighlighter computer) {
|
||||
isSyntaxHighlightingEnabled = true;
|
||||
Objects.requireNonNull(computer, "The syntax highlighting computer 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));
|
||||
}
|
||||
|
||||
setSyntaxHighlightingComputer(computer);
|
||||
}
|
||||
|
||||
|
||||
private void setSyntaxHighlightingComputer(SyntaxHighlighter computer) {
|
||||
this.highlightingComputer = computer;
|
||||
if (executorService != null) {
|
||||
executorService.shutdown();
|
||||
}
|
||||
if (isSyntaxHighlightingEnabled && highlightingComputer != null) {
|
||||
executorService = Executors.newSingleThreadExecutor();
|
||||
this.richChanges()
|
||||
.filter(ch -> !ch.getInserted().equals(ch.getRemoved())) // XXX
|
||||
.successionEnds(Duration.ofMillis(500))
|
||||
.supplyTask(() -> highlightingComputer.computeHighlightingAsync(this.getText(), executorService))
|
||||
.awaitLatest(this.richChanges())
|
||||
.filterMap(t -> {
|
||||
if (t.isSuccess()) {
|
||||
return Optional.of(t.get());
|
||||
} else {
|
||||
t.getFailure().printStackTrace();
|
||||
return Optional.empty();
|
||||
}
|
||||
})
|
||||
.subscribe(bounds -> {
|
||||
StyleLayer layer = styleContext.getLayer(SYNTAX_HIGHLIGHT_LAYER_ID);
|
||||
assert layer != null;
|
||||
layer.setBounds(bounds);
|
||||
this.paintCss();
|
||||
});
|
||||
this.getStylesheets().add(computer.getCssFileIdentifier());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
package net.sourceforge.pmd.util.fxdesigner.util.codearea;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@ -14,9 +13,6 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.fxmisc.richtext.StyleSpan;
|
||||
@ -24,67 +20,22 @@ import org.fxmisc.richtext.StyleSpans;
|
||||
import org.fxmisc.richtext.StyleSpansBuilder;
|
||||
|
||||
/**
|
||||
* Stores the current style layers and flattens them into a {@link StyleSpans} to style the text.
|
||||
* Stores the current style layers and can flattens them into a {@link StyleSpans} to style the text.
|
||||
*/
|
||||
class StyleContext implements Iterable<StyleLayer> {
|
||||
|
||||
private static final String SYNTAX_HIGHLIGHT_LAYER_ID = "syntax";
|
||||
|
||||
private final CustomCodeArea codeArea;
|
||||
|
||||
/** Contains the primary highlighting layers. */
|
||||
private Map<String, StyleLayer> layersById = new HashMap<>();
|
||||
|
||||
private SyntaxHighlighter highlightingComputer;
|
||||
private ExecutorService executorService;
|
||||
private boolean isSyntaxHighlightingEnabled;
|
||||
|
||||
|
||||
StyleContext(CustomCodeArea codeArea) {
|
||||
this.codeArea = codeArea;
|
||||
}
|
||||
|
||||
|
||||
public boolean isSyntaxHighlightingEnabled() {
|
||||
return isSyntaxHighlightingEnabled;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Disables syntax highlighting if enabled.
|
||||
*/
|
||||
public void disableSyntaxHighlighting() {
|
||||
if (isSyntaxHighlightingEnabled) {
|
||||
isSyntaxHighlightingEnabled = false;
|
||||
if (executorService != null) {
|
||||
executorService.shutdown();
|
||||
}
|
||||
StyleLayer syntaxHighlightLayer = layersById.get(SYNTAX_HIGHLIGHT_LAYER_ID);
|
||||
if (syntaxHighlightLayer != null) {
|
||||
syntaxHighlightLayer.clearStyles();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enables syntax highlighting if disabled and sets it to use the given computer.
|
||||
*
|
||||
* @param computer The computer to use
|
||||
*/
|
||||
public void setSyntaxHighlighting(SyntaxHighlighter computer) {
|
||||
isSyntaxHighlightingEnabled = true;
|
||||
Objects.requireNonNull(computer, "The syntax highlighting computer cannot be null");
|
||||
|
||||
StyleLayer syntaxHighlightLayer = layersById.get(SYNTAX_HIGHLIGHT_LAYER_ID);
|
||||
if (syntaxHighlightLayer == null) {
|
||||
layersById.put(SYNTAX_HIGHLIGHT_LAYER_ID, new StyleLayer(SYNTAX_HIGHLIGHT_LAYER_ID, codeArea));
|
||||
}
|
||||
|
||||
setSyntaxHighlightingComputer(computer);
|
||||
}
|
||||
|
||||
|
||||
void addLayer(String id, StyleLayer layer) {
|
||||
layersById.put(id, layer);
|
||||
}
|
||||
@ -95,37 +46,6 @@ class StyleContext implements Iterable<StyleLayer> {
|
||||
}
|
||||
|
||||
|
||||
private void setSyntaxHighlightingComputer(SyntaxHighlighter computer) {
|
||||
this.highlightingComputer = computer;
|
||||
if (executorService != null) {
|
||||
executorService.shutdown();
|
||||
}
|
||||
if (isSyntaxHighlightingEnabled && highlightingComputer != null) {
|
||||
executorService = Executors.newSingleThreadExecutor();
|
||||
codeArea.richChanges()
|
||||
.filter(ch -> !ch.getInserted().equals(ch.getRemoved())) // XXX
|
||||
.successionEnds(Duration.ofMillis(500))
|
||||
.supplyTask(() -> highlightingComputer.computeHighlightingAsync(codeArea.getText(), executorService))
|
||||
.awaitLatest(codeArea.richChanges())
|
||||
.filterMap(t -> {
|
||||
if (t.isSuccess()) {
|
||||
return Optional.of(t.get());
|
||||
} else {
|
||||
t.getFailure().printStackTrace();
|
||||
return Optional.empty();
|
||||
}
|
||||
})
|
||||
.subscribe(bounds -> {
|
||||
StyleLayer layer = layersById.get(SYNTAX_HIGHLIGHT_LAYER_ID);
|
||||
assert layer != null;
|
||||
layer.setBounds(bounds);
|
||||
codeArea.paintCss(); // TODO too high level method
|
||||
});
|
||||
codeArea.getStylesheets().add(computer.getCssFileIdentifier());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Clears the bounds of a layer. */
|
||||
public void clearLayer(String id) {
|
||||
StyleLayer layer = layersById.get(id);
|
||||
|
Reference in New Issue
Block a user