aboutsummaryrefslogtreecommitdiffstats
path: root/src/libs/3rdparty/syntax-highlighting/src/lib/rule.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/3rdparty/syntax-highlighting/src/lib/rule.cpp')
-rw-r--r--src/libs/3rdparty/syntax-highlighting/src/lib/rule.cpp139
1 files changed, 111 insertions, 28 deletions
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/rule.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/rule.cpp
index 22f59e02114..98fe756875a 100644
--- a/src/libs/3rdparty/syntax-highlighting/src/lib/rule.cpp
+++ b/src/libs/3rdparty/syntax-highlighting/src/lib/rule.cpp
@@ -10,8 +10,8 @@
#include "definition_p.h"
#include "ksyntaxhighlighting_logging.h"
#include "rule_p.h"
-#include "xml_p.h"
#include "worddelimiters_p.h"
+#include "xml_p.h"
#include <QString>
#include <QXmlStreamReader>
@@ -31,9 +31,7 @@ static bool isOctalChar(QChar c)
static bool isHexChar(QChar c)
{
- return isDigit(c)
- || (c <= QLatin1Char('f') && QLatin1Char('a') <= c)
- || (c <= QLatin1Char('F') && QLatin1Char('A') <= c);
+ return isDigit(c) || (c <= QLatin1Char('f') && QLatin1Char('a') <= c) || (c <= QLatin1Char('F') && QLatin1Char('A') <= c);
}
static int matchEscapedChar(const QString &text, int offset)
@@ -44,9 +42,18 @@ static int matchEscapedChar(const QString &text, int offset)
const auto c = text.at(offset + 1);
switch (c.unicode()) {
// control chars
- case 'a': case 'b': case 'e': case 'f':
- case 'n': case 'r': case 't': case 'v':
- case '"': case '\'': case '?': case '\\':
+ case 'a':
+ case 'b':
+ case 'e':
+ case 'f':
+ case 'n':
+ case 'r':
+ case 't':
+ case 'v':
+ case '"':
+ case '\'':
+ case '?':
+ case '\\':
return offset + 2;
// hex encoded character
@@ -59,8 +66,14 @@ static int matchEscapedChar(const QString &text, int offset)
return offset;
// octal encoding, simple \0 is OK, too, unlike simple \x above
- case '0': case '1': case '2': case '3':
- case '4': case '5': case '6': case '7':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
if (offset + 2 < text.size() && isOctalChar(text.at(offset + 2))) {
if (offset + 3 < text.size() && isOctalChar(text.at(offset + 3)))
return offset + 4;
@@ -81,6 +94,13 @@ static QString replaceCaptures(const QString &pattern, const QStringList &captur
return result;
}
+Rule::~Rule()
+{
+ if (!m_additionalDeliminator.isEmpty() || !m_weakDeliminator.isEmpty()) {
+ delete m_wordDelimiters;
+ }
+}
+
Definition Rule::definition() const
{
return m_def.definition();
@@ -124,12 +144,17 @@ bool Rule::load(QXmlStreamReader &reader)
void Rule::resolveContext()
{
- auto const& def = m_def.definition();
+ auto const &def = m_def.definition();
m_context.resolve(def);
// cache for DefinitionData::wordDelimiters, is accessed VERY often
m_wordDelimiters = &DefinitionData::get(def)->wordDelimiters;
+ if (!m_additionalDeliminator.isEmpty() || !m_weakDeliminator.isEmpty()) {
+ m_wordDelimiters = new WordDelimiters(*m_wordDelimiters);
+ m_wordDelimiters->append(m_additionalDeliminator);
+ m_wordDelimiters->remove(m_weakDeliminator);
+ }
}
void Rule::resolveAttributeFormat(Context *lookupContext)
@@ -151,7 +176,13 @@ bool Rule::doLoad(QXmlStreamReader &reader)
return true;
}
-Rule::Ptr Rule::create(const QStringView &name)
+void Rule::loadAdditionalWordDelimiters(QXmlStreamReader &reader)
+{
+ m_additionalDeliminator = reader.attributes().value(QLatin1String("additionalDeliminator")).toString();
+ m_weakDeliminator = reader.attributes().value(QLatin1String("weakDeliminator")).toString();
+}
+
+Rule::Ptr Rule::create(const QStringRef &name)
{
if (name == QLatin1String("AnyChar"))
return std::make_shared<AnyChar>();
@@ -283,6 +314,12 @@ MatchResult DetectSpaces::doMatch(const QString &text, int offset, const QString
return offset;
}
+bool Float::doLoad(QXmlStreamReader &reader)
+{
+ loadAdditionalWordDelimiters(reader);
+ return true;
+}
+
MatchResult Float::doMatch(const QString &text, int offset, const QStringList &) const
{
if (offset > 0 && !isWordDelimiter(text.at(offset - 1)))
@@ -344,6 +381,12 @@ MatchResult HlCChar::doMatch(const QString &text, int offset, const QStringList
return offset;
}
+bool HlCHex::doLoad(QXmlStreamReader &reader)
+{
+ loadAdditionalWordDelimiters(reader);
+ return true;
+}
+
MatchResult HlCHex::doMatch(const QString &text, int offset, const QStringList &) const
{
if (offset > 0 && !isWordDelimiter(text.at(offset - 1)))
@@ -367,6 +410,12 @@ MatchResult HlCHex::doMatch(const QString &text, int offset, const QStringList &
return offset;
}
+bool HlCOct::doLoad(QXmlStreamReader &reader)
+{
+ loadAdditionalWordDelimiters(reader);
+ return true;
+}
+
MatchResult HlCOct::doMatch(const QString &text, int offset, const QStringList &) const
{
if (offset > 0 && !isWordDelimiter(text.at(offset - 1)))
@@ -411,11 +460,7 @@ bool IncludeRules::includeAttribute() const
bool IncludeRules::doLoad(QXmlStreamReader &reader)
{
const auto s = reader.attributes().value(QLatin1String("context"));
-#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
- const auto split = s.split(QLatin1String("##"), QString::KeepEmptyParts);
-#else
- const auto split = s.split(QString::fromLatin1("##"), Qt::KeepEmptyParts);
-#endif
+ const auto split = s.split(QLatin1String("##"), Qt::KeepEmptyParts);
if (split.isEmpty())
return false;
m_contextName = split.at(0).toString();
@@ -433,6 +478,12 @@ MatchResult IncludeRules::doMatch(const QString &text, int offset, const QString
return offset;
}
+bool Int::doLoad(QXmlStreamReader &reader)
+{
+ loadAdditionalWordDelimiters(reader);
+ return true;
+}
+
MatchResult Int::doMatch(const QString &text, int offset, const QStringList &) const
{
if (offset > 0 && !isWordDelimiter(text.at(offset - 1)))
@@ -466,6 +517,8 @@ bool KeywordListRule::doLoad(QXmlStreamReader &reader)
m_hasCaseSensitivityOverride = false;
}
+ loadAdditionalWordDelimiters(reader);
+
return !m_keywordList->isEmpty();
}
@@ -478,11 +531,10 @@ MatchResult KeywordListRule::doMatch(const QString &text, int offset, const QStr
return offset;
if (m_hasCaseSensitivityOverride) {
- if (m_keywordList->contains(QStringView(text).mid(offset, newOffset - offset),
- m_caseSensitivityOverride))
+ if (m_keywordList->contains(text.midRef(offset, newOffset - offset), m_caseSensitivityOverride))
return newOffset;
} else {
- if (m_keywordList->contains(QStringView(text).mid(offset, newOffset - offset)))
+ if (m_keywordList->contains(text.midRef(offset, newOffset - offset)))
return newOffset;
}
@@ -540,21 +592,52 @@ bool RegExpr::doLoad(QXmlStreamReader &reader)
const auto isMinimal = Xml::attrToBool(reader.attributes().value(QLatin1String("minimal")));
const auto isCaseInsensitive = Xml::attrToBool(reader.attributes().value(QLatin1String("insensitive")));
- m_regexp.setPatternOptions((isMinimal ? QRegularExpression::InvertedGreedinessOption : QRegularExpression::NoPatternOption) | (isCaseInsensitive ? QRegularExpression::CaseInsensitiveOption : QRegularExpression::NoPatternOption));
+ m_regexp.setPatternOptions((isMinimal ? QRegularExpression::InvertedGreedinessOption : QRegularExpression::NoPatternOption)
+ | (isCaseInsensitive ? QRegularExpression::CaseInsensitiveOption : QRegularExpression::NoPatternOption)
+ // DontCaptureOption is removed by resolvePostProcessing() when necessary
+ | QRegularExpression::DontCaptureOption);
- // optimize the pattern for the non-dynamic case, we use them OFTEN
m_dynamic = Xml::attrToBool(reader.attributes().value(QLatin1String("dynamic")));
+
+ return !m_regexp.pattern().isEmpty();
+}
+
+void KSyntaxHighlighting::RegExpr::resolvePostProcessing()
+{
+ if (m_isResolved)
+ return;
+
+ m_isResolved = true;
+ bool hasCapture = false;
+
+ // disable DontCaptureOption when reference a context with dynamic rule
+ if (auto *ctx = context().context()) {
+ for (const Rule::Ptr &rule : ctx->rules()) {
+ if (rule->isDynamic()) {
+ hasCapture = true;
+ m_regexp.setPatternOptions(m_regexp.patternOptions() & ~QRegularExpression::DontCaptureOption);
+ break;
+ }
+ }
+ }
+
+ // optimize the pattern for the non-dynamic case, we use them OFTEN
if (!m_dynamic) {
m_regexp.optimize();
}
- // always using m_regexp.isValid() would be better, but parses the regexp and thus is way too expensive for release builds
+ bool isValid = m_regexp.isValid();
+ if (!isValid) {
+ // DontCaptureOption with back reference capture is an error, remove this option then try again
+ if (!hasCapture) {
+ m_regexp.setPatternOptions(m_regexp.patternOptions() & ~QRegularExpression::DontCaptureOption);
+ isValid = m_regexp.isValid();
+ }
- if (Log().isDebugEnabled()) {
- if (!m_regexp.isValid())
+ if (!isValid) {
qCDebug(Log) << "Invalid regexp:" << m_regexp.pattern();
+ }
}
- return !m_regexp.pattern().isEmpty();
}
MatchResult RegExpr::doMatch(const QString &text, int offset, const QStringList &captures) const
@@ -606,8 +689,7 @@ MatchResult StringDetect::doMatch(const QString &text, int offset, const QString
*/
const auto &pattern = m_dynamic ? replaceCaptures(m_string, captures, false) : m_string;
- if (offset + pattern.size() <= text.size()
- && QStringView(text).mid(offset, pattern.size()).compare(pattern, m_caseSensitivity) == 0)
+ if (text.midRef(offset, pattern.size()).compare(pattern, m_caseSensitivity) == 0)
return offset + pattern.size();
return offset;
}
@@ -616,6 +698,7 @@ bool WordDetect::doLoad(QXmlStreamReader &reader)
{
m_word = reader.attributes().value(QLatin1String("String")).toString();
m_caseSensitivity = Xml::attrToBool(reader.attributes().value(QLatin1String("insensitive"))) ? Qt::CaseInsensitive : Qt::CaseSensitive;
+ loadAdditionalWordDelimiters(reader);
return !m_word.isEmpty();
}
@@ -631,7 +714,7 @@ MatchResult WordDetect::doMatch(const QString &text, int offset, const QStringLi
if (offset > 0 && !isWordDelimiter(text.at(offset - 1)) && !isWordDelimiter(text.at(offset)))
return offset;
- if (QStringView(text).mid(offset, m_word.size()).compare(m_word, m_caseSensitivity) != 0)
+ if (text.midRef(offset, m_word.size()).compare(m_word, m_caseSensitivity) != 0)
return offset;
if (text.size() == offset + m_word.size() || isWordDelimiter(text.at(offset + m_word.size())) || isWordDelimiter(text.at(offset + m_word.size() - 1)))