mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 18:29:37 +02:00
GT-2901: Python interpreter keybinding improvements.
This commit is contained in:
parent
d0ee2aa26b
commit
b2c65d147d
4 changed files with 89 additions and 37 deletions
|
@ -21,7 +21,16 @@ import javax.swing.ImageIcon;
|
||||||
|
|
||||||
import ghidra.app.plugin.core.console.CodeCompletion;
|
import ghidra.app.plugin.core.console.CodeCompletion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A connection between an implementation of an interpreter and its generic GUI components.
|
||||||
|
*/
|
||||||
public interface InterpreterConnection {
|
public interface InterpreterConnection {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the title of the interpreter.
|
||||||
|
*
|
||||||
|
* @return The title of the interpreter
|
||||||
|
*/
|
||||||
public String getTitle();
|
public String getTitle();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,15 +40,11 @@ public interface InterpreterConnection {
|
||||||
*/
|
*/
|
||||||
public ImageIcon getIcon();
|
public ImageIcon getIcon();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a {@link List} of {@link CodeCompletion code completions} for the given command.
|
||||||
|
*
|
||||||
|
* @param cmd The command to get code completions for
|
||||||
|
* @return A {@link List} of {@link CodeCompletion code completions} for the given command
|
||||||
|
*/
|
||||||
public List<CodeCompletion> getCompletions(String cmd);
|
public List<CodeCompletion> getCompletions(String cmd);
|
||||||
|
|
||||||
/**
|
|
||||||
* Interrupts what the interpreter is currently doing.
|
|
||||||
*/
|
|
||||||
public void interrupt();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resets the interpreter. Each interpreter can define what "reset" for them means.
|
|
||||||
*/
|
|
||||||
public void reset();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -277,20 +277,6 @@ public class InterpreterPanel extends JPanel implements OptionsChangeListener {
|
||||||
completionWindow.setVisible(false);
|
completionWindow.setVisible(false);
|
||||||
e.consume();
|
e.consume();
|
||||||
break;
|
break;
|
||||||
case KeyEvent.VK_D:
|
|
||||||
if (e.isControlDown()) {
|
|
||||||
// Ctrl+D - reset interpreter
|
|
||||||
e.consume();
|
|
||||||
interpreter.reset();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case KeyEvent.VK_I:
|
|
||||||
if (e.isControlDown()) {
|
|
||||||
// Ctrl+I - interrupt interpreter
|
|
||||||
e.consume();
|
|
||||||
interpreter.interrupt();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
|
|
||||||
// Check for the completion window trigger on input that contains text
|
// Check for the completion window trigger on input that contains text
|
||||||
|
|
|
@ -55,13 +55,34 @@
|
||||||
</P>
|
</P>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
<H2><A name="Clear_Interpreter"></A>Clear <IMG border="0" src="images/erase16.png"></H2>
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<P>
|
||||||
|
This command clears the interpreter's display. Its effect is purely visual.
|
||||||
|
It does not affect the state of the interpreter in any way.
|
||||||
|
</P>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
<H2><A name="Interrupt_Interpreter"></A>Interrupt <IMG border="0" src="images/dialog-cancel.png"></H2>
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<P>
|
||||||
|
This command issues a keyboard interrupt to the interpreter, which can be used to interrupt
|
||||||
|
long running commands or loops.
|
||||||
|
</P>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
<H2><A name="Reset_Interpreter"></A>Reset <IMG border="0" src="images/reload3.png"></H2>
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<P>
|
||||||
|
This command resets the interpreter, which clears the display and resets all state.
|
||||||
|
</P>
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
<H2>Keybindings</H2>
|
<H2>Keybindings</H2>
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<P>
|
<P>
|
||||||
The Ghidra <I>Python Interpreter</I> supports the following keybindings:
|
The Ghidra <I>Python Interpreter</I> supports the following hard-coded keybindings:
|
||||||
<UL>
|
<UL>
|
||||||
<LI><B>CTRL+D:</B> Clear the console and reset the interpreter</LI>
|
|
||||||
<LI><B>CTRL+I:</B> Interrupt the interpreter</LI>
|
|
||||||
<LI><B>(up):</B> Move backward in command stack</LI>
|
<LI><B>(up):</B> Move backward in command stack</LI>
|
||||||
<LI><B>(down):</B> Move forward in command stack</LI>
|
<LI><B>(down):</B> Move forward in command stack</LI>
|
||||||
<LI><B>TAB:</B> Show code completion window</LI>
|
<LI><B>TAB:</B> Show code completion window</LI>
|
||||||
|
@ -142,14 +163,6 @@
|
||||||
</P>
|
</P>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
<H2><A name="Clear_Interpreter"></A>Clear <IMG border="0" src="images/erase16.png"></H2>
|
|
||||||
<BLOCKQUOTE>
|
|
||||||
<P>
|
|
||||||
This command clears the interpreter's display. Its effect is purely visual.
|
|
||||||
It does not affect the state of the interpreter in any way.
|
|
||||||
</P>
|
|
||||||
</BLOCKQUOTE>
|
|
||||||
|
|
||||||
<P align="left" class="providedbyplugin">Provided by: <I>PythonPlugin</I></P>
|
<P align="left" class="providedbyplugin">Provided by: <I>PythonPlugin</I></P>
|
||||||
|
|
||||||
<P class="relatedtopic">Related Topics:</P>
|
<P class="relatedtopic">Related Topics:</P>
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.python;
|
package ghidra.python;
|
||||||
|
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -22,6 +23,9 @@ import javax.swing.ImageIcon;
|
||||||
|
|
||||||
import org.python.core.PySystemState;
|
import org.python.core.PySystemState;
|
||||||
|
|
||||||
|
import docking.ActionContext;
|
||||||
|
import docking.DockingUtils;
|
||||||
|
import docking.action.*;
|
||||||
import generic.jar.ResourceFile;
|
import generic.jar.ResourceFile;
|
||||||
import ghidra.app.CorePluginPackage;
|
import ghidra.app.CorePluginPackage;
|
||||||
import ghidra.app.plugin.PluginCategoryNames;
|
import ghidra.app.plugin.PluginCategoryNames;
|
||||||
|
@ -34,6 +38,7 @@ import ghidra.framework.options.ToolOptions;
|
||||||
import ghidra.framework.plugintool.PluginInfo;
|
import ghidra.framework.plugintool.PluginInfo;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.framework.plugintool.util.PluginStatus;
|
import ghidra.framework.plugintool.util.PluginStatus;
|
||||||
|
import ghidra.util.HelpLocation;
|
||||||
import ghidra.util.task.*;
|
import ghidra.util.task.*;
|
||||||
import resources.ResourceManager;
|
import resources.ResourceManager;
|
||||||
|
|
||||||
|
@ -120,6 +125,45 @@ public class PythonPlugin extends ProgramPlugin
|
||||||
getTool().getService(InterpreterPanelService.class).createInterpreterPanel(this, false);
|
getTool().getService(InterpreterPanelService.class).createInterpreterPanel(this, false);
|
||||||
welcome();
|
welcome();
|
||||||
console.addFirstActivationCallback(() -> resetInterpreter());
|
console.addFirstActivationCallback(() -> resetInterpreter());
|
||||||
|
createActions();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates various actions for the plugin.
|
||||||
|
*/
|
||||||
|
private void createActions() {
|
||||||
|
|
||||||
|
// Interrupt Interpreter
|
||||||
|
DockingAction interruptAction = new DockingAction("Interrupt Interpreter", getName()) {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionContext context) {
|
||||||
|
interrupt();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
interruptAction.setDescription("Interrupt Interpreter");
|
||||||
|
interruptAction.setToolBarData(
|
||||||
|
new ToolBarData(ResourceManager.loadImage("images/dialog-cancel.png"), null));
|
||||||
|
interruptAction.setEnabled(true);
|
||||||
|
interruptAction.setKeyBindingData(
|
||||||
|
new KeyBindingData(KeyEvent.VK_I, DockingUtils.CONTROL_KEY_MODIFIER_MASK));
|
||||||
|
interruptAction.setHelpLocation(new HelpLocation(getTitle(), "Interrupt_Interpreter"));
|
||||||
|
console.addAction(interruptAction);
|
||||||
|
|
||||||
|
// Reset Interpreter
|
||||||
|
DockingAction resetAction = new DockingAction("Reset Interpreter", getName()) {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionContext context) {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
resetAction.setDescription("Reset Interpreter");
|
||||||
|
resetAction.setToolBarData(
|
||||||
|
new ToolBarData(ResourceManager.loadImage("images/reload3.png"), null));
|
||||||
|
resetAction.setEnabled(true);
|
||||||
|
resetAction.setKeyBindingData(
|
||||||
|
new KeyBindingData(KeyEvent.VK_D, DockingUtils.CONTROL_KEY_MODIFIER_MASK));
|
||||||
|
resetAction.setHelpLocation(new HelpLocation(getTitle(), "Reset_Interpreter"));
|
||||||
|
console.addAction(resetAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -253,7 +297,9 @@ public class PythonPlugin extends ProgramPlugin
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
|
* Interrupts what the interpreter is currently doing.
|
||||||
|
*/
|
||||||
public void interrupt() {
|
public void interrupt() {
|
||||||
if (interpreter == null) {
|
if (interpreter == null) {
|
||||||
return;
|
return;
|
||||||
|
@ -262,7 +308,9 @@ public class PythonPlugin extends ProgramPlugin
|
||||||
console.setPrompt(interpreter.getPrimaryPrompt());
|
console.setPrompt(interpreter.getPrimaryPrompt());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
|
* Resets the interpreter's state.
|
||||||
|
*/
|
||||||
public void reset() {
|
public void reset() {
|
||||||
|
|
||||||
// Do an interrupt in case there is a loop or something running
|
// Do an interrupt in case there is a loop or something running
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue