diff --git a/share/qtcreator/snippets/cpp.xml b/share/qtcreator/snippets/cpp.xml new file mode 100644 index 00000000000..6a8037be306 --- /dev/null +++ b/share/qtcreator/snippets/cpp.xml @@ -0,0 +1,9 @@ + + +for (int var = 0; var < total; ++var) { +} + +for (var; var < total; ++var) { +} + + diff --git a/src/plugins/cpptools/cppcodecompletion.cpp b/src/plugins/cpptools/cppcodecompletion.cpp index 32a51d7f2cc..4426cb64a72 100644 --- a/src/plugins/cpptools/cppcodecompletion.cpp +++ b/src/plugins/cpptools/cppcodecompletion.cpp @@ -75,6 +75,7 @@ #include // Qt::escape() #include #include +#include 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(""), context.snapshot()); addMacros(context.thisDocument()->fileName(), context.snapshot()); } @@ -1759,18 +1764,19 @@ void CppCodeCompletion::completions(QList *completio QList CppCodeCompletion::removeDuplicates(const QList &items) { - // Remove duplicates + // Duplicates are kept only if they are snippets. QList uniquelist; QSet 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(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(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 89753ce72ee..49317eb096e 100644 --- a/src/plugins/cpptools/cppcodecompletion.h +++ b/src/plugins/cpptools/cppcodecompletion.h @@ -37,6 +37,7 @@ #include #include +#include #include #include @@ -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 4219b3b302c..cde1a659e6f 100644 --- a/src/plugins/qmljseditor/qmljscodecompletion.cpp +++ b/src/plugins/qmljseditor/qmljscodecompletion.cpp @@ -50,7 +50,6 @@ #include #include #include -#include #include #include @@ -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("
")); - infotip.replace(QLatin1Char(' '), QLatin1String(" ")); - { - QString s = QLatin1String(""); - 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(""); - } else { - if (infotip.at(i-1) == QChar::ObjectReplacementCharacter) - s += QLatin1String("..."); - s += QLatin1String(""); - } - } - 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 a2e2284c192..586d30e498b 100644 --- a/src/plugins/qmljseditor/qmljscodecompletion.h +++ b/src/plugins/qmljseditor/qmljscodecompletion.h @@ -32,6 +32,7 @@ #include #include +#include #include #include @@ -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; - QList m_completions; - - QList m_snippets; - QDateTime m_snippetFileLastModified; - QPointer m_functionArgumentWidget; bool m_restartCompletion; + TextEditor::SnippetsParser m_snippetsParser; + QList m_completions; + QPointer m_functionArgumentWidget; }; diff --git a/src/plugins/texteditor/icompletioncollector.h b/src/plugins/texteditor/icompletioncollector.h index 5ec3574326e..5f248937852 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 new file mode 100644 index 00000000000..929ac6b9229 Binary files /dev/null and b/src/plugins/texteditor/images/snippet.png differ diff --git a/src/plugins/texteditor/snippetsparser.cpp b/src/plugins/texteditor/snippetsparser.cpp new file mode 100644 index 00000000000..63ddc93aeee --- /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 +#include +#include +#include +#include +#include +#include + +using namespace TextEditor; + +SnippetsParser::SnippetsParser(const QString &fileName) : m_fileName(fileName) +{} + +const QList &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("
")); + infotip.replace(QLatin1Char(' '), QLatin1String(" ")); + { + QString s = QLatin1String("
"); + 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(""); + } else { + if (infotip.at(i-1) == QChar::ObjectReplacementCharacter) + s += QLatin1String("..."); + s += QLatin1String(""); + } + } + 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 00000000000..90cb6c18cf2 --- /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 +#include +#include +#include + +namespace TextEditor { + +class TEXTEDITOR_EXPORT SnippetsParser +{ +public: + SnippetsParser(const QString &fileName); + + const QList &execute(ICompletionCollector *collector, + const QIcon &icon, + int order = 0); + +private: + QString m_fileName; + QDateTime m_lastTrackedFileChange; + QList m_snippets; +}; + +} // namespace TextEditor + +#endif // SNIPPETSPARSER_H diff --git a/src/plugins/texteditor/texteditor.pro b/src/plugins/texteditor/texteditor.pro index e917e279e71..21a4ba99528 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 0d131c4f3b1..86fa6638cbc 100644 --- a/src/plugins/texteditor/texteditor.qrc +++ b/src/plugins/texteditor/texteditor.qrc @@ -4,5 +4,6 @@ images/finddirectory.png TextEditor.mimetypes.xml images/refactormarker.png + images/snippet.png