diff --git a/ros2launch/ros2launch/api/__init__.py b/ros2launch/ros2launch/api/__init__.py index 2240859a..788b0b21 100644 --- a/ros2launch/ros2launch/api/__init__.py +++ b/ros2launch/ros2launch/api/__init__.py @@ -18,6 +18,7 @@ from launch.launch_description_sources import InvalidPythonLaunchFileError from .api import get_share_file_path_from_package +from .api import is_launch_file from .api import launch_a_launch_file from .api import LaunchFileNameCompleter from .api import MultipleLaunchFilesError @@ -28,6 +29,7 @@ __all__ = [ 'get_share_file_path_from_package', + 'is_launch_file', 'InvalidLaunchFileError', 'InvalidPythonLaunchFileError', 'LaunchFileNameCompleter', diff --git a/ros2launch/ros2launch/api/api.py b/ros2launch/ros2launch/api/api.py index bcdf4915..72b491c2 100644 --- a/ros2launch/ros2launch/api/api.py +++ b/ros2launch/ros2launch/api/api.py @@ -73,16 +73,22 @@ def get_launch_file_paths(*, path): launch_file_paths = [] for root, dirs, files in os.walk(path): for file_name in files: - if file_name.endswith(get_launch_file_paths.extensions): - launch_file_paths.append(os.path.join(root, file_name)) + file_path = os.path.join(root, file_name) + if is_launch_file(path=file_path): + launch_file_paths.append(file_path) return launch_file_paths -get_launch_file_paths.extensions = [ +def is_launch_file(path): + """Return True if the path is a launch file.""" + return path.endswith(is_launch_file.extensions) and os.path.isfile(path) + + +is_launch_file.extensions = [ 'launch.' + extension for extension in Parser.get_available_extensions() ] -get_launch_file_paths.extensions.append('launch.py') -get_launch_file_paths.extensions = tuple(get_launch_file_paths.extensions) +is_launch_file.extensions.append('launch.py') +is_launch_file.extensions = tuple(is_launch_file.extensions) def print_a_launch_file(*, launch_file_path): diff --git a/ros2launch/ros2launch/command/launch.py b/ros2launch/ros2launch/command/launch.py index 5b800e76..0e39dc9e 100644 --- a/ros2launch/ros2launch/command/launch.py +++ b/ros2launch/ros2launch/command/launch.py @@ -16,8 +16,15 @@ from ament_index_python.packages import get_package_prefix from ament_index_python.packages import PackageNotFoundError +from argcomplete.completers import FilesCompleter +try: + from argcomplete.completers import SuppressCompleter +except ImportError: + # argcomplete < 1.9.0 + SuppressCompleter = object from ros2cli.command import CommandExtension from ros2launch.api import get_share_file_path_from_package +from ros2launch.api import is_launch_file from ros2launch.api import launch_a_launch_file from ros2launch.api import LaunchFileNameCompleter from ros2launch.api import MultipleLaunchFilesError @@ -26,6 +33,32 @@ from ros2pkg.api import package_name_completer +class SuppressCompleterWorkaround(SuppressCompleter): + """Workaround https://github.com/kislyuk/argcomplete/pull/289 .""" + + def __call__(self, *args, **kwargs): + """Make SupressCompleter callable by returning no completions.""" + return () + + +def package_name_or_launch_file_completer(prefix, parsed_args, **kwargs): + """Complete package names or paths to launch files.""" + pass_through_kwargs = dict(kwargs) + pass_through_kwargs['prefix'] = prefix + pass_through_kwargs['parsed_args'] = parsed_args + + # Complete package names + completions = list(package_name_completer(**pass_through_kwargs)) + + def is_launch_file_or_dir(path): + return is_launch_file(path) or os.path.isdir(path) + + # Complete paths to launch files + completions.extend(filter(is_launch_file_or_dir, FilesCompleter()(**pass_through_kwargs))) + + return completions + + class LaunchCommand(CommandExtension): """Run a launch file.""" @@ -49,17 +82,18 @@ def add_arguments(self, parser, cli_name): arg = parser.add_argument( 'package_name', help='Name of the ROS package which contains the launch file') - arg.completer = package_name_completer + arg.completer = package_name_or_launch_file_completer arg = parser.add_argument( 'launch_file_name', # TODO(wjwwood) make this not optional when full launch path is supported. nargs='?', help='Name of the launch file') + arg.completer = LaunchFileNameCompleter() arg = parser.add_argument( 'launch_arguments', nargs='*', help="Arguments to the launch file; ':=' (for duplicates, last one wins)") - arg.completer = LaunchFileNameCompleter() + arg.completer = SuppressCompleterWorkaround() def main(self, *, parser, args): """Entry point for CLI program."""