Skip to content

Commit 526401b

Browse files
Custom Completion Finder for fetching topic prototype. (#995)
* Double yaml encoding for fetching topic prototype. Signed-off-by: Leander Stephen D'Souza <[email protected]> * Use a custom completer for yaml strings. Signed-off-by: Leander Stephen D'Souza <[email protected]> --------- Signed-off-by: Leander Stephen D'Souza <[email protected]>
1 parent 1a0dcf2 commit 526401b

File tree

2 files changed

+33
-3
lines changed

2 files changed

+33
-3
lines changed

ros2topic/ros2topic/api/__init__.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
from time import sleep
1818
from typing import Optional
1919

20+
from argcomplete import CompletionFinder
21+
2022
import rclpy
2123

2224
from rclpy.duration import Duration
@@ -161,6 +163,17 @@ def _get_msg_class(node, topic, include_hidden_topics):
161163
raise RuntimeError("The message type '%s' is invalid" % message_type)
162164

163165

166+
class YamlCompletionFinder(CompletionFinder):
167+
def quote_completions(
168+
self, completions: list[str],
169+
cword_prequote: str, last_wordbreak_pos: Optional[int]):
170+
171+
# For YAML content, return as-is without escaping
172+
if not any('-' in c for c in completions):
173+
return completions
174+
return super().quote_completions(completions, cword_prequote, last_wordbreak_pos)
175+
176+
164177
class TopicMessagePrototypeCompleter:
165178
"""Callable returning a message prototype."""
166179

@@ -169,7 +182,8 @@ def __init__(self, *, topic_type_key=None):
169182

170183
def __call__(self, prefix, parsed_args, **kwargs):
171184
message = get_message(getattr(parsed_args, self.topic_type_key))
172-
return [message_to_yaml(message())]
185+
yaml_snippet = "'" + message_to_yaml(message()) + "'"
186+
return [yaml_snippet]
173187

174188

175189
def profile_configure_short_keys(

ros2topic/ros2topic/verb/pub.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
from typing import Optional
1717
from typing import TypeVar
1818

19+
import argcomplete
20+
1921
import rclpy
2022
from rclpy.node import Node
2123
from rclpy.qos import QoSProfile
@@ -25,7 +27,7 @@
2527
from ros2topic.api import add_qos_arguments
2628
from ros2topic.api import positive_float
2729
from ros2topic.api import profile_configure_short_keys
28-
from ros2topic.api import TopicMessagePrototypeCompleter
30+
from ros2topic.api import TopicMessagePrototypeCompleter, YamlCompletionFinder
2931
from ros2topic.api import TopicNameCompleter
3032
from ros2topic.api import TopicTypeCompleter
3133
from ros2topic.verb import VerbExtension
@@ -108,6 +110,10 @@ def add_arguments(self, parser, cli_name):
108110
parser.add_argument(
109111
'-n', '--node-name',
110112
help='Name of the created publishing node')
113+
114+
# Use the custom completion finder
115+
argcomplete.autocomplete = YamlCompletionFinder(parser)
116+
111117
add_qos_arguments(parser, 'publish', 'default')
112118
add_direct_node_arguments(parser)
113119

@@ -173,7 +179,17 @@ def publisher(
173179
if yaml_file:
174180
msg_reader = read_msg_from_yaml(yaml_file)
175181
else:
176-
values_dictionary = yaml.safe_load(values)
182+
try:
183+
# Handle cases where the user pastes the autocompleted bash safe string
184+
if '^J' in values:
185+
values = values.replace("'", '')
186+
values = values.replace('^J', '\n')
187+
188+
values_dictionary = yaml.safe_load(values)
189+
190+
except (yaml.parser.ParserError, yaml.scanner.ScannerError):
191+
return 'The passed value needs to be in YAML string or a dictionary'
192+
177193
if not isinstance(values_dictionary, dict):
178194
return 'The passed value needs to be a dictionary in YAML format'
179195

0 commit comments

Comments
 (0)