Binary Ninja Blog

Taking Action With the Command Palette

One of the many issues facing the development of a complex software product like Binary Ninja is discoverability. In UX design, a feature is “discoverable” if a user is able to locate that feature, understand what they can do with it, and use it to accomplish their goal.

There are many ways of solving this problem, but our favorite is the Command Palette. Unfortunately, the Command Palette itself has a bit of a discoverability problem. So, today, we’re going to show it off a bit and explain why you should consider spending more time using it.

What is the Command Palette?

The Command Palette is, essentially, a search function for available actions. Many users will immediately recognize this as a feature from other software, like Visual Studio Code. Ours works the same way.

Anything a user might want to do through the UI can be registered as an “action”. Actions can range from changing the type of a function (which we’ve bound to the y hotkey while a function name is selected and an entry on the right-click menu on a function name) or opening the Plugin Manager (which is in the menu under Plugins -> Manage Plugins or the hotkey Ctrl-Shift-M/Cmd-Shift-M). They’re a fundamental part of interacting with the UI.

The Command Palette

There’s also an action for launching the command palette, which is Ctrl-P (or Cmd-P for us enlightened macOS users). It’s also available under the View -> Command Palette menu.

Why should I care?

In case it isn’t immediately obvious: Being able to search for any action within Binary Ninja is pretty powerful. You don’t need to remember where it is within menus or which button to click or which hotkey to use. You just need to have a vague idea of what word(s) are part of the action’s name. (Searching is fuzzy as well, so you don’t have to be exact.)

This makes it a great option for new users trying to figure out what features are available. If it’s something you can use, it’s probably in the Command Palette. It’s also a great option for experienced users since it’s often faster to reach a particular command via the keyboard, especially if it doesn’t have a hotkey. If it does have a hotkey, the Command Palette will show you what it is currently bound to.

How do I register actions with the Command Palette myself?

One of the primary selling points of Binary Ninja is our API. Since our entire UI is, itself, a big plugin, we’ve exposed a lot of the things plugin authors might need to make more specialized tooling to fit their needs.

In the case of registering actions with the Command Palette, it’s very easy. Take a look at this example I’ve adapted from our API Docs plugin, written in Python:

# Registering actions, which we often also refer to as "registering you plugin," all stem off of `PluginCommand`
from binaryninja import PluginCommand, BinaryView, Function

# This function will be called when your action is triggered in the ui
def my_plugin_action(bv: BinaryView, func: Function):
  log_info(f"My plugin was called on func `{func}` in bv `{bv}`")

# Register an action with the name "My Plugin Action" in the "Plugins" dropdown menu.
PluginCommand.register_for_function("My Plugin Action", "My plugin description (not used)", my_plugin_action)

We also support this from C++, where you would do the following instead:

// Registering a command using a lambda expression
PluginCommand::RegisterForFunction("MyPlugin\\MyFunctionAction", "Perform an action on a function",
  [](BinaryView* view, Function* func)
  {
    // Perform an action on a view and function
  }
);

// Registering a command using a standard static function
// This also works with functions in the global namespace, e.g.
// "void myCommand(BinaryView* view, Function* func)"
void MyPlugin::MyCommand(BinaryView* view, Function* func)
{
  // Perform an action on a view
}

PluginCommand::RegisterForFunction("MyPlugin\\MySecondFunctionAction", "Perform an action", MyPlugin::MyCommand);

Alternatively, if you’re a UI plugin or want to put your action in a menu other than the Plugins menu, you can take a look at this example I’ve simplified from our Tanto plugin, written in Python:

# You'll need to add these imports to use the action system
from binaryninjaui import Menu, UIAction, UIActionHandler, UIActionContext

# Register an action with the name "My Action" in the namespace "My Plugin"
UIAction.registerAction(f"My Plugin\\My Action")

# Bind the action to the my_plugin_action handler
UIActionHandler.globalActions().bindAction(f"My Plugin\\My Action", UIAction(my_plugin_action()))

# Add the action to the "Plugins" section of the main menu
# This will be placed in the "MyPluginGroup" group at the first position
Menu.mainMenu("Plugins").addAction(f"My Plugin\\My Action", "MyPluginGroup", 0)

We also support this from C++, where you would do the following instead:

#include <action.h>

auto actionName = QString("My Plugin\\My Action");
auto windowMenu = Menu::mainMenu("Plugins");

UIAction::registerAction(actionName);
globalActions()->bindAction(actionName, UIAction([=]() { my_plugin_action();}));
windowMenu->addAction(actionName, "MyPluginGroup", 0);

If you want to see some other ways you can mess with actions in the UI, check out this earlier blog post.