Reorganise style context

This commit is contained in:
Clément Fournier
2017-09-24 13:37:06 +02:00
parent fa19af9a57
commit 49dd443a48
2 changed files with 82 additions and 90 deletions

View File

@ -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());
}
}
}

View File

@ -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);