diff --git a/language/highlighting/codehighlighting.cpp b/language/highlighting/codehighlighting.cpp index 59a7ffc8da..7eb3355d56 100644 --- a/language/highlighting/codehighlighting.cpp +++ b/language/highlighting/codehighlighting.cpp @@ -1,610 +1,627 @@ /* * This file is part of KDevelop * * Copyright 2007-2010 David Nolden * Copyright 2006 Hamish Rodda * Copyright 2009 Milian Wolff * * This program 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 program 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 General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "codehighlighting.h" #include #include "../../interfaces/icore.h" #include "../../interfaces/ilanguagecontroller.h" #include "../../interfaces/icompletionsettings.h" #include "../../util/foregroundlock.h" #include "util/debug.h" #include "../duchain/declaration.h" #include "../duchain/types/functiontype.h" #include "../duchain/types/enumeratortype.h" #include "../duchain/types/typealiastype.h" #include "../duchain/types/enumerationtype.h" #include "../duchain/types/structuretype.h" #include "../duchain/functiondefinition.h" #include "../duchain/use.h" #include "colorcache.h" #include "configurablecolors.h" #include #include #include #include using namespace KTextEditor; static const float highlightingZDepth = -500; #define ifDebug(x) namespace KDevelop { ///@todo Don't highlighting everything, only what is visible on-demand CodeHighlighting::CodeHighlighting( QObject * parent ) : QObject(parent), m_localColorization(true), m_globalColorization(true), m_dataMutex(QMutex::Recursive) { qRegisterMetaType("KDevelop::IndexedString"); adaptToColorChanges(); connect(ColorCache::self(), &ColorCache::colorsGotChanged, this, &CodeHighlighting::adaptToColorChanges); } CodeHighlighting::~CodeHighlighting( ) { qDeleteAll(m_highlights.values()); } void CodeHighlighting::adaptToColorChanges() { QMutexLocker lock(&m_dataMutex); // disable local highlighting if the ratio is set to 0 m_localColorization = ICore::self()->languageController()->completionSettings()->localColorizationLevel() > 0; // disable global highlighting if the ratio is set to 0 m_globalColorization = ICore::self()->languageController()->completionSettings()->globalColorizationLevel() > 0; m_declarationAttributes.clear(); m_definitionAttributes.clear(); m_depthAttributes.clear(); m_referenceAttributes.clear(); } KTextEditor::Attribute::Ptr CodeHighlighting::attributeForType( Types type, Contexts context, const QColor &color ) const { QMutexLocker lock(&m_dataMutex); KTextEditor::Attribute::Ptr a; switch (context) { case DefinitionContext: a = m_definitionAttributes[type]; break; case DeclarationContext: a = m_declarationAttributes[type]; break; case ReferenceContext: a = m_referenceAttributes[type]; break; } if ( !a || color.isValid() ) { a = KTextEditor::Attribute::Ptr(new KTextEditor::Attribute(*ColorCache::self()->defaultColors()->getAttribute(type))); if ( context == DefinitionContext || context == DeclarationContext ) { if (ICore::self()->languageController()->completionSettings()->boldDeclarations()) { a->setFontBold(); } } if( color.isValid() ) { a->setForeground(color); // a->setBackground(QColor(mix(0xffffff-color, backgroundColor(), 255-backgroundTinting))); } else { switch (context) { case DefinitionContext: m_definitionAttributes.insert(type, a); break; case DeclarationContext: m_declarationAttributes.insert(type, a); break; case ReferenceContext: m_referenceAttributes.insert(type, a); break; } } } return a; } ColorMap emptyColorMap() { ColorMap ret(ColorCache::self()->validColorCount()+1, 0); return ret; } CodeHighlightingInstance* CodeHighlighting::createInstance() const { return new CodeHighlightingInstance(this); } bool CodeHighlighting::hasHighlighting(IndexedString url) const { DocumentChangeTracker* tracker = ICore::self()->languageController()->backgroundParser()->trackerForUrl(url); if(tracker) { QMutexLocker lock(&m_dataMutex); return m_highlights.contains(tracker) && !m_highlights[tracker]->m_highlightedRanges.isEmpty(); } return false; } void CodeHighlighting::highlightDUChain(ReferencedTopDUContext context) { ENSURE_CHAIN_NOT_LOCKED IndexedString url; { DUChainReadLocker lock; if (!context) return; url = context->url(); } // This prevents the background-parser from updating the top-context while we're working with it UrlParseLock urlLock(context->url()); DUChainReadLocker lock; qint64 revision = context->parsingEnvironmentFile()->modificationRevision().revision; qCDebug(LANGUAGE) << "highlighting du chain" << url.toUrl(); if ( !m_localColorization && !m_globalColorization ) { qCDebug(LANGUAGE) << "highlighting disabled"; QMetaObject::invokeMethod(this, "clearHighlightingForDocument", Qt::QueuedConnection, Q_ARG(KDevelop::IndexedString, url)); return; } CodeHighlightingInstance* instance = createInstance(); lock.unlock(); instance->highlightDUChain(context.data()); DocumentHighlighting* highlighting = new DocumentHighlighting; highlighting->m_document = url; highlighting->m_waitingRevision = revision; highlighting->m_waiting = instance->m_highlight; std::sort(highlighting->m_waiting.begin(), highlighting->m_waiting.end()); QMetaObject::invokeMethod(this, "applyHighlighting", Qt::QueuedConnection, Q_ARG(void*, highlighting)); delete instance; } void CodeHighlightingInstance::highlightDUChain(TopDUContext* context) { m_contextClasses.clear(); m_useClassCache = true; //Highlight highlightDUChain(context, QHash(), emptyColorMap()); m_functionColorsForDeclarations.clear(); m_functionDeclarationsForColors.clear(); m_useClassCache = false; m_contextClasses.clear(); } void CodeHighlightingInstance::highlightDUChain(DUContext* context, QHash colorsForDeclarations, ColorMap declarationsForColors) { DUChainReadLocker lock; TopDUContext* top = context->topContext(); //Merge the colors from the function arguments foreach( const DUContext::Import &imported, context->importedParentContexts() ) { if(!imported.context(top) || (imported.context(top)->type() != DUContext::Other && imported.context(top)->type() != DUContext::Function)) continue; //For now it's enough simply copying them, because we only pass on colors within function bodies. if (m_functionColorsForDeclarations.contains(imported.context(top))) colorsForDeclarations = m_functionColorsForDeclarations[imported.context(top)]; if (m_functionDeclarationsForColors.contains(imported.context(top))) declarationsForColors = m_functionDeclarationsForColors[imported.context(top)]; } QList takeFreeColors; foreach (Declaration* dec, context->localDeclarations()) { if (!useRainbowColor(dec)) { highlightDeclaration(dec, QColor(QColor::Invalid)); continue; } //Initially pick a color using the hash, so the chances are good that the same identifier gets the same color always. - uint colorNum = dec->identifier().hash() % ColorCache::self()->validColorCount(); + uint colorNum = dec->identifier().hash() % ColorCache::self()->primaryColorCount(); if( declarationsForColors[colorNum] ) { takeFreeColors << dec; //Use one of the colors that stays free continue; } colorsForDeclarations[dec] = colorNum; declarationsForColors[colorNum] = dec; highlightDeclaration(dec, ColorCache::self()->generatedColor(colorNum)); } - foreach( Declaration* dec, takeFreeColors ) { - uint colorNum = dec->identifier().hash() % ColorCache::self()->validColorCount(); - uint oldColorNum = colorNum; - while( declarationsForColors[colorNum] ) { - colorNum = (colorNum+1) % ColorCache::self()->validColorCount(); - if( colorNum == oldColorNum ) { - colorNum = ColorCache::self()->validColorCount(); - break; - } + foreach (Declaration* dec, takeFreeColors) { + uint colorNum = dec->identifier().hash() % ColorCache::self()->primaryColorCount(); + uint oldColorNum = colorNum; + while (declarationsForColors[colorNum]) { + colorNum = (colorNum + 1) % ColorCache::self()->primaryColorCount(); + if (colorNum == oldColorNum) { + colorNum = ColorCache::self()->primaryColorCount(); + break; + } + } + + if (colorNum < ColorCache::self()->primaryColorCount()) { + // Use primary color + colorsForDeclarations[dec] = colorNum; + declarationsForColors[colorNum] = dec; + highlightDeclaration(dec, ColorCache::self()->generatedColor(colorNum)); + } else { + // Try to use supplementary color + colorNum = ColorCache::self()->primaryColorCount(); + while (declarationsForColors[colorNum]) { + colorNum++; + if (colorNum == ColorCache::self()->validColorCount()) { + //If no color could be found, use default color + highlightDeclaration(dec, QColor(QColor::Invalid)); + break; + } + } + if (colorNum < ColorCache::self()->validColorCount()) { + // Use supplementary color + colorsForDeclarations[dec] = colorNum; + declarationsForColors[colorNum] = dec; + highlightDeclaration(dec, ColorCache::self()->generatedColor(colorNum)); + } + + } } - if(colorNum != ColorCache::self()->validColorCount()) { - //If no color could be found, use default color,, not black - colorsForDeclarations[dec] = colorNum; - declarationsForColors[colorNum] = dec; - highlightDeclaration(dec, ColorCache::self()->generatedColor(colorNum)); - }else{ - highlightDeclaration(dec, QColor(QColor::Invalid)); - } - } for(int a = 0; a < context->usesCount(); ++a) { Declaration* decl = context->topContext()->usedDeclarationForIndex(context->uses()[a].m_declarationIndex); QColor color(QColor::Invalid); if( colorsForDeclarations.contains(decl) ) color = ColorCache::self()->generatedColor(colorsForDeclarations[decl]); highlightUse(context, a, color); } if(context->type() == DUContext::Other || context->type() == DUContext::Function) { m_functionColorsForDeclarations[IndexedDUContext(context)] = colorsForDeclarations; m_functionDeclarationsForColors[IndexedDUContext(context)] = declarationsForColors; } QVector< DUContext* > children = context->childContexts(); lock.unlock(); // Periodically release the lock, so that the UI won't be blocked too much foreach (DUContext* child, children) highlightDUChain(child, colorsForDeclarations, declarationsForColors ); } KTextEditor::Attribute::Ptr CodeHighlighting::attributeForDepth(int depth) const { while (depth >= m_depthAttributes.count()) { KTextEditor::Attribute::Ptr a(new KTextEditor::Attribute()); a->setBackground(QColor(Qt::white).dark(100 + (m_depthAttributes.count() * 25))); a->setBackgroundFillWhitespace(true); if (depth % 2) a->setOutline(Qt::red); m_depthAttributes.append(a); } return m_depthAttributes[depth]; } KDevelop::Declaration* CodeHighlightingInstance::localClassFromCodeContext(KDevelop::DUContext* context) const { if(!context) return 0; if(m_contextClasses.contains(context)) return m_contextClasses[context]; DUContext* startContext = context; while( context->parentContext() && context->type() == DUContext::Other && context->parentContext()->type() == DUContext::Other ) { //Move context to the top context of type "Other". This is needed because every compound-statement creates a new sub-context. context = context->parentContext(); } ///Step 1: Find the function-declaration for the function we are in Declaration* functionDeclaration = 0; if( FunctionDefinition* def = dynamic_cast(context->owner()) ) { if(m_contextClasses.contains(context)) return m_contextClasses[context]; functionDeclaration = def->declaration(startContext->topContext()); } if( !functionDeclaration && context->owner() ) functionDeclaration = context->owner(); if(!functionDeclaration) { if(m_useClassCache) m_contextClasses[context] = 0; return 0; } Declaration* decl = functionDeclaration->context()->owner(); if(m_useClassCache) m_contextClasses[context] = decl; return decl; } CodeHighlightingInstance::Types CodeHighlightingInstance::typeForDeclaration(Declaration * dec, DUContext* context) const { /** * We highlight in 3 steps by priority: * 1. Is the item in the local class or an inherited class? If yes, highlight. * 2. What kind of item is it? If it's a type/function/enumerator, highlight by type. * 3. Else, highlight by scope. * * */ // if(ClassMemberDeclaration* classMember = dynamic_cast(dec)) // if(!Cpp::isAccessible(context, classMember)) // return ErrorVariableType; if(!dec) return ErrorVariableType; Types type = LocalVariableType; if(dec->kind() == Declaration::Namespace) return NamespaceType; if(dec->kind() == Declaration::Macro){ return MacroType; } if (context && dec->context() && dec->context()->type() == DUContext::Class) { //It is a use. //Determine the class we're in Declaration* klass = localClassFromCodeContext(context); if(klass) { if (klass->internalContext() == dec->context()) type = LocalClassMemberType; //Using Member of the local class else if (dec->context()->type() == DUContext::Class && klass->internalContext() && klass->internalContext()->imports(dec->context())) type = InheritedClassMemberType; //Using Member of an inherited class } } if (type == LocalVariableType) { if (dec->kind() == Declaration::Type || dec->type() || dec->type()) { if (dec->isForwardDeclaration()) type = ForwardDeclarationType; else if (dec->type()) type = FunctionType; else if(dec->type()) type = ClassType; else if(dec->type()) type = TypeAliasType; else if(dec->type()) type = EnumType; else if(dec->type()) type = EnumeratorType; } } if (type == LocalVariableType) { switch (dec->context()->type()) { case DUContext::Namespace: type = NamespaceVariableType; break; case DUContext::Class: type = MemberVariableType; break; case DUContext::Function: type = FunctionVariableType; break; default: break; } } return type; } bool CodeHighlightingInstance::useRainbowColor(Declaration* dec) const { return dec->context()->type() == DUContext::Function || (dec->context()->type() == DUContext::Other && dec->context()->owner()); } void CodeHighlightingInstance::highlightDeclaration(Declaration * declaration, const QColor &color) { HighlightedRange h; h.range = declaration->range(); h.attribute = m_highlighting->attributeForType(typeForDeclaration(declaration, 0), DeclarationContext, color); m_highlight.push_back(h); } void CodeHighlightingInstance::highlightUse(DUContext* context, int index, const QColor &color) { Types type = ErrorVariableType; Declaration* decl = context->topContext()->usedDeclarationForIndex(context->uses()[index].m_declarationIndex); type = typeForDeclaration(decl, context); if(type != ErrorVariableType || ICore::self()->languageController()->completionSettings()->highlightSemanticProblems()) { HighlightedRange h; h.range = context->uses()[index].m_range; h.attribute = m_highlighting->attributeForType(type, ReferenceContext, color); m_highlight.push_back(h); } } void CodeHighlightingInstance::highlightUses(DUContext* context) { for(int a = 0; a < context->usesCount(); ++a) highlightUse(context, a, QColor(QColor::Invalid)); } void CodeHighlighting::clearHighlightingForDocument(IndexedString document) { VERIFY_FOREGROUND_LOCKED QMutexLocker lock(&m_dataMutex); DocumentChangeTracker* tracker = ICore::self()->languageController()->backgroundParser()->trackerForUrl(document); if(m_highlights.contains(tracker)) { disconnect(tracker, &DocumentChangeTracker::destroyed, this, &CodeHighlighting::trackerDestroyed); qDeleteAll(m_highlights[tracker]->m_highlightedRanges); delete m_highlights[tracker]; m_highlights.remove(tracker); } } void CodeHighlighting::applyHighlighting(void* _highlighting) { CodeHighlighting::DocumentHighlighting* highlighting = static_cast(_highlighting); VERIFY_FOREGROUND_LOCKED QMutexLocker lock(&m_dataMutex); DocumentChangeTracker* tracker = ICore::self()->languageController()->backgroundParser()->trackerForUrl(highlighting->m_document); if(!tracker) { qCDebug(LANGUAGE) << "no document found for the planned highlighting of" << highlighting->m_document.str(); delete highlighting; return; } QVector< MovingRange* > oldHighlightedRanges; if(m_highlights.contains(tracker)) { oldHighlightedRanges = m_highlights[tracker]->m_highlightedRanges; delete m_highlights[tracker]; }else{ // we newly add this tracker, so add the connection // This can't use new style connect syntax since MovingInterface is not a QObject connect(tracker->document(), SIGNAL(aboutToInvalidateMovingInterfaceContent(KTextEditor::Document*)), this, SLOT(aboutToInvalidateMovingInterfaceContent(KTextEditor::Document*))); connect(tracker->document(), SIGNAL(aboutToRemoveText(KTextEditor::Range)), this, SLOT(aboutToRemoveText(KTextEditor::Range))); connect(tracker, &DocumentChangeTracker::destroyed, this, &CodeHighlighting::trackerDestroyed); } m_highlights[tracker] = highlighting; // Now create MovingRanges (match old ones with the incoming ranges) KTextEditor::Range tempRange; QVector::iterator movingIt = oldHighlightedRanges.begin(); QVector::iterator rangeIt = highlighting->m_waiting.begin(); while(rangeIt != highlighting->m_waiting.end()) { // Translate the range into the current revision KTextEditor::Range transformedRange = tracker->transformToCurrentRevision(rangeIt->range, highlighting->m_waitingRevision); while(movingIt != oldHighlightedRanges.end() && ((*movingIt)->start().line() < transformedRange.start().line() || ((*movingIt)->start().line() == transformedRange.start().line() && (*movingIt)->start().column() < transformedRange.start().column()))) { delete *movingIt; // Skip ranges that are in front of the current matched range ++movingIt; } tempRange = transformedRange; if(movingIt == oldHighlightedRanges.end() || transformedRange.start().line() != (*movingIt)->start().line() || transformedRange.start().column() != (*movingIt)->start().column() || transformedRange.end().line() != (*movingIt)->end().line() || transformedRange.end().column() != (*movingIt)->end().column()) { Q_ASSERT(rangeIt->attribute); // The moving range is behind or unequal, create a new range highlighting->m_highlightedRanges.push_back(tracker->documentMovingInterface()->newMovingRange(tempRange)); highlighting->m_highlightedRanges.back()->setAttribute(rangeIt->attribute); highlighting->m_highlightedRanges.back()->setZDepth(highlightingZDepth); } else { // Update the existing moving range (*movingIt)->setAttribute(rangeIt->attribute); (*movingIt)->setRange(tempRange); highlighting->m_highlightedRanges.push_back(*movingIt); ++movingIt; } ++rangeIt; } for(; movingIt != oldHighlightedRanges.end(); ++movingIt) delete *movingIt; // Delete unmatched moving ranges behind } void CodeHighlighting::trackerDestroyed(QObject* object) { // Called when a document is destroyed VERIFY_FOREGROUND_LOCKED QMutexLocker lock(&m_dataMutex); DocumentChangeTracker* tracker = static_cast(object); Q_ASSERT(m_highlights.contains(tracker)); delete m_highlights[tracker]; // No need to care about the individual ranges, as the document is being destroyed m_highlights.remove(tracker); } void CodeHighlighting::aboutToInvalidateMovingInterfaceContent(Document* doc) { clearHighlightingForDocument(IndexedString(doc->url())); } void CodeHighlighting::aboutToRemoveText( const KTextEditor::Range& range ) { if (range.onSingleLine()) // don't try to optimize this return; VERIFY_FOREGROUND_LOCKED QMutexLocker lock(&m_dataMutex); Q_ASSERT(dynamic_cast(sender())); KTextEditor::Document* doc = static_cast(sender()); DocumentChangeTracker* tracker = ICore::self()->languageController()->backgroundParser() ->trackerForUrl(IndexedString(doc->url())); if(m_highlights.contains(tracker)) { QVector& ranges = m_highlights.value(tracker)->m_highlightedRanges; QVector::iterator it = ranges.begin(); while(it != ranges.end()) { if (range.contains((*it)->toRange())) { delete (*it); it = ranges.erase(it); } else { ++it; } } } } } // kate: space-indent on; indent-width 2; replace-trailing-space-save on; show-tabs on; tab-indents on; tab-width 2; diff --git a/language/highlighting/colorcache.cpp b/language/highlighting/colorcache.cpp index 8539d3270e..53e4b73c5a 100644 --- a/language/highlighting/colorcache.cpp +++ b/language/highlighting/colorcache.cpp @@ -1,347 +1,334 @@ /* * This file is part of KDevelop * * Copyright 2009 Milian Wolff * * This program 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 program 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 General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "colorcache.h" #include "configurablecolors.h" #include #include "../../interfaces/icore.h" #include "../../interfaces/ilanguagecontroller.h" #include "../../interfaces/icompletionsettings.h" #include "../../interfaces/idocument.h" #include "../../interfaces/idocumentcontroller.h" #include "../interfaces/ilanguagesupport.h" #include "../duchain/duchain.h" #include "../duchain/duchainlock.h" #include "util/debug.h" #include #include #include #include #define ifDebug(x) namespace KDevelop { ColorCache* ColorCache::m_self = 0; ColorCache::ColorCache(QObject* parent) : QObject(parent), m_defaultColors(0), m_validColorCount(0), m_colorOffset(0), m_localColorRatio(0), m_globalColorRatio(0), m_boldDeclarations(true) { Q_ASSERT(m_self == 0); updateColorsFromScheme(); // default / fallback updateColorsFromSettings(); connect(ICore::self()->languageController()->completionSettings(), &ICompletionSettings::settingsChanged, this, &ColorCache::updateColorsFromSettings, Qt::QueuedConnection); connect(ICore::self()->documentController(), &IDocumentController::documentActivated, this, &ColorCache::slotDocumentActivated); bool hadDoc = tryActiveDocument(); updateInternal(); m_self = this; if (!hadDoc) { // try to update later on again QMetaObject::invokeMethod(this, "tryActiveDocument", Qt::QueuedConnection); } } bool ColorCache::tryActiveDocument() { KTextEditor::View* view = ICore::self()->documentController()->activeTextDocumentView(); if ( view ) { updateColorsFromView(view); return true; } return false; } ColorCache::~ColorCache() { m_self = 0; delete m_defaultColors; m_defaultColors = 0; } ColorCache* ColorCache::self() { if (!m_self) { m_self = new ColorCache; } return m_self; } void ColorCache::generateColors() { if ( m_defaultColors ) { delete m_defaultColors; } m_defaultColors = new CodeHighlightingColors(this); - const static QList colors = { - {"#00FF00"}, - {"#0000FF"}, - {"#FF0000"}, - {"#01FFFE"}, - {"#A80041"}, - {"#007DB5"}, - {"#0076FF"}, - {"#CCF400"}, - {"#FF029D"}, - {"#FE8900"}, - {"#C673D3"}, - {"#7E2DD2"}, - {"#65CA00"}, - {"#FF0056"}, - {"#D92F00"}, - {"#00B917"}, - {"#9E008E"}, - {"#002884"}, - {"#FF74A3"}, - {"#01D0FF"}, - {"#E56FFE"}, - {"#0E4CA1"}, - {"#968AE8"}, - {"#AC0071"}, - {"#00FFC6"}, - {"#E4CE00"}, - {"#00A5B4"}, - {"#7544B1"}, - {"#B500FF"}, - {"#00FF78"}, - {"#FF6E41"}, - {"#008F56"}, - {"#48E300"}, - {"#009BFF"}, - {"#E85EBE"} + // Primary colors taken from: http://colorbrewer2.org/?type=qualitative&scheme=Paired&n=12 + const QColor colors[] = { + {"#b15928"}, {"#ff7f00"}, {"#b2df8a"}, {"#33a02c"}, {"#a6cee3"}, + {"#1f78b4"}, {"#6a3d9a"}, {"#cab2d6"}, {"#e31a1c"}, {"#fb9a99"} + }; + + // Supplementary colors generated by: http://tools.medialab.sciences-po.fr/iwanthue/ + const QColor supplementaryColors[] = { + {"#D33B67"}, {"#5EC764"}, {"#6CC82D"}, {"#995729"}, {"#FB4D84"}, + {"#4B8828"}, {"#D847D0"}, {"#B56AC5"}, {"#E96F0C"}, {"#DC7161"}, + {"#4D7279"}, {"#01AAF1"}, {"#D2A237"}, {"#F08CA5"}, {"#C83E93"}, + {"#5D7DF7"}, {"#EFBB51"}, {"#108BBB"}, {"#5C84B8"}, {"#02F8BC"}, + {"#A5A9F7"}, {"#F28E64"}, {"#A461E6"}, {"#6372D3"} }; m_colors.clear(); for(const auto& color: colors){ m_colors.append(blendLocalColor(color)); } + m_primaryColorCount = m_colors.count(); + + for(const auto& color: supplementaryColors){ + m_colors.append(blendLocalColor(color)); + } + m_validColorCount = m_colors.count(); - m_colors.append(m_foregroundColor); } void ColorCache::slotDocumentActivated() { KTextEditor::View* view = ICore::self()->documentController()->activeTextDocumentView(); ifDebug(qCDebug(LANGUAGE) << "doc activated:" << doc;) if ( view ) { updateColorsFromView(view); } } void ColorCache::slotViewSettingsChanged() { KTextEditor::View* view = qobject_cast(sender()); Q_ASSERT(view); ifDebug(qCDebug(LANGUAGE) << "settings changed" << view;) updateColorsFromView(view); } void ColorCache::updateColorsFromView(KTextEditor::View* view) { if ( !view ) { // yeah, the HighlightInterface methods returning an Attribute // require a View... kill me for that mess return; } QColor foreground(QColor::Invalid); QColor background(QColor::Invalid); KTextEditor::Attribute::Ptr style = view->defaultStyleAttribute(KTextEditor::dsNormal); foreground = style->foreground().color(); if (style->hasProperty(QTextFormat::BackgroundBrush)) { background = style->background().color(); } // FIXME: this is in kateview // qCDebug(LANGUAGE) << "got foreground:" << foreground.name() << "old is:" << m_foregroundColor.name(); //NOTE: this slot is defined in KatePart > 4.4, see ApiDocs of the ConfigInterface // the signal is not defined in ConfigInterface, but according to the docs it should be // can't use new signal slot syntax here, since ConfigInterface is not a QObject if ( KTextEditor::View* view = m_view.data() ) { Q_ASSERT(qobject_cast(view)); // we only listen to a single view, i.e. the active one disconnect(view, SIGNAL(configChanged()), this, SLOT(slotViewSettingsChanged())); } Q_ASSERT(qobject_cast(view)); connect(view, SIGNAL(configChanged()), this, SLOT(slotViewSettingsChanged())); m_view = view; if ( !foreground.isValid() ) { // fallback to colorscheme variant ifDebug(qCDebug(LANGUAGE) << "updating from scheme";) updateColorsFromScheme(); } else if ( m_foregroundColor != foreground || m_backgroundColor != background ) { m_foregroundColor = foreground; m_backgroundColor = background; ifDebug(qCDebug(LANGUAGE) << "updating from document";) update(); } } void ColorCache::updateColorsFromScheme() { KColorScheme scheme(QPalette::Normal, KColorScheme::View); QColor foreground = scheme.foreground(KColorScheme::NormalText).color(); QColor background = scheme.background(KColorScheme::NormalBackground).color(); if ( foreground != m_foregroundColor || background != m_backgroundColor ) { m_foregroundColor = foreground; m_backgroundColor = background; update(); } } void ColorCache::updateColorsFromSettings() { int localRatio = ICore::self()->languageController()->completionSettings()->localColorizationLevel(); int globalRatio = ICore::self()->languageController()->completionSettings()->globalColorizationLevel(); bool boldDeclartions = ICore::self()->languageController()->completionSettings()->boldDeclarations(); if ( localRatio != m_localColorRatio || globalRatio != m_globalColorRatio ) { m_localColorRatio = localRatio; m_globalColorRatio = globalRatio; update(); } if (boldDeclartions != m_boldDeclarations) { m_boldDeclarations = boldDeclartions; update(); } } void ColorCache::update() { if ( !m_self ) { ifDebug(qCDebug(LANGUAGE) << "not updating - still initializating";) // don't update on startup, updateInternal is called directly there return; } QMetaObject::invokeMethod(this, "updateInternal", Qt::QueuedConnection); } void ColorCache::updateInternal() { ifDebug(qCDebug(LANGUAGE) << "update internal" << m_self;) generateColors(); if ( !m_self ) { // don't do anything else fancy on startup return; } emit colorsGotChanged(); // rehighlight open documents foreach (IDocument* doc, ICore::self()->documentController()->openDocuments()) { foreach (auto lang, ICore::self()->languageController()->languagesForUrl(doc->url())) { ReferencedTopDUContext top; { DUChainReadLocker lock; top = lang->standardContext(doc->url()); } if(top) { if ( ICodeHighlighting* highlighting = lang->codeHighlighting() ) { highlighting->highlightDUChain(top); } } } } } QColor ColorCache::blend(QColor color, uchar ratio) const { Q_ASSERT(m_backgroundColor.isValid()); Q_ASSERT(m_foregroundColor.isValid()); if ( KColorUtils::luma(m_foregroundColor) > KColorUtils::luma(m_backgroundColor) ) { // for dark color schemes, produce a fitting color first color = KColorUtils::tint(m_foregroundColor, color, 0.5); } // adapt contrast return KColorUtils::mix( m_foregroundColor, color, float(ratio) / float(0xff) ); } QColor ColorCache::blendBackground(QColor color, uchar ratio) const { /* if ( KColorUtils::luma(m_foregroundColor) > KColorUtils::luma(m_backgroundColor) ) { // for dark color schemes, produce a fitting color first color = KColorUtils::tint(m_foregroundColor, color, 0.5).rgb(); }*/ // adapt contrast return KColorUtils::mix( m_backgroundColor, color, float(ratio) / float(0xff) ); } QColor ColorCache::blendGlobalColor(QColor color) const { return blend(color, m_globalColorRatio); } QColor ColorCache::blendLocalColor(QColor color) const { return blend(color, m_localColorRatio); } CodeHighlightingColors* ColorCache::defaultColors() const { Q_ASSERT(m_defaultColors); return m_defaultColors; } QColor ColorCache::generatedColor(uint num) const { - return m_colors[num]; + return num > (uint)m_colors.size() ? foregroundColor() : m_colors[num]; } uint ColorCache::validColorCount() const { return m_validColorCount; } +uint ColorCache::primaryColorCount() const +{ + return m_primaryColorCount; +} + QColor ColorCache::foregroundColor() const { return m_foregroundColor; } } // kate: space-indent on; indent-width 2; replace-trailing-space-save on; show-tabs on; tab-indents on; tab-width 2; diff --git a/language/highlighting/colorcache.h b/language/highlighting/colorcache.h index d04dfc5c90..4183626431 100644 --- a/language/highlighting/colorcache.h +++ b/language/highlighting/colorcache.h @@ -1,167 +1,182 @@ /* * This file is part of KDevelop * * Copyright 2009 Milian Wolff * * This program 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 program 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 General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDEVPLATFORM_COLORCACHE_H #define KDEVPLATFORM_COLORCACHE_H #include #include #include #include #include namespace KTextEditor { class Document; class View; } namespace KDevelop { class CodeHighlightingColors; class IDocument; /** * A singleton which holds the global default colors, adapted to the current color scheme */ class KDEVPLATFORMLANGUAGE_EXPORT ColorCache : public QObject { Q_OBJECT public: ~ColorCache(); /// access the global color cache static ColorCache* self(); /// adapt a given foreground color to the current color scheme /// @p ratio between 0 and 255 where 0 gives @see m_foregroundColor /// and 255 gives @p color /// /// @note if you are looking for a background color, simply setting an alpha /// value should work. QColor blend(QColor color, uchar ratio) const; /// adapt a given background color to the current color scheme /// @p ratio between 0 and 255 where 0 gives @see m_foregroundColor /// and 255 gives @p color /// /// @note if you are looking for a background color, simply setting an alpha /// value should work. QColor blendBackground(QColor color, uchar ratio) const; /// blend a color for local colorization according to the user settings /// @see blend() QColor blendLocalColor(QColor color) const; /// blend a color for global colorization according to the user settings /// @see blend() QColor blendGlobalColor(QColor color) const; /// access the default colors CodeHighlightingColors* defaultColors() const; - /// access the generated colors - /// @see validColorCount() + /** + * @returns a primary color if @p num less primaryColorCount and a supplementary color if @p num >= primaryColorCount and < validColorCount + * @see validColorCount() + * @see primaryColorCount() + */ QColor generatedColor(uint num) const; - /// returns the number of valid generated colors - /// @see generatedColor() + /** + * @returns the number of primary and supplementary colors + * + * @see generatedColor() + * @see primaryColorCount() + */ uint validColorCount() const; + /** + * @returns number of primary colors + * + * When you run out of primary colors use supplementary colors + */ + uint primaryColorCount() const; + /// access the foreground color QColor foregroundColor() const; signals: /// will be emitted whenever the colors got changed /// @see update() void colorsGotChanged(); private slots: /// if necessary, adapt to the colors of this document void slotDocumentActivated(); /// settings got changed, update to the settings of the sender void slotViewSettingsChanged(); /// will regenerate colors from global KDE color scheme void updateColorsFromScheme(); /// will regenerate colors with the proper intensity settings void updateColorsFromSettings(); /// regenerate colors and emits @p colorsGotChanged() /// and finally triggers a rehighlight of the opened documents void updateInternal(); bool tryActiveDocument(); private: ColorCache(QObject *parent = 0); static ColorCache* m_self; /// get @p totalGeneratedColors colors from the color wheel and adapt them to the current color scheme void generateColors(); /// calls @c updateInternal() delayed to prevent double loading of language plugins. void update(); /// try to access the KatePart settings for the given doc or fallback to the global KDE scheme /// and update the colors if necessary /// @see generateColors(), updateColorsFromScheme() void updateColorsFromView(KTextEditor::View* view); /// the default colors for the different types CodeHighlightingColors* m_defaultColors; /// the generated colors QList m_colors; - /// Must always be m_colors.count()-1, because the last color must be the fallback text color uint m_validColorCount; + uint m_primaryColorCount; + /// Maybe make this configurable: An offset where to start stepping through the color wheel uint m_colorOffset; /// the text color for the current color scheme QColor m_foregroundColor; /// the editor background color color for the current color scheme QColor m_backgroundColor; /// How generated colors for local variables should be mixed with the foreground color. /// Between 0 and 255, where 255 means only foreground color, and 0 only the chosen color. uchar m_localColorRatio; /// How global colors (i.e. for types, uses, etc.) should be mixed with the foreground color. /// Between 0 and 255, where 255 means only foreground color, and 0 only the chosen color. uchar m_globalColorRatio; /// 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 m_view; }; } #endif // KDEVPLATFORM_COLORCACHE_H // kate: space-indent on; indent-width 2; replace-trailing-space-save on; show-tabs on; tab-indents on; tab-width 2;