Tab completion is nice, but it still requires you to remember how the name starts. Now you can just type any part you remember and see everything that matches. Think “search” not “autocomplete”.
Tab Completion vs. Fuzzy Finding
Tab completion:
$ ros2 topic echo /rob<TAB>
# Shows: /robot/
$ ros2 topic echo /robot/<TAB><TAB><TAB>...
# Cycles through: base_controller, cmd_vel, diagnostics, joint_states...
# Was it under /robot? Or /robot/sensors? Or was it /sensors/robot?
# Start over: Ctrl+C
Fuzzy finding (new):
$ ros2 topic echo
# Type: "lidar"
# Instantly see ALL topics with "lidar" anywhere in the name:
/robot/sensors/lidar/scan
/front_lidar/points
/safety/lidar_monitor
# Pick with arrows, done!
What Works Now?
ros2 topic echo / info / hz / bw / type - Find topics by any part of their name
ros2 node info - Browse all nodes, filter as you type
ros2 param get - Pick node, then browse its parameters
ros2 run - Find packages/executables without remembering exact names
There are plenty more opportunities where we could integrate fzf, not only in more verbs of ros2cli (e.g. ros2 service) but also in other tools in the ROS ecosystem (e.g. colcon).
I’d love to to see this practice propagate but for this I need the help of the community!
I’m confused by the error message about missing fzf mentioned in the PR. So is fzf only a soft dependency? Or is this just a transitional warning for users who would update a source checkout without running rosdep?
And what about backports? Are they possible? Will you go for them?
This is great! Would love to see that integrated into ros2 cli! I have been using a custom fzf extension over the last couple of years that has been incredibly useful, but also has its limitations
Maybe that’s helpful for the impatient that don’t want to build ros2cli from source in their env or people not using Rolling right now
I am especially interested in the param and run extensions of your PR, as my quick solution doesn’t cover that.
fzf is a dependency in the package.xml so should be handled by rosdep. But since fzf is internally called via subprocess and is not a python import, if it’s not installed, it won’t fail with a clear ModuleNotFoundError so I added a an extra check in the code that will check if it’s installed. If it’s not it will print out a clearer error message.
Regading backports, rolling and kilted have diverged quite a bit - I think for a clean backport of this feature we would need to backport about 4 other commits before this one (and that’s if they are not API-breaking, which I think some are)… so quite a hassle
That’s right! ros2controlcli already depends on ros2cli so fzf should be installed. Then you need to:
1- Update your verb to make the positional argument optional
2- Provide your own list generator function
3- Make use of the interactive_select function with that list you generated
pseudo-code example:
def get_loaded_controller_names(
node,
controller_manager: str,
valid_states:
) -> list[str]:
"""Get list of loaded controller names filtered by state."""
controllers = list_controllers(node, controller_manager).controller
return [c.name for c in controllers if c.state in valid_states]
from ros2cli.helpers import interactive_select
# In add_arguments:
arg = parser.add_argument(
"controller_name",
nargs="?",
default=None,
help="Controller name (optional, interactive if not provided)"
)
# In main:
if args.controller_name is None:
controller_names = get_loaded_controller_names(node, args.controller_manager)
if not controller_names:
return 'No controllers available.'
selected = interactive_select(controller_names, prompt='Select controller:')
if selected is None:
return None # User cancelled
args.controller_name = selected