diff options
author | Leandro Melo <leandro.melo@nokia.com> | 2010-09-24 20:16:34 +0200 |
---|---|---|
committer | Leandro Melo <leandro.melo@nokia.com> | 2010-09-24 20:22:03 +0200 |
commit | cafcce0c3738264d48795f60b85472c06d587079 (patch) | |
tree | a7e1a60562a6e235a7043d66732f9d3472b08038 | |
parent | f64cf427f2a0cd37789b9feb782cd1b4e26563ec (diff) |
Add support for C++ snippets.
-rw-r--r-- | share/qtcreator/snippets/cpp.xml | 9 | ||||
-rw-r--r-- | src/plugins/cpptools/cppcodecompletion.cpp | 30 | ||||
-rw-r--r-- | src/plugins/cpptools/cppcodecompletion.h | 4 | ||||
-rw-r--r-- | src/plugins/qmljseditor/qmljscodecompletion.cpp | 104 | ||||
-rw-r--r-- | src/plugins/qmljseditor/qmljscodecompletion.h | 8 | ||||
-rw-r--r-- | src/plugins/texteditor/icompletioncollector.h | 4 | ||||
-rw-r--r-- | src/plugins/texteditor/images/snippet.png | bin | 0 -> 367 bytes | |||
-rw-r--r-- | src/plugins/texteditor/snippetsparser.cpp | 139 | ||||
-rw-r--r-- | src/plugins/texteditor/snippetsparser.h | 60 | ||||
-rw-r--r-- | src/plugins/texteditor/texteditor.pro | 6 | ||||
-rw-r--r-- | src/plugins/texteditor/texteditor.qrc | 1 |
11 files changed, 247 insertions, 118 deletions
diff --git a/share/qtcreator/snippets/cpp.xml b/share/qtcreator/snippets/cpp.xml new file mode 100644 index 0000000000..6a8037be30 --- /dev/null +++ b/share/qtcreator/snippets/cpp.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<snippets> +<snippet>for (int <tab>var</tab> = 0; <tab>var</tab> < <tab>total</tab>; ++<tab>var</tab>) { +} +</snippet> +<snippet>for (<tab>var</tab>; <tab>var</tab> < <tab>total</tab>; ++<tab>var</tab>) { +} +</snippet> +</snippets> diff --git a/src/plugins/cpptools/cppcodecompletion.cpp b/src/plugins/cpptools/cppcodecompletion.cpp index 32a51d7f2c..4426cb64a7 100644 --- a/src/plugins/cpptools/cppcodecompletion.cpp +++ b/src/plugins/cpptools/cppcodecompletion.cpp @@ -75,6 +75,7 @@ #include <QtGui/QTextDocument> // Qt::escape() #include <QtGui/QToolButton> #include <QtGui/QVBoxLayout> +#include <QtAlgorithms> namespace { const bool debug = ! qgetenv("CPLUSPLUS_DEBUG").isEmpty(); @@ -460,7 +461,8 @@ CppCodeCompletion::CppCodeCompletion(CppModelManager *manager) m_shouldRestartCompletion(false), m_automaticCompletion(false), m_completionOperator(T_EOF_SYMBOL), - m_objcEnabled(true) + m_objcEnabled(true), + m_snippetsParser(Core::ICore::instance()->resourcePath() + QLatin1String("/snippets/cpp.xml")) { } @@ -1123,7 +1125,10 @@ void CppCodeCompletion::globalCompletion(Scope *currentScope) foreach (ClassOrNamespace *b, usingBindings) completeNamespace(b); + addSnippets(); addKeywords(); + qStableSort(m_completions.begin(), m_completions.end(), completionItemLessThan); + addMacros(QLatin1String("<configuration>"), context.snapshot()); addMacros(context.thisDocument()->fileName(), context.snapshot()); } @@ -1759,18 +1764,19 @@ void CppCodeCompletion::completions(QList<TextEditor::CompletionItem> *completio QList<TextEditor::CompletionItem> CppCodeCompletion::removeDuplicates(const QList<TextEditor::CompletionItem> &items) { - // Remove duplicates + // Duplicates are kept only if they are snippets. QList<TextEditor::CompletionItem> uniquelist; QSet<QString> processed; - foreach (const TextEditor::CompletionItem &item, items) { - if (! processed.contains(item.text)) { - processed.insert(item.text); + if (!processed.contains(item.text) || item.isSnippet) { uniquelist.append(item); - if (Symbol *symbol = qvariant_cast<Symbol *>(item.data)) { - if (Function *funTy = symbol->type()->asFunctionType()) { - if (funTy->hasArguments()) - ++uniquelist.back().duplicateCount; + if (!item.isSnippet) { + processed.insert(item.text); + if (Symbol *symbol = qvariant_cast<Symbol *>(item.data)) { + if (Function *funTy = symbol->type()->asFunctionType()) { + if (funTy->hasArguments()) + ++uniquelist.back().duplicateCount; + } } } } @@ -2018,4 +2024,10 @@ bool CppCodeCompletion::objcKeywordsWanted() const return mdb->findByFile(fileName).type() == CppTools::Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE; } +void CppCodeCompletion::addSnippets() +{ + static const QIcon icon(QLatin1String(":/texteditor/images/snippet.png")); + m_completions.append(m_snippetsParser.execute(this, icon)); +} + #include "cppcodecompletion.moc" diff --git a/src/plugins/cpptools/cppcodecompletion.h b/src/plugins/cpptools/cppcodecompletion.h index 89753ce72e..49317eb096 100644 --- a/src/plugins/cpptools/cppcodecompletion.h +++ b/src/plugins/cpptools/cppcodecompletion.h @@ -37,6 +37,7 @@ #include <cplusplus/TypeOfExpression.h> #include <texteditor/icompletioncollector.h> +#include <texteditor/snippetsparser.h> #include <QtCore/QObject> #include <QtCore/QPointer> @@ -87,6 +88,7 @@ public: QIcon iconForSymbol(CPlusPlus::Symbol *symbol) const; private: + void addSnippets(); void addKeywords(); void addMacros(const QString &fileName, const CPlusPlus::Snapshot &snapshot); void addMacros_helper(const CPlusPlus::Snapshot &snapshot, @@ -151,6 +153,8 @@ private: unsigned m_completionOperator; bool m_objcEnabled; + TextEditor::SnippetsParser m_snippetsParser; + CPlusPlus::Icons m_icons; CPlusPlus::Overview overview; CPlusPlus::TypeOfExpression typeOfExpression; diff --git a/src/plugins/qmljseditor/qmljscodecompletion.cpp b/src/plugins/qmljseditor/qmljscodecompletion.cpp index 4219b3b302..cde1a659e6 100644 --- a/src/plugins/qmljseditor/qmljscodecompletion.cpp +++ b/src/plugins/qmljseditor/qmljscodecompletion.cpp @@ -50,7 +50,6 @@ #include <QtCore/QFile> #include <QtCore/QFileInfo> #include <QtCore/QDir> -#include <QtCore/QXmlStreamReader> #include <QtCore/QDebug> #include <QtGui/QApplication> @@ -485,7 +484,8 @@ CodeCompletion::CodeCompletion(ModelManagerInterface *modelManager, QObject *par m_modelManager(modelManager), m_editor(0), m_startPosition(0), - m_restartCompletion(false) + m_restartCompletion(false), + m_snippetsParser(Core::ICore::instance()->resourcePath() + QLatin1String("/snippets/qml.xml")) { Q_ASSERT(modelManager); } @@ -860,8 +860,7 @@ int CodeCompletion::startCompletion(TextEditor::ITextEditable *editor) } if (isQmlFile && (completionOperator.isNull() || completionOperator.isSpace() || isDelimiter(completionOperator))) { - updateSnippets(); - m_completions.append(m_snippets); + m_completions.append(m_snippetsParser.execute(this, iconForColor(Qt::red), SnippetOrder)); } if (! m_completions.isEmpty()) @@ -960,103 +959,6 @@ void CodeCompletion::cleanup() m_completions.clear(); } - -void CodeCompletion::updateSnippets() -{ - QString qmlsnippets = Core::ICore::instance()->resourcePath() + QLatin1String("/snippets/qml.xml"); - if (!QFile::exists(qmlsnippets)) - return; - - QDateTime lastModified = QFileInfo(qmlsnippets).lastModified(); - if (!m_snippetFileLastModified.isNull() && lastModified == m_snippetFileLastModified) - return; - - const QIcon icon = iconForColor(Qt::red); - - m_snippetFileLastModified = lastModified; - QFile file(qmlsnippets); - file.open(QIODevice::ReadOnly); - QXmlStreamReader xml(&file); - if (xml.readNextStartElement()) { - if (xml.name() == QLatin1String("snippets")) { - while (xml.readNextStartElement()) { - if (xml.name() == QLatin1String("snippet")) { - TextEditor::CompletionItem item(this); - QString title, data; - QString description = xml.attributes().value("description").toString(); - - while (!xml.atEnd()) { - xml.readNext(); - if (xml.isEndElement()) { - int i = 0; - while (i < data.size() && data.at(i).isLetterOrNumber()) - ++i; - title = data.left(i); - item.text = title; - if (!description.isEmpty()) { - item.text += QLatin1Char(' '); - item.text += description; - } - item.data = QVariant::fromValue(data); - - - QString infotip = data; - while (infotip.size() && infotip.at(infotip.size()-1).isSpace()) - infotip.chop(1); - infotip.replace(QLatin1Char('\n'), QLatin1String("<br>")); - infotip.replace(QLatin1Char(' '), QLatin1String(" ")); - { - QString s = QLatin1String("<nobr>"); - int count = 0; - for (int i = 0; i < infotip.count(); ++i) { - if (infotip.at(i) != QChar::ObjectReplacementCharacter) { - s += infotip.at(i); - continue; - } - if (++count % 2) { - s += QLatin1String("<b>"); - } else { - if (infotip.at(i-1) == QChar::ObjectReplacementCharacter) - s += QLatin1String("..."); - s += QLatin1String("</b>"); - } - } - infotip = s; - } - - item.details = infotip; - - item.icon = icon; - item.order = SnippetOrder; - m_snippets.append(item); - break; - } - - if (xml.isCharacters()) - data += xml.text(); - else if (xml.isStartElement()) { - if (xml.name() != QLatin1String("tab")) - xml.raiseError(QLatin1String("invalid snippets file")); - else { - data += QChar::ObjectReplacementCharacter; - data += xml.readElementText(); - data += QChar::ObjectReplacementCharacter; - } - } - } - } else { - xml.skipCurrentElement(); - } - } - } else { - xml.skipCurrentElement(); - } - } - if (xml.hasError()) - qWarning() << qmlsnippets << xml.errorString() << xml.lineNumber() << xml.columnNumber(); - file.close(); -} - static bool qmlCompletionItemLessThan(const TextEditor::CompletionItem &l, const TextEditor::CompletionItem &r) { if (l.order != r.order) diff --git a/src/plugins/qmljseditor/qmljscodecompletion.h b/src/plugins/qmljseditor/qmljscodecompletion.h index a2e2284c19..586d30e498 100644 --- a/src/plugins/qmljseditor/qmljscodecompletion.h +++ b/src/plugins/qmljseditor/qmljscodecompletion.h @@ -32,6 +32,7 @@ #include <qmljs/qmljsdocument.h> #include <texteditor/icompletioncollector.h> +#include <texteditor/snippetsparser.h> #include <QtCore/QDateTime> #include <QtCore/QPointer> @@ -76,7 +77,6 @@ public: virtual void cleanup(); private: - void updateSnippets(); bool maybeTriggersCompletion(TextEditor::ITextEditable *editor); bool isDelimiter(QChar ch) const; @@ -92,12 +92,10 @@ private: QmlJS::ModelManagerInterface *m_modelManager; TextEditor::ITextEditable *m_editor; int m_startPosition; + bool m_restartCompletion; + TextEditor::SnippetsParser m_snippetsParser; QList<TextEditor::CompletionItem> m_completions; - - QList<TextEditor::CompletionItem> m_snippets; - QDateTime m_snippetFileLastModified; QPointer<FunctionArgumentWidget> m_functionArgumentWidget; - bool m_restartCompletion; }; diff --git a/src/plugins/texteditor/icompletioncollector.h b/src/plugins/texteditor/icompletioncollector.h index 5ec3574326..5f24893785 100644 --- a/src/plugins/texteditor/icompletioncollector.h +++ b/src/plugins/texteditor/icompletioncollector.h @@ -54,7 +54,8 @@ public: duplicateCount(0), order(0), originalIndex(0), - collector(collector) + collector(collector), + isSnippet(false) { } bool isValid() const @@ -69,6 +70,7 @@ public: int order; int originalIndex; ICompletionCollector *collector; + bool isSnippet; }; /* Defines the interface to completion collectors. A completion collector tells diff --git a/src/plugins/texteditor/images/snippet.png b/src/plugins/texteditor/images/snippet.png Binary files differnew file mode 100644 index 0000000000..929ac6b922 --- /dev/null +++ b/src/plugins/texteditor/images/snippet.png diff --git a/src/plugins/texteditor/snippetsparser.cpp b/src/plugins/texteditor/snippetsparser.cpp new file mode 100644 index 0000000000..63ddc93aee --- /dev/null +++ b/src/plugins/texteditor/snippetsparser.cpp @@ -0,0 +1,139 @@ +/************************************************************************** +** +** 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 "snippetsparser.h" + +#include <QtCore/QFile> +#include <QtCore/QFileInfo> +#include <QtCore/QLatin1String> +#include <QtCore/QLatin1Char> +#include <QtCore/QVariant> +#include <QtCore/QXmlStreamReader> +#include <QtCore/QDebug> + +using namespace TextEditor; + +SnippetsParser::SnippetsParser(const QString &fileName) : m_fileName(fileName) +{} + +const QList<CompletionItem> &SnippetsParser::execute(ICompletionCollector *collector, + const QIcon &icon, + int order) +{ + const QDateTime &lastModified = QFileInfo(m_fileName).lastModified(); + if (m_lastTrackedFileChange.isNull() || m_lastTrackedFileChange != lastModified) { + m_snippets.clear(); + + QFile file(m_fileName); + file.open(QIODevice::ReadOnly); + QXmlStreamReader xml(&file); + if (xml.readNextStartElement()) { + if (xml.name() == QLatin1String("snippets")) { + while (xml.readNextStartElement()) { + if (xml.name() == QLatin1String("snippet")) { + TextEditor::CompletionItem item(collector); + QString title; + QString data; + QString description = xml.attributes().value("description").toString(); + + while (!xml.atEnd()) { + xml.readNext(); + if (xml.isEndElement()) { + int i = 0; + while (i < data.size() && data.at(i).isLetterOrNumber()) + ++i; + title = data.left(i); + item.text = title; + if (!description.isEmpty()) { + item.text += QLatin1Char(' '); + item.text += description; + } + item.data = QVariant::fromValue(data); + + QString infotip = data; + while (infotip.size() && infotip.at(infotip.size()-1).isSpace()) + infotip.chop(1); + infotip.replace(QLatin1Char('\n'), QLatin1String("<br>")); + infotip.replace(QLatin1Char(' '), QLatin1String(" ")); + { + QString s = QLatin1String("<nobr>"); + int count = 0; + for (int i = 0; i < infotip.count(); ++i) { + if (infotip.at(i) != QChar::ObjectReplacementCharacter) { + s += infotip.at(i); + continue; + } + if (++count % 2) { + s += QLatin1String("<b>"); + } else { + if (infotip.at(i-1) == QChar::ObjectReplacementCharacter) + s += QLatin1String("..."); + s += QLatin1String("</b>"); + } + } + infotip = s; + } + item.details = infotip; + + item.icon = icon; + item.order = order; + item.isSnippet = true; + m_snippets.append(item); + break; + } + + if (xml.isCharacters()) + data += xml.text(); + else if (xml.isStartElement()) { + if (xml.name() != QLatin1String("tab")) + xml.raiseError(QLatin1String("invalid snippets file")); + else { + data += QChar::ObjectReplacementCharacter; + data += xml.readElementText(); + data += QChar::ObjectReplacementCharacter; + } + } + } + } else { + xml.skipCurrentElement(); + } + } + } else { + xml.skipCurrentElement(); + } + } + if (xml.hasError()) + qWarning() << m_fileName << xml.errorString() << xml.lineNumber() << xml.columnNumber(); + file.close(); + + m_lastTrackedFileChange = lastModified; + } + + return m_snippets; +} diff --git a/src/plugins/texteditor/snippetsparser.h b/src/plugins/texteditor/snippetsparser.h new file mode 100644 index 0000000000..90cb6c18cf --- /dev/null +++ b/src/plugins/texteditor/snippetsparser.h @@ -0,0 +1,60 @@ +/************************************************************************** +** +** 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 SNIPPETSPARSER_H +#define SNIPPETSPARSER_H + +#include "texteditor_global.h" +#include "icompletioncollector.h" + +#include <QtCore/QString> +#include <QtCore/QList> +#include <QtCore/QDateTime> +#include <QtGui/QIcon> + +namespace TextEditor { + +class TEXTEDITOR_EXPORT SnippetsParser +{ +public: + SnippetsParser(const QString &fileName); + + const QList<CompletionItem> &execute(ICompletionCollector *collector, + const QIcon &icon, + int order = 0); + +private: + QString m_fileName; + QDateTime m_lastTrackedFileChange; + QList<CompletionItem> m_snippets; +}; + +} // namespace TextEditor + +#endif // SNIPPETSPARSER_H diff --git a/src/plugins/texteditor/texteditor.pro b/src/plugins/texteditor/texteditor.pro index e917e279e7..21a4ba9952 100644 --- a/src/plugins/texteditor/texteditor.pro +++ b/src/plugins/texteditor/texteditor.pro @@ -69,7 +69,8 @@ SOURCES += texteditorplugin.cpp \ tooltip/tipcontents.cpp \ tooltip/tipfactory.cpp \ basehoverhandler.cpp \ - helpitem.cpp + helpitem.cpp \ + snippetsparser.cpp HEADERS += texteditorplugin.h \ textfilewizard.h \ @@ -142,7 +143,8 @@ HEADERS += texteditorplugin.h \ tooltip/effects.h \ tooltip/tipfactory.h \ basehoverhandler.h \ - helpitem.h + helpitem.h \ + snippetsparser.h FORMS += behaviorsettingspage.ui \ displaysettingspage.ui \ diff --git a/src/plugins/texteditor/texteditor.qrc b/src/plugins/texteditor/texteditor.qrc index 0d131c4f3b..86fa6638cb 100644 --- a/src/plugins/texteditor/texteditor.qrc +++ b/src/plugins/texteditor/texteditor.qrc @@ -4,5 +4,6 @@ <file>images/finddirectory.png</file> <file>TextEditor.mimetypes.xml</file> <file>images/refactormarker.png</file> + <file>images/snippet.png</file> </qresource> </RCC> |