diff options
author | Leandro Melo <leandro.melo@nokia.com> | 2010-05-20 13:56:11 +0200 |
---|---|---|
committer | Leandro Melo <leandro.melo@nokia.com> | 2010-05-20 14:10:34 +0200 |
commit | 55b26868e61bdfc11cb7de23471ebd1dbc9973d3 (patch) | |
tree | 05ecd2ad062dd61734dd7c95cbfb7b093e773954 /src/plugins/texteditor | |
parent | 9b6c86bdc17f184937c36c5a101e6eead36d9e89 (diff) | |
download | qt-creator-55b26868e61bdfc11cb7de23471ebd1dbc9973d3.tar.gz |
Generic highlighter is now part of the text editor plugin.
Diffstat (limited to 'src/plugins/texteditor')
33 files changed, 4310 insertions, 7 deletions
diff --git a/src/plugins/texteditor/generichighlighter/context.cpp b/src/plugins/texteditor/generichighlighter/context.cpp new file mode 100644 index 0000000000..d9fea72fd5 --- /dev/null +++ b/src/plugins/texteditor/generichighlighter/context.cpp @@ -0,0 +1,156 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "context.h" +#include "rule.h" +#include "reuse.h" +#include "dynamicrule.h" +#include "highlightdefinition.h" + +using namespace TextEditor; +using namespace Internal; + +Context::Context() : m_fallthrough(false), m_dynamic(false) +{} + +Context::Context(const Context &context) : + m_id(context.m_id), m_name(context.m_name), m_lineBeginContext(context.m_lineBeginContext), + m_lineEndContext(context.m_lineEndContext), m_fallthroughContext(context.m_fallthroughContext), + m_itemData(context.m_itemData), m_fallthrough(context.m_fallthrough), + m_dynamic(context.m_dynamic), m_instructions(context.m_instructions), + m_definition(context.m_definition) +{ + // Rules need to be deeply copied because of dynamic contexts. + foreach (const QSharedPointer<Rule> &rule, context.m_rules) + m_rules.append(QSharedPointer<Rule>(rule->clone())); +} + +const Context &Context::operator=(Context copy) +{ + swap(copy); + return *this; +} + +Context::~Context() +{} + +void Context::swap(Context &context) +{ + qSwap(m_id, context.m_id); + qSwap(m_name, context.m_name); + qSwap(m_lineBeginContext, context.m_lineBeginContext); + qSwap(m_lineEndContext, context.m_lineEndContext); + qSwap(m_fallthroughContext, context.m_fallthroughContext); + qSwap(m_itemData, context.m_itemData); + qSwap(m_fallthrough, context.m_fallthrough); + qSwap(m_dynamic, context.m_dynamic); + qSwap(m_rules, context.m_rules); + qSwap(m_instructions, context.m_instructions); + qSwap(m_definition, context.m_definition); +} + +void Context::configureId(const int unique) +{ m_id.append(QString::number(unique)); } + +const QString &Context::id() const +{ return m_id; } + +void Context::setName(const QString &name) +{ + m_name = name; + m_id = name; +} + +const QString &Context::name() const +{ return m_name; } + +void Context::setLineBeginContext(const QString &context) +{ m_lineBeginContext = context; } + +const QString &Context::lineBeginContext() const +{ return m_lineBeginContext; } + +void Context::setLineEndContext(const QString &context) +{ m_lineEndContext = context; } + +const QString &Context::lineEndContext() const +{ return m_lineEndContext; } + +void Context::setFallthroughContext(const QString &context) +{ m_fallthroughContext = context; } + +const QString &Context::fallthroughContext() const +{ return m_fallthroughContext; } + +void Context::setItemData(const QString &itemData) +{ m_itemData = itemData; } + +const QString &Context::itemData() const +{ return m_itemData; } + +void Context::setFallthrough(const QString &fallthrough) +{ m_fallthrough = toBool(fallthrough); } + +bool Context::isFallthrough() const +{ return m_fallthrough; } + +void Context::setDynamic(const QString &dynamic) +{ m_dynamic = toBool(dynamic); } + +bool Context::isDynamic() const +{ return m_dynamic; } + +void Context::updateDynamicRules(const QStringList &captures) const +{ + TextEditor::Internal::updateDynamicRules(m_rules, captures); +} + +void Context::addRule(const QSharedPointer<Rule> &rule) +{ m_rules.append(rule); } + +void Context::addRule(const QSharedPointer<Rule> &rule, int index) +{ m_rules.insert(index, rule); } + +const QList<QSharedPointer<Rule> > & Context::rules() const +{ return m_rules; } + +void Context::addIncludeRulesInstruction(const IncludeRulesInstruction &instruction) +{ m_instructions.append(instruction); } + +const QList<IncludeRulesInstruction> &Context::includeRulesInstructions() const +{ return m_instructions; } + +void Context::clearIncludeRulesInstructions() +{ m_instructions.clear(); } + +void Context::setDefinition(const QSharedPointer<HighlightDefinition> &definition) +{ m_definition = definition; } + +const QSharedPointer<HighlightDefinition> &Context::definition() const +{ return m_definition; } diff --git a/src/plugins/texteditor/generichighlighter/context.h b/src/plugins/texteditor/generichighlighter/context.h new file mode 100644 index 0000000000..1d800ca2e5 --- /dev/null +++ b/src/plugins/texteditor/generichighlighter/context.h @@ -0,0 +1,110 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef CONTEXT_H +#define CONTEXT_H + +#include "includerulesinstruction.h" + +#include <QtCore/QString> +#include <QtCore/QList> +#include <QtCore/QSharedPointer> + +namespace TextEditor { +namespace Internal { + +class Rule; +class HighlightDefinition; + +class Context +{ +public: + Context(); + Context(const Context &context); + const Context &operator=(Context copy); + ~Context(); + + void configureId(const int unique); + const QString &id() const; + + void setName(const QString &name); + const QString &name() const; + + void setLineBeginContext(const QString &context); + const QString &lineBeginContext() const; + + void setLineEndContext(const QString &context); + const QString &lineEndContext() const; + + void setFallthroughContext(const QString &context); + const QString &fallthroughContext() const; + + void setItemData(const QString &itemData); + const QString &itemData() const; + + void setFallthrough(const QString &fallthrough); + bool isFallthrough() const; + + void setDynamic(const QString &dynamic); + bool isDynamic() const; + void updateDynamicRules(const QStringList &captures) const; + + void addRule(const QSharedPointer<Rule> &rule); + void addRule(const QSharedPointer<Rule> &rule, int index); + const QList<QSharedPointer<Rule> > &rules() const; + + void addIncludeRulesInstruction(const IncludeRulesInstruction &instruction); + const QList<IncludeRulesInstruction> &includeRulesInstructions() const; + void clearIncludeRulesInstructions(); + + void setDefinition(const QSharedPointer<HighlightDefinition> &definition); + const QSharedPointer<HighlightDefinition> &definition() const; + + void swap(Context &context); + +private: + QString m_id; + QString m_name; + QString m_lineBeginContext; + QString m_lineEndContext; + QString m_fallthroughContext; + QString m_itemData; + bool m_fallthrough; + bool m_dynamic; + + QList<QSharedPointer<Rule> > m_rules; + QList<IncludeRulesInstruction> m_instructions; + + QSharedPointer<HighlightDefinition> m_definition; +}; + +} // namespace Internal +} // namespace TextEditor + +#endif // CONTEXT_H diff --git a/src/plugins/texteditor/generichighlighter/dynamicrule.cpp b/src/plugins/texteditor/generichighlighter/dynamicrule.cpp new file mode 100644 index 0000000000..24d7414dd7 --- /dev/null +++ b/src/plugins/texteditor/generichighlighter/dynamicrule.cpp @@ -0,0 +1,67 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "dynamicrule.h" +#include "reuse.h" + +using namespace TextEditor; +using namespace Internal; + +DynamicRule::DynamicRule() : m_active(false) +{} + +DynamicRule::~DynamicRule() +{} + +void DynamicRule::setActive(const QString &active) +{ m_active = toBool(active); } + +bool DynamicRule::isActive() const +{ return m_active; } + +void DynamicRule::replaceExpressions(const QStringList &captures) +{ + doReplaceExpressions(captures); + updateDynamicRules(childs(), captures); +} + +namespace TextEditor { +namespace Internal { + +void updateDynamicRules(const QList<QSharedPointer<Rule> > &rules, const QStringList &captures) +{ + foreach (QSharedPointer<Rule> rule, rules) { + DynamicRule *dynamicRule = dynamic_cast<DynamicRule *>(rule.data()); + if (dynamicRule && dynamicRule->isActive()) + dynamicRule->replaceExpressions(captures); + } +} + +} // namespace Internal +} // namespace TextEditor diff --git a/src/plugins/texteditor/generichighlighter/dynamicrule.h b/src/plugins/texteditor/generichighlighter/dynamicrule.h new file mode 100644 index 0000000000..1e116d31bd --- /dev/null +++ b/src/plugins/texteditor/generichighlighter/dynamicrule.h @@ -0,0 +1,64 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef DYNAMICRULE_H +#define DYNAMICRULE_H + +#include "rule.h" + +QT_BEGIN_NAMESPACE +class QStringList; +QT_END_NAMESPACE + +namespace TextEditor { +namespace Internal { + +class DynamicRule : public Rule +{ +public: + DynamicRule(); + virtual ~DynamicRule(); + + void setActive(const QString &active); + bool isActive() const; + + virtual void replaceExpressions(const QStringList &captures); + +private: + virtual void doReplaceExpressions(const QStringList &captures) = 0; + + bool m_active; +}; + +void updateDynamicRules(const QList<QSharedPointer<Rule> > &rules, const QStringList &captures); + +} // namespace Internal +} // namespace TextEditor + +#endif // DYNAMICRULE_H diff --git a/src/plugins/texteditor/generichighlighter/highlightdefinition.cpp b/src/plugins/texteditor/generichighlighter/highlightdefinition.cpp new file mode 100644 index 0000000000..7c723229d9 --- /dev/null +++ b/src/plugins/texteditor/generichighlighter/highlightdefinition.cpp @@ -0,0 +1,167 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "highlightdefinition.h" +#include "highlighterexception.h" +#include "context.h" +#include "keywordlist.h" +#include "itemdata.h" +#include "reuse.h" + +#include <QLatin1String> + +using namespace TextEditor; +using namespace Internal; + +HighlightDefinition::HighlightDefinition() : + m_delimiters(QLatin1String(".():!+,-<=>%&/;?[]^{|}~\\*, \t")), + m_singleLineCommentAfterWhiteSpaces(false), + m_keywordCaseSensitivity(Qt::CaseSensitive) +{} + +HighlightDefinition::~HighlightDefinition() +{} + +template <class Element, class Container> +const QSharedPointer<Element> &HighlightDefinition:: +GenericHelper::create(const QString &name, Container &container) +{ + if (name.isEmpty()) + throw HighlighterException(); + + if (container.contains(name)) + throw HighlighterException(); + + container.insert(name, QSharedPointer<Element>(new Element)); + return *container.constFind(name); +} + +template <class Element, class Container> +const QSharedPointer<Element> &HighlightDefinition:: +GenericHelper::find(const QString &name, const Container &container) const +{ + typename Container::const_iterator it = container.find(name); + if (it == container.end()) + throw HighlighterException(); + + return it.value(); +} + +const QSharedPointer<KeywordList> &HighlightDefinition::createKeywordList(const QString &list) +{ return m_helper.create<KeywordList>(list, m_lists); } + +const QSharedPointer<KeywordList> &HighlightDefinition::keywordList(const QString &list) +{ return m_helper.find<KeywordList>(list, m_lists); } + +const QSharedPointer<Context> &HighlightDefinition::createContext(const QString &context, + bool initial) +{ + if (initial) + m_initialContext = context; + + return m_helper.create<Context>(context, m_contexts); +} + +const QSharedPointer<Context> &HighlightDefinition::initialContext() const +{ return m_helper.find<Context>(m_initialContext, m_contexts); } + +const QSharedPointer<Context> &HighlightDefinition::context(const QString &context) const +{ return m_helper.find<Context>(context, m_contexts); } + +const QHash<QString, QSharedPointer<Context> > &HighlightDefinition::contexts() const +{ return m_contexts; } + +const QSharedPointer<ItemData> &HighlightDefinition::createItemData(const QString &itemData) +{ return m_helper.create<ItemData>(itemData, m_itemsData); } + +const QSharedPointer<ItemData> &HighlightDefinition::itemData(const QString &itemData) const +{ return m_helper.find<ItemData>(itemData, m_itemsData); } + +void HighlightDefinition::setSingleLineComment(const QString &start) +{ m_singleLineComment = start; } + +const QString &HighlightDefinition::singleLineComment() const +{ return m_singleLineComment; } + +void HighlightDefinition::setCommentAfterWhitespaces(const QString &after) +{ + if (after == QLatin1String("afterwhitespace")) + m_singleLineCommentAfterWhiteSpaces = true; +} + +bool HighlightDefinition::isCommentAfterWhiteSpaces() const +{ return m_singleLineCommentAfterWhiteSpaces; } + +void HighlightDefinition::setMultiLineCommentStart(const QString &start) +{ m_multiLineCommentStart = start; } + +const QString &HighlightDefinition::multiLineCommentStart() const +{ return m_multiLineCommentStart; } + +void HighlightDefinition::setMultiLineCommentEnd(const QString &end) +{ m_multiLineCommentEnd = end; } + +const QString &HighlightDefinition::multiLineCommentEnd() const +{ return m_multiLineCommentEnd; } + +void HighlightDefinition::setMultiLineCommentRegion(const QString ®ion) +{ m_multiLineCommentRegion = region; } + +const QString &HighlightDefinition::multiLineCommentRegion() const +{ return m_multiLineCommentRegion; } + +void HighlightDefinition::removeDelimiters(const QString &characters) +{ + for (int i = 0; i < characters.length(); ++i) + m_delimiters.remove(characters.at(i)); +} + +void HighlightDefinition::addDelimiters(const QString &characters) +{ + for (int i = 0; i < characters.length(); ++i) { + if (!m_delimiters.contains(characters.at(i))) + m_delimiters.append(characters.at(i)); + } +} + +bool HighlightDefinition::isDelimiter(const QChar &character) const +{ + if (m_delimiters.contains(character)) + return true; + return false; +} + +void HighlightDefinition::setKeywordsSensitive(const QString &sensitivity) +{ + if (!sensitivity.isEmpty()) + m_keywordCaseSensitivity = toCaseSensitivity(toBool(sensitivity)); +} + +Qt::CaseSensitivity HighlightDefinition::keywordsSensitive() const +{ return m_keywordCaseSensitivity; } diff --git a/src/plugins/texteditor/generichighlighter/highlightdefinition.h b/src/plugins/texteditor/generichighlighter/highlightdefinition.h new file mode 100644 index 0000000000..2f48bc24c8 --- /dev/null +++ b/src/plugins/texteditor/generichighlighter/highlightdefinition.h @@ -0,0 +1,117 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef HIGHLIGHTDEFINITION_H +#define HIGHLIGHTDEFINITION_H + +#include <QtCore/QString> +#include <QtCore/QHash> +#include <QtCore/QSharedPointer> + +namespace TextEditor { +namespace Internal { + +class KeywordList; +class Context; +class ItemData; + +class HighlightDefinition +{ +public: + HighlightDefinition(); + ~HighlightDefinition(); + + const QSharedPointer<KeywordList> &createKeywordList(const QString &list); + const QSharedPointer<KeywordList> &keywordList(const QString &list); + + const QSharedPointer<Context> &createContext(const QString &context, bool initial); + const QSharedPointer<Context> &initialContext() const; + const QSharedPointer<Context> &context(const QString &context) const; + const QHash<QString, QSharedPointer<Context> > &contexts() const; + + const QSharedPointer<ItemData> &createItemData(const QString &itemData); + const QSharedPointer<ItemData> &itemData(const QString &itemData) const; + + void setKeywordsSensitive(const QString &sensitivity); + Qt::CaseSensitivity keywordsSensitive() const; + + void addDelimiters(const QString &characters); + void removeDelimiters(const QString &characters); + bool isDelimiter(const QChar &character) const; + + void setSingleLineComment(const QString &start); + const QString &singleLineComment() const; + + void setCommentAfterWhitespaces(const QString &after); + bool isCommentAfterWhiteSpaces() const; + + void setMultiLineCommentStart(const QString &start); + const QString &multiLineCommentStart() const; + + void setMultiLineCommentEnd(const QString &end); + const QString &multiLineCommentEnd() const; + + void setMultiLineCommentRegion(const QString ®ion); + const QString &multiLineCommentRegion() const; + +private: + Q_DISABLE_COPY(HighlightDefinition) + + struct GenericHelper + { + template <class Element, class Container> + const QSharedPointer<Element> &create(const QString &name, Container &container); + + template <class Element, class Container> + const QSharedPointer<Element> &find(const QString &name, const Container &container) const; + }; + GenericHelper m_helper; + + QHash<QString, QSharedPointer<KeywordList> > m_lists; + QHash<QString, QSharedPointer<Context> > m_contexts; + QHash<QString, QSharedPointer<ItemData> > m_itemsData; + + QString m_initialContext; + + QString m_delimiters; + + QString m_singleLineComment; + bool m_singleLineCommentAfterWhiteSpaces; + + QString m_multiLineCommentStart; + QString m_multiLineCommentEnd; + QString m_multiLineCommentRegion; + + Qt::CaseSensitivity m_keywordCaseSensitivity; +}; + +} // namespace Internal +} // namespace TextEditor + +#endif // HIGHLIGHTDEFINITION_H diff --git a/src/plugins/texteditor/generichighlighter/highlightdefinitionhandler.cpp b/src/plugins/texteditor/generichighlighter/highlightdefinitionhandler.cpp new file mode 100644 index 0000000000..f18d3de8f2 --- /dev/null +++ b/src/plugins/texteditor/generichighlighter/highlightdefinitionhandler.cpp @@ -0,0 +1,461 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "highlightdefinitionhandler.h" +#include "highlightdefinition.h" +#include "specificrules.h" +#include "itemdata.h" +#include "keywordlist.h" +#include "context.h" +#include "reuse.h" +#include "manager.h" +#include "highlighterexception.h" + +#include <QLatin1String> + +using namespace TextEditor; +using namespace Internal; + +namespace { + static const QLatin1String kName("name"); + static const QLatin1String kList("list"); + static const QLatin1String kItem("item"); + static const QLatin1String kContext("context"); + static const QLatin1String kAttribute("attribute"); + static const QLatin1String kDynamic("dynamic"); + static const QLatin1String kFallthrough("fallthrough"); + static const QLatin1String kLineEndContext("lineEndContext"); + static const QLatin1String kLineBeginContext("lineBeginContext"); + static const QLatin1String kFallthroughContext("fallthroughContext"); + static const QLatin1String kBeginRegion("beginRegion"); + static const QLatin1String kEndRegion("endRegion"); + static const QLatin1String kLookAhead("lookAhead"); + static const QLatin1String kFirstNonSpace("firstNonSpace"); + static const QLatin1String kColumn("column"); + static const QLatin1String kItemData("itemData"); + static const QLatin1String kDefStyleNum("defStyleNum"); + static const QLatin1String kColor("color"); + static const QLatin1String kSelColor("selColor"); + static const QLatin1String kItalic("italic"); + static const QLatin1String kBold("bold"); + static const QLatin1String kUnderline("underline"); + static const QLatin1String kStrikeout("strikeout"); + static const QLatin1String kChar("char"); + static const QLatin1String kChar1("char1"); + static const QLatin1String kString("String"); + static const QLatin1String kInsensitive("insensitive"); + static const QLatin1String kMinimal("minimal"); + static const QLatin1String kKeywords("keywords"); + static const QLatin1String kCaseSensitive("casesensitive"); + static const QLatin1String kWeakDeliminator("weakDeliminator"); + static const QLatin1String kAdditionalDeliminator("additionalDeliminator"); + static const QLatin1String kWordWrapDeliminator("wordWrapDeliminator"); + static const QLatin1String kComment("comment"); + static const QLatin1String kPosition("position"); + static const QLatin1String kSingleLine("singleline"); + static const QLatin1String kMultiLine("multiline"); + static const QLatin1String kStart("start"); + static const QLatin1String kEnd("end"); + static const QLatin1String kRegion("region"); + static const QLatin1String kDetectChar("DetectChar"); + static const QLatin1String kDetect2Chars("Detect2Chars"); + static const QLatin1String kAnyChar("AnyChar"); + static const QLatin1String kStringDetect("StringDetect"); + static const QLatin1String kRegExpr("RegExpr"); + static const QLatin1String kKeyword("keyword"); + static const QLatin1String kInt("Int"); + static const QLatin1String kFloat("Float"); + static const QLatin1String kHlCOct("HlCOct"); + static const QLatin1String kHlCHex("HlCHex"); + static const QLatin1String kHlCStringChar("HlCStringChar"); + static const QLatin1String kHlCChar("HlCChar"); + static const QLatin1String kRangeDetect("RangeDetect"); + static const QLatin1String kLineContinue("LineContinue"); + static const QLatin1String kIncludeRules("IncludeRules"); + static const QLatin1String kDetectSpaces("DetectSpaces"); + static const QLatin1String kDetectIdentifier("DetectIdentifier"); + static const QLatin1String kLanguage("language"); + static const QLatin1String kExtensions("extensions"); + static const QLatin1String kIncludeAttrib("includeAttrib"); + static const QLatin1String kHash("#"); + static const QLatin1String kDoubleHash("##"); +} + +HighlightDefinitionHandler:: +HighlightDefinitionHandler(const QSharedPointer<HighlightDefinition> &definition) : + m_definition(definition), + m_processingKeyword(false), + m_initialContext(true) +{} + +HighlightDefinitionHandler::~HighlightDefinitionHandler() +{} + +bool HighlightDefinitionHandler::startDocument() +{ + return true; +} + +bool HighlightDefinitionHandler::endDocument() +{ + processIncludeRules(); + return true; +} + +bool HighlightDefinitionHandler::startElement(const QString &, + const QString &, + const QString &qName, + const QXmlAttributes &atts) +{ + if (qName == kList) { + listElementStarted(atts); + } else if (qName == kItem) { + itemElementStarted(); + } else if (qName == kContext) { + contextElementStarted(atts); + } else if (qName == kItemData) { + itemDataElementStarted(atts); + } else if (qName == kComment) { + commentElementStarted(atts); + } else if (qName == kKeywords) { + keywordsElementStarted(atts); + } else if (qName == kDetectChar) { + detectCharStarted(atts); + } else if (qName == kDetect2Chars) { + detect2CharsStarted(atts); + } else if (qName == kAnyChar) { + anyCharStarted(atts); + } else if (qName == kStringDetect) { + stringDetectedStarted(atts); + } else if (qName == kRegExpr) { + regExprStarted(atts); + } else if (qName == kKeyword) { + keywordStarted(atts); + } else if (qName == kInt) { + intStarted(atts); + } else if (qName == kFloat) { + floatStarted(atts); + } else if (qName == kHlCOct) { + hlCOctStarted(atts); + } else if (qName == kHlCHex) { + hlCHexStarted(atts); + } else if (qName == kHlCStringChar) { + hlCStringCharStarted(atts); + } else if (qName == kHlCChar) { + hlCCharStarted(atts); + } else if (qName == kRangeDetect) { + rangeDetectStarted(atts); + } else if (qName == kLineContinue) { + lineContinue(atts); + } else if (qName == kIncludeRules) { + includeRulesStarted(atts); + } else if (qName == kDetectSpaces) { + detectSpacesStarted(atts); + } else if (qName == kDetectIdentifier) { + detectIdentifier(atts); + } + + return true; +} + +bool HighlightDefinitionHandler::endElement(const QString &, const QString &, const QString &qName) +{ + if (qName == kItem) { + m_currentList->addKeyword(m_currentKeyword.trimmed()); + m_processingKeyword = false; + } else if (qName == kDetectChar || qName == kDetect2Chars || qName == kAnyChar || + qName == kStringDetect || qName == kRegExpr || qName == kKeyword || qName == kInt || + qName == kFloat || qName == kHlCOct || qName == kHlCHex || qName == kHlCStringChar || + qName == kHlCChar || qName == kRangeDetect || qName == kLineContinue || + qName == kDetectSpaces || qName == kDetectIdentifier) { + m_currentRule.pop(); + } + + return true; +} + +bool HighlightDefinitionHandler::characters(const QString& ch) +{ + // Character data of an element may be reported in more than one chunk. + if (m_processingKeyword) + m_currentKeyword.append(ch); + + return true; +} + +void HighlightDefinitionHandler::listElementStarted(const QXmlAttributes &atts) +{ + m_currentList = m_definition->createKeywordList(atts.value(kName)); +} + +void HighlightDefinitionHandler::itemElementStarted() +{ + m_currentKeyword.clear(); + m_processingKeyword = true; +} + +void HighlightDefinitionHandler::contextElementStarted(const QXmlAttributes &atts) +{ + m_currentContext = m_definition->createContext(atts.value(kName), m_initialContext); + m_currentContext->setDefinition(m_definition); + m_currentContext->setName(atts.value(kName)); + m_currentContext->setItemData(atts.value(kAttribute)); + m_currentContext->setDynamic(atts.value(kDynamic)); + m_currentContext->setFallthrough(atts.value(kFallthrough)); + m_currentContext->setFallthroughContext(atts.value(kFallthroughContext)); + m_currentContext->setLineBeginContext(atts.value(kLineBeginContext)); + m_currentContext->setLineEndContext(atts.value(kLineEndContext)); + + m_initialContext = false; +} + +void HighlightDefinitionHandler::ruleElementStarted(const QXmlAttributes &atts, + const QSharedPointer<Rule> &rule) +{ + // The definition of a rule is not necessarily the same of its enclosing context because of + // externally included rules. + rule->setDefinition(m_definition); + rule->setItemData(atts.value(kAttribute)); + rule->setContext(atts.value(kContext)); + rule->setBeginRegion(atts.value(kBeginRegion)); + rule->setEndRegion(atts.value(kEndRegion)); + rule->setLookAhead(atts.value(kLookAhead)); + rule->setFirstNonSpace(atts.value(kFirstNonSpace)); + rule->setColumn(atts.value(kColumn)); + + if (m_currentRule.isEmpty()) + m_currentContext->addRule(rule); + else + m_currentRule.top()->addChild(rule); + + m_currentRule.push(rule); +} + +void HighlightDefinitionHandler::itemDataElementStarted(const QXmlAttributes &atts) const +{ + QSharedPointer<ItemData> itemData = m_definition->createItemData(atts.value(kName)); + itemData->setStyle(atts.value(kDefStyleNum)); + itemData->setColor(atts.value(kColor)); + itemData->setSelectionColor(atts.value(kSelColor)); + itemData->setItalic(atts.value(kItalic)); + itemData->setBold(atts.value(kBold)); + itemData->setUnderlined(atts.value(kUnderline)); + itemData->setStrikedOut(atts.value(kStrikeout)); +} + +void HighlightDefinitionHandler::commentElementStarted(const QXmlAttributes &atts) const +{ + const QString &commentType = atts.value(kName); + if (commentType.compare(kSingleLine, Qt::CaseInsensitive) == 0) { + m_definition->setSingleLineComment(atts.value(kStart)); + m_definition->setCommentAfterWhitespaces(atts.value(kPosition)); + } else if (commentType.compare(kMultiLine, Qt::CaseInsensitive) == 0) { + m_definition->setMultiLineCommentStart(atts.value(kStart)); + m_definition->setMultiLineCommentEnd(atts.value(kEnd)); + m_definition->setMultiLineCommentRegion(atts.value(kRegion)); + } +} + +void HighlightDefinitionHandler::keywordsElementStarted(const QXmlAttributes &atts) const +{ + // Global case sensitivity appears last in the document (required by dtd) and is set here. + m_definition->setKeywordsSensitive(atts.value(kCaseSensitive)); + m_definition->removeDelimiters(atts.value(kWeakDeliminator)); + m_definition->addDelimiters(atts.value(kAdditionalDeliminator)); + //@todo: wordWrapDelimiters? +} + +void HighlightDefinitionHandler::detectCharStarted(const QXmlAttributes &atts) +{ + DetectCharRule *rule = new DetectCharRule; + rule->setChar(atts.value(kChar)); + rule->setActive(atts.value(kDynamic)); + ruleElementStarted(atts, QSharedPointer<Rule>(rule)); +} + +void HighlightDefinitionHandler::detect2CharsStarted(const QXmlAttributes &atts) +{ + Detect2CharsRule *rule = new Detect2CharsRule; + rule->setChar(atts.value(kChar)); + rule->setChar1(atts.value(kChar1)); + rule->setActive(atts.value(kDynamic)); + ruleElementStarted(atts, QSharedPointer<Rule>(rule)); +} + +void HighlightDefinitionHandler::anyCharStarted(const QXmlAttributes &atts) +{ + AnyCharRule *rule = new AnyCharRule; + rule->setCharacterSet(atts.value(kString)); + ruleElementStarted(atts, QSharedPointer<Rule>(rule)); +} + +void HighlightDefinitionHandler::stringDetectedStarted(const QXmlAttributes &atts) +{ + StringDetectRule *rule = new StringDetectRule; + rule->setString(atts.value(kString)); + rule->setInsensitive(atts.value(kInsensitive)); + rule->setActive(atts.value(kDynamic)); + ruleElementStarted(atts, QSharedPointer<Rule>(rule)); +} + +void HighlightDefinitionHandler::regExprStarted(const QXmlAttributes &atts) +{ + RegExprRule *rule = new RegExprRule; + rule->setPattern(atts.value(kString)); + rule->setMinimal(atts.value(kMinimal)); + rule->setInsensitive(atts.value(kInsensitive)); + rule->setActive(atts.value(kDynamic)); + ruleElementStarted(atts, QSharedPointer<Rule>(rule)); +} + +void HighlightDefinitionHandler::keywordStarted(const QXmlAttributes &atts) +{ + KeywordRule *rule = new KeywordRule(m_definition); + rule->setList(atts.value(kString)); + rule->setInsensitive(atts.value(kInsensitive)); + ruleElementStarted(atts, QSharedPointer<Rule>(rule)); +} + +void HighlightDefinitionHandler::intStarted(const QXmlAttributes &atts) +{ + ruleElementStarted(atts, QSharedPointer<Rule>(new IntRule)); +} + +void HighlightDefinitionHandler::floatStarted(const QXmlAttributes &atts) +{ + ruleElementStarted(atts, QSharedPointer<Rule>(new FloatRule)); +} + +void HighlightDefinitionHandler::hlCOctStarted(const QXmlAttributes &atts) +{ + ruleElementStarted(atts, QSharedPointer<Rule>(new HlCOctRule)); +} + +void HighlightDefinitionHandler::hlCHexStarted(const QXmlAttributes &atts) +{ + ruleElementStarted(atts, QSharedPointer<Rule>(new HlCHexRule)); +} + +void HighlightDefinitionHandler::hlCStringCharStarted(const QXmlAttributes &atts) +{ + ruleElementStarted(atts, QSharedPointer<Rule>(new HlCStringCharRule)); +} + +void HighlightDefinitionHandler::hlCCharStarted(const QXmlAttributes &atts) +{ + ruleElementStarted(atts, QSharedPointer<Rule>(new HlCCharRule)); +} + +void HighlightDefinitionHandler::rangeDetectStarted(const QXmlAttributes &atts) +{ + RangeDetectRule *rule = new RangeDetectRule; + rule->setChar(atts.value(kChar)); + rule->setChar1(atts.value(kChar1)); + ruleElementStarted(atts, QSharedPointer<Rule>(rule)); +} + +void HighlightDefinitionHandler::lineContinue(const QXmlAttributes &atts) +{ + ruleElementStarted(atts, QSharedPointer<Rule>(new LineContinueRule)); +} + +void HighlightDefinitionHandler::includeRulesStarted(const QXmlAttributes &atts) +{ + // Include rules are treated as instructions for latter processing. + IncludeRulesInstruction instruction(atts.value(kContext), m_currentContext->rules().size(), + atts.value(kIncludeAttrib)); + + // Include rules (as many others) are not allowed as a child. + m_currentContext->addIncludeRulesInstruction(instruction); +} + +void HighlightDefinitionHandler::detectSpacesStarted(const QXmlAttributes &atts) +{ + ruleElementStarted(atts, QSharedPointer<Rule>(new DetectSpacesRule)); +} + +void HighlightDefinitionHandler::detectIdentifier(const QXmlAttributes &atts) +{ + ruleElementStarted(atts, QSharedPointer<Rule>(new DetectIdentifierRule)); +} + +void HighlightDefinitionHandler::processIncludeRules() const +{ + const QHash<QString, QSharedPointer<Context> > &allContexts = m_definition->contexts(); + foreach (const QSharedPointer<Context> &context, allContexts) + processIncludeRules(context); +} + +void HighlightDefinitionHandler::processIncludeRules(const QSharedPointer<Context> &context) const +{ + if (context->includeRulesInstructions().isEmpty()) + return; + + int rulesIncluded = 0; + const QList<IncludeRulesInstruction> &instructions = context->includeRulesInstructions(); + foreach (const IncludeRulesInstruction &instruction, instructions) { + + QSharedPointer<Context> sourceContext; + const QString &sourceName = instruction.sourceContext(); + if (sourceName.startsWith(kDoubleHash)) { + // This refers to an external definition. The rules included are the ones from its + // initial context. Others contexts and rules from the external definition will work + // transparently to the highlighter. This is because contexts and rules know the + // definition they are from. + QString externalName = QString::fromRawData(sourceName.unicode() + 2, + sourceName.length() - 2); + const QString &id = Manager::instance()->definitionIdByName(externalName); + + // If there is an incorrect circular dependency among definitions this is skipped. + if (Manager::instance()->isBuildingDefinition(id)) + continue; + + const QSharedPointer<HighlightDefinition> &externalDefinition = + Manager::instance()->definition(id); + sourceContext = externalDefinition->initialContext(); + } else if (!sourceName.startsWith(kHash)) { + sourceContext = m_definition->context(sourceName); + + // Recursion is done only for context direct rules. Child rules are not processed + // because they cannot be include rules. + processIncludeRules(sourceContext); + } + + if (instruction.replaceItemData()) { + context->setItemData(sourceContext->itemData()); + context->setDefinition(sourceContext->definition()); + } + + foreach (QSharedPointer<Rule> rule, sourceContext->rules()) { + context->addRule(rule, instruction.indexHint() + rulesIncluded); + ++rulesIncluded; + } + } + context->clearIncludeRulesInstructions(); +} diff --git a/src/plugins/texteditor/generichighlighter/highlightdefinitionhandler.h b/src/plugins/texteditor/generichighlighter/highlightdefinitionhandler.h new file mode 100644 index 0000000000..e08be97649 --- /dev/null +++ b/src/plugins/texteditor/generichighlighter/highlightdefinitionhandler.h @@ -0,0 +1,106 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef HIGHLIGHTDEFINITIONHANDLER_H +#define HIGHLIGHTDEFINITIONHANDLER_H + +#include <QtCore/QString> +#include <QtCore/QList> +#include <QtCore/QSharedPointer> +#include <QtCore/QStack> + +#include <QtXml/QXmlDefaultHandler> + +namespace TextEditor { +namespace Internal { + +class KeywordList; +class Context; +class Rule; +class HighlightDefinition; + +class HighlightDefinitionHandler : public QXmlDefaultHandler +{ +public: + HighlightDefinitionHandler(const QSharedPointer<HighlightDefinition> &definition); + ~HighlightDefinitionHandler(); + + bool startDocument(); + bool endDocument(); + bool startElement(const QString &namespaceURI, const QString &localName, + const QString &qName, const QXmlAttributes &atts); + bool endElement(const QString &namespaceURI, const QString &localName, const QString &qName); + bool characters(const QString &ch); + +private: + void listElementStarted(const QXmlAttributes &atts); + void itemElementStarted(); + void contextElementStarted(const QXmlAttributes &atts); + void itemDataElementStarted(const QXmlAttributes &atts) const; + void commentElementStarted(const QXmlAttributes &atts) const; + void keywordsElementStarted(const QXmlAttributes &atts) const; + void ruleElementStarted(const QXmlAttributes &atts, const QSharedPointer<Rule> &rule); + + // Specific rules. + void detectCharStarted(const QXmlAttributes &atts); + void detect2CharsStarted(const QXmlAttributes &atts); + void anyCharStarted(const QXmlAttributes &atts); + void stringDetectedStarted(const QXmlAttributes &atts); + void regExprStarted(const QXmlAttributes &atts); + void keywordStarted(const QXmlAttributes &atts); + void intStarted(const QXmlAttributes &atts); + void floatStarted(const QXmlAttributes &atts); + void hlCOctStarted(const QXmlAttributes &atts); + void hlCHexStarted(const QXmlAttributes &atts); + void hlCStringCharStarted(const QXmlAttributes &atts); + void hlCCharStarted(const QXmlAttributes &atts); + void rangeDetectStarted(const QXmlAttributes &atts); + void lineContinue(const QXmlAttributes &atts); + void includeRulesStarted(const QXmlAttributes &atts); + void detectSpacesStarted(const QXmlAttributes &atts); + void detectIdentifier(const QXmlAttributes &atts); + + void processIncludeRules() const; + void processIncludeRules(const QSharedPointer<Context> &context) const; + + QSharedPointer<HighlightDefinition> m_definition; + + bool m_processingKeyword; + QString m_currentKeyword; + QSharedPointer<KeywordList> m_currentList; + QSharedPointer<Context> m_currentContext; + QStack<QSharedPointer<Rule> > m_currentRule; + + bool m_initialContext; +}; + +} // namespace Internal +} // namespace TextEditor + +#endif // HIGHLIGHTDEFINITIONHANDLER_H diff --git a/src/plugins/texteditor/generichighlighter/highlighter.cpp b/src/plugins/texteditor/generichighlighter/highlighter.cpp new file mode 100644 index 0000000000..27b0309795 --- /dev/null +++ b/src/plugins/texteditor/generichighlighter/highlighter.cpp @@ -0,0 +1,478 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "highlighter.h" +#include "highlightdefinition.h" +#include "context.h" +#include "rule.h" +#include "itemdata.h" +#include "highlighterexception.h" +#include "progressdata.h" +#include "reuse.h" +#include "texteditorconstants.h" +#include "fontsettings.h" + +#include <QtCore/QLatin1String> +#include <QtCore/QLatin1Char> + +using namespace TextEditor; +using namespace Internal; + +namespace { + static const QLatin1String kStay("#stay"); + static const QLatin1String kPop("#pop"); + static const QLatin1Char kBackSlash('\\'); + static const QLatin1Char kHash('#'); +} + +Highlighter::Highlighter(const QSharedPointer<Context> &defaultContext,QTextDocument *parent) : + QSyntaxHighlighter(parent), + m_persistentStatesCounter(PersistentsStart), + m_dynamicContextsCounter(0), + m_isBroken(false), + m_defaultContext(defaultContext) +{ + m_persistentStates.insert(m_defaultContext->name(), Default); +} + +Highlighter::~Highlighter() +{} + +Highlighter::BlockData::BlockData() +{} + +Highlighter::BlockData::~BlockData() +{} + +void Highlighter::highlightBlock(const QString &text) +{ + if (m_isBroken) + return; + + try { + setupDataForBlock(text); + + handleContextChange(m_currentContext->lineBeginContext(), m_currentContext->definition()); + + ProgressData progress; + const int length = text.length(); + while (progress.offset() < length) { + + if (progress.offset() > 0 && + progress.onlySpacesSoFar() && + !text.at(progress.offset()).isSpace()) { + progress.setOnlySpacesSoFar(false); + } + + iterateThroughRules(text, length, &progress, false, m_currentContext->rules()); + } + + handleContextChange(m_currentContext->lineEndContext(), m_currentContext->definition(), + false); + } catch (const HighlighterException &) { + m_isBroken = true; + return; + } + + m_contexts.clear(); + applyVisualWhitespaceFormat(text); +} + +void Highlighter::setupDataForBlock(const QString &text) +{ + if (currentBlockState() == WillContinue) + analyseConsistencyOfWillContinueBlock(text); + + if (previousBlockState() == Default || previousBlockState() == -1) + setupDefault(); + else if (previousBlockState() == WillContinue) + setupFromWillContinue(); + else if (previousBlockState() == Continued) + setupFromContinued(); + else + setupFromPersistent(); + + setCurrentContext(); +} + +void Highlighter::setupDefault() +{ + m_contexts.push_back(m_defaultContext); + + setCurrentBlockState(Default); +} + +void Highlighter::setupFromWillContinue() +{ + BlockData *previousData = static_cast<BlockData *>(currentBlock().previous().userData()); + m_contexts.push_back(previousData->m_contextToContinue); + + if (!currentBlockUserData()) { + BlockData *data = initializeBlockData(); + data->m_originalState = previousData->m_originalState; + } + + if (currentBlockState() == Default || currentBlockState() == -1) + setCurrentBlockState(Continued); +} + +void Highlighter::setupFromContinued() +{ + BlockData *previousData = static_cast<BlockData *>(currentBlock().previous().userData()); + + Q_ASSERT(previousData->m_originalState != WillContinue && + previousData->m_originalState != Continued); + + if (previousData->m_originalState == Default || previousData->m_originalState == -1) + m_contexts.push_back(m_defaultContext); + else + pushContextSequence(previousData->m_originalState); + + setCurrentBlockState(previousData->m_originalState); +} + +void Highlighter::setupFromPersistent() +{ + pushContextSequence(previousBlockState()); + + setCurrentBlockState(previousBlockState()); +} + +void Highlighter::iterateThroughRules(const QString &text, + const int length, + ProgressData *progress, + const bool childRule, + const QList<QSharedPointer<Rule> > &rules) +{ + typedef QList<QSharedPointer<Rule> >::const_iterator RuleIterator; + + bool contextChanged = false; + bool atLeastOneMatch = false; + + RuleIterator it = rules.begin(); + RuleIterator endIt = rules.end(); + while (it != endIt && progress->offset() < length) { + int startOffset = progress->offset(); + + const QSharedPointer<Rule> &rule = *it; + if (rule->matchSucceed(text, length, progress)) { + atLeastOneMatch = true; + + if (progress->willContinueLine()) { + createWillContinueBlock(); + progress->setWillContinueLine(false); + progress->setOffset(length); + } else { + if (rule->hasChild()) + iterateThroughRules(text, length, progress, true, rule->childs()); + + if (!rule->context().isEmpty() && contextChangeRequired(rule->context())) { + m_currentCaptures = progress->captures(); + changeContext(rule->context(), rule->definition()); + contextChanged = true; + } + } + + // Format is not applied to child rules directly (but relative to the offset of their + // parent) nor to look ahead rules. + if (!childRule && !rule->isLookAhead()) { + if (rule->itemData().isEmpty()) + applyFormat(startOffset, progress->offset() - startOffset, + m_currentContext->itemData(), m_currentContext->definition()); + else + applyFormat(startOffset, progress->offset() - startOffset, rule->itemData(), + rule->definition()); + } + + // When there is a match of one child rule the others should be skipped. Otherwise + // the highlighting would be incorret in a case like 9ULLLULLLUULLULLUL, for example. + if (contextChanged || childRule) { + break; + } else { + it = rules.begin(); + continue; + } + } + ++it; + } + + if (!childRule && !atLeastOneMatch) { + if (m_currentContext->isFallthrough()) { + handleContextChange(m_currentContext->fallthroughContext(), + m_currentContext->definition()); + iterateThroughRules(text, length, progress, false, m_currentContext->rules()); + } else { + applyFormat(progress->offset(), 1, m_currentContext->itemData(), + m_currentContext->definition()); + progress->incrementOffset(); + } + } +} + +bool Highlighter::contextChangeRequired(const QString &contextName) const +{ + if (contextName == kStay) + return false; + return true; +} + +void Highlighter::changeContext(const QString &contextName, + const QSharedPointer<HighlightDefinition> &definition, + const bool setCurrent) +{ + if (contextName.startsWith(kPop)) { + QStringList list = contextName.split(kHash, QString::SkipEmptyParts); + for (int i = 0; i < list.size(); ++i) + m_contexts.pop_back(); + + if (currentBlockState() >= PersistentsStart) { + // One or more persistent contexts were popped. + const QString ¤tSequence = currentContextSequence(); + if (m_persistentStates.contains(currentSequence)) + setCurrentBlockState(m_persistentStates.value(currentContextSequence())); + else + setCurrentBlockState(m_leadingStates.value(currentContextSequence())); + } + } else { + const QSharedPointer<Context> &context = definition->context(contextName); + + if (context->isDynamic()) + pushDynamicContext(context); + else + m_contexts.push_back(context); + + if (m_contexts.back()->lineEndContext() == kStay) { + // A persistent context was pushed. + const QString ¤tSequence = currentContextSequence(); + mapContextSequence(currentSequence); + setCurrentBlockState(m_persistentStates.value(currentSequence)); + } + } + + if (setCurrent) + setCurrentContext(); +} + +void Highlighter::handleContextChange(const QString &contextName, + const QSharedPointer<HighlightDefinition> &definition, + const bool setCurrent) +{ + if (!contextName.isEmpty() && contextChangeRequired(contextName)) + changeContext(contextName, definition, setCurrent); +} + +void Highlighter::applyFormat(int offset, + int count, + const QString &itemDataName, + const QSharedPointer<HighlightDefinition> &definition) +{ + if (count == 0) + return; + + QSharedPointer<ItemData> itemData; + try { + itemData = definition->itemData(itemDataName); + } catch (const HighlighterException &) { + // There are some broken files. For instance, the Printf context in java.xml points to an + // inexistent Printf item data. These cases are considered to have normal text style. + return; + } + + if (itemData->style() != ItemData::kDsNormal) { + QTextCharFormat format = m_genericFormats.value(itemData->style()); + + if (itemData->isCustomized()) { + // Please notice that the following are applied every time for item datas which have + // customizations. The configureFormats method could be used to provide a "one time" + // configuration, but it would probably require to traverse all item datas from all + // definitions available/loaded (either to set the values or for some "notifying" + // strategy). This is because the highlighter does not really know on which + // definition(s) it is working. Since not many item datas specify customizations I + // think this approach would fit better. If there are other ideas... + if (itemData->color().isValid()) + format.setForeground(itemData->color()); + if (itemData->isItalicSpecified()) + format.setFontItalic(itemData->isItalic()); + if (itemData->isBoldSpecified()) + format.setFontWeight(toFontWeight(itemData->isBold())); + if (itemData->isUnderlinedSpecified()) + format.setFontUnderline(itemData->isUnderlined()); + if (itemData->isStrikedOutSpecified()) + format.setFontStrikeOut(itemData->isStrikedOut()); + } + + setFormat(offset, count, format); + } +} + +void Highlighter::applyVisualWhitespaceFormat(const QString &text) +{ + int offset = 0; + const int length = text.length(); + while (offset < length) { + if (text.at(offset).isSpace()) { + int start = offset++; + while (offset < length && text.at(offset).isSpace()) + ++offset; + setFormat(start, offset - start, m_visualWhitespaceFormat); + } else { + ++offset; + } + } +} + +void Highlighter::createWillContinueBlock() +{ + if (!currentBlockUserData()) + initializeBlockData(); + + BlockData *data = static_cast<BlockData *>(currentBlockUserData()); + if (currentBlockState() == Continued) { + BlockData *previousData = static_cast<BlockData *>(currentBlock().previous().userData()); + data->m_originalState = previousData->m_originalState; + } else if (currentBlockState() != WillContinue) { + data->m_originalState = currentBlockState(); + } + data->m_contextToContinue = m_currentContext; + + setCurrentBlockState(WillContinue); +} + +void Highlighter::analyseConsistencyOfWillContinueBlock(const QString &text) +{ + if (currentBlock().next().isValid() && ( + text.length() == 0 || text.at(text.length() - 1) != kBackSlash) && + currentBlock().next().userState() != Continued) { + currentBlock().next().setUserState(Continued); + } + + if (text.length() == 0 || text.at(text.length() - 1) != kBackSlash) { + BlockData *data = static_cast<BlockData *>(currentBlockUserData()); + data->m_contextToContinue.clear(); + setCurrentBlockState(data->m_originalState); + } +} + +void Highlighter::mapContextSequence(const QString &contextSequence) +{ + if (currentBlockState() < PersistentsStart && !m_leadingStates.contains(contextSequence)) + m_leadingStates.insert(contextSequence, currentBlockState()); + + if (!m_persistentStates.contains(contextSequence)) { + int newState = m_persistentStatesCounter; + m_persistentStates.insert(contextSequence, newState); + m_persistentContexts.insert(newState, m_contexts); + ++m_persistentStatesCounter; + } +} + +void Highlighter::pushContextSequence(int state) +{ + const QVector<QSharedPointer<Context> > &contexts = m_persistentContexts.value(state); + for (int i = 0; i < contexts.size(); ++i) + m_contexts.push_back(contexts.at(i)); +} + +QString Highlighter::currentContextSequence() const +{ + QString sequence; + for (int i = 0; i < m_contexts.size(); ++i) + sequence.append(m_contexts.at(i)->id()); + + return sequence; +} + +Highlighter::BlockData *Highlighter::initializeBlockData() +{ + BlockData *data = new BlockData; + setCurrentBlockUserData(data); + return data; +} + +void Highlighter::pushDynamicContext(const QSharedPointer<Context> &baseContext) +{ + // A dynamic context is created from another context which serves as its basis. Then, + // its rules are updated according to the captures from the calling regular expression which + // triggered the push of the dynamic context. + QSharedPointer<Context> context(new Context(*baseContext)); + context->configureId(m_dynamicContextsCounter); + context->updateDynamicRules(m_currentCaptures); + m_contexts.push_back(context); + ++m_dynamicContextsCounter; +} + +void Highlighter::setCurrentContext() +{ + if (m_contexts.isEmpty()) { + // This is not supposed to happen. However, there are broken files (for example, php.xml) + // which will cause this behaviour. In such cases pushing the default context is enough to + // keep highlighter working. + m_contexts.push_back(m_defaultContext); + } + m_currentContext = m_contexts.back(); +} + +void Highlighter::configureFormats(const FontSettings & fs) +{ + m_visualWhitespaceFormat = fs.toTextCharFormat( + QLatin1String(TextEditor::Constants::C_VISUAL_WHITESPACE)); + + m_genericFormats[ItemData::kDsKeyword] = fs.toTextCharFormat( + QLatin1String(TextEditor::Constants::C_KEYWORD)); + m_genericFormats[ItemData::kDsDataType] = fs.toTextCharFormat( + QLatin1String(TextEditor::Constants::C_TYPE)); + // Currenlty using C_NUMBER for all kinds of numbers. + m_genericFormats[ItemData::kDsDecVal] = fs.toTextCharFormat( + QLatin1String(TextEditor::Constants::C_NUMBER)); + m_genericFormats[ItemData::kDsBaseN] = fs.toTextCharFormat( + QLatin1String(TextEditor::Constants::C_NUMBER)); + m_genericFormats[ItemData::kDsFloat] = fs.toTextCharFormat( + QLatin1String(TextEditor::Constants::C_NUMBER)); + // Currently using C_STRING for strings and chars. + m_genericFormats[ItemData::kDsChar] = fs.toTextCharFormat( + QLatin1String(TextEditor::Constants::C_STRING)); + m_genericFormats[ItemData::kDsString] = fs.toTextCharFormat( + QLatin1String(TextEditor::Constants::C_STRING)); + m_genericFormats[ItemData::kDsComment] = fs.toTextCharFormat( + QLatin1String(TextEditor::Constants::C_COMMENT)); + + // Currently Creator does not have corresponding formats for the following items. We can + // implement them... Just for now I will leave hardcoded colors. + QTextCharFormat format; + format.setForeground(Qt::blue); + m_genericFormats[ItemData::kDsOthers] = format; + format.setForeground(Qt::red); + m_genericFormats[ItemData::kDsAlert] = format; + format.setForeground(Qt::darkBlue); + m_genericFormats[ItemData::kDsFunction] = format; + format.setForeground(Qt::darkGray); + m_genericFormats[ItemData::kDsRegionMarker] = format; + format.setForeground(Qt::darkRed); + m_genericFormats[ItemData::kDsError] = format; +} diff --git a/src/plugins/texteditor/generichighlighter/highlighter.h b/src/plugins/texteditor/generichighlighter/highlighter.h new file mode 100644 index 0000000000..bbf7e76992 --- /dev/null +++ b/src/plugins/texteditor/generichighlighter/highlighter.h @@ -0,0 +1,154 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef HIGHLIGHTER_H +#define HIGHLIGHTER_H + +#include "basetextdocumentlayout.h" + +#include <QtCore/QString> +#include <QtCore/QVector> +#include <QtCore/QSharedPointer> +#include <QtCore/QStringList> + +#include <QtGui/QSyntaxHighlighter> + +namespace TextEditor { +class FontSettings; +} + +namespace TextEditor { +namespace Internal { + +class Rule; +class Context; +class HighlightDefinition; +class ProgressData; + +class Highlighter : public QSyntaxHighlighter +{ +public: + Highlighter(const QSharedPointer<Context> &defaultContext, QTextDocument *parent = 0); + virtual ~Highlighter(); + + void configureFormats(const FontSettings & fs); + +protected: + virtual void highlightBlock(const QString &text); + +private: + + void setupDataForBlock(const QString &text); + void setupDefault(); + void setupFromWillContinue(); + void setupFromContinued(); + void setupFromPersistent(); + + void iterateThroughRules(const QString &text, + const int length, + ProgressData *progress, + const bool childRule, + const QList<QSharedPointer<Rule> > &rules); + + bool contextChangeRequired(const QString &contextName) const; + void handleContextChange(const QString &contextName, + const QSharedPointer<HighlightDefinition> &definition, + const bool setCurrent = true); + void changeContext(const QString &contextName, + const QSharedPointer<HighlightDefinition> &definition, + const bool setCurrent = true); + + void applyFormat(int offset, + int count, + const QString &itemDataName, + const QSharedPointer<HighlightDefinition> &definition); + void applyVisualWhitespaceFormat(const QString &text); + + QString currentContextSequence() const; + void mapContextSequence(const QString &contextSequence); + void pushContextSequence(int state); + void pushDynamicContext(const QSharedPointer<Context> &baseContext); + + void setCurrentContext(); + + void createWillContinueBlock(); + void analyseConsistencyOfWillContinueBlock(const QString &text); + + struct BlockData : TextBlockUserData + { + BlockData(); + virtual ~BlockData(); + + int m_originalState; + QSharedPointer<Context> m_contextToContinue; + }; + BlockData *initializeBlockData(); + + // Block states + // - Default [0]: Nothing special. + // - WillContinue [1]: When there is match of the LineContinue rule (backslash as the last + // character). + // - Continued [2]: Blocks that happen after a WillContinue block and continued from their + // context until the next line end. + // - Persistent(s) [Anything >= 3]: Correspond to persistent contexts which last until a pop + // occurs due to a matching rule. Every sequence of persistent contexts seen so far is + // associated with a number (incremented by a unit each time). + enum BlockState { + Default = 0, + WillContinue, + Continued, + PersistentsStart + }; + int m_persistentStatesCounter; + int m_dynamicContextsCounter; + + bool m_isBroken; + + QSharedPointer<Context> m_defaultContext; + QSharedPointer<Context> m_currentContext; + QVector<QSharedPointer<Context> > m_contexts; + + // Mapping from context sequences to the persistent state they represent. + QHash<QString, int> m_persistentStates; + // Mapping from context sequences to the non-persistent state that led to them. + QHash<QString, int> m_leadingStates; + // Mapping from persistent states to context sequences (the actual "stack"). + QHash<int, QVector<QSharedPointer<Context> > > m_persistentContexts; + + // Captures used in dynamic rules. + QStringList m_currentCaptures; + + QTextCharFormat m_visualWhitespaceFormat; + QHash<QString, QTextCharFormat> m_genericFormats; +}; + +} // namespace Internal +} // namespace TextEditor + +#endif // HIGHLIGHTER_H diff --git a/src/plugins/texteditor/generichighlighter/highlighterexception.h b/src/plugins/texteditor/generichighlighter/highlighterexception.h new file mode 100644 index 0000000000..13310402fa --- /dev/null +++ b/src/plugins/texteditor/generichighlighter/highlighterexception.h @@ -0,0 +1,41 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef HIGHLIGHTEREXCEPTION_H +#define HIGHLIGHTEREXCEPTION_H + +namespace TextEditor { +namespace Internal { + +class HighlighterException {}; + +} // namespace Internal +} // namespace TextEditor + +#endif // HIGHLIGHTEREXCEPTION_H diff --git a/src/plugins/texteditor/generichighlighter/includerulesinstruction.cpp b/src/plugins/texteditor/generichighlighter/includerulesinstruction.cpp new file mode 100644 index 0000000000..8b04826633 --- /dev/null +++ b/src/plugins/texteditor/generichighlighter/includerulesinstruction.cpp @@ -0,0 +1,50 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "includerulesinstruction.h" +#include "reuse.h" + +using namespace TextEditor; +using namespace Internal; + +IncludeRulesInstruction::IncludeRulesInstruction(const QString &context, + int hint, + const QString &replaceItemData) : + m_sourceContext(context), m_indexHint(hint), m_replaceItemData(toBool(replaceItemData)) +{ +} + +const QString &IncludeRulesInstruction::sourceContext() const +{ return m_sourceContext; } + +int IncludeRulesInstruction::indexHint() const +{ return m_indexHint; } + +bool IncludeRulesInstruction::replaceItemData() const +{ return m_replaceItemData; } diff --git a/src/plugins/texteditor/generichighlighter/includerulesinstruction.h b/src/plugins/texteditor/generichighlighter/includerulesinstruction.h new file mode 100644 index 0000000000..c0f5d6e498 --- /dev/null +++ b/src/plugins/texteditor/generichighlighter/includerulesinstruction.h @@ -0,0 +1,56 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef INCLUDERULESINSTRUCTION_H +#define INCLUDERULESINSTRUCTION_H + +#include <QtCore/QString> + +namespace TextEditor { +namespace Internal { + +class IncludeRulesInstruction +{ +public: + IncludeRulesInstruction(const QString &context, int hint, const QString &replaceItemData); + + const QString &sourceContext() const; + int indexHint() const; + bool replaceItemData() const; + +private: + QString m_sourceContext; + int m_indexHint; + bool m_replaceItemData; +}; + +} // namespace Internal +} // namespace TextEditor + +#endif // INCLUDERULESINSTRUCTION_H diff --git a/src/plugins/texteditor/generichighlighter/itemdata.cpp b/src/plugins/texteditor/generichighlighter/itemdata.cpp new file mode 100644 index 0000000000..498d968e32 --- /dev/null +++ b/src/plugins/texteditor/generichighlighter/itemdata.cpp @@ -0,0 +1,148 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "itemdata.h" +#include "reuse.h" + +using namespace TextEditor; +using namespace Internal; + +const QLatin1String ItemData::kDsNormal("dsNormal"); +const QLatin1String ItemData::kDsKeyword("dsKeyword"); +const QLatin1String ItemData::kDsDataType("dsDataType"); +const QLatin1String ItemData::kDsDecVal("dsDecVal"); +const QLatin1String ItemData::kDsBaseN("dsBaseN"); +const QLatin1String ItemData::kDsFloat("dsFloat"); +const QLatin1String ItemData::kDsChar("dsChar"); +const QLatin1String ItemData::kDsString("dsString"); +const QLatin1String ItemData::kDsComment("dsComment"); +const QLatin1String ItemData::kDsOthers("dsOthers"); +const QLatin1String ItemData::kDsAlert("dsAlert"); +const QLatin1String ItemData::kDsFunction("dsFunction"); +const QLatin1String ItemData::kDsRegionMarker("dsRegionMarker"); +const QLatin1String ItemData::kDsError("dsError"); + +ItemData::ItemData() : + m_italicSpecified(false), + m_boldSpecified(false), + m_underlinedSpecified(false), + m_strikedOutSpecified(false), + m_isCustomized(false) +{} + +void ItemData::setStyle(const QString &style) +{ m_style = style; } + +const QString &ItemData::style() const +{ return m_style; } + +void ItemData::setColor(const QString &color) +{ + if (!color.isEmpty()) { + m_color.setNamedColor(color); + m_isCustomized = true; + } +} + +const QColor &ItemData::color() const +{ return m_color; } + +void ItemData::setSelectionColor(const QString &color) +{ + if (!color.isEmpty()) { + m_selectionColor.setNamedColor(color); + m_isCustomized = true; + } +} + +const QColor &ItemData::selectionColor() const +{ return m_selectionColor; } + +void ItemData::setItalic(const QString &italic) +{ + if (!italic.isEmpty()) { + m_italic = toBool(italic); + m_italicSpecified = true; + m_isCustomized = true; + } +} + +bool ItemData::isItalic() const +{ return m_italic; } + +bool ItemData::isItalicSpecified() const +{ return m_italicSpecified; } + +void ItemData::setBold(const QString &bold) +{ + if (!bold.isEmpty()) { + m_bold = toBool(bold); + m_boldSpecified = true; + m_isCustomized = true; + } +} + +bool ItemData::isBold() const +{ return m_bold; } + +bool ItemData::isBoldSpecified() const +{ return m_boldSpecified; } + +void ItemData::setUnderlined(const QString &underlined) +{ + if (!underlined.isEmpty()) { + m_underlined = toBool(underlined); + m_underlinedSpecified = true; + m_isCustomized = true; + } +} + +bool ItemData::isUnderlined() const +{ return m_underlined; } + +bool ItemData::isUnderlinedSpecified() const +{ return m_underlinedSpecified; } + +void ItemData::setStrikedOut(const QString &striked) +{ + if (!striked.isEmpty()) { + m_strikedOut = toBool(striked); + m_strikedOutSpecified = true; + m_isCustomized = true; + } +} + +bool ItemData::isStrikedOut() const +{ return m_strikedOut; } + +bool ItemData::isStrikedOutSpecified() const +{ return m_strikedOutSpecified; } + +bool ItemData::isCustomized() const +{ return m_isCustomized; } diff --git a/src/plugins/texteditor/generichighlighter/itemdata.h b/src/plugins/texteditor/generichighlighter/itemdata.h new file mode 100644 index 0000000000..c130239749 --- /dev/null +++ b/src/plugins/texteditor/generichighlighter/itemdata.h @@ -0,0 +1,104 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef ITEMDATA_H +#define ITEMDATA_H + +#include <QtCore/QString> +#include <QtGui/QColor> + +namespace TextEditor { +namespace Internal { + +class ItemData +{ +public: + ItemData(); + + void setStyle(const QString &style); + const QString &style() const; + + void setColor(const QString &color); + const QColor &color() const; + + void setSelectionColor(const QString &color); + const QColor &selectionColor() const; + + void setItalic(const QString &italic); + bool isItalic() const; + bool isItalicSpecified() const; + + void setBold(const QString &bold); + bool isBold() const; + bool isBoldSpecified() const; + + void setUnderlined(const QString &underlined); + bool isUnderlined() const; + bool isUnderlinedSpecified() const; + + void setStrikedOut(const QString &striked); + bool isStrikedOut() const; + bool isStrikedOutSpecified() const; + + bool isCustomized() const; + + static const QLatin1String kDsNormal; + static const QLatin1String kDsKeyword; + static const QLatin1String kDsDataType; + static const QLatin1String kDsDecVal; + static const QLatin1String kDsBaseN; + static const QLatin1String kDsFloat; + static const QLatin1String kDsChar; + static const QLatin1String kDsString; + static const QLatin1String kDsComment; + static const QLatin1String kDsOthers; + static const QLatin1String kDsAlert; + static const QLatin1String kDsFunction; + static const QLatin1String kDsRegionMarker; + static const QLatin1String kDsError; + +private: + QString m_style; + QColor m_color; + QColor m_selectionColor; + bool m_italic; + bool m_italicSpecified; + bool m_bold; + bool m_boldSpecified; + bool m_underlined; + bool m_underlinedSpecified; + bool m_strikedOut; + bool m_strikedOutSpecified; + bool m_isCustomized; +}; + +} // namespace Internal +} // namespace TextEditor + +#endif // ITEMDATA_H diff --git a/src/plugins/texteditor/generichighlighter/keywordlist.cpp b/src/plugins/texteditor/generichighlighter/keywordlist.cpp new file mode 100644 index 0000000000..48c9672915 --- /dev/null +++ b/src/plugins/texteditor/generichighlighter/keywordlist.cpp @@ -0,0 +1,61 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "keywordlist.h" + +using namespace TextEditor; +using namespace Internal; + +void KeywordList::addKeyword(const QString &keyword) +{ + if (keyword.isEmpty()) + return; + + m_keywords.insert(keyword); +} + +bool KeywordList::isKeyword(const QString &keyword, Qt::CaseSensitivity sensitivity) const +{ + if (keyword.isEmpty()) + return false; + + // Case sensitivity could be implemented, for example, by converting all keywords to lower + // if the global sensitivity attribute is insensitive, then always checking for containment + // (with a conversion to lower in the necessary cases). But the code below is one alternative + // to support the existence of local sensitivity attributes (which override the global one - + // currently not documented). + if (sensitivity == Qt::CaseSensitive) { + return m_keywords.contains(keyword); + } else { + foreach (const QString &s, m_keywords) + if (keyword.compare(s, Qt::CaseInsensitive) == 0) + return true; + return false; + } +} diff --git a/src/plugins/texteditor/generichighlighter/keywordlist.h b/src/plugins/texteditor/generichighlighter/keywordlist.h new file mode 100644 index 0000000000..82495ca220 --- /dev/null +++ b/src/plugins/texteditor/generichighlighter/keywordlist.h @@ -0,0 +1,53 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef KEYWORDLIST_H +#define KEYWORDLIST_H + +#include <QtCore/QString> +#include <QtCore/QSet> + +namespace TextEditor { +namespace Internal { + +class KeywordList +{ +public: + + void addKeyword(const QString &keyword); + bool isKeyword(const QString &keyword, Qt::CaseSensitivity sensitivity) const; + +private: + QSet<QString> m_keywords; +}; + +} // namespace Internal +} // namespace TextEditor + +#endif // KEYWORDLIST_H diff --git a/src/plugins/texteditor/generichighlighter/manager.cpp b/src/plugins/texteditor/generichighlighter/manager.cpp new file mode 100644 index 0000000000..6080c4f764 --- /dev/null +++ b/src/plugins/texteditor/generichighlighter/manager.cpp @@ -0,0 +1,236 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "manager.h" +#include "highlightdefinition.h" +#include "highlightdefinitionhandler.h" +#include "highlighterexception.h" +#include "texteditorplugin.h" +#include "texteditorsettings.h" +#include "plaintexteditorfactory.h" + +#include <coreplugin/icore.h> +#include <utils/qtcassert.h> +#include <qtconcurrent/QtConcurrentTools> + +#include <QtCore/QtAlgorithms> +#include <QtCore/QtPlugin> +#include <QtCore/QString> +#include <QtCore/QLatin1Char> +#include <QtCore/QLatin1String> +#include <QtCore/QStringList> +#include <QtCore/QFile> +#include <QtCore/QFileInfo> +#include <QtCore/QDir> +#include <QtCore/QList> +#include <QtCore/QRegExp> +#include <QtCore/QFuture> +#include <QtCore/QtConcurrentRun> +#include <QtXml/QXmlSimpleReader> +#include <QtXml/QXmlInputSource> +#include <QtXml/QXmlStreamReader> +#include <QtXml/QXmlStreamAttributes> + +using namespace TextEditor; +using namespace Internal; + +Manager::Manager() +{} + +Manager *Manager::instance() +{ + static Manager manager; + return &manager; +} + +QString Manager::definitionIdByName(const QString &name) const +{ return m_idByName.value(name); } + +QString Manager::definitionIdByMimeType(const QString &mimeType) const +{ + Q_ASSERT(!mimeType.isEmpty()); + + if (m_idByMimeType.count(mimeType) <= 1) { + return m_idByMimeType.value(mimeType); + } else { + QStringList candidateIds; + QMultiHash<QString, QString>::const_iterator it = m_idByMimeType.find(mimeType); + QMultiHash<QString, QString>::const_iterator endIt = m_idByMimeType.end(); + for (; it != endIt && it.key() == mimeType; ++it) + candidateIds.append(it.value()); + + qSort(candidateIds.begin(), candidateIds.end(), m_priorityComp); + return candidateIds.last(); + } +} + +const QSharedPointer<HighlightDefinition> &Manager::definition(const QString &id) +{ + if (!m_definitions.contains(id)) { + m_isBuilding.insert(id); + + QFile definitionFile(id); + if (!definitionFile.open(QIODevice::ReadOnly | QIODevice::Text)) + throw HighlighterException(); + QXmlInputSource source(&definitionFile); + + QSharedPointer<HighlightDefinition> definition(new HighlightDefinition); + HighlightDefinitionHandler handler(definition); + + QXmlSimpleReader reader; + reader.setContentHandler(&handler); + reader.parse(source); + + m_definitions.insert(id, definition); + definitionFile.close(); + m_isBuilding.remove(id); + } + + return *m_definitions.constFind(id); +} + +bool Manager::isBuildingDefinition(const QString &id) const +{ return m_isBuilding.contains(id); } + +void Manager::registerMimeTypes() +{ + QFuture<Core::MimeType> future = + QtConcurrent::run(&Manager::gatherDefinitionsMimeTypes, this); + m_watcher.setFuture(future); + + connect(&m_watcher, SIGNAL(resultReadyAt(int)), this, SLOT(registerMimeType(int))); +} + +void Manager::gatherDefinitionsMimeTypes(QFutureInterface<Core::MimeType> &future) +{ + QDir definitionsDir(Core::ICore::instance()->resourcePath() + + QLatin1String("/generic-highlighter")); + + QStringList filter(QLatin1String("*.xml")); + definitionsDir.setNameFilters(filter); + + const QFileInfoList &filesInfo = definitionsDir.entryInfoList(); + foreach (const QFileInfo &fileInfo, filesInfo) { + QString comment; + QStringList mimeTypes; + QStringList patterns; + parseDefinitionMetadata(fileInfo, &comment, &mimeTypes, &patterns); + + // A definition can specify multiple MIME types and file extensions/patterns. However, each + // thing is done with a single string. Then, there is no direct way to tell which patterns + // belong to which MIME types nor whether a MIME type is just an alias for the other. + // Currently, I associate all expressions/patterns with all MIME types from a definition. + + static const QStringList textPlain(QLatin1String("text/plain")); + + QList<QRegExp> expressions; + foreach (const QString &type, mimeTypes) { + Core::MimeType mimeType = Core::ICore::instance()->mimeDatabase()->findByType(type); + if (mimeType.isNull()) { + if (expressions.isEmpty()) { + foreach (const QString &pattern, patterns) + expressions.append(QRegExp(pattern, Qt::CaseSensitive, QRegExp::Wildcard)); + } + + mimeType.setType(type); + mimeType.setSubClassesOf(textPlain); + mimeType.setComment(comment); + mimeType.setGlobPatterns(expressions); + + future.reportResult(mimeType); + } + } + } +} + +void Manager::registerMimeType(int index) const +{ + const Core::MimeType &mimeType = m_watcher.resultAt(index); + Core::ICore::instance()->mimeDatabase()->addMimeType(mimeType); + TextEditorPlugin::instance()->editorFactory()->addMimeType(mimeType.type()); +} + +void Manager::parseDefinitionMetadata(const QFileInfo &fileInfo, + QString *comment, + QStringList *mimeTypes, + QStringList *patterns) +{ + static const QLatin1Char kSemiColon(';'); + static const QLatin1Char kSlash('/'); + static const QLatin1String kLanguage("language"); + static const QLatin1String kName("name"); + static const QLatin1String kExtensions("extensions"); + static const QLatin1String kMimeType("mimetype"); + static const QLatin1String kPriority("priority"); + static const QLatin1String kArtificial("artificial"); + + const QString &id = fileInfo.absoluteFilePath(); + + QFile definitionFile(id); + if (!definitionFile.open(QIODevice::ReadOnly | QIODevice::Text)) + return; + + QXmlStreamReader reader(&definitionFile); + while (!reader.atEnd() && !reader.hasError()) { + if (reader.readNext() == QXmlStreamReader::StartElement && + reader.name() == kLanguage) { + const QXmlStreamAttributes &attr = reader.attributes(); + + *comment = attr.value(kName).toString(); + m_idByName.insert(*comment, id); + + *patterns = attr.value(kExtensions).toString().split(kSemiColon, + QString::SkipEmptyParts); + + *mimeTypes = attr.value(kMimeType).toString().split(kSemiColon, + QString::SkipEmptyParts); + if (mimeTypes->isEmpty()) { + // There are definitions which do not specify a MIME type, but specify file + // patterns. Creating an artificial MIME type is a workaround. + QString artificialType(kArtificial); + artificialType.append(kSlash).append(*comment); + m_idByMimeType.insert(artificialType, id); + mimeTypes->append(artificialType); + } else { + foreach (const QString &type, *mimeTypes) + m_idByMimeType.insert(type, id); + } + + // The priority below should not be confused with the priority used when matching files + // to MIME types. Kate uses this when there are definitions which share equal + // extensions/patterns. Here it is for choosing a highlight definition if there are + // multiple ones associated with the same MIME type (should not happen in general). + m_priorityComp.m_priorityById.insert(id, attr.value(kPriority).toString().toInt()); + + break; + } + } + reader.clear(); + definitionFile.close(); +} diff --git a/src/plugins/texteditor/generichighlighter/manager.h b/src/plugins/texteditor/generichighlighter/manager.h new file mode 100644 index 0000000000..f5663dc411 --- /dev/null +++ b/src/plugins/texteditor/generichighlighter/manager.h @@ -0,0 +1,98 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef MANAGER_H +#define MANAGER_H + +#include <coreplugin/mimedatabase.h> + +#include <QtCore/QString> +#include <QtCore/QHash> +#include <QtCore/QMultiHash> +#include <QtCore/QSet> +#include <QtCore/QSharedPointer> +#include <QtCore/QFutureWatcher> + +QT_BEGIN_NAMESPACE +class QFileInfo; +class QStringList; +template <class> class QFutureInterface; +QT_END_NAMESPACE + +namespace TextEditor { +namespace Internal { + +class HighlightDefinition; + +class Manager : public QObject +{ + Q_OBJECT +public: + static Manager *instance(); + + QString definitionIdByName(const QString &name) const; + QString definitionIdByMimeType(const QString &mimeType) const; + bool isBuildingDefinition(const QString &id) const; + const QSharedPointer<HighlightDefinition> &definition(const QString &id); + +private slots: + void registerMimeTypes(); + void registerMimeType(int index) const; + +private: + Manager(); + Q_DISABLE_COPY(Manager) + + void gatherDefinitionsMimeTypes(QFutureInterface<Core::MimeType> &future); + void parseDefinitionMetadata(const QFileInfo &fileInfo, + QString *comment, + QStringList *mimeTypes, + QStringList *patterns); + + struct PriorityCompare + { + bool operator()(const QString &a, const QString &b) const + { return m_priorityById.value(a) < m_priorityById.value(b); } + + QHash<QString, int> m_priorityById; + }; + PriorityCompare m_priorityComp; + + QFutureWatcher<Core::MimeType> m_watcher; + + QHash<QString, QString> m_idByName; + QMultiHash<QString, QString> m_idByMimeType; + QHash<QString, QSharedPointer<HighlightDefinition> > m_definitions; + QSet<QString> m_isBuilding; +}; + +} // namespace Internal +} // namespace TextEditor + +#endif // MANAGER_H diff --git a/src/plugins/texteditor/generichighlighter/progressdata.cpp b/src/plugins/texteditor/generichighlighter/progressdata.cpp new file mode 100644 index 0000000000..12c76079d8 --- /dev/null +++ b/src/plugins/texteditor/generichighlighter/progressdata.cpp @@ -0,0 +1,82 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "progressdata.h" + +#include <QtCore/QtGlobal> + +using namespace TextEditor; +using namespace Internal; + +ProgressData::ProgressData() : + m_offset(0), + m_savedOffset(-1), + m_onlySpacesSoFar(true), + m_willContinueLine(false) +{} + +void ProgressData::setOffset(const int offset) +{ m_offset = offset; } + +int ProgressData::offset() const +{ return m_offset; } + +void ProgressData::incrementOffset() +{ ++m_offset; } + +void ProgressData::incrementOffset(const int increment) +{ m_offset += increment; } + +void ProgressData::saveOffset() +{ m_savedOffset = m_offset; } + +void ProgressData::restoreOffset() +{ + Q_ASSERT(m_savedOffset != -1); + m_offset = m_savedOffset; + m_savedOffset = -1; +} + +void ProgressData::setOnlySpacesSoFar(const bool onlySpaces) +{ m_onlySpacesSoFar = onlySpaces; } + +bool ProgressData::onlySpacesSoFar() const +{ return m_onlySpacesSoFar; } + +void ProgressData::setWillContinueLine(const bool willContinue) +{ m_willContinueLine = willContinue; } + +bool ProgressData::willContinueLine() const +{ return m_willContinueLine; } + +void ProgressData::setCaptures(const QStringList &captures) +{ m_captures = captures; } + +const QStringList &ProgressData::captures() const +{ return m_captures; } diff --git a/src/plugins/texteditor/generichighlighter/progressdata.h b/src/plugins/texteditor/generichighlighter/progressdata.h new file mode 100644 index 0000000000..a621c15dc7 --- /dev/null +++ b/src/plugins/texteditor/generichighlighter/progressdata.h @@ -0,0 +1,72 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef PROGRESSDATA_H +#define PROGRESSDATA_H + +#include <QtCore/QStringList> + +namespace TextEditor { +namespace Internal { + +class ProgressData +{ +public: + ProgressData(); + + void setOffset(const int offset); + int offset() const; + + void incrementOffset(); + void incrementOffset(const int increment); + + void saveOffset(); + void restoreOffset(); + + void setOnlySpacesSoFar(const bool onlySpaces); + bool onlySpacesSoFar() const; + + void setWillContinueLine(const bool willContinue); + bool willContinueLine() const; + + void setCaptures(const QStringList &captures); + const QStringList &captures() const; + +private: + int m_offset; + int m_savedOffset; + bool m_onlySpacesSoFar; + bool m_willContinueLine; + QStringList m_captures; +}; + +} // namespace Internal +} // namespace TextEditor + +#endif // PROGRESSDATA_H diff --git a/src/plugins/texteditor/generichighlighter/reuse.h b/src/plugins/texteditor/generichighlighter/reuse.h new file mode 100644 index 0000000000..197e3042d9 --- /dev/null +++ b/src/plugins/texteditor/generichighlighter/reuse.h @@ -0,0 +1,100 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef REUSE_H +#define REUSE_H + +#include <Qt> +#include <QtCore/QString> +#include <QtCore/QLatin1String> +#include <QtCore/QChar> +#include <QtGui/QFont> + +namespace TextEditor { +namespace Internal { + +inline bool toBool(const QString &s) +{ + static const QLatin1String kTrue("true"); + static const QLatin1String k1("1"); + + if (s.toLower() == kTrue || s == k1) + return true; + return false; +} + + +inline Qt::CaseSensitivity toCaseSensitivity(const bool sensitive) +{ + if (sensitive) + return Qt::CaseSensitive; + return Qt::CaseInsensitive; +} + +inline QFont::Weight toFontWeight(const bool bold) +{ + if (bold) + return QFont::Bold; + else + return QFont::Normal; +} + +inline bool isOctalDigit(const QChar &c) +{ + static const QLatin1Char k0('0'); + static const QLatin1Char k7('7'); + + return c >= k0 && c <= k7; +} + +inline bool isHexDigit(const QChar &c) +{ + static const QLatin1Char k0('0'); + static const QLatin1Char k9('9'); + static const QLatin1Char kA('A'); + static const QLatin1Char kF('F'); + static const QLatin1Char ka('a'); + static const QLatin1Char kf('f'); + + if ((c >= k0 && c <= k9) || (c >= kA && c <= kF) || (c >= ka && c <= kf)) + return true; + + return false; +} + +inline void setStartCharacter(QChar &c, const QString &character) +{ + if (!character.isEmpty()) + c = character.at(0); +} + +} // namespace Internal +} // namespace TextEditor + +#endif // REUSE_H diff --git a/src/plugins/texteditor/generichighlighter/rule.cpp b/src/plugins/texteditor/generichighlighter/rule.cpp new file mode 100644 index 0000000000..1ca7349c6a --- /dev/null +++ b/src/plugins/texteditor/generichighlighter/rule.cpp @@ -0,0 +1,292 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "rule.h" +#include "highlighterexception.h" +#include "progressdata.h" +#include "highlightdefinition.h" +#include "reuse.h" + +#include <QtCore/QStringList> + +#include <functional> + +using namespace TextEditor; +using namespace Internal; + +const QLatin1Char Rule::kBackSlash('\\'); +const QLatin1Char Rule::kUnderscore('_'); +const QLatin1Char Rule::kDot('.'); +const QLatin1Char Rule::kPlus('+'); +const QLatin1Char Rule::kMinus('-'); +const QLatin1Char Rule::kZero('0'); +const QLatin1Char Rule::kQuote('\"'); +const QLatin1Char Rule::kSingleQuote('\''); +const QLatin1Char Rule::kQuestion('?'); +const QLatin1Char Rule::kX('x'); +const QLatin1Char Rule::kA('a'); +const QLatin1Char Rule::kB('b'); +const QLatin1Char Rule::kE('e'); +const QLatin1Char Rule::kF('f'); +const QLatin1Char Rule::kN('n'); +const QLatin1Char Rule::kR('r'); +const QLatin1Char Rule::kT('t'); +const QLatin1Char Rule::kV('v'); + +Rule::Rule(bool consumesNonSpace) : + m_lookAhead(false), m_firstNonSpace(false), m_column(-1), m_consumesNonSpace(consumesNonSpace) +{} + +Rule::~Rule() +{} + +void Rule::setContext(const QString &context) +{ m_context = context; } + +const QString &Rule::context() const +{ return m_context; } + +void Rule::setItemData(const QString &itemData) +{ m_itemData = itemData; } + +const QString &Rule::itemData() const +{ return m_itemData; } + +void Rule::setBeginRegion(const QString &begin) +{ m_beginRegion = begin; } + +const QString &Rule::beginRegion() const +{ return m_beginRegion; } + +void Rule::setEndRegion(const QString &end) +{ m_endRegion = end; } + +const QString &Rule::endRegion() const +{ return m_endRegion; } + +void Rule::setLookAhead(const QString &lookAhead) +{ m_lookAhead = toBool(lookAhead); } + +bool Rule::isLookAhead() const +{ return m_lookAhead; } + +void Rule::setFirstNonSpace(const QString &firstNonSpace) +{ m_firstNonSpace = toBool(firstNonSpace); } + +bool Rule::isFirstNonSpace() const +{ return m_firstNonSpace; } + +void Rule::setColumn(const QString &column) +{ + bool ok; + m_column = column.toInt(&ok); + if (!ok) + m_column = -1; +} + +int Rule::column() const +{ return m_column; } + +void Rule::addChild(const QSharedPointer<Rule> &rule) +{ m_childRules.append(rule); } + +bool Rule::hasChild() const +{ return !m_childRules.isEmpty(); } + +const QList<QSharedPointer<Rule> > &Rule::childs() const +{ return m_childRules; } + +void Rule::setDefinition(const QSharedPointer<HighlightDefinition> &definition) +{ m_definition = definition; } + +const QSharedPointer<HighlightDefinition> &Rule::definition() const +{ return m_definition; } + +template <class predicate_t> +bool Rule::predicateMatchSucceed(const QString &text, + const int length, + ProgressData *progress, + const predicate_t &p) const +{ + int original = progress->offset(); + while (progress->offset() < length && p(text.at(progress->offset()))) + progress->incrementOffset(); + + if (original != progress->offset()) + return true; + + return false; +} + +bool Rule::charPredicateMatchSucceed(const QString &text, + const int length, + ProgressData *progress, + bool (QChar::* predicate)() const) const +{ + return predicateMatchSucceed(text, length, progress, std::mem_fun_ref(predicate)); +} + +bool Rule::charPredicateMatchSucceed(const QString &text, + const int length, + ProgressData *progress, + bool (*predicate)(const QChar &)) const +{ + return predicateMatchSucceed(text, length, progress, std::ptr_fun(predicate)); +} + +bool Rule::matchSucceed(const QString &text, const int length, ProgressData *progress) const +{ + if (m_firstNonSpace && !progress->onlySpacesSoFar()) + return false; + + if (m_column != -1 && m_column != progress->offset()) + return false; + + int original = progress->offset(); + if (doMatchSucceed(text, length, progress)) { + if (progress->onlySpacesSoFar() && !m_lookAhead && m_consumesNonSpace) + progress->setOnlySpacesSoFar(false); + + if (m_lookAhead) + progress->setOffset(original); + + return true; + } + + return false; +} + +Rule *Rule::clone() const +{ return doClone(); } + +bool Rule::matchCharacter(const QString &text, + const int length, + ProgressData *progress, + const QChar &c, + bool saveRestoreOffset) const +{ + Q_UNUSED(length) + Q_ASSERT(progress->offset() < length); + + if (text.at(progress->offset()) == c) { + if (saveRestoreOffset) + progress->saveOffset(); + progress->incrementOffset(); + return true; + } + + return false; +} + +bool Rule::matchEscapeSequence(const QString &text, + const int length, + ProgressData *progress, + bool saveRestoreOffset) const +{ + if (matchCharacter(text, length, progress, kBackSlash, saveRestoreOffset)) { + + if (progress->offset() < length) { + const QChar &c = text.at(progress->offset()); + if (c == kA || c == kB || c == kE || c == kF || c == kN || c == kR || c == kT || + c == kV || c == kV || c == kQuestion || c == kSingleQuote || c == kQuote || + c == kBackSlash) { + progress->incrementOffset(); + return true; + } else if (saveRestoreOffset) { + progress->restoreOffset(); + } + } else if (saveRestoreOffset) { + progress->restoreOffset(); + } + } + + return false; +} + +bool Rule::matchOctalSequence(const QString &text, + const int length, + ProgressData *progress, + bool saveRestoreOffset) const +{ + // An octal sequence is identified as in the C++ Standard. + // octal-escape-sequence: + // \ octal-digit + // \ octal-digit octal-digit + // \ octal-digit octal-digit octal-digit + + if (matchCharacter(text, length, progress, kBackSlash, saveRestoreOffset)) { + + int count = 0; + while (progress->offset() < length && + count < 3 && + isOctalDigit(text.at(progress->offset()))) { + ++count; + progress->incrementOffset(); + } + + if (count > 0) + return true; + else if (saveRestoreOffset) + progress->restoreOffset(); + } + + return false; +} + +bool Rule::matchHexSequence(const QString &text, + const int length, + ProgressData *progress, + bool saveRestoreOffset) const +{ + // An hex sequence is identified as in the C++ Standard. + // hexadecimal-escape-sequence: + // \x hexadecimal-digit + // hexadecimal-escape-sequence hexadecimal-digit + + if (matchCharacter(text, length, progress, kBackSlash, saveRestoreOffset)) { + + if (progress->offset() < length && matchCharacter(text, length, progress, kX, false)) { + bool found = false; + while (progress->offset() < length && isHexDigit(text.at(progress->offset()))) { + if (!found) + found = true; + progress->incrementOffset(); + } + + if (found) + return true; + else if (saveRestoreOffset) + progress->restoreOffset(); + } else if (saveRestoreOffset) { + progress->restoreOffset(); + } + } + + return false; +} diff --git a/src/plugins/texteditor/generichighlighter/rule.h b/src/plugins/texteditor/generichighlighter/rule.h new file mode 100644 index 0000000000..a53b2f8e5c --- /dev/null +++ b/src/plugins/texteditor/generichighlighter/rule.h @@ -0,0 +1,160 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef RULE_H +#define RULE_H + +#include <QtCore/QString> +#include <QtCore/QList> +#include <QtCore/QSharedPointer> + +namespace TextEditor { +namespace Internal { + +class ProgressData; +class HighlightDefinition; + +class Rule +{ +public: + Rule(bool consumesNonSpace = true); + virtual ~Rule(); + + void setContext(const QString &context); + const QString &context() const; + + void setItemData(const QString &itemData); + const QString &itemData() const; + + void setBeginRegion(const QString &begin); + const QString &beginRegion() const; + + void setEndRegion(const QString &end); + const QString &endRegion() const; + + void setLookAhead(const QString &lookAhead); + bool isLookAhead() const; + + void setFirstNonSpace(const QString &firstNonSpace); + bool isFirstNonSpace() const; + + void setColumn(const QString &column); + int column() const; + + void addChild(const QSharedPointer<Rule> &rule); + const QList<QSharedPointer<Rule> > &childs() const; + bool hasChild() const; + + void setDefinition(const QSharedPointer<HighlightDefinition> &definition); + const QSharedPointer<HighlightDefinition> &definition() const; + + bool matchSucceed(const QString &text, const int length, ProgressData *progress) const; + + Rule *clone() const; + +protected: + bool charPredicateMatchSucceed(const QString &text, + const int length, + ProgressData *progress, + bool (QChar::* predicate)() const) const; + bool charPredicateMatchSucceed(const QString &text, + const int length, + ProgressData *progress, + bool (*predicate)(const QChar &)) const; + + bool matchCharacter(const QString &text, + const int length, + ProgressData *progress, + const QChar &c, + bool saveRestoreOffset = true) const; + bool matchEscapeSequence(const QString &text, + const int length, + ProgressData *progress, + bool saveRestoreOffset = true) const; + bool matchOctalSequence(const QString &text, + const int length, + ProgressData *progress, + bool saveRestoreOffset = true) const; + bool matchHexSequence(const QString &text, + const int length, + ProgressData *progress, + bool saveRestoreOffset = true) const; + + static const QLatin1Char kBackSlash; + static const QLatin1Char kUnderscore; + static const QLatin1Char kDot; + static const QLatin1Char kPlus; + static const QLatin1Char kMinus; + static const QLatin1Char kZero; + static const QLatin1Char kQuote; + static const QLatin1Char kSingleQuote; + static const QLatin1Char kQuestion; + static const QLatin1Char kX; + static const QLatin1Char kA; + static const QLatin1Char kB; + static const QLatin1Char kE; + static const QLatin1Char kF; + static const QLatin1Char kN; + static const QLatin1Char kR; + static const QLatin1Char kT; + static const QLatin1Char kV; + +private: + virtual bool doMatchSucceed(const QString &text, + const int length, + ProgressData *progress) const = 0; + + virtual Rule *doClone() const = 0; + + template <class predicate_t> + bool predicateMatchSucceed(const QString &text, + const int length, + ProgressData *progress, + const predicate_t &p) const; + + QString m_context; + QString m_itemData; + QString m_beginRegion; + QString m_endRegion; + bool m_lookAhead; + bool m_firstNonSpace; + int m_column; + bool m_consumesNonSpace; + + QList<QSharedPointer<Rule> > m_childRules; + + // Rules are represented within contexts. However, they have their own definition because + // of externally included rules. + QSharedPointer<HighlightDefinition> m_definition; +}; + +} // namespace Internal +} // namespace TextEditor + +#endif // RULE_H diff --git a/src/plugins/texteditor/generichighlighter/specificrules.cpp b/src/plugins/texteditor/generichighlighter/specificrules.cpp new file mode 100644 index 0000000000..6fe4269367 --- /dev/null +++ b/src/plugins/texteditor/generichighlighter/specificrules.cpp @@ -0,0 +1,467 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "specificrules.h" +#include "highlightdefinition.h" +#include "keywordlist.h" +#include "progressdata.h" +#include "reuse.h" + +#include <QLatin1Char> + +using namespace TextEditor; +using namespace Internal; + +namespace { + +void replaceByCaptures(QChar *c, const QStringList &captures) +{ + int index = c->digitValue(); + if (index > 0) { + const QString &capture = captures.at(index); + *c = capture.at(0); + } +} + +void replaceByCaptures(QString *s, const QStringList &captures) +{ + static const QLatin1Char kPercent('%'); + + int index; + int from = 0; + while ((index = s->indexOf(kPercent, from)) != -1) { + from = index + 1; + + QString accumulator; + while (from < s->length() && s->at(from).isDigit()) { + accumulator.append(s->at(from)); + ++from; + } + + bool ok; + int number = accumulator.toInt(&ok); + Q_ASSERT(ok); + + s->replace(index, accumulator.length() + 1, captures.at(number)); + index = from; + } +} +} + +// DetectChar +void DetectCharRule::setChar(const QString &character) +{ setStartCharacter(m_char, character); } + +void DetectCharRule::doReplaceExpressions(const QStringList &captures) +{ replaceByCaptures(&m_char, captures); } + +bool DetectCharRule::doMatchSucceed(const QString &text, + const int length, + ProgressData *progress) const +{ + return matchCharacter(text, length, progress, m_char); +} + +// Detect2Chars +void Detect2CharsRule::setChar(const QString &character) +{ setStartCharacter(m_char, character); } + +void Detect2CharsRule::setChar1(const QString &character) +{ setStartCharacter(m_char1, character); } + +void Detect2CharsRule::doReplaceExpressions(const QStringList &captures) +{ + replaceByCaptures(&m_char, captures); + replaceByCaptures(&m_char1, captures); +} + +bool Detect2CharsRule::doMatchSucceed(const QString &text, + const int length, + ProgressData *progress) const +{ + if (matchCharacter(text, length, progress, m_char)) { + if (progress->offset() < length && matchCharacter(text, length, progress, m_char1, false)) + return true; + else + progress->restoreOffset(); + } + + return false; +} + +// AnyChar +void AnyCharRule::setCharacterSet(const QString &s) +{ m_characterSet = s; } + +bool AnyCharRule::doMatchSucceed(const QString &text, + const int length, + ProgressData *progress) const +{ + Q_UNUSED(length) + + if (m_characterSet.contains(text.at(progress->offset()))) { + progress->incrementOffset(); + return true; + } + + return false; +} + +// StringDetect +void StringDetectRule::setString(const QString &s) +{ + m_string = s; + m_length = m_string.length(); +} + +void StringDetectRule::setInsensitive(const QString &insensitive) +{ m_caseSensitivity = toCaseSensitivity(!toBool(insensitive)); } + +void StringDetectRule::doReplaceExpressions(const QStringList &captures) +{ replaceByCaptures(&m_string, captures); } + +bool StringDetectRule::doMatchSucceed(const QString &text, + const int length, + ProgressData *progress) const +{ + if (length - progress->offset() >= m_length) { + QString candidate = text.fromRawData(text.unicode() + progress->offset(), m_length); + if (candidate.compare(m_string, m_caseSensitivity) == 0) { + progress->incrementOffset(m_length); + return true; + } + } + + return false; +} + +// RegExpr +void RegExprRule::setPattern(const QString &pattern) +{ m_expression.setPattern(pattern); } + +void RegExprRule::setInsensitive(const QString &insensitive) +{ m_expression.setCaseSensitivity(toCaseSensitivity(!toBool(insensitive))); } + +void RegExprRule::setMinimal(const QString &minimal) +{ m_expression.setMinimal(toBool(minimal)); } + +void RegExprRule::doReplaceExpressions(const QStringList &captures) +{ + QString s = m_expression.pattern(); + replaceByCaptures(&s, captures); + m_expression.setPattern(s); +} + +bool RegExprRule::doMatchSucceed(const QString &text, + const int length, + ProgressData *progress) const +{ + Q_UNUSED(length) + + // This is not documented but a regular expression match is considered valid if it starts + // at the current position and if the match length is not zero. Checked in Kate's source code + // after some unexpected problems. + const int offset = progress->offset(); + if (m_expression.indexIn(text, offset, QRegExp::CaretAtZero) == offset) { + if (m_expression.matchedLength() == 0) + return false; + progress->incrementOffset(m_expression.matchedLength()); + progress->setCaptures(m_expression.capturedTexts()); + return true; + } + + return false; +} + +// Keyword +KeywordRule::KeywordRule(const QSharedPointer<HighlightDefinition> &definition) : + m_overrideGlobal(false) +{ + setDefinition(definition); +} + +KeywordRule::~KeywordRule() +{} + +void KeywordRule::setInsensitive(const QString &insensitive) +{ + if (!insensitive.isEmpty()) { + m_overrideGlobal = true; + m_localCaseSensitivity = toCaseSensitivity(!toBool(insensitive)); + } +} + +void KeywordRule::setList(const QString &listName) +{ m_list = definition()->keywordList(listName); } + +bool KeywordRule::doMatchSucceed(const QString &text, + const int length, + ProgressData *progress) const +{ + int current = progress->offset(); + + if (current > 0 && !definition()->isDelimiter(text.at(current - 1))) + return false; + if (definition()->isDelimiter(text.at(current))) + return false; + + while (current < length && !definition()->isDelimiter(text.at(current))) + ++current; + + QString candidate = + QString::fromRawData(text.unicode() + progress->offset(), current - progress->offset()); + if ((m_overrideGlobal && m_list->isKeyword(candidate, m_localCaseSensitivity)) || + (!m_overrideGlobal && m_list->isKeyword(candidate, definition()->keywordsSensitive()))) { + progress->setOffset(current); + return true; + } + + return false; +} + +// Int +bool IntRule::doMatchSucceed(const QString &text, + const int length, + ProgressData *progress) const +{ + const int offset = progress->offset(); + + // This is necessary to correctly highlight an invalid octal like 09, for example. + if (offset > 0 && text.at(offset - 1).isDigit()) + return false; + + if (text.at(offset).isDigit() && text.at(offset) != kZero) { + progress->incrementOffset(); + charPredicateMatchSucceed(text, length, progress, &QChar::isDigit); + return true; + } + + return false; +} + +// Float +bool FloatRule::doMatchSucceed(const QString &text, const int length, ProgressData *progress) const +{ + progress->saveOffset(); + + bool integralPart = charPredicateMatchSucceed(text, length, progress, &QChar::isDigit); + + bool decimalPoint = false; + if (progress->offset() < length && text.at(progress->offset()) == kDot) { + progress->incrementOffset(); + decimalPoint = true; + } + + bool fractionalPart = charPredicateMatchSucceed(text, length, progress, &QChar::isDigit); + + bool exponentialPart = false; + int offset = progress->offset(); + if (offset < length && (text.at(offset) == kE || text.at(offset).toLower() == kE)) { + progress->incrementOffset(); + + offset = progress->offset(); + if (offset < length && (text.at(offset) == kPlus || text.at(offset) == kMinus)) + progress->incrementOffset(); + + if (charPredicateMatchSucceed(text, length, progress, &QChar::isDigit)) { + exponentialPart = true; + } else { + progress->restoreOffset(); + return false; + } + } + + if ((integralPart || fractionalPart) && (decimalPoint || exponentialPart)) + return true; + + progress->restoreOffset(); + return false; +} + +// COctal +bool HlCOctRule::doMatchSucceed(const QString &text, + const int length, + ProgressData *progress) const +{ + if (matchCharacter(text, length, progress, kZero)) { + // In the definition files the number matching rules which are more restrictive should + // appear before the rules which are least resctritive. Although this happens in general + // there is at least one case where this is not strictly followed for existent definition + // files (specifically, HlCHex comes before HlCOct). So the condition below. + const int offset = progress->offset(); + if (offset < length && (text.at(offset) == kX || text.at(offset).toLower() == kX)) { + progress->restoreOffset(); + return false; + } + + charPredicateMatchSucceed(text, length, progress, &isOctalDigit); + return true; + } + + return false; +} + +// CHex +bool HlCHexRule::doMatchSucceed(const QString &text, + const int length, + ProgressData *progress) const +{ + if (matchCharacter(text, length, progress, kZero)) { + const int offset = progress->offset(); + if (offset < length && text.at(offset) != kX && text.at(offset).toLower() != kX) { + progress->restoreOffset(); + return false; + } + + progress->incrementOffset(); + if (charPredicateMatchSucceed(text, length, progress, &isHexDigit)) + return true; + else + progress->restoreOffset(); + } + + return false; +} + +// CString +bool HlCStringCharRule::doMatchSucceed(const QString &text, + const int length, + ProgressData *progress) const +{ + if (matchEscapeSequence(text, length, progress)) + return true; + + if (matchOctalSequence(text, length, progress)) + return true; + + if (matchHexSequence(text, length, progress)) + return true; + + return false; +} + +// CChar +bool HlCCharRule::doMatchSucceed(const QString &text, + const int length, + ProgressData *progress) const +{ + if (matchCharacter(text, length, progress, kSingleQuote)) { + if (progress->offset() < length) { + if (text.at(progress->offset()) != kBackSlash && + text.at(progress->offset()) != kSingleQuote) { + progress->incrementOffset(); + } else if (!matchEscapeSequence(text, length, progress, false)) { + progress->restoreOffset(); + return false; + } + + if (progress->offset() < length && + matchCharacter(text, length, progress, kSingleQuote, false)) { + return true; + } else { + progress->restoreOffset(); + } + } else { + progress->restoreOffset(); + } + } + + return false; +} + +// RangeDetect +void RangeDetectRule::setChar(const QString &character) +{ setStartCharacter(m_char, character); } + +void RangeDetectRule::setChar1(const QString &character) +{ setStartCharacter(m_char1, character); } + +bool RangeDetectRule::doMatchSucceed(const QString &text, + const int length, + ProgressData *progress) const +{ + if (matchCharacter(text, length, progress, m_char)) { + while (progress->offset() < length) { + if (matchCharacter(text, length, progress, m_char1, false)) + return true; + progress->incrementOffset(); + } + progress->restoreOffset(); + } + + return false; +} + +// LineContinue +bool LineContinueRule::doMatchSucceed(const QString &text, + const int length, + ProgressData *progress) const +{ + if (progress->offset() != length - 1) + return false; + + if (text.at(progress->offset()) == kBackSlash) { + progress->setWillContinueLine(true); + return true; + } + + return false; +} + +// DetectSpaces +DetectSpacesRule::DetectSpacesRule() : Rule(false) +{} + +bool DetectSpacesRule::doMatchSucceed(const QString &text, + const int length, + ProgressData *progress) const +{ + return charPredicateMatchSucceed(text, length, progress, &QChar::isSpace); +} + +// DetectIdentifier +bool DetectIdentifierRule::doMatchSucceed(const QString &text, + const int length, + ProgressData *progress) const +{ + // Identifiers are characterized by a letter or underscore as the first character and then + // zero or more word characters (\w*). + if (text.at(progress->offset()).isLetter() || text.at(progress->offset()) == kUnderscore) { + progress->incrementOffset(); + while (progress->offset() < length) { + const QChar ¤t = text.at(progress->offset()); + if (current.isLetterOrNumber() || current.isMark() || current == kUnderscore) + progress->incrementOffset(); + else + break; + } + return true; + } + return false; +} diff --git a/src/plugins/texteditor/generichighlighter/specificrules.h b/src/plugins/texteditor/generichighlighter/specificrules.h new file mode 100644 index 0000000000..7ec2552dad --- /dev/null +++ b/src/plugins/texteditor/generichighlighter/specificrules.h @@ -0,0 +1,288 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef SPECIFICRULES_H +#define SPECIFICRULES_H + +#include "rule.h" +#include "dynamicrule.h" + +#include <QtCore/QChar> +#include <QtCore/QString> +#include <QtCore/QRegExp> +#include <QtCore/QSharedPointer> + +namespace TextEditor { +namespace Internal { + +class KeywordList; +class HighlightDefinition; + +class DetectCharRule : public DynamicRule +{ +public: + virtual ~DetectCharRule() {} + + void setChar(const QString &character); + +private: + virtual bool doMatchSucceed(const QString &text, + const int length, + ProgressData *progress) const; + virtual DetectCharRule *doClone() const { return new DetectCharRule(*this); } + virtual void doReplaceExpressions(const QStringList &captures); + + QChar m_char; +}; + +class Detect2CharsRule : public DynamicRule +{ +public: + virtual ~Detect2CharsRule() {} + + void setChar(const QString &character); + void setChar1(const QString &character); + +private: + virtual bool doMatchSucceed(const QString &text, + const int length, + ProgressData *progress) const; + virtual Detect2CharsRule *doClone() const { return new Detect2CharsRule(*this); } + virtual void doReplaceExpressions(const QStringList &captures); + + QChar m_char; + QChar m_char1; +}; + +class AnyCharRule : public Rule +{ +public: + virtual ~AnyCharRule() {} + + void setCharacterSet(const QString &s); + +private: + virtual bool doMatchSucceed(const QString &text, + const int length, + ProgressData *progress) const; + virtual AnyCharRule *doClone() const { return new AnyCharRule(*this); } + + QString m_characterSet; +}; + +class StringDetectRule : public DynamicRule +{ +public: + virtual ~StringDetectRule() {} + + void setString(const QString &s); + void setInsensitive(const QString &insensitive); + +private: + virtual bool doMatchSucceed(const QString &text, + const int length, + ProgressData *progress) const; + virtual StringDetectRule *doClone() const { return new StringDetectRule(*this); } + virtual void doReplaceExpressions(const QStringList &captures); + + QString m_string; + int m_length; + Qt::CaseSensitivity m_caseSensitivity; +}; + +class RegExprRule : public DynamicRule +{ +public: + virtual ~RegExprRule() {} + + void setPattern(const QString &pattern); + void setInsensitive(const QString &insensitive); + void setMinimal(const QString &minimal); + +private: + virtual bool doMatchSucceed(const QString &text, + const int length, + ProgressData *progress) const; + virtual RegExprRule *doClone() const { return new RegExprRule(*this); } + virtual void doReplaceExpressions(const QStringList &captures); + + QRegExp m_expression; +}; + +class KeywordRule : public Rule +{ +public: + KeywordRule(const QSharedPointer<HighlightDefinition> &definition); + virtual ~KeywordRule(); + + void setInsensitive(const QString &insensitive); + void setList(const QString &listName); + +private: + virtual bool doMatchSucceed(const QString &text, + const int length, + ProgressData *progress) const; + virtual KeywordRule *doClone() const { return new KeywordRule(*this); } + + bool m_overrideGlobal; + Qt::CaseSensitivity m_localCaseSensitivity; + QSharedPointer<KeywordList> m_list; +}; + +class IntRule : public Rule +{ +public: + virtual ~IntRule() {} + +private: + virtual bool doMatchSucceed(const QString &text, + const int length, + ProgressData *progress) const; + virtual IntRule *doClone() const { return new IntRule(*this); } +}; + +class FloatRule : public Rule +{ +public: + virtual ~FloatRule() {} + +private: + virtual bool doMatchSucceed(const QString &text, + const int length, + ProgressData *progress) const; + virtual FloatRule *doClone() const { return new FloatRule(*this); } +}; + +class HlCOctRule : public Rule +{ +public: + virtual ~HlCOctRule() {} + +private: + virtual bool doMatchSucceed(const QString &text, + const int length, + ProgressData *progress) const; + virtual HlCOctRule *doClone() const { return new HlCOctRule(*this); } +}; + +class HlCHexRule : public Rule +{ +public: + virtual ~HlCHexRule() {} + +private: + virtual bool doMatchSucceed(const QString &text, + const int length, + ProgressData *progress) const; + virtual HlCHexRule *doClone() const { return new HlCHexRule(*this); } +}; + +class HlCStringCharRule : public Rule +{ +public: + virtual ~HlCStringCharRule() {} + +private: + virtual bool doMatchSucceed(const QString &text, + const int length, + ProgressData *progress) const; + virtual HlCStringCharRule *doClone() const { return new HlCStringCharRule(*this); } +}; + +class HlCCharRule : public Rule +{ +public: + virtual ~HlCCharRule() {} + +private: + virtual bool doMatchSucceed(const QString &text, + const int length, + ProgressData *progress) const; + virtual HlCCharRule *doClone() const { return new HlCCharRule(*this); } +}; + +class RangeDetectRule : public Rule +{ +public: + virtual ~RangeDetectRule() {} + + void setChar(const QString &character); + void setChar1(const QString &character); + +private: + virtual bool doMatchSucceed(const QString &text, + const int length, + ProgressData *progress) const; + virtual RangeDetectRule *doClone() const { return new RangeDetectRule(*this); } + + QChar m_char; + QChar m_char1; +}; + +class LineContinueRule : public Rule +{ +public: + virtual ~LineContinueRule() {} + +private: + virtual bool doMatchSucceed(const QString &text, + const int length, + ProgressData *progress) const; + virtual LineContinueRule *doClone() const { return new LineContinueRule(*this); } +}; + +class DetectSpacesRule : public Rule +{ +public: + DetectSpacesRule(); + virtual ~DetectSpacesRule() {} + +private: + virtual bool doMatchSucceed(const QString &text, + const int length, + ProgressData *progress) const; + virtual DetectSpacesRule *doClone() const { return new DetectSpacesRule(*this); } +}; + +class DetectIdentifierRule : public Rule +{ +public: + virtual ~DetectIdentifierRule() {} + +private: + virtual bool doMatchSucceed(const QString &text, + const int length, + ProgressData *progress) const; + virtual DetectIdentifierRule *doClone() const { return new DetectIdentifierRule(*this); } +}; + +} // namespace Internal +} // namespace TextEditor + +#endif // SPECIFICRULES_H diff --git a/src/plugins/texteditor/plaintexteditor.cpp b/src/plugins/texteditor/plaintexteditor.cpp index 257280b0b3..2c748b0738 100644 --- a/src/plugins/texteditor/plaintexteditor.cpp +++ b/src/plugins/texteditor/plaintexteditor.cpp @@ -31,9 +31,20 @@ #include "tabsettings.h" #include "texteditorconstants.h" #include "texteditorplugin.h" +#include "texteditorsettings.h" +#include "basetextdocument.h" +#include "highlightdefinition.h" +#include "highlighter.h" +#include "highlighterexception.h" +#include "manager.h" #include <coreplugin/coreconstants.h> #include <coreplugin/uniqueidmanager.h> +#include <coreplugin/icore.h> +#include <coreplugin/mimedatabase.h> + +#include <QtCore/QSharedPointer> +#include <QtCore/QFileInfo> using namespace TextEditor; using namespace TextEditor::Internal; @@ -54,8 +65,11 @@ PlainTextEditor::PlainTextEditor(QWidget *parent) setRequestMarkEnabled(false); setLineSeparatorsAllowed(true); - setMimeType(QLatin1String(TextEditor::Constants::C_TEXTEDITOR_MIMETYPE_TEXT)); setDisplayName(tr(Core::Constants::K_DEFAULT_TEXT_EDITOR_DISPLAY_NAME)); + + m_commentDefinition.clearCommentStyles(); + + connect(file(), SIGNAL(changed()), this, SLOT(configure())); } QList<int> PlainTextEditorEditable::context() const @@ -76,7 +90,56 @@ QString PlainTextEditorEditable::id() const return QLatin1String(Core::Constants::K_DEFAULT_TEXT_EDITOR_ID); } +void PlainTextEditor::unCommentSelection() +{ + Utils::unCommentSelection(this, m_commentDefinition); +} + +void PlainTextEditor::setFontSettings(const TextEditor::FontSettings & fs) +{ + TextEditor::BaseTextEditor::setFontSettings(fs); + + Highlighter *highlighter = static_cast<Highlighter *>(baseTextDocument()->syntaxHighlighter()); + if (!highlighter) + return; + + highlighter->configureFormats(fs); + highlighter->rehighlight(); +} + +void PlainTextEditor::configure() +{ + const QString &mimeType = Core::ICore::instance()->mimeDatabase()->findByFile( + QFileInfo(file()->fileName())).type(); + baseTextDocument()->setMimeType(mimeType); + + const QString &definitionId = Manager::instance()->definitionIdByMimeType(mimeType); + if (!definitionId.isEmpty()) { + try { + const QSharedPointer<HighlightDefinition> &definition = + Manager::instance()->definition(definitionId); + + Highlighter *highlighter = new Highlighter(definition->initialContext()); + highlighter->configureFormats(TextEditor::TextEditorSettings::instance()->fontSettings()); + + baseTextDocument()->setSyntaxHighlighter(highlighter); + + m_commentDefinition.setAfterWhiteSpaces(definition->isCommentAfterWhiteSpaces()); + m_commentDefinition.setSingleLine(definition->singleLineComment()); + m_commentDefinition.setMultiLineStart(definition->multiLineCommentStart()); + m_commentDefinition.setMultiLineEnd(definition->multiLineCommentEnd()); + } catch (const HighlighterException &) { + } + } + + // @todo: Indentation specification through the definition files is not really being + // used because Kate recommends to configure indentation through another feature. + // Maybe we should provide something similar in Creator? For now, only normal + // indentation is supported. + m_indenter.reset(new TextEditor::NormalIndenter); +} + void PlainTextEditor::indentBlock(QTextDocument *doc, QTextBlock block, QChar typedChar) { - m_indenter.indentBlock(doc, block, typedChar, tabSettings()); + m_indenter->indentBlock(doc, block, typedChar, tabSettings()); } diff --git a/src/plugins/texteditor/plaintexteditor.h b/src/plugins/texteditor/plaintexteditor.h index f2c5673fb0..fdc85423d2 100644 --- a/src/plugins/texteditor/plaintexteditor.h +++ b/src/plugins/texteditor/plaintexteditor.h @@ -33,7 +33,10 @@ #include "basetexteditor.h" #include "normalindenter.h" +#include <utils/uncommentselection.h> + #include <QtCore/QList> +#include <QtCore/QScopedPointer> namespace TextEditor { @@ -61,13 +64,20 @@ class TEXTEDITOR_EXPORT PlainTextEditor : public BaseTextEditor public: PlainTextEditor(QWidget *parent); +public slots: + virtual void unCommentSelection(); + virtual void setFontSettings(const TextEditor::FontSettings &); + +private slots: + void configure(); + protected: virtual BaseTextEditorEditable *createEditableInterface() { return new PlainTextEditorEditable(this); } virtual void indentBlock(QTextDocument *doc, QTextBlock block, QChar typedChar); private: - // Indent a text block based on previous line. - NormalIndenter m_indenter; + Utils::CommentDefinition m_commentDefinition; + QScopedPointer<TextEditor::Indenter> m_indenter; }; } // namespace TextEditor diff --git a/src/plugins/texteditor/plaintexteditorfactory.cpp b/src/plugins/texteditor/plaintexteditorfactory.cpp index 3a960bc180..7a21997395 100644 --- a/src/plugins/texteditor/plaintexteditorfactory.cpp +++ b/src/plugins/texteditor/plaintexteditorfactory.cpp @@ -44,7 +44,8 @@ PlainTextEditorFactory::PlainTextEditorFactory(QObject *parent) { m_actionHandler = new TextEditorActionHandler( QLatin1String(TextEditor::Constants::C_TEXTEDITOR), - TextEditorActionHandler::Format); + TextEditorActionHandler::Format | + TextEditorActionHandler::UnCommentSelection); m_mimeTypes << QLatin1String(TextEditor::Constants::C_TEXTEDITOR_MIMETYPE_TEXT); } @@ -76,6 +77,11 @@ Core::IEditor *PlainTextEditorFactory::createEditor(QWidget *parent) return rc->editableInterface(); } +void PlainTextEditorFactory::addMimeType(const QString &type) +{ + m_mimeTypes.append(type); +} + QStringList PlainTextEditorFactory::mimeTypes() const { return m_mimeTypes; diff --git a/src/plugins/texteditor/plaintexteditorfactory.h b/src/plugins/texteditor/plaintexteditorfactory.h index d55f4808af..a01a80f7f1 100644 --- a/src/plugins/texteditor/plaintexteditorfactory.h +++ b/src/plugins/texteditor/plaintexteditorfactory.h @@ -50,6 +50,7 @@ public: PlainTextEditorFactory(QObject *parent = 0); virtual ~PlainTextEditorFactory(); + void addMimeType(const QString &type); virtual QStringList mimeTypes() const; //Core::IEditorFactory QString id() const; diff --git a/src/plugins/texteditor/texteditor.pro b/src/plugins/texteditor/texteditor.pro index bf64eb51c1..4892ff2992 100644 --- a/src/plugins/texteditor/texteditor.pro +++ b/src/plugins/texteditor/texteditor.pro @@ -1,8 +1,11 @@ TEMPLATE = lib TARGET = TextEditor DEFINES += TEXTEDITOR_LIBRARY +QT += xml include(../../qtcreatorplugin.pri) include(texteditor_dependencies.pri) +INCLUDEPATH += generichighlighter +DEPENDPATH += generichighlighter SOURCES += texteditorplugin.cpp \ textfilewizard.cpp \ plaintexteditor.cpp \ @@ -37,7 +40,19 @@ SOURCES += texteditorplugin.cpp \ basetextdocumentlayout.cpp \ completionsettings.cpp \ normalindenter.cpp \ - indenter.cpp + indenter.cpp \ + generichighlighter/itemdata.cpp \ + generichighlighter/specificrules.cpp \ + generichighlighter/rule.cpp \ + generichighlighter/dynamicrule.cpp \ + generichighlighter/context.cpp \ + generichighlighter/includerulesinstruction.cpp \ + generichighlighter/progressdata.cpp \ + generichighlighter/keywordlist.cpp \ + generichighlighter/highlightdefinition.cpp \ + generichighlighter/highlighter.cpp \ + generichighlighter/manager.cpp \ + generichighlighter/highlightdefinitionhandler.cpp HEADERS += texteditorplugin.h \ textfilewizard.h \ @@ -77,7 +92,22 @@ HEADERS += texteditorplugin.h \ basetextdocumentlayout.h \ completionsettings.h \ normalindenter.h \ - indenter.h + indenter.h \ + generichighlighter/reuse.h \ + generichighlighter/itemdata.h \ + generichighlighter/specificrules.h \ + generichighlighter/rule.h \ + generichighlighter/reuse.h \ + generichighlighter/dynamicrule.h \ + generichighlighter/context.h \ + generichighlighter/includerulesinstruction.h \ + generichighlighter/progressdata.h \ + generichighlighter/keywordlist.h \ + generichighlighter/highlighterexception.h \ + generichighlighter/highlightdefinition.h \ + generichighlighter/highlighter.h \ + generichighlighter/manager.h \ + generichighlighter/highlightdefinitionhandler.h FORMS += behaviorsettingspage.ui \ diff --git a/src/plugins/texteditor/texteditorplugin.cpp b/src/plugins/texteditor/texteditorplugin.cpp index dca81bed5a..058fcd6350 100644 --- a/src/plugins/texteditor/texteditorplugin.cpp +++ b/src/plugins/texteditor/texteditorplugin.cpp @@ -39,6 +39,7 @@ #include "plaintexteditorfactory.h" #include "plaintexteditor.h" #include "storagesettings.h" +#include "manager.h" #include <coreplugin/icore.h> #include <coreplugin/coreconstants.h> @@ -70,6 +71,9 @@ TextEditorPlugin::TextEditorPlugin() { QTC_ASSERT(!m_instance, return); m_instance = this; + + connect(Core::ICore::instance(), SIGNAL(coreOpened()), + Manager::instance(), SLOT(registerMimeTypes())); } TextEditorPlugin::~TextEditorPlugin() diff --git a/src/plugins/texteditor/texteditorplugin.h b/src/plugins/texteditor/texteditorplugin.h index 1fc4c10be7..93f8870e1f 100644 --- a/src/plugins/texteditor/texteditorplugin.h +++ b/src/plugins/texteditor/texteditorplugin.h @@ -64,6 +64,7 @@ public: void initializeEditor(PlainTextEditor *editor); + PlainTextEditorFactory *editorFactory() { return m_editorFactory; } LineNumberFilter *lineNumberFilter() { return m_lineNumberFilter; } private slots: |