Skip to content

Commit

Permalink
EditorConfig support
Browse files Browse the repository at this point in the history
CHANGELOG: Support for EditorConfig configuration files (for details: http://editorconfig.org/)

Contributed by Grzegorz Szymaszek.

Differential Revision: https://phabricator.kde.org/D4537
  • Loading branch information
gszy authored and christoph-cullmann committed Feb 19, 2017
1 parent 6fa17cd commit f9f133b
Show file tree
Hide file tree
Showing 4 changed files with 254 additions and 2 deletions.
11 changes: 11 additions & 0 deletions src/CMakeLists.txt
Expand Up @@ -11,6 +11,12 @@ if(LIBGIT2_FOUND)
set (KTEXTEDITOR_OPTIONAL_LIBS ${KTEXTEDITOR_OPTIONAL_LIBS} LibGit2::LibGit2)
endif()

if(EditorConfig_FOUND)
add_definitions(-DEDITORCONFIG_FOUND)
SET (CMAKE_REQUIRED_LIBRARIES editorconfig)
set (KTEXTEDITOR_OPTIONAL_LIBS ${KTEXTEDITOR_OPTIONAL_LIBS} editorconfig)
endif()

# handle include files, both normal ones and generated ones
add_subdirectory(include)

Expand Down Expand Up @@ -213,6 +219,11 @@ inputmode/katenormalinputmode.cpp
inputmode/katenormalinputmodefactory.cpp
)

# optionally compile with EditorConfig support
if(EditorConfig_FOUND)
set(ktexteditor_LIB_SRCS ${ktexteditor_LIB_SRCS} document/editorconfig.cpp)
endif()

ki18n_wrap_ui(ktexteditor_LIB_SRCS
dialogs/textareaappearanceconfigwidget.ui
dialogs/bordersappearanceconfigwidget.ui
Expand Down
166 changes: 166 additions & 0 deletions src/document/editorconfig.cpp
@@ -0,0 +1,166 @@
/* This file is part of the KTextEditor project.
*
* Copyright (C) 2017 Grzegorz Szymaszek <gszymaszek@short.pl>
*
* 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 "editorconfig.h"

/**
* @return whether a string value could be converted to a bool value as
* supported. The value is put in *result.
*/
static bool checkBoolValue(QString val, bool *result)
{
val = val.trimmed().toLower();
static const QStringList trueValues = QStringList() << QStringLiteral("1") << QStringLiteral("on") << QStringLiteral("true");
if (trueValues.contains(val)) {
*result = true;
return true;
}

static const QStringList falseValues = QStringList() << QStringLiteral("0") << QStringLiteral("off") << QStringLiteral("false");
if (falseValues.contains(val)) {
*result = false;
return true;
}
return false;
}

/**
* @return whether a string value could be converted to a integer value. The
* value is put in *result.
*/
static bool checkIntValue(QString val, int *result)
{
bool ret(false);
*result = val.toInt(&ret);
return ret;
}

EditorConfig::EditorConfig(KTextEditor::DocumentPrivate *document)
: m_document(document)
, m_handle(editorconfig_handle_init())
{
}

EditorConfig::~EditorConfig()
{
editorconfig_handle_destroy(m_handle);
}

int EditorConfig::parse()
{
const int code = editorconfig_parse(m_document->url().toLocalFile().toStdString().c_str(), m_handle);

if (code != 0) {
if (code == EDITORCONFIG_PARSE_MEMORY_ERROR) {
qCDebug(LOG_KTE) << "Failed to parse .editorconfig, memory error occurred";
} else if (code > 0) {
qCDebug(LOG_KTE) << "Failed to parse .editorconfig, error in line" << code;
} else {
qCDebug(LOG_KTE) << "Failed to parse .editorconfig, unknown error";
}

return code;
}

// count of found key-value pairs
const unsigned int count = editorconfig_handle_get_name_value_count(m_handle);

// if indent_size=tab
bool setIndentSizeAsTabWidth = false;

// whether corresponding fields were found in .editorconfig
bool indentSizeSet = false;
bool tabWidthSet = false;

// the following only applies if indent_size=tab and there isn’t tab_width
int tabWidth = m_document->config()->tabWidth();

for (unsigned int i = 0; i < count; ++ i) {
// raw values from EditorConfig library
const char *rawKey = nullptr;
const char *rawValue = nullptr;

// buffers for integer/boolean values
int intValue;
bool boolValue;

// fetch next key-value pair
editorconfig_handle_get_name_value(m_handle, i, &rawKey, &rawValue);

// and convert to Qt strings
const QLatin1String key = QLatin1String(rawKey);
const QLatin1String value = QLatin1String(rawValue);

if (QLatin1String("charset") == key) {
m_document->setEncoding(value);
} else if (QLatin1String("end_of_line") == key) {
QStringList eols;

// NOTE: EOLs are declared in Kate::TextBuffer::EndOfLineMode
eols << QLatin1String("lf") << QLatin1String("crlf") << QLatin1String("cr");

if ((intValue = eols.indexOf(value)) != -1) {
m_document->config()->setEol(intValue);
m_document->config()->setAllowEolDetection(false);
} else {
qCDebug(LOG_KTE) << "End of line in .editorconfig other than unix/dos/mac";
}
} else if (QLatin1String("indent_size") == key) {
if (QLatin1String("tab") == value) {
setIndentSizeAsTabWidth = true;
} else if (checkIntValue(value, &intValue)) {
m_document->config()->setIndentationWidth(intValue);
indentSizeSet = true;
} else {
qCDebug(LOG_KTE) << "Indent size in .editorconfig not a number, nor tab";
}
} else if (QLatin1String("indent_style") == key) {
if (QLatin1String("tab") == value) {
m_document->config()->setReplaceTabsDyn(false);
} else if (QLatin1String("space") == value) {
m_document->config()->setReplaceTabsDyn(true);
} else {
qCDebug(LOG_KTE) << "Indent style in .editorconfig other than tab or space";
}
} else if (QLatin1String("insert_final_newline") == key && checkBoolValue(value, &boolValue)) {
m_document->config()->setNewLineAtEof(boolValue);
} else if (QLatin1String("max_line_length") == key && checkIntValue(value, &intValue)) {
m_document->config()->setWordWrapAt(intValue);
} else if (QLatin1String("tab_width") == key && checkIntValue(value, &intValue)) {
m_document->config()->setTabWidth(intValue);
tabWidth = intValue;
tabWidthSet = true;
} else if (QLatin1String("trim_trailing_whitespace") == key && checkBoolValue(value, &boolValue)) {
if (boolValue) {
m_document->config()->setRemoveSpaces(2);
} else {
m_document->config()->setRemoveSpaces(0);
}
}
}

if (setIndentSizeAsTabWidth) {
m_document->config()->setIndentationWidth(tabWidth);
} else if (! tabWidthSet && indentSizeSet) {
m_document->config()->setTabWidth(m_document->config()->indentationWidth());
}

return 0;
}
63 changes: 63 additions & 0 deletions src/document/editorconfig.h
@@ -0,0 +1,63 @@
/* This file is part of the KTextEditor project.
*
* Copyright (C) 2017 Grzegorz Szymaszek <gszymaszek@short.pl>
*
* 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.
*/

#ifndef _EDITORCONFIG_H_
#define _EDITORCONFIG_H_

#include <QLatin1String>

#include <editorconfig/editorconfig.h>
#include <editorconfig/editorconfig_handle.h>

#include "kateconfig.h"
#include "katedocument.h"
#include "kateglobal.h"
#include "katepartdebug.h"

class KateDocumentConfig;

namespace KTextEditor { class DocumentPrivate; }

class EditorConfig
{
public:
EditorConfig(KTextEditor::DocumentPrivate *document);
~EditorConfig();
/**
* Runs EditorConfig parser and sets proper parent DocumentPrivate
* configuration. Implemented options: charset, end_of_line, indent_size,
* indent_style, insert_final_newline, max_line_length, tab_width,
* trim_trailing_whitespace.
*
* @see https://github.com/editorconfig/editorconfig/wiki/EditorConfig-Properties
*
* @return 0 if EditorConfig library reported successful file parsing,
* positive integer if a parsing error occurred (the return value would be
* the line number of parsing error), negative integer if another error
* occurred. In other words, returns value returned by editorconfig_parse.
*/
int parse();

private:
KTextEditor::DocumentPrivate *m_document;
editorconfig_handle m_handle;
};

#endif
16 changes: 14 additions & 2 deletions src/document/katedocument.cpp
Expand Up @@ -51,6 +51,10 @@
#include "kateabstractinputmode.h"
#include "katetemplatehandler.h"

#ifdef EDITORCONFIG_FOUND
#include "editorconfig.h"
#endif

#include <KTextEditor/DocumentCursor>
#include <KTextEditor/Attribute>

Expand Down Expand Up @@ -2545,7 +2549,7 @@ void KTextEditor::DocumentPrivate::readDirConfig()
}

/**
* search .kateconfig upwards
* first search .kateconfig upwards
* with recursion guard
*/
QSet<QString> seenDirectories;
Expand All @@ -2571,7 +2575,7 @@ void KTextEditor::DocumentPrivate::readDirConfig()
linesRead++;
}

break;
return;
}

/**
Expand All @@ -2581,6 +2585,14 @@ void KTextEditor::DocumentPrivate::readDirConfig()
break;
}
}

#ifdef EDITORCONFIG_FOUND
// if there wasn’t any .kateconfig file and KTextEditor was compiled with
// EditorConfig support, try to load document config from a .editorconfig
// file, if such is provided
EditorConfig editorConfig(this);
editorConfig.parse();
#endif
}

void KTextEditor::DocumentPrivate::activateDirWatch(const QString &useFileName)
Expand Down

0 comments on commit f9f133b

Please sign in to comment.