From 0d8bb7eebd5753a92b872ade68c6d61bec54eacb Mon Sep 17 00:00:00 2001 From: Le Tan Date: Sat, 28 Aug 2021 07:45:10 +0800 Subject: [PATCH] Editor: support line ending --- libs/vtextedit | 2 +- src/core/editorconfig.cpp | 54 +++++++++++++++++++++ src/core/editorconfig.h | 10 ++++ src/core/global.h | 8 +++ src/core/historymgr.cpp | 4 +- src/data/core/vnotex.json | 4 +- src/utils/fileutils.cpp | 9 ++-- src/widgets/dialogs/settings/editorpage.cpp | 28 +++++++++++ src/widgets/dialogs/settings/editorpage.h | 2 + src/widgets/markdownviewwindow.cpp | 3 +- src/widgets/textviewwindow.cpp | 3 +- src/widgets/textviewwindowhelper.h | 21 +++++++- 12 files changed, 138 insertions(+), 10 deletions(-) diff --git a/libs/vtextedit b/libs/vtextedit index 59e53f2c..0733259f 160000 --- a/libs/vtextedit +++ b/libs/vtextedit @@ -1 +1 @@ -Subproject commit 59e53f2cdc2f5a4b9dc44cbf28c483cca07559ba +Subproject commit 0733259fed01ecaa11678fac00fd67397ff7c39c diff --git a/src/core/editorconfig.cpp b/src/core/editorconfig.cpp index 37672e39..bfaa0d51 100644 --- a/src/core/editorconfig.cpp +++ b/src/core/editorconfig.cpp @@ -95,6 +95,11 @@ void EditorConfig::loadCore(const QJsonObject &p_app, const QJsonObject &p_user) if (m_spellCheckDefaultDictionary.isEmpty()) { m_spellCheckDefaultDictionary = QStringLiteral("en_US"); } + + { + auto lineEnding = READSTR(QStringLiteral("line_ending")); + m_lineEnding = stringToLineEndingPolicy(lineEnding); + } } QJsonObject EditorConfig::saveCore() const @@ -107,6 +112,7 @@ QJsonObject EditorConfig::saveCore() const obj[QStringLiteral("shortcuts")] = saveShortcuts(); obj[QStringLiteral("spell_check_auto_detect_language")] = m_spellCheckAutoDetectLanguageEnabled; obj[QStringLiteral("spell_check_default_dictionary")] = m_spellCheckDefaultDictionary; + obj[QStringLiteral("line_ending")] = lineEndingPolicyToString(m_lineEnding); return obj; } @@ -211,6 +217,44 @@ EditorConfig::AutoSavePolicy EditorConfig::stringToAutoSavePolicy(const QString } } +QString EditorConfig::lineEndingPolicyToString(LineEndingPolicy p_ending) const +{ + switch (p_ending) { + case LineEndingPolicy::Platform: + return QStringLiteral("platform"); + + case LineEndingPolicy::File: + return QStringLiteral("file"); + + case LineEndingPolicy::LF: + return QStringLiteral("lf"); + + case LineEndingPolicy::CRLF: + return QStringLiteral("crlf"); + + case LineEndingPolicy::CR: + return QStringLiteral("cr"); + } + + return QStringLiteral("platform"); +} + +LineEndingPolicy EditorConfig::stringToLineEndingPolicy(const QString &p_str) const +{ + auto ending = p_str.toLower(); + if (ending == QStringLiteral("file")) { + return LineEndingPolicy::File; + } else if (ending == QStringLiteral("lf")) { + return LineEndingPolicy::LF; + } else if (ending == QStringLiteral("crlf")) { + return LineEndingPolicy::CRLF; + } else if (ending == QStringLiteral("cr")) { + return LineEndingPolicy::CR; + } else { + return LineEndingPolicy::Platform; + } +} + EditorConfig::AutoSavePolicy EditorConfig::getAutoSavePolicy() const { return m_autoSavePolicy; @@ -221,6 +265,16 @@ void EditorConfig::setAutoSavePolicy(EditorConfig::AutoSavePolicy p_policy) updateConfig(m_autoSavePolicy, p_policy, this); } +LineEndingPolicy EditorConfig::getLineEndingPolicy() const +{ + return m_lineEnding; +} + +void EditorConfig::setLineEndingPolicy(LineEndingPolicy p_ending) +{ + updateConfig(m_lineEnding, p_ending, this); +} + const QString &EditorConfig::getBackupFileDirectory() const { return m_backupFileDirectory; diff --git a/src/core/editorconfig.h b/src/core/editorconfig.h index 04659a44..9252ce4c 100644 --- a/src/core/editorconfig.h +++ b/src/core/editorconfig.h @@ -8,6 +8,8 @@ #include #include +#include "global.h" + namespace vte { class ViConfig; @@ -127,6 +129,9 @@ namespace vnotex const QSharedPointer &getViConfig() const; + LineEndingPolicy getLineEndingPolicy() const; + void setLineEndingPolicy(LineEndingPolicy p_ending); + private: friend class MainConfig; @@ -141,6 +146,9 @@ namespace vnotex QString autoSavePolicyToString(AutoSavePolicy p_policy) const; AutoSavePolicy stringToAutoSavePolicy(const QString &p_str) const; + QString lineEndingPolicyToString(LineEndingPolicy p_ending) const; + LineEndingPolicy stringToLineEndingPolicy(const QString &p_str) const; + void loadImageHost(const QJsonObject &p_app, const QJsonObject &p_user); QJsonObject saveImageHost() const; @@ -174,6 +182,8 @@ namespace vnotex bool m_clearObsoleteImageAtImageHost = false; QSharedPointer m_viConfig; + + LineEndingPolicy m_lineEnding = LineEndingPolicy::LF; }; } diff --git a/src/core/global.h b/src/core/global.h index 79f478dd..74cc2b7d 100644 --- a/src/core/global.h +++ b/src/core/global.h @@ -133,6 +133,14 @@ namespace vnotex int m_length = -1; }; + enum class LineEndingPolicy + { + Platform, + File, + LF, + CRLF, + CR + }; } // ns vnotex Q_DECLARE_OPERATORS_FOR_FLAGS(vnotex::FindOptions); diff --git a/src/core/historymgr.cpp b/src/core/historymgr.cpp index c25270d9..d3dada70 100644 --- a/src/core/historymgr.cpp +++ b/src/core/historymgr.cpp @@ -145,8 +145,8 @@ void HistoryMgr::add(const QString &p_path, file.m_mode = p_mode; file.m_readOnly = p_readOnly; - if (m_lastClosedFiles.size() > 100) { - m_lastClosedFiles.remove(0, m_lastClosedFiles.size() - 100); + if (m_lastClosedFiles.size() > s_maxHistoryCount) { + m_lastClosedFiles.remove(0, m_lastClosedFiles.size() - s_maxHistoryCount); } } diff --git a/src/data/core/vnotex.json b/src/data/core/vnotex.json index bbc85de5..3341914e 100644 --- a/src/data/core/vnotex.json +++ b/src/data/core/vnotex.json @@ -119,7 +119,9 @@ "ApplySnippet" : "Ctrl+G, I" }, "spell_check_auto_detect_language" : false, - "spell_check_default_dictionary" : "en_US" + "spell_check_default_dictionary" : "en_US", + "//comment" : "platform/file/lf/crlf/cr", + "line_ending" : "lf" }, "text_editor" : { "theme" : "", diff --git a/src/utils/fileutils.cpp b/src/utils/fileutils.cpp index bf041c7a..6a3eda5d 100644 --- a/src/utils/fileutils.cpp +++ b/src/utils/fileutils.cpp @@ -6,7 +6,9 @@ #include #include -#include "../core/exception.h" +#include +#include + #include "pathutils.h" using namespace vnotex; @@ -25,11 +27,12 @@ QByteArray FileUtils::readFile(const QString &p_filePath) QString FileUtils::readTextFile(const QString &p_filePath) { QFile file(p_filePath); - if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + if (!file.open(QIODevice::ReadOnly)) { Exception::throwOne(Exception::Type::FailToReadFile, QString("failed to read file: %1").arg(p_filePath)); } + // TODO: determine the encoding of the text. QString text(file.readAll()); file.close(); return text; @@ -55,7 +58,7 @@ void FileUtils::writeFile(const QString &p_filePath, const QByteArray &p_data) void FileUtils::writeFile(const QString &p_filePath, const QString &p_text) { QFile file(p_filePath); - if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + if (!file.open(QIODevice::WriteOnly)) { Exception::throwOne(Exception::Type::FailToWriteFile, QString("failed to write to file: %1").arg(p_filePath)); } diff --git a/src/widgets/dialogs/settings/editorpage.cpp b/src/widgets/dialogs/settings/editorpage.cpp index 53e60563..5404fcfa 100644 --- a/src/widgets/dialogs/settings/editorpage.cpp +++ b/src/widgets/dialogs/settings/editorpage.cpp @@ -42,6 +42,23 @@ void EditorPage::setupUI() this, &EditorPage::pageIsChanged); } + { + m_lineEndingComboBox = WidgetsFactory::createComboBox(this); + m_lineEndingComboBox->setToolTip(tr("Line ending")); + + m_lineEndingComboBox->addItem(tr("Follow Platform"), (int)LineEndingPolicy::Platform); + m_lineEndingComboBox->addItem(tr("Follow File"), (int)LineEndingPolicy::File); + m_lineEndingComboBox->addItem(tr("LF (Linux/macOS)"), (int)LineEndingPolicy::LF); + m_lineEndingComboBox->addItem(tr("CR LF (Windows)"), (int)LineEndingPolicy::CRLF); + m_lineEndingComboBox->addItem(tr("CR"), (int)LineEndingPolicy::CR); + + const QString label(tr("Line ending:")); + mainLayout->addRow(label, m_lineEndingComboBox); + addSearchItem(label, m_lineEndingComboBox->toolTip(), m_lineEndingComboBox); + connect(m_lineEndingComboBox, QOverload::of(&QComboBox::currentIndexChanged), + this, &EditorPage::pageIsChanged); + } + { m_toolBarIconSizeSpinBox = WidgetsFactory::createSpinBox(this); m_toolBarIconSizeSpinBox->setToolTip(tr("Icon size of the editor tool bar")); @@ -105,6 +122,12 @@ void EditorPage::loadInternal() m_autoSavePolicyComboBox->setCurrentIndex(idx); } + { + int idx = m_lineEndingComboBox->findData(static_cast(editorConfig.getLineEndingPolicy())); + Q_ASSERT(idx != -1); + m_lineEndingComboBox->setCurrentIndex(idx); + } + m_toolBarIconSizeSpinBox->setValue(editorConfig.getToolBarIconSize()); { @@ -122,6 +145,11 @@ bool EditorPage::saveInternal() editorConfig.setAutoSavePolicy(static_cast(policy)); } + { + auto ending = m_lineEndingComboBox->currentData().toInt(); + editorConfig.setLineEndingPolicy(static_cast(ending)); + } + editorConfig.setToolBarIconSize(m_toolBarIconSizeSpinBox->value()); { diff --git a/src/widgets/dialogs/settings/editorpage.h b/src/widgets/dialogs/settings/editorpage.h index b017a44f..4ff72cdc 100644 --- a/src/widgets/dialogs/settings/editorpage.h +++ b/src/widgets/dialogs/settings/editorpage.h @@ -32,6 +32,8 @@ namespace vnotex QSpinBox *m_toolBarIconSizeSpinBox = nullptr; QComboBox *m_spellCheckDictComboBox = nullptr; + + QComboBox *m_lineEndingComboBox = nullptr; }; } diff --git a/src/widgets/markdownviewwindow.cpp b/src/widgets/markdownviewwindow.cpp index aeb7fc46..b25b4952 100644 --- a/src/widgets/markdownviewwindow.cpp +++ b/src/widgets/markdownviewwindow.cpp @@ -863,7 +863,8 @@ QSharedPointer MarkdownViewWindow::createMarkdownEdit auto textEditorConfig = TextViewWindowHelper::createTextEditorConfig(p_config.getTextEditorConfig(), p_editorConfig.getViConfig(), themeMgr.getFile(Theme::File::MarkdownEditorStyle), - themeMgr.getMarkdownEditorHighlightTheme()); + themeMgr.getMarkdownEditorHighlightTheme(), + p_editorConfig.getLineEndingPolicy()); auto editorConfig = QSharedPointer::create(textEditorConfig); editorConfig->overrideTextFontFamily(p_config.getEditorOverriddenFontFamily()); diff --git a/src/widgets/textviewwindow.cpp b/src/widgets/textviewwindow.cpp index 84bcefff..dda20e08 100644 --- a/src/widgets/textviewwindow.cpp +++ b/src/widgets/textviewwindow.cpp @@ -176,7 +176,8 @@ QSharedPointer TextViewWindow::createTextEditorConfig(con auto config = TextViewWindowHelper::createTextEditorConfig(p_config, p_editorConfig.getViConfig(), themeMgr.getFile(Theme::File::TextEditorStyle), - themeMgr.getEditorHighlightTheme()); + themeMgr.getEditorHighlightTheme(), + p_editorConfig.getLineEndingPolicy()); return config; } diff --git a/src/widgets/textviewwindowhelper.h b/src/widgets/textviewwindowhelper.h index 7e841e28..07525827 100644 --- a/src/widgets/textviewwindowhelper.h +++ b/src/widgets/textviewwindowhelper.h @@ -66,7 +66,8 @@ namespace vnotex static QSharedPointer createTextEditorConfig(const TextEditorConfig &p_config, const QSharedPointer &p_viConfig, const QString &p_themeFile, - const QString &p_syntaxTheme) + const QString &p_syntaxTheme, + LineEndingPolicy p_lineEndingPolicy) { auto editorConfig = QSharedPointer::create(); @@ -141,6 +142,24 @@ namespace vnotex editorConfig->m_expandTab = p_config.getExpandTabEnabled(); editorConfig->m_tabStopWidth = p_config.getTabStopWidth(); + switch (p_lineEndingPolicy) { + case LineEndingPolicy::Platform: + editorConfig->m_lineEndingPolicy = vte::LineEndingPolicy::Platform; + break; + case LineEndingPolicy::File: + editorConfig->m_lineEndingPolicy = vte::LineEndingPolicy::File; + break; + case LineEndingPolicy::LF: + editorConfig->m_lineEndingPolicy = vte::LineEndingPolicy::LF; + break; + case LineEndingPolicy::CRLF: + editorConfig->m_lineEndingPolicy = vte::LineEndingPolicy::CRLF; + break; + case LineEndingPolicy::CR: + editorConfig->m_lineEndingPolicy = vte::LineEndingPolicy::CR; + break; + } + return editorConfig; }