mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-23 04:32:12 +00:00
Merge remote-tracking branch
'origin/GP-3494-dragonmacher-decompiler-highlight-nav-v2--SQUASHED' (Closes #538)
This commit is contained in:
commit
0e33958c76
@ -22,7 +22,6 @@ import java.util.stream.Collectors;
|
||||
import docking.widgets.EventTrigger;
|
||||
import docking.widgets.fieldpanel.field.Field;
|
||||
import docking.widgets.fieldpanel.support.FieldLocation;
|
||||
import generic.theme.GColor;
|
||||
import ghidra.app.decompiler.ClangSyntaxToken;
|
||||
import ghidra.app.decompiler.ClangToken;
|
||||
import ghidra.app.decompiler.component.*;
|
||||
@ -120,7 +119,7 @@ public class DiffClangHighlightController extends LocationClangHighlightControll
|
||||
List<ClangToken> tokens = addPrimaryHighlightToTokensForParenthesis(
|
||||
(ClangSyntaxToken) tok, defaultParenColor);
|
||||
reHighlightDiffs(tokens);
|
||||
addBraceHighlight((ClangSyntaxToken) tok, defaultParenColor);
|
||||
addPrimaryHighlightToTokensForBrace((ClangSyntaxToken) tok, defaultParenColor);
|
||||
}
|
||||
|
||||
TokenBin tokenBin = null;
|
||||
|
@ -4370,7 +4370,8 @@
|
||||
<title>Middle-Click</title>
|
||||
<para>
|
||||
Highlights every occurrence of a variable, constant, or operator represented by the selected
|
||||
token, within the Decompiler window.
|
||||
token, within the Decompiler window. There are actions available from the popup menu and from
|
||||
the keyboard to navigate to each highlighted token.
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
@ -4770,6 +4771,17 @@
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="GoToMiddleMouseHighlight">
|
||||
<title>Go To Next/Previous Highlight</title>
|
||||
<para>
|
||||
These actions are available from the popup menu and keyboard. Only tokens highlighted from the
|
||||
middle-mouse will be navigated. <emphasis role="bold">Shift-Comma</emphasis> will go to the
|
||||
previous highlighted token. <emphasis role="bold">Shift-Period</emphasis> will go to the
|
||||
next highlighted token. These key bindings can be changed via the
|
||||
<link xlink:href="help/topics/Tool/ToolOptions_Dialog.htm#KeyBindings_Option">Tool Options Dialog</link>.
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="ActionHighlight">
|
||||
<title>Highlight</title>
|
||||
<para>
|
||||
|
@ -533,8 +533,10 @@
|
||||
|
||||
<p>
|
||||
Highlights every occurrence of a variable, constant, or operator represented by the selected
|
||||
token, within the Decompiler window.
|
||||
token, within the Decompiler window. There are actions available from the popup menu and from
|
||||
the keyboard to navigate to each highlighted token.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@ -958,6 +960,19 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="sect2">
|
||||
<div class="titlepage"><div><div><h3 class="title">
|
||||
<a name="GoToMiddleMouseHighlight"></a>Go To Next/Previous Highlight</h3></div></div></div>
|
||||
|
||||
<p>
|
||||
These actions are available from the popup menu and keyboard. Only tokens highlighted from the
|
||||
middle-mouse will be navigated. <span class="bold"><strong>Shift-Comma</strong></span> will
|
||||
go to the previous highlighted token. <span class="bold"><strong>Shift-Period</strong></span>
|
||||
will go to the next highlighted token. These key bindings can be changed via the
|
||||
<a class="ulink" href="help/topics/Tool/ToolOptions_Dialog.htm#KeyBindings_Option" target="_top">Tool Options Dialog</a>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="sect2">
|
||||
<div class="titlepage"><div><div><h3 class="title">
|
||||
<a name="ActionHighlight"></a>Highlight</h3></div></div></div>
|
||||
|
@ -25,7 +25,7 @@ import java.util.*;
|
||||
*/
|
||||
public class ClangLine {
|
||||
private int indent_level;
|
||||
private ArrayList<ClangToken> tokens;
|
||||
private List<ClangToken> tokens;
|
||||
private int lineNumber;
|
||||
|
||||
public ClangLine(int lineNumber, int indent) {
|
||||
@ -35,7 +35,7 @@ public class ClangLine {
|
||||
}
|
||||
|
||||
public String getIndentString() {
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
for (int i = 0; i < indent_level; i++) {
|
||||
buffer.append(PrettyPrinter.INDENT_STRING);
|
||||
}
|
||||
@ -51,7 +51,7 @@ public class ClangLine {
|
||||
tok.setLineParent(this);
|
||||
}
|
||||
|
||||
public ArrayList<ClangToken> getAllTokens() {
|
||||
public List<ClangToken> getAllTokens() {
|
||||
return tokens;
|
||||
}
|
||||
|
||||
@ -95,7 +95,6 @@ public class ClangLine {
|
||||
if (isCallout) {
|
||||
buffy.append(end);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return buffy.toString();
|
||||
|
@ -37,7 +37,7 @@ public class PrettyPrinter {
|
||||
|
||||
private Function function;
|
||||
private ClangTokenGroup tokgroup;
|
||||
private ArrayList<ClangLine> lines = new ArrayList<>();
|
||||
private List<ClangLine> lines = new ArrayList<>();
|
||||
private NameTransformer transformer;
|
||||
|
||||
/**
|
||||
@ -58,7 +58,7 @@ public class PrettyPrinter {
|
||||
|
||||
private void padEmptyLines() {
|
||||
for (ClangLine line : lines) {
|
||||
ArrayList<ClangToken> tokenList = line.getAllTokens();
|
||||
List<ClangToken> tokenList = line.getAllTokens();
|
||||
if (tokenList.size() == 0) {
|
||||
ClangToken spacer = ClangToken.buildSpacer(null, line.getIndent(), INDENT_STRING);
|
||||
spacer.setLineParent(line);
|
||||
@ -72,11 +72,11 @@ public class PrettyPrinter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array list of the C language lines contained in the
|
||||
* Returns a list of the C language lines contained in the
|
||||
* C language token group.
|
||||
* @return an array list of the C language lines
|
||||
* @return a list of the C language lines
|
||||
*/
|
||||
public ArrayList<ClangLine> getLines() {
|
||||
public List<ClangLine> getLines() {
|
||||
return lines;
|
||||
}
|
||||
|
||||
@ -86,8 +86,7 @@ public class PrettyPrinter {
|
||||
* @return a string of readable C code
|
||||
*/
|
||||
public DecompiledFunction print() {
|
||||
StringBuffer buff = new StringBuffer();
|
||||
|
||||
StringBuilder buff = new StringBuilder();
|
||||
for (ClangLine line : lines) {
|
||||
buff.append(line.getIndentString());
|
||||
List<ClangToken> tokens = line.getAllTokens();
|
||||
|
@ -15,8 +15,7 @@
|
||||
*/
|
||||
package ghidra.app.decompiler;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* An iterator over ClangToken objects. The iterator walks a tree of ClangNode objects based on
|
||||
@ -126,7 +125,7 @@ public class TokenIterator implements Iterator<ClangToken> {
|
||||
* @param forward is true for a forward iterator, false for a backward iterator
|
||||
*/
|
||||
public TokenIterator(ClangToken token, boolean forward) {
|
||||
ArrayList<ClangTokenGroup> groupList = new ArrayList<>();
|
||||
List<ClangTokenGroup> groupList = new ArrayList<>();
|
||||
ClangNode node = token.Parent();
|
||||
while (node != null) {
|
||||
groupList.add((ClangTokenGroup) node);
|
||||
@ -154,7 +153,7 @@ public class TokenIterator implements Iterator<ClangToken> {
|
||||
* @param forward is true for a forward iterator, false for a backward iterator
|
||||
*/
|
||||
public TokenIterator(ClangTokenGroup group, boolean forward) {
|
||||
ArrayList<ClangTokenGroup> groupList = new ArrayList<>();
|
||||
List<ClangTokenGroup> groupList = new ArrayList<>();
|
||||
ClangNode node = group;
|
||||
while (node instanceof ClangTokenGroup) {
|
||||
ClangTokenGroup curGroup = (ClangTokenGroup) node;
|
||||
|
@ -19,6 +19,7 @@ import java.awt.Color;
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import generic.json.Json;
|
||||
import ghidra.app.decompiler.*;
|
||||
|
||||
/**
|
||||
@ -95,7 +96,7 @@ class ClangDecompilerHighlighter implements DecompilerHighlighter {
|
||||
}
|
||||
|
||||
Supplier<? extends Collection<ClangToken>> tokens = () -> highlights.keySet();
|
||||
ColorProvider colorProvider = t -> highlights.get(t);
|
||||
ColorProvider colorProvider = new MappedTokenColorProvider(highlights);
|
||||
decompilerPanel.addHighlighterHighlights(this, tokens, colorProvider);
|
||||
|
||||
clones.forEach(c -> c.applyHighlights());
|
||||
@ -155,6 +156,25 @@ class ClangDecompilerHighlighter implements DecompilerHighlighter {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + ' ' + id;
|
||||
return Json.toString(this, "matcher", "id");
|
||||
}
|
||||
|
||||
private class MappedTokenColorProvider implements ColorProvider {
|
||||
|
||||
private Map<ClangToken, Color> highlights;
|
||||
|
||||
MappedTokenColorProvider(Map<ClangToken, Color> highlights) {
|
||||
this.highlights = highlights;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getColor(ClangToken token) {
|
||||
return highlights.get(token);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Token Matcher Color " + matcher.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,8 +20,6 @@ import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.apache.commons.collections4.map.LazyMap;
|
||||
|
||||
import docking.widgets.EventTrigger;
|
||||
import docking.widgets.fieldpanel.field.Field;
|
||||
import docking.widgets.fieldpanel.support.FieldLocation;
|
||||
@ -42,7 +40,7 @@ import util.CollectionUtils;
|
||||
*
|
||||
* <p>This class maintains the following types of highlights:
|
||||
* <UL>
|
||||
* <LI>Primary Highlights - triggered by user clicking and some user actions; considered transient
|
||||
* <LI> Context Highlights - triggered by user clicking and some user actions; considered transient
|
||||
* and get cleared whenever the location changes. These highlights show state such as the
|
||||
* current field, impact of a variable (via a slicing action), or related syntax (such as
|
||||
* matching braces)
|
||||
@ -77,21 +75,8 @@ public abstract class ClangHighlightController {
|
||||
protected Color defaultHighlightColor = DEFAULT_HIGHLIGHT_COLOR;
|
||||
protected Color defaultParenColor = DEFAULT_HIGHLIGHT_COLOR;
|
||||
|
||||
private TokenHighlights primaryHighlightTokens = new TokenHighlights();
|
||||
|
||||
private Map<Function, List<ClangDecompilerHighlighter>> secondaryHighlightersbyFunction =
|
||||
LazyMap.lazyMap(new HashMap<>(), f -> new ArrayList<>());
|
||||
|
||||
// store the secondary highlighters here in addition to the map below so that we may discern
|
||||
// between secondary highlights and highlight service highlights
|
||||
private Set<ClangDecompilerHighlighter> secondaryHighlighters = new HashSet<>();
|
||||
|
||||
// all highlighters, including secondary and highlight service highlighters
|
||||
private Map<ClangDecompilerHighlighter, TokenHighlights> highlighterHighlights =
|
||||
new HashMap<>();
|
||||
|
||||
// color supplier for secondary highlights
|
||||
private TokenHighlightColors secondaryHighlightColors = new TokenHighlightColors();
|
||||
private TokenHighlights contextHighlightTokens = new TokenHighlights();
|
||||
private UserHighlights userHighlights = new UserHighlights();
|
||||
|
||||
/**
|
||||
* A counter to track updates so that clients know when a buffered highlight request is invalid
|
||||
@ -113,21 +98,8 @@ public abstract class ClangHighlightController {
|
||||
* color.
|
||||
* @return the color provider
|
||||
*/
|
||||
public ColorProvider getRandomColorProvider() {
|
||||
return token -> secondaryHighlightColors.getColor(token.getText());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the token that has the primary highlight applied, if any. If multiple tokens are
|
||||
* highlighted, then the return value is arbitrary.
|
||||
* @return the highlighted text
|
||||
*/
|
||||
public String getPrimaryHighlightedText() {
|
||||
ClangToken highlightedToken = getHighlightedToken();
|
||||
if (highlightedToken != null) {
|
||||
return highlightedToken.getText();
|
||||
}
|
||||
return null;
|
||||
public ColorProvider getGeneratedColorProvider() {
|
||||
return new GeneratedColorProvider();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -139,8 +111,8 @@ public abstract class ClangHighlightController {
|
||||
return updateId;
|
||||
}
|
||||
|
||||
public boolean hasPrimaryHighlight(ClangToken token) {
|
||||
return primaryHighlightTokens.contains(token);
|
||||
public boolean hasContextHighlight(ClangToken token) {
|
||||
return contextHighlightTokens.contains(token);
|
||||
}
|
||||
|
||||
public boolean hasSecondaryHighlight(ClangToken token) {
|
||||
@ -148,26 +120,19 @@ public abstract class ClangHighlightController {
|
||||
}
|
||||
|
||||
public boolean hasSecondaryHighlights(Function function) {
|
||||
return !secondaryHighlightersbyFunction.get(function).isEmpty();
|
||||
return userHighlights.hasSecondaryHighlights(function);
|
||||
}
|
||||
|
||||
public Color getSecondaryHighlight(ClangToken token) {
|
||||
DecompilerHighlighter highlighter = getSecondaryHighlighter(token);
|
||||
if (highlighter != null) {
|
||||
TokenHighlights highlights = highlighterHighlights.get(highlighter);
|
||||
HighlightToken hlToken = highlights.get(token);
|
||||
return hlToken.getColor();
|
||||
}
|
||||
|
||||
return null;
|
||||
return userHighlights.getSecondaryHighlight(token);
|
||||
}
|
||||
|
||||
public TokenHighlightColors getSecondaryHighlightColors() {
|
||||
return secondaryHighlightColors;
|
||||
return userHighlights.getSecondaryHighlightColors();
|
||||
}
|
||||
|
||||
public TokenHighlights getPrimaryHighlights() {
|
||||
return primaryHighlightTokens;
|
||||
return contextHighlightTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -177,8 +142,8 @@ public abstract class ClangHighlightController {
|
||||
* @param function the function
|
||||
* @return the highlighters
|
||||
*/
|
||||
public Set<ClangDecompilerHighlighter> getSecondaryHighlighters(Function function) {
|
||||
return new HashSet<>(secondaryHighlightersbyFunction.get(function));
|
||||
public Set<DecompilerHighlighter> getSecondaryHighlighters(Function function) {
|
||||
return userHighlights.getSecondaryHighlighters(function);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -187,11 +152,8 @@ public abstract class ClangHighlightController {
|
||||
* function-specific.
|
||||
* @return the highlighters
|
||||
*/
|
||||
public Set<ClangDecompilerHighlighter> getGlobalHighlighters() {
|
||||
Set<ClangDecompilerHighlighter> allHighlighters = highlighterHighlights.keySet();
|
||||
Set<ClangDecompilerHighlighter> results = new HashSet<>(allHighlighters);
|
||||
results.removeAll(secondaryHighlighters);
|
||||
return results;
|
||||
public Set<DecompilerHighlighter> getGlobalHighlighters() {
|
||||
return userHighlights.getGlobalHighlighters();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -201,7 +163,7 @@ public abstract class ClangHighlightController {
|
||||
* @see #getPrimaryHighlights()
|
||||
*/
|
||||
public TokenHighlights getHighlighterHighlights(DecompilerHighlighter highlighter) {
|
||||
return highlighterHighlights.get(highlighter);
|
||||
return userHighlights.getHighlights(highlighter);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -209,8 +171,8 @@ public abstract class ClangHighlightController {
|
||||
* @return token or null
|
||||
*/
|
||||
public ClangToken getHighlightedToken() {
|
||||
if (primaryHighlightTokens.size() == 1) {
|
||||
HighlightToken hlToken = CollectionUtils.any(primaryHighlightTokens);
|
||||
if (contextHighlightTokens.size() == 1) {
|
||||
HighlightToken hlToken = CollectionUtils.any(contextHighlightTokens);
|
||||
return hlToken.getToken();
|
||||
}
|
||||
return null;
|
||||
@ -236,7 +198,7 @@ public abstract class ClangHighlightController {
|
||||
updateHighlightColor(token);
|
||||
};
|
||||
|
||||
doClearHighlights(primaryHighlightTokens, clearAll);
|
||||
doClearHighlights(contextHighlightTokens, clearAll);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@ -262,10 +224,9 @@ public abstract class ClangHighlightController {
|
||||
* @param tokens the tokens
|
||||
*/
|
||||
public void togglePrimaryHighlights(Color hlColor, Supplier<List<ClangToken>> tokens) {
|
||||
|
||||
boolean isAllHighlighted = true;
|
||||
for (ClangToken otherToken : tokens.get()) {
|
||||
if (!hasPrimaryHighlight(otherToken)) {
|
||||
for (ClangToken token : tokens.get()) {
|
||||
if (!hasContextHighlight(token)) {
|
||||
isAllHighlighted = false;
|
||||
break;
|
||||
}
|
||||
@ -276,8 +237,7 @@ public abstract class ClangHighlightController {
|
||||
clearPrimaryHighlights();
|
||||
|
||||
if (isAllHighlighted) {
|
||||
// nothing to do; we toggled from 'all on' to 'all off'
|
||||
return;
|
||||
return; // nothing to do; we toggled from 'all on' to 'all off'
|
||||
}
|
||||
|
||||
addPrimaryHighlights(tokens, hlColor);
|
||||
@ -289,9 +249,11 @@ public abstract class ClangHighlightController {
|
||||
*/
|
||||
public void removeSecondaryHighlights(Function f) {
|
||||
|
||||
List<ClangDecompilerHighlighter> highlighters = secondaryHighlightersbyFunction.get(f);
|
||||
for (ClangDecompilerHighlighter highlighter : highlighters) {
|
||||
TokenHighlights highlights = highlighterHighlights.get(highlighter);
|
||||
List<DecompilerHighlighter> highlighters =
|
||||
userHighlights.getSecondaryHighlightersByFunction(f);
|
||||
|
||||
for (DecompilerHighlighter highlighter : highlighters) {
|
||||
TokenHighlights highlights = userHighlights.getHighlights(highlighter);
|
||||
Consumer<ClangToken> clearHighlight = token -> updateHighlightColor(token);
|
||||
doClearHighlights(highlights, clearHighlight);
|
||||
}
|
||||
@ -305,38 +267,16 @@ public abstract class ClangHighlightController {
|
||||
* @see #removeSecondaryHighlights(Function)
|
||||
*/
|
||||
public void removeSecondaryHighlights(ClangToken token) {
|
||||
DecompilerHighlighter highlighter = getSecondaryHighlighter(token);
|
||||
DecompilerHighlighter highlighter = userHighlights.getSecondaryHighlighter(token);
|
||||
if (highlighter != null) {
|
||||
highlighter.dispose(); // this will call removeHighlighterHighlights()
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
private DecompilerHighlighter getSecondaryHighlighter(ClangToken token) {
|
||||
for (DecompilerHighlighter highlighter : secondaryHighlighters) {
|
||||
TokenHighlights highlights = highlighterHighlights.get(highlighter);
|
||||
HighlightToken hlToken = highlights.get(token);
|
||||
if (hlToken != null) {
|
||||
return highlighter;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void removeHighlighter(DecompilerHighlighter highlighter) {
|
||||
|
||||
removeHighlighterHighlights(highlighter);
|
||||
highlighterHighlights.remove(highlighter);
|
||||
secondaryHighlighters.remove(highlighter);
|
||||
|
||||
Collection<List<ClangDecompilerHighlighter>> lists =
|
||||
secondaryHighlightersbyFunction.values();
|
||||
for (List<ClangDecompilerHighlighter> highlighters : lists) {
|
||||
if (highlighters.remove(highlighter)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
userHighlights.remove(highlighter);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -345,7 +285,7 @@ public abstract class ClangHighlightController {
|
||||
*/
|
||||
public void removeHighlighterHighlights(DecompilerHighlighter highlighter) {
|
||||
|
||||
TokenHighlights highlighterTokens = highlighterHighlights.get(highlighter);
|
||||
TokenHighlights highlighterTokens = userHighlights.get(highlighter);
|
||||
if (highlighterTokens == null) {
|
||||
return;
|
||||
}
|
||||
@ -361,59 +301,56 @@ public abstract class ClangHighlightController {
|
||||
* @param function the function
|
||||
* @param highlighter the highlighter
|
||||
*/
|
||||
public void addSecondaryHighlighter(Function function, ClangDecompilerHighlighter highlighter) {
|
||||
|
||||
// Note: this highlighter has likely already been added the the this class, but has not
|
||||
// yet been bound to the given function.
|
||||
secondaryHighlightersbyFunction.get(function).add(highlighter);
|
||||
secondaryHighlighters.add(highlighter);
|
||||
highlighterHighlights.putIfAbsent(highlighter, new TokenHighlights());
|
||||
public void addSecondaryHighlighter(Function function, DecompilerHighlighter highlighter) {
|
||||
userHighlights.addSecondaryHighlighter(function, highlighter);
|
||||
}
|
||||
|
||||
// Note: this is used for all highlight types, secondary and highlighter service highlighters
|
||||
public void addHighlighter(ClangDecompilerHighlighter highlighter) {
|
||||
highlighterHighlights.putIfAbsent(highlighter, new TokenHighlights());
|
||||
userHighlights.add(highlighter);
|
||||
}
|
||||
|
||||
// Note: this is used for all highlight types, secondary and highlighter service highlights
|
||||
public void addHighlighterHighlights(ClangDecompilerHighlighter highlighter,
|
||||
Supplier<? extends Collection<ClangToken>> tokens,
|
||||
ColorProvider colorProvider) {
|
||||
public void addHighlighterHighlights(DecompilerHighlighter highlighter,
|
||||
Supplier<? extends Collection<ClangToken>> tokens, ColorProvider colorProvider) {
|
||||
|
||||
Objects.requireNonNull(highlighter);
|
||||
TokenHighlights highlighterTokens =
|
||||
highlighterHighlights.computeIfAbsent(highlighter, k -> new TokenHighlights());
|
||||
TokenHighlights highlighterTokens = userHighlights.add(highlighter);
|
||||
addTokensToHighlights(tokens.get(), colorProvider, highlighterTokens);
|
||||
}
|
||||
|
||||
private void addPrimaryHighlights(Supplier<? extends Collection<ClangToken>> tokens,
|
||||
Color hlColor) {
|
||||
ColorProvider colorProvider = token -> hlColor;
|
||||
addTokensToHighlights(tokens.get(), colorProvider, primaryHighlightTokens);
|
||||
addPrimaryHighlights(tokens.get(), hlColor);
|
||||
}
|
||||
|
||||
private void addPrimaryHighlights(Collection<ClangToken> tokens, Color hlColor) {
|
||||
ColorProvider colorProvider = new DefaultColorProvider("Tokens Highlight Color", hlColor);
|
||||
addTokensToHighlights(tokens, colorProvider, contextHighlightTokens);
|
||||
}
|
||||
|
||||
public void addPrimaryHighlights(ClangNode parentNode, Set<PcodeOp> ops, Color hlColor) {
|
||||
|
||||
addPrimaryHighlights(parentNode, token -> {
|
||||
PcodeOp op = token.getPcodeOp();
|
||||
return ops.contains(op) ? hlColor : null;
|
||||
});
|
||||
ColorProvider colorProvider = new DefaultColorProvider("PcodeOp Highlight Color", hlColor) {
|
||||
@Override
|
||||
public Color getColor(ClangToken token) {
|
||||
PcodeOp op = token.getPcodeOp();
|
||||
return ops.contains(op) ? hlColor : null;
|
||||
}
|
||||
};
|
||||
|
||||
addPrimaryHighlights(parentNode, colorProvider);
|
||||
}
|
||||
|
||||
public void addPrimaryHighlights(ClangNode parentNode, ColorProvider colorProvider) {
|
||||
|
||||
Set<ClangToken> tokens = new HashSet<>();
|
||||
gatherAllTokens(parentNode, tokens);
|
||||
addTokensToHighlights(tokens, colorProvider::getColor, primaryHighlightTokens);
|
||||
addTokensToHighlights(tokens, colorProvider, contextHighlightTokens);
|
||||
}
|
||||
|
||||
private void addPrimaryHighlights(Collection<ClangToken> tokens, Color hlColor) {
|
||||
ColorProvider colorProvider = token -> hlColor;
|
||||
addTokensToHighlights(tokens, colorProvider, primaryHighlightTokens);
|
||||
}
|
||||
|
||||
private void addTokensToHighlights(Collection<ClangToken> tokens,
|
||||
ColorProvider colorProvider, TokenHighlights currentHighlights) {
|
||||
private void addTokensToHighlights(Collection<ClangToken> tokens, ColorProvider colorProvider,
|
||||
TokenHighlights currentHighlights) {
|
||||
|
||||
updateId++;
|
||||
|
||||
@ -441,7 +378,7 @@ public abstract class ClangHighlightController {
|
||||
}
|
||||
|
||||
private void updateHighlightColor(ClangToken t) {
|
||||
// set the color to the current combined value of both highlight types
|
||||
// set the color to the current combined value of all highlight types
|
||||
Color combinedColor = getCombinedColor(t);
|
||||
t.setHighlight(combinedColor);
|
||||
}
|
||||
@ -469,7 +406,7 @@ public abstract class ClangHighlightController {
|
||||
// note: not sure whether we should always blend all colors or decide to allow some
|
||||
// highlighters have precedence for highlighting
|
||||
|
||||
HighlightToken primaryHl = primaryHighlightTokens.get(t);
|
||||
HighlightToken primaryHl = contextHighlightTokens.get(t);
|
||||
Color blendedHlColor = blendHighlighterColors(t);
|
||||
|
||||
List<Color> allColors = new ArrayList<>();
|
||||
@ -506,12 +443,12 @@ public abstract class ClangHighlightController {
|
||||
return null; // not sure if this can happen
|
||||
}
|
||||
|
||||
Set<ClangDecompilerHighlighter> global = getGlobalHighlighters();
|
||||
Set<ClangDecompilerHighlighter> secondary = getSecondaryHighlighters(function);
|
||||
Iterable<ClangDecompilerHighlighter> it = CollectionUtils.asIterable(global, secondary);
|
||||
Set<DecompilerHighlighter> global = getGlobalHighlighters();
|
||||
Set<DecompilerHighlighter> secondary = getSecondaryHighlighters(function);
|
||||
Iterable<DecompilerHighlighter> it = CollectionUtils.asIterable(global, secondary);
|
||||
Color lastColor = null;
|
||||
for (ClangDecompilerHighlighter highlighter : it) {
|
||||
TokenHighlights highlights = highlighterHighlights.get(highlighter);
|
||||
for (DecompilerHighlighter highlighter : it) {
|
||||
TokenHighlights highlights = userHighlights.get(highlighter);
|
||||
HighlightToken hlToken = highlights.get(token);
|
||||
if (hlToken == null) {
|
||||
continue;
|
||||
@ -542,6 +479,24 @@ public abstract class ClangHighlightController {
|
||||
return highFunction.getFunction();
|
||||
}
|
||||
|
||||
protected void addPrimaryHighlightToTokensForBrace(ClangSyntaxToken token,
|
||||
Color highlightColor) {
|
||||
|
||||
if (DecompilerUtils.isBrace(token)) {
|
||||
highlightBrace(token, highlightColor);
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
private void highlightBrace(ClangSyntaxToken startToken, Color highlightColor) {
|
||||
|
||||
ClangSyntaxToken matchingBrace = DecompilerUtils.getMatchingBrace(startToken);
|
||||
if (matchingBrace != null) {
|
||||
matchingBrace.setMatchingToken(true); // this is a signal to the painter
|
||||
addPrimaryHighlights(Set.of(matchingBrace), highlightColor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If input token is a parenthesis, highlight all tokens between it and its match
|
||||
* @param tok potential parenthesis token
|
||||
@ -609,23 +564,6 @@ public abstract class ClangHighlightController {
|
||||
return results;
|
||||
}
|
||||
|
||||
public void addBraceHighlight(ClangSyntaxToken token, Color highlightColor) {
|
||||
|
||||
if (DecompilerUtils.isBrace(token)) {
|
||||
highlightBrace(token, highlightColor);
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
private void highlightBrace(ClangSyntaxToken startToken, Color highlightColor) {
|
||||
|
||||
ClangSyntaxToken matchingBrace = DecompilerUtils.getMatchingBrace(startToken);
|
||||
if (matchingBrace != null) {
|
||||
matchingBrace.setMatchingToken(true); // this is a signal to the painter
|
||||
addPrimaryHighlights(Set.of(matchingBrace), highlightColor);
|
||||
}
|
||||
}
|
||||
|
||||
public void addListener(ClangHighlightListener listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
@ -642,9 +580,21 @@ public abstract class ClangHighlightController {
|
||||
|
||||
public void dispose() {
|
||||
listeners.clear();
|
||||
primaryHighlightTokens.clear();
|
||||
secondaryHighlighters.clear();
|
||||
secondaryHighlightersbyFunction.clear();
|
||||
highlighterHighlights.clear();
|
||||
contextHighlightTokens.clear();
|
||||
userHighlights.dispose();
|
||||
}
|
||||
|
||||
private class GeneratedColorProvider implements ColorProvider {
|
||||
|
||||
@Override
|
||||
public Color getColor(ClangToken token) {
|
||||
return userHighlights.getSecondaryColor(token.getText());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Generated Color Provider " + userHighlights.getAppliedColorsString();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -39,8 +39,6 @@ import ghidra.program.model.pcode.HighFunction;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Control the GUI layout for displaying tokenized C code
|
||||
*/
|
||||
public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
||||
@ -53,10 +51,10 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
||||
private Field[] fieldList; // Array of fields comprising layout
|
||||
private FontMetrics metrics;
|
||||
private FieldHighlightFactory hlFactory;
|
||||
private ArrayList<LayoutModelListener> listeners;
|
||||
private List<LayoutModelListener> listeners;
|
||||
private Color[] syntaxColor; // Foreground colors.
|
||||
private BigInteger numIndexes = BigInteger.ZERO;
|
||||
private ArrayList<ClangLine> lines = new ArrayList<>();
|
||||
private List<ClangLine> lines = new ArrayList<>();
|
||||
|
||||
private boolean showLineNumbers = true;
|
||||
|
||||
@ -71,7 +69,7 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
||||
buildLayouts(null, null, null, false);
|
||||
}
|
||||
|
||||
public ArrayList<ClangLine> getLines() {
|
||||
public List<ClangLine> getLines() {
|
||||
return lines;
|
||||
}
|
||||
|
||||
@ -248,7 +246,7 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
||||
}
|
||||
}
|
||||
|
||||
private void splitToMaxWidthLines(ArrayList<String> res, String line) {
|
||||
private void splitToMaxWidthLines(List<String> res, String line) {
|
||||
int maxchar;
|
||||
if ((maxWidth == 0) || (indentWidth == 0)) {
|
||||
maxchar = 40;
|
||||
@ -257,7 +255,7 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
||||
maxchar = maxWidth / indentWidth;
|
||||
}
|
||||
String[] toklist = line.split("[ \t]+");
|
||||
StringBuffer buf = new StringBuffer();
|
||||
StringBuilder buf = new StringBuilder();
|
||||
int cursize = 0;
|
||||
boolean atleastone = false;
|
||||
int i = 0;
|
||||
@ -275,7 +273,7 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
||||
res.add(finishLine);
|
||||
cursize = 5;
|
||||
atleastone = false;
|
||||
buf = new StringBuffer();
|
||||
buf = new StringBuilder();
|
||||
buf.append(" ");
|
||||
}
|
||||
else {
|
||||
@ -302,7 +300,7 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
||||
return false; // No error message to add
|
||||
}
|
||||
String[] errlines_init = errmsg.split("[\n\r]+");
|
||||
ArrayList<String> errlines = new ArrayList<>();
|
||||
List<String> errlines = new ArrayList<>();
|
||||
for (String element : errlines_init) {
|
||||
splitToMaxWidthLines(errlines, element);
|
||||
}
|
||||
@ -345,16 +343,16 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
||||
java.util.function.Function<String, SearchMatch> matcher, String searchString,
|
||||
FieldLocation currentLocation) {
|
||||
|
||||
int row = currentLocation.getIndex().intValue();
|
||||
for (int i = row; i < fieldList.length; i++) {
|
||||
ClangTextField field = (ClangTextField) fieldList[i];
|
||||
String partialLine =
|
||||
getTextLineFromOffset((i == row) ? currentLocation : null, field, true);
|
||||
SearchMatch match = matcher.apply(partialLine);
|
||||
int startRow = currentLocation.getIndex().intValue();
|
||||
for (int row = startRow; row < fieldList.length; row++) {
|
||||
ClangTextField field = (ClangTextField) fieldList[row];
|
||||
FieldLocation location = (row == startRow) ? currentLocation : null;
|
||||
String lineText = getLineTextFromOffset(location, field, true);
|
||||
SearchMatch match = matcher.apply(lineText);
|
||||
if (match == SearchMatch.NO_MATCH) {
|
||||
continue;
|
||||
}
|
||||
if (i == row) { // cursor is on this line
|
||||
if (row == startRow) { // cursor is on this line
|
||||
//
|
||||
// The match start for all lines without the cursor will be relative to the start
|
||||
// of the line, which is 0. However, when searching on the row with the cursor,
|
||||
@ -362,13 +360,15 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
||||
// compensate for the difference between the start of the line and the cursor.
|
||||
//
|
||||
String fullLine = field.getText();
|
||||
int cursorOffset = fullLine.length() - partialLine.length();
|
||||
int cursorOffset = fullLine.length() - lineText.length();
|
||||
match.start += cursorOffset;
|
||||
match.end += cursorOffset;
|
||||
}
|
||||
FieldNumberColumnPair pair = getFieldIndexFromOffset(match.start, field);
|
||||
FieldLocation fieldLocation =
|
||||
new FieldLocation(i, pair.getFieldNumber(), 0, pair.getColumn());
|
||||
|
||||
// we use 0 here because currently there is only one field, which is the entire line
|
||||
int fieldNum = 0;
|
||||
int column = getScreenColumnFromOffset(match.start, field);
|
||||
FieldLocation fieldLocation = new FieldLocation(row, fieldNum, 0, column);
|
||||
|
||||
return new FieldBasedSearchLocation(fieldLocation, match.start, match.end - 1,
|
||||
searchString, true);
|
||||
@ -380,17 +380,19 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
||||
java.util.function.Function<String, SearchMatch> matcher, String searchString,
|
||||
FieldLocation currentLocation) {
|
||||
|
||||
int row = currentLocation.getIndex().intValue();
|
||||
for (int i = row; i >= 0; i--) {
|
||||
ClangTextField field = (ClangTextField) fieldList[i];
|
||||
String textLine =
|
||||
getTextLineFromOffset((i == row) ? currentLocation : null, field, false);
|
||||
int startRow = currentLocation.getIndex().intValue();
|
||||
for (int row = startRow; row >= 0; row--) {
|
||||
ClangTextField field = (ClangTextField) fieldList[row];
|
||||
FieldLocation location = (row == startRow) ? currentLocation : null;
|
||||
String lineText = getLineTextFromOffset(location, field, false);
|
||||
|
||||
SearchMatch match = matcher.apply(textLine);
|
||||
SearchMatch match = matcher.apply(lineText);
|
||||
if (match != SearchMatch.NO_MATCH) {
|
||||
FieldNumberColumnPair pair = getFieldIndexFromOffset(match.start, field);
|
||||
FieldLocation fieldLocation =
|
||||
new FieldLocation(i, pair.getFieldNumber(), 0, pair.getColumn());
|
||||
|
||||
// we use 0 here because currently there is only one field, which is the entire line
|
||||
int fieldNum = 0;
|
||||
int column = getScreenColumnFromOffset(match.start, field);
|
||||
FieldLocation fieldLocation = new FieldLocation(row, fieldNum, 0, column);
|
||||
|
||||
return new FieldBasedSearchLocation(fieldLocation, match.start, match.end - 1,
|
||||
searchString, false);
|
||||
@ -483,7 +485,7 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
||||
return findNextTokenGoingBackward(function, searchString, currentLocation);
|
||||
}
|
||||
|
||||
private String getTextLineFromOffset(FieldLocation location, ClangTextField textField,
|
||||
private String getLineTextFromOffset(FieldLocation location, ClangTextField textField,
|
||||
boolean forwardSearch) {
|
||||
|
||||
if (location == null) { // the cursor location is not on this line; use all of the text
|
||||
@ -494,32 +496,28 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
||||
return "";
|
||||
}
|
||||
|
||||
String partialText = textField.getText();
|
||||
|
||||
String lineText = textField.getText();
|
||||
if (forwardSearch) {
|
||||
|
||||
int nextCol = location.getCol();
|
||||
|
||||
// protects against the location column being out of range (this can happen if we're
|
||||
// searching forward and the cursor is past the last token)
|
||||
if (nextCol >= partialText.length()) {
|
||||
if (nextCol >= lineText.length()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// skip a character to start the next search; this prevents matching the previous match
|
||||
return partialText.substring(nextCol);
|
||||
return lineText.substring(nextCol);
|
||||
}
|
||||
|
||||
// backwards search
|
||||
return partialText.substring(0, location.getCol());
|
||||
return lineText.substring(0, location.getCol());
|
||||
}
|
||||
|
||||
private FieldNumberColumnPair getFieldIndexFromOffset(int screenOffset,
|
||||
ClangTextField textField) {
|
||||
RowColLocation rowColLocation = textField.textOffsetToScreenLocation(screenOffset);
|
||||
|
||||
// we use 0 here because currently there is only one field, which is the entire line
|
||||
return new FieldNumberColumnPair(0, rowColLocation.col());
|
||||
private int getScreenColumnFromOffset(int textOffset, ClangTextField textField) {
|
||||
RowColLocation rowColLocation = textField.textOffsetToScreenLocation(textOffset);
|
||||
return rowColLocation.col();
|
||||
}
|
||||
|
||||
private static class SearchMatch {
|
||||
@ -565,27 +563,4 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
||||
public void flushChanges() {
|
||||
// nothing to do
|
||||
}
|
||||
//==================================================================================================
|
||||
// Inner Classes
|
||||
//==================================================================================================
|
||||
|
||||
private class FieldNumberColumnPair {
|
||||
private final int fieldNumber;
|
||||
private final int column;
|
||||
|
||||
FieldNumberColumnPair(int fieldNumber, int column) {
|
||||
this.fieldNumber = fieldNumber;
|
||||
this.column = column;
|
||||
|
||||
}
|
||||
|
||||
int getFieldNumber() {
|
||||
return fieldNumber;
|
||||
}
|
||||
|
||||
int getColumn() {
|
||||
return column;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -18,8 +18,8 @@ package ghidra.app.decompiler.component;
|
||||
import java.util.List;
|
||||
|
||||
import docking.widgets.fieldpanel.field.*;
|
||||
import docking.widgets.fieldpanel.support.FieldLocation;
|
||||
import docking.widgets.fieldpanel.support.FieldHighlightFactory;
|
||||
import docking.widgets.fieldpanel.support.FieldLocation;
|
||||
import ghidra.app.decompiler.ClangToken;
|
||||
|
||||
public class ClangTextField extends WrappingVerticalLayoutTextField {
|
||||
@ -123,4 +123,12 @@ public class ClangTextField extends WrappingVerticalLayoutTextField {
|
||||
public int getLineNumber() {
|
||||
return lineNumber;
|
||||
}
|
||||
|
||||
public ClangToken getFirstToken() {
|
||||
return tokenList.get(0);
|
||||
}
|
||||
|
||||
public ClangToken getLastToken() {
|
||||
return tokenList.get(tokenList.size() - 1);
|
||||
}
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
||||
|
||||
private FieldHighlightFactory hlFactory;
|
||||
private ClangHighlightController highlightController;
|
||||
private Map<String, ClangDecompilerHighlighter> highlightersById = new HashMap<>();
|
||||
private Map<String, DecompilerHighlighter> highlightersById = new HashMap<>();
|
||||
private PendingHighlightUpdate pendingHighlightUpdate;
|
||||
private SwingUpdateManager highlighCursorUpdater = new SwingUpdateManager(() -> {
|
||||
if (pendingHighlightUpdate != null) {
|
||||
@ -89,6 +89,7 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
||||
}
|
||||
});
|
||||
|
||||
private ActiveMiddleMouse activeMiddleMouse;
|
||||
private int middleMouseHighlightButton;
|
||||
private Color middleMouseHighlightColor;
|
||||
private Color currentVariableHighlightColor;
|
||||
@ -194,7 +195,14 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
||||
return highlightController.getHighlighterHighlights(highligter);
|
||||
}
|
||||
|
||||
private Set<ClangDecompilerHighlighter> getSecondaryHighlihgtersByFunction(Function function) {
|
||||
public TokenHighlights getMiddleMouseHighlights() {
|
||||
if (activeMiddleMouse != null) {
|
||||
return activeMiddleMouse.getHighlights();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Set<DecompilerHighlighter> getSecondaryHighlihgtersByFunction(Function function) {
|
||||
return highlightController.getSecondaryHighlighters(function);
|
||||
}
|
||||
|
||||
@ -212,31 +220,50 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
||||
}
|
||||
|
||||
public void addSecondaryHighlight(ClangToken token) {
|
||||
ColorProvider cp = highlightController.getRandomColorProvider();
|
||||
ColorProvider cp = highlightController.getGeneratedColorProvider();
|
||||
addSecondaryHighlight(token.getText(), cp);
|
||||
}
|
||||
|
||||
public void addSecondaryHighlight(ClangToken token, Color color) {
|
||||
ColorProvider cp = t -> color;
|
||||
ColorProvider cp = new DefaultColorProvider("User Secondary Highlight", color);
|
||||
addSecondaryHighlight(token.getText(), cp);
|
||||
}
|
||||
|
||||
private void addSecondaryHighlight(String tokenText, ColorProvider colorProvider) {
|
||||
NameTokenMatcher matcher = new NameTokenMatcher(tokenText, colorProvider);
|
||||
ClangDecompilerHighlighter highlighter = createHighlighter(matcher);
|
||||
DecompilerHighlighter highlighter = createHighlighter(matcher);
|
||||
applySecondaryHighlights(highlighter);
|
||||
}
|
||||
|
||||
private void applySecondaryHighlights(ClangDecompilerHighlighter highlighter) {
|
||||
private void applySecondaryHighlights(DecompilerHighlighter highlighter) {
|
||||
Function function = decompileData.getFunction();
|
||||
highlightController.addSecondaryHighlighter(function, highlighter);
|
||||
highlighter.applyHighlights();
|
||||
}
|
||||
|
||||
private void togglePrimaryHighlight(FieldLocation location, Field field, Color highlightColor) {
|
||||
private void toggleMiddleMouseHighlight(FieldLocation location, Field field) {
|
||||
ClangToken token = ((ClangTextField) field).getToken(location);
|
||||
Supplier<List<ClangToken>> lazyTokens = () -> findTokensByName(token.getText());
|
||||
highlightController.togglePrimaryHighlights(middleMouseHighlightColor, lazyTokens);
|
||||
ColorProvider cp = new MiddleMouseColorProvider();
|
||||
NameTokenMatcher matcher = new NameTokenMatcher(token.getText(), cp);
|
||||
|
||||
ActiveMiddleMouse previousMiddleMouse = activeMiddleMouse;
|
||||
activeMiddleMouse = null;
|
||||
|
||||
if (previousMiddleMouse != null) {
|
||||
|
||||
// middle mousing always clears the last middle-mouse highlight
|
||||
previousMiddleMouse.clear();
|
||||
|
||||
if (previousMiddleMouse.matches(token)) {
|
||||
// middle mousing on the same token clears, but does not create a new highlight
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DecompilerHighlighter newMiddleMouseHighlighter = createHighlighter(matcher);
|
||||
ActiveMiddleMouse newMiddleMouse = new ActiveMiddleMouse(token, newMiddleMouseHighlighter);
|
||||
newMiddleMouse.apply();
|
||||
activeMiddleMouse = newMiddleMouse;
|
||||
}
|
||||
|
||||
void addHighlighterHighlights(ClangDecompilerHighlighter highlighter,
|
||||
@ -248,14 +275,14 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
||||
highlightController.removeHighlighterHighlights(highlighter);
|
||||
}
|
||||
|
||||
public ClangDecompilerHighlighter createHighlighter(CTokenHighlightMatcher tm) {
|
||||
public DecompilerHighlighter createHighlighter(CTokenHighlightMatcher tm) {
|
||||
UUID uuId = UUID.randomUUID();
|
||||
String id = uuId.toString();
|
||||
return createHighlighter(id, tm);
|
||||
}
|
||||
|
||||
public ClangDecompilerHighlighter createHighlighter(String id, CTokenHighlightMatcher tm) {
|
||||
ClangDecompilerHighlighter currentHighlighter = highlightersById.get(id);
|
||||
public DecompilerHighlighter createHighlighter(String id, CTokenHighlightMatcher tm) {
|
||||
DecompilerHighlighter currentHighlighter = highlightersById.get(id);
|
||||
if (currentHighlighter != null) {
|
||||
currentHighlighter.dispose();
|
||||
}
|
||||
@ -271,7 +298,7 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
||||
}
|
||||
|
||||
void removeHighlighter(String id) {
|
||||
ClangDecompilerHighlighter highlighter = highlightersById.remove(id);
|
||||
DecompilerHighlighter highlighter = highlightersById.remove(id);
|
||||
highlightController.removeHighlighter(highlighter);
|
||||
}
|
||||
|
||||
@ -339,11 +366,15 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
||||
|
||||
private void cloneGlobalHighlighters(DecompilerPanel sourcePanel) {
|
||||
|
||||
Set<ClangDecompilerHighlighter> globalHighlighters =
|
||||
Set<DecompilerHighlighter> globalHighlighters =
|
||||
sourcePanel.highlightController.getGlobalHighlighters();
|
||||
for (ClangDecompilerHighlighter otherHighlighter : globalHighlighters) {
|
||||
for (DecompilerHighlighter otherHighlighter : globalHighlighters) {
|
||||
|
||||
ClangDecompilerHighlighter newHighlighter = otherHighlighter.clone(this);
|
||||
if (!(otherHighlighter instanceof ClangDecompilerHighlighter clangHighlighter)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DecompilerHighlighter newHighlighter = clangHighlighter.clone(this);
|
||||
highlightersById.put(newHighlighter.getId(), newHighlighter);
|
||||
|
||||
TokenHighlights otherHighlighterTokens =
|
||||
@ -374,15 +405,20 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
||||
// clone will match the cloned decompiler.
|
||||
//
|
||||
Function function = decompileData.getFunction();
|
||||
Set<ClangDecompilerHighlighter> secondaryHighlighters =
|
||||
Set<DecompilerHighlighter> secondaryHighlighters =
|
||||
sourcePanel.getSecondaryHighlihgtersByFunction(function);
|
||||
|
||||
//
|
||||
// We do NOT clone the secondary highlighters. This allows the user the remove them
|
||||
// from the primary provider without effecting the cloned provider and vice versa.
|
||||
//
|
||||
for (ClangDecompilerHighlighter highlighter : secondaryHighlighters) {
|
||||
ClangDecompilerHighlighter newHighlighter = highlighter.copy(this);
|
||||
for (DecompilerHighlighter highlighter : secondaryHighlighters) {
|
||||
|
||||
if (!(highlighter instanceof ClangDecompilerHighlighter clangHighlighter)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DecompilerHighlighter newHighlighter = clangHighlighter.copy(this);
|
||||
highlightersById.put(newHighlighter.getId(), newHighlighter);
|
||||
applySecondaryHighlights(newHighlighter);
|
||||
}
|
||||
@ -455,9 +491,8 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
||||
return;
|
||||
}
|
||||
|
||||
Set<ClangDecompilerHighlighter> globalHighlighters =
|
||||
highlightController.getGlobalHighlighters();
|
||||
for (ClangDecompilerHighlighter highlighter : globalHighlighters) {
|
||||
Set<DecompilerHighlighter> globalHighlighters = highlightController.getGlobalHighlighters();
|
||||
for (DecompilerHighlighter highlighter : globalHighlighters) {
|
||||
highlighter.clearHighlights();
|
||||
highlighter.applyHighlights();
|
||||
}
|
||||
@ -470,9 +505,9 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
||||
return;
|
||||
}
|
||||
|
||||
Set<ClangDecompilerHighlighter> secondaryHighlighters =
|
||||
Set<DecompilerHighlighter> secondaryHighlighters =
|
||||
getSecondaryHighlihgtersByFunction(function);
|
||||
for (ClangDecompilerHighlighter highlighter : secondaryHighlighters) {
|
||||
for (DecompilerHighlighter highlighter : secondaryHighlighters) {
|
||||
highlighter.clearHighlights();
|
||||
highlighter.applyHighlights();
|
||||
}
|
||||
@ -719,7 +754,7 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
||||
}
|
||||
|
||||
if (buttonState == middleMouseHighlightButton && clickCount == 1) {
|
||||
togglePrimaryHighlight(location, field, middleMouseHighlightColor);
|
||||
toggleMiddleMouseHighlight(location, field);
|
||||
}
|
||||
}
|
||||
|
||||
@ -770,19 +805,6 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
||||
controller.goToFunction(function, newWindow);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO no idea what this is supposed to be handling...someone doc this please
|
||||
// String labelName = functionToken.getText();
|
||||
// if (labelName.startsWith("func_0x")) {
|
||||
// try {
|
||||
// Address addr =
|
||||
// decompileData.getFunction().getEntryPoint().getAddress(labelName.substring(7));
|
||||
// controller.goToAddress(addr, newWindow);
|
||||
// }
|
||||
// catch (AddressFormatException e) {
|
||||
// controller.goToLabel(labelName, newWindow);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
private void tryGoToLabel(ClangLabelToken token, boolean newWindow) {
|
||||
@ -1377,4 +1399,57 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class MiddleMouseColorProvider implements ColorProvider {
|
||||
|
||||
@Override
|
||||
public Color getColor(ClangToken token) {
|
||||
return middleMouseHighlightColor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Middle Mouse Color Provider " + middleMouseHighlightColor;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A class to track the current middle moused token.
|
||||
*/
|
||||
private class ActiveMiddleMouse {
|
||||
|
||||
private ClangToken token;
|
||||
private DecompilerHighlighter highlighter;
|
||||
|
||||
ActiveMiddleMouse(ClangToken token, DecompilerHighlighter highlighter) {
|
||||
this.token = token;
|
||||
this.highlighter = highlighter;
|
||||
}
|
||||
|
||||
TokenHighlights getHighlights() {
|
||||
return highlightController.getHighlighterHighlights(highlighter);
|
||||
}
|
||||
|
||||
DecompilerHighlighter getHighlighter() {
|
||||
return highlighter;
|
||||
}
|
||||
|
||||
boolean matches(ClangToken other) {
|
||||
return token.getText().equals(other.getText());
|
||||
}
|
||||
|
||||
void clear() {
|
||||
highlightController.removeHighlighter(highlighter);
|
||||
}
|
||||
|
||||
void apply() {
|
||||
applySecondaryHighlights(highlighter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Middle Mouse Token " + token;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -33,9 +33,10 @@ import ghidra.program.model.pcode.*;
|
||||
public class DecompilerUtils {
|
||||
|
||||
/**
|
||||
* Gaither decompiler options from tool and program. If tool is null or does not provide
|
||||
* Gather decompiler options from tool and program. If tool is null or does not provide
|
||||
* a {@link OptionsService} provider only options stored within the program will be consumed.
|
||||
* @param serviceProvider plugin tool or service provider providing access to {@link OptionsService}
|
||||
* @param serviceProvider plugin tool or service provider providing access to
|
||||
* {@link OptionsService}
|
||||
* @param program program
|
||||
* @return decompiler options
|
||||
*/
|
||||
@ -281,7 +282,7 @@ public class DecompilerUtils {
|
||||
* @param function decompiled function
|
||||
* @return true if {@code var} corresponds to existing auto {@code this} parameter, else false
|
||||
*/
|
||||
public static boolean testForAutoParameterThis(HighVariable var, Function function) {
|
||||
public static boolean isThisParameter(HighVariable var, Function function) {
|
||||
if (var instanceof HighParam) {
|
||||
int slot = ((HighParam) var).getSlot();
|
||||
Parameter parameter = function.getParameter(slot);
|
||||
@ -626,7 +627,7 @@ public class DecompilerUtils {
|
||||
|
||||
String destinationStart = label.getText() + ':';
|
||||
Address address = label.getMinAddress();
|
||||
List<ClangToken> tokens = DecompilerUtils.getTokens(root, address);
|
||||
List<ClangToken> tokens = getTokens(root, address);
|
||||
for (ClangToken token : tokens) {
|
||||
if (isGoToStatement(token)) {
|
||||
continue; // ignore any goto statements
|
||||
@ -791,7 +792,7 @@ public class DecompilerUtils {
|
||||
* @param group is the token hierarchy
|
||||
* @return the array of ClangLine objects
|
||||
*/
|
||||
public static ArrayList<ClangLine> toLines(ClangTokenGroup group) {
|
||||
public static List<ClangLine> toLines(ClangTokenGroup group) {
|
||||
|
||||
List<ClangNode> alltoks = new ArrayList<>();
|
||||
group.flatten(alltoks);
|
||||
|
@ -0,0 +1,49 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.decompiler.component;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import ghidra.app.decompiler.ClangToken;
|
||||
|
||||
/**
|
||||
* A color provider that returns a specific color.
|
||||
*/
|
||||
public class DefaultColorProvider implements ColorProvider {
|
||||
|
||||
private Color color;
|
||||
private String prefix;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param prefix a descriptive prefix used in the {@link #toString()} method
|
||||
* @param color the color
|
||||
*/
|
||||
DefaultColorProvider(String prefix, Color color) {
|
||||
this.prefix = prefix;
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getColor(ClangToken token) {
|
||||
return color;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return prefix + ' ' + color;
|
||||
}
|
||||
}
|
@ -50,7 +50,7 @@ public class LocationClangHighlightController extends ClangHighlightController {
|
||||
addPrimaryHighlight(tok, defaultHighlightColor);
|
||||
if (tok instanceof ClangSyntaxToken) {
|
||||
addPrimaryHighlightToTokensForParenthesis((ClangSyntaxToken) tok, defaultParenColor);
|
||||
addBraceHighlight((ClangSyntaxToken) tok, defaultParenColor);
|
||||
addPrimaryHighlightToTokensForBrace((ClangSyntaxToken) tok, defaultParenColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ package ghidra.app.decompiler.component;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import generic.json.Json;
|
||||
import ghidra.app.decompiler.CTokenHighlightMatcher;
|
||||
import ghidra.app.decompiler.ClangToken;
|
||||
|
||||
@ -40,4 +41,9 @@ class NameTokenMatcher implements CTokenHighlightMatcher {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Json.toString(this);
|
||||
}
|
||||
}
|
||||
|
@ -35,11 +35,6 @@ public class NullClangHighlightController extends ClangHighlightController {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrimaryHighlightedText() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPrimaryHighlights(ClangNode parentNode, ColorProvider colorProvider) {
|
||||
// stub
|
||||
@ -51,7 +46,7 @@ public class NullClangHighlightController extends ClangHighlightController {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBraceHighlight(ClangSyntaxToken token, Color highlightColor) {
|
||||
public void addPrimaryHighlightToTokensForBrace(ClangSyntaxToken token, Color highlightColor) {
|
||||
// stub
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ import generic.theme.Gui;
|
||||
*/
|
||||
public class TokenHighlightColors {
|
||||
|
||||
private Map<String, Color> colorsByName = new HashMap<>();
|
||||
private Map<String, Color> colorsByText = new HashMap<>();
|
||||
private List<Color> recentColors = new ArrayList<>();
|
||||
|
||||
private Color generateColor() {
|
||||
@ -42,15 +42,27 @@ public class TokenHighlightColors {
|
||||
}
|
||||
|
||||
public Color getColor(String text) {
|
||||
return colorsByName.computeIfAbsent(text, t -> generateColor());
|
||||
return colorsByText.computeIfAbsent(text, t -> generateColor());
|
||||
}
|
||||
|
||||
public void setColor(String text, Color color) {
|
||||
colorsByName.put(text, color);
|
||||
colorsByText.put(text, color);
|
||||
recentColors.add(color);
|
||||
}
|
||||
|
||||
public List<Color> getRecentColors() {
|
||||
return recentColors;
|
||||
}
|
||||
|
||||
public String getAppliedColorsString() {
|
||||
if (colorsByText.isEmpty()) {
|
||||
return "No tokens highlighted";
|
||||
}
|
||||
return colorsByText.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getAppliedColorsString();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,148 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.decompiler.component;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.commons.collections4.map.LazyMap;
|
||||
|
||||
import ghidra.app.decompiler.ClangToken;
|
||||
import ghidra.app.decompiler.DecompilerHighlighter;
|
||||
import ghidra.program.model.listing.Function;
|
||||
|
||||
/**
|
||||
* A class to manage and track Decompiler highlights created by the user via the UI or from a
|
||||
* script. This class manages secondary and global highlights. For a description of these terms,
|
||||
* see {@link ClangHighlightController}.
|
||||
* <p>
|
||||
* These highlights will remain until cleared explicitly by the user or a client API call.
|
||||
* Contrastingly, context highlights are cleared as the user moves the cursor around the Decompiler
|
||||
* display.
|
||||
*/
|
||||
public class UserHighlights {
|
||||
|
||||
private Map<Function, List<DecompilerHighlighter>> secondaryHighlightersByFunction =
|
||||
LazyMap.lazyMap(new HashMap<>(), f -> new ArrayList<>());
|
||||
|
||||
// store the secondary highlighters here in addition to the map below so that we may discern
|
||||
// between secondary highlights and highlight service highlights
|
||||
private Set<DecompilerHighlighter> secondaryHighlighters = new HashSet<>();
|
||||
|
||||
// all highlighters, including secondary and global highlight service highlighters
|
||||
private Map<DecompilerHighlighter, TokenHighlights> allHighlighterHighlights = new HashMap<>();
|
||||
|
||||
// color supplier for secondary highlights
|
||||
private TokenHighlightColors secondaryHighlightColors = new TokenHighlightColors();
|
||||
|
||||
Color getSecondaryColor(String text) {
|
||||
// Note: this call is used to generate colors for secondary highlighters that this API
|
||||
// creates. Client highlighters will create their own colors.
|
||||
return secondaryHighlightColors.getColor(text);
|
||||
}
|
||||
|
||||
String getAppliedColorsString() {
|
||||
return secondaryHighlightColors.getAppliedColorsString();
|
||||
}
|
||||
|
||||
boolean hasSecondaryHighlights(Function function) {
|
||||
return !secondaryHighlightersByFunction.get(function).isEmpty();
|
||||
}
|
||||
|
||||
Color getSecondaryHighlight(ClangToken token) {
|
||||
DecompilerHighlighter highlighter = getSecondaryHighlighter(token);
|
||||
if (highlighter != null) {
|
||||
TokenHighlights highlights = allHighlighterHighlights.get(highlighter);
|
||||
HighlightToken hlToken = highlights.get(token);
|
||||
return hlToken.getColor();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
TokenHighlightColors getSecondaryHighlightColors() {
|
||||
return secondaryHighlightColors;
|
||||
}
|
||||
|
||||
Set<DecompilerHighlighter> getSecondaryHighlighters(Function function) {
|
||||
return new HashSet<>(secondaryHighlightersByFunction.get(function));
|
||||
}
|
||||
|
||||
Set<DecompilerHighlighter> getGlobalHighlighters() {
|
||||
Set<DecompilerHighlighter> allHighlighters = allHighlighterHighlights.keySet();
|
||||
Set<DecompilerHighlighter> results = new HashSet<>(allHighlighters);
|
||||
results.removeAll(secondaryHighlighters);
|
||||
return results;
|
||||
}
|
||||
|
||||
List<DecompilerHighlighter> getSecondaryHighlightersByFunction(Function f) {
|
||||
return secondaryHighlightersByFunction.get(f);
|
||||
}
|
||||
|
||||
TokenHighlights getHighlights(DecompilerHighlighter highlighter) {
|
||||
return allHighlighterHighlights.get(highlighter);
|
||||
}
|
||||
|
||||
DecompilerHighlighter getSecondaryHighlighter(ClangToken token) {
|
||||
for (DecompilerHighlighter highlighter : secondaryHighlighters) {
|
||||
TokenHighlights highlights = allHighlighterHighlights.get(highlighter);
|
||||
HighlightToken hlToken = highlights.get(token);
|
||||
if (hlToken != null) {
|
||||
return highlighter;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
void addSecondaryHighlighter(Function function, DecompilerHighlighter highlighter) {
|
||||
|
||||
// Note: this highlighter has likely already been added the the this class, but has not
|
||||
// yet been bound to the given function.
|
||||
secondaryHighlightersByFunction.get(function).add(highlighter);
|
||||
secondaryHighlighters.add(highlighter);
|
||||
allHighlighterHighlights.putIfAbsent(highlighter, new TokenHighlights());
|
||||
}
|
||||
|
||||
// This adds the given highlighter. This is for global and secondary highlights. Secondary
|
||||
// highlights will be later registered to this class for the function they apply to.
|
||||
TokenHighlights add(DecompilerHighlighter highlighter) {
|
||||
allHighlighterHighlights.putIfAbsent(highlighter, new TokenHighlights());
|
||||
return allHighlighterHighlights.get(highlighter);
|
||||
}
|
||||
|
||||
void remove(DecompilerHighlighter highlighter) {
|
||||
allHighlighterHighlights.remove(highlighter);
|
||||
secondaryHighlighters.remove(highlighter);
|
||||
|
||||
Collection<List<DecompilerHighlighter>> lists = secondaryHighlightersByFunction.values();
|
||||
for (List<DecompilerHighlighter> highlighters : lists) {
|
||||
if (highlighters.remove(highlighter)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TokenHighlights get(DecompilerHighlighter highlighter) {
|
||||
return allHighlighterHighlights.get(highlighter);
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
secondaryHighlighters.clear();
|
||||
secondaryHighlightersByFunction.clear();
|
||||
allHighlighterHighlights.clear();
|
||||
}
|
||||
}
|
@ -120,7 +120,7 @@ public class FillOutStructureCmd extends BackgroundCommand<Program> {
|
||||
pointerDT = program.getDataTypeManager()
|
||||
.addDataType(pointerDT, DataTypeConflictHandler.DEFAULT_HANDLER);
|
||||
|
||||
boolean isThisParam = DecompilerUtils.testForAutoParameterThis(var, function);
|
||||
boolean isThisParam = DecompilerUtils.isThisParameter(var, function);
|
||||
if (!isThisParam) {
|
||||
commitVariable(var, pointerDT, isThisParam);
|
||||
}
|
||||
|
@ -132,7 +132,7 @@ public class FillOutStructureHelper {
|
||||
}
|
||||
|
||||
if (structDT == null) {
|
||||
if (createClassIfNeeded && DecompilerUtils.testForAutoParameterThis(var, function)) {
|
||||
if (createClassIfNeeded && DecompilerUtils.isThisParameter(var, function)) {
|
||||
structDT = createUniqueClassNamespaceAndStructure(var, (int) size, function);
|
||||
}
|
||||
else {
|
||||
|
@ -1016,6 +1016,13 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
||||
new RemoveAllSecondaryHighlightsAction();
|
||||
setGroupInfo(removeAllSecondadryHighlightsAction, highlightGroup, subGroupPosition++);
|
||||
|
||||
PreviousHighlightedTokenAction previousHighlightedTokenAction =
|
||||
new PreviousHighlightedTokenAction();
|
||||
setGroupInfo(previousHighlightedTokenAction, highlightGroup, subGroupPosition++);
|
||||
|
||||
NextHighlightedTokenAction nextHighlightedTokenAction = new NextHighlightedTokenAction();
|
||||
setGroupInfo(nextHighlightedTokenAction, highlightGroup, subGroupPosition++);
|
||||
|
||||
String convertGroup = "7 - Convert Group";
|
||||
subGroupPosition = 0;
|
||||
RemoveEquateAction removeEquateAction = new RemoveEquateAction();
|
||||
@ -1122,6 +1129,8 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
||||
addLocalAction(setSecondaryHighlightColorChooserAction);
|
||||
addLocalAction(removeSecondaryHighlightAction);
|
||||
addLocalAction(removeAllSecondadryHighlightsAction);
|
||||
addLocalAction(nextHighlightedTokenAction);
|
||||
addLocalAction(previousHighlightedTokenAction);
|
||||
addLocalAction(convertBinaryAction);
|
||||
addLocalAction(convertDecAction);
|
||||
addLocalAction(convertFloatAction);
|
||||
@ -1165,7 +1174,14 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
||||
private void setGroupInfo(DockingAction action, String group, int subGroupPosition) {
|
||||
MenuData popupMenuData = action.getPopupMenuData();
|
||||
popupMenuData.setMenuGroup(group);
|
||||
popupMenuData.setMenuSubGroup(Integer.toString(subGroupPosition));
|
||||
|
||||
// Some groups have numbers reach double-digits. These will not compare correctly unless
|
||||
// padded. Ensure all string numbers are at least 2 digits.
|
||||
String numberString = Integer.toString(subGroupPosition);
|
||||
if (numberString.length() == 1) {
|
||||
numberString = '0' + numberString;
|
||||
}
|
||||
popupMenuData.setMenuSubGroup(numberString);
|
||||
}
|
||||
|
||||
private void graphServiceRemoved() {
|
||||
|
@ -61,7 +61,7 @@ public class DecompilerStructureVariableAction extends CreateStructureVariableAc
|
||||
HighVariable var = tokenAtCursor.getHighVariable();
|
||||
if (var != null && !(var instanceof HighConstant)) {
|
||||
dt = var.getDataType();
|
||||
isThisParam = DecompilerUtils.testForAutoParameterThis(var, function);
|
||||
isThisParam = DecompilerUtils.isThisParameter(var, function);
|
||||
}
|
||||
|
||||
if (dt == null || dt.getLength() > maxPointerSize) {
|
||||
|
@ -0,0 +1,99 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.decompile.actions;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import docking.action.KeyBindingData;
|
||||
import docking.action.MenuData;
|
||||
import docking.widgets.fieldpanel.field.Field;
|
||||
import ghidra.app.decompiler.ClangToken;
|
||||
import ghidra.app.decompiler.TokenIterator;
|
||||
import ghidra.app.decompiler.component.*;
|
||||
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
|
||||
import ghidra.app.util.HelpTopics;
|
||||
import ghidra.util.HelpLocation;
|
||||
|
||||
/**
|
||||
* An action to navigate to the next token highlighted by the user via the middle-mouse.
|
||||
*/
|
||||
public class NextHighlightedTokenAction extends AbstractDecompilerAction {
|
||||
|
||||
public NextHighlightedTokenAction() {
|
||||
super("Next Highlihted Token");
|
||||
|
||||
setPopupMenuData(new MenuData(new String[] { "Next Highlight" }, "Decompile"));
|
||||
setKeyBindingData(new KeyBindingData("Ctrl period"));
|
||||
setHelpLocation(new HelpLocation(HelpTopics.DECOMPILER, "GoToMiddleMouseHighlight"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
|
||||
if (!context.hasRealFunction()) {
|
||||
return false;
|
||||
}
|
||||
DecompilerPanel panel = context.getDecompilerPanel();
|
||||
TokenHighlights highlights = panel.getMiddleMouseHighlights();
|
||||
if (highlights != null) {
|
||||
return highlights.size() > 1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void decompilerActionPerformed(DecompilerActionContext context) {
|
||||
|
||||
DecompilerPanel panel = context.getDecompilerPanel();
|
||||
TokenHighlights highlights = panel.getMiddleMouseHighlights();
|
||||
ClangToken cursorToken = context.getTokenAtCursor();
|
||||
TokenIterator it = new TokenIterator(cursorToken, true);
|
||||
it.next(); // ignore the current token
|
||||
|
||||
if (goToNexToken(panel, it, highlights)) {
|
||||
return; // found another token in the current direction
|
||||
}
|
||||
|
||||
// this means there are no more occurrences in the current direction; wrap the search
|
||||
ClangToken firstToken = getFirstToken(panel);
|
||||
it = new TokenIterator(firstToken, true);
|
||||
goToNexToken(panel, it, highlights);
|
||||
}
|
||||
|
||||
private ClangToken getFirstToken(DecompilerPanel panel) {
|
||||
List<Field> fields = panel.getFields();
|
||||
Field line = fields.get(0);
|
||||
ClangTextField tf = (ClangTextField) line;
|
||||
return tf.getFirstToken();
|
||||
}
|
||||
|
||||
private boolean goToNexToken(DecompilerPanel panel, TokenIterator it,
|
||||
TokenHighlights highlights) {
|
||||
|
||||
while (it.hasNext()) {
|
||||
ClangToken nextToken = it.next();
|
||||
HighlightToken hlToken = highlights.get(nextToken);
|
||||
if (hlToken == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ClangToken token = hlToken.getToken();
|
||||
panel.goToToken(token);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,100 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.decompile.actions;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import docking.action.KeyBindingData;
|
||||
import docking.action.MenuData;
|
||||
import docking.widgets.fieldpanel.field.Field;
|
||||
import ghidra.app.decompiler.ClangToken;
|
||||
import ghidra.app.decompiler.TokenIterator;
|
||||
import ghidra.app.decompiler.component.*;
|
||||
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
|
||||
import ghidra.app.util.HelpTopics;
|
||||
import ghidra.util.HelpLocation;
|
||||
|
||||
/**
|
||||
* An action to navigate to the previous token highlighted by the user via the middle-mouse.
|
||||
*/
|
||||
public class PreviousHighlightedTokenAction extends AbstractDecompilerAction {
|
||||
|
||||
public PreviousHighlightedTokenAction() {
|
||||
super("Previous Highlighted Token");
|
||||
|
||||
setPopupMenuData(new MenuData(new String[] { "Previous Highlight" }, "Decompile"));
|
||||
setKeyBindingData(new KeyBindingData("Ctrl comma"));
|
||||
setHelpLocation(new HelpLocation(HelpTopics.DECOMPILER, "GoToMiddleMouseHighlight"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
|
||||
if (!context.hasRealFunction()) {
|
||||
return false;
|
||||
}
|
||||
DecompilerPanel panel = context.getDecompilerPanel();
|
||||
TokenHighlights highlights = panel.getMiddleMouseHighlights();
|
||||
if (highlights != null) {
|
||||
return highlights.size() > 1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void decompilerActionPerformed(DecompilerActionContext context) {
|
||||
|
||||
DecompilerPanel panel = context.getDecompilerPanel();
|
||||
TokenHighlights highlights = panel.getMiddleMouseHighlights();
|
||||
ClangToken cursorToken = context.getTokenAtCursor();
|
||||
TokenIterator it = new TokenIterator(cursorToken, false);
|
||||
it.next(); // ignore the current token
|
||||
|
||||
if (goToNexToken(panel, it, highlights)) {
|
||||
return; // found another token in the current direction
|
||||
}
|
||||
|
||||
// this means there are no more occurrences in the current direction; wrap the search
|
||||
ClangToken lastToken = getLastToken(panel);
|
||||
it = new TokenIterator(lastToken, false);
|
||||
goToNexToken(panel, it, highlights);
|
||||
}
|
||||
|
||||
private ClangToken getLastToken(DecompilerPanel panel) {
|
||||
List<Field> fields = panel.getFields();
|
||||
int lastLine = fields.size();
|
||||
Field line = fields.get(lastLine - 1);
|
||||
ClangTextField tf = (ClangTextField) line;
|
||||
return tf.getLastToken();
|
||||
}
|
||||
|
||||
private boolean goToNexToken(DecompilerPanel panel, TokenIterator it,
|
||||
TokenHighlights highlights) {
|
||||
|
||||
while (it.hasNext()) {
|
||||
ClangToken nextToken = it.next();
|
||||
HighlightToken hlToken = highlights.get(nextToken);
|
||||
if (hlToken == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ClangToken token = hlToken.getToken();
|
||||
panel.goToToken(token);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -70,4 +70,9 @@ public class SliceHighlightColorProvider implements ColorProvider {
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Slice Color Provider " + hlColor;
|
||||
}
|
||||
}
|
||||
|
@ -46,6 +46,7 @@ import ghidra.app.plugin.core.decompile.actions.*;
|
||||
import ghidra.app.util.AddEditDialog;
|
||||
import ghidra.framework.options.ToolOptions;
|
||||
import ghidra.program.model.listing.CodeUnit;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
public class DecompilerClangTest extends AbstractDecompilerTest {
|
||||
|
||||
@ -383,29 +384,32 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
|
||||
setDecompilerLocation(line, charPosition);
|
||||
|
||||
ClangToken token = getToken();
|
||||
String text = token.getText();
|
||||
assertEquals("_printf", text);
|
||||
String printfText = token.getText();
|
||||
assertEquals("_printf", printfText);
|
||||
|
||||
highlight();
|
||||
highlight(); // "printf" is secondary highlighted
|
||||
|
||||
// 5:30 "a->name"
|
||||
line = 5;
|
||||
charPosition = 38;
|
||||
setDecompilerLocation(line, charPosition);
|
||||
ClangToken token2 = getToken();
|
||||
String text2 = token2.getText();
|
||||
assertEquals("name", text2);
|
||||
String nameText = token2.getText();
|
||||
assertEquals("name", nameText);
|
||||
|
||||
Color color2 = highlight();
|
||||
Color color2 = highlight(); // "name" is secondary highlighted
|
||||
|
||||
// 5:2 "_printf"
|
||||
line = 5;
|
||||
charPosition = 2;
|
||||
setDecompilerLocation(line, charPosition);
|
||||
removeSecondaryHighlight();
|
||||
|
||||
assertNoFieldsSecondaryHighlighted(text);
|
||||
assertAllFieldsHighlighted(text2, color2);
|
||||
Msg.debug(this, "test - remove");
|
||||
|
||||
removeSecondaryHighlight(); // remove "printf" highlight
|
||||
|
||||
assertNoFieldsSecondaryHighlighted(printfText);
|
||||
assertAllFieldsHighlighted(nameText, color2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -812,7 +816,106 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSecondaryHighlighting_MiddleMouseDoesNotClearSecondaryHighlight() {
|
||||
public void testSecondaryHighlighting_MiddleMouse_SecondaryHighlight() {
|
||||
|
||||
/*
|
||||
|
||||
The middle mouse is a secondary highlight so that it persists as the user clicks around.
|
||||
|
||||
Middle mousing on an already middle moused highlight should clear the highlight. Middle
|
||||
mousing on a new token should clear the original highlight and highlight the new token.
|
||||
|
||||
Decomp of '_call_structure_A':
|
||||
|
||||
1|
|
||||
2| void _call_structure_A(A *a)
|
||||
3|
|
||||
4| {
|
||||
5| _printf("call_structure_A: %s\n",a->name);
|
||||
6| _printf("call_structure_A: %s\n",(a->b).name);
|
||||
7| _printf("call_structure_A: %s\n",(a->b).c.name);
|
||||
8| _printf("call_structure_A: %s\n",(a->b).c.d.name);
|
||||
9| _printf("call_structure_A: %s\n",(a->b).c.d.e.name);
|
||||
10| _call_structure_B(&a->b);
|
||||
11| return;
|
||||
12| }
|
||||
|
||||
*/
|
||||
|
||||
decompile("100000d60"); // '_call_structure_A'
|
||||
|
||||
// 5:2 "_printf"
|
||||
int line = 5;
|
||||
int charPosition = 2;
|
||||
setDecompilerLocation(line, charPosition);
|
||||
|
||||
ClangToken token = getToken();
|
||||
String tokenText = token.getText();
|
||||
assertEquals("_printf", tokenText);
|
||||
|
||||
middleMouse();
|
||||
assertCombinedHighlightColor(token);
|
||||
|
||||
middleMouse();
|
||||
assertNoFieldsSecondaryHighlighted(tokenText);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSecondaryHighlighting_MiddleMouse_SecondaryHighlight_NewToken() {
|
||||
|
||||
/*
|
||||
|
||||
The middle mouse is a secondary highlight so that it persists as the user clicks around.
|
||||
|
||||
Middle mousing on an already middle moused highlight should clear the highlight. Middle
|
||||
mousing on a new token should clear the original highlight and highlight the new token.
|
||||
|
||||
Decomp of '_call_structure_A':
|
||||
|
||||
1|
|
||||
2| void _call_structure_A(A *a)
|
||||
3|
|
||||
4| {
|
||||
5| _printf("call_structure_A: %s\n",a->name);
|
||||
6| _printf("call_structure_A: %s\n",(a->b).name);
|
||||
7| _printf("call_structure_A: %s\n",(a->b).c.name);
|
||||
8| _printf("call_structure_A: %s\n",(a->b).c.d.name);
|
||||
9| _printf("call_structure_A: %s\n",(a->b).c.d.e.name);
|
||||
10| _call_structure_B(&a->b);
|
||||
11| return;
|
||||
12| }
|
||||
|
||||
*/
|
||||
|
||||
decompile("100000d60"); // '_call_structure_A'
|
||||
|
||||
// 5:2 "_printf"
|
||||
int line = 5;
|
||||
int charPosition = 2;
|
||||
setDecompilerLocation(line, charPosition);
|
||||
|
||||
ClangToken token = getToken();
|
||||
String tokenText = token.getText();
|
||||
assertEquals("_printf", tokenText);
|
||||
|
||||
middleMouse();
|
||||
assertCombinedHighlightColor(token);
|
||||
|
||||
// 5:30 "a->name"
|
||||
line = 5;
|
||||
charPosition = 38;
|
||||
setDecompilerLocation(line, charPosition);
|
||||
ClangToken token2 = getToken();
|
||||
String text2 = token2.getText();
|
||||
assertEquals("name", text2);
|
||||
|
||||
middleMouse();
|
||||
assertNoFieldsSecondaryHighlighted(tokenText);
|
||||
assertCombinedHighlightColor(token2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSecondaryHighlighting_MiddleMouseDoesNotClearSecondaryHighlight_ExistingHighlight() {
|
||||
|
||||
/*
|
||||
|
||||
@ -850,7 +953,9 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
|
||||
assertCombinedHighlightColor(token);
|
||||
|
||||
middleMouse();
|
||||
assertAllFieldsHighlighted(tokenText, color);
|
||||
ClangToken cursorToken = getToken(provider);
|
||||
Predicate<ClangToken> ignores = t -> t == cursorToken;
|
||||
assertAllFieldsHighlighted(tokenText, color, ignores);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
Loading…
Reference in New Issue
Block a user