moveit2
The MoveIt Motion Planning Framework for ROS 2.
Loading...
Searching...
No Matches
xml_syntax_highlighter.cpp
Go to the documentation of this file.
1/*********************************************************************
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 2022, Bielefeld University, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
17 * * Neither the name of Bielefeld University nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 *********************************************************************/
34
35/* Author: Robert Haschke */
36
38#include <assert.h>
39
40namespace moveit_setup
41{
42XmlSyntaxHighlighter::XmlSyntaxHighlighter(QTextDocument* parent) : QSyntaxHighlighter(parent)
43{
44}
45
46void XmlSyntaxHighlighter::addTag(const QString& tag, const QTextCharFormat& format, const QString& parent)
47{
48 const QString start_pattern("<%1.*?/?>");
49 Rule rule;
50 rule.start = QRegularExpression(start_pattern.arg(tag));
51 rule.end = QRegularExpression(QString("</%1>|<%1[^>]*?/>").arg(tag));
52 rule.format = format;
53 if (!parent.isEmpty())
54 {
55 QString parent_start = start_pattern.arg(parent);
56 rule.parent = std::find_if(rules_.begin(), rules_.end(), [&](const std::pair<int, Rule>& rule) {
57 return rule.second.start.pattern() == parent_start;
58 });
59 }
60 else
61 {
62 rule.parent = rules_.end();
63 }
64
65 rules_.insert(std::make_pair(rules_.size(), rule));
66}
67
68XmlSyntaxHighlighter::Rules::const_iterator
69XmlSyntaxHighlighter::highlight(Rules::const_iterator active, QStringView text, int start, bool search_end, int& end)
70{
71 int offset = end; // when passed, end indicates the end of the opening expression
72 auto next = active; // return value: active rule at end of text
73
74 if (search_end) // find end of active rule
75 {
76#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
77 auto match = active->second.end.matchView(text);
78#else
79 auto match = active->second.end.match(text);
80#endif
81 // when returned, end indicates the end of the closing expression
82 end = match.hasMatch() ? match.capturedEnd() : text.size();
83 setFormat(start, end, active->second.format);
84 if (match.hasMatch())
85 {
86 text = text.left(match.capturedStart()); // drop text after (and including) closing expression
87 next = active->second.parent;
88 }
89 }
90 text = text.mid(offset); // skip opening expression
91 start += offset; // adjust start by skipped offset
92 if (text.isEmpty())
93 return next; // early return
94
95 // highlight remaining text using active's children's rules
96 for (auto it = rules_.begin(); it != rules_.end(); ++it)
97 {
98 const auto& rule = it->second;
99 if (rule.parent != active)
100 continue; // skip wrong rules
101
102 offset = 0; // (re)start at beginning of (clipped) text
103 while (true) // process all matches of rule
104 {
105#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
106 auto match = rule.start.matchView(text, offset);
107#else
108 auto match = rule.start.match(text, offset);
109#endif
110 if (!match.hasMatch())
111 break;
112
113 offset = match.capturedEnd() - match.capturedStart(); // mark end of opening expression in passed text
114 auto result = highlight(it, text.mid(match.capturedStart()), start + match.capturedStart(), true, offset);
115 // returned offset is w.r.t. beginning of _passed_ text: add passed start offset to yield offset w.r.t. text
116 offset += match.capturedStart();
117 if (result == it) // text is ending with this rule
118 {
119 assert(next == active || next == active->second.parent);
120 assert(offset == text.size()); // end should mark the end of the text
121 next = result; // remember return value: active rule at end of text
122 break;
123 }
124 }
125 }
126
127 return next;
128}
129
131{
132 Rules::const_iterator active = previousBlockState() < 0 ? rules_.end() : rules_.find(previousBlockState());
133 int unused = 0;
134 active = highlight(active, QStringView(text), 0, active != rules_.cend(), unused);
135 setCurrentBlockState(active != rules_.cend() ? active->first : -1);
136}
137
138} // namespace moveit_setup
void highlightBlock(const QString &text) override
void addTag(const QString &tag, const QTextCharFormat &format, const QString &parent=QString())
XmlSyntaxHighlighter(QTextDocument *parent=nullptr)