Skip to content

Commit

Permalink
Merge remote-tracking branch
Browse files Browse the repository at this point in the history
'origin/GP-2090-dragonmacher-decompiler-brace-actions' (Closes NationalSecurityAgency#4264)
  • Loading branch information
ryanmkurtz committed Jun 8, 2022
2 parents 9030a84 + 951f34a commit dd08e5c
Show file tree
Hide file tree
Showing 9 changed files with 568 additions and 102 deletions.
16 changes: 15 additions & 1 deletion Ghidra/Features/Decompiler/src/main/doc/decompileplugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3981,7 +3981,8 @@
<listitem><para>
Double-clicking a '{' or '}' token, causes the window to navigate to the <emphasis>matching</emphasis> brace
within the window. The cursor is set and the window view is adjusted if
necessary to ensure that the matching brace is visible.
necessary to ensure that the matching brace is visible. Braces may also be navigated via
the keyboard.
</para></listitem>
</varlistentry>
</variablelist>
Expand Down Expand Up @@ -4321,6 +4322,19 @@
</para>
</sect2>

<sect2 id="GoToBrace">
<title>Go To Next/Previous Brace</title>
<para>
These actions are available from the keyboard.
<emphasis role="bold">Shift-Open Bracket</emphasis> will go to the previous enclosing
open brace. <emphasis role="bold">Shift-Close Bracket</emphasis> will go to the
next enclosing closing brace. These key bindings can be changes via the tool options.
</para>
<para>
Paired braces can also be navigated by double-clicking.
</para>
</sect2>

<sect2 id="ActionHighlight">
<title>Highlight</title>
<para>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,12 @@
Double-clicking a '{' or '}' token, causes the window to navigate to the <span class="emphasis"><em>matching</em></span> brace
within the window. The cursor is set and the window view is adjusted if
necessary to ensure that the matching brace is visible.
</p></dd>
</p>
<p>
Braces may also be navigated
<a class="xref" href="DecompilerWindow.html#GoToBrace" title="Go To Brace">via the keyboard</a>.
</p>
</dd>
</dl></div>
</div>
<p>
Expand Down Expand Up @@ -807,6 +812,24 @@
</p>
</div>



<div class="sect2">
<div class="titlepage"><div><div><h3 class="title">
<a name="GoToBrace"></a>Go To Next/Previous Brace</h3></div></div></div>

<p>
These actions are available from the keyboard.
<span class="bold"><strong>Shift-Open Bracket</strong></span> will go to the previous enclosing
open brace. <span class="bold"><strong>Shift-Close Bracket</strong></span> will go to the
next enclosing closing brace. These key bindings can be changes via the tool options.
</p>
<p>
Paired braces can also be navigated by double-clicking.
</p>
</div>


<div class="sect2">
<div class="titlepage"><div><div><h3 class="title">
<a name="ActionHighlight"></a>Highlight</h3></div></div></div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -564,7 +564,7 @@ private void goToBeginningOfLine(List<ClangToken> tokens) {
}
}

private void goToToken(ClangToken token) {
public void goToToken(ClangToken token) {

ClangLine line = token.getLineParent();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,73 @@ public static ClangLabelToken getGoToTargetToken(ClangTokenGroup root, ClangLabe
return null;
}

/**
* Starts at the given token and finds the next enclosing brace, depending on the given
* direction. If going forward, the next unpaired closing brace will be returned; if going
* backward, the next enclosing open brace will be found. If no enclosing braces exist,
* then null is returned.
*
* @param startToken the starting token
* @param forward true for forward; false for backward
* @return the next enclosing brace or null
*/
public static ClangSyntaxToken getNextBrace(ClangToken startToken, boolean forward) {

ClangNode parent = startToken.Parent();
List<ClangNode> list = new ArrayList<>();

ClangNode node = parent;
while (node != null) {
parent = node;
node = node.Parent();
}

parent.flatten(list);

String desiredBrace = "}"; // going down/forward, look for the containing closing brace
if (!forward) {
desiredBrace = "{"; // going up/backward, look for the containing closing brace
Collections.reverse(list);
}

ClangSyntaxToken brace = moveToNextBrace(startToken, list, desiredBrace, forward);
return brace;
}

private static ClangSyntaxToken moveToNextBrace(ClangToken startToken,
List<ClangNode> list, String targetBrace, boolean forward) {

int balance = 0;
int index = list.indexOf(startToken);
int start = index + 1;
for (int i = start; i < list.size(); i++) {

ClangToken token = (ClangToken) list.get(i);
if (!(token instanceof ClangSyntaxToken)) {
continue;
}

ClangSyntaxToken syntaxToken = (ClangSyntaxToken) token;
if (!isBrace(syntaxToken)) {
continue;
}

String nextBrace = syntaxToken.getText();
if (!targetBrace.equals(nextBrace)) { // opposite brace
balance++;
continue;
}

// matching brace; see if it is balanced
if (balance == 0) {
return syntaxToken; // found an unmatched brace of the type we are seeking
}
balance--;
}

return null;
}

public static ClangSyntaxToken getMatchingBrace(ClangSyntaxToken startToken) {

ClangNode parent = startToken.Parent();
Expand Down Expand Up @@ -627,7 +694,7 @@ public static boolean isMatchingBrace(ClangSyntaxToken braceToken,
return !brace.equals(otherBrace);
}

public static boolean isBrace(ClangSyntaxToken token) {
public static boolean isBrace(ClangToken token) {
String text = token.getText();
return "{".equals(text) || "}".equals(text);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -972,6 +972,8 @@ public boolean isEnabledForContext(ActionContext context) {
DebugDecompilerAction debugFunctionAction = new DebugDecompilerAction(controller);
ExportToCAction convertAction = new ExportToCAction();
CloneDecompilerAction cloneDecompilerAction = new CloneDecompilerAction();
GoToNextBraceAction goToNextBraceAction = new GoToNextBraceAction();
GoToPreviousBraceAction goToPreviousBraceAction = new GoToPreviousBraceAction();

addLocalAction(refreshAction);
addLocalAction(selectAllAction);
Expand Down Expand Up @@ -1018,6 +1020,8 @@ public boolean isEnabledForContext(ActionContext context) {
addLocalAction(findReferencesAction);
addLocalAction(propertiesAction);
addLocalAction(cloneDecompilerAction);
addLocalAction(goToNextBraceAction);
addLocalAction(goToPreviousBraceAction);

graphServiceAdded();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/* ###
* 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:https://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.awt.event.InputEvent;
import java.awt.event.KeyEvent;

import docking.action.KeyBindingData;
import ghidra.app.decompiler.ClangSyntaxToken;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.app.decompiler.component.DecompilerUtils;
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.app.util.HelpTopics;
import ghidra.util.HelpLocation;

/**
* Go to the next enclosing closing brace in the forward direction.
*/
public class GoToNextBraceAction extends AbstractDecompilerAction {

public GoToNextBraceAction() {
super("Go To Next Brace");

setHelpLocation(new HelpLocation(HelpTopics.DECOMPILER, "GoToBrace"));
setKeyBindingData(
new KeyBindingData(KeyEvent.VK_CLOSE_BRACKET, InputEvent.SHIFT_DOWN_MASK));
}

@Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
return true;
}

@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {

ClangToken token = context.getTokenAtCursor();
ClangSyntaxToken brace = DecompilerUtils.getNextBrace(token, true);
if (brace != null) {
DecompilerPanel panel = context.getDecompilerPanel();
panel.goToToken(brace);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/* ###
* 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:https://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.awt.event.InputEvent;
import java.awt.event.KeyEvent;

import docking.action.KeyBindingData;
import ghidra.app.decompiler.ClangSyntaxToken;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.app.decompiler.component.DecompilerUtils;
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.app.util.HelpTopics;
import ghidra.util.HelpLocation;

/**
* Go to the previous enclosing opening brace in the backward direction.
*/
public class GoToPreviousBraceAction extends AbstractDecompilerAction {

public GoToPreviousBraceAction() {
super("Go To Previous Brace");

setHelpLocation(new HelpLocation(HelpTopics.DECOMPILER, "GoToBrace"));
setKeyBindingData(new KeyBindingData(KeyEvent.VK_OPEN_BRACKET, InputEvent.SHIFT_DOWN_MASK));
}

@Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
return true;
}

@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {

ClangToken token = context.getTokenAtCursor();
ClangSyntaxToken brace = DecompilerUtils.getNextBrace(token, false);
if (brace != null) {
DecompilerPanel panel = context.getDecompilerPanel();
panel.goToToken(brace);
}
}

}
Loading

0 comments on commit dd08e5c

Please sign in to comment.