Add support for C++ snippets.

This commit is contained in:
Leandro Melo
2010-09-24 20:16:34 +02:00
parent f64cf427f2
commit cafcce0c37
11 changed files with 248 additions and 119 deletions

View File

@@ -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>

View File

@@ -75,6 +75,7 @@
#include <QtGui/QTextDocument> // Qt::escape() #include <QtGui/QTextDocument> // Qt::escape()
#include <QtGui/QToolButton> #include <QtGui/QToolButton>
#include <QtGui/QVBoxLayout> #include <QtGui/QVBoxLayout>
#include <QtAlgorithms>
namespace { namespace {
const bool debug = ! qgetenv("CPLUSPLUS_DEBUG").isEmpty(); const bool debug = ! qgetenv("CPLUSPLUS_DEBUG").isEmpty();
@@ -460,7 +461,8 @@ CppCodeCompletion::CppCodeCompletion(CppModelManager *manager)
m_shouldRestartCompletion(false), m_shouldRestartCompletion(false),
m_automaticCompletion(false), m_automaticCompletion(false),
m_completionOperator(T_EOF_SYMBOL), 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) foreach (ClassOrNamespace *b, usingBindings)
completeNamespace(b); completeNamespace(b);
addSnippets();
addKeywords(); addKeywords();
qStableSort(m_completions.begin(), m_completions.end(), completionItemLessThan);
addMacros(QLatin1String("<configuration>"), context.snapshot()); addMacros(QLatin1String("<configuration>"), context.snapshot());
addMacros(context.thisDocument()->fileName(), context.snapshot()); addMacros(context.thisDocument()->fileName(), context.snapshot());
} }
@@ -1759,14 +1764,14 @@ void CppCodeCompletion::completions(QList<TextEditor::CompletionItem> *completio
QList<TextEditor::CompletionItem> CppCodeCompletion::removeDuplicates(const QList<TextEditor::CompletionItem> &items) QList<TextEditor::CompletionItem> CppCodeCompletion::removeDuplicates(const QList<TextEditor::CompletionItem> &items)
{ {
// Remove duplicates // Duplicates are kept only if they are snippets.
QList<TextEditor::CompletionItem> uniquelist; QList<TextEditor::CompletionItem> uniquelist;
QSet<QString> processed; QSet<QString> processed;
foreach (const TextEditor::CompletionItem &item, items) { foreach (const TextEditor::CompletionItem &item, items) {
if (! processed.contains(item.text)) { if (!processed.contains(item.text) || item.isSnippet) {
processed.insert(item.text);
uniquelist.append(item); uniquelist.append(item);
if (!item.isSnippet) {
processed.insert(item.text);
if (Symbol *symbol = qvariant_cast<Symbol *>(item.data)) { if (Symbol *symbol = qvariant_cast<Symbol *>(item.data)) {
if (Function *funTy = symbol->type()->asFunctionType()) { if (Function *funTy = symbol->type()->asFunctionType()) {
if (funTy->hasArguments()) if (funTy->hasArguments())
@@ -1775,6 +1780,7 @@ QList<TextEditor::CompletionItem> CppCodeCompletion::removeDuplicates(const QLis
} }
} }
} }
}
return uniquelist; return uniquelist;
} }
@@ -2018,4 +2024,10 @@ bool CppCodeCompletion::objcKeywordsWanted() const
return mdb->findByFile(fileName).type() == CppTools::Constants::OBJECTIVE_CPP_SOURCE_MIMETYPE; 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" #include "cppcodecompletion.moc"

View File

@@ -37,6 +37,7 @@
#include <cplusplus/TypeOfExpression.h> #include <cplusplus/TypeOfExpression.h>
#include <texteditor/icompletioncollector.h> #include <texteditor/icompletioncollector.h>
#include <texteditor/snippetsparser.h>
#include <QtCore/QObject> #include <QtCore/QObject>
#include <QtCore/QPointer> #include <QtCore/QPointer>
@@ -87,6 +88,7 @@ public:
QIcon iconForSymbol(CPlusPlus::Symbol *symbol) const; QIcon iconForSymbol(CPlusPlus::Symbol *symbol) const;
private: private:
void addSnippets();
void addKeywords(); void addKeywords();
void addMacros(const QString &fileName, const CPlusPlus::Snapshot &snapshot); void addMacros(const QString &fileName, const CPlusPlus::Snapshot &snapshot);
void addMacros_helper(const CPlusPlus::Snapshot &snapshot, void addMacros_helper(const CPlusPlus::Snapshot &snapshot,
@@ -151,6 +153,8 @@ private:
unsigned m_completionOperator; unsigned m_completionOperator;
bool m_objcEnabled; bool m_objcEnabled;
TextEditor::SnippetsParser m_snippetsParser;
CPlusPlus::Icons m_icons; CPlusPlus::Icons m_icons;
CPlusPlus::Overview overview; CPlusPlus::Overview overview;
CPlusPlus::TypeOfExpression typeOfExpression; CPlusPlus::TypeOfExpression typeOfExpression;

View File

@@ -50,7 +50,6 @@
#include <QtCore/QFile> #include <QtCore/QFile>
#include <QtCore/QFileInfo> #include <QtCore/QFileInfo>
#include <QtCore/QDir> #include <QtCore/QDir>
#include <QtCore/QXmlStreamReader>
#include <QtCore/QDebug> #include <QtCore/QDebug>
#include <QtGui/QApplication> #include <QtGui/QApplication>
@@ -485,7 +484,8 @@ CodeCompletion::CodeCompletion(ModelManagerInterface *modelManager, QObject *par
m_modelManager(modelManager), m_modelManager(modelManager),
m_editor(0), m_editor(0),
m_startPosition(0), m_startPosition(0),
m_restartCompletion(false) m_restartCompletion(false),
m_snippetsParser(Core::ICore::instance()->resourcePath() + QLatin1String("/snippets/qml.xml"))
{ {
Q_ASSERT(modelManager); Q_ASSERT(modelManager);
} }
@@ -860,8 +860,7 @@ int CodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
} }
if (isQmlFile && (completionOperator.isNull() || completionOperator.isSpace() || isDelimiter(completionOperator))) { if (isQmlFile && (completionOperator.isNull() || completionOperator.isSpace() || isDelimiter(completionOperator))) {
updateSnippets(); m_completions.append(m_snippetsParser.execute(this, iconForColor(Qt::red), SnippetOrder));
m_completions.append(m_snippets);
} }
if (! m_completions.isEmpty()) if (! m_completions.isEmpty())
@@ -960,103 +959,6 @@ void CodeCompletion::cleanup()
m_completions.clear(); 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) static bool qmlCompletionItemLessThan(const TextEditor::CompletionItem &l, const TextEditor::CompletionItem &r)
{ {
if (l.order != r.order) if (l.order != r.order)

View File

@@ -32,6 +32,7 @@
#include <qmljs/qmljsdocument.h> #include <qmljs/qmljsdocument.h>
#include <texteditor/icompletioncollector.h> #include <texteditor/icompletioncollector.h>
#include <texteditor/snippetsparser.h>
#include <QtCore/QDateTime> #include <QtCore/QDateTime>
#include <QtCore/QPointer> #include <QtCore/QPointer>
@@ -76,7 +77,6 @@ public:
virtual void cleanup(); virtual void cleanup();
private: private:
void updateSnippets();
bool maybeTriggersCompletion(TextEditor::ITextEditable *editor); bool maybeTriggersCompletion(TextEditor::ITextEditable *editor);
bool isDelimiter(QChar ch) const; bool isDelimiter(QChar ch) const;
@@ -92,12 +92,10 @@ private:
QmlJS::ModelManagerInterface *m_modelManager; QmlJS::ModelManagerInterface *m_modelManager;
TextEditor::ITextEditable *m_editor; TextEditor::ITextEditable *m_editor;
int m_startPosition; int m_startPosition;
QList<TextEditor::CompletionItem> m_completions;
QList<TextEditor::CompletionItem> m_snippets;
QDateTime m_snippetFileLastModified;
QPointer<FunctionArgumentWidget> m_functionArgumentWidget;
bool m_restartCompletion; bool m_restartCompletion;
TextEditor::SnippetsParser m_snippetsParser;
QList<TextEditor::CompletionItem> m_completions;
QPointer<FunctionArgumentWidget> m_functionArgumentWidget;
}; };

View File

@@ -54,7 +54,8 @@ public:
duplicateCount(0), duplicateCount(0),
order(0), order(0),
originalIndex(0), originalIndex(0),
collector(collector) collector(collector),
isSnippet(false)
{ } { }
bool isValid() const bool isValid() const
@@ -69,6 +70,7 @@ public:
int order; int order;
int originalIndex; int originalIndex;
ICompletionCollector *collector; ICompletionCollector *collector;
bool isSnippet;
}; };
/* Defines the interface to completion collectors. A completion collector tells /* Defines the interface to completion collectors. A completion collector tells

Binary file not shown.

After

Width:  |  Height:  |  Size: 367 B

View File

@@ -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;
}

View File

@@ -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

View File

@@ -69,7 +69,8 @@ SOURCES += texteditorplugin.cpp \
tooltip/tipcontents.cpp \ tooltip/tipcontents.cpp \
tooltip/tipfactory.cpp \ tooltip/tipfactory.cpp \
basehoverhandler.cpp \ basehoverhandler.cpp \
helpitem.cpp helpitem.cpp \
snippetsparser.cpp
HEADERS += texteditorplugin.h \ HEADERS += texteditorplugin.h \
textfilewizard.h \ textfilewizard.h \
@@ -142,7 +143,8 @@ HEADERS += texteditorplugin.h \
tooltip/effects.h \ tooltip/effects.h \
tooltip/tipfactory.h \ tooltip/tipfactory.h \
basehoverhandler.h \ basehoverhandler.h \
helpitem.h helpitem.h \
snippetsparser.h
FORMS += behaviorsettingspage.ui \ FORMS += behaviorsettingspage.ui \
displaysettingspage.ui \ displaysettingspage.ui \

View File

@@ -4,5 +4,6 @@
<file>images/finddirectory.png</file> <file>images/finddirectory.png</file>
<file>TextEditor.mimetypes.xml</file> <file>TextEditor.mimetypes.xml</file>
<file>images/refactormarker.png</file> <file>images/refactormarker.png</file>
<file>images/snippet.png</file>
</qresource> </qresource>
</RCC> </RCC>