Commit 3e9ba992 authored by Martin Seher's avatar Martin Seher Committed by Milian Wolff
Browse files

Configurable semantic colors

Add a page under _Color Themes -> Highlighting Text Styles -> KDevelop/Semantic Colors_
that allows the configuration of the semantic highlight colors.
Additionally, add a "global color source" setting to the language
settings. When the latter is set to "From Editor Theme" we will
use the colors from the theme as-is without any further blending.
_Bold font for declarations_ will still make the declarations bold
in addition to the bold settings from the config dialog.

Local rainbox colorization is not affected by this at all.

See also:
https://invent.kde.org/kdevelop/kdevelop/uploads/500467bc355906d363e3a76a9044e138/ColorConfig.png

BUG: 395856
parent 03a023bc
Pipeline #201506 passed with stage
in 17 minutes and 44 seconds
......@@ -33,6 +33,12 @@ public:
AllProblemsInlineNotesLevel
};
enum class GlobalColorSource {
AutoGenerated,
FromTheme,
};
Q_ENUM(GlobalColorSource)
virtual int minFilesForSimplifiedParsing() const = 0;
virtual CompletionLevel completionLevel() const = 0;
......@@ -41,6 +47,7 @@ public:
virtual int localColorizationLevel() const = 0;
virtual int globalColorizationLevel() const = 0;
virtual GlobalColorSource globalColorSource() const = 0;
virtual bool highlightSemanticProblems() const = 0;
virtual bool highlightProblematicLines() const = 0;
......
......@@ -151,6 +151,7 @@ set(KDevPlatformLanguage_LIB_SRCS
highlighting/colorcache.cpp
highlighting/configurablecolors.cpp
highlighting/codehighlighting.cpp
highlighting/syntax/syntax.qrc
checks/dataaccessrepository.cpp checks/dataaccess.cpp
checks/controlflowgraph.cpp checks/controlflownode.cpp
......
......@@ -26,12 +26,60 @@
#include <KTextEditor/Document>
#include <KTextEditor/View>
#include <KTextEditor/ConfigInterface>
#include <KSyntaxHighlighting/Definition>
#include <KSyntaxHighlighting/Format>
#define ifDebug(x)
namespace KDevelop {
ColorCache* ColorCache::m_self = nullptr;
CodeHighlightingType getHighlightingTypeFromName(const QString& name)
{
if (name == QLatin1String("Class")) {
return CodeHighlightingType::Class;
} else if (name == QLatin1String("Local Member Variable")) {
return CodeHighlightingType::LocalClassMember;
} else if (name == QLatin1String("Local Member Function")) {
return CodeHighlightingType::LocalMemberFunction;
} else if (name == QLatin1String("Inherited Member Variable")) {
return CodeHighlightingType::InheritedClassMember;
} else if (name == QLatin1String("Inherited Member Function")) {
return CodeHighlightingType::InheritedMemberFunction;
} else if (name == QLatin1String("Function")) {
return CodeHighlightingType::Function;
} else if (name == QLatin1String("Function Argument")) {
return CodeHighlightingType::FunctionVariable;
} else if (name == QLatin1String("Type Alias")) {
return CodeHighlightingType::TypeAlias;
} else if (name == QLatin1String("Forward Declaration")) {
return CodeHighlightingType::ForwardDeclaration;
} else if (name == QLatin1String("Namespace")) {
return CodeHighlightingType::Namespace;
} else if (name == QLatin1String("Local Variable")) {
return CodeHighlightingType::LocalVariable;
} else if (name == QLatin1String("Global Variable")) {
return CodeHighlightingType::GlobalVariable;
} else if (name == QLatin1String("Member Variable")) {
return CodeHighlightingType::MemberVariable;
} else if (name == QLatin1String("Namespace Variable")) {
return CodeHighlightingType::NamespaceVariable;
} else if (name == QLatin1String("Enumeration")) {
return CodeHighlightingType::Enum;
} else if (name == QLatin1String("Enumerator")) {
return CodeHighlightingType::Enumerator;
} else if (name == QLatin1String("Macro")) {
return CodeHighlightingType::Macro;
} else if (name == QLatin1String("Macro Function")) {
return CodeHighlightingType::MacroFunctionLike;
} else if (name == QLatin1String("Highlight Uses")) {
return CodeHighlightingType::HighlightUses;
} else if (name == QLatin1String("Error Variable")) {
return CodeHighlightingType::ErrorVariable;
}
return CodeHighlightingType::Unknown;
}
ColorCache::ColorCache(QObject* parent)
: QObject(parent)
, m_defaultColors(new ConfigurableHighlightingColors)
......@@ -39,6 +87,7 @@ ColorCache::ColorCache(QObject* parent)
, m_colorOffset(0)
, m_localColorRatio(0)
, m_globalColorRatio(0)
, m_globalColorSource(ICompletionSettings::GlobalColorSource::AutoGenerated)
, m_boldDeclarations(true)
{
Q_ASSERT(m_self == nullptr);
......@@ -91,8 +140,6 @@ ColorCache* ColorCache::self()
void ColorCache::generateColors()
{
m_defaultColors->reset(this, m_view.data());
// Primary colors taken from: http://colorbrewer2.org/?type=qualitative&scheme=Paired&n=12
const QColor colors[] = {
{"#b15928"}, {"#ff7f00"}, {"#b2df8a"}, {"#33a02c"}, {"#a6cee3"},
......@@ -184,6 +231,7 @@ void ColorCache::updateColorsFromView(KTextEditor::View* view)
#endif
m_view = view;
bool anyAttrChanged = false;
if (!foreground.isValid()) {
// fallback to colorscheme variant
ifDebug(qCDebug(LANGUAGE) << "updating from scheme"; )
......@@ -191,12 +239,77 @@ void ColorCache::updateColorsFromView(KTextEditor::View* view)
} else if (m_foregroundColor != foreground || m_backgroundColor != background) {
m_foregroundColor = foreground;
m_backgroundColor = background;
m_defaultColors->reset(this, view);
anyAttrChanged = true;
}
anyAttrChanged |= updateColorsFromTheme(view->theme());
if (anyAttrChanged) {
ifDebug(qCDebug(LANGUAGE) << "updating from document"; )
update();
}
}
bool ColorCache::updateColorsFromTheme(const KSyntaxHighlighting::Theme& theme)
{
if (m_globalColorSource != ICompletionSettings::GlobalColorSource::FromTheme)
return false;
// from ktexteditor/src/syntax/kateextendedattribute.h
static const int SelectedBackground = QTextFormat::UserProperty + 2;
const auto schemeDefinition = m_schemeRepo.definitionForName(QStringLiteral("Semantic Colors"));
const auto formats = schemeDefinition.formats();
bool anyAttrChanged = false;
for (const auto& format : formats) {
const auto type = getHighlightingTypeFromName(format.name());
const auto attr = m_defaultColors->attribute(type);
auto forwardProperty = [&](auto formatGetter, auto attrProperty, auto attrSetter) {
auto formatProperty = (format.*formatGetter)(theme);
if (attrProperty != formatProperty) {
(attr.data()->*attrSetter)(formatProperty);
anyAttrChanged = true;
}
};
using namespace KSyntaxHighlighting;
using namespace KTextEditor;
forwardProperty(&Format::isBold, attr->fontBold(), &Attribute::setFontBold);
forwardProperty(&Format::isItalic, attr->fontItalic(), &Attribute::setFontItalic);
forwardProperty(&Format::isUnderline, attr->fontUnderline(), &Attribute::setFontUnderline);
forwardProperty(&Format::isStrikeThrough, attr->fontStrikeOut(), &Attribute::setFontStrikeOut);
forwardProperty(&Format::textColor, attr->foreground().color(), &Attribute::setForeground);
forwardProperty(&Format::selectedTextColor, attr->selectedForeground().color(),
&Attribute::setSelectedForeground);
if (format.hasBackgroundColor(theme)) {
forwardProperty(&Format::backgroundColor, attr->background().color(), &Attribute::setBackground);
} else if (type == CodeHighlightingType::HighlightUses) {
auto background = QColor::fromRgb(theme.editorColor(KSyntaxHighlighting::Theme::SearchHighlight));
if (attr->background().color() != background) {
attr->setBackground(background);
anyAttrChanged = true;
}
} else if (attr->background() != QBrush()) {
attr->setBackground(QBrush());
anyAttrChanged = true;
}
// from KSyntaxHighlighting::Format::isDefaultTextStyle
if (format.selectedBackgroundColor(theme).rgba() != theme.selectedBackgroundColor(KSyntaxHighlighting::Theme::Normal)) {
forwardProperty(&Format::selectedBackgroundColor, attr->selectedBackground().color(),
&Attribute::setSelectedBackground);
} else if (attr->hasProperty(SelectedBackground)) {
attr->clearProperty(SelectedBackground);
anyAttrChanged = true;
}
}
return anyAttrChanged;
}
void ColorCache::updateColorsFromScheme()
{
KColorScheme scheme(QPalette::Normal, KColorScheme::View);
......@@ -213,21 +326,45 @@ void ColorCache::updateColorsFromScheme()
void ColorCache::updateColorsFromSettings()
{
int localRatio = ICore::self()->languageController()->completionSettings()->localColorizationLevel();
int globalRatio = ICore::self()->languageController()->completionSettings()->globalColorizationLevel();
bool boldDeclartions = ICore::self()->languageController()->completionSettings()->boldDeclarations();
auto settings = ICore::self()->languageController()->completionSettings();
if (localRatio != m_localColorRatio || globalRatio != m_globalColorRatio) {
m_localColorRatio = localRatio;
m_globalColorRatio = globalRatio;
update();
const auto globalColorSource = settings->globalColorSource();
const auto globalColorSourceChanged = globalColorSource != m_globalColorSource;
m_globalColorSource = globalColorSource;
const auto globalRatio = settings->globalColorizationLevel();
const auto globalRatioChanged = globalRatio != m_globalColorRatio;
m_globalColorRatio = globalRatio;
const auto localRatio = settings->localColorizationLevel();
const auto localRatioChanged = localRatio != m_localColorRatio;
m_localColorRatio = localRatio;
const auto boldDeclartions = settings->boldDeclarations();
const auto boldDeclarationsChanged = boldDeclartions != m_boldDeclarations;
m_boldDeclarations = boldDeclartions;
if (m_view && (globalRatioChanged || globalColorSourceChanged)) {
updateDefaultColorsFromSource();
}
if (boldDeclartions != m_boldDeclarations) {
m_boldDeclarations = boldDeclartions;
if (globalColorSourceChanged || globalRatioChanged || localRatioChanged || boldDeclarationsChanged) {
update();
}
}
void ColorCache::updateDefaultColorsFromSource()
{
switch (m_globalColorSource) {
case ICompletionSettings::GlobalColorSource::AutoGenerated:
m_defaultColors->reset(this, m_view.data());
break;
case ICompletionSettings::GlobalColorSource::FromTheme:
updateColorsFromTheme(m_view->theme());
break;
}
}
void ColorCache::update()
{
if (!m_self) {
......
......@@ -12,8 +12,15 @@
#include <QColor>
#include <QPointer>
#include <KSyntaxHighlighting/Repository>
#include <interfaces/icompletionsettings.h>
#include <language/languageexport.h>
namespace KSyntaxHighlighting {
class Theme;
}
namespace KTextEditor {
class Document;
class View;
......@@ -126,6 +133,10 @@ private:
/// @see generateColors(), updateColorsFromScheme()
void updateColorsFromView(KTextEditor::View* view);
bool updateColorsFromTheme(const KSyntaxHighlighting::Theme& theme);
void updateDefaultColorsFromSource();
/// the default colors for the different types
ConfigurableHighlightingColors* m_defaultColors;
......@@ -153,11 +164,15 @@ private:
/// Between 0 and 255, where 255 means only foreground color, and 0 only the chosen color.
uchar m_globalColorRatio;
ICompletionSettings::GlobalColorSource m_globalColorSource;
/// Whether declarations have to be rendered with a bold style or not.
bool m_boldDeclarations;
/// The view we are listening to for setting changes.
QPointer<KTextEditor::View> m_view;
KSyntaxHighlighting::Repository m_schemeRepo;
};
}
......
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE language SYSTEM "language.dtd">
<language
name="Semantic Colors"
section="KDevelop"
version="1"
kateversion="5.0"
mimetype=""
extensions=""
author="Martin Seher (martin.seher@gmail.com)"
hidden="true"
>
<highlighting>
<contexts>
<context attribute="Class" lineEndContext="#stay" name="Normal">
</context>
</contexts>
<itemDatas>
<itemData name="Class" defStyleNum="dsDataType" spellChecking="false" bold="0" italic="0" />
<itemData name="Local Member Variable" defStyleNum="dsVariable" spellChecking="false" />
<itemData name="Local Member Function" defStyleNum="dsFunction" spellChecking="false" />
<itemData name="Inherited Member Variable" defStyleNum="dsVariable" spellChecking="false" />
<itemData name="Inherited Member Function" defStyleNum="dsFunction" spellChecking="false" />
<itemData name="Function" defStyleNum="dsFunction" spellChecking="false" />
<itemData name="Function Argument" defStyleNum="dsNormal" spellChecking="false" />
<itemData name="Type Alias" defStyleNum="dsDataType" spellChecking="false" bold="0" italic="0" />
<itemData name="Forward Declaration" defStyleNum="dsNormal" spellChecking="false" />
<itemData name="Namespace" defStyleNum="dsNormal" spellChecking="false" />
<itemData name="Local Variable" defStyleNum="dsVariable" spellChecking="false" />
<itemData name="Global Variable" defStyleNum="dsVariable" spellChecking="false" />
<itemData name="Member Variable" defStyleNum="dsVariable" spellChecking="false" />
<itemData name="Namespace Variable" defStyleNum="dsVariable" spellChecking="false" />
<itemData name="Enumeration" defStyleNum="dsNormal" spellChecking="false" />
<itemData name="Enumerator" defStyleNum="dsNormal" spellChecking="false" />
<itemData name="Macro" defStyleNum="dsPreprocessor" spellChecking="false" />
<itemData name="Macro Function" defStyleNum="dsPreprocessor" spellChecking="false" />
<itemData name="Highlight Uses" defStyleNum="dsNormal" spellChecking="false" />
<itemData name="Error Variable" defStyleNum="dsError" spellChecking="false" />
</itemDatas>
</highlighting>
</language>
<!-- kate: indent-width 2; tab-width 2; -->
<!DOCTYPE RCC><RCC version="1.0">
<qresource prefix="/org.kde.syntax-highlighting/syntax-addons">
<file>kdevelop-semantic-colors.xml</file>
</qresource>
</RCC>
......@@ -7,9 +7,12 @@
#include "completionsettings.h"
#include "languageconfig.h"
#include <KShell>
#include <KSharedConfig>
#include <QMetaEnum>
namespace KDevelop
{
......@@ -70,6 +73,15 @@ int CompletionSettings::globalColorizationLevel() const
return m_languageGroup.readEntry("globalColorization", m_globalColorizationLevel);
}
CompletionSettings::GlobalColorSource CompletionSettings::globalColorSource() const
{
const auto metaEnum = QMetaEnum::fromType<GlobalColorSource>();
const auto globalColorSource = m_languageGroup.readEntry("globalColorSource", QByteArray());
bool ok = false;
auto value = metaEnum.keyToValue(globalColorSource.constData(), &ok);
return ok ? static_cast<GlobalColorSource>(value) : m_globalColorSource;
}
int CompletionSettings::localColorizationLevel() const
{
return m_languageGroup.readEntry("localColorization", m_localColorizationLevel);
......
......@@ -27,6 +27,8 @@ public:
int globalColorizationLevel() const override;
GlobalColorSource globalColorSource() const override;
bool highlightSemanticProblems() const override;
bool highlightProblematicLines() const override;
......@@ -54,6 +56,7 @@ private:
const bool m_boldDeclarations = true;
const int m_localColorizationLevel = 170;
const int m_globalColorizationLevel = 255;
const GlobalColorSource m_globalColorSource = GlobalColorSource::AutoGenerated;
const int m_minFilesForSimplifiedParsing = 100000;
const QString m_todoMarkerWords;
......
......@@ -24,6 +24,12 @@
<entry name="localColorization" key="localColorization" type="Int">
<default>170</default>
</entry>
<entry name="globalColorSource" key="globalColorSource" type="Enum">
<choices>
<choice name="AutoGenerated" />
<choice name="FromTheme" />
</choices>
</entry>
<entry name="globalColorization" key="globalColorization" type="Int">
<default>255</default>
</entry>
......
......@@ -33,6 +33,12 @@ LanguagePreferences::LanguagePreferences(QWidget* parent)
preferencesDialog = new Ui::LanguagePreferences;
preferencesDialog->setupUi(this);
preferencesDialog->kcfg_minFilesForSimplifiedParsing->setSuffix(ki18ncp("@item:valuesuffix", " file", " files"));
connect(preferencesDialog->kcfg_globalColorSource, qOverload<int>(&QComboBox::currentIndexChanged), this,
[this](int index) {
auto canColorize = index == static_cast<int>(ICompletionSettings::GlobalColorSource::AutoGenerated);
preferencesDialog->kcfg_globalColorization->setEnabled(canColorize);
});
}
void LanguagePreferences::notifySettingsChanged()
......
......@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>599</width>
<height>592</height>
<height>709</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
......@@ -29,6 +29,19 @@
<string comment="@title:group">Code Completion</string>
</property>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_6">
<property name="toolTip">
<string comment="@info:tooltip">If disabled, the code completion widget will never show automatically.</string>
</property>
<property name="text">
<string comment="@label">Enable automatic invocation:</string>
</property>
<property name="buddy">
<cstring>kcfg_automaticInvocation</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="kcfg_automaticInvocation">
<property name="toolTip">
......@@ -39,6 +52,19 @@
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_7">
<property name="toolTip">
<string comment="@info:tooltip">Choose whether to display additional information for the currently selected codecompletion item.</string>
</property>
<property name="text">
<string comment="@label">Additional information for current item:</string>
</property>
<property name="buddy">
<cstring>kcfg_showMultiLineSelectionInformation</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="kcfg_showMultiLineSelectionInformation">
<property name="toolTip">
......@@ -49,6 +75,19 @@
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label">
<property name="toolTip">
<string>&lt;p&gt;The code completion UI has a minimal mode and a detailed mode.&lt;br&gt;Choose in what cases full code completion will be displayed.&lt;/p&gt;</string>
</property>
<property name="text">
<string comment="@label">Detailed completion:</string>
</property>
<property name="buddy">
<cstring>kcfg_completionDetail</cstring>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="kcfg_completionDetail">
<property name="toolTip">
......@@ -74,36 +113,6 @@
</item>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label">
<property name="toolTip">
<string>&lt;p&gt;The code completion UI has a minimal mode and a detailed mode.&lt;br&gt;Choose in what cases full code completion will be displayed.&lt;/p&gt;</string>
</property>
<property name="text">
<string comment="@label">Detailed completion:</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_6">
<property name="toolTip">
<string comment="@info:tooltip">If disabled, the code completion widget will never show automatically.</string>
</property>
<property name="text">
<string comment="@label">Enable automatic invocation:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_7">
<property name="toolTip">
<string comment="@info:tooltip">Choose whether to display additional information for the currently selected codecompletion item.</string>
</property>
<property name="text">
<string comment="@label">Additional information for current item:</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
......@@ -151,20 +160,37 @@
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QLabel" name="globalColorizationLabel">
<item row="2" column="1">
<widget class="QComboBox" name="kcfg_globalColorSource">
<property name="toolTip">
<string>&lt;p&gt;This settings lets you change the intensity of globally accessible types, for example classes, methods, functions etc.&lt;/p&gt;</string>
<string>&lt;p&gt;By default, the global colors for types, functions, namespaces and so forth are auto-generated and will adapt to the selected editor theme based on the global colorization intensity. If you want to have full control over the colors, you can instead use the editor theme as color source. You can then fine tune the colors in the settings under Editor &amp;gt; Color Themes &amp;gt; Highlight Text Styles &amp;gt; KDevelop/Semantic Colors.&lt;/p&gt;</string>
</property>
<item>
<property name="text">
<string>Auto-Generated</string>
</property>
</item>
<item>
<property name="text">
<string>From Editor Theme</string>
</property>
</item>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_9">
<property name="toolTip">
<string>&lt;p&gt;By default, the global colors for types, functions, namespaces and so forth are auto-generated and will adapt to the selected editor theme based on the global colorization intensity. If you want to have full control over the colors, you can instead use the editor theme as color source. You can then fine tune the colors in the settings under Editor &amp;gt; Color Themes &amp;gt; Highlight Text Styles &amp;gt; KDevelop/Semantic Colors.&lt;/p&gt;</string>
</property>
<property name="text">
<string comment="@label:slider">Global colorization intensity:</string>
<string>Global colors:</string>
</property>
<property name="buddy">
<cstring>kcfg_localColorization</cstring>
<cstring>kcfg_globalColorSource</cstring>
</property>
</widget>
</item>
<item row="8" column="1">
<item row="3" column="1">
<widget class="QSlider" name="kcfg_globalColorization">
<property name="toolTip">
<string>&lt;p&gt;This settings lets you change the intensity of globally accessible types, for example classes, methods, functions etc.&lt;/p&gt;</string>
......@@ -186,7 +212,20 @@
</property>
</widget>
</item>
<item row="9" column="0">
<item row="3" column="0">
<widget class="QLabel" name="globalColorizationLabel">
<property name="toolTip">
<string>&lt;p&gt;This settings lets you change the intensity of globally accessible types, for example classes, methods, functions etc.&lt;/p&gt;</string>
</property>
<property name="text">
<string comment="@label:slider">Global colorization intensity:</string>
</property>
<property name="buddy">
<cstring>kcfg_globalColorization</cstring>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_4">
<property name="toolTip">
<string>&lt;p&gt;Highlight semantic problems, such as non-existent or inaccessible declarations.&lt;/p&gt;</string>
......@@ -194,9 +233,12 @@
<property name="text">
<string comment="@label">Highlight semantic problems:</string>
</property>
<property name="buddy">
<cstring>kcfg_highlightSemanticProblems</cstring>
</property>
</widget>
</item>