Commit 5b8a6786 authored by mae's avatar mae
Browse files

first iteration of snippet support for qml

Done with Thorbjorn and Roberto
parent f0b1c33b
<?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>
......@@ -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();
}
......@@ -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();
};
......
......@@ -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;
}
......
......@@ -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();
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment