From f191de25147e1f941cc1fa03ac515a30222e0176 Mon Sep 17 00:00:00 2001
From: pajlada <rasmus.karlsson@pajlada.com>
Date: Sun, 18 Oct 2020 15:54:48 +0200
Subject: [PATCH] Add CI workflow to check line endings of all source files
 (#2082)

In addition, all found errors (formatting & line ending) have been fixed in this PR.
---
 .github/workflows/check-formatting.yml        |  13 +-
 README.md                                     |   2 +-
 src/common/ChannelChatters.cpp                | 144 ++---
 src/common/ChannelChatters.hpp                |  74 +--
 src/common/Credentials.cpp                    | 470 ++++++++--------
 src/common/Credentials.hpp                    |  46 +-
 src/providers/irc/Irc2.cpp                    | 520 +++++++++---------
 src/providers/irc/Irc2.hpp                    | 134 ++---
 src/providers/irc/IrcCommands.cpp             | 152 ++---
 src/providers/irc/IrcCommands.hpp             |  24 +-
 src/providers/twitch/IrcMessageHandler.cpp    |   2 +-
 src/providers/twitch/TwitchMessageBuilder.hpp |   2 +-
 src/util/Overloaded.hpp                       |  26 +-
 src/widgets/dialogs/IrcConnectionEditor.cpp   | 194 +++----
 src/widgets/dialogs/IrcConnectionEditor.hpp   |  64 +--
 tools/check-format.sh                         |   4 +-
 tools/check-line-endings.sh                   |  22 +
 17 files changed, 959 insertions(+), 934 deletions(-)
 create mode 100755 tools/check-line-endings.sh

diff --git a/.github/workflows/check-formatting.yml b/.github/workflows/check-formatting.yml
index 4d2659a33..4759c27db 100644
--- a/.github/workflows/check-formatting.yml
+++ b/.github/workflows/check-formatting.yml
@@ -8,18 +8,19 @@ on:
   pull_request:
 
 jobs:
-  build:
-    runs-on: ubuntu-latest
-    container:
-      image: ubuntu:19.10
+  check:
+    runs-on: ubuntu-20.04
 
     steps:
     - uses: actions/checkout@v2.3.3
     - name: apt-get update
-      run: apt-get update
+      run: sudo apt-get update
 
     - name: Install clang-format
-      run: apt-get -y install clang-format
+      run: sudo apt-get -y install clang-format dos2unix
 
     - name: Check formatting
       run: ./tools/check-format.sh
+
+    - name: Check line-endings
+      run: ./tools/check-line-endings.sh
diff --git a/README.md b/README.md
index 87c685b8b..798a72175 100644
--- a/README.md
+++ b/README.md
@@ -41,7 +41,7 @@ git submodule update --init --recursive
 The code is formatted using clang format in Qt Creator. [.clang-format](src/.clang-format) contains the style file for clang format.
 
 ### Get it automated with QT Creator + Beautifier + Clang Format
-1. Download LLVM: https://releases.llvm.org/9.0.0/LLVM-9.0.0-win64.exe
+1. Download LLVM: https://github.com/llvm/llvm-project/releases/download/llvmorg-11.0.0/LLVM-11.0.0-win64.exe
 2. During the installation, make sure to add it to your path
 3. In QT Creator, select `Help` > `About Plugins` > `C++` > `Beautifier` to enable the plugin
 4. Restart QT Creator
diff --git a/src/common/ChannelChatters.cpp b/src/common/ChannelChatters.cpp
index 396c74aa8..ea50bee58 100644
--- a/src/common/ChannelChatters.cpp
+++ b/src/common/ChannelChatters.cpp
@@ -1,72 +1,72 @@
-#include "ChannelChatters.hpp"
-
-#include "messages/Message.hpp"
-#include "messages/MessageBuilder.hpp"
-
-namespace chatterino {
-
-ChannelChatters::ChannelChatters(Channel &channel)
-    : channel_(channel)
-{
-}
-
-AccessGuard<const UsernameSet> ChannelChatters::accessChatters() const
-{
-    return this->chatters_.accessConst();
-}
-
-void ChannelChatters::addRecentChatter(const QString &user)
-{
-    this->chatters_.access()->insert(user);
-}
-
-void ChannelChatters::addJoinedUser(const QString &user)
-{
-    auto joinedUsers = this->joinedUsers_.access();
-    joinedUsers->append(user);
-
-    if (!this->joinedUsersMergeQueued_)
-    {
-        this->joinedUsersMergeQueued_ = true;
-
-        QTimer::singleShot(500, &this->lifetimeGuard_, [this] {
-            auto joinedUsers = this->joinedUsers_.access();
-
-            MessageBuilder builder(systemMessage,
-                                   "Users joined: " + joinedUsers->join(", "));
-            builder->flags.set(MessageFlag::Collapsed);
-            joinedUsers->clear();
-            this->channel_.addMessage(builder.release());
-            this->joinedUsersMergeQueued_ = false;
-        });
-    }
-}
-
-void ChannelChatters::addPartedUser(const QString &user)
-{
-    auto partedUsers = this->partedUsers_.access();
-    partedUsers->append(user);
-
-    if (!this->partedUsersMergeQueued_)
-    {
-        this->partedUsersMergeQueued_ = true;
-
-        QTimer::singleShot(500, &this->lifetimeGuard_, [this] {
-            auto partedUsers = this->partedUsers_.access();
-
-            MessageBuilder builder(systemMessage,
-                                   "Users parted: " + partedUsers->join(", "));
-            builder->flags.set(MessageFlag::Collapsed);
-            this->channel_.addMessage(builder.release());
-            partedUsers->clear();
-
-            this->partedUsersMergeQueued_ = false;
-        });
-    }
-}
-void ChannelChatters::setChatters(UsernameSet &&set)
-{
-    *this->chatters_.access() = set;
-}
-
-}  // namespace chatterino
+#include "ChannelChatters.hpp"
+
+#include "messages/Message.hpp"
+#include "messages/MessageBuilder.hpp"
+
+namespace chatterino {
+
+ChannelChatters::ChannelChatters(Channel &channel)
+    : channel_(channel)
+{
+}
+
+AccessGuard<const UsernameSet> ChannelChatters::accessChatters() const
+{
+    return this->chatters_.accessConst();
+}
+
+void ChannelChatters::addRecentChatter(const QString &user)
+{
+    this->chatters_.access()->insert(user);
+}
+
+void ChannelChatters::addJoinedUser(const QString &user)
+{
+    auto joinedUsers = this->joinedUsers_.access();
+    joinedUsers->append(user);
+
+    if (!this->joinedUsersMergeQueued_)
+    {
+        this->joinedUsersMergeQueued_ = true;
+
+        QTimer::singleShot(500, &this->lifetimeGuard_, [this] {
+            auto joinedUsers = this->joinedUsers_.access();
+
+            MessageBuilder builder(systemMessage,
+                                   "Users joined: " + joinedUsers->join(", "));
+            builder->flags.set(MessageFlag::Collapsed);
+            joinedUsers->clear();
+            this->channel_.addMessage(builder.release());
+            this->joinedUsersMergeQueued_ = false;
+        });
+    }
+}
+
+void ChannelChatters::addPartedUser(const QString &user)
+{
+    auto partedUsers = this->partedUsers_.access();
+    partedUsers->append(user);
+
+    if (!this->partedUsersMergeQueued_)
+    {
+        this->partedUsersMergeQueued_ = true;
+
+        QTimer::singleShot(500, &this->lifetimeGuard_, [this] {
+            auto partedUsers = this->partedUsers_.access();
+
+            MessageBuilder builder(systemMessage,
+                                   "Users parted: " + partedUsers->join(", "));
+            builder->flags.set(MessageFlag::Collapsed);
+            this->channel_.addMessage(builder.release());
+            partedUsers->clear();
+
+            this->partedUsersMergeQueued_ = false;
+        });
+    }
+}
+void ChannelChatters::setChatters(UsernameSet &&set)
+{
+    *this->chatters_.access() = set;
+}
+
+}  // namespace chatterino
diff --git a/src/common/ChannelChatters.hpp b/src/common/ChannelChatters.hpp
index a0c5896e1..2aba51da8 100644
--- a/src/common/ChannelChatters.hpp
+++ b/src/common/ChannelChatters.hpp
@@ -1,37 +1,37 @@
-#pragma once
-
-#include "common/Channel.hpp"
-#include "common/UniqueAccess.hpp"
-#include "common/UsernameSet.hpp"
-
-namespace chatterino {
-
-class ChannelChatters
-{
-public:
-    ChannelChatters(Channel &channel);
-    virtual ~ChannelChatters() = default;  // add vtable
-
-    AccessGuard<const UsernameSet> accessChatters() const;
-
-    void addRecentChatter(const QString &user);
-    void addJoinedUser(const QString &user);
-    void addPartedUser(const QString &user);
-    void setChatters(UsernameSet &&set);
-
-private:
-    Channel &channel_;
-
-    // maps 2 char prefix to set of names
-    UniqueAccess<UsernameSet> chatters_;
-
-    // combines multiple joins/parts into one message
-    UniqueAccess<QStringList> joinedUsers_;
-    bool joinedUsersMergeQueued_ = false;
-    UniqueAccess<QStringList> partedUsers_;
-    bool partedUsersMergeQueued_ = false;
-
-    QObject lifetimeGuard_;
-};
-
-}  // namespace chatterino
+#pragma once
+
+#include "common/Channel.hpp"
+#include "common/UniqueAccess.hpp"
+#include "common/UsernameSet.hpp"
+
+namespace chatterino {
+
+class ChannelChatters
+{
+public:
+    ChannelChatters(Channel &channel);
+    virtual ~ChannelChatters() = default;  // add vtable
+
+    AccessGuard<const UsernameSet> accessChatters() const;
+
+    void addRecentChatter(const QString &user);
+    void addJoinedUser(const QString &user);
+    void addPartedUser(const QString &user);
+    void setChatters(UsernameSet &&set);
+
+private:
+    Channel &channel_;
+
+    // maps 2 char prefix to set of names
+    UniqueAccess<UsernameSet> chatters_;
+
+    // combines multiple joins/parts into one message
+    UniqueAccess<QStringList> joinedUsers_;
+    bool joinedUsersMergeQueued_ = false;
+    UniqueAccess<QStringList> partedUsers_;
+    bool partedUsersMergeQueued_ = false;
+
+    QObject lifetimeGuard_;
+};
+
+}  // namespace chatterino
diff --git a/src/common/Credentials.cpp b/src/common/Credentials.cpp
index 87abecb98..0812fb039 100644
--- a/src/common/Credentials.cpp
+++ b/src/common/Credentials.cpp
@@ -1,235 +1,235 @@
-#include "Credentials.hpp"
-
-#include "debug/AssertInGuiThread.hpp"
-#include "keychain.h"
-#include "singletons/Paths.hpp"
-#include "singletons/Settings.hpp"
-#include "util/CombinePath.hpp"
-#include "util/Overloaded.hpp"
-
-#include <QSaveFile>
-#include <boost/variant.hpp>
-
-#define FORMAT_NAME                                                  \
-    ([&] {                                                           \
-        assert(!provider.contains(":"));                             \
-        return QString("chatterino:%1:%2").arg(provider).arg(name_); \
-    })()
-
-namespace chatterino {
-
-namespace {
-    bool useKeyring()
-    {
-        if (getPaths()->isPortable())
-        {
-            return false;
-        }
-        else
-        {
-#ifdef Q_OS_LINUX
-            return getSettings()->useKeyring;
-#else
-            return true;
-#endif
-        }
-    }
-
-    // Insecure storage:
-    QString insecurePath()
-    {
-        return combinePath(getPaths()->settingsDirectory, "credentials.json");
-    }
-
-    QJsonDocument loadInsecure()
-    {
-        QFile file(insecurePath());
-        file.open(QIODevice::ReadOnly);
-        return QJsonDocument::fromJson(file.readAll());
-    }
-
-    void storeInsecure(const QJsonDocument &doc)
-    {
-        QSaveFile file(insecurePath());
-        file.open(QIODevice::WriteOnly);
-        file.write(doc.toJson());
-        file.commit();
-    }
-
-    QJsonDocument &insecureInstance()
-    {
-        static auto store = loadInsecure();
-        return store;
-    }
-
-    void queueInsecureSave()
-    {
-        static bool isQueued = false;
-
-        if (!isQueued)
-        {
-            isQueued = true;
-            QTimer::singleShot(200, qApp, [] {
-                storeInsecure(insecureInstance());
-                isQueued = false;
-            });
-        }
-    }
-
-    // QKeychain runs jobs asyncronously, so we have to assure that set/erase
-    // jobs gets executed in order.
-    struct SetJob {
-        QString name;
-        QString credential;
-    };
-
-    struct EraseJob {
-        QString name;
-    };
-
-    using Job = boost::variant<SetJob, EraseJob>;
-
-    static std::queue<Job> &jobQueue()
-    {
-        static std::queue<Job> jobs;
-        return jobs;
-    }
-
-    static void runNextJob()
-    {
-        auto &&queue = jobQueue();
-
-        if (!queue.empty())
-        {
-            // we were gonna use std::visit here but macos is shit
-
-            auto &&item = queue.front();
-
-            if (item.which() == 0)  // set job
-            {
-                auto set = boost::get<SetJob>(item);
-                auto job = new QKeychain::WritePasswordJob("chatterino");
-                job->setAutoDelete(true);
-                job->setKey(set.name);
-                job->setTextData(set.credential);
-                QObject::connect(job, &QKeychain::Job::finished, qApp,
-                                 [](auto) { runNextJob(); });
-                job->start();
-            }
-            else  // erase job
-            {
-                auto erase = boost::get<EraseJob>(item);
-                auto job = new QKeychain::DeletePasswordJob("chatterino");
-                job->setAutoDelete(true);
-                job->setKey(erase.name);
-                QObject::connect(job, &QKeychain::Job::finished, qApp,
-                                 [](auto) { runNextJob(); });
-                job->start();
-            }
-
-            queue.pop();
-        }
-    }
-
-    static void queueJob(Job &&job)
-    {
-        auto &&queue = jobQueue();
-
-        queue.push(std::move(job));
-        if (queue.size() == 1)
-        {
-            runNextJob();
-        }
-    }
-}  // namespace
-
-Credentials &Credentials::instance()
-{
-    static Credentials creds;
-    return creds;
-}
-
-Credentials::Credentials()
-{
-}
-
-void Credentials::get(const QString &provider, const QString &name_,
-                      QObject *receiver,
-                      std::function<void(const QString &)> &&onLoaded)
-{
-    assertInGuiThread();
-
-    auto name = FORMAT_NAME;
-
-    if (useKeyring())
-    {
-        auto job = new QKeychain::ReadPasswordJob("chatterino");
-        job->setAutoDelete(true);
-        job->setKey(name);
-        QObject::connect(
-            job, &QKeychain::Job::finished, receiver,
-            [job, onLoaded = std::move(onLoaded)](auto) mutable {
-                onLoaded(job->textData());
-            },
-            Qt::DirectConnection);
-        job->start();
-    }
-    else
-    {
-        auto &instance = insecureInstance();
-
-        onLoaded(instance.object().find(name).value().toString());
-    }
-}
-
-void Credentials::set(const QString &provider, const QString &name_,
-                      const QString &credential)
-{
-    assertInGuiThread();
-
-    /// On linux, we try to use a keychain but show a message to disable it when it fails.
-    /// XXX: add said message
-
-    auto name = FORMAT_NAME;
-
-    if (useKeyring())
-    {
-        queueJob(SetJob{name, credential});
-    }
-    else
-    {
-        auto &instance = insecureInstance();
-
-        auto obj = instance.object();
-        obj[name] = credential;
-        instance.setObject(obj);
-
-        queueInsecureSave();
-    }
-}
-
-void Credentials::erase(const QString &provider, const QString &name_)
-{
-    assertInGuiThread();
-
-    auto name = FORMAT_NAME;
-
-    if (useKeyring())
-    {
-        queueJob(EraseJob{name});
-    }
-    else
-    {
-        auto &instance = insecureInstance();
-
-        if (auto it = instance.object().find(name);
-            it != instance.object().end())
-        {
-            instance.object().erase(it);
-        }
-
-        queueInsecureSave();
-    }
-}
-
-}  // namespace chatterino
+#include "Credentials.hpp"
+
+#include "debug/AssertInGuiThread.hpp"
+#include "keychain.h"
+#include "singletons/Paths.hpp"
+#include "singletons/Settings.hpp"
+#include "util/CombinePath.hpp"
+#include "util/Overloaded.hpp"
+
+#include <QSaveFile>
+#include <boost/variant.hpp>
+
+#define FORMAT_NAME                                                  \
+    ([&] {                                                           \
+        assert(!provider.contains(":"));                             \
+        return QString("chatterino:%1:%2").arg(provider).arg(name_); \
+    })()
+
+namespace chatterino {
+
+namespace {
+    bool useKeyring()
+    {
+        if (getPaths()->isPortable())
+        {
+            return false;
+        }
+        else
+        {
+#ifdef Q_OS_LINUX
+            return getSettings()->useKeyring;
+#else
+            return true;
+#endif
+        }
+    }
+
+    // Insecure storage:
+    QString insecurePath()
+    {
+        return combinePath(getPaths()->settingsDirectory, "credentials.json");
+    }
+
+    QJsonDocument loadInsecure()
+    {
+        QFile file(insecurePath());
+        file.open(QIODevice::ReadOnly);
+        return QJsonDocument::fromJson(file.readAll());
+    }
+
+    void storeInsecure(const QJsonDocument &doc)
+    {
+        QSaveFile file(insecurePath());
+        file.open(QIODevice::WriteOnly);
+        file.write(doc.toJson());
+        file.commit();
+    }
+
+    QJsonDocument &insecureInstance()
+    {
+        static auto store = loadInsecure();
+        return store;
+    }
+
+    void queueInsecureSave()
+    {
+        static bool isQueued = false;
+
+        if (!isQueued)
+        {
+            isQueued = true;
+            QTimer::singleShot(200, qApp, [] {
+                storeInsecure(insecureInstance());
+                isQueued = false;
+            });
+        }
+    }
+
+    // QKeychain runs jobs asyncronously, so we have to assure that set/erase
+    // jobs gets executed in order.
+    struct SetJob {
+        QString name;
+        QString credential;
+    };
+
+    struct EraseJob {
+        QString name;
+    };
+
+    using Job = boost::variant<SetJob, EraseJob>;
+
+    static std::queue<Job> &jobQueue()
+    {
+        static std::queue<Job> jobs;
+        return jobs;
+    }
+
+    static void runNextJob()
+    {
+        auto &&queue = jobQueue();
+
+        if (!queue.empty())
+        {
+            // we were gonna use std::visit here but macos is shit
+
+            auto &&item = queue.front();
+
+            if (item.which() == 0)  // set job
+            {
+                auto set = boost::get<SetJob>(item);
+                auto job = new QKeychain::WritePasswordJob("chatterino");
+                job->setAutoDelete(true);
+                job->setKey(set.name);
+                job->setTextData(set.credential);
+                QObject::connect(job, &QKeychain::Job::finished, qApp,
+                                 [](auto) { runNextJob(); });
+                job->start();
+            }
+            else  // erase job
+            {
+                auto erase = boost::get<EraseJob>(item);
+                auto job = new QKeychain::DeletePasswordJob("chatterino");
+                job->setAutoDelete(true);
+                job->setKey(erase.name);
+                QObject::connect(job, &QKeychain::Job::finished, qApp,
+                                 [](auto) { runNextJob(); });
+                job->start();
+            }
+
+            queue.pop();
+        }
+    }
+
+    static void queueJob(Job &&job)
+    {
+        auto &&queue = jobQueue();
+
+        queue.push(std::move(job));
+        if (queue.size() == 1)
+        {
+            runNextJob();
+        }
+    }
+}  // namespace
+
+Credentials &Credentials::instance()
+{
+    static Credentials creds;
+    return creds;
+}
+
+Credentials::Credentials()
+{
+}
+
+void Credentials::get(const QString &provider, const QString &name_,
+                      QObject *receiver,
+                      std::function<void(const QString &)> &&onLoaded)
+{
+    assertInGuiThread();
+
+    auto name = FORMAT_NAME;
+
+    if (useKeyring())
+    {
+        auto job = new QKeychain::ReadPasswordJob("chatterino");
+        job->setAutoDelete(true);
+        job->setKey(name);
+        QObject::connect(
+            job, &QKeychain::Job::finished, receiver,
+            [job, onLoaded = std::move(onLoaded)](auto) mutable {
+                onLoaded(job->textData());
+            },
+            Qt::DirectConnection);
+        job->start();
+    }
+    else
+    {
+        auto &instance = insecureInstance();
+
+        onLoaded(instance.object().find(name).value().toString());
+    }
+}
+
+void Credentials::set(const QString &provider, const QString &name_,
+                      const QString &credential)
+{
+    assertInGuiThread();
+
+    /// On linux, we try to use a keychain but show a message to disable it when it fails.
+    /// XXX: add said message
+
+    auto name = FORMAT_NAME;
+
+    if (useKeyring())
+    {
+        queueJob(SetJob{name, credential});
+    }
+    else
+    {
+        auto &instance = insecureInstance();
+
+        auto obj = instance.object();
+        obj[name] = credential;
+        instance.setObject(obj);
+
+        queueInsecureSave();
+    }
+}
+
+void Credentials::erase(const QString &provider, const QString &name_)
+{
+    assertInGuiThread();
+
+    auto name = FORMAT_NAME;
+
+    if (useKeyring())
+    {
+        queueJob(EraseJob{name});
+    }
+    else
+    {
+        auto &instance = insecureInstance();
+
+        if (auto it = instance.object().find(name);
+            it != instance.object().end())
+        {
+            instance.object().erase(it);
+        }
+
+        queueInsecureSave();
+    }
+}
+
+}  // namespace chatterino
diff --git a/src/common/Credentials.hpp b/src/common/Credentials.hpp
index 5cf9da920..260109f6a 100644
--- a/src/common/Credentials.hpp
+++ b/src/common/Credentials.hpp
@@ -1,23 +1,23 @@
-#pragma once
-
-#include <QString>
-#include <functional>
-
-namespace chatterino {
-
-class Credentials
-{
-public:
-    static Credentials &instance();
-
-    void get(const QString &provider, const QString &name, QObject *receiver,
-             std::function<void(const QString &)> &&onLoaded);
-    void set(const QString &provider, const QString &name,
-             const QString &credential);
-    void erase(const QString &provider, const QString &name);
-
-private:
-    Credentials();
-};
-
-}  // namespace chatterino
+#pragma once
+
+#include <QString>
+#include <functional>
+
+namespace chatterino {
+
+class Credentials
+{
+public:
+    static Credentials &instance();
+
+    void get(const QString &provider, const QString &name, QObject *receiver,
+             std::function<void(const QString &)> &&onLoaded);
+    void set(const QString &provider, const QString &name,
+             const QString &credential);
+    void erase(const QString &provider, const QString &name);
+
+private:
+    Credentials();
+};
+
+}  // namespace chatterino
diff --git a/src/providers/irc/Irc2.cpp b/src/providers/irc/Irc2.cpp
index 2f685be63..1876c383a 100644
--- a/src/providers/irc/Irc2.cpp
+++ b/src/providers/irc/Irc2.cpp
@@ -1,260 +1,260 @@
-#include "Irc2.hpp"
-
-#include <pajlada/serialize.hpp>
-#include "common/Credentials.hpp"
-#include "common/SignalVectorModel.hpp"
-#include "singletons/Paths.hpp"
-#include "util/CombinePath.hpp"
-#include "util/RapidjsonHelpers.hpp"
-#include "util/StandardItemHelper.hpp"
-
-#include <QSaveFile>
-#include <QtConcurrent>
-
-namespace chatterino {
-
-namespace {
-    QString configPath()
-    {
-        return combinePath(getPaths()->settingsDirectory, "irc.json");
-    }
-
-    class Model : public SignalVectorModel<IrcServerData>
-    {
-    public:
-        Model(QObject *parent)
-            : SignalVectorModel<IrcServerData>(6, parent)
-        {
-        }
-
-        // turn a vector item into a model row
-        IrcServerData getItemFromRow(std::vector<QStandardItem *> &row,
-                                     const IrcServerData &original)
-        {
-            return IrcServerData{
-                row[0]->data(Qt::EditRole).toString(),      // host
-                row[1]->data(Qt::EditRole).toInt(),         // port
-                row[2]->data(Qt::CheckStateRole).toBool(),  // ssl
-                row[3]->data(Qt::EditRole).toString(),      // user
-                row[4]->data(Qt::EditRole).toString(),      // nick
-                row[5]->data(Qt::EditRole).toString(),      // real
-                original.authType,                          // authType
-                original.connectCommands,                   // connectCommands
-                original.id,                                // id
-            };
-        }
-
-        // turns a row in the model into a vector item
-        void getRowFromItem(const IrcServerData &item,
-                            std::vector<QStandardItem *> &row)
-        {
-            setStringItem(row[0], item.host, false);
-            setStringItem(row[1], QString::number(item.port));
-            setBoolItem(row[2], item.ssl);
-            setStringItem(row[3], item.user);
-            setStringItem(row[4], item.nick);
-            setStringItem(row[5], item.real);
-        }
-    };
-}  // namespace
-
-inline QString escape(QString str)
-{
-    return str.replace(":", "::");
-}
-
-// This returns a unique id for every server which is understandeable in the systems credential manager.
-inline QString getCredentialName(const IrcServerData &data)
-{
-    return escape(QString::number(data.id)) + ":" + escape(data.user) + "@" +
-           escape(data.host);
-}
-
-void IrcServerData::getPassword(
-    QObject *receiver, std::function<void(const QString &)> &&onLoaded) const
-{
-    Credentials::instance().get("irc", getCredentialName(*this), receiver,
-                                std::move(onLoaded));
-}
-
-void IrcServerData::setPassword(const QString &password)
-{
-    Credentials::instance().set("irc", getCredentialName(*this), password);
-}
-
-Irc::Irc()
-{
-    this->connections.itemInserted.connect([this](auto &&args) {
-        // make sure only one id can only exist for one server
-        assert(this->servers_.find(args.item.id) == this->servers_.end());
-
-        // add new server
-        if (auto ab = this->abandonedChannels_.find(args.item.id);
-            ab != this->abandonedChannels_.end())
-        {
-            auto server = std::make_unique<IrcServer>(args.item, ab->second);
-
-            // set server of abandoned channels
-            for (auto weak : ab->second)
-                if (auto shared = weak.lock())
-                    if (auto ircChannel =
-                            dynamic_cast<IrcChannel *>(shared.get()))
-                        ircChannel->setServer(server.get());
-
-            // add new server with abandoned channels
-            this->servers_.emplace(args.item.id, std::move(server));
-            this->abandonedChannels_.erase(ab);
-        }
-        else
-        {
-            // add new server
-            this->servers_.emplace(args.item.id,
-                                   std::make_unique<IrcServer>(args.item));
-        }
-    });
-
-    this->connections.itemRemoved.connect([this](auto &&args) {
-        // restore
-        if (auto server = this->servers_.find(args.item.id);
-            server != this->servers_.end())
-        {
-            auto abandoned = server->second->getChannels();
-
-            // set server of abandoned servers to nullptr
-            for (auto weak : abandoned)
-                if (auto shared = weak.lock())
-                    if (auto ircChannel =
-                            dynamic_cast<IrcChannel *>(shared.get()))
-                        ircChannel->setServer(nullptr);
-
-            this->abandonedChannels_[args.item.id] = abandoned;
-            this->servers_.erase(server);
-        }
-
-        if (args.caller != Irc::noEraseCredentialCaller)
-        {
-            Credentials::instance().erase("irc", getCredentialName(args.item));
-        }
-    });
-
-    this->connections.delayedItemsChanged.connect([this] { this->save(); });
-}
-
-QAbstractTableModel *Irc::newConnectionModel(QObject *parent)
-{
-    auto model = new Model(parent);
-    model->initialize(&this->connections);
-    return model;
-}
-
-ChannelPtr Irc::getOrAddChannel(int id, QString name)
-{
-    if (auto server = this->servers_.find(id); server != this->servers_.end())
-    {
-        return server->second->getOrAddChannel(name);
-    }
-    else
-    {
-        auto channel = std::make_shared<IrcChannel>(name, nullptr);
-
-        this->abandonedChannels_[id].push_back(channel);
-
-        return std::move(channel);
-    }
-}
-
-Irc &Irc::instance()
-{
-    static Irc irc;
-    return irc;
-}
-
-int Irc::uniqueId()
-{
-    int i = this->currentId_ + 1;
-    auto it = this->servers_.find(i);
-    auto it2 = this->abandonedChannels_.find(i);
-
-    while (it != this->servers_.end() || it2 != this->abandonedChannels_.end())
-    {
-        i++;
-        it = this->servers_.find(i);
-        it2 = this->abandonedChannels_.find(i);
-    }
-
-    return (this->currentId_ = i);
-}
-
-void Irc::save()
-{
-    QJsonDocument doc;
-    QJsonObject root;
-    QJsonArray servers;
-
-    for (auto &&conn : this->connections)
-    {
-        QJsonObject obj;
-        obj.insert("host", conn.host);
-        obj.insert("port", conn.port);
-        obj.insert("ssl", conn.ssl);
-        obj.insert("username", conn.user);
-        obj.insert("nickname", conn.nick);
-        obj.insert("realname", conn.real);
-        obj.insert("connectCommands",
-                   QJsonArray::fromStringList(conn.connectCommands));
-        obj.insert("id", conn.id);
-        obj.insert("authType", int(conn.authType));
-
-        servers.append(obj);
-    }
-
-    root.insert("servers", servers);
-    doc.setObject(root);
-
-    QSaveFile file(configPath());
-    file.open(QIODevice::WriteOnly);
-    file.write(doc.toJson());
-    file.commit();
-}
-
-void Irc::load()
-{
-    if (this->loaded_)
-        return;
-    this->loaded_ = true;
-
-    QString config = configPath();
-    QFile file(configPath());
-    file.open(QIODevice::ReadOnly);
-    auto object = QJsonDocument::fromJson(file.readAll()).object();
-
-    std::unordered_set<int> ids;
-
-    // load servers
-    for (auto server : object.value("servers").toArray())
-    {
-        auto obj = server.toObject();
-        IrcServerData data;
-        data.host = obj.value("host").toString(data.host);
-        data.port = obj.value("port").toInt(data.port);
-        data.ssl = obj.value("ssl").toBool(data.ssl);
-        data.user = obj.value("username").toString(data.user);
-        data.nick = obj.value("nickname").toString(data.nick);
-        data.real = obj.value("realname").toString(data.real);
-        data.connectCommands =
-            obj.value("connectCommands").toVariant().toStringList();
-        data.id = obj.value("id").toInt(data.id);
-        data.authType =
-            IrcAuthType(obj.value("authType").toInt(int(data.authType)));
-
-        // duplicate id's are not allowed :(
-        if (ids.find(data.id) == ids.end())
-        {
-            ids.insert(data.id);
-
-            this->connections.append(data);
-        }
-    }
-}
-
-}  // namespace chatterino
+#include "Irc2.hpp"
+
+#include <pajlada/serialize.hpp>
+#include "common/Credentials.hpp"
+#include "common/SignalVectorModel.hpp"
+#include "singletons/Paths.hpp"
+#include "util/CombinePath.hpp"
+#include "util/RapidjsonHelpers.hpp"
+#include "util/StandardItemHelper.hpp"
+
+#include <QSaveFile>
+#include <QtConcurrent>
+
+namespace chatterino {
+
+namespace {
+    QString configPath()
+    {
+        return combinePath(getPaths()->settingsDirectory, "irc.json");
+    }
+
+    class Model : public SignalVectorModel<IrcServerData>
+    {
+    public:
+        Model(QObject *parent)
+            : SignalVectorModel<IrcServerData>(6, parent)
+        {
+        }
+
+        // turn a vector item into a model row
+        IrcServerData getItemFromRow(std::vector<QStandardItem *> &row,
+                                     const IrcServerData &original)
+        {
+            return IrcServerData{
+                row[0]->data(Qt::EditRole).toString(),      // host
+                row[1]->data(Qt::EditRole).toInt(),         // port
+                row[2]->data(Qt::CheckStateRole).toBool(),  // ssl
+                row[3]->data(Qt::EditRole).toString(),      // user
+                row[4]->data(Qt::EditRole).toString(),      // nick
+                row[5]->data(Qt::EditRole).toString(),      // real
+                original.authType,                          // authType
+                original.connectCommands,                   // connectCommands
+                original.id,                                // id
+            };
+        }
+
+        // turns a row in the model into a vector item
+        void getRowFromItem(const IrcServerData &item,
+                            std::vector<QStandardItem *> &row)
+        {
+            setStringItem(row[0], item.host, false);
+            setStringItem(row[1], QString::number(item.port));
+            setBoolItem(row[2], item.ssl);
+            setStringItem(row[3], item.user);
+            setStringItem(row[4], item.nick);
+            setStringItem(row[5], item.real);
+        }
+    };
+}  // namespace
+
+inline QString escape(QString str)
+{
+    return str.replace(":", "::");
+}
+
+// This returns a unique id for every server which is understandeable in the systems credential manager.
+inline QString getCredentialName(const IrcServerData &data)
+{
+    return escape(QString::number(data.id)) + ":" + escape(data.user) + "@" +
+           escape(data.host);
+}
+
+void IrcServerData::getPassword(
+    QObject *receiver, std::function<void(const QString &)> &&onLoaded) const
+{
+    Credentials::instance().get("irc", getCredentialName(*this), receiver,
+                                std::move(onLoaded));
+}
+
+void IrcServerData::setPassword(const QString &password)
+{
+    Credentials::instance().set("irc", getCredentialName(*this), password);
+}
+
+Irc::Irc()
+{
+    this->connections.itemInserted.connect([this](auto &&args) {
+        // make sure only one id can only exist for one server
+        assert(this->servers_.find(args.item.id) == this->servers_.end());
+
+        // add new server
+        if (auto ab = this->abandonedChannels_.find(args.item.id);
+            ab != this->abandonedChannels_.end())
+        {
+            auto server = std::make_unique<IrcServer>(args.item, ab->second);
+
+            // set server of abandoned channels
+            for (auto weak : ab->second)
+                if (auto shared = weak.lock())
+                    if (auto ircChannel =
+                            dynamic_cast<IrcChannel *>(shared.get()))
+                        ircChannel->setServer(server.get());
+
+            // add new server with abandoned channels
+            this->servers_.emplace(args.item.id, std::move(server));
+            this->abandonedChannels_.erase(ab);
+        }
+        else
+        {
+            // add new server
+            this->servers_.emplace(args.item.id,
+                                   std::make_unique<IrcServer>(args.item));
+        }
+    });
+
+    this->connections.itemRemoved.connect([this](auto &&args) {
+        // restore
+        if (auto server = this->servers_.find(args.item.id);
+            server != this->servers_.end())
+        {
+            auto abandoned = server->second->getChannels();
+
+            // set server of abandoned servers to nullptr
+            for (auto weak : abandoned)
+                if (auto shared = weak.lock())
+                    if (auto ircChannel =
+                            dynamic_cast<IrcChannel *>(shared.get()))
+                        ircChannel->setServer(nullptr);
+
+            this->abandonedChannels_[args.item.id] = abandoned;
+            this->servers_.erase(server);
+        }
+
+        if (args.caller != Irc::noEraseCredentialCaller)
+        {
+            Credentials::instance().erase("irc", getCredentialName(args.item));
+        }
+    });
+
+    this->connections.delayedItemsChanged.connect([this] { this->save(); });
+}
+
+QAbstractTableModel *Irc::newConnectionModel(QObject *parent)
+{
+    auto model = new Model(parent);
+    model->initialize(&this->connections);
+    return model;
+}
+
+ChannelPtr Irc::getOrAddChannel(int id, QString name)
+{
+    if (auto server = this->servers_.find(id); server != this->servers_.end())
+    {
+        return server->second->getOrAddChannel(name);
+    }
+    else
+    {
+        auto channel = std::make_shared<IrcChannel>(name, nullptr);
+
+        this->abandonedChannels_[id].push_back(channel);
+
+        return std::move(channel);
+    }
+}
+
+Irc &Irc::instance()
+{
+    static Irc irc;
+    return irc;
+}
+
+int Irc::uniqueId()
+{
+    int i = this->currentId_ + 1;
+    auto it = this->servers_.find(i);
+    auto it2 = this->abandonedChannels_.find(i);
+
+    while (it != this->servers_.end() || it2 != this->abandonedChannels_.end())
+    {
+        i++;
+        it = this->servers_.find(i);
+        it2 = this->abandonedChannels_.find(i);
+    }
+
+    return (this->currentId_ = i);
+}
+
+void Irc::save()
+{
+    QJsonDocument doc;
+    QJsonObject root;
+    QJsonArray servers;
+
+    for (auto &&conn : this->connections)
+    {
+        QJsonObject obj;
+        obj.insert("host", conn.host);
+        obj.insert("port", conn.port);
+        obj.insert("ssl", conn.ssl);
+        obj.insert("username", conn.user);
+        obj.insert("nickname", conn.nick);
+        obj.insert("realname", conn.real);
+        obj.insert("connectCommands",
+                   QJsonArray::fromStringList(conn.connectCommands));
+        obj.insert("id", conn.id);
+        obj.insert("authType", int(conn.authType));
+
+        servers.append(obj);
+    }
+
+    root.insert("servers", servers);
+    doc.setObject(root);
+
+    QSaveFile file(configPath());
+    file.open(QIODevice::WriteOnly);
+    file.write(doc.toJson());
+    file.commit();
+}
+
+void Irc::load()
+{
+    if (this->loaded_)
+        return;
+    this->loaded_ = true;
+
+    QString config = configPath();
+    QFile file(configPath());
+    file.open(QIODevice::ReadOnly);
+    auto object = QJsonDocument::fromJson(file.readAll()).object();
+
+    std::unordered_set<int> ids;
+
+    // load servers
+    for (auto server : object.value("servers").toArray())
+    {
+        auto obj = server.toObject();
+        IrcServerData data;
+        data.host = obj.value("host").toString(data.host);
+        data.port = obj.value("port").toInt(data.port);
+        data.ssl = obj.value("ssl").toBool(data.ssl);
+        data.user = obj.value("username").toString(data.user);
+        data.nick = obj.value("nickname").toString(data.nick);
+        data.real = obj.value("realname").toString(data.real);
+        data.connectCommands =
+            obj.value("connectCommands").toVariant().toStringList();
+        data.id = obj.value("id").toInt(data.id);
+        data.authType =
+            IrcAuthType(obj.value("authType").toInt(int(data.authType)));
+
+        // duplicate id's are not allowed :(
+        if (ids.find(data.id) == ids.end())
+        {
+            ids.insert(data.id);
+
+            this->connections.append(data);
+        }
+    }
+}
+
+}  // namespace chatterino
diff --git a/src/providers/irc/Irc2.hpp b/src/providers/irc/Irc2.hpp
index 0cdc60c77..c4ae8c733 100644
--- a/src/providers/irc/Irc2.hpp
+++ b/src/providers/irc/Irc2.hpp
@@ -1,67 +1,67 @@
-#pragma once
-
-#include <rapidjson/rapidjson.h>
-#include <common/SignalVector.hpp>
-
-#include "providers/irc/IrcChannel2.hpp"
-#include "providers/irc/IrcServer.hpp"
-
-class QAbstractTableModel;
-
-namespace chatterino {
-
-enum class IrcAuthType { Anonymous, Custom, Pass, Sasl };
-
-struct IrcServerData {
-    QString host;
-    int port = 6697;
-    bool ssl = true;
-
-    QString user;
-    QString nick;
-    QString real;
-
-    IrcAuthType authType = IrcAuthType::Anonymous;
-    void getPassword(QObject *receiver,
-                     std::function<void(const QString &)> &&onLoaded) const;
-    void setPassword(const QString &password);
-
-    QStringList connectCommands;
-
-    int id;
-};
-
-class Irc
-{
-public:
-    Irc();
-
-    static Irc &instance();
-
-    static inline void *const noEraseCredentialCaller =
-        reinterpret_cast<void *>(1);
-
-    SignalVector<IrcServerData> connections;
-    QAbstractTableModel *newConnectionModel(QObject *parent);
-
-    ChannelPtr getOrAddChannel(int serverId, QString name);
-
-    void save();
-    void load();
-
-    int uniqueId();
-
-private:
-    int currentId_{};
-    bool loaded_{};
-
-    // Servers have a unique id.
-    // When a server gets changed it gets removed and then added again.
-    // So we store the channels of that server in abandonedChannels_ temporarily.
-    // Or if the server got removed permanently then it's still stored there.
-    std::unordered_map<int, std::unique_ptr<IrcServer>> servers_;
-    std::unordered_map<int, std::vector<std::weak_ptr<Channel>>>
-        abandonedChannels_;
-};
-
-}  // namespace chatterino
+#pragma once
+
+#include <rapidjson/rapidjson.h>
+#include <common/SignalVector.hpp>
+
+#include "providers/irc/IrcChannel2.hpp"
+#include "providers/irc/IrcServer.hpp"
+
+class QAbstractTableModel;
+
+namespace chatterino {
+
+enum class IrcAuthType { Anonymous, Custom, Pass, Sasl };
+
+struct IrcServerData {
+    QString host;
+    int port = 6697;
+    bool ssl = true;
+
+    QString user;
+    QString nick;
+    QString real;
+
+    IrcAuthType authType = IrcAuthType::Anonymous;
+    void getPassword(QObject *receiver,
+                     std::function<void(const QString &)> &&onLoaded) const;
+    void setPassword(const QString &password);
+
+    QStringList connectCommands;
+
+    int id;
+};
+
+class Irc
+{
+public:
+    Irc();
+
+    static Irc &instance();
+
+    static inline void *const noEraseCredentialCaller =
+        reinterpret_cast<void *>(1);
+
+    SignalVector<IrcServerData> connections;
+    QAbstractTableModel *newConnectionModel(QObject *parent);
+
+    ChannelPtr getOrAddChannel(int serverId, QString name);
+
+    void save();
+    void load();
+
+    int uniqueId();
+
+private:
+    int currentId_{};
+    bool loaded_{};
+
+    // Servers have a unique id.
+    // When a server gets changed it gets removed and then added again.
+    // So we store the channels of that server in abandonedChannels_ temporarily.
+    // Or if the server got removed permanently then it's still stored there.
+    std::unordered_map<int, std::unique_ptr<IrcServer>> servers_;
+    std::unordered_map<int, std::vector<std::weak_ptr<Channel>>>
+        abandonedChannels_;
+};
+
+}  // namespace chatterino
diff --git a/src/providers/irc/IrcCommands.cpp b/src/providers/irc/IrcCommands.cpp
index 87d07c8d9..a423f9189 100644
--- a/src/providers/irc/IrcCommands.cpp
+++ b/src/providers/irc/IrcCommands.cpp
@@ -1,76 +1,76 @@
-#include "IrcCommands.hpp"
-
-#include "messages/MessageBuilder.hpp"
-#include "providers/irc/IrcChannel2.hpp"
-#include "providers/irc/IrcServer.hpp"
-#include "util/Overloaded.hpp"
-#include "util/QStringHash.hpp"
-
-namespace chatterino {
-
-Outcome invokeIrcCommand(const QString &commandName, const QString &allParams,
-                         IrcChannel &channel)
-{
-    if (!channel.server())
-    {
-        return Failure;
-    }
-
-    // STATIC MESSAGES
-    static auto staticMessages = std::unordered_map<QString, QString>{
-        {"join", "/join is not supported. Press ctrl+r to change the "
-                 "channel. If required use /raw JOIN #channel."},
-        {"part", "/part is not supported. Press ctrl+r to change the "
-                 "channel. If required use /raw PART #channel."},
-    };
-    auto cmd = commandName.toLower();
-
-    if (auto it = staticMessages.find(cmd); it != staticMessages.end())
-    {
-        channel.addMessage(makeSystemMessage(it->second));
-        return Success;
-    }
-
-    // CUSTOM COMMANDS
-    auto params = allParams.split(' ');
-    auto paramsAfter = [&](int i) { return params.mid(i + 1).join(' '); };
-
-    auto sendRaw = [&](QString str) { channel.server()->sendRawMessage(str); };
-
-    if (cmd == "msg")
-    {
-        sendRaw("PRIVMSG " + params[0] + " :" + paramsAfter(0));
-    }
-    else if (cmd == "away")
-    {
-        sendRaw("AWAY" + params[0] + " :" + paramsAfter(0));
-    }
-    else if (cmd == "knock")
-    {
-        sendRaw("KNOCK #" + params[0] + " " + paramsAfter(0));
-    }
-    else if (cmd == "kick")
-    {
-        if (paramsAfter(1).isEmpty())
-            sendRaw("KICK " + params[0] + " " + params[1]);
-        else
-            sendRaw("KICK " + params[0] + " " + params[1] + " :" +
-                    paramsAfter(1));
-    }
-    else if (cmd == "wallops")
-    {
-        sendRaw("WALLOPS :" + allParams);
-    }
-    else if (cmd == "raw")
-    {
-        sendRaw(allParams);
-    }
-    else
-    {
-        sendRaw(cmd.toUpper() + " " + allParams);
-    }
-
-    return Success;
-}
-
-}  // namespace chatterino
+#include "IrcCommands.hpp"
+
+#include "messages/MessageBuilder.hpp"
+#include "providers/irc/IrcChannel2.hpp"
+#include "providers/irc/IrcServer.hpp"
+#include "util/Overloaded.hpp"
+#include "util/QStringHash.hpp"
+
+namespace chatterino {
+
+Outcome invokeIrcCommand(const QString &commandName, const QString &allParams,
+                         IrcChannel &channel)
+{
+    if (!channel.server())
+    {
+        return Failure;
+    }
+
+    // STATIC MESSAGES
+    static auto staticMessages = std::unordered_map<QString, QString>{
+        {"join", "/join is not supported. Press ctrl+r to change the "
+                 "channel. If required use /raw JOIN #channel."},
+        {"part", "/part is not supported. Press ctrl+r to change the "
+                 "channel. If required use /raw PART #channel."},
+    };
+    auto cmd = commandName.toLower();
+
+    if (auto it = staticMessages.find(cmd); it != staticMessages.end())
+    {
+        channel.addMessage(makeSystemMessage(it->second));
+        return Success;
+    }
+
+    // CUSTOM COMMANDS
+    auto params = allParams.split(' ');
+    auto paramsAfter = [&](int i) { return params.mid(i + 1).join(' '); };
+
+    auto sendRaw = [&](QString str) { channel.server()->sendRawMessage(str); };
+
+    if (cmd == "msg")
+    {
+        sendRaw("PRIVMSG " + params[0] + " :" + paramsAfter(0));
+    }
+    else if (cmd == "away")
+    {
+        sendRaw("AWAY" + params[0] + " :" + paramsAfter(0));
+    }
+    else if (cmd == "knock")
+    {
+        sendRaw("KNOCK #" + params[0] + " " + paramsAfter(0));
+    }
+    else if (cmd == "kick")
+    {
+        if (paramsAfter(1).isEmpty())
+            sendRaw("KICK " + params[0] + " " + params[1]);
+        else
+            sendRaw("KICK " + params[0] + " " + params[1] + " :" +
+                    paramsAfter(1));
+    }
+    else if (cmd == "wallops")
+    {
+        sendRaw("WALLOPS :" + allParams);
+    }
+    else if (cmd == "raw")
+    {
+        sendRaw(allParams);
+    }
+    else
+    {
+        sendRaw(cmd.toUpper() + " " + allParams);
+    }
+
+    return Success;
+}
+
+}  // namespace chatterino
diff --git a/src/providers/irc/IrcCommands.hpp b/src/providers/irc/IrcCommands.hpp
index ffdbde023..d6d0dd795 100644
--- a/src/providers/irc/IrcCommands.hpp
+++ b/src/providers/irc/IrcCommands.hpp
@@ -1,12 +1,12 @@
-#pragma once
-
-#include "common/Outcome.hpp"
-
-namespace chatterino {
-
-class IrcChannel;
-
-Outcome invokeIrcCommand(const QString &command, const QString &params,
-                         IrcChannel &channel);
-
-}  // namespace chatterino
+#pragma once
+
+#include "common/Outcome.hpp"
+
+namespace chatterino {
+
+class IrcChannel;
+
+Outcome invokeIrcCommand(const QString &command, const QString &params,
+                         IrcChannel &channel);
+
+}  // namespace chatterino
diff --git a/src/providers/twitch/IrcMessageHandler.cpp b/src/providers/twitch/IrcMessageHandler.cpp
index 78398f3b0..4f6a2695a 100644
--- a/src/providers/twitch/IrcMessageHandler.cpp
+++ b/src/providers/twitch/IrcMessageHandler.cpp
@@ -1,4 +1,4 @@
-#include "IrcMessageHandler.hpp"
+#include "IrcMessageHandler.hpp"
 
 #include "Application.hpp"
 #include "controllers/accounts/AccountController.hpp"
diff --git a/src/providers/twitch/TwitchMessageBuilder.hpp b/src/providers/twitch/TwitchMessageBuilder.hpp
index 6df45fedc..5481832d3 100644
--- a/src/providers/twitch/TwitchMessageBuilder.hpp
+++ b/src/providers/twitch/TwitchMessageBuilder.hpp
@@ -1,4 +1,4 @@
-#pragma once
+#pragma once
 
 #include "common/Aliases.hpp"
 #include "common/Outcome.hpp"
diff --git a/src/util/Overloaded.hpp b/src/util/Overloaded.hpp
index 7deb38a67..e5bc805f5 100644
--- a/src/util/Overloaded.hpp
+++ b/src/util/Overloaded.hpp
@@ -1,13 +1,13 @@
-#pragma once
-
-namespace chatterino {
-
-template <class... Ts>
-struct Overloaded : Ts... {
-    using Ts::operator()...;
-};
-
-template <class... Ts>
-Overloaded(Ts...)->Overloaded<Ts...>;
-
-}  // namespace chatterino
+#pragma once
+
+namespace chatterino {
+
+template <class... Ts>
+struct Overloaded : Ts... {
+    using Ts::operator()...;
+};
+
+template <class... Ts>
+Overloaded(Ts...) -> Overloaded<Ts...>;
+
+}  // namespace chatterino
diff --git a/src/widgets/dialogs/IrcConnectionEditor.cpp b/src/widgets/dialogs/IrcConnectionEditor.cpp
index 055b229fe..c24b3ae89 100644
--- a/src/widgets/dialogs/IrcConnectionEditor.cpp
+++ b/src/widgets/dialogs/IrcConnectionEditor.cpp
@@ -1,97 +1,97 @@
-#include "IrcConnectionEditor.hpp"
-#include "ui_IrcConnectionEditor.h"
-
-namespace chatterino {
-
-IrcConnectionEditor::IrcConnectionEditor(const IrcServerData &data, bool isAdd,
-                                         QWidget *parent)
-
-    : QDialog(parent, Qt::WindowStaysOnTopHint)
-    , ui_(new Ui::IrcConnectionEditor)
-    , data_(data)
-{
-    this->ui_->setupUi(this);
-
-    this->setWindowTitle(QString(isAdd ? "Add " : "Edit ") + "Irc Connection");
-
-    QObject::connect(this->ui_->userNameLineEdit, &QLineEdit::textChanged, this,
-                     [this](const QString &text) {
-                         this->ui_->nickNameLineEdit->setPlaceholderText(text);
-                         this->ui_->realNameLineEdit->setPlaceholderText(text);
-                     });
-
-    this->ui_->serverLineEdit->setText(data.host);
-    this->ui_->portSpinBox->setValue(data.port);
-    this->ui_->securityCheckBox->setChecked(data.ssl);
-    this->ui_->userNameLineEdit->setText(data.user);
-    this->ui_->nickNameLineEdit->setText(data.nick);
-    this->ui_->realNameLineEdit->setText(data.real);
-    this->ui_->connectCommandsEditor->setPlainText(
-        data.connectCommands.join('\n'));
-
-    data.getPassword(this, [this](const QString &password) {
-        this->ui_->passwordLineEdit->setText(password);
-    });
-
-    this->ui_->loginMethodComboBox->setCurrentIndex([&] {
-        switch (data.authType)
-        {
-            case IrcAuthType::Custom:
-                return 1;
-            case IrcAuthType::Pass:
-                return 2;
-            case IrcAuthType::Sasl:
-                return 3;
-            default:
-                return 0;
-        }
-    }());
-
-    QObject::connect(this->ui_->loginMethodComboBox,
-                     qOverload<int>(&QComboBox::currentIndexChanged), this,
-                     [this](int index) {
-                         if (index == 1)  // Custom
-                         {
-                             this->ui_->connectCommandsEditor->setFocus();
-                         }
-                     });
-
-    QFont font("Monospace");
-    font.setStyleHint(QFont::TypeWriter);
-    this->ui_->connectCommandsEditor->setFont(font);
-}
-
-IrcConnectionEditor::~IrcConnectionEditor()
-{
-    delete ui_;
-}
-
-IrcServerData IrcConnectionEditor::data()
-{
-    auto data = this->data_;
-    data.host = this->ui_->serverLineEdit->text();
-    data.port = this->ui_->portSpinBox->value();
-    data.ssl = this->ui_->securityCheckBox->isChecked();
-    data.user = this->ui_->userNameLineEdit->text();
-    data.nick = this->ui_->nickNameLineEdit->text();
-    data.real = this->ui_->realNameLineEdit->text();
-    data.connectCommands =
-        this->ui_->connectCommandsEditor->toPlainText().split('\n');
-    data.setPassword(this->ui_->passwordLineEdit->text());
-    data.authType = [this] {
-        switch (this->ui_->loginMethodComboBox->currentIndex())
-        {
-            case 1:
-                return IrcAuthType::Custom;
-            case 2:
-                return IrcAuthType::Pass;
-            case 3:
-                return IrcAuthType::Sasl;
-            default:
-                return IrcAuthType::Anonymous;
-        }
-    }();
-    return data;
-}
-
-}  // namespace chatterino
+#include "IrcConnectionEditor.hpp"
+#include "ui_IrcConnectionEditor.h"
+
+namespace chatterino {
+
+IrcConnectionEditor::IrcConnectionEditor(const IrcServerData &data, bool isAdd,
+                                         QWidget *parent)
+
+    : QDialog(parent, Qt::WindowStaysOnTopHint)
+    , ui_(new Ui::IrcConnectionEditor)
+    , data_(data)
+{
+    this->ui_->setupUi(this);
+
+    this->setWindowTitle(QString(isAdd ? "Add " : "Edit ") + "Irc Connection");
+
+    QObject::connect(this->ui_->userNameLineEdit, &QLineEdit::textChanged, this,
+                     [this](const QString &text) {
+                         this->ui_->nickNameLineEdit->setPlaceholderText(text);
+                         this->ui_->realNameLineEdit->setPlaceholderText(text);
+                     });
+
+    this->ui_->serverLineEdit->setText(data.host);
+    this->ui_->portSpinBox->setValue(data.port);
+    this->ui_->securityCheckBox->setChecked(data.ssl);
+    this->ui_->userNameLineEdit->setText(data.user);
+    this->ui_->nickNameLineEdit->setText(data.nick);
+    this->ui_->realNameLineEdit->setText(data.real);
+    this->ui_->connectCommandsEditor->setPlainText(
+        data.connectCommands.join('\n'));
+
+    data.getPassword(this, [this](const QString &password) {
+        this->ui_->passwordLineEdit->setText(password);
+    });
+
+    this->ui_->loginMethodComboBox->setCurrentIndex([&] {
+        switch (data.authType)
+        {
+            case IrcAuthType::Custom:
+                return 1;
+            case IrcAuthType::Pass:
+                return 2;
+            case IrcAuthType::Sasl:
+                return 3;
+            default:
+                return 0;
+        }
+    }());
+
+    QObject::connect(this->ui_->loginMethodComboBox,
+                     qOverload<int>(&QComboBox::currentIndexChanged), this,
+                     [this](int index) {
+                         if (index == 1)  // Custom
+                         {
+                             this->ui_->connectCommandsEditor->setFocus();
+                         }
+                     });
+
+    QFont font("Monospace");
+    font.setStyleHint(QFont::TypeWriter);
+    this->ui_->connectCommandsEditor->setFont(font);
+}
+
+IrcConnectionEditor::~IrcConnectionEditor()
+{
+    delete ui_;
+}
+
+IrcServerData IrcConnectionEditor::data()
+{
+    auto data = this->data_;
+    data.host = this->ui_->serverLineEdit->text();
+    data.port = this->ui_->portSpinBox->value();
+    data.ssl = this->ui_->securityCheckBox->isChecked();
+    data.user = this->ui_->userNameLineEdit->text();
+    data.nick = this->ui_->nickNameLineEdit->text();
+    data.real = this->ui_->realNameLineEdit->text();
+    data.connectCommands =
+        this->ui_->connectCommandsEditor->toPlainText().split('\n');
+    data.setPassword(this->ui_->passwordLineEdit->text());
+    data.authType = [this] {
+        switch (this->ui_->loginMethodComboBox->currentIndex())
+        {
+            case 1:
+                return IrcAuthType::Custom;
+            case 2:
+                return IrcAuthType::Pass;
+            case 3:
+                return IrcAuthType::Sasl;
+            default:
+                return IrcAuthType::Anonymous;
+        }
+    }();
+    return data;
+}
+
+}  // namespace chatterino
diff --git a/src/widgets/dialogs/IrcConnectionEditor.hpp b/src/widgets/dialogs/IrcConnectionEditor.hpp
index 958b2a2f5..e71bb1f1a 100644
--- a/src/widgets/dialogs/IrcConnectionEditor.hpp
+++ b/src/widgets/dialogs/IrcConnectionEditor.hpp
@@ -1,32 +1,32 @@
-#pragma once
-
-#include <boost/optional.hpp>
-
-#include "providers/irc/Irc2.hpp"
-#include "widgets/BaseWindow.hpp"
-
-namespace Ui {
-class IrcConnectionEditor;
-}
-
-namespace chatterino {
-
-struct IrcServerData;
-
-class IrcConnectionEditor : public QDialog
-{
-    Q_OBJECT
-
-public:
-    explicit IrcConnectionEditor(const IrcServerData &data, bool isAdd = false,
-                                 QWidget *parent = nullptr);
-    ~IrcConnectionEditor();
-
-    IrcServerData data();
-
-private:
-    Ui::IrcConnectionEditor *ui_;
-    IrcServerData data_;
-};
-
-}  // namespace chatterino
+#pragma once
+
+#include <boost/optional.hpp>
+
+#include "providers/irc/Irc2.hpp"
+#include "widgets/BaseWindow.hpp"
+
+namespace Ui {
+class IrcConnectionEditor;
+}
+
+namespace chatterino {
+
+struct IrcServerData;
+
+class IrcConnectionEditor : public QDialog
+{
+    Q_OBJECT
+
+public:
+    explicit IrcConnectionEditor(const IrcServerData &data, bool isAdd = false,
+                                 QWidget *parent = nullptr);
+    ~IrcConnectionEditor();
+
+    IrcServerData data();
+
+private:
+    Ui::IrcConnectionEditor *ui_;
+    IrcServerData data_;
+};
+
+}  // namespace chatterino
diff --git a/tools/check-format.sh b/tools/check-format.sh
index 1af2b596a..e7722ed6f 100755
--- a/tools/check-format.sh
+++ b/tools/check-format.sh
@@ -4,12 +4,14 @@ set -eu
 
 fail="0"
 
+clang-format --version
+
 while read -r file; do
     if ! diff -u <(cat "$file") <(clang-format "$file"); then
         echo "$file differs!!!!!!!"
         fail="1"
     fi
-done < <(find src/ \( -iname "*.hpp" -o -iname "*.cpp" \))
+done < <(find src/ -type f \( -iname "*.hpp" -o -iname "*.cpp" \))
 
 if [ "$fail" = "1" ]; then
     echo "At least one file is poorly formatted - check the output above"
diff --git a/tools/check-line-endings.sh b/tools/check-line-endings.sh
new file mode 100755
index 000000000..92ae21b81
--- /dev/null
+++ b/tools/check-line-endings.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+set -eu
+
+fail="0"
+
+dos2unix --version
+
+while read -r file; do
+    num_dos_line_endings=$(dos2unix -id "$file" | awk '/[0-9]+/{print $(NF-1)}')
+    if [ "$num_dos_line_endings" -gt "0" ]; then
+        >&2 echo "File '$file' contains $num_dos_line_endings DOS line-endings, it should only be using unix line-endings!"
+        fail="1"
+    fi
+done < <(find src/ -type f \( -iname "*.hpp" -o -iname "*.cpp" \))
+
+if [ "$fail" = "1" ]; then
+    >&2 echo "At least one file is not using unix line-endings - check the output above"
+    exit 1
+fi
+
+>&2 echo "Every file seems to be using unix line-endings. Good job!"