GT-2901: Python interpreter keybinding improvements.

This commit is contained in:
Ryan Kurtz 2019-06-10 13:48:27 -04:00
parent d0ee2aa26b
commit b2c65d147d
4 changed files with 89 additions and 37 deletions

View file

@ -21,7 +21,16 @@ import javax.swing.ImageIcon;
import ghidra.app.plugin.core.console.CodeCompletion;
/**
* A connection between an implementation of an interpreter and its generic GUI components.
*/
public interface InterpreterConnection {
/**
* Gets the title of the interpreter.
*
* @return The title of the interpreter
*/
public String getTitle();
/**
@ -31,15 +40,11 @@ public interface InterpreterConnection {
*/
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);
/**
* 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();
}

View file

@ -277,20 +277,6 @@ public class InterpreterPanel extends JPanel implements OptionsChangeListener {
completionWindow.setVisible(false);
e.consume();
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:
// Check for the completion window trigger on input that contains text

View file

@ -55,13 +55,34 @@
</P>
</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>
<BLOCKQUOTE>
<P>
The Ghidra <I>Python Interpreter</I> supports the following keybindings:
The Ghidra <I>Python Interpreter</I> supports the following hard-coded keybindings:
<UL>
<LI><B>CTRL+D:</B>&nbsp;&nbsp;Clear the console and reset the interpreter</LI>
<LI><B>CTRL+I:</B>&nbsp;&nbsp;Interrupt the interpreter</LI>
<LI><B>(up):</B>&nbsp;&nbsp;Move backward in command stack</LI>
<LI><B>(down):</B>&nbsp;&nbsp;Move forward in command stack</LI>
<LI><B>TAB:</B>&nbsp;&nbsp;Show code completion window</LI>
@ -142,14 +163,6 @@
</P>
</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 class="relatedtopic">Related Topics:</P>

View file

@ -15,6 +15,7 @@
*/
package ghidra.python;
import java.awt.event.KeyEvent;
import java.io.*;
import java.util.List;
@ -22,6 +23,9 @@ import javax.swing.ImageIcon;
import org.python.core.PySystemState;
import docking.ActionContext;
import docking.DockingUtils;
import docking.action.*;
import generic.jar.ResourceFile;
import ghidra.app.CorePluginPackage;
import ghidra.app.plugin.PluginCategoryNames;
@ -34,6 +38,7 @@ import ghidra.framework.options.ToolOptions;
import ghidra.framework.plugintool.PluginInfo;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.util.PluginStatus;
import ghidra.util.HelpLocation;
import ghidra.util.task.*;
import resources.ResourceManager;
@ -120,6 +125,45 @@ public class PythonPlugin extends ProgramPlugin
getTool().getService(InterpreterPanelService.class).createInterpreterPanel(this, false);
welcome();
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();
}
@Override
/**
* Interrupts what the interpreter is currently doing.
*/
public void interrupt() {
if (interpreter == null) {
return;
@ -262,7 +308,9 @@ public class PythonPlugin extends ProgramPlugin
console.setPrompt(interpreter.getPrimaryPrompt());
}
@Override
/**
* Resets the interpreter's state.
*/
public void reset() {
// Do an interrupt in case there is a loop or something running