GP-6751 - Decompiler - Updated highlights to be painted using the

highlight controller and not from the color value in the token.  This
fixes shared highlight state issues in snapshots.
This commit is contained in:
dragonmacher
2026-04-22 18:31:14 -04:00
parent b6524957ab
commit a11758eb5e
11 changed files with 244 additions and 252 deletions
@@ -241,9 +241,8 @@ public class DecompilerCodeComparisonView extends CodeComparisonView {
private void linkHighlightControllers() {
DiffClangHighlightController left = cDisplays.get(LEFT).getHighlightController();
DiffClangHighlightController right = cDisplays.get(RIGHT).getHighlightController();
left.addListener(right);
right.addListener(left);
left.setTokenChangedListener(right);
right.setTokenChangedListener(left);
}
@Override
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -17,18 +17,16 @@ package ghidra.features.codecompare.decompile;
import java.awt.Color;
import java.util.*;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import docking.widgets.EventTrigger;
import docking.widgets.fieldpanel.field.Field;
import docking.widgets.fieldpanel.support.FieldLocation;
import ghidra.app.decompiler.ClangSyntaxToken;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.*;
import ghidra.app.decompiler.component.*;
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.
@@ -40,202 +38,251 @@ public class DiffClangHighlightController extends LocationClangHighlightControll
private Set<ClangToken> diffTokenSet = new HashSet<>();
private ClangToken locationToken;
private TokenBin locationTokenBin;
private List<TokenBin> highlightBins;
private List<DiffClangHighlightListener> listenerList = new ArrayList<>();
private TokenBin matchingTokenBin;
private List<TokenBin> allTokenBins;
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) {
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) {
this.highlightBins = highlightBins;
doClearDiffHighlights();
for (ClangToken clangToken : tokenSet) {
clangToken.setHighlight(comparisonOptions.getDiffHighlightColor());
diffTokenSet.add(clangToken);
this.allTokenBins = highlightBins;
clearDiffHighlights();
if (!tokenSet.isEmpty()) {
Color color = comparisonOptions.getDiffHighlightColor();
diffColorHighlighter = new DiffTokenHighlighter(new ArrayList<>(tokenSet), color);
diffColorHighlighter.applyHighlights();
}
notifyListeners();
}
@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;
}
// Get the token for the location so we can highlight its token bin.
// Also we will use it when notifying the other panel to highlight.
ClangToken tok = ((ClangTextField) field).getToken(location);
if (SystemUtilities.isEqual(locationToken, tok)) {
return; // Current location's token hasn't changed.
// Get the token for the location so we can highlight its token bin. Also we will use it
// when notifying the other panel to highlight.
ClangToken tok = textField.getToken(location);
if (Objects.equals(locationToken, tok)) {
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();
addPrimaryHighlight(tok, defaultHighlightColor);
if (tok instanceof ClangSyntaxToken) {
List<ClangToken> tokens = addPrimaryHighlightToTokensForParenthesis(
(ClangSyntaxToken) tok, defaultParenColor);
reHighlightDiffs(tokens);
addPrimaryHighlightToTokensForBrace((ClangSyntaxToken) tok, defaultParenColor);
clearCurrentLocationHighlight();
clearMatchingTokenBin();
highlightTokensBetweenParens(tok);
highlightCurrentLocationToken(tok);
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;
if (tok != null) {
Color highlightColor = comparisonOptions.getFocusedTokenIneligibleHighlightColor(); // Don't know
if (highlightBins != null) {
tokenBin = TokenBin.getBinContainingToken(highlightBins, tok);
if (tokenBin != null) {
if (tokenBin.getMatch() != null) {
highlightColor = comparisonOptions.getFocusedTokenMatchHighlightColor();
}
else if (tokenBin.getMatch() == null) {
highlightColor = comparisonOptions.getFocusedTokenUnmatchedHighlightColor();
}
else {
// All the tokens that didn't fall into the "has a match" or "no match"
// 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;
locationTokenBin = tokenBin;
if (tokenBin == null) {
addPrimaryHighlight(tok, highlightColor);
if (tok != null && allTokenBins != null) {
tokenBin = TokenBin.getBinContainingToken(allTokenBins, tok);
}
Color binHlColor = comparisonOptions.getFocusedTokenIneligibleHighlightColor();
if (tokenBin != null) {
if (tokenBin.getMatch() != null) {
binHlColor = comparisonOptions.getFocusedTokenMatchHighlightColor();
}
else {
addTokenBinHighlight(tokenBin, highlightColor);
binHlColor = comparisonOptions.getFocusedTokenUnmatchedHighlightColor();
}
}
// Notify other decompiler panel highlight controller we have a new location token.
for (DiffClangHighlightListener listener : listenerList) {
listener.locationTokenChanged(tok, tokenBin);
locationToken = tok;
locationTokenBin = tokenBin;
List<ClangToken> tokens = List.of();
if (tokenBin != null) {
tokens = toList(tokenBin);
}
else if (tok != null) {
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();
}
}
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 static List<ClangToken> toList(TokenBin tokens) {
return StreamSupport.stream(tokens.spliterator(), false).collect(Collectors.toList());
}
private void clearCurrentLocationHighlight() {
if (locationTokenBin != null) {
clearTokenBinHighlight(locationTokenBin);
locationTokenBin = null;
locationToken = null;
if (currentTokenHighlighter != null) {
currentTokenHighlighter.dispose();
currentTokenHighlighter = null;
}
if (locationToken != null) {
clearNonDiffHighlight(locationToken);
locationToken = null;
locationTokenBin = null;
locationToken = null;
}
private void clearDiffHighlights() {
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) {
for (ClangToken token : tokenBin) {
addPrimaryHighlight(token, highlightColor);
private void installCurrentTokenHighlighter(List<ClangToken> tokens, Color highlightColor) {
if (tokens.isEmpty()) {
return;
}
currentTokenHighlighter = new BasicTokenHighlighter(tokens, highlightColor);
currentTokenHighlighter.applyHighlights();
}
private void clearTokenBinHighlight(TokenBin tokenBin) {
for (ClangToken token : tokenBin) {
clearNonDiffHighlight(token);
private void installMatchingTokenBinHighlighter(TokenBin tokenBin, Color highlightColor) {
clearMatchingTokenBin();
if (tokenBin == null) {
return;
}
matchingTokenHighlighter = new BasicTokenHighlighter(tokenBin, highlightColor);
matchingTokenHighlighter.applyHighlights();
}
private void doClearHighlights(TokenHighlights tokens) {
List<ClangToken> clangTokens =
CollectionUtils.asStream(tokens).map(ht -> ht.getToken()).collect(Collectors.toList());
for (ClangToken clangToken : clangTokens) {
clearNonDiffHighlight(clangToken);
}
tokens.clear();
notifyListeners();
public void setTokenChangedListener(DiffClangHighlightListener listener) {
this.listener = listener == null ? new DummyListener() : listener;
}
@Override
public void clearPrimaryHighlights() {
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) {
public void locationTokenChanged(TokenBin tokenBin) {
clearCurrentLocationHighlight();
refreshDiffHighlightsForCurrentLocationChange();
// 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.
if (matchingTokenBin != null && matchingTokenBin.getMatch() != null) {
clearTokenBinHighlight(matchingTokenBin.getMatch());
//=================================================================================================
// Inner Classes
//=================================================================================================
/**
* 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.
if (tokenBin != null && tokenBin.getMatch() != null) {
addTokenBinHighlight(tokenBin.getMatch(),
comparisonOptions.getFocusedTokenMatchHighlightColor());
BasicTokenHighlighter(List<ClangToken> tokens, Color color) {
this.color = color;
UUID uuId = UUID.randomUUID();
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
}
}
}
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -15,16 +15,14 @@
*/
package ghidra.features.codecompare.decompile;
import ghidra.app.decompiler.ClangToken;
import ghidra.features.codecompare.graphanalysis.TokenBin;
public interface DiffClangHighlightListener {
/**
* Notifier that the current location changed to the specified token.
* @param tok the token
* @param tokenBin the bin which contains the token. Otherwise, null.
*/
public void locationTokenChanged(ClangToken tok, TokenBin tokenBin);
public void locationTokenChanged(TokenBin tokenBin);
}
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -33,7 +33,6 @@ public class ClangCommentToken extends ClangToken {
newToken.setText(text);
newToken.setLineParent(source.getLineParent());
newToken.setSyntaxType(source.getSyntaxType());
newToken.setHighlight(source.getHighlight());
newToken.srcaddr = source.srcaddr;
return newToken;
}
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -15,7 +15,6 @@
*/
package ghidra.app.decompiler;
import java.awt.Color;
import java.util.List;
import ghidra.program.model.address.Address;
@@ -45,12 +44,6 @@ public interface ClangNode {
*/
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 child groupings
@@ -18,7 +18,6 @@ package ghidra.app.decompiler;
import static ghidra.program.model.pcode.AttributeId.*;
import static ghidra.program.model.pcode.ElementId.*;
import java.awt.Color;
import java.util.Iterator;
import java.util.List;
@@ -49,13 +48,11 @@ public class ClangToken implements ClangNode {
private ClangLine lineparent;
private String text;
private int syntax_type;
private Color highlight; // Color to highlight with or null if no highlight
private boolean matchingToken;
public ClangToken(ClangNode par) {
parent = par;
text = null;
highlight = null;
syntax_type = DEFAULT_COLOR;
lineparent = null;
}
@@ -63,14 +60,12 @@ public class ClangToken implements ClangNode {
public ClangToken(ClangNode par, String txt) {
parent = par;
text = txt;
highlight = null;
syntax_type = DEFAULT_COLOR;
}
public ClangToken(ClangNode par, String txt, int color) {
parent = par;
text = txt;
highlight = null;
syntax_type = color;
}
@@ -123,19 +118,6 @@ public class ClangToken implements ClangNode {
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.
* Currently this means a bounding box is drawn around the token.
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -17,7 +17,6 @@ package ghidra.app.decompiler;
import static ghidra.program.model.pcode.ElementId.*;
import java.awt.Color;
import java.util.*;
import java.util.stream.Stream;
@@ -97,13 +96,6 @@ public class ClangTokenGroup implements ClangNode, Iterable<ClangNode> {
return parent.getClangFunction();
}
@Override
public void setHighlight(Color val) {
for (ClangNode element : tokgroup) {
element.setHighlight(val);
}
}
@Override
public void flatten(List<ClangNode> list) {
for (ClangNode element : tokgroup) {
@@ -168,9 +160,7 @@ public class ClangTokenGroup implements ClangNode, Iterable<ClangNode> {
public String toString() {
String lastTokenStr = null;
StringBuffer buffer = new StringBuffer();
Iterator<ClangNode> iter = tokgroup.iterator();
while (iter.hasNext()) {
ClangNode node = iter.next();
for (ClangNode node : tokgroup) {
String tokenStr = node.toString();
if (tokenStr.length() == 0) {
continue;
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -27,9 +27,12 @@ import ghidra.app.decompiler.ClangToken;
public class ClangFieldElement extends AbstractTextFieldElement {
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);
this.hlController = hlController;
this.token = token;
}
@@ -39,7 +42,7 @@ public class ClangFieldElement extends AbstractTextFieldElement {
@Override
public void paint(JComponent c, Graphics g, int x, int y) {
Color highlightColor = token.getHighlight();
Color highlightColor = hlController.getCombinedColor(token);
if (highlightColor != null) {
g.setColor(highlightColor);
g.fillRect(x, y - getHeightAbove(), getStringWidth(),
@@ -63,12 +66,12 @@ public class ClangFieldElement extends AbstractTextFieldElement {
if (as == attributedString) {
return this;
}
return new ClangFieldElement(token, as, column + start);
return new ClangFieldElement(hlController, token, as, column + start);
}
@Override
public FieldElement replaceAll(char[] targets, char replacement) {
return new ClangFieldElement(token, attributedString.replaceAll(targets, replacement),
column);
AttributedString as = attributedString.replaceAll(targets, replacement);
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.util.ColorUtils;
import util.CollectionUtils;
import utility.function.Dummy;
/**
* 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
private int maxColorBlendSize = 5;
private boolean isRebuilding;
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
// blending affecting performance.
//
isRebuilding = true;
Set<DecompilerHighlighter> service = getServiceHighlighters();
Set<DecompilerHighlighter> secondary = getSecondaryHighlighters(function);
Iterable<DecompilerHighlighter> it = CollectionUtils.asIterable(service, secondary);
try {
for (DecompilerHighlighter highlighter : it) {
highlighter.clearHighlights();
highlighter.applyHighlights();
}
}
finally {
isRebuilding = false;
for (DecompilerHighlighter highlighter : it) {
highlighter.clearHighlights();
highlighter.applyHighlights();
}
// gather all highlighted tokens and then update their color
Set<ClangToken> allTokens = new HashSet<>();
it = CollectionUtils.asIterable(service, secondary);
for (DecompilerHighlighter highlighter : it) {
TokenHighlights hlTokens = userHighlights.add(highlighter);
for (HighlightToken hlToken : hlTokens) {
allTokens.add(hlToken.getToken());
}
}
for (ClangToken token : allTokens) {
updateHighlightColor(token);
userHighlights.add(highlighter);
}
}
@@ -238,7 +224,6 @@ public abstract class ClangHighlightController {
public void clearPrimaryHighlights() {
Consumer<ClangToken> clearAll = token -> {
token.setMatchingToken(false);
updateHighlightColor(token);
};
doClearHighlights(contextHighlightTokens, clearAll);
@@ -297,7 +282,7 @@ public abstract class ClangHighlightController {
for (DecompilerHighlighter highlighter : highlighters) {
TokenHighlights highlights = userHighlights.getHighlights(highlighter);
Consumer<ClangToken> clearHighlight = token -> updateHighlightColor(token);
Consumer<ClangToken> clearHighlight = Dummy.consumer();
doClearHighlights(highlights, clearHighlight);
}
highlighters.clear();
@@ -333,7 +318,7 @@ public abstract class ClangHighlightController {
return;
}
Consumer<ClangToken> clearHighlight = token -> updateHighlightColor(token);
Consumer<ClangToken> clearHighlight = Dummy.consumer();
doClearHighlights(highlighterTokens, clearHighlight);
notifyListeners();
}
@@ -417,18 +402,6 @@ public abstract class ClangHighlightController {
// store the actual requested color
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) {
@@ -183,20 +183,21 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
FieldElement[] elements = new FieldElement[tokens.size()];
int columnPosition = 0;
Program program = decompilerPanel.getProgram();
ClangHighlightController hlController = decompilerPanel.getHighlightController();
for (int i = 0; i < tokens.size(); ++i) {
ClangToken token = tokens.get(i);
Color color = getTokenColor(token);
if (token instanceof ClangCommentToken) {
AttributedString prototype = new AttributedString("prototype", color, metrics);
Program program = decompilerPanel.getProgram();
elements[i] =
CommentUtils.parseTextForAnnotations(token.getText(), program, prototype, 0);
columnPosition += elements[i].length();
}
else {
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();
}
}
@@ -2048,7 +2048,7 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
private void assertCombinedHighlightColor(ClangToken token) {
Color combinedColor = getCombinedHighlightColor(token);
Color actual = token.getHighlight();
Color actual = getHighlight(provider, token);
assertEquals(combinedColor, actual);
}
@@ -2139,7 +2139,8 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
DecompilerController controller = provider.getController();
DecompilerPanel panel = controller.getDecompilerPanel();
Color hlColor = panel.getCurrentVariableHighlightColor();
assertEquals(hlColor, token.getHighlight());
Color actual = getHighlight(provider, token);
assertEquals(hlColor, actual);
}
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
Color combinedColor = getCombinedHighlightColor(theProvider, cursorToken);
ColorMatcher cm = new ColorMatcher(color, combinedColor);
Color actual = cursorToken.getHighlight();
Color actual = getHighlight(theProvider, cursorToken);
assertTrue("Token is not highlighted: '" + cursorToken + "'" + "\n\texpected: " + cm +
"; 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
Color combinedColor = getCombinedHighlightColor(token);
ColorMatcher cm = new ColorMatcher(color, combinedColor);
Color actual = token.getHighlight();
Color actual = getHighlight(provider, token);
String tokenString = token.toString() + " at line " + token.getLineParent().getLineNumber();
assertTrue("Token is not highlighted: '" + tokenString + "'" + "\n\texpected: " + cm +
"; found: " + toString(actual), cm.matches(actual));
@@ -2330,7 +2331,7 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
continue;
}
Color actual = token.getHighlight();
Color actual = getHighlight(provider, token);
assertTrue("Token is not highlighted: '" + token + "'" + "\n\texpected: " + cm +
"; found: " + toString(actual), cm.matches(actual));
}
@@ -2357,7 +2358,7 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
continue;
}
Color actual = token.getHighlight();
Color actual = getHighlight(theProvider, token);
assertTrue("Token is not highlighted: '" + token + "'" + "\n\texpected: " + cm +
"; found: " + toString(actual), cm.matches(actual));
}
@@ -2378,7 +2379,7 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
assertAllFieldsHighlighted(theProvider, name, cm, ignores);
// 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 +
"; found: " + toString(actual), cm.matches(actual));
}
@@ -2394,7 +2395,7 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
continue;
}
Color actual = otherToken.getHighlight();
Color actual = getHighlight(theProvider, otherToken);
assertTrue("Token is not highlighted: '" + otherToken + "'" + "\n\texpected: " +
colorMatcher + "; found: " + toString(actual), colorMatcher.matches(actual));
}
@@ -2411,7 +2412,7 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
continue;
}
Color actual = otherToken.getHighlight();
Color actual = getHighlight(theProvider, otherToken);
Color combinedColor = getCombinedHighlightColor(otherToken);
ColorMatcher combinedColorMatcher = colorMatcher.with(combinedColor);
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) {
if (c == null) {
return "Color{null}";