Skip to content

Commit

Permalink
The keyword 'dirname' in launch files is now also interpreted in the …
Browse files Browse the repository at this point in the history
…editor.
  • Loading branch information
atiderko committed Aug 9, 2023
1 parent 8451e7c commit 9060bcf
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 61 deletions.
21 changes: 20 additions & 1 deletion fkie_node_manager/src/fkie_node_manager/editor/graph_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ class GraphViewWidget(QDockWidget):
DATA_ARGS = Qt.UserRole + 7
DATA_DEF_ARGS_NOT_SET = Qt.UserRole + 8
DATA_ARG_NAME = Qt.UserRole + 9
ITEM_TYPE = Qt.UserRole + 10
DATA_FILE_EXISTS = Qt.UserRole + 10
ITEM_TYPE = Qt.UserRole + 11
ITEM_TYPE_INC_FILE = 1
ITEM_TYPE_INC_GROUP_ARG = 2
ITEM_TYPE_INC_ARG = 3
Expand Down Expand Up @@ -216,6 +217,21 @@ def get_include_args(self, arglist, inc_string, from_file):
result[arg].append(incargs[arg])
return result

def get_included_files(self, from_file, inc_string=''):
'''
Returns all included files. If inc_string is not empty only files with raw string containing in inc_string are returned.
:rtype: Tuple(path, raw_data, line, exists)
'''
result = []
file_items = self.graphTreeView.model().match(self.graphTreeView.model().index(0, 0), self.DATA_INC_FILE, from_file, 10, Qt.MatchRecursive)
for index in file_items:
item = self.graphTreeView.model().itemFromIndex(index)
for row_idx in range(item.rowCount()):
inc_item = item.child(row_idx)
if not inc_string or inc_item.data(self.DATA_RAW) in inc_string:
result.append((inc_item.data(self.DATA_INC_FILE), inc_item.data(self.DATA_RAW), inc_item.data(self.DATA_LINE), inc_item.data(self.DATA_FILE_EXISTS)))
return result

def _refill_tree(self, tree, create_tree=True):
deep = 0
file_dsrc = self._root_path
Expand Down Expand Up @@ -304,6 +320,7 @@ def _append_items(self, item, deep, items=[]):
inc_item.setData(inc_file.raw_inc_path, self.DATA_RAW)
inc_item.setData(inc_file.args, self.DATA_ARGS)
inc_item.setData(inc_file.unset_default_args, self.DATA_DEF_ARGS_NOT_SET)
inc_item.setData(inc_file.exists, self.DATA_FILE_EXISTS)
# add included arguments
if inc_file.unset_default_args or inc_file.args:
arg_item = QStandardItem('arguments')
Expand All @@ -316,6 +333,7 @@ def _append_items(self, item, deep, items=[]):
da_item.setData(self.ITEM_TYPE_INC_ARG, self.ITEM_TYPE)
da_item.setData(inc_file.path_or_str, self.DATA_FILE)
da_item.setData(inc_file.inc_path, self.DATA_INC_FILE)
da_item.setData(inc_file.exists, self.DATA_FILE_EXISTS)
da_item.setData(da_name, self.DATA_ARG_NAME)
da_item.setToolTip("This argument is definded as default, but no set while include.")
arg_item.appendRow(da_item)
Expand All @@ -325,6 +343,7 @@ def _append_items(self, item, deep, items=[]):
da_item.setData(self.ITEM_TYPE_INC_ARG, self.ITEM_TYPE)
da_item.setData(inc_file.path_or_str, self.DATA_FILE)
da_item.setData(inc_file.inc_path, self.DATA_INC_FILE)
da_item.setData(inc_file.exists, self.DATA_FILE_EXISTS)
da_item.setData(da_name, self.DATA_ARG_NAME)
arg_item.appendRow(da_item)
inc_item.appendRow(arg_item)
Expand Down
124 changes: 67 additions & 57 deletions fkie_node_manager/src/fkie_node_manager/editor/text_edit.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
# POSSIBILITY OF SUCH DAMAGE.



from python_qt_binding.QtCore import QRegExp, Qt, Signal, QEvent
from python_qt_binding.QtGui import QColor, QFont, QKeySequence, QTextCursor, QTextDocument
import os
Expand Down Expand Up @@ -318,7 +317,9 @@ def file_changed(self, mtime):

def _strip_bad_parts(self, textblock, current_position):
result = textblock
startidx = textblock.rfind('$(find ', 0, current_position)
startidx = textblock.rfind('$(dirname)', 0, current_position)
if startidx == -1:
startidx = textblock.rfind('$(find ', 0, current_position)
if startidx == -1:
startidx = textblock.rfind(' /', 0, current_position)
if startidx == -1:
Expand Down Expand Up @@ -351,67 +352,76 @@ def mouseReleaseEvent(self, event):
if event.modifiers() == Qt.ControlModifier or event.modifiers() == Qt.ShiftModifier:
cursor = self.cursorForPosition(event.pos())
try:
textblock = self._strip_bad_parts(cursor.block().text(), cursor.positionInBlock())
for inc_file in find_included_files(textblock, False, False, search_in_ext=[]):
aval = inc_file.raw_inc_path
aitems = aval.split("'")
for search_for in aitems:
if not search_for:
continue
try:
rospy.logdebug("try to interpret: %s" % search_for)
args_in_name = get_arg_names(search_for)
textblock = self._strip_bad_parts(
cursor.block().text(), cursor.positionInBlock())
r = self.parent.graph_view.get_included_files(
self.filename, textblock)
for inc_file, _raw_text, _line, file_exists in r:
try:
rospy.logdebug(
f"found included file: {inc_file}, exists: {file_exists}")
if file_exists:
event.setAccepted(True)
self.load_request_signal.emit(inc_file)
else:
rospy.logdebug(
f" included files does not exists, look for arguments in {inc_file}")
args_in_name = get_arg_names(inc_file)
resolved_args = {}
# if found arg in the name, try to detect values
if args_in_name:
rospy.logdebug(" args %s in filename found, try to resolve..." % args_in_name)
resolved_args = self.parent.graph_view.get_include_args(args_in_name, search_for, self.filename)
if resolved_args:
params = {}
self._internal_args
# create parameter dialog
for key, val in resolved_args.items():
values = list(val)
# add args defined in current file
if key in self._internal_args and self._internal_args[key] not in values:
values.append(self._internal_args[key])
params[key] = {':type': 'string', ':value': values}
dia = ParameterDialog(params, store_geometry="open_launch_on_click")
dia.setFilterVisible(False)
dia.setWindowTitle('Select Parameter')
if dia.exec_():
params = dia.getKeywords()
search_for = replace_arg(search_for, params)
else:
# canceled -> cancel interpretation
QTextEdit.mouseReleaseEvent(self, event)
return
# now resolve find-statements
rospy.logdebug(" send interpret request to daemon: %s" % search_for)
inc_files = nm.nmd().launch.get_interpreted_path(self.filename, text=[search_for])
for path, exists in inc_files:
try:
rospy.logdebug(" received interpret request from daemon: %s, exists: %d" % (path, exists))
if exists:
event.setAccepted(True)
self.load_request_signal.emit(path)
rospy.logdebug(
f" args {args_in_name} in filename found, try to resolve...")
resolved_args = self.parent.graph_view.get_include_args(
args_in_name, _raw_text, self.filename)
if resolved_args:
params = {}
self._internal_args
# create parameter dialog
for key, val in resolved_args.items():
values = list(val)
# add args defined in current file
if key in self._internal_args and self._internal_args[key] not in values:
values.append(
self._internal_args[key])
params[key] = {
':type': 'string', ':value': values}
dia = ParameterDialog(
params, store_geometry="open_launch_on_click")
dia.setFilterVisible(False)
dia.setWindowTitle('Select Parameter')
if dia.exec_():
params = dia.getKeywords()
inc_file = replace_arg(
inc_file, params)
self.load_request_signal.emit(
inc_file)
else:
_filename, file_extension = os.path.splitext(path)
if file_extension in nm.settings().launch_view_file_ext:
# create a new file, if it does not exists
result = MessageBox.question(self, "File not exists", '\n\n'.join(["Create a new file?", path]), buttons=MessageBox.Yes | MessageBox.No)
if result == MessageBox.Yes:
content = '<launch>\n\n</launch>' if path.endswith('.launch') else ''
nm.nmd().file.save_file(path, content.encode(), 0)
event.setAccepted(True)
self.load_request_signal.emit(path)
except Exception as e:
MessageBox.critical(self, "Error", "File not found %s" % path, detailed_text=utf8(e))
except exceptions.ResourceNotFound as not_found:
MessageBox.critical(self, "Error", "Resource not found %s" % search_for, detailed_text=utf8(not_found.error))
# canceled -> cancel interpretation
QTextEdit.mouseReleaseEvent(
self, event)
return
else:
_filename, file_extension = os.path.splitext(
inc_file)
if file_extension in nm.settings().launch_view_file_ext:
# create a new file, if it does not exists
result = MessageBox.question(self, "File not exists", '\n\n'.join(
["Create a new file?", inc_file]), buttons=MessageBox.Yes | MessageBox.No)
if result == MessageBox.Yes:
content = '<launch>\n\n</launch>' if inc_file.endswith(
'.launch') else ''
nm.nmd().file.save_file(inc_file, content.encode(), 0)
event.setAccepted(True)
self.load_request_signal.emit(
inc_file)
except (Exception, exceptions.ResourceNotFound) as e:
MessageBox.critical(
self, "Error", "File not found %s" % inc_file, detailed_text=utf8(e))
except Exception as err:
print(traceback.format_exc())
MessageBox.critical(self, "Error", "Error while request included file %s" % self.filename, detailed_text=utf8(err))
MessageBox.critical(
self, "Error", "Error while request included file %s" % self.filename, detailed_text=utf8(err))
QTextEdit.mouseReleaseEvent(self, event)

def mouseMoveEvent(self, event):
Expand Down
12 changes: 9 additions & 3 deletions fkie_node_manager_daemon/src/fkie_node_manager_daemon/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
PACKAGE_FILE = 'package.xml'
EMPTY_PATTERN = re.compile('\b', re.I)
INCLUDE_PATTERN = [r"\s*(\$\(find.*?\)[^\"]*)",
r"\s*(\$\(dirname\)[^\"]*)",
r"file=\"(.*?)\"",
r"textfile=\"(.*?)\"",
r"binfile=\"(.*?)\"",
Expand Down Expand Up @@ -257,7 +258,7 @@ def interpret_path(path, pwd='.'):
Otherwise the parameter itself normalized by :py:func:`os.path.normpath` will be returned.
:rtype: str
'''
result = path.strip()
result = path.strip().replace("$(dirname)", pwd)
# try replace package name by package path
pkg_pattern = re.compile(r"\$\(find (.*?)\)/|pkg:\/\/(.*?)/|package:\/\/(.*?)/")
for groups in pkg_pattern.finditer(path):
Expand Down Expand Up @@ -302,7 +303,7 @@ def replace_paths(text, pwd='.'):
Like meth:interpret_path(), but replaces all matches in the text and retain other text.
'''
result = text
path_pattern = re.compile(r"(\$\(find .*?\)/)|(pkg:\/\/.*?/)|(package:\/\/.*?/)")
path_pattern = re.compile(r"(\$\(dirname\)|\$\(find .*?\)/)|(pkg:\/\/.*?/)|(package:\/\/.*?/)")
for groups in path_pattern.finditer(text):
for index in range(groups.lastindex):
path = groups.groups()[index]
Expand Down Expand Up @@ -455,7 +456,8 @@ def find_included_files(string,
search_in_ext=SEARCH_IN_EXT,
resolve_args={},
unique_files=[],
rec_depth=0):
rec_depth=0,
filename=None):
'''
If the `string` parameter is a valid file the content of this file will be parsed.
In other case the `string` is parsed to find included files.
Expand Down Expand Up @@ -492,6 +494,10 @@ def find_included_files(string,
count_nl = content[match.start():match.end()].count('\n')
content = content[:match.start()] + '\n' * count_nl + content[match.end():]
match = comment_pattern.search(content, match.start())
if filename is not None:
pwd = os.path.dirname(filename)
if '://' in pwd:
pwd = re.sub(r"^.*://[^/]*", "", pwd)
inc_files_forward_args = []
# replace the arguments and detect arguments for include-statements
resolve_args_intern = {}
Expand Down

0 comments on commit 9060bcf

Please sign in to comment.