From 5b8a6786720a839f8fade2dcf9d24d4e48d580a4 Mon Sep 17 00:00:00 2001 From: mae <qt-info@nokia.com> Date: Tue, 19 Jan 2010 18:50:50 +0100 Subject: [PATCH] first iteration of snippet support for qml Done with Thorbjorn and Roberto --- share/qtcreator/snippets/qml.xml | 96 ++++++++++++++ src/plugins/qmljseditor/qmlcodecompletion.cpp | 124 ++++++++++-------- src/plugins/qmljseditor/qmlcodecompletion.h | 5 + src/plugins/texteditor/basetexteditor.cpp | 53 ++++++-- src/plugins/texteditor/texteditoroverlay.h | 3 + 5 files changed, 214 insertions(+), 67 deletions(-) create mode 100644 share/qtcreator/snippets/qml.xml diff --git a/share/qtcreator/snippets/qml.xml b/share/qtcreator/snippets/qml.xml new file mode 100644 index 00000000000..4a2c3a5f2ea --- /dev/null +++ b/share/qtcreator/snippets/qml.xml @@ -0,0 +1,96 @@ +<?xml version="1.0" encoding="utf-8"?> +<snippets> +<snippet>property <tab>name</tab> <tab>name</tab> : <tab>name</tab> +</snippet> +<snippet>Item { + id: <tab>name</tab> + + } +</snippet> +<snippet>BorderImage { + id: <tab>name</tab> + width: <tab>name</tab>; height: <tab>name</tab> + border.left: <tab>name</tab>; border.top: <tab>name</tab> + border.right: <tab>name</tab>; border.bottom: <tab>name</tab> + source: "<tab>name</tab>" +} +</snippet> + +<snippet>Image { + id: <tab>name</tab> + source: "<tab>name</tab>" +} +</snippet> + +<snippet>Text { + id: <tab>name</tab> + text: "<tab>name</tab>" +} +</snippet> + +<snippet>states: [ + State { + name: "<tab>name</tab>" + PropertyChanges { + target: <tab>name</tab> + <tab/> + } + } +] +</snippet> + +<snippet>State { + name: "<tab>name</tab>" + PropertyChanges { + target: <tab>name</tab> + <tab/> + } +} +</snippet> + +<snippet>transitions: [ + Transition { + from: "<tab>name</tab>" + to: "<tab>name</tab>" + <tab/> + } +] +</snippet> + +<snippet>Transition { + from: "<tab>name</tab>" + to: "<tab>name</tab>" + <tab/> +} +</snippet> + +<snippet>PropertyChanges { + target: <tab>name</tab> + <tab/> + } +</snippet> + +<snippet description="matchTargets">NumberAnimation { matchTargets: "<tab>name</tab>"; matchProperties: "<tab>name</tab>"; duration: <tab>200</tab> } +</snippet> +<snippet description="target">NumberAnimation { target: "<tab>name</tab>"; property: "<tab>name</tab>"; value: <tab>name</tab>; duration: <tab>200</tab> } +</snippet> +<snippet>PropertyAction { matchTargets: "<tab>name</tab>"; matchProperties: "<tab>name</tab>"; duration: <tab>200</tab> } +</snippet> +<snippet>PropertyAction { target: "<tab>name</tab>"; property: "<tab>name</tab>"; value: <tab>name</tab>; duration: <tab>200</tab> } +</snippet> +<snippet>PauseAnimation { duration: <tab>name</tab>} +</snippet> +<snippet>ColorAnimation { from: <tab>name</tab>; to: <tab>name</tab>; duration: <tab>200</tab> } +</snippet> +<snippet>effect: Colorize { color: "<tab>name</tab>" } +</snippet> +<snippet>effect: Blur { blurRadius: "<tab>200</tab>" } +</snippet> +<snippet>effect: DropShadow { + blurRadius: <tab>200</tab> + offset.x: <tab>200</tab> + offset.y: <tab>200</tab> + } +</snippet> + +</snippets> diff --git a/src/plugins/qmljseditor/qmlcodecompletion.cpp b/src/plugins/qmljseditor/qmlcodecompletion.cpp index af7391854ba..56376c13c0e 100644 --- a/src/plugins/qmljseditor/qmlcodecompletion.cpp +++ b/src/plugins/qmljseditor/qmlcodecompletion.cpp @@ -37,6 +37,12 @@ #include <qmljs/qmljssymbol.h> #include <texteditor/basetexteditor.h> +#include <coreplugin/icore.h> + +#include <QtCore/QFile> +#include <QtCore/QFileInfo> +#include <QtCore/QXmlStreamReader> + #include <QtDebug> using namespace QmlJSEditor; @@ -134,61 +140,9 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor) } } + updateSnippets(); - // snippets completion - TextEditor::CompletionItem item(this); - item.text = QLatin1String("Rectangle - declaration"); - item.data = QVariant::fromValue(QString("Rectangle {\nwidth: $100$;\nheight: 100;\n$$\n}")); - m_completions.append(item); - - item.text = QLatin1String("Item - declaration"); - item.data = QVariant::fromValue(QString("Item {\nwidth: $100$;\nheight: 100;\n$$\n}")); - m_completions.append(item); - - item.text = QLatin1String("BorderImage - declaration"); - item.data = QLatin1String("BorderImage {\n" - "id: $name$;\n" - "width: $100$; height: $100$;\n" - "border.left: $2$; border.top: $2$;\n" - "border.right: $2$; border.bottom: $2$;\n" - "source: \"$name$\";\n" - "}\n"); - m_completions.append(item); - - item.text = QLatin1String("State - declaration"); - item.data = QLatin1String("State {\n" - "name: \"$state$;\"\n" - "PropertyChanges {\n" - "target: $target$;\n" - "$$\n" - "}\n" - "}\n"); - m_completions.append(item); - - item.text = QLatin1String("states - declaration"); - item.data = QLatin1String("states: [\n" - "State {\n" - "name: \"$state$\";\n" - "PropertyChanges {\n" - "target: $target$;\n" - "$$\n" - "}\n" - "}\n" - "]\n"); - m_completions.append(item); - - item.text = QLatin1String("property - declaration"); - item.data = QLatin1String("property $var$ $name$: $value$;\n"); - m_completions.append(item); - - item.text = QLatin1String("readonly property - declaration"); - item.data = QLatin1String("readonly property $var$ $name$: $value$;\n"); - m_completions.append(item); - - item.text = QLatin1String("NumericAnimation - declaration"); - item.data = QLatin1String("NumberAnimation { matchTargets: \"$target$\"; matchProperties: \"$properties$\"; duration: $1000$ }\n"); - m_completions.append(item); - + m_completions.append(m_snippets); return pos; } @@ -299,3 +253,65 @@ void QmlCodeCompletion::cleanup() m_completions.clear(); } + +void QmlCodeCompletion::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; + + m_snippetFileLastModified = lastModified; + QFile file(qmlsnippets); + file.open(QIODevice::ReadOnly); + QXmlStreamReader xml(&file); + while (!xml.atEnd() && 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); + 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; + } + } + + } + } + } + } + } + if (xml.hasError()) + qWarning() << qmlsnippets << xml.errorString(); + file.close(); +} diff --git a/src/plugins/qmljseditor/qmlcodecompletion.h b/src/plugins/qmljseditor/qmlcodecompletion.h index cb8687cde66..8adc066fc9f 100644 --- a/src/plugins/qmljseditor/qmlcodecompletion.h +++ b/src/plugins/qmljseditor/qmlcodecompletion.h @@ -32,6 +32,7 @@ #include <qmljs/qmljstypesystem.h> #include <texteditor/icompletioncollector.h> +#include <QtCore/QDateTime> namespace TextEditor { class ITextEditable; @@ -69,6 +70,10 @@ private: QList<TextEditor::CompletionItem> m_completions; Qt::CaseSensitivity m_caseSensitivity; QmlJS::TypeSystem *m_typeSystem; + + QList<TextEditor::CompletionItem> m_snippets; + QDateTime m_snippetFileLastModified; + void updateSnippets(); }; diff --git a/src/plugins/texteditor/basetexteditor.cpp b/src/plugins/texteditor/basetexteditor.cpp index 7fc9eaf18fa..718e757803d 100644 --- a/src/plugins/texteditor/basetexteditor.cpp +++ b/src/plugins/texteditor/basetexteditor.cpp @@ -960,11 +960,15 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e) d->m_lastEventWasBlockSelectionEvent = false; if (e->key() == Qt::Key_Escape) { - e->accept(); - QTextCursor cursor = textCursor(); - cursor.clearSelection(); - setTextCursor(cursor); - return; + if (d->m_snippetOverlay->isVisible()) { + e->accept(); + d->m_snippetOverlay->hide(); + d->m_snippetOverlay->clear(); + QTextCursor cursor = textCursor(); + cursor.clearSelection(); + setTextCursor(cursor); + return; + } } bool ro = isReadOnly(); @@ -996,6 +1000,17 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e) || (!d->m_lineSeparatorsAllowed && e == QKeySequence::InsertLineSeparator)) ) { + if (d->m_snippetOverlay->isVisible()) { + e->accept(); + d->m_snippetOverlay->hide(); + d->m_snippetOverlay->clear(); + QTextCursor cursor = textCursor(); + cursor.movePosition(QTextCursor::EndOfBlock); + setTextCursor(cursor); + return; + } + + QTextCursor cursor = textCursor(); if (d->m_inBlockSelectionMode) cursor.clearSelection(); @@ -1190,9 +1205,10 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e) return; } - if (d->m_snippetOverlay->isVisible() && - (e->key() == Qt::Key_Delete || e->key() == Qt::Key_Backspace)) + if (d->m_snippetOverlay->isVisible() + && (e->key() == Qt::Key_Delete || e->key() == Qt::Key_Backspace)) { d->snippetCheckCursor(textCursor()); + } if (ro || e->text().isEmpty() || !e->text().at(0).isPrint()) { QPlainTextEdit::keyPressEvent(e); @@ -1263,21 +1279,21 @@ void BaseTextEditor::insertCodeSnippet(const QString &snippet) QMap<int, int> positions; while (pos < snippet.size()) { - if (snippet.at(pos) != QLatin1Char('$')) { + if (snippet.at(pos) != QChar::ObjectReplacementCharacter) { const int start = pos; do { ++pos; } - while (pos < snippet.size() && snippet.at(pos) != QLatin1Char('$')); + while (pos < snippet.size() && snippet.at(pos) != QChar::ObjectReplacementCharacter); cursor.insertText(snippet.mid(start, pos - start)); } else { // the start of a place holder. const int start = ++pos; for (; pos < snippet.size(); ++pos) { - if (snippet.at(pos) == QLatin1Char('$')) + if (snippet.at(pos) == QChar::ObjectReplacementCharacter) break; } Q_ASSERT(pos < snippet.size()); - Q_ASSERT(snippet.at(pos) == QLatin1Char('$')); + Q_ASSERT(snippet.at(pos) == QChar::ObjectReplacementCharacter); const QString textToInsert = snippet.mid(start, pos - start); @@ -1319,7 +1335,12 @@ void BaseTextEditor::insertCodeSnippet(const QString &snippet) const QTextEdit::ExtraSelection &selection = selections.first(); cursor = textCursor(); - cursor.setPosition(selection.cursor.anchor() + 1); + if (selection.cursor.hasSelection()) { + cursor.setPosition(selection.cursor.selectionStart()+1); + cursor.setPosition(selection.cursor.selectionEnd(), QTextCursor::KeepAnchor); + } else { + cursor.setPosition(selection.cursor.position()); + } setTextCursor(cursor); } } @@ -1416,8 +1437,13 @@ bool BaseTextEditor::event(QEvent *e) d->m_contentsChanged = false; switch (e->type()) { case QEvent::ShortcutOverride: + if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape && d->m_snippetOverlay->isVisible()) { + e->accept(); + return true; + } e->ignore(); // we are a really nice citizen return true; + break; default: break; } @@ -1746,7 +1772,8 @@ void BaseTextEditorPrivate::snippetTabOrBacktab(bool forward) if (forward) { for (int i = 0; i < m_snippetOverlay->m_selections.count(); ++i){ const OverlaySelection &selection = m_snippetOverlay->m_selections.at(i); - if (selection.m_cursor_begin.position() > cursor.position()) { + if (selection.m_cursor_begin.position() >= cursor.position() + && selection.m_cursor_end.position() > cursor.position()) { final = selection; break; } diff --git a/src/plugins/texteditor/texteditoroverlay.h b/src/plugins/texteditor/texteditoroverlay.h index 5e8c6d76874..9f98c38c9ac 100644 --- a/src/plugins/texteditor/texteditoroverlay.h +++ b/src/plugins/texteditor/texteditoroverlay.h @@ -72,6 +72,9 @@ public: bool isVisible() const { return m_visible; } void setVisible(bool b); + inline void hide() { setVisible(false); } + inline void show() { setVisible(true); } + void setBorderWidth(int bw) {m_borderWidth = bw; } void update(); -- GitLab