diff --git a/kdevplatform/language/duchain/navigation/abstractdeclarationnavigationcontext.cpp b/kdevplatform/language/duchain/navigation/abstractdeclarationnavigationcontext.cpp index bbb3b8f1b2..4940736c68 100644 --- a/kdevplatform/language/duchain/navigation/abstractdeclarationnavigationcontext.cpp +++ b/kdevplatform/language/duchain/navigation/abstractdeclarationnavigationcontext.cpp @@ -1,891 +1,891 @@ /* Copyright 2007 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. 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 "abstractdeclarationnavigationcontext.h" #include #include #include "../functiondeclaration.h" #include "../functiondefinition.h" #include "../classfunctiondeclaration.h" #include "../namespacealiasdeclaration.h" #include "../forwarddeclaration.h" #include "../types/enumeratortype.h" #include "../types/enumerationtype.h" #include "../types/functiontype.h" #include "../duchainutils.h" #include "../types/pointertype.h" #include "../types/referencetype.h" #include "../types/typeutils.h" #include "../types/typesystem.h" #include "../persistentsymboltable.h" #include #include #include #include #include #include namespace KDevelop { class AbstractDeclarationNavigationContextPrivate { public: DeclarationPointer m_declaration; bool m_fullBackwardSearch = false; }; AbstractDeclarationNavigationContext::AbstractDeclarationNavigationContext(const DeclarationPointer& decl, const TopDUContextPointer& topContext, AbstractNavigationContext* previousContext) : AbstractNavigationContext((topContext ? topContext : TopDUContextPointer( decl ? decl->topContext() : nullptr)), previousContext) , d(new AbstractDeclarationNavigationContextPrivate) { d->m_declaration = decl; //Jump from definition to declaration if possible auto* definition = dynamic_cast(d->m_declaration.data()); if (definition && definition->declaration()) d->m_declaration = DeclarationPointer(definition->declaration()); } AbstractDeclarationNavigationContext::~AbstractDeclarationNavigationContext() { } QString AbstractDeclarationNavigationContext::name() const { if (d->m_declaration.data()) return prettyQualifiedIdentifier(d->m_declaration).toString(); else return declarationName(d->m_declaration); } QString AbstractDeclarationNavigationContext::html(bool shorten) { DUChainReadLocker lock(DUChain::lock(), 300); if (!lock.locked()) { return {}; } clear(); AbstractNavigationContext::html(shorten); - modifyHtml() += QLatin1String("

") + fontSizePrefix(shorten); + modifyHtml() += QLatin1String("

"); addExternalHtml(prefix()); if (!d->m_declaration.data()) { modifyHtml() += i18n("
lost declaration
"); return currentHtml(); } if (auto context = previousContext()) { const QString link = createLink(context->name(), context->name(), NavigationAction(context)); modifyHtml() += navigationHighlight(i18n("Back to %1
", link)); } QExplicitlySharedDataPointer doc; if (!shorten) { doc = ICore::self()->documentationController()->documentationForDeclaration(d->m_declaration.data()); const auto* function = dynamic_cast(d->m_declaration.data()); if (function) { htmlFunction(); } else if (d->m_declaration->isTypeAlias() || d->m_declaration->type() || d->m_declaration->kind() == Declaration::Instance) { if (d->m_declaration->isTypeAlias()) modifyHtml() += importantHighlight(QStringLiteral("typedef ")); if (d->m_declaration->type()) modifyHtml() += i18n("enumerator "); AbstractType::Ptr useType = d->m_declaration->abstractType(); if (d->m_declaration->isTypeAlias()) { //Do not show the own name as type of typedefs if (useType.cast()) useType = useType.cast()->type(); } eventuallyMakeTypeLinks(useType); modifyHtml() += QLatin1Char(' ') + identifierHighlight(declarationName( d->m_declaration).toHtmlEscaped(), d->m_declaration); if (auto integralType = d->m_declaration->type()) { const QString plainValue = integralType->valueAsString(); if (!plainValue.isEmpty()) { modifyHtml() += QStringLiteral(" = ") + plainValue; } } modifyHtml() += QStringLiteral("
"); } else { if (d->m_declaration->kind() == Declaration::Type && d->m_declaration->abstractType().cast()) { htmlClass(); } if (d->m_declaration->kind() == Declaration::Namespace) { modifyHtml() += i18n("namespace %1 ", identifierHighlight(d->m_declaration->qualifiedIdentifier().toString().toHtmlEscaped(), d->m_declaration)); } else if (d->m_declaration->kind() == Declaration::NamespaceAlias) { modifyHtml() += identifierHighlight(declarationName(d->m_declaration).toHtmlEscaped(), d->m_declaration); } if (d->m_declaration->type()) { EnumerationType::Ptr enumeration = d->m_declaration->type(); modifyHtml() += i18n("enumeration %1 ", identifierHighlight( d->m_declaration->identifier().toString().toHtmlEscaped(), d->m_declaration)); } if (d->m_declaration->isForwardDeclaration()) { auto* forwardDec = static_cast(d->m_declaration.data()); Declaration* resolved = forwardDec->resolve(topContext().data()); if (resolved) { modifyHtml() += i18n("(resolved forward-declaration: "); makeLink(resolved->identifier().toString(), DeclarationPointer( resolved), NavigationAction::NavigateDeclaration); modifyHtml() += i18n(") "); } else { modifyHtml() += i18n("(unresolved forward-declaration) "); QualifiedIdentifier id = forwardDec->qualifiedIdentifier(); const auto& forwardDecFile = forwardDec->topContext()->parsingEnvironmentFile(); uint count; const IndexedDeclaration* decls; PersistentSymbolTable::self().declarations(id, count, decls); for (uint a = 0; a < count; ++a) { auto dec = decls[a].data(); if (!dec || dec->isForwardDeclaration()) { continue; } const auto& decFile = forwardDec->topContext()->parsingEnvironmentFile(); if ((static_cast(decFile) != static_cast(forwardDecFile)) || (decFile && forwardDecFile && decFile->language() != forwardDecFile->language())) { // the language of the declarations must match continue; } modifyHtml() += QStringLiteral("
"); makeLink(i18n("possible resolution from"), DeclarationPointer( dec), NavigationAction::NavigateDeclaration); modifyHtml() += QLatin1Char(' ') + dec->url().str(); } } } modifyHtml() += QStringLiteral("
"); } } else { AbstractType::Ptr showType = d->m_declaration->abstractType(); if (showType && showType.cast()) { showType = showType.cast()->returnType(); if (showType) modifyHtml() += labelHighlight(i18n("Returns: ")); } else if (showType) { modifyHtml() += labelHighlight(i18n("Type: ")); } if (showType) { eventuallyMakeTypeLinks(showType); modifyHtml() += QStringLiteral(" "); } } QualifiedIdentifier identifier = d->m_declaration->qualifiedIdentifier(); if (identifier.count() > 1) { if (d->m_declaration->context() && d->m_declaration->context()->owner()) { Declaration* decl = d->m_declaration->context()->owner(); auto* definition = dynamic_cast(decl); if (definition && definition->declaration()) decl = definition->declaration(); if (decl->abstractType().cast()) modifyHtml() += labelHighlight(i18n("Enum: ")); else modifyHtml() += labelHighlight(i18n("Container: ")); makeLink(declarationName(DeclarationPointer(decl)), DeclarationPointer( decl), NavigationAction::NavigateDeclaration); modifyHtml() += QStringLiteral(" "); } else { QualifiedIdentifier parent = identifier; parent.pop(); modifyHtml() += labelHighlight(i18n("Scope: %1 ", typeHighlight(parent.toString().toHtmlEscaped()))); } } if (shorten && !d->m_declaration->comment().isEmpty()) { QString comment = QString::fromUtf8(d->m_declaration->comment()); if (comment.length() > 60) { comment.truncate(60); comment += QLatin1String("..."); } comment.replace(QLatin1Char('\n'), QLatin1Char(' ')); comment.replace(QLatin1String("
"), QLatin1String(" ")); comment.replace(QLatin1String("
"), QLatin1String(" ")); modifyHtml() += commentHighlight(comment.toHtmlEscaped()) + QLatin1String(" "); } QString access = stringFromAccess(d->m_declaration); if (!access.isEmpty()) modifyHtml() += labelHighlight(i18n("Access: %1 ", propertyHighlight(access.toHtmlEscaped()))); ///@todo Enumerations QString detailsHtml; const QStringList details = declarationDetails(d->m_declaration); if (!details.isEmpty()) { bool first = true; for (const QString& str : details) { if (!first) detailsHtml += QLatin1String(", "); first = false; detailsHtml += propertyHighlight(str); } } QString kind = declarationKind(d->m_declaration); if (!kind.isEmpty()) { if (!detailsHtml.isEmpty()) modifyHtml() += labelHighlight(i18n("Kind: %1 %2 ", importantHighlight(kind.toHtmlEscaped()), detailsHtml)); else modifyHtml() += labelHighlight(i18n("Kind: %1 ", importantHighlight(kind.toHtmlEscaped()))); } if (d->m_declaration->isDeprecated()) { modifyHtml() += labelHighlight(i18n("Status: %1 ", propertyHighlight(i18n("Deprecated")))); } modifyHtml() += QStringLiteral("
"); if (!shorten) htmlAdditionalNavigation(); if (!shorten) { if (dynamic_cast(d->m_declaration.data())) modifyHtml() += labelHighlight(i18n("Def.: ")); else modifyHtml() += labelHighlight(i18n("Decl.: ")); makeLink(QStringLiteral("%1 :%2").arg(d->m_declaration->url().toUrl().fileName()).arg(d->m_declaration-> rangeInCurrentRevision(). start().line() + 1), d->m_declaration, NavigationAction::JumpToSource); modifyHtml() += QStringLiteral(" "); //modifyHtml() += "
"; if (!dynamic_cast(d->m_declaration.data())) { if (FunctionDefinition* definition = FunctionDefinition::definition(d->m_declaration.data())) { modifyHtml() += labelHighlight(i18n(" Def.: ")); makeLink(QStringLiteral("%1 :%2").arg(definition->url().toUrl().fileName()).arg(definition-> rangeInCurrentRevision() .start().line() + 1), DeclarationPointer( definition), NavigationAction::JumpToSource); } } if (auto* definition = dynamic_cast(d->m_declaration.data())) { if (definition->declaration()) { modifyHtml() += labelHighlight(i18n(" Decl.: ")); makeLink(QStringLiteral("%1 :%2").arg(definition->declaration()->url().toUrl().fileName()).arg( definition->declaration()->rangeInCurrentRevision().start().line() + 1), DeclarationPointer(definition->declaration()), NavigationAction::JumpToSource); } } modifyHtml() += QStringLiteral(" "); //The action name _must_ stay "show_uses", since that is also used from outside makeLink(i18n("Show uses"), QStringLiteral("show_uses"), NavigationAction(d->m_declaration, NavigationAction::NavigateUses)); } QByteArray declarationComment = d->m_declaration->comment(); if (!shorten && (!declarationComment.isEmpty() || doc)) { modifyHtml() += QStringLiteral("

"); if (doc) { QString comment = doc->description(); connect( doc.data(), &IDocumentation::descriptionChanged, this, &AbstractDeclarationNavigationContext::contentsChanged); if (!comment.isEmpty()) { modifyHtml() += QLatin1String("

") + commentHighlight(comment) + QLatin1String("

"); } } QString comment = QString::fromUtf8(declarationComment); if (!comment.isEmpty()) { // if the first paragraph does not contain a tag, we assume that this is a plain-text comment if (!Qt::mightBeRichText(comment)) { // still might contain extra html tags for line breaks (this is the case for doxygen-style comments sometimes) // let's protect them from being removed completely comment.replace(QRegExp(QStringLiteral("
")), QStringLiteral("\n")); comment = comment.toHtmlEscaped(); comment.replace(QLatin1Char('\n'), QLatin1String("
")); //Replicate newlines in html } modifyHtml() += commentHighlight(comment); modifyHtml() += QStringLiteral("

"); } } if (!shorten) { modifyHtml() += declarationSizeInformation(d->m_declaration, topContext().data()); } if (!shorten && doc) { modifyHtml() += QLatin1String("

") + i18n("Show documentation for "); makeLink(prettyQualifiedName(d->m_declaration), d->m_declaration, NavigationAction::ShowDocumentation); modifyHtml() += QStringLiteral("

"); } //modifyHtml() += "
"; addExternalHtml(suffix()); - modifyHtml() += fontSizeSuffix(shorten) + QLatin1String("

"); + modifyHtml() += QLatin1String("

"); return currentHtml(); } AbstractType::Ptr AbstractDeclarationNavigationContext::typeToShow(AbstractType::Ptr type) { return type; } void AbstractDeclarationNavigationContext::htmlFunction() { const auto* function = dynamic_cast(d->m_declaration.data()); Q_ASSERT(function); const auto* classFunDecl = dynamic_cast(d->m_declaration.data()); const FunctionType::Ptr type = d->m_declaration->abstractType().cast(); if (!type) { modifyHtml() += errorHighlight(QStringLiteral("Invalid type
")); return; } if (!classFunDecl || (!classFunDecl->isConstructor() && !classFunDecl->isDestructor())) { // only print return type for global functions and non-ctor/dtor methods eventuallyMakeTypeLinks(type->returnType()); } modifyHtml() += QLatin1Char(' ') + identifierHighlight(prettyIdentifier( d->m_declaration).toString().toHtmlEscaped(), d->m_declaration); if (type->indexedArgumentsSize() == 0) { modifyHtml() += QStringLiteral("()"); } else { modifyHtml() += QStringLiteral("( "); bool first = true; int firstDefaultParam = type->indexedArgumentsSize() - function->defaultParametersSize(); int currentArgNum = 0; QVector decls; if (DUContext* argumentContext = DUChainUtils::argumentContext(d->m_declaration.data())) { decls = argumentContext->localDeclarations(topContext().data()); } foreach (const AbstractType::Ptr& argType, type->arguments()) { if (!first) modifyHtml() += QStringLiteral(", "); first = false; eventuallyMakeTypeLinks(argType); // Must count from the back to skip possible template arguments before the function arguments. int currentArgIndex = decls.size() - type->arguments().size() + currentArgNum; if (currentArgIndex >= 0 && currentArgIndex < decls.size()) { modifyHtml() += QLatin1Char(' ') + identifierHighlight( decls[currentArgIndex]->identifier().toString().toHtmlEscaped(), d->m_declaration); } if (currentArgNum >= firstDefaultParam) { IndexedString defaultStr = function->defaultParameters()[currentArgNum - firstDefaultParam]; if (!defaultStr.isEmpty()) { modifyHtml() += QLatin1String(" = ") + defaultStr.str().toHtmlEscaped(); } } ++currentArgNum; } modifyHtml() += QStringLiteral(" )"); } modifyHtml() += QStringLiteral("
"); } Identifier AbstractDeclarationNavigationContext::prettyIdentifier(const DeclarationPointer& decl) const { Identifier ret; QualifiedIdentifier q = prettyQualifiedIdentifier(decl); if (!q.isEmpty()) ret = q.last(); return ret; } QualifiedIdentifier AbstractDeclarationNavigationContext::prettyQualifiedIdentifier(const DeclarationPointer& decl) const { if (decl) return decl->qualifiedIdentifier(); else return QualifiedIdentifier(); } QString AbstractDeclarationNavigationContext::prettyQualifiedName(const DeclarationPointer& decl) const { const auto qid = prettyQualifiedIdentifier(decl); if (qid.isEmpty()) { return i18nc("An anonymous declaration (class, function, etc.)", ""); } return qid.toString(); } void AbstractDeclarationNavigationContext::htmlAdditionalNavigation() { ///Check if the function overrides or hides another one const auto* classFunDecl = dynamic_cast(d->m_declaration.data()); if (classFunDecl) { Declaration* overridden = DUChainUtils::overridden(d->m_declaration.data()); if (overridden) { modifyHtml() += i18n("Overrides a "); makeLink(i18n("function"), QStringLiteral("jump_to_overridden"), NavigationAction(DeclarationPointer(overridden), NavigationAction::NavigateDeclaration)); modifyHtml() += i18n(" from "); makeLink(prettyQualifiedName(DeclarationPointer(overridden->context()->owner())), QStringLiteral("jump_to_overridden_container"), NavigationAction(DeclarationPointer(overridden->context()->owner()), NavigationAction::NavigateDeclaration)); modifyHtml() += QStringLiteral("
"); } else { //Check if this declarations hides other declarations QList decls; foreach (const DUContext::Import& import, d->m_declaration->context()->importedParentContexts()) if (import.context(topContext().data())) decls += import.context(topContext().data())->findDeclarations(QualifiedIdentifier(d->m_declaration-> identifier()), CursorInRevision::invalid(), AbstractType::Ptr(), topContext().data(), DUContext::DontSearchInParent); uint num = 0; foreach (Declaration* decl, decls) { modifyHtml() += i18n("Hides a "); makeLink(i18n("function"), QStringLiteral("jump_to_hide_%1").arg(num), NavigationAction(DeclarationPointer(decl), NavigationAction::NavigateDeclaration)); modifyHtml() += i18n(" from "); makeLink(prettyQualifiedName(DeclarationPointer(decl->context()->owner())), QStringLiteral("jump_to_hide_container_%1").arg(num), NavigationAction(DeclarationPointer(decl->context()->owner()), NavigationAction::NavigateDeclaration)); modifyHtml() += QStringLiteral("
"); ++num; } } ///Show all places where this function is overridden if (classFunDecl->isVirtual()) { Declaration* classDecl = d->m_declaration->context()->owner(); if (classDecl) { uint maxAllowedSteps = d->m_fullBackwardSearch ? (uint) - 1 : 10; const QList overriders = DUChainUtils::overriders(classDecl, classFunDecl, maxAllowedSteps); if (!overriders.isEmpty()) { modifyHtml() += i18n("Overridden in "); bool first = true; for (Declaration* overrider : overriders) { if (!first) modifyHtml() += QStringLiteral(", "); first = false; const auto owner = DeclarationPointer(overrider->context()->owner()); const QString name = prettyQualifiedName(owner); makeLink(name, name, NavigationAction(DeclarationPointer(overrider), NavigationAction::NavigateDeclaration)); } modifyHtml() += QStringLiteral("
"); } if (maxAllowedSteps == 0) createFullBackwardSearchLink(overriders.isEmpty() ? i18n("Overriders possible, show all") : i18n( "More overriders possible, show all")); } } } ///Show all classes that inherit this one uint maxAllowedSteps = d->m_fullBackwardSearch ? (uint) - 1 : 10; const QList inheriters = DUChainUtils::inheriters(d->m_declaration.data(), maxAllowedSteps); if (!inheriters.isEmpty()) { modifyHtml() += i18n("Inherited by "); bool first = true; for (Declaration* importer : inheriters) { if (!first) modifyHtml() += QStringLiteral(", "); first = false; const QString importerName = prettyQualifiedName(DeclarationPointer(importer)); makeLink(importerName, importerName, NavigationAction(DeclarationPointer(importer), NavigationAction::NavigateDeclaration)); } modifyHtml() += QStringLiteral("
"); } if (maxAllowedSteps == 0) createFullBackwardSearchLink(inheriters.isEmpty() ? i18n("Inheriters possible, show all") : i18n( "More inheriters possible, show all")); } void AbstractDeclarationNavigationContext::createFullBackwardSearchLink(const QString& string) { makeLink(string, QStringLiteral("m_fullBackwardSearch=true"), NavigationAction(QStringLiteral("m_fullBackwardSearch=true"))); modifyHtml() += QStringLiteral("
"); } NavigationContextPointer AbstractDeclarationNavigationContext::executeKeyAction(const QString& key) { if (key == QLatin1String("m_fullBackwardSearch=true")) { d->m_fullBackwardSearch = true; clear(); } return NavigationContextPointer(this); } void AbstractDeclarationNavigationContext::htmlClass() { StructureType::Ptr klass = d->m_declaration->abstractType().cast(); Q_ASSERT(klass); ClassDeclaration* classDecl = dynamic_cast(klass->declaration(topContext().data())); if (classDecl) { switch (classDecl->classType()) { case ClassDeclarationData::Class: modifyHtml() += QStringLiteral("class "); break; case ClassDeclarationData::Struct: modifyHtml() += QStringLiteral("struct "); break; case ClassDeclarationData::Union: modifyHtml() += QStringLiteral("union "); break; case ClassDeclarationData::Interface: modifyHtml() += QStringLiteral("interface "); break; case ClassDeclarationData::Trait: modifyHtml() += QStringLiteral("trait "); break; } eventuallyMakeTypeLinks(klass.cast()); FOREACH_FUNCTION(const BaseClassInstance &base, classDecl->baseClasses) { modifyHtml() += QLatin1String(", ") + stringFromAccess(base.access) + QLatin1Char(' ') + (base.virtualInheritance ? QStringLiteral("virtual") : QString()) + QLatin1Char(' '); eventuallyMakeTypeLinks(base.baseClass.abstractType()); } } else { /// @todo How can we get here? and should this really be a class? modifyHtml() += QStringLiteral("class "); eventuallyMakeTypeLinks(klass.cast()); } modifyHtml() += QStringLiteral(" "); } void AbstractDeclarationNavigationContext::htmlIdentifiedType(AbstractType::Ptr type, const IdentifiedType* idType) { if (!type) { qCDebug(LANGUAGE) << "null type!"; return; } if (!idType) { qCDebug(LANGUAGE) << "no identified type for" << type->toString(); modifyHtml() += typeHighlight(type->toString().toHtmlEscaped()); return; } auto* decl = idType->declaration(topContext().data()); if (!decl) { qCDebug(LANGUAGE) << "could not resolve declaration:" << idType->declarationId().isDirect() << idType->qualifiedIdentifier().toString() << "in top-context" << topContext()->url().str(); modifyHtml() += typeHighlight(type->toString().toHtmlEscaped()); return; } //Remove the last template-identifiers, because we create those directly QualifiedIdentifier id = prettyQualifiedIdentifier(DeclarationPointer(decl)); Identifier lastId = id.last(); id.pop(); lastId.clearTemplateIdentifiers(); id.push(lastId); if (decl->context() && decl->context()->owner()) { //Also create full type-links for the context around AbstractType::Ptr contextType = decl->context()->owner()->abstractType(); auto* contextIdType = dynamic_cast(contextType.data()); if (contextIdType && !contextIdType->equals(idType)) { //Create full type information for the context if (!id.isEmpty()) id = id.mid(id.count() - 1); htmlIdentifiedType(contextType, contextIdType); modifyHtml() += QStringLiteral("::").toHtmlEscaped(); } } //We leave out the * and & reference and pointer signs, those are added to the end makeLink(id.toString(), DeclarationPointer(idType->declaration( topContext().data())), NavigationAction::NavigateDeclaration); } void AbstractDeclarationNavigationContext::eventuallyMakeTypeLinks(AbstractType::Ptr type) { type = typeToShow(type); if (!type) { modifyHtml() += typeHighlight(QStringLiteral("").toHtmlEscaped()); return; } AbstractType::Ptr target = TypeUtils::targetTypeKeepAliases(type, topContext().data()); const auto* idType = dynamic_cast(target.data()); qCDebug(LANGUAGE) << "making type-links for" << type->toString(); if (idType && idType->declaration(topContext().data())) { ///@todo This is C++ specific, move into subclass if (target->modifiers() & AbstractType::ConstModifier) modifyHtml() += typeHighlight(QStringLiteral("const ")); htmlIdentifiedType(target, idType); //We need to exchange the target type, else template-parameters may confuse this SimpleTypeExchanger exchangeTarget(target, AbstractType::Ptr()); AbstractType::Ptr exchanged = exchangeTarget.exchange(type); if (exchanged) { QString typeSuffixString = exchanged->toString(); QRegExp suffixExp(QStringLiteral("\\&|\\*")); int suffixPos = typeSuffixString.indexOf(suffixExp); if (suffixPos != -1) modifyHtml() += typeHighlight(typeSuffixString.mid(suffixPos)); } } else { if (idType) { qCDebug(LANGUAGE) << "identified type could not be resolved:" << idType->qualifiedIdentifier() << idType->declarationId().isValid() << idType->declarationId().isDirect(); } modifyHtml() += typeHighlight(type->toString().toHtmlEscaped()); } } DeclarationPointer AbstractDeclarationNavigationContext::declaration() const { return d->m_declaration; } QString AbstractDeclarationNavigationContext::identifierHighlight(const QString& identifier, const DeclarationPointer& decl) const { QString ret = nameHighlight(identifier); if (!decl) { return ret; } if (decl->isDeprecated()) { ret = QStringLiteral("") + ret + QStringLiteral(""); } return ret; } QString AbstractDeclarationNavigationContext::stringFromAccess(Declaration::AccessPolicy access) { switch (access) { case Declaration::Private: return QStringLiteral("private"); case Declaration::Protected: return QStringLiteral("protected"); case Declaration::Public: return QStringLiteral("public"); default: break; } return QString(); } QString AbstractDeclarationNavigationContext::stringFromAccess(const DeclarationPointer& decl) { const auto* memberDecl = dynamic_cast(decl.data()); if (memberDecl) { return stringFromAccess(memberDecl->accessPolicy()); } return QString(); } QString AbstractDeclarationNavigationContext::declarationName(const DeclarationPointer& decl) const { if (auto* alias = dynamic_cast(decl.data())) { if (alias->identifier().isEmpty()) return QLatin1String("using namespace ") + alias->importIdentifier().toString(); else return QLatin1String("namespace ") + alias->identifier().toString() + QLatin1String(" = ") + alias->importIdentifier().toString(); } if (!decl) return i18nc("A declaration that is unknown", "Unknown"); else return prettyIdentifier(decl).toString(); } QStringList AbstractDeclarationNavigationContext::declarationDetails(const DeclarationPointer& decl) { QStringList details; const auto* function = dynamic_cast(decl.data()); const auto* memberDecl = dynamic_cast(decl.data()); if (memberDecl) { if (memberDecl->isMutable()) details << QStringLiteral("mutable"); if (memberDecl->isRegister()) details << QStringLiteral("register"); if (memberDecl->isStatic()) details << QStringLiteral("static"); if (memberDecl->isAuto()) details << QStringLiteral("auto"); if (memberDecl->isExtern()) details << QStringLiteral("extern"); if (memberDecl->isFriend()) details << QStringLiteral("friend"); } if (decl->isDefinition()) details << i18nc("tells if a declaration is defining the variable's value", "definition"); if (decl->isExplicitlyDeleted()) details << QStringLiteral("deleted"); if (memberDecl && memberDecl->isForwardDeclaration()) details << i18nc("as in c++ forward declaration", "forward"); AbstractType::Ptr t(decl->abstractType()); if (t) { if (t->modifiers() & AbstractType::ConstModifier) details << i18nc("a variable that won't change, const", "constant"); if (t->modifiers() & AbstractType::VolatileModifier) details << QStringLiteral("volatile"); } if (function) { if (function->isInline()) details << QStringLiteral("inline"); if (function->isExplicit()) details << QStringLiteral("explicit"); if (function->isVirtual()) details << QStringLiteral("virtual"); const auto* classFunDecl = dynamic_cast(decl.data()); if (classFunDecl) { if (classFunDecl->isSignal()) details << QStringLiteral("signal"); if (classFunDecl->isSlot()) details << QStringLiteral("slot"); if (classFunDecl->isFinal()) details << QStringLiteral("final"); if (classFunDecl->isConstructor()) details << QStringLiteral("constructor"); if (classFunDecl->isDestructor()) details << QStringLiteral("destructor"); if (classFunDecl->isConversionFunction()) details << QStringLiteral("conversion-function"); if (classFunDecl->isAbstract()) details << QStringLiteral("abstract"); } } return details; } QString AbstractDeclarationNavigationContext::declarationSizeInformation(const DeclarationPointer& decl, const TopDUContext* topContext) { if (!decl) { return {}; } if (decl->isTypeAlias()) { // show size information for underlying type of aliases / typedefs etc. const auto type = TypeUtils::targetType(decl->abstractType(), topContext); if (const auto* idType = dynamic_cast(type.data())) { return declarationSizeInformation(DeclarationPointer(idType->declaration(topContext)), topContext); } return {}; } // Note that ClassMemberDeclaration also includes ClassDeclaration, which uses the sizeOf and alignOf fields, // but normally leaves the bitOffsetOf unset (-1). const auto* memberDecl = dynamic_cast(decl.data()); if (memberDecl && (memberDecl->bitOffsetOf() > 0 || memberDecl->sizeOf() > 0 || memberDecl->alignOf() > 0)) { QString sizeInfo = QStringLiteral("

"); if (memberDecl->bitOffsetOf() >= 0) { const auto byteOffset = memberDecl->bitOffsetOf() / 8; const auto bitOffset = memberDecl->bitOffsetOf() % 8; const QString byteOffsetStr = i18np("1 Byte", "%1 Bytes", byteOffset); const QString bitOffsetStr = bitOffset ? i18np("1 Bit", "%1 Bits", bitOffset) : QString(); sizeInfo += i18n("offset in parent: %1", bitOffset ? i18nc("%1: bytes, %2: bits", "%1, %2", byteOffsetStr, bitOffsetStr) : byteOffsetStr) + QLatin1String("; "); } if (memberDecl->sizeOf() >= 0) { sizeInfo += i18n("size: %1 Bytes", memberDecl->sizeOf()) + QLatin1String("; "); } if (memberDecl->alignOf() >= 0) { sizeInfo += i18n("aligned to: %1 Bytes", memberDecl->alignOf()); } sizeInfo += QStringLiteral("

"); return sizeInfo; } return QString(); } } diff --git a/kdevplatform/language/duchain/navigation/abstractincludenavigationcontext.cpp b/kdevplatform/language/duchain/navigation/abstractincludenavigationcontext.cpp index 1bdaba5edf..f5a706ff90 100644 --- a/kdevplatform/language/duchain/navigation/abstractincludenavigationcontext.cpp +++ b/kdevplatform/language/duchain/navigation/abstractincludenavigationcontext.cpp @@ -1,183 +1,183 @@ /* Copyright 2007 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. 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 "abstractincludenavigationcontext.h" #include #include #include #include namespace KDevelop { AbstractIncludeNavigationContext::AbstractIncludeNavigationContext(const IncludeItem& item, const TopDUContextPointer& topContext, const ParsingEnvironmentType& type) : AbstractNavigationContext(topContext) , m_type(type) , m_item(item) {} TopDUContext* pickContextWithData(const QList& duchains, uint maxDepth, const ParsingEnvironmentType& type, bool forcePick = true) { TopDUContext* duchain = nullptr; for (TopDUContext* ctx : duchains) { if (!ctx->parsingEnvironmentFile() || ctx->parsingEnvironmentFile()->type() != type) continue; if (ctx->childContexts().count() != 0 && (duchain == nullptr || ctx->childContexts().count() > duchain->childContexts().count())) { duchain = ctx; } if (ctx->localDeclarations().count() != 0 && (duchain == nullptr || ctx->localDeclarations().count() > duchain->localDeclarations().count())) { duchain = ctx; } } if (!duchain && maxDepth != 0) { if (maxDepth != 0) { for (TopDUContext* ctx : duchains) { QList children; foreach (const DUContext::Import& import, ctx->importedParentContexts()) if (import.context(nullptr)) children << import.context(nullptr)->topContext(); duchain = pickContextWithData(children, maxDepth - 1, type, false); if (duchain) break; } } } if (!duchain && !duchains.isEmpty() && forcePick) duchain = duchains.first(); return duchain; } QString AbstractIncludeNavigationContext::html(bool shorten) { clear(); - modifyHtml() += QLatin1String("

") + fontSizePrefix(shorten); + modifyHtml() += QLatin1String("

"); addExternalHtml(prefix()); QUrl u = m_item.url(); NavigationAction action(u, KTextEditor::Cursor(0, 0)); makeLink(u.toDisplayString(QUrl::PreferLocalFile), u.toString(), action); modifyHtml() += QStringLiteral("
"); QList duchains = DUChain::self()->chainsForDocument(u); //Pick the one duchain for this document that has the most child-contexts/declarations. //This prevents picking a context that is empty due to header-guards. TopDUContext* duchain = pickContextWithData(duchains, 2, m_type); if (duchain) { getFileInfo(duchain); if (!shorten) { modifyHtml() += labelHighlight(i18n("Declarations:")) + QLatin1String("
"); bool first = true; QVector decs; addDeclarationsFromContext(duchain, first, decs); } } else if (duchains.isEmpty()) { modifyHtml() += i18n("not parsed yet"); } addExternalHtml(suffix()); - modifyHtml() += fontSizeSuffix(shorten) + QLatin1String("

"); + modifyHtml() += QLatin1String("

"); return currentHtml(); } void AbstractIncludeNavigationContext::getFileInfo(TopDUContext* duchain) { modifyHtml() += QStringLiteral("%1: %2 %3: %4").arg(labelHighlight(i18nc("Files included into this file", "Includes"))).arg( duchain->importedParentContexts().count()).arg(labelHighlight(i18nc( "Count of files this file was included into", "Included by"))).arg( duchain->importers().count()); modifyHtml() += QStringLiteral("
"); } QString AbstractIncludeNavigationContext::name() const { return m_item.name; } bool AbstractIncludeNavigationContext::filterDeclaration(Declaration* /*decl*/) { return true; } void AbstractIncludeNavigationContext::addDeclarationsFromContext(KDevelop::DUContext* ctx, bool& first, QVector& addedDeclarations, const QString& indent) { //modifyHtml() += indent + ctx->localScopeIdentifier().toString() + "{
"; QVector children = ctx->childContexts(); QVector declarations = ctx->localDeclarations(); QVector::const_iterator childIterator = children.constBegin(); QVector::const_iterator declarationIterator = declarations.constBegin(); while (childIterator != children.constEnd() || declarationIterator != declarations.constEnd()) { //Show declarations/contexts in the order they appear in the file int currentDeclarationLine = -1; int currentContextLine = -1; if (declarationIterator != declarations.constEnd()) currentDeclarationLine = (*declarationIterator)->rangeInCurrentRevision().start().line(); if (childIterator != children.constEnd()) currentDeclarationLine = (*childIterator)->rangeInCurrentRevision().start().line(); if ((currentDeclarationLine <= currentContextLine || currentContextLine == -1 || childIterator == children.constEnd()) && declarationIterator != declarations.constEnd()) { IdentifierPair id = qMakePair(static_cast((*declarationIterator)->kind()), (*declarationIterator)->qualifiedIdentifier().index()); if (!addedDeclarations.contains(id) && filterDeclaration(*declarationIterator)) { //Show the declaration if (!first) modifyHtml() += QStringLiteral(", "); else first = false; modifyHtml() += QString(indent + declarationKind(DeclarationPointer( *declarationIterator)) + QLatin1Char(' ')).toHtmlEscaped(); makeLink((*declarationIterator)->qualifiedIdentifier().toString(), DeclarationPointer(*declarationIterator), NavigationAction::NavigateDeclaration); addedDeclarations << id; } ++declarationIterator; } else { //Eventually Recurse into the context if ((*childIterator)->type() == DUContext::Global || (*childIterator)->type() == DUContext::Namespace /*|| (*childIterator)->type() == DUContext::Class*/) addDeclarationsFromContext(*childIterator, first, addedDeclarations, indent + QLatin1Char(' ')); ++childIterator; } } //modifyHtml() += "}
"; } } diff --git a/kdevplatform/language/duchain/navigation/abstractnavigationcontext.cpp b/kdevplatform/language/duchain/navigation/abstractnavigationcontext.cpp index 92d52b8868..a4c29e68bc 100644 --- a/kdevplatform/language/duchain/navigation/abstractnavigationcontext.cpp +++ b/kdevplatform/language/duchain/navigation/abstractnavigationcontext.cpp @@ -1,582 +1,572 @@ /* Copyright 2007 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. 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 "abstractnavigationcontext.h" #include #include #include "abstractdeclarationnavigationcontext.h" #include "abstractnavigationwidget.h" #include "usesnavigationcontext.h" #include "../../../interfaces/icore.h" #include "../../../interfaces/idocumentcontroller.h" #include "../functiondeclaration.h" #include "../namespacealiasdeclaration.h" #include "../types/functiontype.h" #include "../types/structuretype.h" #include #include #include #include namespace KDevelop { class AbstractNavigationContextPrivate { public: QVector m_children; //Used to keep alive all children until this is deleted int m_selectedLink = 0; //The link currently selected NavigationAction m_selectedLinkAction; //Target of the currently selected link bool m_shorten = false; //A counter used while building the html-code to count the used links. int m_linkCount = -1; //Something else than -1 if the current position is represented by a line-number, not a link. int m_currentLine = 0; int m_currentPositionLine = 0; QMap m_links; QMap m_linkLines; //Holds the line for each link QMap m_intLinks; AbstractNavigationContext* m_previousContext; QString m_prefix, m_suffix; TopDUContextPointer m_topContext; QString m_currentText; //Here the text is built }; void AbstractNavigationContext::setTopContext(const TopDUContextPointer& context) { d->m_topContext = context; } TopDUContextPointer AbstractNavigationContext::topContext() const { return d->m_topContext; } AbstractNavigationContext::AbstractNavigationContext(const TopDUContextPointer& topContext, AbstractNavigationContext* previousContext) : d(new AbstractNavigationContextPrivate) { d->m_previousContext = previousContext; d->m_topContext = topContext; qRegisterMetaType("KTextEditor::Cursor"); qRegisterMetaType("IDocumentation::Ptr"); } AbstractNavigationContext::~AbstractNavigationContext() { } void AbstractNavigationContext::addExternalHtml(const QString& text) { int lastPos = 0; int pos = 0; QString fileMark = QStringLiteral("KDEV_FILE_LINK{"); while (pos < text.length() && (pos = text.indexOf(fileMark, pos)) != -1) { modifyHtml() += text.mid(lastPos, pos - lastPos); pos += fileMark.length(); if (pos != text.length()) { int fileEnd = text.indexOf(QLatin1Char('}'), pos); if (fileEnd != -1) { QString file = text.mid(pos, fileEnd - pos); pos = fileEnd + 1; const QUrl url = QUrl::fromUserInput(file); makeLink(url.fileName(), file, NavigationAction(url, KTextEditor::Cursor())); } } lastPos = pos; } modifyHtml() += text.mid(lastPos, text.length() - lastPos); } void AbstractNavigationContext::makeLink(const QString& name, const DeclarationPointer& declaration, NavigationAction::Type actionType) { NavigationAction action(declaration, actionType); makeLink(name, QString(), action); } QString AbstractNavigationContext::createLink(const QString& name, const QString&, const NavigationAction& action) { if (d->m_shorten) { //Do not create links in shortened mode, it's only for viewing return typeHighlight(name.toHtmlEscaped()); } // NOTE: Since the by definition in the HTML standard some uri components // are case-insensitive, we define a new lowercase link-id for each // link. Otherwise Qt 5 seems to mess up the casing and the link // cannot be matched when it's executed. QString hrefId = QStringLiteral("link_%1").arg(d->m_links.count()); d->m_links[hrefId] = action; d->m_intLinks[d->m_linkCount] = action; d->m_linkLines[d->m_linkCount] = d->m_currentLine; if (d->m_currentPositionLine == d->m_currentLine) { d->m_currentPositionLine = -1; d->m_selectedLink = d->m_linkCount; } QString str = name.toHtmlEscaped(); if (d->m_linkCount == d->m_selectedLink) str = QLatin1String("") + str + QLatin1String( ""); QString ret = QLatin1String("m_linkCount == d->m_selectedLink && d->m_currentPositionLine == -1) ? QStringLiteral(" name = \"currentPosition\"") : QString()) + QLatin1Char('>') + str + QLatin1String(""); if (d->m_selectedLink == d->m_linkCount) d->m_selectedLinkAction = action; ++d->m_linkCount; return ret; } void AbstractNavigationContext::makeLink(const QString& name, const QString& targetId, const NavigationAction& action) { modifyHtml() += createLink(name, targetId, action); } void AbstractNavigationContext::clear() { d->m_linkCount = 0; d->m_currentLine = 0; d->m_currentText.clear(); d->m_links.clear(); d->m_intLinks.clear(); d->m_linkLines.clear(); } void AbstractNavigationContext::executeLink(const QString& link) { const auto actionIt = d->m_links.constFind(link); if (actionIt == d->m_links.constEnd()) return; execute(*actionIt); } NavigationContextPointer AbstractNavigationContext::executeKeyAction(const QString& key) { Q_UNUSED(key); return NavigationContextPointer(this); } NavigationContextPointer AbstractNavigationContext::execute(const NavigationAction& action) { if (action.targetContext) return NavigationContextPointer(action.targetContext); if (action.type == NavigationAction::ExecuteKey) return executeKeyAction(action.key); if (!action.decl && (action.type != NavigationAction::JumpToSource || action.document.isEmpty())) { qCDebug(LANGUAGE) << "Navigation-action has invalid declaration" << endl; return NavigationContextPointer(this); } switch (action.type) { case NavigationAction::ExecuteKey: break; case NavigationAction::None: qCDebug(LANGUAGE) << "Tried to execute an invalid action in navigation-widget" << endl; break; case NavigationAction::NavigateDeclaration: { auto ctx = dynamic_cast(d->m_previousContext); if (ctx && ctx->declaration() == action.decl) return NavigationContextPointer(d->m_previousContext); return registerChild(action.decl); } case NavigationAction::NavigateUses: { auto* browser = ICore::self()->pluginController()->extensionForPlugin(); if (browser) { browser->showUses(action.decl); return NavigationContextPointer(this); } Q_FALLTHROUGH(); } case NavigationAction::ShowUses: { return registerChild(new UsesNavigationContext(action.decl.data(), this)); } case NavigationAction::JumpToSource: { QUrl doc = action.document; KTextEditor::Cursor cursor = action.cursor; { DUChainReadLocker lock(DUChain::lock()); if (action.decl) { if (doc.isEmpty()) { doc = action.decl->url().toUrl(); /* if(action.decl->internalContext()) cursor = action.decl->internalContext()->range().start() + KTextEditor::Cursor(0, 1); else*/ cursor = action.decl->rangeInCurrentRevision().start(); } action.decl->activateSpecialization(); } } //This is used to execute the slot delayed in the event-loop, so crashes are avoided QMetaObject::invokeMethod(ICore::self()->documentController(), "openDocument", Qt::QueuedConnection, Q_ARG(QUrl, doc), Q_ARG(KTextEditor::Cursor, cursor)); break; } case NavigationAction::ShowDocumentation: { auto doc = ICore::self()->documentationController()->documentationForDeclaration(action.decl.data()); // This is used to execute the slot delayed in the event-loop, so crashes are avoided // which can happen e.g. due to focus change events resulting in tooltip destruction and thus this object QMetaObject::invokeMethod( ICore::self()->documentationController(), "showDocumentation", Qt::QueuedConnection, Q_ARG(IDocumentation::Ptr, doc)); } break; } return NavigationContextPointer(this); } AbstractNavigationContext* AbstractNavigationContext::previousContext() const { return d->m_previousContext; } void AbstractNavigationContext::setPreviousContext(AbstractNavigationContext* previous) { d->m_previousContext = previous; } NavigationContextPointer AbstractNavigationContext::registerChild(AbstractNavigationContext* context) { d->m_children << NavigationContextPointer(context); return d->m_children.last(); } NavigationContextPointer AbstractNavigationContext::registerChild(const DeclarationPointer& declaration) { //We create a navigation-widget here, and steal its context.. evil ;) QScopedPointer navigationWidget(declaration->context()->createNavigationWidget(declaration.data())); if (auto* abstractNavigationWidget = dynamic_cast(navigationWidget.data())) { NavigationContextPointer ret = abstractNavigationWidget->context(); ret->setPreviousContext(this); d->m_children << ret; return ret; } else { return NavigationContextPointer(this); } } const int lineJump = 3; void AbstractNavigationContext::down() { //Make sure link-count is valid if (d->m_linkCount == -1) { DUChainReadLocker lock; html(); } int fromLine = d->m_currentPositionLine; if (d->m_selectedLink >= 0 && d->m_selectedLink < d->m_linkCount) { if (fromLine == -1) fromLine = d->m_linkLines[d->m_selectedLink]; for (int newSelectedLink = d->m_selectedLink + 1; newSelectedLink < d->m_linkCount; ++newSelectedLink) { if (d->m_linkLines[newSelectedLink] > fromLine && d->m_linkLines[newSelectedLink] - fromLine <= lineJump) { d->m_selectedLink = newSelectedLink; d->m_currentPositionLine = -1; return; } } } if (fromLine == -1) fromLine = 0; d->m_currentPositionLine = fromLine + lineJump; if (d->m_currentPositionLine > d->m_currentLine) d->m_currentPositionLine = d->m_currentLine; } void AbstractNavigationContext::up() { //Make sure link-count is valid if (d->m_linkCount == -1) { DUChainReadLocker lock; html(); } int fromLine = d->m_currentPositionLine; if (d->m_selectedLink >= 0 && d->m_selectedLink < d->m_linkCount) { if (fromLine == -1) fromLine = d->m_linkLines[d->m_selectedLink]; for (int newSelectedLink = d->m_selectedLink - 1; newSelectedLink >= 0; --newSelectedLink) { if (d->m_linkLines[newSelectedLink] < fromLine && fromLine - d->m_linkLines[newSelectedLink] <= lineJump) { d->m_selectedLink = newSelectedLink; d->m_currentPositionLine = -1; return; } } } if (fromLine == -1) fromLine = d->m_currentLine; d->m_currentPositionLine = fromLine - lineJump; if (d->m_currentPositionLine < 0) d->m_currentPositionLine = 0; } void AbstractNavigationContext::nextLink() { //Make sure link-count is valid if (d->m_linkCount == -1) { DUChainReadLocker lock; html(); } d->m_currentPositionLine = -1; if (d->m_linkCount > 0) d->m_selectedLink = (d->m_selectedLink + 1) % d->m_linkCount; } void AbstractNavigationContext::previousLink() { //Make sure link-count is valid if (d->m_linkCount == -1) { DUChainReadLocker lock; html(); } d->m_currentPositionLine = -1; if (d->m_linkCount > 0) { --d->m_selectedLink; if (d->m_selectedLink < 0) d->m_selectedLink += d->m_linkCount; } Q_ASSERT(d->m_selectedLink >= 0); } int AbstractNavigationContext::linkCount() const { return d->m_linkCount; } QString AbstractNavigationContext::prefix() const { return d->m_prefix; } QString AbstractNavigationContext::suffix() const { return d->m_suffix; } void AbstractNavigationContext::setPrefixSuffix(const QString& prefix, const QString& suffix) { d->m_prefix = prefix; d->m_suffix = suffix; } NavigationContextPointer AbstractNavigationContext::back() { if (d->m_previousContext) return NavigationContextPointer(d->m_previousContext); else return NavigationContextPointer(this); } NavigationContextPointer AbstractNavigationContext::accept() { if (d->m_selectedLink >= 0 && d->m_selectedLink < d->m_linkCount) { NavigationAction action = d->m_intLinks[d->m_selectedLink]; return execute(action); } return NavigationContextPointer(this); } NavigationContextPointer AbstractNavigationContext::accept(IndexedDeclaration decl) { if (decl.data()) { NavigationAction action(DeclarationPointer(decl.data()), NavigationAction::NavigateDeclaration); return execute(action); } else { return NavigationContextPointer(this); } } NavigationContextPointer AbstractNavigationContext::acceptLink(const QString& link) { const auto actionIt = d->m_links.constFind(link); if (actionIt == d->m_links.constEnd()) { qCDebug(LANGUAGE) << "Executed unregistered link " << link << endl; return NavigationContextPointer(this); } return execute(*actionIt); } NavigationAction AbstractNavigationContext::currentAction() const { return d->m_selectedLinkAction; } QString AbstractNavigationContext::declarationKind(const DeclarationPointer& decl) { const auto* function = dynamic_cast(decl.data()); QString kind; if (decl->isTypeAlias()) kind = i18n("Typedef"); else if (decl->kind() == Declaration::Type) { if (decl->type()) kind = i18n("Class"); } else if (decl->kind() == Declaration::Instance) { kind = i18n("Variable"); } else if (decl->kind() == Declaration::Namespace) { kind = i18n("Namespace"); } if (auto* alias = dynamic_cast(decl.data())) { if (alias->identifier().isEmpty()) kind = i18n("Namespace import"); else kind = i18n("Namespace alias"); } if (function) kind = i18n("Function"); if (decl->isForwardDeclaration()) kind = i18n("Forward Declaration"); return kind; } QString AbstractNavigationContext::html(bool shorten) { d->m_shorten = shorten; return QString(); } bool AbstractNavigationContext::alreadyComputed() const { return !d->m_currentText.isEmpty(); } bool AbstractNavigationContext::isWidgetMaximized() const { return true; } QWidget* AbstractNavigationContext::widget() const { return nullptr; } ///Splits the string by the given regular expression, but keeps the split-matches at the end of each line static QStringList splitAndKeep(QString str, const QRegExp& regExp) { QStringList ret; int place = regExp.indexIn(str); while (place != -1) { ret << str.left(place + regExp.matchedLength()); str.remove(0, place + regExp.matchedLength()); place = regExp.indexIn(str); } ret << str; return ret; } void AbstractNavigationContext::addHtml(const QString& html) { QRegExp newLineRegExp(QStringLiteral("
|
")); foreach (const QString& line, splitAndKeep(html, newLineRegExp)) { d->m_currentText += line; if (line.indexOf(newLineRegExp) != -1) { ++d->m_currentLine; if (d->m_currentLine == d->m_currentPositionLine) { d->m_currentText += QStringLiteral( " <-> "); // ><-> is <-> } } } } QString AbstractNavigationContext::currentHtml() const { return d->m_currentText; } -QString AbstractNavigationContext::fontSizePrefix(bool /*shorten*/) const -{ - return QString(); -} - -QString AbstractNavigationContext::fontSizeSuffix(bool /*shorten*/) const -{ - return QString(); -} - QString Colorizer::operator()(const QString& str) const { QString ret = QLatin1String("") + str + QLatin1String(""); if (m_formatting & Fixed) ret = QLatin1String("") + ret + QLatin1String(""); if (m_formatting & Bold) ret = QLatin1String("") + ret + QLatin1String(""); if (m_formatting & Italic) ret = QLatin1String("") + ret + QLatin1String(""); return ret; } const Colorizer AbstractNavigationContext::typeHighlight(QStringLiteral("006000")); const Colorizer AbstractNavigationContext::errorHighlight(QStringLiteral("990000")); const Colorizer AbstractNavigationContext::labelHighlight(QStringLiteral("000000")); const Colorizer AbstractNavigationContext::codeHighlight(QStringLiteral("005000")); const Colorizer AbstractNavigationContext::propertyHighlight(QStringLiteral("009900")); const Colorizer AbstractNavigationContext::navigationHighlight(QStringLiteral("000099")); const Colorizer AbstractNavigationContext::importantHighlight(QStringLiteral( "000000"), Colorizer::Bold | Colorizer::Italic); const Colorizer AbstractNavigationContext::commentHighlight(QStringLiteral("303030")); const Colorizer AbstractNavigationContext::nameHighlight(QStringLiteral("000000"), Colorizer::Bold); } diff --git a/kdevplatform/language/duchain/navigation/abstractnavigationcontext.h b/kdevplatform/language/duchain/navigation/abstractnavigationcontext.h index 6583f88d1a..59e2e03b82 100644 --- a/kdevplatform/language/duchain/navigation/abstractnavigationcontext.h +++ b/kdevplatform/language/duchain/navigation/abstractnavigationcontext.h @@ -1,185 +1,180 @@ /* Copyright 2007 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. 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 KDEVPLATFORM_ABSTRACTNAVIGATIONCONTEXT_H #define KDEVPLATFORM_ABSTRACTNAVIGATIONCONTEXT_H #include #include #include "../indexeddeclaration.h" #include "navigationaction.h" namespace KDevelop { /** A helper-class for elegant colorization of html-strings . * * Initialize it with a html-color like "990000". and colorize strings * using operator() */ struct KDEVPLATFORMLANGUAGE_EXPORT Colorizer { enum FormattingFlag { Nothing = 0x0, Bold = 0x1, Italic = 0x2, Fixed = 0x4 }; Q_DECLARE_FLAGS(Formatting, FormattingFlag) explicit Colorizer(const QString& color, Formatting formatting = Nothing) : m_color(color) , m_formatting(formatting) { } QString operator()(const QString& str) const; QString m_color; Formatting m_formatting; }; class AbstractNavigationContext; using NavigationContextPointer = QExplicitlySharedDataPointer; class KDEVPLATFORMLANGUAGE_EXPORT AbstractNavigationContext : public QObject , public QSharedData { Q_OBJECT public: explicit AbstractNavigationContext(const TopDUContextPointer& topContext = TopDUContextPointer(), AbstractNavigationContext* previousContext = nullptr); ~AbstractNavigationContext() override; void nextLink(); void previousLink(); int linkCount() const; void up(); void down(); QString prefix() const; QString suffix() const; void setPrefixSuffix(const QString& prefix, const QString& suffix); NavigationContextPointer accept(); NavigationContextPointer back(); NavigationContextPointer accept(IndexedDeclaration decl); NavigationContextPointer acceptLink(const QString& link); NavigationAction currentAction() const; virtual QString name() const = 0; ///Here the context can return html to be displayed. ///NOTE: The DUChain must be locked while this is called. virtual QString html(bool shorten = false); ///Here the context can return a widget to be displayed. ///The widget stays owned by this navigation-context. ///The widget may have a signal "navigateDeclaration(KDevelop::IndexedDeclaration)". ///If that signal is emitted, the new declaration is navigated in the navigation-wdiget. virtual QWidget* widget() const; ///Whether the widget returned by widget() should take the maximum possible spsace. ///The default implementation returns true. virtual bool isWidgetMaximized() const; ///Returns whether this context's string has already been computed, and is up to date. ///After clear() was called, this returns false again. bool alreadyComputed() const; TopDUContextPointer topContext() const; void setTopContext(const TopDUContextPointer& context); void executeLink(const QString& link); NavigationContextPointer execute(const NavigationAction& action); Q_SIGNALS: void contentsChanged(); protected: - /// Returns the html font-size prefix (aka. <small> or similar) for the given mode - QString fontSizePrefix(bool shorten) const; - /// Returns the html font-size suffix (aka. <small> or similar) for the given mode - QString fontSizeSuffix(bool shorten) const; - AbstractNavigationContext* previousContext() const; virtual void setPreviousContext(AbstractNavigationContext* previousContext); struct TextHandler { explicit TextHandler(AbstractNavigationContext* c) : context(c) { } void operator+=(const QString& str) const { context->addHtml(str); } AbstractNavigationContext* context; }; ///Override this to execute own key-actions using NavigationAction virtual NavigationContextPointer executeKeyAction(const QString& key); ///Adds given the text to currentHtml() void addHtml(const QString& html); ///Returns the html text being built in its current state QString currentHtml() const; ///Returns a convenience object that allows writing "modifyHtml() += "Hallo";" TextHandler modifyHtml() { return TextHandler(this); } //Clears the computed html and links void clear(); void addExternalHtml(const QString& text); ///Creates and registers a link to the given declaration, labeled by the given name virtual void makeLink(const QString& name, const DeclarationPointer& declaration, NavigationAction::Type actionType); ///Creates a link that executes the given action and adds it to the current context void makeLink(const QString& name, const QString& targetId, const NavigationAction& action); ///Creates a link that executes the given action and returns it QString createLink(const QString& name, const QString& targetId, const NavigationAction& action); NavigationContextPointer registerChild(const DeclarationPointer& /*declaration*/); NavigationContextPointer registerChild(AbstractNavigationContext* context); virtual QString declarationKind(const DeclarationPointer& decl); static const Colorizer typeHighlight; static const Colorizer errorHighlight; static const Colorizer labelHighlight; static const Colorizer codeHighlight; static const Colorizer propertyHighlight; static const Colorizer navigationHighlight; static const Colorizer importantHighlight; static const Colorizer commentHighlight; static const Colorizer nameHighlight; private: const QScopedPointer d; }; } Q_DECLARE_OPERATORS_FOR_FLAGS(KDevelop::Colorizer::Formatting) #endif diff --git a/kdevplatform/language/duchain/navigation/usesnavigationcontext.cpp b/kdevplatform/language/duchain/navigation/usesnavigationcontext.cpp index 9ea5c8ae01..80c18c7c25 100644 --- a/kdevplatform/language/duchain/navigation/usesnavigationcontext.cpp +++ b/kdevplatform/language/duchain/navigation/usesnavigationcontext.cpp @@ -1,70 +1,70 @@ /* Copyright 2008 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. 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 "usesnavigationcontext.h" #include "useswidget.h" #include #include #include #include using namespace KDevelop; UsesNavigationContext::UsesNavigationContext(IndexedDeclaration declaration, AbstractNavigationContext* previousContext) : AbstractNavigationContext(TopDUContextPointer(), previousContext) , m_declaration(declaration) { m_widget = new UsesWidget(m_declaration); } UsesNavigationContext::~UsesNavigationContext() { delete m_widget; } QString UsesNavigationContext::name() const { return QStringLiteral("Uses"); } QString UsesNavigationContext::html(bool shorten) { clear(); - modifyHtml() += QLatin1String("

") + fontSizePrefix(shorten); + modifyHtml() += QLatin1String("

"); if (auto context = previousContext()) { modifyHtml() += navigationHighlight(i18n("Uses of ")); makeLink(context->name(), context->name(), NavigationAction(context)); } else { KDevelop::DUChainReadLocker lock(DUChain::lock()); if (Declaration* decl = m_declaration.data()) { makeLink(i18n("Uses of %1", decl->toString()), DeclarationPointer( decl), NavigationAction::NavigateDeclaration); } } - modifyHtml() += fontSizeSuffix(shorten) + QLatin1String("

"); + modifyHtml() += QLatin1String("

"); return currentHtml(); } QWidget* UsesNavigationContext::widget() const { return m_widget; } diff --git a/plugins/clang/duchain/macronavigationcontext.cpp b/plugins/clang/duchain/macronavigationcontext.cpp index 3c065efdcd..187f44e654 100644 --- a/plugins/clang/duchain/macronavigationcontext.cpp +++ b/plugins/clang/duchain/macronavigationcontext.cpp @@ -1,95 +1,95 @@ /* Copyright 2007 David Nolden Copyright 2014 Kevin Funk This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. 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 "macronavigationcontext.h" #include "util/clangdebug.h" #include "util/clangutils.h" #include #include using namespace KDevelop; MacroNavigationContext::MacroNavigationContext(const MacroDefinition::Ptr& macro, const KDevelop::DocumentCursor& /* expansionLocation */) : m_macro(macro) { } MacroNavigationContext::~MacroNavigationContext() { } QString MacroNavigationContext::name() const { return m_macro->identifier().toString(); } QString MacroNavigationContext::html(bool shorten) { clear(); - modifyHtml() += QLatin1String("

") + fontSizePrefix(shorten); + modifyHtml() += QLatin1String("

"); addExternalHtml(prefix()); QStringList parameterList; parameterList.reserve(m_macro->parametersSize()); FOREACH_FUNCTION(const auto& parameter, m_macro->parameters) { parameterList << parameter.str(); } const QString parameters = (!parameterList.isEmpty() ? QStringLiteral("(%1)").arg(parameterList.join(QStringLiteral(", "))) : QString()); const QUrl url = m_macro->url().toUrl(); const QString path = url.toLocalFile(); KTextEditor::Cursor cursor(m_macro->rangeInCurrentRevision().start()); NavigationAction action(url, cursor); modifyHtml() += i18nc("%1: macro type, i.e.: 'Function macro' or just 'Macro'" "%2: the macro name and arguments", "%1: %2", (m_macro->isFunctionLike() ? i18n("Function macro") : i18n("Macro")), importantHighlight(name()) + parameters); modifyHtml() += QStringLiteral("
"); modifyHtml() += i18nc("%1: the link to the definition", "Defined in: %1", createLink(QStringLiteral("%1 :%2").arg(url.fileName()).arg(cursor.line()+1), path, action)); modifyHtml() += QStringLiteral(" "); //The action name _must_ stay "show_uses", since that is also used from outside makeLink(i18n("Show uses"), QStringLiteral("show_uses"), NavigationAction(m_macro.dynamicCast(), NavigationAction::NavigateUses)); auto code = m_macro->definition().str(); modifyHtml() += QLatin1String("

") + i18n("Body: "); modifyHtml() += QLatin1String("") + code.toHtmlEscaped().replace(QLatin1Char('\n'), QStringLiteral("
")) + QLatin1String("
"); modifyHtml() += QStringLiteral("

"); - modifyHtml() += fontSizeSuffix(shorten) + QLatin1String("

"); + modifyHtml() += QLatin1String("

"); return currentHtml(); } QString MacroNavigationContext::retrievePreprocessedBody(const DocumentCursor& /*expansionLocation*/) const { const TopDUContext* topContext = m_macro->topContext(); if (!topContext) { return QString(); } // TODO: Implement me. Still not exactly sure what do to here... return QString(); }