OSDN Git Service

Add support for C++ snippets.
authorLeandro Melo <leandro.melo@nokia.com>
Fri, 24 Sep 2010 18:16:34 +0000 (20:16 +0200)
committerLeandro Melo <leandro.melo@nokia.com>
Fri, 24 Sep 2010 18:22:03 +0000 (20:22 +0200)
share/qtcreator/snippets/cpp.xml [new file with mode: 0644]
src/plugins/cpptools/cppcodecompletion.cpp
src/plugins/cpptools/cppcodecompletion.h
src/plugins/qmljseditor/qmljscodecompletion.cpp
src/plugins/qmljseditor/qmljscodecompletion.h
src/plugins/texteditor/icompletioncollector.h
src/plugins/texteditor/images/snippet.png [new file with mode: 0644]
src/plugins/texteditor/snippetsparser.cpp [new file with mode: 0644]
src/plugins/texteditor/snippetsparser.h [new file with mode: 0644]
src/plugins/texteditor/texteditor.pro
src/plugins/texteditor/texteditor.qrc

diff --git a/share/qtcreator/snippets/cpp.xml b/share/qtcreator/snippets/cpp.xml
new file mode 100644 (file)
index 0000000..6a8037b
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<snippets>
+<snippet>for (int <tab>var</tab> = 0; <tab>var</tab> &lt; <tab>total</tab>; ++<tab>var</tab>) {
+}
+</snippet>
+<snippet>for (<tab>var</tab>; <tab>var</tab> &lt; <tab>total</tab>; ++<tab>var</tab>) {
+}
+</snippet>
+</snippets>
index 32a51d7..4426cb6 100644 (file)
@@ -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"
index 89753ce..49317eb 100644 (file)
@@ -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;
index 4219b3b..cde1a65 100644 (file)
@@ -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("&nbsp;"));
-                            {
-                                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)
index a2e2284..586d30e 100644 (file)
@@ -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;
 };
 
 
index 5ec3574..5f24893 100644 (file)
@@ -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 (file)
index 0000000..929ac6b
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 (file)
index 0000000..63ddc93
--- /dev/null
@@ -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("&nbsp;"));
+                                {
+                                    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 (file)
index 0000000..90cb6c1
--- /dev/null
@@ -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
index e917e27..21a4ba9 100644 (file)
@@ -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 \
index 0d131c4..86fa663 100644 (file)
@@ -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>