File indexing completed on 2024-04-21 03:57:24

0001 /*
0002     SPDX-FileCopyrightText: 2017 Grzegorz Szymaszek <gszymaszek@short.pl>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "editorconfig.h"
0008 #include "kateconfig.h"
0009 #include "katedocument.h"
0010 #include "katepartdebug.h"
0011 
0012 /**
0013  * @return whether a string value could be converted to a bool value as
0014  * supported. The value is put in *result.
0015  */
0016 static bool checkBoolValue(QString val, bool *result)
0017 {
0018     val = val.trimmed().toLower();
0019     static const QStringList trueValues{QStringLiteral("1"), QStringLiteral("on"), QStringLiteral("true")};
0020     if (trueValues.contains(val)) {
0021         *result = true;
0022         return true;
0023     }
0024 
0025     static const QStringList falseValues{QStringLiteral("0"), QStringLiteral("off"), QStringLiteral("false")};
0026     if (falseValues.contains(val)) {
0027         *result = false;
0028         return true;
0029     }
0030     return false;
0031 }
0032 
0033 /**
0034  * @return whether a string value could be converted to a integer value. The
0035  * value is put in *result.
0036  */
0037 static bool checkIntValue(const QString &val, int *result)
0038 {
0039     bool ret(false);
0040     *result = val.toInt(&ret);
0041     return ret;
0042 }
0043 
0044 EditorConfig::EditorConfig(KTextEditor::DocumentPrivate *document)
0045     : m_document(document)
0046     , m_handle(editorconfig_handle_init())
0047 {
0048 }
0049 
0050 EditorConfig::~EditorConfig()
0051 {
0052     editorconfig_handle_destroy(m_handle);
0053 }
0054 
0055 int EditorConfig::parse()
0056 {
0057     const int code = editorconfig_parse(m_document->url().toLocalFile().toStdString().c_str(), m_handle);
0058 
0059     if (code != 0) {
0060         if (code == EDITORCONFIG_PARSE_MEMORY_ERROR) {
0061             qCDebug(LOG_KTE) << "Failed to parse .editorconfig, memory error occurred";
0062         } else if (code > 0) {
0063             qCDebug(LOG_KTE) << "Failed to parse .editorconfig, error in line" << code;
0064         } else {
0065             qCDebug(LOG_KTE) << "Failed to parse .editorconfig, unknown error";
0066         }
0067 
0068         return code;
0069     }
0070 
0071     // count of found key-value pairs
0072     const unsigned int count = editorconfig_handle_get_name_value_count(m_handle);
0073 
0074     // if indent_size=tab
0075     bool setIndentSizeAsTabWidth = false;
0076 
0077     // whether corresponding fields were found in .editorconfig
0078     bool indentSizeSet = false;
0079     bool tabWidthSet = false;
0080 
0081     // the following only applies if indent_size=tab and there isn’t tab_width
0082     int tabWidth = m_document->config()->tabWidth();
0083 
0084     for (unsigned int i = 0; i < count; ++i) {
0085         // raw values from EditorConfig library
0086         const char *rawKey = nullptr;
0087         const char *rawValue = nullptr;
0088 
0089         // buffers for integer/boolean values
0090         int intValue;
0091         bool boolValue;
0092 
0093         // fetch next key-value pair
0094         editorconfig_handle_get_name_value(m_handle, i, &rawKey, &rawValue);
0095 
0096         // and convert to Qt strings
0097         const QLatin1String key = QLatin1String(rawKey);
0098         const QLatin1String value = QLatin1String(rawValue);
0099 
0100         if (QLatin1String("charset") == key) {
0101             m_document->setEncoding(value);
0102         } else if (QLatin1String("end_of_line") == key) {
0103             QStringList eols;
0104 
0105             // NOTE: EOLs are declared in Kate::TextBuffer::EndOfLineMode
0106             eols << QStringLiteral("lf") << QStringLiteral("crlf") << QStringLiteral("cr");
0107 
0108             if ((intValue = eols.indexOf(value)) != -1) {
0109                 m_document->config()->setEol(intValue);
0110                 m_document->config()->setAllowEolDetection(false);
0111             } else {
0112                 qCDebug(LOG_KTE) << "End of line in .editorconfig other than unix/dos/mac";
0113             }
0114         } else if (QLatin1String("indent_size") == key) {
0115             if (QLatin1String("tab") == value) {
0116                 setIndentSizeAsTabWidth = true;
0117             } else if (checkIntValue(value, &intValue)) {
0118                 m_document->config()->setIndentationWidth(intValue);
0119                 indentSizeSet = true;
0120             } else {
0121                 qCDebug(LOG_KTE) << "Indent size in .editorconfig not a number, nor tab";
0122             }
0123         } else if (QLatin1String("indent_style") == key) {
0124             if (QLatin1String("tab") == value) {
0125                 m_document->config()->setReplaceTabsDyn(false);
0126             } else if (QLatin1String("space") == value) {
0127                 m_document->config()->setReplaceTabsDyn(true);
0128             } else {
0129                 qCDebug(LOG_KTE) << "Indent style in .editorconfig other than tab or space";
0130             }
0131         } else if (QLatin1String("insert_final_newline") == key && checkBoolValue(value, &boolValue)) {
0132             m_document->config()->setNewLineAtEof(boolValue);
0133         } else if (QLatin1String("max_line_length") == key && checkIntValue(value, &intValue)) {
0134             m_document->config()->setWordWrapAt(intValue);
0135         } else if (QLatin1String("tab_width") == key && checkIntValue(value, &intValue)) {
0136             m_document->config()->setTabWidth(intValue);
0137             tabWidth = intValue;
0138             tabWidthSet = true;
0139         } else if (QLatin1String("trim_trailing_whitespace") == key && checkBoolValue(value, &boolValue)) {
0140             if (boolValue) {
0141                 m_document->config()->setRemoveSpaces(1 /* remove trailing spaces of modified lines (line modification system) */);
0142             } else {
0143                 m_document->config()->setRemoveSpaces(0);
0144             }
0145         }
0146     }
0147 
0148     if (setIndentSizeAsTabWidth) {
0149         m_document->config()->setIndentationWidth(tabWidth);
0150     } else if (!tabWidthSet && indentSizeSet) {
0151         m_document->config()->setTabWidth(m_document->config()->indentationWidth());
0152     }
0153 
0154     return 0;
0155 }