diff --git a/addons/filetree/autotests/filetree_model_test.cpp b/addons/filetree/autotests/filetree_model_test.cpp index aa7444900..16420733a 100644 --- a/addons/filetree/autotests/filetree_model_test.cpp +++ b/addons/filetree/autotests/filetree_model_test.cpp @@ -1,574 +1,574 @@ /* This file is part of the KDE project * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "filetree_model_test.h" #include "katefiletreemodel.h" #include "document_dummy.h" #include QTEST_GUILESS_MAIN(FileTreeModelTest) // BEGIN ResultNode class ResultNode { public: ResultNode() = default; // root node ResultNode(const ResultNode &other) : name(other.name) , dir(other.dir) , children(other.children) { } ResultNode(const char *_name, const bool _dir = false) : ResultNode(QString::fromLatin1(_name), _dir) { } ResultNode(const QString &_name, const bool _dir = false) : name(_name) , dir(_dir) , children() { } ResultNode &operator<<(const ResultNode &node) { children << node; return *this; } bool operator!=(const ResultNode &other) const { return !(*this == other); } bool operator==(const ResultNode &other) const { return (other.name == name) && (other.dir == dir) && (other.children == children); } friend QDebug operator<<(QDebug s, const ResultNode &node) { s << node.toString(); return s; } friend void debugOutput(QString &s, const ResultNode &rootNode, const int level = 0) { for (int i = 0; i < level; i++) { s += QLatin1String(" "); } const QString name = rootNode.name.isEmpty() ? QStringLiteral("ROOT") : rootNode.name; s += QLatin1String("( ") + name; if (rootNode.dir) { s += QLatin1String(", {D}"); } if (rootNode.children.isEmpty()) { s += QLatin1String(" )"); } else { s += QLatin1String(",\n"); for (int i = 0; i < rootNode.children.size(); i++) { const ResultNode &node = rootNode.children[i]; debugOutput(s, node, level + 1); if ((i + 1) < rootNode.children.size()) { s += QLatin1Char('\n'); } } s += (level == 0) ? QLatin1String("\n);") : QLatin1String(")"); } } QString toString() const { QString out; debugOutput(out, *this, 0); return out; } QString name; bool dir = true; QList children; }; Q_DECLARE_METATYPE(ResultNode) namespace QTest { inline bool qCompare(const ResultNode &t1, const ResultNode &t2, const char *actual, const char *expected, const char *file, int line) { /* compare_helper is not helping that much, we need to prepare copy of data */ const QByteArray a = t1.toString().toLatin1(); const QByteArray b = t2.toString().toLatin1(); char *val1 = new char[a.size() + 1]; char *val2 = new char[b.size() + 1]; memcpy(val1, a.constData(), a.size() + 1); memcpy(val2, b.constData(), b.size() + 1); return compare_helper(t1 == t2, "Compared ResultNode trees are not the same", val1, val2, actual, expected, file, line); } } // END ResultNode void FileTreeModelTest::initTestCase() { } void FileTreeModelTest::cleanupTestCase() { } void FileTreeModelTest::init() { } void FileTreeModelTest::cleanup() { } void FileTreeModelTest::basic() { QScopedPointer d1(new DummyDocument()); QScopedPointer d2(new DummyDocument()); KateFileTreeModel m(this); QCOMPARE(m.rowCount(QModelIndex()), 0); m.documentOpened(d1.data()); QCOMPARE(m.rowCount(QModelIndex()), 1); m.documentOpened(d2.data()); QCOMPARE(m.rowCount(QModelIndex()), 2); } void FileTreeModelTest::buildTree_data() { QTest::addColumn>("documents"); QTest::addColumn("nodes"); QTest::newRow("easy") << (QList() << new DummyDocument("file:///a/foo.txt")) << (ResultNode() << (ResultNode("a", true) << ResultNode("foo.txt"))); QTest::newRow("two") << (QList() << new DummyDocument("file:///a/foo.txt") << new DummyDocument("file:///a/bar.txt")) << (ResultNode() << (ResultNode("a", true) << ResultNode("foo.txt") << ResultNode("bar.txt"))); QTest::newRow("strangers") << (QList() << new DummyDocument("file:///a/foo.txt") << new DummyDocument("file:///b/bar.txt")) << (ResultNode() << (ResultNode("a", true) << ResultNode("foo.txt")) << (ResultNode("b", true) << ResultNode("bar.txt"))); QTest::newRow("lvl1 strangers") << (QList() << new DummyDocument("file:///c/a/foo.txt") << new DummyDocument("file:///c/b/bar.txt")) << (ResultNode() << (ResultNode("a", true) << ResultNode("foo.txt")) << (ResultNode("b", true) << ResultNode("bar.txt"))); QTest::newRow("multiples") << (QList() << new DummyDocument("file:///c/a/foo.txt") << new DummyDocument("file:///c/b/bar.txt") << new DummyDocument("file:///c/a/bar.txt")) << (ResultNode() << (ResultNode("a", true) << ResultNode("foo.txt") << ResultNode("bar.txt")) << (ResultNode("b", true) << ResultNode("bar.txt"))); QTest::newRow("stairs") << (QList() << new DummyDocument("file:///c/a/foo.txt") << new DummyDocument("file:///c/bar.txt")) << (ResultNode() << (ResultNode("c", true) << (ResultNode("a", true) << ResultNode("foo.txt")) << ResultNode("bar.txt"))); QTest::newRow("reverse stairs") << (QList() << new DummyDocument("file:///c/bar.txt") << new DummyDocument("file:///c/a/foo.txt")) << (ResultNode() << (ResultNode("c", true) << ResultNode("bar.txt") << (ResultNode("a", true) << ResultNode("foo.txt")))); QTest::newRow("matching") << (QList() << new DummyDocument("file:///a/x/foo.txt") << new DummyDocument("file:///b/x/bar.txt")) << (ResultNode() << (ResultNode("a", true) << (ResultNode("x", true) << ResultNode("foo.txt"))) << (ResultNode("b", true) << (ResultNode("x", true) << ResultNode("bar.txt")))); QTest::newRow("matching even more") << (QList() << new DummyDocument("file:///a/x/y/z/foo.txt") << new DummyDocument("file:///b/x/y/z/bar.txt")) << (ResultNode() << (ResultNode("a", true) << (ResultNode("x", true) << (ResultNode("y", true) << (ResultNode("z", true) << ResultNode("foo.txt"))))) << (ResultNode("b", true) << (ResultNode("x", true) << (ResultNode("y", true) << (ResultNode("z", true) << ResultNode("bar.txt")))))); QTest::newRow("matching with booby trap") << (QList() << new DummyDocument("file:///x/y/foo.txt") << new DummyDocument("file:///c/x/bar.txt") << new DummyDocument("file:///d/y/baz.txt")) << (ResultNode() << (ResultNode("x", true) << (ResultNode("y", true) << ResultNode("foo.txt"))) << (ResultNode("d", true) << (ResultNode("y", true) << ResultNode("baz.txt"))) << (ResultNode("c", true) << (ResultNode("x", true) << ResultNode("bar.txt")))); QTest::newRow("branches") << (QList() << new DummyDocument("file:///c/a/foo.txt") << new DummyDocument("file:///c/b/bar.txt") << new DummyDocument("file:///d/a/foo.txt")) << (ResultNode() << (ResultNode("c", true) << (ResultNode("a", true) << ResultNode("foo.txt")) << (ResultNode("b", true) << ResultNode("bar.txt"))) << (ResultNode("d", true) << (ResultNode("a", true) << ResultNode("foo.txt")))); QTest::newRow("branches (more)") << (QList() << new DummyDocument("file:///c/a/foo.txt") << new DummyDocument("file:///c/b/bar.txt") << new DummyDocument("file:///c/c/bar.txt") << new DummyDocument("file:///d/a/foo.txt")) << (ResultNode() << (ResultNode("c", true) << (ResultNode("a", true) << ResultNode("foo.txt")) << (ResultNode("b", true) << ResultNode("bar.txt")) << (ResultNode("c", true) << ResultNode("bar.txt"))) << (ResultNode("d", true) << (ResultNode("a", true) << ResultNode("foo.txt")))); QTest::newRow("bug347578") << (QList() << new DummyDocument("file:///f/g/a/b/c/d/e.txt") << new DummyDocument("file:///f/g/a/t/b/c/d/e.txt")) << (ResultNode() << (ResultNode("a", true) << (ResultNode("b", true) << (ResultNode("c", true) << (ResultNode("d", true) << ResultNode("e.txt")))) << (ResultNode("t", true) << (ResultNode("b", true) << (ResultNode("c", true) << (ResultNode("d", true) << ResultNode("e.txt"))))))); QTest::newRow("levels") << (QList() << new DummyDocument("file:///c/a/foo.txt") << new DummyDocument("file:///c/b/bar.txt") << new DummyDocument("file:///d/foo.txt")) << (ResultNode() << (ResultNode("a", true) << ResultNode("foo.txt")) << (ResultNode("b", true) << ResultNode("bar.txt")) << (ResultNode("d", true) << ResultNode("foo.txt"))); QTest::newRow("remote simple") << (QList() << new DummyDocument("http://example.org/foo.txt")) << (ResultNode() << (ResultNode("[example.org]", true) << ResultNode("foo.txt"))); QTest::newRow("remote nested") << (QList() << new DummyDocument("http://example.org/a/foo.txt")) << (ResultNode() << (ResultNode("[example.org]a", true) << ResultNode("foo.txt"))); /* NOTE: this one is also not completely ok, is it? * on other hand, it would get confusing or overly leveled if opening * something like http://example.org/a/b/c/d/e/f/g.txt */ QTest::newRow("remote diverge") << (QList() << new DummyDocument("http://example.org/a/foo.txt") << new DummyDocument("http://example.org/b/foo.txt")) << (ResultNode() << (ResultNode("[example.org]a", true) << ResultNode("foo.txt")) << (ResultNode("[example.org]b", true) << ResultNode("foo.txt"))); } void FileTreeModelTest::buildTree() { KateFileTreeModel m(this); - QFETCH(QList, documents); + QFETCH(const QList, documents); QFETCH(ResultNode, nodes); - foreach (DummyDocument *doc, documents) { + for (DummyDocument *doc : documents) { m.documentOpened(doc); } ResultNode root; walkTree(m, QModelIndex(), root); QCOMPARE(root, nodes); qDeleteAll(documents); } void FileTreeModelTest::buildTreeBatch_data() { // the easiest way to verify the equality of those two calls:) buildTree_data(); } void FileTreeModelTest::buildTreeBatch() { KateFileTreeModel m(this); - QFETCH(QList, documents); + QFETCH(const QList, documents); QFETCH(ResultNode, nodes); QList list; - foreach (DummyDocument *doc, documents) { + for (DummyDocument *doc : documents) { list << doc; } m.documentsOpened(list); ResultNode root; walkTree(m, QModelIndex(), root); QCOMPARE(root, nodes); qDeleteAll(documents); } void FileTreeModelTest::buildTreeBatchPrefill_data() { QTest::addColumn>("prefill"); QTest::addColumn>("documents"); QTest::addColumn("nodes"); QTest::newRow("easy") << (QList() << new DummyDocument("file:///a/foo.txt")) << (QList() << new DummyDocument("file:///a/bar.txt")) << (ResultNode() << (ResultNode("a", true) << ResultNode("foo.txt") << ResultNode("bar.txt"))); QTest::newRow("split") << (QList() << new DummyDocument("file:///a/foo.txt")) << (QList() << new DummyDocument("file:///b/foo.txt")) << (ResultNode() << (ResultNode("a", true) << ResultNode("foo.txt")) << (ResultNode("b", true) << ResultNode("foo.txt"))); } void FileTreeModelTest::buildTreeBatchPrefill() { KateFileTreeModel m(this); - QFETCH(QList, prefill); - QFETCH(QList, documents); + QFETCH(const QList, prefill); + QFETCH(const QList, documents); QFETCH(ResultNode, nodes); - foreach (DummyDocument *doc, prefill) { + for (DummyDocument *doc : prefill) { m.documentOpened(doc); } QList list; - foreach (DummyDocument *doc, documents) { + for (DummyDocument *doc : documents) { list << doc; } m.documentsOpened(list); ResultNode root; walkTree(m, QModelIndex(), root); QCOMPARE(root, nodes); qDeleteAll(prefill); qDeleteAll(documents); } void FileTreeModelTest::walkTree(KateFileTreeModel &model, const QModelIndex &rootIndex, ResultNode &rootNode) { if (!model.hasChildren(rootIndex)) { return; } const int rows = model.rowCount(rootIndex); for (int i = 0; i < rows; i++) { const QModelIndex idx = model.index(i, 0, rootIndex); ResultNode node(model.data(idx).toString(), model.isDir(idx)); walkTree(model, idx, node); rootNode << node; } } void FileTreeModelTest::buildTreeFullPath_data() { QTest::addColumn>("documents"); QTest::addColumn("nodes"); QTest::newRow("two") << (QList() << new DummyDocument("file:///a/foo.txt") << new DummyDocument("file:///a/bar.txt")) << (ResultNode() << (ResultNode("/a", true) << ResultNode("foo.txt") << ResultNode("bar.txt"))); QTest::newRow("multiples") << (QList() << new DummyDocument("file:///c/a/foo.txt") << new DummyDocument("file:///c/b/bar.txt") << new DummyDocument("file:///c/a/bar.txt")) << (ResultNode() << (ResultNode("/c/a", true) << ResultNode("foo.txt") << ResultNode("bar.txt")) << (ResultNode("/c/b", true) << ResultNode("bar.txt"))); /* This one and the case after can get a little bit tricky and * in some situation could end up in little bit confusing layout. * current root merge algorithm sees the divergent paths, so it * doesn't invoke merging. QTest::newRow("branches") << ( QList() << new DummyDocument("file:///c/a/foo.txt") << new DummyDocument("file:///c/b/bar.txt") ) << ( ResultNode() << (ResultNode("/c", true) << (ResultNode("a", true) << ResultNode("foo.txt")) << (ResultNode("b", true) << ResultNode("bar.txt"))) ); */ /* QTest::newRow("levels") << ( QList() << new DummyDocument("file:///c/a/foo.txt") << new DummyDocument("file:///c/b/bar.txt") << new DummyDocument("file:///d/foo.txt") ) << ( ResultNode() << (ResultNode("/c", true) << (ResultNode("a", true) << ResultNode("foo.txt")) << (ResultNode("b", true) << ResultNode("bar.txt"))) << (ResultNode("/d", true) << ResultNode("foo.txt")) ); */ QTest::newRow("remote simple") << (QList() << new DummyDocument("http://example.org/foo.txt")) << (ResultNode() << (ResultNode("[example.org]", true) << ResultNode("foo.txt"))); QTest::newRow("remote nested") << (QList() << new DummyDocument("http://example.org/a/b/foo.txt")) << (ResultNode() << (ResultNode("[example.org]/a/b", true) << ResultNode("foo.txt"))); /* NOTE: see the similar testcase in buildTree */ QTest::newRow("remote diverge") << (QList() << new DummyDocument("http://example.org/c/a/foo.txt") << new DummyDocument("http://example.org/c/b/foo.txt")) << (ResultNode() << (ResultNode("[example.org]/c/a", true) << ResultNode("foo.txt")) << (ResultNode("[example.org]/c/b", true) << ResultNode("foo.txt"))); } void FileTreeModelTest::buildTreeFullPath() { KateFileTreeModel m(this); m.setShowFullPathOnRoots(true); - QFETCH(QList, documents); + QFETCH(const QList, documents); QFETCH(ResultNode, nodes); - foreach (DummyDocument *doc, documents) { + for (DummyDocument *doc : documents) { m.documentOpened(doc); } ResultNode root; walkTree(m, QModelIndex(), root); QCOMPARE(root, nodes); qDeleteAll(documents); } void FileTreeModelTest::listMode_data() { QTest::addColumn>("documents"); QTest::addColumn("nodes"); QTest::newRow("easy") << (QList() << new DummyDocument("file:///a/foo.txt")) << (ResultNode() << ResultNode("foo.txt")); QTest::newRow("two") << (QList() << new DummyDocument("file:///a/foo.txt") << new DummyDocument("file:///a/bar.txt")) << (ResultNode() << ResultNode("foo.txt") << ResultNode("bar.txt")); QTest::newRow("multiples") << (QList() << new DummyDocument("file:///c/a/foo.txt") << new DummyDocument("file:///c/b/bar.txt") << new DummyDocument("file:///c/a/bar.txt")) << (ResultNode() << ResultNode("foo.txt") << ResultNode("bar.txt") << ResultNode("bar.txt")); QTest::newRow("remote diverge") << (QList() << new DummyDocument("http://example.org/a/foo.txt") << new DummyDocument("http://example.org/b/foo.txt")) << (ResultNode() << ResultNode("[example.org]foo.txt") << ResultNode("[example.org]foo.txt")); } void FileTreeModelTest::listMode() { KateFileTreeModel m(this); m.setListMode(true); - QFETCH(QList, documents); + QFETCH(const QList, documents); QFETCH(ResultNode, nodes); - foreach (DummyDocument *doc, documents) { + for (DummyDocument *doc : documents) { m.documentOpened(doc); } ResultNode root; walkTree(m, QModelIndex(), root); QCOMPARE(root, nodes); qDeleteAll(documents); } void FileTreeModelTest::deleteDocument_data() { QTest::addColumn>("documents"); QTest::addColumn>("remove"); QTest::addColumn("nodes"); QTest::newRow("empty") << (QList() << new DummyDocument("file:///a/foo.txt")) << (QList() << 0) << (ResultNode()); QTest::newRow("two") << (QList() << new DummyDocument("file:///a/foo.txt") << new DummyDocument("file:///a/bar.txt")) << (QList() << 0) << (ResultNode() << (ResultNode("a", true) << ResultNode("bar.txt"))); QTest::newRow("multiple") << (QList() << new DummyDocument("file:///a/foo0.txt") << new DummyDocument("file:///a/foo1.txt") << new DummyDocument("file:///a/foo2.txt") << new DummyDocument("file:///a/foo3.txt") << new DummyDocument("file:///a/foo4.txt") << new DummyDocument("file:///a/foo5.txt") << new DummyDocument("file:///a/foo6.txt") << new DummyDocument("file:///a/foo7.txt")) << (QList() << 1 << 2 << 4 << 6) << (ResultNode() << (ResultNode("a", true) << ResultNode("foo0.txt") << ResultNode("foo3.txt") << ResultNode("foo5.txt") << ResultNode("foo7.txt"))); QTest::newRow("strangers") << (QList() << new DummyDocument("file:///a/foo.txt") << new DummyDocument("file:///b/bar.txt")) << (QList() << 1) << (ResultNode() << (ResultNode("a", true) << ResultNode("foo.txt"))); QTest::newRow("branches") << (QList() << new DummyDocument("file:///c/a/foo.txt") << new DummyDocument("file:///c/b/bar.txt") << new DummyDocument("file:///d/a/foo.txt")) << (QList() << 1) << (ResultNode() << (ResultNode("c", true) << (ResultNode("a", true) << ResultNode("foo.txt"))) << (ResultNode("d", true) << (ResultNode("a", true) << ResultNode("foo.txt")))); QTest::newRow("levels") << (QList() << new DummyDocument("file:///c/a/foo.txt") << new DummyDocument("file:///c/b/bar.txt") << new DummyDocument("file:///d/foo.txt")) << (QList() << 0) << (ResultNode() << (ResultNode("b", true) << ResultNode("bar.txt")) << (ResultNode("d", true) << ResultNode("foo.txt"))); QTest::newRow("levels extra") << (QList() << new DummyDocument("file:///c/a/foo.txt") << new DummyDocument("file:///c/b/bar.txt") << new DummyDocument("file:///d/foo.txt")) << (QList() << 2) << (ResultNode() << (ResultNode("a", true) << ResultNode("foo.txt")) << (ResultNode("b", true) << ResultNode("bar.txt"))); QTest::newRow("remote diverge") << (QList() << new DummyDocument("http://example.org/a/foo.txt") << new DummyDocument("http://example.org/b/foo.txt")) << (QList() << 1) << (ResultNode() << (ResultNode("[example.org]a", true) << ResultNode("foo.txt"))); } void FileTreeModelTest::deleteDocument() { KateFileTreeModel m(this); - QFETCH(QList, documents); - QFETCH(QList, remove); + QFETCH(const QList, documents); + QFETCH(const QList, remove); QFETCH(ResultNode, nodes); - foreach (DummyDocument *doc, documents) { + for (DummyDocument *doc : documents) { m.documentOpened(doc); } - foreach (const int &index, remove) { + for (const int &index : remove) { m.documentClosed(documents[index]); } ResultNode root; walkTree(m, QModelIndex(), root); QCOMPARE(root, nodes); qDeleteAll(documents); } void FileTreeModelTest::deleteDocumentBatch_data() { QTest::addColumn>("documents"); QTest::addColumn>("remove"); QTest::addColumn>("fail"); QTest::addColumn("nodes"); QTest::newRow("neo") << (QList() << new DummyDocument("file:///a/foo0.txt") << new DummyDocument("file:///a/foo1.txt") << new DummyDocument("file:///a/foo2.txt") << new DummyDocument("file:///a/foo3.txt") << new DummyDocument("file:///a/foo4.txt") << new DummyDocument("file:///a/foo5.txt") << new DummyDocument("file:///a/foo6.txt") << new DummyDocument("file:///a/foo7.txt")) << (QList() << 1 << 2 << 4 << 6) << (QList() << 2 << 4) << (ResultNode() << (ResultNode("a", true) << ResultNode("foo0.txt") << ResultNode("foo2.txt") << ResultNode("foo3.txt") << ResultNode("foo4.txt") << ResultNode("foo5.txt") << ResultNode("foo7.txt"))); } void FileTreeModelTest::deleteDocumentBatch() { KateFileTreeModel m(this); - QFETCH(QList, documents); - QFETCH(QList, remove); - QFETCH(QList, fail); + QFETCH(const QList, documents); + QFETCH(const QList, remove); + QFETCH(const QList, fail); QFETCH(ResultNode, nodes); - foreach (DummyDocument *doc, documents) { + for (DummyDocument *doc : documents) { m.documentOpened(doc); } QList removing; - foreach (const int &index, remove) { + for (const int &index : remove) { removing << documents[index]; } m.slotAboutToDeleteDocuments(removing); - foreach (const int &index, remove) { + for (const int &index : remove) { if (!fail.contains(index)) { m.documentClosed(documents[index]); } } removing.clear(); - foreach (const int &index, fail) { + for (const int &index : fail) { removing << documents[index]; } m.slotDocumentsDeleted(removing); ResultNode root; walkTree(m, QModelIndex(), root); QCOMPARE(root, nodes); qDeleteAll(documents); } void FileTreeModelTest::rename_data() { QTest::addColumn>("documents"); QTest::addColumn("rename_idx"); QTest::addColumn("rename_url"); QTest::addColumn("nodes"); QTest::newRow("empty") << (QList() << new DummyDocument()) << 0 << QStringLiteral("file:///a/foo.txt") << (ResultNode() << (ResultNode("a", true) << ResultNode("foo.txt"))); QTest::newRow("moving") << (QList() << new DummyDocument("file:///a/foo.txt")) << 0 << QStringLiteral("file:///b/foo.txt") << (ResultNode() << (ResultNode("b", true) << ResultNode("foo.txt"))); QTest::newRow("splitting") << (QList() << new DummyDocument("file:///a/foo.txt") << new DummyDocument("file:///a/bar.txt")) << 0 << QStringLiteral("file:///b/foo.txt") << (ResultNode() << (ResultNode("a", true) << ResultNode("bar.txt")) << (ResultNode("b", true) << ResultNode("foo.txt"))); } void FileTreeModelTest::rename() { KateFileTreeModel m(this); - QFETCH(QList, documents); + QFETCH(const QList, documents); QFETCH(int, rename_idx); QFETCH(QString, rename_url); QFETCH(ResultNode, nodes); - foreach (DummyDocument *doc, documents) { + for (DummyDocument *doc : documents) { m.documentOpened(doc); } documents[rename_idx]->setUrl(rename_url); m.documentNameChanged(documents[rename_idx]); ResultNode root; walkTree(m, QModelIndex(), root); QCOMPARE(root, nodes); qDeleteAll(documents); } // kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/addons/snippets/snippetrepository.cpp b/addons/snippets/snippetrepository.cpp index 6b32da517..96180fe79 100644 --- a/addons/snippets/snippetrepository.cpp +++ b/addons/snippets/snippetrepository.cpp @@ -1,405 +1,403 @@ /* This file is part of the Kate project. * Based on the snippet plugin from KDevelop 4. * * Copyright (C) 2007 Robert Gruber * Copyright (C) 2010 Milian Wolff * Copyright (C) 2012 Christoph Cullmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "snippetrepository.h" #include "snippet.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "snippetstore.h" static const QString defaultScript = QStringLiteral( "\ function fileName() { return document.fileName(); }\n\ function fileUrl() { return document.url(); }\n\ function encoding() { return document.encoding(); }\n\ function selection() { return view.selectedText(); }\n\ function year() { return new Date().getFullYear(); }\n\ function upper(x) { return x.toUpperCase(); }\n\ function lower(x) { return x.toLowerCase(); }\n"); SnippetRepository::SnippetRepository(const QString &file) : QStandardItem(i18n("")) , m_file(file) , m_script(defaultScript) { setIcon(QIcon::fromTheme(QStringLiteral("folder"))); const auto &config = SnippetStore::self()->getConfig(); bool activated = config.readEntry("enabledRepositories", QStringList()).contains(file); setCheckState(activated ? Qt::Checked : Qt::Unchecked); if (QFile::exists(file)) { // Tell the new repository to load it's snippets QTimer::singleShot(0, this, &SnippetRepository::slotParseFile); } qDebug() << "created new snippet repo" << file << this; } SnippetRepository::~SnippetRepository() { // remove all our children from both the model and our internal data structures removeRows(0, rowCount()); } QDir SnippetRepository::dataPath() { auto dir = QDir(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)); const auto &subdir = QLatin1String("ktexteditor_snippets/data/"); bool success = dir.mkpath(dir.absoluteFilePath(subdir)); Q_ASSERT(success); dir.setPath(dir.path() + QLatin1String("/") + subdir); return dir; } SnippetRepository *SnippetRepository::createRepoFromName(const QString &name) { QString cleanName = name; cleanName.replace(QLatin1Char('/'), QLatin1Char('-')); const auto &dir = dataPath(); const auto &path = dir.absoluteFilePath(cleanName + QLatin1String(".xml")); qDebug() << "repo path:" << path << cleanName; SnippetRepository *repo = new SnippetRepository(path); repo->setText(name); repo->setCheckState(Qt::Checked); KUser user; repo->setAuthors(user.property(KUser::FullName).toString()); SnippetStore::self()->appendRow(repo); return repo; } const QString &SnippetRepository::file() const { return m_file; } QString SnippetRepository::authors() const { return m_authors; } void SnippetRepository::setAuthors(const QString &authors) { m_authors = authors; } QStringList SnippetRepository::fileTypes() const { return m_filetypes; } void SnippetRepository::setFileTypes(const QStringList &filetypes) { if (filetypes.contains(QLatin1String("*"))) { m_filetypes.clear(); } else { m_filetypes = filetypes; } } QString SnippetRepository::license() const { return m_license; } void SnippetRepository::setLicense(const QString &license) { m_license = license; } QString SnippetRepository::completionNamespace() const { return m_namespace; } void SnippetRepository::setCompletionNamespace(const QString &completionNamespace) { m_namespace = completionNamespace; } QString SnippetRepository::script() const { return m_script; } void SnippetRepository::setScript(const QString &script) { m_script = script; } void SnippetRepository::remove() { QFile::remove(m_file); setCheckState(Qt::Unchecked); model()->invisibleRootItem()->removeRow(row()); } /// copied code from snippets_tng/lib/completionmodel.cpp ///@copyright 2009 Joseph Wenninger static void addAndCreateElement(QDomDocument &doc, QDomElement &item, const QString &name, const QString &content) { QDomElement element = doc.createElement(name); element.appendChild(doc.createTextNode(content)); item.appendChild(element); } void SnippetRepository::save() { qDebug() << "*** called"; /// based on the code from snippets_tng/lib/completionmodel.cpp ///@copyright 2009 Joseph Wenninger /* prefix test1 postfix (param1, param2) This is a test testtemplate This is a test ${WHAT} template */ QDomDocument doc; QDomElement root = doc.createElement(QStringLiteral("snippets")); root.setAttribute(QStringLiteral("name"), text()); root.setAttribute(QStringLiteral("filetypes"), m_filetypes.isEmpty() ? QStringLiteral("*") : m_filetypes.join(QLatin1Char(';'))); root.setAttribute(QStringLiteral("authors"), m_authors); root.setAttribute(QStringLiteral("license"), m_license); root.setAttribute(QStringLiteral("namespace"), m_namespace); doc.appendChild(root); addAndCreateElement(doc, root, QStringLiteral("script"), m_script); for (int i = 0; i < rowCount(); ++i) { Snippet *snippet = dynamic_cast(child(i)); if (!snippet) { continue; } QDomElement item = doc.createElement(QStringLiteral("item")); addAndCreateElement(doc, item, QStringLiteral("match"), snippet->text()); addAndCreateElement(doc, item, QStringLiteral("fillin"), snippet->snippet()); root.appendChild(item); } // KMessageBox::information(0,doc.toString()); QFileInfo fi(m_file); QDir dir = dataPath(); QString outname = dir.absoluteFilePath(fi.fileName()); if (m_file != outname) { QFileInfo fiout(outname); // there could be cases that new new name clashes with a global file, but I guess it is not that often. int i = 0; while (QFile::exists(outname)) { i++; outname = dir.absoluteFilePath(QString::number(i) + fi.fileName()); } KMessageBox::information(QApplication::activeWindow(), i18n("You have edited a data file not located in your personal data directory; as such, a renamed clone of the original data file has been created within your personal data directory.")); } QFile outfile(outname); if (!outfile.open(QIODevice::WriteOnly)) { KMessageBox::error(nullptr, i18n("Output file '%1' could not be opened for writing", outname)); return; } outfile.write(doc.toByteArray()); outfile.close(); m_file = outname; // save shortcuts KConfigGroup config = SnippetStore::self()->getConfig().group(QLatin1String("repository ") + m_file); for (int i = 0; i < rowCount(); ++i) { Snippet *snippet = dynamic_cast(child(i)); if (!snippet) { continue; } QStringList shortcuts; foreach (const QKeySequence &keys, snippet->action()->shortcuts()) { shortcuts << keys.toString(); } config.writeEntry(QLatin1String("shortcut ") + snippet->text(), shortcuts); } config.sync(); } void SnippetRepository::slotParseFile() { /// based on the code from snippets_tng/lib/completionmodel.cpp ///@copyright 2009 Joseph Wenninger QFile f(m_file); if (!f.open(QIODevice::ReadOnly)) { KMessageBox::error(QApplication::activeWindow(), i18n("Cannot open snippet repository %1.", m_file)); return; } QDomDocument doc; QString errorMsg; int line, col; bool success = doc.setContent(&f, &errorMsg, &line, &col); f.close(); if (!success) { KMessageBox::error(QApplication::activeWindow(), i18n("The error %4
has been detected in the file %1 at %2/%3
", m_file, line, col, i18nc("QXml", errorMsg.toUtf8().data()))); return; } // parse root item const QDomElement &docElement = doc.documentElement(); if (docElement.tagName() != QLatin1String("snippets")) { KMessageBox::error(QApplication::activeWindow(), i18n("Invalid XML snippet file: %1", m_file)); return; } setLicense(docElement.attribute(QStringLiteral("license"))); setAuthors(docElement.attribute(QStringLiteral("authors"))); setFileTypes(docElement.attribute(QStringLiteral("filetypes")).split(QLatin1Char(';'), QString::SkipEmptyParts)); setText(docElement.attribute(QStringLiteral("name"))); setCompletionNamespace(docElement.attribute(QStringLiteral("namespace"))); // load shortcuts KConfigGroup config = SnippetStore::self()->getConfig().group(QLatin1String("repository ") + m_file); // parse children, i.e. 's const QDomNodeList &nodes = docElement.childNodes(); for (int i = 0; i < nodes.size(); ++i) { const QDomNode &node = nodes.at(i); if (!node.isElement()) { continue; } const QDomElement &item = node.toElement(); if (item.tagName() == QLatin1String("script")) { setScript(item.text()); } if (item.tagName() != QLatin1String("item")) { continue; } Snippet *snippet = new Snippet; const QDomNodeList &children = node.childNodes(); for (int j = 0; j < children.size(); ++j) { const QDomNode &childNode = children.at(j); if (!childNode.isElement()) { continue; } const QDomElement &child = childNode.toElement(); if (child.tagName() == QLatin1String("match")) { snippet->setText(child.text()); } else if (child.tagName() == QLatin1String("fillin")) { snippet->setSnippet(child.text()); } } // require at least a non-empty name and snippet if (snippet->text().isEmpty() || snippet->snippet().isEmpty()) { delete snippet; continue; } else { const QStringList shortcuts = config.readEntry(QLatin1String("shortcut ") + snippet->text(), QStringList()); - QList sequences; - - foreach (const QString &shortcut, shortcuts) { + for (const QString &shortcut : shortcuts) { sequences << QKeySequence::fromString(shortcut); } snippet->action()->setShortcuts(sequences); appendRow(snippet); } } } QVariant SnippetRepository::data(int role) const { if (role == Qt::ToolTipRole) { if (checkState() != Qt::Checked) { return i18n("Repository is disabled, the contained snippets will not be shown during code-completion."); } if (m_filetypes.isEmpty()) { return i18n("Applies to all filetypes"); } else { return i18n("Applies to the following filetypes: %1", m_filetypes.join(QLatin1String(", "))); } } else if (role == Qt::ForegroundRole && checkState() != Qt::Checked) { /// TODO: make the selected items also "disalbed" so the toggle action is seen directly KColorScheme scheme(QPalette::Disabled, KColorScheme::View); QColor c = scheme.foreground(KColorScheme::NormalText).color(); return QVariant(c); } return QStandardItem::data(role); } void SnippetRepository::setData(const QVariant &value, int role) { if (role == Qt::CheckStateRole) { const int state = value.toInt(); if (state != checkState()) { KConfigGroup config = SnippetStore::self()->getConfig(); QStringList currentlyEnabled = config.readEntry("enabledRepositories", QStringList()); bool shouldSave = false; if (state == Qt::Checked && !currentlyEnabled.contains(m_file)) { currentlyEnabled << m_file; shouldSave = true; } else if (state == Qt::Unchecked && currentlyEnabled.contains(m_file)) { currentlyEnabled.removeAll(m_file); shouldSave = true; } if (shouldSave) { config.writeEntry("enabledRepositories", currentlyEnabled); config.sync(); } } } QStandardItem::setData(value, role); } diff --git a/addons/snippets/snippetstore.cpp b/addons/snippets/snippetstore.cpp index 0052bcacd..3ecbb0aca 100644 --- a/addons/snippets/snippetstore.cpp +++ b/addons/snippets/snippetstore.cpp @@ -1,130 +1,130 @@ /* This file is part of the Kate project. * Based on the snippet plugin from KDevelop 4. * * Copyright (C) 2007 Robert Gruber * Copyright (C) 2010 Milian Wolff * Copyright (C) 2012 Christoph Cullmann * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "snippetstore.h" #include "katesnippetglobal.h" #include "snippetrepository.h" #include #include #include #include Q_DECLARE_METATYPE(KSharedConfig::Ptr) SnippetStore *SnippetStore::m_self = nullptr; SnippetStore::SnippetStore(KateSnippetGlobal *plugin) : m_plugin(plugin) { m_self = this; const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("ktexteditor_snippets/data"), QStandardPaths::LocateDirectory) << QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("ktexteditor_snippets/ghns"), QStandardPaths::LocateDirectory); QStringList files; - foreach (const QString &dir, dirs) { + for (const QString &dir : dirs) { const QStringList fileNames = QDir(dir).entryList(QStringList() << QStringLiteral("*.xml")); - foreach (const QString &file, fileNames) { + for (const QString &file : fileNames) { files.append(dir + QLatin1Char('/') + file); } } - foreach (const QString &file, files) { + for (const QString &file : qAsConst(files)) { SnippetRepository *repo = new SnippetRepository(file); appendRow(repo); } } SnippetStore::~SnippetStore() { invisibleRootItem()->removeRows(0, invisibleRootItem()->rowCount()); m_self = nullptr; } void SnippetStore::init(KateSnippetGlobal *plugin) { Q_ASSERT(!SnippetStore::self()); new SnippetStore(plugin); } SnippetStore *SnippetStore::self() { return m_self; } Qt::ItemFlags SnippetStore::flags(const QModelIndex &index) const { Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable; if (!index.parent().isValid()) { flags |= Qt::ItemIsUserCheckable; } return flags; } KConfigGroup SnippetStore::getConfig() { return KSharedConfig::openConfig()->group("Snippets"); } bool SnippetStore::setData(const QModelIndex &index, const QVariant &value, int role) { if (role == Qt::EditRole && value.toString().isEmpty()) { // don't allow empty names return false; } const bool success = QStandardItemModel::setData(index, value, role); if (!success || role != Qt::EditRole) { return success; } // when we edited something, save the repository QStandardItem *repoItem = nullptr; if (index.parent().isValid()) { repoItem = itemFromIndex(index.parent()); } else { repoItem = itemFromIndex(index); } SnippetRepository *repo = dynamic_cast(repoItem); if (repo) { repo->save(); } return true; } SnippetRepository *SnippetStore::repositoryForFile(const QString &file) { for (int i = 0; i < rowCount(); ++i) { if (SnippetRepository *repo = dynamic_cast(item(i))) { if (repo->file() == file) { return repo; } } } return nullptr; }