Merge remote-tracking branch 'origin/GP-6751-dragonmacher-decompiler-highlights'

This commit is contained in:
Ryan Kurtz
2026-04-24 12:47:18 -04:00
11 changed files with 244 additions and 252 deletions
@@ -241,9 +241,8 @@ public class DecompilerCodeComparisonView extends CodeComparisonView {
private void linkHighlightControllers() { private void linkHighlightControllers() {
DiffClangHighlightController left = cDisplays.get(LEFT).getHighlightController(); DiffClangHighlightController left = cDisplays.get(LEFT).getHighlightController();
DiffClangHighlightController right = cDisplays.get(RIGHT).getHighlightController(); DiffClangHighlightController right = cDisplays.get(RIGHT).getHighlightController();
left.addListener(right); left.setTokenChangedListener(right);
right.addListener(left); right.setTokenChangedListener(left);
} }
@Override @Override
@@ -17,18 +17,16 @@ package ghidra.features.codecompare.decompile;
import java.awt.Color; import java.awt.Color;
import java.util.*; import java.util.*;
import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import docking.widgets.EventTrigger; import docking.widgets.EventTrigger;
import docking.widgets.fieldpanel.field.Field; import docking.widgets.fieldpanel.field.Field;
import docking.widgets.fieldpanel.support.FieldLocation; import docking.widgets.fieldpanel.support.FieldLocation;
import ghidra.app.decompiler.ClangSyntaxToken; import ghidra.app.decompiler.*;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.*; import ghidra.app.decompiler.component.*;
import ghidra.features.codecompare.graphanalysis.TokenBin; import ghidra.features.codecompare.graphanalysis.TokenBin;
import ghidra.util.ColorUtils;
import ghidra.util.SystemUtilities;
import util.CollectionUtils;
/** /**
* Class to handle Function Difference highlights for a decompiled function. * Class to handle Function Difference highlights for a decompiled function.
@@ -40,202 +38,251 @@ public class DiffClangHighlightController extends LocationClangHighlightControll
private Set<ClangToken> diffTokenSet = new HashSet<>(); private Set<ClangToken> diffTokenSet = new HashSet<>();
private ClangToken locationToken; private ClangToken locationToken;
private TokenBin locationTokenBin; private TokenBin locationTokenBin;
private List<TokenBin> highlightBins; private List<TokenBin> allTokenBins;
private List<DiffClangHighlightListener> listenerList = new ArrayList<>();
private TokenBin matchingTokenBin;
private DecompilerCodeComparisonOptions comparisonOptions; private DecompilerCodeComparisonOptions comparisonOptions;
private DiffClangHighlightListener listener = new DummyListener();
private DiffTokenHighlighter diffColorHighlighter;
private BasicTokenHighlighter currentTokenHighlighter;
// highlights the token in this highlighter for the selected token in the other highlighter
private BasicTokenHighlighter matchingTokenHighlighter;
public DiffClangHighlightController(DecompilerCodeComparisonOptions comparisonOptions) { public DiffClangHighlightController(DecompilerCodeComparisonOptions comparisonOptions) {
this.comparisonOptions = comparisonOptions; this.comparisonOptions = comparisonOptions;
} }
public void clearDiffHighlights() {
doClearDiffHighlights();
notifyListeners();
}
private void doClearDiffHighlights() {
ClangToken[] array = diffTokenSet.toArray(new ClangToken[diffTokenSet.size()]);
for (ClangToken clangToken : array) {
clearDiffHighlight(clangToken);
}
}
private void clearDiffHighlight(ClangToken clangToken) {
Color highlight = clangToken.getHighlight();
if (highlight != null && highlight.equals(comparisonOptions.getDiffHighlightColor())) {
clangToken.setHighlight(null);
}
diffTokenSet.remove(clangToken);
}
private void clearNonDiffHighlight(ClangToken clangToken) {
if (diffTokenSet.contains(clangToken)) {
clangToken.setHighlight(comparisonOptions.getDiffHighlightColor());
}
else {
clangToken.setHighlight(null);
}
if (clangToken.isMatchingToken()) {
clangToken.setMatchingToken(false);
}
}
public void setDiffHighlights(List<TokenBin> highlightBins, Set<ClangToken> tokenSet) { public void setDiffHighlights(List<TokenBin> highlightBins, Set<ClangToken> tokenSet) {
this.highlightBins = highlightBins; this.allTokenBins = highlightBins;
doClearDiffHighlights();
for (ClangToken clangToken : tokenSet) { clearDiffHighlights();
clangToken.setHighlight(comparisonOptions.getDiffHighlightColor());
diffTokenSet.add(clangToken); if (!tokenSet.isEmpty()) {
Color color = comparisonOptions.getDiffHighlightColor();
diffColorHighlighter = new DiffTokenHighlighter(new ArrayList<>(tokenSet), color);
diffColorHighlighter.applyHighlights();
} }
notifyListeners(); notifyListeners();
} }
@Override @Override
public void fieldLocationChanged(FieldLocation location, Field field, EventTrigger trigger) { public void fieldLocationChanged(FieldLocation location, Field field,
EventTrigger trigger) {
if (!(field instanceof ClangTextField)) { if (!(field instanceof ClangTextField textField)) {
return; return;
} }
// Get the token for the location so we can highlight its token bin. // Get the token for the location so we can highlight its token bin. Also we will use it
// Also we will use it when notifying the other panel to highlight. // when notifying the other panel to highlight.
ClangToken tok = ((ClangTextField) field).getToken(location); ClangToken tok = textField.getToken(location);
if (SystemUtilities.isEqual(locationToken, tok)) { if (Objects.equals(locationToken, tok)) {
return; // Current location's token hasn't changed. return; // current location's token hasn't changed
} }
// Undo any highlight of the previous matching tokenBin.
if (matchingTokenBin != null && matchingTokenBin.getMatch() != null) {
clearTokenBinHighlight(matchingTokenBin.getMatch());
matchingTokenBin = null;
}
clearCurrentLocationHighlight();
clearPrimaryHighlights(); clearPrimaryHighlights();
addPrimaryHighlight(tok, defaultHighlightColor); clearCurrentLocationHighlight();
if (tok instanceof ClangSyntaxToken) { clearMatchingTokenBin();
List<ClangToken> tokens = addPrimaryHighlightToTokensForParenthesis(
(ClangSyntaxToken) tok, defaultParenColor); highlightTokensBetweenParens(tok);
reHighlightDiffs(tokens); highlightCurrentLocationToken(tok);
addPrimaryHighlightToTokensForBrace((ClangSyntaxToken) tok, defaultParenColor);
listener.locationTokenChanged(locationTokenBin);
} }
private void highlightTokensBetweenParens(ClangToken tok) {
if (!(tok instanceof ClangSyntaxToken syntaxToken)) {
return;
}
addPrimaryHighlightToTokensForParenthesis(syntaxToken, defaultParenColor);
addPrimaryHighlightToTokensForBrace(syntaxToken, defaultParenColor);
}
private void highlightCurrentLocationToken(ClangToken tok) {
TokenBin tokenBin = null; TokenBin tokenBin = null;
if (tok != null) { if (tok != null && allTokenBins != null) {
Color highlightColor = comparisonOptions.getFocusedTokenIneligibleHighlightColor(); // Don't know tokenBin = TokenBin.getBinContainingToken(allTokenBins, tok);
if (highlightBins != null) { }
tokenBin = TokenBin.getBinContainingToken(highlightBins, tok);
Color binHlColor = comparisonOptions.getFocusedTokenIneligibleHighlightColor();
if (tokenBin != null) { if (tokenBin != null) {
if (tokenBin.getMatch() != null) { if (tokenBin.getMatch() != null) {
highlightColor = comparisonOptions.getFocusedTokenMatchHighlightColor(); binHlColor = comparisonOptions.getFocusedTokenMatchHighlightColor();
}
else if (tokenBin.getMatch() == null) {
highlightColor = comparisonOptions.getFocusedTokenUnmatchedHighlightColor();
} }
else { else {
// All the tokens that didn't fall into the "has a match" or "no match" binHlColor = comparisonOptions.getFocusedTokenUnmatchedHighlightColor();
// categories above are in a single token bin.
// We don't want all these highlighted at the same time, so set the
// tokenBin to null. By doing this, only the current token gets highlighted.
tokenBin = null;
}
} }
} }
locationToken = tok; locationToken = tok;
locationTokenBin = tokenBin; locationTokenBin = tokenBin;
if (tokenBin == null) {
addPrimaryHighlight(tok, highlightColor); List<ClangToken> tokens = List.of();
if (tokenBin != null) {
tokens = toList(tokenBin);
} }
else { else if (tok != null) {
addTokenBinHighlight(tokenBin, highlightColor); tokens = List.of(tok);
}
installCurrentTokenHighlighter(tokens, binHlColor);
refreshDiffHighlightsForCurrentLocationChange();
}
/*
* The diff highlighter is smart enough to ignore the token at the current location. We have to
* kick it when the location changes so it will update the highlights.
*/
private void refreshDiffHighlightsForCurrentLocationChange() {
if (diffColorHighlighter != null) {
diffColorHighlighter.clearHighlights();
diffColorHighlighter.applyHighlights();
} }
} }
// Notify other decompiler panel highlight controller we have a new location token. private static List<ClangToken> toList(TokenBin tokens) {
for (DiffClangHighlightListener listener : listenerList) { return StreamSupport.stream(tokens.spliterator(), false).collect(Collectors.toList());
listener.locationTokenChanged(tok, tokenBin);
}
}
private void reHighlightDiffs(List<ClangToken> tokenList) {
Color averageColor =
ColorUtils.blend(defaultParenColor, comparisonOptions.getDiffHighlightColor(), 0.5);
for (ClangToken clangToken : tokenList) {
if (diffTokenSet.contains(clangToken)) {
clangToken.setHighlight(averageColor);
}
}
} }
private void clearCurrentLocationHighlight() { private void clearCurrentLocationHighlight() {
if (locationTokenBin != null) {
clearTokenBinHighlight(locationTokenBin); if (currentTokenHighlighter != null) {
currentTokenHighlighter.dispose();
currentTokenHighlighter = null;
}
locationTokenBin = null; locationTokenBin = null;
locationToken = null; locationToken = null;
} }
if (locationToken != null) {
clearNonDiffHighlight(locationToken); private void clearDiffHighlights() {
locationToken = null;
if (diffColorHighlighter != null) {
diffColorHighlighter.dispose();
diffColorHighlighter = null;
}
diffTokenSet.clear();
}
private void clearMatchingTokenBin() {
if (matchingTokenHighlighter != null) {
matchingTokenHighlighter.dispose();
matchingTokenHighlighter = null;
} }
} }
private void addTokenBinHighlight(TokenBin tokenBin, Color highlightColor) { private void installCurrentTokenHighlighter(List<ClangToken> tokens, Color highlightColor) {
for (ClangToken token : tokenBin) { if (tokens.isEmpty()) {
addPrimaryHighlight(token, highlightColor); return;
}
} }
private void clearTokenBinHighlight(TokenBin tokenBin) { currentTokenHighlighter = new BasicTokenHighlighter(tokens, highlightColor);
for (ClangToken token : tokenBin) { currentTokenHighlighter.applyHighlights();
clearNonDiffHighlight(token);
}
} }
private void doClearHighlights(TokenHighlights tokens) { private void installMatchingTokenBinHighlighter(TokenBin tokenBin, Color highlightColor) {
List<ClangToken> clangTokens =
CollectionUtils.asStream(tokens).map(ht -> ht.getToken()).collect(Collectors.toList()); clearMatchingTokenBin();
for (ClangToken clangToken : clangTokens) {
clearNonDiffHighlight(clangToken); if (tokenBin == null) {
return;
} }
tokens.clear();
notifyListeners(); matchingTokenHighlighter = new BasicTokenHighlighter(tokenBin, highlightColor);
matchingTokenHighlighter.applyHighlights();
}
public void setTokenChangedListener(DiffClangHighlightListener listener) {
this.listener = listener == null ? new DummyListener() : listener;
} }
@Override @Override
public void clearPrimaryHighlights() { public void locationTokenChanged(TokenBin tokenBin) {
doClearHighlights(getPrimaryHighlights());
}
public boolean addListener(DiffClangHighlightListener listener) {
return listenerList.add(listener);
}
public boolean removeListener(DiffClangHighlightListener listener) {
return listenerList.remove(listener);
}
@Override
public void locationTokenChanged(ClangToken tok, TokenBin tokenBin) {
clearCurrentLocationHighlight(); clearCurrentLocationHighlight();
refreshDiffHighlightsForCurrentLocationChange();
// The token Changed in our other matching DiffClangHighlightController // The token Changed in our other matching DiffClangHighlightController
highlightMatchingToken(tok, tokenBin); if (tokenBin != null) {
TokenBin match = tokenBin.getMatch();
Color color = comparisonOptions.getFocusedTokenMatchHighlightColor();
installMatchingTokenBinHighlighter(match, color);
}
} }
private void highlightMatchingToken(ClangToken tok, TokenBin tokenBin) { //=================================================================================================
// Undo any highlight of the previous matching tokenBin. // Inner Classes
if (matchingTokenBin != null && matchingTokenBin.getMatch() != null) { //=================================================================================================
clearTokenBinHighlight(matchingTokenBin.getMatch());
/**
* Highlights a given set of tokens with the given color.
*/
private class BasicTokenHighlighter implements DecompilerHighlighter {
private String id;
protected List<ClangToken> tokens;
private Color color;
BasicTokenHighlighter(TokenBin tokens, Color color) {
this(toList(tokens), color);
} }
// Highlight the new matching tokenBin. BasicTokenHighlighter(List<ClangToken> tokens, Color color) {
if (tokenBin != null && tokenBin.getMatch() != null) { this.color = color;
addTokenBinHighlight(tokenBin.getMatch(), UUID uuId = UUID.randomUUID();
comparisonOptions.getFocusedTokenMatchHighlightColor()); this.id = uuId.toString();
this.tokens = tokens;
} }
matchingTokenBin = tokenBin; // subclass overrides this method
protected List<ClangToken> getCurrentTokens() {
return tokens;
}
@Override
public void applyHighlights() {
Supplier<? extends Collection<ClangToken>> tokenSupplier = this::getCurrentTokens;
ColorProvider cp = t -> color;
addHighlighterHighlights(this, tokenSupplier, cp);
}
@Override
public void clearHighlights() {
removeHighlighterHighlights(this);
}
@Override
public void dispose() {
removeHighlighterHighlights(this);
}
@Override
public String getId() {
return id;
}
}
private class DiffTokenHighlighter extends BasicTokenHighlighter {
DiffTokenHighlighter(List<ClangToken> tokens, Color color) {
super(tokens, color);
}
@Override
protected List<ClangToken> getCurrentTokens() {
// ignore the selected token so that it paints with its own color and does not get
// blended with this highlighter's color
return tokens.stream().filter(t -> t != locationToken).collect(Collectors.toList());
}
}
private class DummyListener implements DiffClangHighlightListener {
@Override
public void locationTokenChanged(TokenBin tokenBin) {
// stub
}
} }
} }
@@ -15,16 +15,14 @@
*/ */
package ghidra.features.codecompare.decompile; package ghidra.features.codecompare.decompile;
import ghidra.app.decompiler.ClangToken;
import ghidra.features.codecompare.graphanalysis.TokenBin; import ghidra.features.codecompare.graphanalysis.TokenBin;
public interface DiffClangHighlightListener { public interface DiffClangHighlightListener {
/** /**
* Notifier that the current location changed to the specified token. * Notifier that the current location changed to the specified token.
* @param tok the token
* @param tokenBin the bin which contains the token. Otherwise, null. * @param tokenBin the bin which contains the token. Otherwise, null.
*/ */
public void locationTokenChanged(ClangToken tok, TokenBin tokenBin); public void locationTokenChanged(TokenBin tokenBin);
} }
@@ -33,7 +33,6 @@ public class ClangCommentToken extends ClangToken {
newToken.setText(text); newToken.setText(text);
newToken.setLineParent(source.getLineParent()); newToken.setLineParent(source.getLineParent());
newToken.setSyntaxType(source.getSyntaxType()); newToken.setSyntaxType(source.getSyntaxType());
newToken.setHighlight(source.getHighlight());
newToken.srcaddr = source.srcaddr; newToken.srcaddr = source.srcaddr;
return newToken; return newToken;
} }
@@ -15,7 +15,6 @@
*/ */
package ghidra.app.decompiler; package ghidra.app.decompiler;
import java.awt.Color;
import java.util.List; import java.util.List;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
@@ -45,12 +44,6 @@ public interface ClangNode {
*/ */
public Address getMaxAddress(); public Address getMaxAddress();
/**
* Set a highlighting background color for all text elements
* @param c is the color to set
*/
public void setHighlight(Color c);
/** /**
* Return the number of immediate groupings this text breaks up into * Return the number of immediate groupings this text breaks up into
* @return the number of child groupings * @return the number of child groupings
@@ -18,7 +18,6 @@ package ghidra.app.decompiler;
import static ghidra.program.model.pcode.AttributeId.*; import static ghidra.program.model.pcode.AttributeId.*;
import static ghidra.program.model.pcode.ElementId.*; import static ghidra.program.model.pcode.ElementId.*;
import java.awt.Color;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
@@ -49,13 +48,11 @@ public class ClangToken implements ClangNode {
private ClangLine lineparent; private ClangLine lineparent;
private String text; private String text;
private int syntax_type; private int syntax_type;
private Color highlight; // Color to highlight with or null if no highlight
private boolean matchingToken; private boolean matchingToken;
public ClangToken(ClangNode par) { public ClangToken(ClangNode par) {
parent = par; parent = par;
text = null; text = null;
highlight = null;
syntax_type = DEFAULT_COLOR; syntax_type = DEFAULT_COLOR;
lineparent = null; lineparent = null;
} }
@@ -63,14 +60,12 @@ public class ClangToken implements ClangNode {
public ClangToken(ClangNode par, String txt) { public ClangToken(ClangNode par, String txt) {
parent = par; parent = par;
text = txt; text = txt;
highlight = null;
syntax_type = DEFAULT_COLOR; syntax_type = DEFAULT_COLOR;
} }
public ClangToken(ClangNode par, String txt, int color) { public ClangToken(ClangNode par, String txt, int color) {
parent = par; parent = par;
text = txt; text = txt;
highlight = null;
syntax_type = color; syntax_type = color;
} }
@@ -123,19 +118,6 @@ public class ClangToken implements ClangNode {
return null; return null;
} }
@Override
public void setHighlight(Color val) {
highlight = val;
}
/**
* Get the background highlight color used to render this token, or null if not highlighted
* @return the Color or null
*/
public Color getHighlight() {
return highlight;
}
/** /**
* Set whether or not additional "matching" highlighting is applied to this token. * Set whether or not additional "matching" highlighting is applied to this token.
* Currently this means a bounding box is drawn around the token. * Currently this means a bounding box is drawn around the token.
@@ -17,7 +17,6 @@ package ghidra.app.decompiler;
import static ghidra.program.model.pcode.ElementId.*; import static ghidra.program.model.pcode.ElementId.*;
import java.awt.Color;
import java.util.*; import java.util.*;
import java.util.stream.Stream; import java.util.stream.Stream;
@@ -97,13 +96,6 @@ public class ClangTokenGroup implements ClangNode, Iterable<ClangNode> {
return parent.getClangFunction(); return parent.getClangFunction();
} }
@Override
public void setHighlight(Color val) {
for (ClangNode element : tokgroup) {
element.setHighlight(val);
}
}
@Override @Override
public void flatten(List<ClangNode> list) { public void flatten(List<ClangNode> list) {
for (ClangNode element : tokgroup) { for (ClangNode element : tokgroup) {
@@ -168,9 +160,7 @@ public class ClangTokenGroup implements ClangNode, Iterable<ClangNode> {
public String toString() { public String toString() {
String lastTokenStr = null; String lastTokenStr = null;
StringBuffer buffer = new StringBuffer(); StringBuffer buffer = new StringBuffer();
Iterator<ClangNode> iter = tokgroup.iterator(); for (ClangNode node : tokgroup) {
while (iter.hasNext()) {
ClangNode node = iter.next();
String tokenStr = node.toString(); String tokenStr = node.toString();
if (tokenStr.length() == 0) { if (tokenStr.length() == 0) {
continue; continue;
@@ -27,9 +27,12 @@ import ghidra.app.decompiler.ClangToken;
public class ClangFieldElement extends AbstractTextFieldElement { public class ClangFieldElement extends AbstractTextFieldElement {
private final ClangToken token; private final ClangToken token;
private ClangHighlightController hlController;
public ClangFieldElement(ClangToken token, AttributedString as, int col) { public ClangFieldElement(ClangHighlightController hlController, ClangToken token,
AttributedString as, int col) {
super(as, 0, col); super(as, 0, col);
this.hlController = hlController;
this.token = token; this.token = token;
} }
@@ -39,7 +42,7 @@ public class ClangFieldElement extends AbstractTextFieldElement {
@Override @Override
public void paint(JComponent c, Graphics g, int x, int y) { public void paint(JComponent c, Graphics g, int x, int y) {
Color highlightColor = token.getHighlight(); Color highlightColor = hlController.getCombinedColor(token);
if (highlightColor != null) { if (highlightColor != null) {
g.setColor(highlightColor); g.setColor(highlightColor);
g.fillRect(x, y - getHeightAbove(), getStringWidth(), g.fillRect(x, y - getHeightAbove(), getStringWidth(),
@@ -63,12 +66,12 @@ public class ClangFieldElement extends AbstractTextFieldElement {
if (as == attributedString) { if (as == attributedString) {
return this; return this;
} }
return new ClangFieldElement(token, as, column + start); return new ClangFieldElement(hlController, token, as, column + start);
} }
@Override @Override
public FieldElement replaceAll(char[] targets, char replacement) { public FieldElement replaceAll(char[] targets, char replacement) {
return new ClangFieldElement(token, attributedString.replaceAll(targets, replacement), AttributedString as = attributedString.replaceAll(targets, replacement);
column); return new ClangFieldElement(hlController, token, as, column);
} }
} }
@@ -30,6 +30,7 @@ import ghidra.program.model.pcode.HighFunction;
import ghidra.program.model.pcode.PcodeOp; import ghidra.program.model.pcode.PcodeOp;
import ghidra.util.ColorUtils; import ghidra.util.ColorUtils;
import util.CollectionUtils; import util.CollectionUtils;
import utility.function.Dummy;
/** /**
* Class to handle highlights for a decompiled function. * Class to handle highlights for a decompiled function.
@@ -86,7 +87,6 @@ public abstract class ClangHighlightController {
// arbitrary value chosen by guessing; this can be changed if needed // arbitrary value chosen by guessing; this can be changed if needed
private int maxColorBlendSize = 5; private int maxColorBlendSize = 5;
private boolean isRebuilding;
private List<ClangHighlightListener> listeners = new ArrayList<>(); private List<ClangHighlightListener> listeners = new ArrayList<>();
@@ -169,33 +169,19 @@ public abstract class ClangHighlightController {
// finished. This allows all highlights to calculate their matches without the color // finished. This allows all highlights to calculate their matches without the color
// blending affecting performance. // blending affecting performance.
// //
isRebuilding = true;
Set<DecompilerHighlighter> service = getServiceHighlighters(); Set<DecompilerHighlighter> service = getServiceHighlighters();
Set<DecompilerHighlighter> secondary = getSecondaryHighlighters(function); Set<DecompilerHighlighter> secondary = getSecondaryHighlighters(function);
Iterable<DecompilerHighlighter> it = CollectionUtils.asIterable(service, secondary); Iterable<DecompilerHighlighter> it = CollectionUtils.asIterable(service, secondary);
try {
for (DecompilerHighlighter highlighter : it) { for (DecompilerHighlighter highlighter : it) {
highlighter.clearHighlights(); highlighter.clearHighlights();
highlighter.applyHighlights(); highlighter.applyHighlights();
} }
}
finally {
isRebuilding = false;
}
// gather all highlighted tokens and then update their color // gather all highlighted tokens and then update their color
Set<ClangToken> allTokens = new HashSet<>();
it = CollectionUtils.asIterable(service, secondary); it = CollectionUtils.asIterable(service, secondary);
for (DecompilerHighlighter highlighter : it) { for (DecompilerHighlighter highlighter : it) {
TokenHighlights hlTokens = userHighlights.add(highlighter); userHighlights.add(highlighter);
for (HighlightToken hlToken : hlTokens) {
allTokens.add(hlToken.getToken());
}
}
for (ClangToken token : allTokens) {
updateHighlightColor(token);
} }
} }
@@ -238,7 +224,6 @@ public abstract class ClangHighlightController {
public void clearPrimaryHighlights() { public void clearPrimaryHighlights() {
Consumer<ClangToken> clearAll = token -> { Consumer<ClangToken> clearAll = token -> {
token.setMatchingToken(false); token.setMatchingToken(false);
updateHighlightColor(token);
}; };
doClearHighlights(contextHighlightTokens, clearAll); doClearHighlights(contextHighlightTokens, clearAll);
@@ -297,7 +282,7 @@ public abstract class ClangHighlightController {
for (DecompilerHighlighter highlighter : highlighters) { for (DecompilerHighlighter highlighter : highlighters) {
TokenHighlights highlights = userHighlights.getHighlights(highlighter); TokenHighlights highlights = userHighlights.getHighlights(highlighter);
Consumer<ClangToken> clearHighlight = token -> updateHighlightColor(token); Consumer<ClangToken> clearHighlight = Dummy.consumer();
doClearHighlights(highlights, clearHighlight); doClearHighlights(highlights, clearHighlight);
} }
highlighters.clear(); highlighters.clear();
@@ -333,7 +318,7 @@ public abstract class ClangHighlightController {
return; return;
} }
Consumer<ClangToken> clearHighlight = token -> updateHighlightColor(token); Consumer<ClangToken> clearHighlight = Dummy.consumer();
doClearHighlights(highlighterTokens, clearHighlight); doClearHighlights(highlighterTokens, clearHighlight);
notifyListeners(); notifyListeners();
} }
@@ -417,18 +402,6 @@ public abstract class ClangHighlightController {
// store the actual requested color // store the actual requested color
currentHighlights.add(new HighlightToken(clangToken, highlightColor)); currentHighlights.add(new HighlightToken(clangToken, highlightColor));
updateHighlightColor(clangToken);
}
private void updateHighlightColor(ClangToken t) {
if (isRebuilding) {
return;
}
// set the color to the current combined value of all highlight types
Color combinedColor = getCombinedColor(t);
t.setHighlight(combinedColor);
} }
private void add(Set<Color> colors, HighlightToken hlToken) { private void add(Set<Color> colors, HighlightToken hlToken) {
@@ -183,20 +183,21 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
FieldElement[] elements = new FieldElement[tokens.size()]; FieldElement[] elements = new FieldElement[tokens.size()];
int columnPosition = 0; int columnPosition = 0;
Program program = decompilerPanel.getProgram();
ClangHighlightController hlController = decompilerPanel.getHighlightController();
for (int i = 0; i < tokens.size(); ++i) { for (int i = 0; i < tokens.size(); ++i) {
ClangToken token = tokens.get(i); ClangToken token = tokens.get(i);
Color color = getTokenColor(token); Color color = getTokenColor(token);
if (token instanceof ClangCommentToken) { if (token instanceof ClangCommentToken) {
AttributedString prototype = new AttributedString("prototype", color, metrics); AttributedString prototype = new AttributedString("prototype", color, metrics);
Program program = decompilerPanel.getProgram();
elements[i] = elements[i] =
CommentUtils.parseTextForAnnotations(token.getText(), program, prototype, 0); CommentUtils.parseTextForAnnotations(token.getText(), program, prototype, 0);
columnPosition += elements[i].length(); columnPosition += elements[i].length();
} }
else { else {
AttributedString as = new AttributedString(token.getText(), color, metrics); AttributedString as = new AttributedString(token.getText(), color, metrics);
elements[i] = new ClangFieldElement(token, as, columnPosition); elements[i] = new ClangFieldElement(hlController, token, as, columnPosition);
columnPosition += as.length(); columnPosition += as.length();
} }
} }
@@ -2048,7 +2048,7 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
private void assertCombinedHighlightColor(ClangToken token) { private void assertCombinedHighlightColor(ClangToken token) {
Color combinedColor = getCombinedHighlightColor(token); Color combinedColor = getCombinedHighlightColor(token);
Color actual = token.getHighlight(); Color actual = getHighlight(provider, token);
assertEquals(combinedColor, actual); assertEquals(combinedColor, actual);
} }
@@ -2139,7 +2139,8 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
DecompilerController controller = provider.getController(); DecompilerController controller = provider.getController();
DecompilerPanel panel = controller.getDecompilerPanel(); DecompilerPanel panel = controller.getDecompilerPanel();
Color hlColor = panel.getCurrentVariableHighlightColor(); Color hlColor = panel.getCurrentVariableHighlightColor();
assertEquals(hlColor, token.getHighlight()); Color actual = getHighlight(provider, token);
assertEquals(hlColor, actual);
} }
private void rename(String newName) { private void rename(String newName) {
@@ -2265,7 +2266,7 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
// test the token under the cursor directly, as that may have a combined highlight applied // test the token under the cursor directly, as that may have a combined highlight applied
Color combinedColor = getCombinedHighlightColor(theProvider, cursorToken); Color combinedColor = getCombinedHighlightColor(theProvider, cursorToken);
ColorMatcher cm = new ColorMatcher(color, combinedColor); ColorMatcher cm = new ColorMatcher(color, combinedColor);
Color actual = cursorToken.getHighlight(); Color actual = getHighlight(theProvider, cursorToken);
assertTrue("Token is not highlighted: '" + cursorToken + "'" + "\n\texpected: " + cm + assertTrue("Token is not highlighted: '" + cursorToken + "'" + "\n\texpected: " + cm +
"; found: " + toString(actual), cm.matches(actual)); "; found: " + toString(actual), cm.matches(actual));
} }
@@ -2283,7 +2284,7 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
// test the token under the cursor directly, as that may have a combined highlight applied // test the token under the cursor directly, as that may have a combined highlight applied
Color combinedColor = getCombinedHighlightColor(token); Color combinedColor = getCombinedHighlightColor(token);
ColorMatcher cm = new ColorMatcher(color, combinedColor); ColorMatcher cm = new ColorMatcher(color, combinedColor);
Color actual = token.getHighlight(); Color actual = getHighlight(provider, token);
String tokenString = token.toString() + " at line " + token.getLineParent().getLineNumber(); String tokenString = token.toString() + " at line " + token.getLineParent().getLineNumber();
assertTrue("Token is not highlighted: '" + tokenString + "'" + "\n\texpected: " + cm + assertTrue("Token is not highlighted: '" + tokenString + "'" + "\n\texpected: " + cm +
"; found: " + toString(actual), cm.matches(actual)); "; found: " + toString(actual), cm.matches(actual));
@@ -2330,7 +2331,7 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
continue; continue;
} }
Color actual = token.getHighlight(); Color actual = getHighlight(provider, token);
assertTrue("Token is not highlighted: '" + token + "'" + "\n\texpected: " + cm + assertTrue("Token is not highlighted: '" + token + "'" + "\n\texpected: " + cm +
"; found: " + toString(actual), cm.matches(actual)); "; found: " + toString(actual), cm.matches(actual));
} }
@@ -2357,7 +2358,7 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
continue; continue;
} }
Color actual = token.getHighlight(); Color actual = getHighlight(theProvider, token);
assertTrue("Token is not highlighted: '" + token + "'" + "\n\texpected: " + cm + assertTrue("Token is not highlighted: '" + token + "'" + "\n\texpected: " + cm +
"; found: " + toString(actual), cm.matches(actual)); "; found: " + toString(actual), cm.matches(actual));
} }
@@ -2378,7 +2379,7 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
assertAllFieldsHighlighted(theProvider, name, cm, ignores); assertAllFieldsHighlighted(theProvider, name, cm, ignores);
// test the token under the cursor directly, as that may have a combined highlight applied // test the token under the cursor directly, as that may have a combined highlight applied
Color actual = token.getHighlight(); Color actual = getHighlight(theProvider, token);
assertTrue("Token is not highlighted: '" + token + "'" + "\n\texpected: " + cm + assertTrue("Token is not highlighted: '" + token + "'" + "\n\texpected: " + cm +
"; found: " + toString(actual), cm.matches(actual)); "; found: " + toString(actual), cm.matches(actual));
} }
@@ -2394,7 +2395,7 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
continue; continue;
} }
Color actual = otherToken.getHighlight(); Color actual = getHighlight(theProvider, otherToken);
assertTrue("Token is not highlighted: '" + otherToken + "'" + "\n\texpected: " + assertTrue("Token is not highlighted: '" + otherToken + "'" + "\n\texpected: " +
colorMatcher + "; found: " + toString(actual), colorMatcher.matches(actual)); colorMatcher + "; found: " + toString(actual), colorMatcher.matches(actual));
} }
@@ -2411,7 +2412,7 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
continue; continue;
} }
Color actual = otherToken.getHighlight(); Color actual = getHighlight(theProvider, otherToken);
Color combinedColor = getCombinedHighlightColor(otherToken); Color combinedColor = getCombinedHighlightColor(otherToken);
ColorMatcher combinedColorMatcher = colorMatcher.with(combinedColor); ColorMatcher combinedColorMatcher = colorMatcher.with(combinedColor);
assertTrue( assertTrue(
@@ -2421,6 +2422,12 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
} }
} }
private Color getHighlight(DecompilerProvider theProvider, ClangToken token) {
DecompilerPanel panel = theProvider.getController().getDecompilerPanel();
ClangHighlightController highlightController = panel.getHighlightController();
return highlightController.getCombinedColor(token);
}
private String toString(Color c) { private String toString(Color c) {
if (c == null) { if (c == null) {
return "Color{null}"; return "Color{null}";