diff --git a/CMakeLists.txt b/CMakeLists.txt
index 52527312fba426829eec15bde42ad8a653037deb..bd42938a0ac63ce83b77ba6cf29842faa7e9dac8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -283,6 +283,7 @@ set(SRC_FILES
 	src/ColorImageProvider.cpp
 	src/CommunitiesList.cpp
 	src/CommunitiesListItem.cpp
+	src/CompletionProxyModel.cpp
 	src/DeviceVerificationFlow.cpp
 	src/EventAccessors.cpp
 	src/InviteeItem.cpp
diff --git a/src/CompletionProxyModel.cpp b/src/CompletionProxyModel.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c520bb685230b1e14cc5b2ce257820dfdfdc8663
--- /dev/null
+++ b/src/CompletionProxyModel.cpp
@@ -0,0 +1,133 @@
+#include "CompletionProxyModel.h"
+
+#include <QRegularExpression>
+
+#include "CompletionModelRoles.h"
+#include "Logging.h"
+#include "Utils.h"
+
+CompletionProxyModel::CompletionProxyModel(QAbstractItemModel *model, QObject *parent)
+  : QAbstractProxyModel(parent)
+{
+        setSourceModel(model);
+        QRegularExpression splitPoints("\\s+|-");
+
+        for (int i = 0; i < sourceModel()->rowCount(); i++) {
+                if (i < 7)
+                        mapping.push_back(i);
+
+                auto string1 = sourceModel()
+                                 ->data(sourceModel()->index(i, 0), CompletionModel::SearchRole)
+                                 .toString()
+                                 .toLower();
+                trie_.insert(string1.toUcs4(), i);
+
+                for (const auto &e : string1.split(splitPoints, Qt::SkipEmptyParts)) {
+                        trie_.insert(e.toUcs4(), i);
+                }
+
+                auto string2 = sourceModel()
+                                 ->data(sourceModel()->index(i, 0), CompletionModel::SearchRole2)
+                                 .toString()
+                                 .toLower();
+
+                if (!string2.isEmpty()) {
+                        trie_.insert(string2.toUcs4(), i);
+                        for (const auto &e : string2.split(splitPoints, Qt::SkipEmptyParts)) {
+                                trie_.insert(e.toUcs4(), i);
+                        }
+                }
+        }
+
+        connect(
+          this,
+          &CompletionProxyModel::newSearchString,
+          this,
+          [this](QString s) {
+                  s.remove(":");
+                  s.remove("@");
+                  searchString = s.toLower();
+                  invalidate();
+          },
+          Qt::QueuedConnection);
+}
+
+void
+CompletionProxyModel::invalidate()
+{
+        auto key = searchString.toUcs4();
+        beginResetModel();
+        mapping = trie_.search(key, 7);
+        endResetModel();
+
+        std::string temp;
+        for (auto v : mapping) {
+                temp += std::to_string(v) + ", ";
+        }
+        nhlog::ui()->debug("mapping: {}", temp);
+}
+
+QHash<int, QByteArray>
+CompletionProxyModel::roleNames() const
+{
+        return this->sourceModel()->roleNames();
+}
+
+int
+CompletionProxyModel::rowCount(const QModelIndex &) const
+{
+        return (int)mapping.size();
+}
+
+QModelIndex
+CompletionProxyModel::mapFromSource(const QModelIndex &sourceIndex) const
+{
+        for (int i = 0; i < (int)mapping.size(); i++) {
+                if (mapping[i] == sourceIndex.row()) {
+                        return index(i, 0);
+                }
+        }
+        return QModelIndex();
+}
+
+QModelIndex
+CompletionProxyModel::mapToSource(const QModelIndex &proxyIndex) const
+{
+        auto row = proxyIndex.row();
+        if (row < 0 || row >= (int)mapping.size())
+                return QModelIndex();
+
+        return sourceModel()->index(mapping[row], 0);
+}
+
+QModelIndex
+CompletionProxyModel::index(int row, int column, const QModelIndex &) const
+{
+        return createIndex(row, column);
+}
+
+QModelIndex
+CompletionProxyModel::parent(const QModelIndex &) const
+{
+        return QModelIndex{};
+}
+int
+CompletionProxyModel::columnCount(const QModelIndex &) const
+{
+        return sourceModel()->columnCount();
+}
+
+QVariant
+CompletionProxyModel::completionAt(int i) const
+{
+        if (i >= 0 && i < rowCount())
+                return data(index(i, 0), CompletionModel::CompletionRole);
+        else
+                return {};
+}
+
+void
+CompletionProxyModel::setSearchString(QString s)
+{
+        emit newSearchString(s);
+}
diff --git a/src/CompletionProxyModel.h b/src/CompletionProxyModel.h
index f4ec6a96d4cc5faa552e031b4d9d93a027d95b9d..1517505fe88067073482f3ec2a7a866849be2794 100644
--- a/src/CompletionProxyModel.h
+++ b/src/CompletionProxyModel.h
@@ -3,11 +3,6 @@
 // Class for showing a limited amount of completions at a time
 
 #include <QAbstractProxyModel>
-#include <QRegularExpression>
-
-#include "CompletionModelRoles.h"
-#include "Logging.h"
-#include "Utils.h"
 
 template<typename Key, typename Value>
 struct trie
@@ -133,120 +128,26 @@ class CompletionProxyModel : public QAbstractProxyModel
         Q_OBJECT
 
 public:
-        CompletionProxyModel(QAbstractItemModel *model, QObject *parent = nullptr)
-          : QAbstractProxyModel(parent)
-        {
-                setSourceModel(model);
-                QRegularExpression splitPoints("\\s+|-");
-
-                for (int i = 0; i < sourceModel()->rowCount(); i++) {
-                        if (i < 7)
-                                mapping.push_back(i);
-
-                        auto string1 =
-                          sourceModel()
-                            ->data(sourceModel()->index(i, 0), CompletionModel::SearchRole)
-                            .toString()
-                            .toLower();
-                        trie_.insert(string1.toUcs4(), i);
-
-                        for (const auto &e : string1.split(splitPoints, Qt::SkipEmptyParts)) {
-                                trie_.insert(e.toUcs4(), i);
-                        }
+        CompletionProxyModel(QAbstractItemModel *model, QObject *parent = nullptr);
 
-                        auto string2 =
-                          sourceModel()
-                            ->data(sourceModel()->index(i, 0), CompletionModel::SearchRole2)
-                            .toString()
-                            .toLower();
-
-                        if (!string2.isEmpty()) {
-                                trie_.insert(string2.toUcs4(), i);
-                                for (const auto &e :
-                                     string2.split(splitPoints, Qt::SkipEmptyParts)) {
-                                        trie_.insert(e.toUcs4(), i);
-                                }
-                        }
-                }
+        void invalidate();
 
-                connect(
-                  this,
-                  &CompletionProxyModel::newSearchString,
-                  this,
-                  [this](QString s) {
-                          s.remove(":");
-                          s.remove("@");
-                          searchString = s.toLower();
-                          invalidate();
-                  },
-                  Qt::QueuedConnection);
-        }
+        QHash<int, QByteArray> roleNames() const override;
+        int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+        int columnCount(const QModelIndex &) const override;
 
-        void invalidate()
-        {
-                auto key = searchString.toUcs4();
-                beginResetModel();
-                mapping = trie_.search(key, 7);
-                endResetModel();
-
-                std::string temp;
-                for (auto v : mapping) {
-                        temp += std::to_string(v) + ", ";
-                }
-                nhlog::ui()->debug("mapping: {}", temp);
-        };
-
-        QHash<int, QByteArray> roleNames() const override
-        {
-                return this->sourceModel()->roleNames();
-        }
-
-        int rowCount(const QModelIndex &parent = QModelIndex()) const override
-        {
-                (void)parent;
-                return (int)mapping.size();
-        }
-
-        QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override
-        {
-                for (int i = 0; i < (int)mapping.size(); i++) {
-                        if (mapping[i] == sourceIndex.row()) {
-                                return index(i, 0);
-                        }
-                }
-                return QModelIndex();
-        }
-
-        QModelIndex mapToSource(const QModelIndex &proxyIndex) const override
-        {
-                auto row = proxyIndex.row();
-                if (row < 0 || row >= (int)mapping.size())
-                        return QModelIndex();
-
-                return sourceModel()->index(mapping[row], 0);
-        }
+        QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override;
+        QModelIndex mapToSource(const QModelIndex &proxyIndex) const override;
 
         QModelIndex index(int row,
                           int column,
-                          const QModelIndex &parent = QModelIndex()) const override
-        {
-                (void)parent;
-                return createIndex(row, column);
-        }
-
-        QModelIndex parent(const QModelIndex &) const override { return QModelIndex{}; }
-        int columnCount(const QModelIndex &) const override { return sourceModel()->columnCount(); }
+                          const QModelIndex &parent = QModelIndex()) const override;
+        QModelIndex parent(const QModelIndex &) const override;
 
 public slots:
-        QVariant completionAt(int i) const
-        {
-                if (i >= 0 && i < rowCount())
-                        return data(index(i, 0), CompletionModel::CompletionRole);
-                else
-                        return {};
-        }
+        QVariant completionAt(int i) const;
 
-        void setSearchString(QString s) { emit newSearchString(s); }
+        void setSearchString(QString s);
 
 signals:
         void newSearchString(QString);