diff --git a/addons/tabswitcher/autotests/tabswitchertest.cpp b/addons/tabswitcher/autotests/tabswitchertest.cpp index 81523f06e..858e64db0 100644 --- a/addons/tabswitcher/autotests/tabswitchertest.cpp +++ b/addons/tabswitcher/autotests/tabswitchertest.cpp @@ -1,64 +1,77 @@ /* This file is part of the KDE project * * Copyright (C) 2018 Gregor Mi * * 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 "tabswitchertest.h" #include "tabswitcherfilesmodel.h" #include QTEST_MAIN(KateTabSwitcherTest) void KateTabSwitcherTest::initTestCase() { } void KateTabSwitcherTest::cleanupTestCase() { } void KateTabSwitcherTest::testLongestCommonPrefix() { - // standard case + QFETCH(std::vector, input_strings); + QFETCH(QString, expected); + + QCOMPARE(detail::longestCommonPrefix(input_strings), expected); +} + +void KateTabSwitcherTest::testLongestCommonPrefix_data() +{ + QTest::addColumn>("input_strings"); + QTest::addColumn("expected"); std::vector strs; + + strs.clear(); strs.push_back(QLatin1String("/home/user1/a")); strs.push_back(QLatin1String("/home/user1/bc")); - QCOMPARE(detail::longestCommonPrefix(strs), QLatin1String("/home/user1/")); + QTest::newRow("standard case") << strs << QString(QLatin1String("/home/user1/")); - // empty string at the end of the list strs.clear(); strs.push_back(QLatin1String("/home/a")); strs.push_back(QLatin1String("/home/b")); strs.push_back(QLatin1String("")); - QCOMPARE(detail::longestCommonPrefix(strs), QLatin1String("")); + QTest::newRow("empty string at the end of the list") << strs << QString(); - // empty string not only at the end of the list strs.clear(); strs.push_back(QLatin1String("")); strs.push_back(QLatin1String("/home/a")); strs.push_back(QLatin1String("/home/b")); strs.push_back(QLatin1String("")); - QCOMPARE(detail::longestCommonPrefix(strs), QLatin1String("")); + QTest::newRow("empty string not only at the end of the list") << strs << QString(); - // a prefix with length 1 strs.clear(); strs.push_back(QLatin1String("/home/a")); strs.push_back(QLatin1String("/etc/b")); - QCOMPARE(detail::longestCommonPrefix(strs), QLatin1String("/")); + QTest::newRow("a prefix with length 1") << strs << QString(QLatin1String("/")); + + strs.clear(); + strs.push_back(QLatin1String("a")); + strs.push_back(QLatin1String("a")); + QTest::newRow("two equal strings") << strs << QString(QLatin1String("a")); } diff --git a/addons/tabswitcher/autotests/tabswitchertest.h b/addons/tabswitcher/autotests/tabswitchertest.h index 2cd3ed8cf..84c88ccd7 100644 --- a/addons/tabswitcher/autotests/tabswitchertest.h +++ b/addons/tabswitcher/autotests/tabswitchertest.h @@ -1,36 +1,37 @@ /* This file is part of the KDE project * * Copyright (C) 2018 Gregor Mi * * 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. */ #pragma once #include class KateTabSwitcherTest : public QObject { Q_OBJECT public Q_SLOTS: void initTestCase(); void cleanupTestCase(); private Q_SLOTS: void testLongestCommonPrefix(); + void testLongestCommonPrefix_data(); }; diff --git a/addons/tabswitcher/tabswitcherfilesmodel.cpp b/addons/tabswitcher/tabswitcherfilesmodel.cpp index 93a00d72b..bb4957c8b 100644 --- a/addons/tabswitcher/tabswitcherfilesmodel.cpp +++ b/addons/tabswitcher/tabswitcherfilesmodel.cpp @@ -1,177 +1,177 @@ /* This file is part of the KDE project Copyright (C) 2018 Gregor Mi 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 "tabswitcherfilesmodel.h" #include #include namespace detail { /** * adpated from https://helloacm.com/c-coding-exercise-longest-common-prefix/ * see also http://www.cplusplus.com/forum/beginner/83540/ * Note that if strs contains the empty string, the result will be "" */ - QString longestCommonPrefix(std::vector& strs) { + QString longestCommonPrefix(std::vector const & strs) { int n = INT_MAX; if (strs.size() <= 0) { return QString(); } if (strs.size() == 1) { return strs[0]; } // get the min length for (size_t i = 0; i < strs.size(); i++) { n = strs[i].length() < n ? strs[i].length() : n; } for (int c = 0; c < n; c++) { // check each character for (size_t i = 1; i < strs.size(); i++) { if (strs[i][c] != strs[i - 1][c]) { // we find a mis-match return QStringRef(&strs[0], 0, c).toString(); } } } // prefix is n-length return QStringRef(&strs[0], 0, n).toString(); } void post_process(FilenameList & data) { std::vector paths; std::for_each(data.begin(), data.end(), [&paths](FilenameListItem & item) { paths.push_back(item.fullPath); }); // Removes empty strings because Documents without file have no path and we would // otherwise in this case always get "" paths.erase( // erase-remove idiom, see https://en.cppreference.com/w/cpp/algorithm/remove std::remove_if(paths.begin(), paths.end(), [](QString s) { return s.isEmpty(); }), paths.end() ); QString prefix = longestCommonPrefix(paths); int prefix_length = prefix.length(); if (prefix_length == 1) { // if there is only the "/" at the beginning, then keep it prefix_length = 0; } std::for_each(data.begin(), data.end(), [prefix_length](FilenameListItem & item) { // cut prefix (left side) and cut document name (plus slash) on the right side int len = item.fullPath.length() - prefix_length - item.documentName.length() - 1; if (len > 0) { // only assign in case item.fullPath is not empty // "PREFIXPATH/REMAININGPATH/DOCUMENTNAME" --> "REMAININGPATH" item.displayPathPrefix = QStringRef(&item.fullPath, prefix_length, len).toString(); } }); } } detail::TabswitcherFilesModel::TabswitcherFilesModel(QObject *parent) : QAbstractTableModel(parent) { } detail::TabswitcherFilesModel::TabswitcherFilesModel(const FilenameList & data) { data_ = data; post_process(data_); } bool detail::TabswitcherFilesModel::insertRow(int row, FilenameListItem const * const item) { beginInsertRows(QModelIndex(), row, row + 1); data_.insert(data_.begin() + row, *item); post_process(data_); endInsertRows(); return true; } bool detail::TabswitcherFilesModel::removeRow(int row) { if (data_.begin() + row == data_.end()) { return false; } beginRemoveRows(QModelIndex(), row, row + 1); data_.erase(data_.begin() + row); post_process(data_); endRemoveRows(); return true; } int detail::TabswitcherFilesModel::rowCount() const { return data_.size(); } detail::FilenameListItem * detail::TabswitcherFilesModel::item(int row) const { return const_cast(&data_[row]); } void detail::TabswitcherFilesModel::updateItem(FilenameListItem * item, QString const & documentName, QString const & fullPath) { item->documentName = documentName; item->fullPath = fullPath; post_process(data_); } int detail::TabswitcherFilesModel::columnCount(const QModelIndex & parent) const { Q_UNUSED(parent); return 2; } int detail::TabswitcherFilesModel::rowCount(const QModelIndex & parent) const { Q_UNUSED(parent); return data_.size(); } QVariant detail::TabswitcherFilesModel::data(const QModelIndex & index, int role) const { if (role == Qt::DisplayRole) { const auto & row = data_[index.row()]; if (index.column() == 0) return row.displayPathPrefix; else return row.documentName; } else if (role == Qt::DecorationRole) { if (index.column() == 1) { const auto & row = data_[index.row()]; return row.icon; } } else if (role == Qt::ToolTipRole) { const auto & row = data_[index.row()]; return row.fullPath; } else if (role == Qt::TextAlignmentRole) { if (index.column() == 0) return Qt::AlignRight + Qt::AlignVCenter; else return Qt::AlignVCenter; } else if (role == Qt::ForegroundRole) { if (index.column() == 0) return QBrush(Qt::darkGray); else return QVariant(); } return QVariant(); } diff --git a/addons/tabswitcher/tabswitcherfilesmodel.h b/addons/tabswitcher/tabswitcherfilesmodel.h index d98bab970..ae525efd8 100644 --- a/addons/tabswitcher/tabswitcherfilesmodel.h +++ b/addons/tabswitcher/tabswitcherfilesmodel.h @@ -1,85 +1,85 @@ /* This file is part of the KDE project Copyright (C) 2018 Gregor Mi 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. */ #pragma once #include #include #include namespace detail { /** * The implementation is close to QStandardItem but not all aspects are supported. * Probably it would be better not to derive from QStandardItem. */ struct FilenameListItem : public QStandardItem { FilenameListItem(const QIcon & icon, const QString & documentName, const QString & fullPath) { this->icon = icon; this->documentName = documentName; this->fullPath = fullPath; } QIcon icon; QString documentName; QString fullPath; /** * calculated from documentName and fullPath */ QString displayPathPrefix; }; using FilenameList = std::vector; class TabswitcherFilesModel : public QAbstractTableModel { Q_OBJECT public: explicit TabswitcherFilesModel(QObject *parent = nullptr); virtual ~TabswitcherFilesModel() = default; TabswitcherFilesModel(const FilenameList & data); bool insertRow(int row, FilenameListItem const * const item); bool removeRow(int row); int rowCount() const; /** * NOTE: The returned pointer will become invalid as soon as the underlying vector changes. */ FilenameListItem * item(int row) const; /* * Use this method to update an item. * NOTE: This could be improved if we allow KTextEditor::Document to go into this interface. * Then we could search and update by KTextEditor::Document. */ void updateItem(FilenameListItem * item, QString const & documentName, QString const & fullPath); protected: int columnCount(const QModelIndex & parent) const override; int rowCount(const QModelIndex & parent) const override; QVariant data(const QModelIndex & index, int role) const override; private: FilenameList data_; }; -QString longestCommonPrefix(std::vector& strs); +QString longestCommonPrefix(std::vector const & strs); }