1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 from python_qt_binding.QtCore import QRegExp, Qt
35 from python_qt_binding.QtGui import QColor, QFont, QSyntaxHighlighter, QTextCharFormat
36
37
39 '''
40 Enabled the syntax highlightning for the ROS launch files.
41 '''
42
43 LAUNCH_LAUNCH_CHILDS = ['group', 'node', 'test', 'env', 'remap', 'rosparam', 'param', 'machine', 'include', 'arg']
44 LAUNCH_LAUNCH_ATTR = {'deprecated=': '"message"'}
45 LAUNCH_GROUP_CHILDS = ['node', 'test', 'env', 'remap', 'rosparam', 'param', 'machine', 'include', 'arg']
46 LAUNCH_GROUP_ATTR = {'ns=': '"foo"',
47 'clear_params=': '"true|false"'
48 }
49 LAUNCH_MACHINE_CHILDS = ['env']
50 LAUNCH_MACHINE_ATTR = {'name=': '"machine-name"',
51 'address=': '"blah.willowgarage.com"',
52 'env-loader=': '"/opt/ros/fuerte/env.sh"',
53 'default=': '"true|false|never"',
54 'user=': '"username"',
55 'password=': '"passwhat"',
56 'timeout=': '"10.0"'
57 }
58 LAUNCH_NODE_CHILDS = ['env', 'remap', 'rosparam', 'param']
59 LAUNCH_NODE_ATTR = {'pkg=': '"mypackage"',
60 'type=': '"nodetype"',
61 'name=': '"nodename"',
62 'args=': '"arg1"',
63 'machine=': '"machine-name"',
64 'respawn=': '"true"',
65 'required=': '"true"',
66 'ns=': '"foo"',
67 'clear_params=': '"true|false"',
68 'output=': '"log|screen"',
69 'cwd=': '"ROS_HOME|node"',
70 'launch-prefix=': '"prefix arguments"'
71 }
72 LAUNCH_INCLUDE_CHILDS = ['env', 'arg']
73 LAUNCH_INCLUDE_ATTR = {'file=': '"$(find pkg-name)/path/filename.xml"',
74 'ns=': '"foo"',
75 'clear_params=': '"true|false"',
76 'pass_all_args=': '"true|false"'
77 }
78
79 LAUNCH_REMAP_ATTR = {'from=': '"originalname"',
80 'to=': '"newname"'
81 }
82 LAUNCH_ENV_ATTR = {'name=': '"name"',
83 'value=': '"value"'
84 }
85 LAUNCH_PARAM_ATTR = {'name=': '"namespace/name"',
86 'value=': '"value"',
87 'type=': '"str|int|double|bool"',
88 'textfile=': '"$(find pkg-name)/path/file.txt"',
89 'binfile=': '"$(find pkg-name)/path/file"',
90 'command=': '"$(find pkg-name)/exe \'$(find pkg-name)/arg.txt\'"'
91 }
92
93 LAUNCH_ROSPARAM_ATTR = {'command=': '"load|dump|delete"',
94 'file=': '"$(find pkg-name)/path/foo.yaml"',
95 'param=': '"name"',
96 'ns=': '"foo"',
97 'subst_value=': '"true|false"'
98 }
99 LAUNCH_ARG_ATTR = {'name=': '"name"',
100 'value=': '"bar"',
101 'default=': '"defbar"'
102 }
103 LAUNCH_TEST_CHILDS = ['env', 'remap', 'rosparam', 'param']
104 LAUNCH_TEST_ATTR = {'pkg=': '"mypackage"',
105 'type=': '"nodetype"',
106 'name=': '"nodename"',
107 'test-name=': '"test_name"',
108 'args=': '"arg1"',
109 'ns=': '"foo"',
110 'clear_params=': '"true|false"',
111 'retry=': '"0"',
112 'cwd=': '"ROS_HOME|node"',
113 'launch-prefix=': '"prefix arguments"',
114 'time-limit=': '"60.0"'
115 }
116
117 LAUNCH_CHILDS = {'launch': LAUNCH_LAUNCH_CHILDS,
118 'group': LAUNCH_GROUP_CHILDS,
119 'machine': LAUNCH_MACHINE_CHILDS,
120 'node': LAUNCH_NODE_CHILDS,
121 'include': LAUNCH_INCLUDE_CHILDS,
122 'remap': [],
123 'env': [],
124 'param': [],
125 'rosparam': [],
126 'arg': [],
127 'test': LAUNCH_TEST_CHILDS
128 }
129
130 LAUNCH_ATT_GLOBAL = {'if=': '""', 'unless=': '""'}
131 LAUNCH_LAUNCH_ATTR.update(LAUNCH_ATT_GLOBAL)
132 LAUNCH_GROUP_ATTR.update(LAUNCH_ATT_GLOBAL)
133 LAUNCH_MACHINE_ATTR.update(LAUNCH_ATT_GLOBAL)
134 LAUNCH_NODE_ATTR.update(LAUNCH_ATT_GLOBAL)
135 LAUNCH_INCLUDE_ATTR.update(LAUNCH_ATT_GLOBAL)
136 LAUNCH_REMAP_ATTR.update(LAUNCH_ATT_GLOBAL)
137 LAUNCH_ENV_ATTR.update(LAUNCH_ATT_GLOBAL)
138 LAUNCH_PARAM_ATTR.update(LAUNCH_ATT_GLOBAL)
139 LAUNCH_ROSPARAM_ATTR.update(LAUNCH_ATT_GLOBAL)
140 LAUNCH_ARG_ATTR.update(LAUNCH_ATT_GLOBAL)
141 LAUNCH_TEST_ATTR.update(LAUNCH_ATT_GLOBAL)
142
143 LAUNCH_ATTR = {'launch': LAUNCH_LAUNCH_ATTR,
144 'group': LAUNCH_GROUP_ATTR,
145 'machine': LAUNCH_MACHINE_ATTR,
146 'node': LAUNCH_NODE_ATTR,
147 'include': LAUNCH_INCLUDE_ATTR,
148 'remap': LAUNCH_REMAP_ATTR,
149 'env': LAUNCH_ENV_ATTR,
150 'param': LAUNCH_PARAM_ATTR,
151 'rosparam': LAUNCH_ROSPARAM_ATTR,
152 'arg': LAUNCH_ARG_ATTR,
153 'test': LAUNCH_TEST_ATTR,
154 }
155
156 STATE_COMMENT = 2
157 STATE_STRING = 4
158
160 QSyntaxHighlighter.__init__(self, parent)
161 self.rules = []
162 self.comment_start = QRegExp("<!--")
163 self.comment_end = QRegExp("-->")
164 self.comment_format = self._create_format(Qt.darkGray, 'italic')
165
166
167 self.rules.append((self._create_regexp("</?|/?>"), self._create_format(QColor(24, 24, 24))))
168
169 tag_list = '|'.join(["\\b%s\\b" % t for t in self.LAUNCH_CHILDS.keys()])
170 self.rules.append((self._create_regexp(tag_list), self._create_format(Qt.darkRed)))
171
172 attr_list = '|'.join(set(["\\b%s" % attr for v in self.LAUNCH_ATTR.values() for attr in v.keys()]))
173 self.rules.append((self._create_regexp(attr_list), self._create_format(QColor(0, 100, 0))))
174
175 self.rule_arg = (self._create_regexp("\\$\\(.*\\)"), self._create_format(QColor(77, 0, 38)))
176
177 self.rules.append((self._create_regexp("<!DOCTYPE.*>"), self._create_format(Qt.lightGray)))
178 self.rules.append((self._create_regexp("<\\?xml.*\\?>"), self._create_format(Qt.lightGray)))
179
180 self.rules.append((self._create_regexp("^\s*[_.\w]*\s*:"), self._create_format(Qt.darkBlue)))
181
182 self.rules.append((self._create_regexp("'.*'"), self._create_format(Qt.blue)))
183
184 self.rules.append((self._create_regexp("^\s*-"), self._create_format(Qt.darkRed, 'bold')))
185
186 self.rules.append((self._create_regexp("\\d+"), self._create_format(QColor(127, 64, 127))))
187
188 self.string_pattern = QRegExp("\"")
189 self.string_format = self._create_format(Qt.blue)
190
191 self._tag_hl_range = []
192 self._tag_hl_last = set()
193
195 _regexp = QRegExp()
196 _regexp.setMinimal(True)
197 _regexp.setPattern(pattern)
198 return _regexp
199
210
212 for pattern, form in self.rules:
213 index = pattern.indexIn(text)
214 while index >= 0:
215 length = pattern.matchedLength()
216 frmt = form
217 if self._in_hl_range(index, self._tag_hl_range):
218 frmt = QTextCharFormat(form)
219 if not self._end_tag_found:
220 frmt.setForeground(Qt.red)
221 frmt.setFontWeight(QFont.Bold)
222 self.setFormat(index, length, frmt)
223 index = pattern.indexIn(text, index + length)
224 self._tag_hl_range = []
225 self.setCurrentBlockState(0)
226
227 self._comments_idx = []
228 idx_start_cmt = 0
229 comment_length = 0
230 if self.previousBlockState() == -1 or not self.previousBlockState() & self.STATE_COMMENT:
231 idx_start_cmt = self.comment_start.indexIn(text)
232 while idx_start_cmt >= 0:
233 idx_end = self.comment_end.indexIn(text, idx_start_cmt)
234 comment_length = 0
235 if idx_end == -1:
236 self.setCurrentBlockState(self.STATE_COMMENT)
237 comment_length = len(text) - idx_start_cmt
238 else:
239 comment_length = idx_end - idx_start_cmt + self.comment_end.matchedLength()
240 self._comments_idx.append((idx_start_cmt, comment_length))
241 self.setFormat(idx_start_cmt, comment_length, self.comment_format)
242 idx_start_cmt = self.comment_start.indexIn(text, idx_start_cmt + comment_length)
243
244 idx_start = self.string_pattern.indexIn(text)
245 if self.previousBlockState() != -1 and self.previousBlockState() & self.STATE_STRING:
246 strlen = idx_start + self.string_pattern.matchedLength()
247 if idx_start == -1:
248 strlen = len(text)
249 self.setCurrentBlockState(self.currentBlockState() + self.STATE_STRING)
250 self.setFormat(0, strlen, self.string_format)
251 idx_start = self.string_pattern.indexIn(text, strlen)
252 idx_search = idx_start + 1
253 while idx_start >= 0:
254
255 if not self._in_hl_range(idx_search, self._comments_idx):
256 idx_end = self.string_pattern.indexIn(text, idx_search)
257 strlen = 0
258 if not self._in_hl_range(idx_end, self._comments_idx):
259 if idx_end == -1:
260 self.setCurrentBlockState(self.currentBlockState() + self.STATE_STRING)
261 strlen = len(text) - idx_start
262 else:
263 strlen = idx_end - idx_start + self.string_pattern.matchedLength()
264 idx_search = idx_start + strlen
265 self.setFormat(idx_start, strlen, self.string_format)
266 idx_start = self.string_pattern.indexIn(text, idx_search)
267 idx_search = idx_start + 1
268 else:
269 idx_search = idx_end + 1
270 else:
271 idx_start = self.string_pattern.indexIn(text, idx_search)
272 idx_search = idx_start + 1
273
274 index = self.rule_arg[0].indexIn(text)
275 while index >= 0:
276 if not self._in_hl_range(index, self._comments_idx):
277 length = self.rule_arg[0].matchedLength()
278 self.setFormat(index, length, self.rule_arg[1])
279 index = self.rule_arg[0].indexIn(text, index + length)
280
282 text = block.text()
283 word, idx_word = self._get_current_word(text, position)
284 for hlblock in self._tag_hl_last:
285 self.rehighlightBlock(hlblock)
286 self._tag_hl_last.clear()
287 self._tag_hl_range = [(idx_word, len(word))]
288 next_block = block
289 open_braces = 0
290 closed_braces = 0
291 idx_search = idx_word
292 rindex = -1
293 loop = 0
294 tag_len = 0
295 if self._isclosetag(word):
296
297 opentag = '<%s' % self._get_tag(word)
298 tag_len = len(opentag)
299 while rindex == -1 and next_block.isValid():
300 rindex = text.rfind(opentag, 0, idx_search)
301 obr, cbr = self._get_braces_count(text[rindex if rindex != -1 else 0:idx_search])
302 open_braces += obr
303 closed_braces += cbr
304 loop += 1
305 if loop > 50000:
306 rindex = -1
307 break
308 if rindex == -1:
309 next_block = next_block.previous()
310 text = next_block.text()
311 idx_search = len(text)
312 elif open_braces <= closed_braces:
313 idx_search = rindex
314 rindex = -1
315 elif self._isopentag(word):
316
317 closetag = QRegExp("</%s>|/>" % self._get_tag(word))
318 closetag.setMinimal(True)
319 while rindex == -1 and next_block.isValid():
320 rindex = closetag.indexIn(text, idx_search)
321 max_search_idx = rindex + closetag.matchedLength() if rindex != -1 else len(text)
322 obr, cbr = self._get_braces_count(text[idx_search:max_search_idx])
323 open_braces += obr
324 closed_braces += cbr
325 loop += 1
326 if loop > 50000:
327 rindex = -1
328 break
329 if rindex == -1:
330 next_block = next_block.next()
331 text = next_block.text()
332 idx_search = 0
333 elif open_braces > closed_braces:
334 idx_search = rindex + closetag.matchedLength()
335 rindex = -1
336 tag_len = closetag.matchedLength()
337 else:
338 self._tag_hl_range = []
339 self._end_tag_found = rindex != -1
340 if self._tag_hl_range and block != next_block:
341 self.rehighlightBlock(block)
342 self._tag_hl_last.add(block)
343 if rindex != -1:
344 self._tag_hl_range.append((rindex, tag_len))
345 self.rehighlightBlock(next_block)
346 self._tag_hl_last.add(next_block)
347
349 closed_short = text.count('/>')
350 closed_long = text.count('</')
351 cmnt_long = text.count('<!')
352 openbr = text.count('<') - closed_long - cmnt_long
353 return openbr, closed_short + closed_long
354
356 return word.startswith('<') and '/' not in word
357
359 return '/>' == word or word.startswith('</')
360
362 return word.strip('</>')
363
365 word = ''
366 idx_start = position
367 for i in reversed(range(0, position)):
368 if text[i] in [' ', '\n', '=', '"']:
369 break
370 else:
371 word = "%s%s" % (text[i], word)
372 idx_start = i
373 for i in range(position, len(text)):
374 if text[i] in [' ', '\n', '=', '"']:
375 break
376 else:
377 word += text[i]
378 return word, idx_start
379
385
387 text = block.text()
388 next_block = block
389 idx_search = position
390 rindex = -1
391 loop = 0
392
393 opentag = '<'
394 while rindex == -1 and next_block.isValid():
395 rindex = text.rfind(opentag, 0, idx_search)
396 loop += 1
397 if loop > 100:
398 rindex = -1
399 break
400 if rindex == -1:
401 next_block = next_block.previous()
402 text = next_block.text()
403 idx_search = len(text)
404 tag = ''
405 if rindex != -1:
406 for i in range(rindex + 1, len(text)):
407 if text[i] in [' ', '\n', '=', '"', '>']:
408 break
409 else:
410 tag += text[i]
411 return tag
412