diff --git a/language/duchain/navigation/abstractdeclarationnavigationcontext.cpp b/language/duchain/navigation/abstractdeclarationnavigationcontext.cpp
index 3a3ace5e3..ed0c17ca8 100644
--- a/language/duchain/navigation/abstractdeclarationnavigationcontext.cpp
+++ b/language/duchain/navigation/abstractdeclarationnavigationcontext.cpp
@@ -1,769 +1,788 @@
/*
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 "util/debug.h"
#include
#include
#include
#include
#include
namespace KDevelop {
-AbstractDeclarationNavigationContext::AbstractDeclarationNavigationContext( DeclarationPointer decl, TopDUContextPointer topContext, AbstractNavigationContext* previousContext)
- : AbstractNavigationContext((topContext ? topContext : TopDUContextPointer(decl ? decl->topContext() : nullptr)), previousContext), m_declaration(decl), m_fullBackwardSearch(false)
+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
- FunctionDefinition* definition = dynamic_cast(m_declaration.data());
+ FunctionDefinition* definition = dynamic_cast(d->m_declaration.data());
if(definition && definition->declaration())
- m_declaration = DeclarationPointer(definition->declaration());
+ d->m_declaration = DeclarationPointer(definition->declaration());
+}
+
+AbstractDeclarationNavigationContext::~AbstractDeclarationNavigationContext()
+{
}
QString AbstractDeclarationNavigationContext::name() const
{
- if(m_declaration.data())
- return prettyQualifiedIdentifier(m_declaration).toString();
+ if(d->m_declaration.data())
+ return prettyQualifiedIdentifier(d->m_declaration).toString();
else
- return declarationName(m_declaration);
+ return declarationName(d->m_declaration);
}
QString AbstractDeclarationNavigationContext::html(bool shorten)
{
DUChainReadLocker lock(DUChain::lock(), 300);
if ( !lock.locked() ) {
return {};
}
+
clear();
- m_shorten = shorten;
+ AbstractNavigationContext::html(shorten);
+
modifyHtml() += "" + fontSizePrefix(shorten);
- addExternalHtml(m_prefix);
+ addExternalHtml(prefix());
- if(!m_declaration.data()) {
+ if(!d->m_declaration.data()) {
modifyHtml() += i18n("
lost declaration
");
return currentHtml();
}
- if( m_previousContext ) {
- QString link = createLink( m_previousContext->name(), m_previousContext->name(), NavigationAction(m_previousContext) );
+ 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(m_declaration.data());
+ doc = ICore::self()->documentationController()->documentationForDeclaration(d->m_declaration.data());
- const AbstractFunctionDeclaration* function = dynamic_cast(m_declaration.data());
+ const AbstractFunctionDeclaration* function = dynamic_cast(d->m_declaration.data());
if( function ) {
htmlFunction();
- } else if( m_declaration->isTypeAlias() || m_declaration->type() || m_declaration->kind() == Declaration::Instance ) {
- if( m_declaration->isTypeAlias() )
+ } 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(m_declaration->type())
+ if(d->m_declaration->type())
modifyHtml() += i18n("enumerator ");
- AbstractType::Ptr useType = m_declaration->abstractType();
- if(m_declaration->isTypeAlias()) {
+ 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() += ' ' + identifierHighlight(declarationName(m_declaration).toHtmlEscaped(), m_declaration);
+ modifyHtml() += ' ' + identifierHighlight(declarationName(d->m_declaration).toHtmlEscaped(), d->m_declaration);
- if(auto integralType = m_declaration->type()) {
+ if(auto integralType = d->m_declaration->type()) {
const QString plainValue = integralType->valueAsString();
if (!plainValue.isEmpty()) {
modifyHtml() += QStringLiteral(" = ") + plainValue;
}
}
modifyHtml() += QStringLiteral("
");
}else{
- if( m_declaration->kind() == Declaration::Type && m_declaration->abstractType().cast() ) {
+ if( d->m_declaration->kind() == Declaration::Type && d->m_declaration->abstractType().cast() ) {
htmlClass();
}
- if ( m_declaration->kind() == Declaration::Namespace ) {
- modifyHtml() += i18n("namespace %1 ", identifierHighlight(m_declaration->qualifiedIdentifier().toString().toHtmlEscaped(), m_declaration));
- } else if ( m_declaration->kind() == Declaration::NamespaceAlias ) {
- modifyHtml() += identifierHighlight(declarationName(m_declaration).toHtmlEscaped(), m_declaration);
+ 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(m_declaration->type()) {
- EnumerationType::Ptr enumeration = m_declaration->type();
- modifyHtml() += i18n("enumeration %1 ", identifierHighlight(m_declaration->identifier().toString().toHtmlEscaped(), 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(m_declaration->isForwardDeclaration()) {
- ForwardDeclaration* forwardDec = static_cast(m_declaration.data());
- Declaration* resolved = forwardDec->resolve(m_topContext.data());
+ if(d->m_declaration->isForwardDeclaration()) {
+ ForwardDeclaration* 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() += ' ' + dec->url().str();
}
}
}
modifyHtml() += QStringLiteral("
");
}
}else{
- AbstractType::Ptr showType = m_declaration->abstractType();
+ 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 = m_declaration->qualifiedIdentifier();
+ QualifiedIdentifier identifier = d->m_declaration->qualifiedIdentifier();
if( identifier.count() > 1 ) {
- if( m_declaration->context() && m_declaration->context()->owner() )
+ if( d->m_declaration->context() && d->m_declaration->context()->owner() )
{
- Declaration* decl = m_declaration->context()->owner();
+ Declaration* decl = d->m_declaration->context()->owner();
FunctionDefinition* 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 && !m_declaration->comment().isEmpty() ) {
- QString comment = QString::fromUtf8(m_declaration->comment());
+ 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('\n', QLatin1String(" "));
comment.replace(QLatin1String("
"), QLatin1String(" "));
comment.replace(QLatin1String("
"), QLatin1String(" "));
modifyHtml() += commentHighlight(comment.toHtmlEscaped()) + " ";
}
- QString access = stringFromAccess(m_declaration);
+ QString access = stringFromAccess(d->m_declaration);
if( !access.isEmpty() )
modifyHtml() += labelHighlight(i18n("Access: %1 ", propertyHighlight(access.toHtmlEscaped())));
///@todo Enumerations
QString detailsHtml;
- QStringList details = declarationDetails(m_declaration);
+ QStringList details = declarationDetails(d->m_declaration);
if( !details.isEmpty() ) {
bool first = true;
foreach( const QString &str, details ) {
if( !first )
detailsHtml += QLatin1String(", ");
first = false;
detailsHtml += propertyHighlight(str);
}
}
- QString kind = declarationKind(m_declaration);
+ 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 (m_declaration->isDeprecated()) {
+ if (d->m_declaration->isDeprecated()) {
modifyHtml() += labelHighlight(i18n("Status: %1 ", propertyHighlight(i18n("Deprecated"))));
}
modifyHtml() += QStringLiteral("
");
if(!shorten)
htmlAdditionalNavigation();
if( !shorten ) {
- if(dynamic_cast(m_declaration.data()))
+ if(dynamic_cast(d->m_declaration.data()))
modifyHtml() += labelHighlight(i18n( "Def.: " ));
else
modifyHtml() += labelHighlight(i18n( "Decl.: " ));
- makeLink( QStringLiteral("%1 :%2").arg( m_declaration->url().toUrl().fileName() ).arg( m_declaration->rangeInCurrentRevision().start().line()+1 ), m_declaration, NavigationAction::JumpToSource );
+ 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(m_declaration.data())) {
- if( FunctionDefinition* definition = FunctionDefinition::definition(m_declaration.data()) ) {
+ 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( FunctionDefinition* definition = dynamic_cast(m_declaration.data()) ) {
+ if( FunctionDefinition* 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(m_declaration, NavigationAction::NavigateUses));
+ makeLink(i18n("Show uses"), QStringLiteral("show_uses"), NavigationAction(d->m_declaration, NavigationAction::NavigateUses));
}
- QByteArray declarationComment = m_declaration->comment();
+ 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() += "
" + commentHighlight(comment) + "
";
}
}
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("\n"));
comment = comment.toHtmlEscaped();
comment.replace('\n', QLatin1String("
")); //Replicate newlines in html
}
modifyHtml() += commentHighlight(comment);
modifyHtml() += QStringLiteral("
");
}
}
if(!shorten && doc) {
modifyHtml() += "" + i18n("Show documentation for ");
- makeLink(prettyQualifiedName(m_declaration),
- m_declaration, NavigationAction::ShowDocumentation);
+ makeLink(prettyQualifiedName(d->m_declaration),
+ d->m_declaration, NavigationAction::ShowDocumentation);
modifyHtml() += "
";
}
//modifyHtml() += "
";
- addExternalHtml(m_suffix);
+ addExternalHtml(suffix());
modifyHtml() += fontSizeSuffix(shorten) + "
";
return currentHtml();
}
AbstractType::Ptr AbstractDeclarationNavigationContext::typeToShow(AbstractType::Ptr type) {
return type;
}
void AbstractDeclarationNavigationContext::htmlFunction()
{
- const AbstractFunctionDeclaration* function = dynamic_cast(m_declaration.data());
+ const AbstractFunctionDeclaration* function = dynamic_cast(d->m_declaration.data());
Q_ASSERT(function);
- const ClassFunctionDeclaration* classFunDecl = dynamic_cast(m_declaration.data());
- const FunctionType::Ptr type = m_declaration->abstractType().cast();
+ const ClassFunctionDeclaration* 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() += ' ' + identifierHighlight(prettyIdentifier(m_declaration).toString().toHtmlEscaped(), m_declaration);
+ modifyHtml() += ' ' + 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::getArgumentContext(m_declaration.data())) {
- decls = argumentContext->localDeclarations(m_topContext.data());
+ if (DUContext* argumentContext = DUChainUtils::getArgumentContext(d->m_declaration.data())) {
+ decls = argumentContext->localDeclarations(topContext().data());
}
foreach(const AbstractType::Ptr& argType, type->arguments()) {
if( !first )
modifyHtml() += QStringLiteral(", ");
first = false;
eventuallyMakeTypeLinks( argType );
if (currentArgNum < decls.size()) {
- modifyHtml() += ' ' + identifierHighlight(decls[currentArgNum]->identifier().toString().toHtmlEscaped(), m_declaration);
+ modifyHtml() += ' ' + identifierHighlight(decls[currentArgNum]->identifier().toString().toHtmlEscaped(), d->m_declaration);
}
if( currentArgNum >= firstDefaultParam )
modifyHtml() += " = " + function->defaultParameters()[ currentArgNum - firstDefaultParam ].str().toHtmlEscaped();
++currentArgNum;
}
modifyHtml() += QStringLiteral(" )");
}
modifyHtml() += QStringLiteral("
");
}
Identifier AbstractDeclarationNavigationContext::prettyIdentifier(DeclarationPointer decl) const
{
Identifier ret;
QualifiedIdentifier q = prettyQualifiedIdentifier(decl);
if(!q.isEmpty())
ret = q.last();
return ret;
}
QualifiedIdentifier AbstractDeclarationNavigationContext::prettyQualifiedIdentifier(DeclarationPointer decl) const
{
if(decl)
return decl->qualifiedIdentifier();
else
return QualifiedIdentifier();
}
QString AbstractDeclarationNavigationContext::prettyQualifiedName(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 ClassFunctionDeclaration* classFunDecl = dynamic_cast(m_declaration.data());
+ const ClassFunctionDeclaration* classFunDecl = dynamic_cast(d->m_declaration.data());
if(classFunDecl) {
- Declaration* overridden = DUChainUtils::getOverridden(m_declaration.data());
+ Declaration* overridden = DUChainUtils::getOverridden(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, m_declaration->context()->importedParentContexts())
- if(import.context(m_topContext.data()))
- decls += import.context(m_topContext.data())->findDeclarations(QualifiedIdentifier(m_declaration->identifier()),
- CursorInRevision::invalid(), AbstractType::Ptr(), m_topContext.data(), DUContext::DontSearchInParent);
+ 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 = m_declaration->context()->owner();
+ Declaration* classDecl = d->m_declaration->context()->owner();
if(classDecl) {
- uint maxAllowedSteps = m_fullBackwardSearch ? (uint)-1 : 10;
+ uint maxAllowedSteps = d->m_fullBackwardSearch ? (uint)-1 : 10;
QList overriders = DUChainUtils::getOverriders(classDecl, classFunDecl, maxAllowedSteps);
if(!overriders.isEmpty()) {
modifyHtml() += i18n("Overridden in ");
bool first = true;
foreach(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 = m_fullBackwardSearch ? (uint)-1 : 10;
- QList inheriters = DUChainUtils::getInheriters(m_declaration.data(), maxAllowedSteps);
+ uint maxAllowedSteps = d->m_fullBackwardSearch ? (uint)-1 : 10;
+ QList inheriters = DUChainUtils::getInheriters(d->m_declaration.data(), maxAllowedSteps);
if(!inheriters.isEmpty()) {
modifyHtml() += i18n("Inherited by ");
bool first = true;
foreach(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(QString string)
{
makeLink(string, QStringLiteral("m_fullBackwardSearch=true"), NavigationAction(QStringLiteral("m_fullBackwardSearch=true")));
modifyHtml() += QStringLiteral("
");
}
NavigationContextPointer AbstractDeclarationNavigationContext::executeKeyAction( QString key )
{
if(key == QLatin1String("m_fullBackwardSearch=true")) {
- m_fullBackwardSearch = true;
+ d->m_fullBackwardSearch = true;
clear();
}
return NavigationContextPointer(this);
}
void AbstractDeclarationNavigationContext::htmlClass()
{
- StructureType::Ptr klass = m_declaration->abstractType().cast();
+ StructureType::Ptr klass = d->m_declaration->abstractType().cast();
Q_ASSERT(klass);
- ClassDeclaration* classDecl = dynamic_cast(klass->declaration(m_topContext.data()));
+ 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;
default:
modifyHtml() += QStringLiteral(" ");
break;
}
eventuallyMakeTypeLinks( klass.cast() );
FOREACH_FUNCTION( const BaseClassInstance& base, classDecl->baseClasses ) {
modifyHtml() += ", " + stringFromAccess(base.access) + " " + (base.virtualInheritance ? QStringLiteral("virtual") : QString()) + " ";
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)
{
Q_ASSERT(type);
Q_ASSERT(idType);
- if( Declaration* decl = idType->declaration(m_topContext.data()) ) {
+ if( Declaration* decl = idType->declaration(topContext().data()) ) {
//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();
IdentifiedType* 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(m_topContext.data())), NavigationAction::NavigateDeclaration );
+ makeLink(id.toString() , DeclarationPointer(idType->declaration(topContext().data())), NavigationAction::NavigateDeclaration );
} else {
- qCDebug(LANGUAGE) << "could not resolve declaration:" << idType->declarationId().isDirect() << idType->qualifiedIdentifier().toString() << "in top-context" << m_topContext->url().str();
+ qCDebug(LANGUAGE) << "could not resolve declaration:" << idType->declarationId().isDirect() << idType->qualifiedIdentifier().toString() << "in top-context" << topContext()->url().str();
modifyHtml() += typeHighlight(type->toString().toHtmlEscaped());
}
}
void AbstractDeclarationNavigationContext::eventuallyMakeTypeLinks( AbstractType::Ptr type )
{
type = typeToShow(type);
if( !type ) {
modifyHtml() += typeHighlight(QStringLiteral("").toHtmlEscaped());
return;
}
- AbstractType::Ptr target = TypeUtils::targetTypeKeepAliases( type, m_topContext.data() );
+ AbstractType::Ptr target = TypeUtils::targetTypeKeepAliases( type, topContext().data() );
const IdentifiedType* idType = dynamic_cast( target.data() );
qCDebug(LANGUAGE) << "making type-links for" << type->toString();
- if( idType && idType->declaration(m_topContext.data()) ) {
+ 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("\\&|\\*");
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 m_declaration;
+ 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(DeclarationPointer decl)
{
const ClassMemberDeclaration* memberDecl = dynamic_cast(decl.data());
if( memberDecl ) {
return stringFromAccess(memberDecl->accessPolicy());
}
return QString();
}
QString AbstractDeclarationNavigationContext::declarationName( DeclarationPointer decl ) const
{
if( NamespaceAliasDeclaration* alias = dynamic_cast(decl.data()) ) {
if( alias->identifier().isEmpty() )
return "using namespace " + alias->importIdentifier().toString();
else
return "namespace " + alias->identifier().toString() + " = " + alias->importIdentifier().toString();
}
if( !decl )
return i18nc("A declaration that is unknown", "Unknown");
else
return prettyIdentifier(decl).toString();
}
QStringList AbstractDeclarationNavigationContext::declarationDetails(DeclarationPointer decl)
{
QStringList details;
const AbstractFunctionDeclaration* function = dynamic_cast(decl.data());
const ClassMemberDeclaration* 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 ClassFunctionDeclaration* classFunDecl = dynamic_cast(decl.data());
if( classFunDecl ) {
if( classFunDecl->isSignal() )
details << QStringLiteral("signal");
if( classFunDecl->isSlot() )
details << QStringLiteral("slot");
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;
}
}
diff --git a/language/duchain/navigation/abstractdeclarationnavigationcontext.h b/language/duchain/navigation/abstractdeclarationnavigationcontext.h
index de59c7a3a..1a967e51f 100644
--- a/language/duchain/navigation/abstractdeclarationnavigationcontext.h
+++ b/language/duchain/navigation/abstractdeclarationnavigationcontext.h
@@ -1,95 +1,97 @@
/*
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_ABSTRACTDECLARATIONNAVIGATIONCONTEXT_H
#define KDEVPLATFORM_ABSTRACTDECLARATIONNAVIGATIONCONTEXT_H
#include "abstractnavigationcontext.h"
#include "../declaration.h"
#include "../duchainpointer.h"
#include "../types/abstracttype.h"
namespace KDevelop {
+class AbstractDeclarationNavigationContextPrivate;
class IdentifiedType;
class Identifier;
class QualifiedIdentifier;
class KDEVPLATFORMLANGUAGE_EXPORT AbstractDeclarationNavigationContext : public AbstractNavigationContext
{
Q_OBJECT
public:
- AbstractDeclarationNavigationContext( DeclarationPointer decl, KDevelop::TopDUContextPointer topContext, AbstractNavigationContext* previousContext = nullptr );
+ AbstractDeclarationNavigationContext(const DeclarationPointer& decl, const TopDUContextPointer& topContext,
+ AbstractNavigationContext* previousContext = nullptr);
+ ~AbstractDeclarationNavigationContext() override;
QString name() const override;
DeclarationPointer declaration() const;
///Execute an action. For example "show_uses" shows the uses of the declaration.
///Returns the context pointer for the new state.
NavigationContextPointer executeKeyAction(QString key) override;
- protected:
QString html(bool shorten = false) override;
- DeclarationPointer m_declaration;
-
+ protected:
///Should returns a stripped version of the declarations qualified identifier, with all implicit/redundant parts removed
virtual QualifiedIdentifier prettyQualifiedIdentifier(DeclarationPointer decl) const;
///Returns a stripped version of the declarations identifier, using prettyQualifiedIdentifier
Identifier prettyIdentifier(DeclarationPointer decl) const;
/// @return String version of the qualified identifier of @p decl, '' on an invalid QID
QString prettyQualifiedName(DeclarationPointer decl) const;
/**
* Return a rich-text version of the identifier @p identifier representing the declaration @p decl
*
* @note In case @p declaration is deprecated, the resulting string will get a special formatting
*/
QString identifierHighlight(const QString& identifier, const DeclarationPointer& decl) const;
static QString stringFromAccess(Declaration::AccessPolicy access);
static QString stringFromAccess(DeclarationPointer decl);
QString declarationName( DeclarationPointer decl ) const;
static QStringList declarationDetails(DeclarationPointer decl);
///This can be used for example to resolve typedefs within the type.
///All types that are visualized in the navigation-context are/should be mangled through this.
///The default-implementation returns the original type.
virtual AbstractType::Ptr typeToShow(AbstractType::Ptr type);
///Print the function-signature in a way that return-type and argument can be jumped to
virtual void htmlFunction();
///Navigation for additional less important links, like what function was overloaded etc.
virtual void htmlAdditionalNavigation();
virtual void htmlClass();
virtual void htmlIdentifiedType(AbstractType::Ptr type, const IdentifiedType* idType);
///Creates and registers a link for the given type that jumps to its declaration and to the template-argument declarations
virtual void eventuallyMakeTypeLinks( KDevelop::AbstractType::Ptr type );
///Creates a link that triggers a recomputation of this context with m_fullBackwardSearch set to true
void createFullBackwardSearchLink(QString string);
- bool m_fullBackwardSearch;
+private:
+ QScopedPointer d;
};
}
#endif
diff --git a/language/duchain/navigation/abstractincludenavigationcontext.cpp b/language/duchain/navigation/abstractincludenavigationcontext.cpp
index 037d72ae6..e12a270f5 100644
--- a/language/duchain/navigation/abstractincludenavigationcontext.cpp
+++ b/language/duchain/navigation/abstractincludenavigationcontext.cpp
@@ -1,174 +1,174 @@
/*
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,
TopDUContextPointer topContext,
const ParsingEnvironmentType& type)
: AbstractNavigationContext(topContext), m_type(type), m_item(item)
{}
TopDUContext* pickContextWithData(QList duchains, uint maxDepth,
const ParsingEnvironmentType& type,
bool forcePick = true) {
TopDUContext* duchain = nullptr;
foreach(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) {
foreach(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() += "" + fontSizePrefix(shorten);
- addExternalHtml(m_prefix);
+ 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:")) + "
";
bool first = true;
QList decs;
addDeclarationsFromContext(duchain, first, decs);
}
}else if(duchains.isEmpty()) {
modifyHtml() += i18n("not parsed yet");
}
- addExternalHtml(m_suffix);
+ addExternalHtml(suffix());
modifyHtml() += fontSizeSuffix(shorten) + "
";
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,
QList< IdentifierPair > &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)) + " ").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 + ' ');
++childIterator;
}
}
//modifyHtml() += "}
";
}
}
diff --git a/language/duchain/navigation/abstractnavigationcontext.cpp b/language/duchain/navigation/abstractnavigationcontext.cpp
index 7cad556d4..48558303a 100644
--- a/language/duchain/navigation/abstractnavigationcontext.cpp
+++ b/language/duchain/navigation/abstractnavigationcontext.cpp
@@ -1,506 +1,555 @@
/*
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 "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 "util/debug.h"
#include
#include
#include
namespace KDevelop {
-void AbstractNavigationContext::setTopContext(KDevelop::TopDUContextPointer context) {
- m_topContext = context;
+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;
}
-KDevelop::TopDUContextPointer AbstractNavigationContext::topContext() const {
- return m_topContext;
+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;
+}
-AbstractNavigationContext::AbstractNavigationContext( KDevelop::TopDUContextPointer topContext, AbstractNavigationContext* previousContext)
- : m_selectedLink(0), m_shorten(false), m_linkCount(-1), m_currentPositionLine(0),
- m_previousContext(previousContext), m_topContext(topContext)
+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('}', 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, DeclarationPointer declaration, NavigationAction::Type actionType )
{
NavigationAction action( declaration, actionType );
makeLink(name, QString(), action);
}
QString AbstractNavigationContext::createLink(const QString& name, QString, const NavigationAction& action)
{
- if(m_shorten) {
+ 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 = QString("link_%1").arg(m_links.count());
-
- m_links[ hrefId ] = action;
- m_intLinks[ m_linkCount ] = action;
- m_linkLines[ m_linkCount ] = m_currentLine;
- if(m_currentPositionLine == m_currentLine) {
- m_currentPositionLine = -1;
- m_selectedLink = m_linkCount;
+ QString hrefId = QString("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( m_linkCount == m_selectedLink )
+ if( d->m_linkCount == d->m_selectedLink )
str = "" + str + "";
- QString ret = "" + str + "";
+ QString ret = "m_linkCount == d->m_selectedLink && d->m_currentPositionLine == -1) ? QStringLiteral(" name = \"currentPosition\"") : QString()) + ">" + str + "";
- if( m_selectedLink == m_linkCount )
- m_selectedLinkAction = action;
+ if( d->m_selectedLink == d->m_linkCount )
+ d->m_selectedLinkAction = action;
- ++m_linkCount;
+ ++d->m_linkCount;
return ret;
}
void AbstractNavigationContext::makeLink( const QString& name, QString targetId, const NavigationAction& action)
{
modifyHtml() += createLink(name, targetId, action);
}
void AbstractNavigationContext::clear() {
- m_linkCount = 0;
- m_currentLine = 0;
- m_currentText.clear();
- m_links.clear();
- m_intLinks.clear();
- m_linkLines.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();
}
-NavigationContextPointer AbstractNavigationContext::executeLink (QString link)
+void AbstractNavigationContext::executeLink(const QString& link)
{
- if(!m_links.contains(link))
- return NavigationContextPointer(this);
+ if(!d->m_links.contains(link))
+ return;
- return execute(m_links[link]);
+ execute(d->m_links[link]);
}
-NavigationContextPointer AbstractNavigationContext::executeKeyAction(QString key) {
+NavigationContextPointer AbstractNavigationContext::executeKeyAction(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);
}
qRegisterMetaType("KTextEditor::Cursor");
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:
{
- AbstractDeclarationNavigationContext* ctx = dynamic_cast(m_previousContext);
+ auto ctx = dynamic_cast(d->m_previousContext);
if( ctx && ctx->declaration() == action.decl )
- return NavigationContextPointer( m_previousContext );
- return AbstractNavigationContext::registerChild(action.decl);
+ return NavigationContextPointer(d->m_previousContext);
+ return registerChild(action.decl);
} break;
case NavigationAction::NavigateUses:
{
IContextBrowser* browser = ICore::self()->pluginController()->extensionForPlugin();
if (browser) {
browser->showUses(action.decl);
return NavigationContextPointer(this);
}
// fall-through
}
- case NavigationAction::ShowUses:
+ 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());
ICore::self()->documentationController()->showDocumentation(doc);
}
break;
}
return NavigationContextPointer( this );
}
-void AbstractNavigationContext::setPreviousContext(KDevelop::AbstractNavigationContext* previous) {
- m_previousContext = previous;
+AbstractNavigationContext* AbstractNavigationContext::previousContext() const
+{
+ return d->m_previousContext;
}
-NavigationContextPointer AbstractNavigationContext::registerChild( AbstractNavigationContext* context ) {
- m_children << NavigationContextPointer(context);
- return m_children.last();
+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(DeclarationPointer declaration) {
//We create a navigation-widget here, and steal its context.. evil ;)
QScopedPointer navigationWidget(declaration->context()->createNavigationWidget(declaration.data()));
if (AbstractNavigationWidget* abstractNavigationWidget = dynamic_cast(navigationWidget.data()) ) {
NavigationContextPointer ret = abstractNavigationWidget->context();
ret->setPreviousContext(this);
- m_children << ret;
+ d->m_children << ret;
return ret;
} else {
return NavigationContextPointer(this);
}
}
const int lineJump = 3;
void AbstractNavigationContext::down() {
//Make sure link-count is valid
- if( m_linkCount == -1 ) {
+ if( d->m_linkCount == -1 ) {
DUChainReadLocker lock;
html();
}
- int fromLine = m_currentPositionLine;
+ int fromLine = d->m_currentPositionLine;
- if(m_selectedLink >= 0 && m_selectedLink < m_linkCount) {
+ if(d->m_selectedLink >= 0 && d->m_selectedLink < d->m_linkCount) {
if(fromLine == -1)
- fromLine = m_linkLines[m_selectedLink];
+ fromLine = d->m_linkLines[d->m_selectedLink];
- for(int newSelectedLink = m_selectedLink+1; newSelectedLink < m_linkCount; ++newSelectedLink) {
- if(m_linkLines[newSelectedLink] > fromLine && m_linkLines[newSelectedLink] - fromLine <= lineJump) {
- m_selectedLink = newSelectedLink;
- m_currentPositionLine = -1;
+ 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;
- m_currentPositionLine = fromLine + lineJump;
+ d->m_currentPositionLine = fromLine + lineJump;
- if(m_currentPositionLine > m_currentLine)
- m_currentPositionLine = m_currentLine;
+ if(d->m_currentPositionLine > d->m_currentLine)
+ d->m_currentPositionLine = d->m_currentLine;
}
void AbstractNavigationContext::up() {
//Make sure link-count is valid
- if( m_linkCount == -1 ) {
+ if( d->m_linkCount == -1 ) {
DUChainReadLocker lock;
html();
}
- int fromLine = m_currentPositionLine;
+ int fromLine = d->m_currentPositionLine;
- if(m_selectedLink >= 0 && m_selectedLink < m_linkCount) {
+ if(d->m_selectedLink >= 0 && d->m_selectedLink < d->m_linkCount) {
if(fromLine == -1)
- fromLine = m_linkLines[m_selectedLink];
+ fromLine = d->m_linkLines[d->m_selectedLink];
- for(int newSelectedLink = m_selectedLink-1; newSelectedLink >= 0; --newSelectedLink) {
- if(m_linkLines[newSelectedLink] < fromLine && fromLine - m_linkLines[newSelectedLink] <= lineJump) {
- m_selectedLink = newSelectedLink;
- m_currentPositionLine = -1;
+ 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 = m_currentLine;
+ fromLine = d->m_currentLine;
- m_currentPositionLine = fromLine - lineJump;
- if(m_currentPositionLine < 0)
- m_currentPositionLine = 0;
+ d->m_currentPositionLine = fromLine - lineJump;
+ if(d->m_currentPositionLine < 0)
+ d->m_currentPositionLine = 0;
}
void AbstractNavigationContext::nextLink()
{
//Make sure link-count is valid
- if( m_linkCount == -1 ) {
+ if( d->m_linkCount == -1 ) {
DUChainReadLocker lock;
html();
}
- m_currentPositionLine = -1;
+ d->m_currentPositionLine = -1;
- if( m_linkCount > 0 )
- m_selectedLink = (m_selectedLink+1) % m_linkCount;
+ 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( m_linkCount == -1 ) {
+ if( d->m_linkCount == -1 ) {
DUChainReadLocker lock;
html();
}
- m_currentPositionLine = -1;
+ d->m_currentPositionLine = -1;
- if( m_linkCount > 0 ) {
- --m_selectedLink;
- if( m_selectedLink < 0 )
- m_selectedLink += m_linkCount;
+ if( d->m_linkCount > 0 ) {
+ --d->m_selectedLink;
+ if( d->m_selectedLink < 0 )
+ d->m_selectedLink += d->m_linkCount;
}
- Q_ASSERT(m_selectedLink >= 0);
+ Q_ASSERT(d->m_selectedLink >= 0);
}
int AbstractNavigationContext::linkCount() const
{
- return m_linkCount;
+ return d->m_linkCount;
}
-void AbstractNavigationContext::setPrefixSuffix( const QString& prefix, const QString& suffix ) {
- m_prefix = prefix;
- m_suffix = suffix;
+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(m_previousContext)
- return NavigationContextPointer(m_previousContext);
+ if(d->m_previousContext)
+ return NavigationContextPointer(d->m_previousContext);
else
return NavigationContextPointer(this);
}
NavigationContextPointer AbstractNavigationContext::accept() {
- if( m_selectedLink >= 0 && m_selectedLink < m_linkCount )
+ if( d->m_selectedLink >= 0 && d->m_selectedLink < d->m_linkCount )
{
- NavigationAction action = m_intLinks[m_selectedLink];
+ 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) {
- if( !m_links.contains(link) ) {
+ if( !d->m_links.contains(link) ) {
qCDebug(LANGUAGE) << "Executed unregistered link " << link << endl;
return NavigationContextPointer(this);
}
- return execute(m_links[link]);
+ return execute(d->m_links[link]);
}
NavigationAction AbstractNavigationContext::currentAction() const {
- return m_selectedLinkAction;
+ return d->m_selectedLinkAction;
}
QString AbstractNavigationContext::declarationKind(DeclarationPointer decl)
{
const AbstractFunctionDeclaration* 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( NamespaceAliasDeclaration* 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) {
- m_shorten = shorten;
+ d->m_shorten = shorten;
return QString();
}
bool AbstractNavigationContext::alreadyComputed() const {
- return !m_currentText.isEmpty();
+ 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, QRegExp regExp) {
QStringList ret;
int place = regExp.indexIn(str);
while(place != -1) {
ret << str.left(place + regExp.matchedLength());
str = str.mid(place + regExp.matchedLength());
place = regExp.indexIn(str);
}
ret << str;
return ret;
}
void AbstractNavigationContext::addHtml(QString html) {
QRegExp newLineRegExp("
|
");
foreach(const QString& line, splitAndKeep(html, newLineRegExp)) {
- m_currentText += line;
+ d->m_currentText += line;
if(line.indexOf(newLineRegExp) != -1) {
- ++m_currentLine;
- if(m_currentLine == m_currentPositionLine) {
- m_currentText += QStringLiteral(" <-> "); // ><-> is <->
+ ++d->m_currentLine;
+ if(d->m_currentLine == d->m_currentPositionLine) {
+ d->m_currentText += QStringLiteral(" <-> "); // ><-> is <->
}
}
}
}
QString AbstractNavigationContext::currentHtml() const {
-
- return m_currentText;
+ 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 = "" + str + "";
if( m_formatting & Fixed )
ret = ""+ret+"";
if ( m_formatting & Bold )
ret = ""+ret+"";
if ( m_formatting & Italic )
ret = ""+ret+"";
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/language/duchain/navigation/abstractnavigationcontext.h b/language/duchain/navigation/abstractnavigationcontext.h
index 30cad6f94..605dd2cfa 100644
--- a/language/duchain/navigation/abstractnavigationcontext.h
+++ b/language/duchain/navigation/abstractnavigationcontext.h
@@ -1,195 +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;
typedef QExplicitlySharedDataPointer NavigationContextPointer;
+class AbstractNavigationContextPrivate;
class KDEVPLATFORMLANGUAGE_EXPORT AbstractNavigationContext : public QObject, public QSharedData
{
Q_OBJECT
- public:
- explicit AbstractNavigationContext( KDevelop::TopDUContextPointer topContext = KDevelop::TopDUContextPointer(), AbstractNavigationContext* previousContext = nullptr );
- ~AbstractNavigationContext() override {
- }
+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;
- void setTopContext(TopDUContextPointer context);
-
TopDUContextPointer topContext() const;
+ void setTopContext(const TopDUContextPointer& context);
- NavigationContextPointer executeLink(QString link);
-
+ 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;
- virtual void setPreviousContext(AbstractNavigationContext* previous);
+ 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(QString key);
///Adds given the text to currentHtml()
void addHtml(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, 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, QString targetId, const NavigationAction& action);
///Creates a link that executes the given action and returns it
QString createLink(const QString& name, QString targetId, const NavigationAction& action);
- int m_selectedLink; //The link currently selected
- NavigationAction m_selectedLinkAction; //Target of the currently selected link
-
NavigationContextPointer registerChild(DeclarationPointer /*declaration*/);
- NavigationContextPointer registerChild( AbstractNavigationContext* context );
- QList m_children; //Useed to keep alive all children until this is deleted
-
- bool m_shorten;
-
- int m_currentLine;
-
- //A counter used while building the html-code to count the used links.
- int m_linkCount;
- //Something else than -1 if the current position is represented by a line-number, not a link.
- int m_currentPositionLine;
- QMap m_links;
- QMap m_linkLines; //Holds the line for each link
- QMap m_intLinks;
- AbstractNavigationContext* m_previousContext;
- QString m_prefix, m_suffix;
- KDevelop::TopDUContextPointer m_topContext;
+ NavigationContextPointer registerChild(AbstractNavigationContext* context);
virtual QString declarationKind(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:
- QString m_currentText; //Here the text is built
+
+private:
+ QScopedPointer d;
};
}
Q_DECLARE_OPERATORS_FOR_FLAGS(KDevelop::Colorizer::Formatting)
#endif
diff --git a/language/duchain/navigation/abstractnavigationwidget.cpp b/language/duchain/navigation/abstractnavigationwidget.cpp
index 727b3bcac..7de4fe417 100644
--- a/language/duchain/navigation/abstractnavigationwidget.cpp
+++ b/language/duchain/navigation/abstractnavigationwidget.cpp
@@ -1,303 +1,323 @@
/*
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 "abstractnavigationwidget.h"
#include
#include
#include
#include
#include
#include
#include
#include "../duchainlock.h"
#include "util/debug.h"
+namespace {
+const int maxNavigationWidgetWidth = 580;
+const int maxNavigationWidgetHeight = 400;
+}
+
namespace KDevelop {
+class AbstractNavigationWidgetPrivate
+{
+public:
+ AbstractNavigationWidgetPrivate(AbstractNavigationWidget* q) : q(q) {}
+
+ void anchorClicked(const QUrl&);
+
+ AbstractNavigationWidget* q;
+
+ NavigationContextPointer m_startContext;
+
+ QPointer m_browser;
+ QWidget* m_currentWidget = nullptr;
+ QString m_currentText;
+ mutable QSize m_idealTextSize;
+ AbstractNavigationWidget::DisplayHints m_hints = AbstractNavigationWidget::NoHints;
+
+ NavigationContextPointer m_context;
+};
+
AbstractNavigationWidget::AbstractNavigationWidget()
- : m_browser(nullptr), m_currentWidget(nullptr)
+ : d(new AbstractNavigationWidgetPrivate(this))
{
setPalette( QApplication::palette() );
setFocusPolicy(Qt::NoFocus);
resize(100, 100);
}
-const int maxNavigationWidgetWidth = 800;
-const int maxNavigationWidgetHeight = 400;
-
QSize AbstractNavigationWidget::sizeHint() const
{
- if(m_browser) {
+ if(d->m_browser) {
updateIdealSize();
- QSize ret = QSize(qMin(m_idealTextSize.width(), maxNavigationWidgetWidth),
- qMin(m_idealTextSize.height(), maxNavigationWidgetHeight));
- if(m_idealTextSize.height()>=maxNavigationWidgetHeight) {
+ QSize ret = QSize(qMin(d->m_idealTextSize.width(), maxNavigationWidgetWidth),
+ qMin(d->m_idealTextSize.height(), maxNavigationWidgetHeight));
+ if(d->m_idealTextSize.height()>=maxNavigationWidgetHeight) {
//make space for the scrollbar in case it's not fitting
- ret.rwidth() += 17; //m_browser->verticalScrollBar()->width() returns 100, for some reason
}
- if(m_currentWidget) {
- ret.setHeight( ret.height() + m_currentWidget->sizeHint().height() );
- if(m_currentWidget->sizeHint().width() > ret.width())
- ret.setWidth(m_currentWidget->sizeHint().width());
+ if(d->m_currentWidget) {
+ ret.setHeight( ret.height() + d->m_currentWidget->sizeHint().height() );
+ if(d->m_currentWidget->sizeHint().width() > ret.width())
+ ret.setWidth(d->m_currentWidget->sizeHint().width());
if(ret.width() < 500) //When we embed a widget, give it some space, even if it doesn't have a large size-hint
ret.setWidth(500);
}
return ret;
} else
return QWidget::sizeHint();
}
void AbstractNavigationWidget::initBrowser(int height) {
Q_UNUSED(height);
- m_browser = new QTextBrowser;
+ d->m_browser = new QTextBrowser;
// since we can embed arbitrary HTML we have to make sure it stays readable by forcing a black-white palette
QPalette p;
p.setColor(QPalette::AlternateBase, Qt::white);
p.setColor(QPalette::Base, Qt::white);
p.setColor(QPalette::Text, Qt::black);
- m_browser->setPalette( p );
+ d->m_browser->setPalette( p );
- m_browser->setOpenLinks(false);
- m_browser->setOpenExternalLinks(false);
+ d->m_browser->setOpenLinks(false);
+ d->m_browser->setOpenExternalLinks(false);
QVBoxLayout* layout = new QVBoxLayout;
- layout->addWidget(m_browser);
+ layout->addWidget(d->m_browser);
layout->setMargin(0);
setLayout(layout);
- connect( m_browser.data(), &QTextBrowser::anchorClicked, this, &AbstractNavigationWidget::anchorClicked );
+ connect(d->m_browser.data(), &QTextBrowser::anchorClicked, this, [&](const QUrl& url) { d->anchorClicked(url); });
foreach(QWidget* w, findChildren())
w->setContextMenuPolicy(Qt::NoContextMenu);
}
AbstractNavigationWidget::~AbstractNavigationWidget() {
- if(m_currentWidget)
- layout()->removeWidget(m_currentWidget);
-
+ if(d->m_currentWidget)
+ layout()->removeWidget(d->m_currentWidget);
}
void AbstractNavigationWidget::setContext(NavigationContextPointer context, int initBrows)
{
- if(m_browser == nullptr)
+ if(d->m_browser == nullptr)
initBrowser(initBrows);
if(!context) {
qCDebug(LANGUAGE) << "no new context created";
return;
}
- if(context == m_context && (!context || context->alreadyComputed()))
+ if(context == d->m_context && (!context || context->alreadyComputed()))
return;
- if (!m_startContext)
- m_startContext = m_context;
+ if (!d->m_startContext) {
+ d->m_startContext = context;
+ }
- bool wasInitial = (m_context == m_startContext);
+ bool wasInitial = (d->m_context == d->m_startContext);
- m_context = context;
+ d->m_context = context;
update();
- emit contextChanged(wasInitial, m_context == m_startContext);
+ emit contextChanged(wasInitial, d->m_context == d->m_startContext);
emit sizeHintChanged();
}
void AbstractNavigationWidget::updateIdealSize() const {
- if(m_context && !m_idealTextSize.isValid()) {
+ if(d->m_context && !d->m_idealTextSize.isValid()) {
QTextDocument doc;
- doc.setHtml(m_currentText);
+ doc.setHtml(d->m_currentText);
if(doc.idealWidth() > maxNavigationWidgetWidth) {
doc.setTextWidth(maxNavigationWidgetWidth);
- m_idealTextSize.setWidth(maxNavigationWidgetWidth);
+ d->m_idealTextSize.setWidth(maxNavigationWidgetWidth);
}else{
- m_idealTextSize.setWidth(doc.idealWidth());
+ d->m_idealTextSize.setWidth(doc.idealWidth());
}
- m_idealTextSize.setHeight(doc.size().height());
+ d->m_idealTextSize.setHeight(doc.size().height());
}
}
-void AbstractNavigationWidget::setDisplayHints(DisplayHints hints) {
- m_hints = hints;
+void AbstractNavigationWidget::setDisplayHints(DisplayHints hints)
+{
+ d->m_hints = hints;
}
void AbstractNavigationWidget::update() {
setUpdatesEnabled(false);
- Q_ASSERT( m_context );
+ Q_ASSERT( d->m_context );
QString html;
{
DUChainReadLocker lock;
- html = m_context->html();
+ html = d->m_context->html();
}
if(!html.isEmpty()) {
- int scrollPos = m_browser->verticalScrollBar()->value();
+ int scrollPos = d->m_browser->verticalScrollBar()->value();
- if ( !(m_hints & EmbeddableWidget)) {
+ if ( !(d->m_hints & EmbeddableWidget)) {
// TODO: Only show that the first time, or the first few times this context is shown?
html += QStringLiteral("");
- if (m_context->linkCount() > 0) {
+ if (d->m_context->linkCount() > 0) {
html += i18n("(Hold 'Alt' to show. Navigate via arrow keys, activate by pressing 'Enter')");
} else {
html += i18n("(Hold 'Alt' to show this tooltip)");
}
html += QStringLiteral("
");
}
- m_browser->setHtml( html );
- m_currentText = html;
+ d->m_currentText = html;
- m_idealTextSize = QSize();
QSize hint = sizeHint();
- if(hint.height() >= m_idealTextSize.height()) {
- m_browser->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ if(hint.height() >= d->m_idealTextSize.height()) {
+ d->m_browser->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
}else{
- m_browser->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ d->m_browser->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
}
- m_browser->verticalScrollBar()->setValue(scrollPos);
- m_browser->scrollToAnchor(QStringLiteral("currentPosition"));
- m_browser->show();
+ d->m_browser->verticalScrollBar()->setValue(scrollPos);
+ d->m_browser->scrollToAnchor(QStringLiteral("currentPosition"));
+ d->m_browser->show();
}else{
- m_browser->hide();
+ d->m_browser->hide();
}
- if(m_currentWidget) {
- layout()->removeWidget(m_currentWidget);
- m_currentWidget->setParent(nullptr);
+ if(d->m_currentWidget) {
+ layout()->removeWidget(d->m_currentWidget);
+ d->m_currentWidget->setParent(nullptr);
}
- m_currentWidget = m_context->widget();
+ d->m_currentWidget = d->m_context->widget();
- m_browser->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
- m_browser->setMaximumHeight(10000);
+ d->m_browser->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ d->m_browser->setMaximumHeight(10000);
- if(m_currentWidget) {
- if (m_currentWidget->metaObject()
+ if(d->m_currentWidget) {
+ if (d->m_currentWidget->metaObject()
->indexOfSignal(QMetaObject::normalizedSignature("navigateDeclaration(KDevelop::IndexedDeclaration)")) != -1)
{
- connect(m_currentWidget, SIGNAL(navigateDeclaration(KDevelop::IndexedDeclaration)),
+ connect(d->m_currentWidget, SIGNAL(navigateDeclaration(KDevelop::IndexedDeclaration)),
this, SLOT(navigateDeclaration(KDevelop::IndexedDeclaration)));
}
- layout()->addWidget(m_currentWidget);
- if(m_context->isWidgetMaximized()) {
+ layout()->addWidget(d->m_currentWidget);
+ if(d->m_context->isWidgetMaximized()) {
//Leave unused room to the widget
- m_browser->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
- m_browser->setMaximumHeight(m_idealTextSize.height());
+ d->m_browser->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
+ d->m_browser->setMaximumHeight(d->m_idealTextSize.height());
}
}
setUpdatesEnabled(true);
}
NavigationContextPointer AbstractNavigationWidget::context() {
- return m_context;
+ return d->m_context;
}
-void AbstractNavigationWidget::navigateDeclaration(KDevelop::IndexedDeclaration decl) {
- setContext(m_context->accept(decl));
+void AbstractNavigationWidget::navigateDeclaration(const IndexedDeclaration& decl)
+{
+ setContext(d->m_context->accept(decl));
}
-void AbstractNavigationWidget::anchorClicked(const QUrl& url) {
+void AbstractNavigationWidgetPrivate::anchorClicked(const QUrl& url)
+{
//We may get deleted while the call to acceptLink, so make sure we don't crash in that case
- QPointer thisPtr(this);
- NavigationContextPointer oldContext = m_context;
+ QPointer thisPtr(q);
NavigationContextPointer nextContext = m_context->acceptLink(url.toString());
if(thisPtr)
- setContext( nextContext );
+ q->setContext(nextContext);
}
void AbstractNavigationWidget::next() {
- Q_ASSERT( m_context );
- m_context->nextLink();
+ Q_ASSERT( d->m_context );
+ d->m_context->nextLink();
update();
}
void AbstractNavigationWidget::previous() {
- Q_ASSERT( m_context );
- m_context->previousLink();
+ Q_ASSERT( d->m_context );
+ d->m_context->previousLink();
update();
}
void AbstractNavigationWidget::accept() {
- Q_ASSERT( m_context );
+ Q_ASSERT( d->m_context );
QPointer thisPtr(this);
- NavigationContextPointer oldContext = m_context;
- NavigationContextPointer nextContext = m_context->accept();
+ NavigationContextPointer nextContext = d->m_context->accept();
if(thisPtr)
setContext( nextContext );
}
void AbstractNavigationWidget::back() {
QPointer thisPtr(this);
- NavigationContextPointer oldContext = m_context;
- NavigationContextPointer nextContext = m_context->back();
+ NavigationContextPointer nextContext = d->m_context->back();
if(thisPtr)
setContext( nextContext );
}
void AbstractNavigationWidget::up() {
- m_context->up();
+ d->m_context->up();
update();
}
void AbstractNavigationWidget::down() {
- m_context->down();
+ d->m_context->down();
update();
}
void AbstractNavigationWidget::embeddedWidgetAccept() {
accept();
}
void AbstractNavigationWidget::embeddedWidgetDown() {
down();
}
void AbstractNavigationWidget::embeddedWidgetRight() {
next();
}
void AbstractNavigationWidget::embeddedWidgetLeft() {
previous();
}
void AbstractNavigationWidget::embeddedWidgetUp() {
up();
}
void AbstractNavigationWidget::wheelEvent(QWheelEvent* event )
{
QWidget::wheelEvent(event);
event->accept();
}
}
+#include "moc_abstractnavigationwidget.cpp"
diff --git a/language/duchain/navigation/abstractnavigationwidget.h b/language/duchain/navigation/abstractnavigationwidget.h
index 3315b339c..b73ea5024 100644
--- a/language/duchain/navigation/abstractnavigationwidget.h
+++ b/language/duchain/navigation/abstractnavigationwidget.h
@@ -1,110 +1,102 @@
/*
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_ABSTRACTNAVIGATIONWIDGET_H
#define KDEVPLATFORM_ABSTRACTNAVIGATIONWIDGET_H
+#include
#include
#include
#include "../../interfaces/quickopendataprovider.h"
-#include
#include "abstractnavigationcontext.h"
class QTextBrowser;
+
namespace KDevelop {
+ class AbstractNavigationWidgetPrivate;
+
/**
* This class deleted itself when its part is deleted, so always use a QPointer when referencing it.
* The duchain must be read-locked for most operations
* */
class KDEVPLATFORMLANGUAGE_EXPORT AbstractNavigationWidget : public QWidget, public QuickOpenEmbeddedWidgetInterface
{
Q_OBJECT
public:
enum DisplayHint {
NoHints = 0x0, // < Normal display
EmbeddableWidget = 0x1, // < Omit parts which are only useful for the navigation popup
};
Q_DECLARE_FLAGS(DisplayHints, DisplayHint)
-
AbstractNavigationWidget();
~AbstractNavigationWidget() override;
void setContext(NavigationContextPointer context, int initBrowser = 400);
void setDisplayHints(DisplayHints hints);
QSize sizeHint() const override;
public slots:
///Keyboard-action "next"
void next() override;
///Keyboard-action "previous"
void previous() override;
///Keyboard-action "accept"
void accept() override;
void up() override;
void down() override;
virtual void back();
///These are temporarily for gettings these events directly from kate
///@todo Do this through a public interface post 4.2
void embeddedWidgetRight();
///Keyboard-action "previous"
void embeddedWidgetLeft();
///Keyboard-action "accept"
void embeddedWidgetAccept();
void embeddedWidgetUp();
void embeddedWidgetDown();
NavigationContextPointer context();
+ void navigateDeclaration(const KDevelop::IndexedDeclaration& decl);
+
Q_SIGNALS:
void sizeHintChanged();
/// Emitted whenever the current navigation-context has changed
/// @param wasInitial whether the previous context was the initial context
/// @param isInitial whether the current context is the initial context
void contextChanged(bool wasInitial, bool isInitial);
- public slots:
- void navigateDeclaration(KDevelop::IndexedDeclaration decl);
- private slots:
- void anchorClicked(const QUrl&);
+
protected:
void wheelEvent(QWheelEvent* ) override;
void updateIdealSize() const;
void initBrowser(int height);
void update();
- NavigationContextPointer m_startContext;
-
- TopDUContextPointer m_topContext;
-
- QPointer m_browser;
- QWidget* m_currentWidget;
- QString m_currentText;
- mutable QSize m_idealTextSize;
- DisplayHints m_hints = NoHints;
-
private:
- NavigationContextPointer m_context;
+ QScopedPointer d;
+
+ Q_PRIVATE_SLOT(d, void anchorClicked(const QUrl&))
};
}
-
#endif
diff --git a/language/duchain/navigation/problemnavigationcontext.cpp b/language/duchain/navigation/problemnavigationcontext.cpp
index 69ebe8e2e..51d926837 100644
--- a/language/duchain/navigation/problemnavigationcontext.cpp
+++ b/language/duchain/navigation/problemnavigationcontext.cpp
@@ -1,270 +1,271 @@
/*
Copyright 2009 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 "problemnavigationcontext.h"
#include "util/debug.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace KDevelop;
namespace {
QString KEY_INVOKE_ACTION(int num) { return QString("invoke_action_%1").arg(num); }
QString iconForSeverity(IProblem::Severity severity)
{
switch (severity) {
case IProblem::Hint:
return QStringLiteral("dialog-information");
case IProblem::Warning:
return QStringLiteral("dialog-warning");
case IProblem::Error:
return QStringLiteral("dialog-error");
case IProblem::NoSeverity:
return {};
}
Q_UNREACHABLE();
return {};
}
QString htmlImg(const QString& iconName, KIconLoader::Group group)
{
KIconLoader loader;
const int size = loader.currentSize(group);
return QString::fromLatin1("")
.arg(size)
.arg(loader.iconPath(iconName, group));
}
}
ProblemNavigationContext::ProblemNavigationContext(const QVector& problems, const Flags flags)
: m_problems(problems)
, m_flags(flags)
, m_widget(nullptr)
{
// Sort problems vector:
// 1) By severity
// 2) By sourceString, if severities are equals
std::sort(m_problems.begin(), m_problems.end(), [](const IProblem::Ptr a, const IProblem::Ptr b) {
if (a->severity() < b->severity())
return true;
if (a->severity() > b->severity())
return false;
if (a->sourceString() < b->sourceString())
return true;
return false;
});
}
ProblemNavigationContext::~ProblemNavigationContext()
{
delete m_widget;
}
QWidget* ProblemNavigationContext::widget() const
{
return m_widget;
}
bool ProblemNavigationContext::isWidgetMaximized() const
{
return false;
}
QString ProblemNavigationContext::name() const
{
return i18n("Problem");
}
QString ProblemNavigationContext::escapedHtml(const QString& text) const
{
static const QString htmlStart = QStringLiteral("");
static const QString htmlEnd = QStringLiteral("");
QString result = text.trimmed();
if (!result.startsWith(htmlStart))
return result.toHtmlEscaped();
result.remove(htmlStart);
result.remove(htmlEnd);
return result;
}
void ProblemNavigationContext::html(IProblem::Ptr problem)
{
auto iconPath = iconForSeverity(problem->severity());
+
modifyHtml() += QStringLiteral("");
modifyHtml() += QStringLiteral("%1 | ").arg(htmlImg(iconPath, KIconLoader::Panel));
// BEGIN: right column
modifyHtml() += QStringLiteral("");
modifyHtml() += i18n("Problem in %1", problem->sourceString());
modifyHtml() += QStringLiteral(" ");
if (m_flags & ShowLocation) {
modifyHtml() += labelHighlight(i18n("Location: "));
makeLink(QStringLiteral("%1 :%2")
.arg(problem->finalLocation().document.toUrl().fileName())
.arg(problem->finalLocation().start().line() + 1),
QString(),
NavigationAction(problem->finalLocation().document.toUrl(), problem->finalLocation().start())
);
modifyHtml() += QStringLiteral(" ");
}
QString description = escapedHtml(problem->description());
QString explanation = escapedHtml(problem->explanation());
modifyHtml() += description;
// Add only non-empty explanation which differs from the problem description.
// Skip this if we have more than one problem.
if (m_problems.size() == 1 && !explanation.isEmpty() && explanation != description)
modifyHtml() += "" + explanation + " ";
modifyHtml() += QStringLiteral(" | ");
// END: right column
modifyHtml() += QStringLiteral("
");
auto diagnostics = problem->diagnostics();
if (!diagnostics.isEmpty()) {
DUChainReadLocker lock;
for (auto diagnostic : diagnostics) {
modifyHtml() += QStringLiteral("");
modifyHtml() += labelHighlight(QStringLiteral("%1: ").arg(diagnostic->severityString()));
modifyHtml() += escapedHtml(diagnostic->description());
const DocumentRange range = diagnostic->finalLocation();
Declaration* declaration = DUChainUtils::itemUnderCursor(range.document.toUrl(), range.start()).declaration;
if (declaration) {
modifyHtml() += i18n("
See: ");
makeLink(declaration->toString(), DeclarationPointer(declaration), NavigationAction::NavigateDeclaration);
modifyHtml() += i18n(" in ");
makeLink(QStringLiteral("%1 :%2")
.arg(declaration->url().toUrl().fileName())
.arg(declaration->rangeInCurrentRevision().start().line() + 1),
DeclarationPointer(declaration), NavigationAction::NavigateDeclaration);
}
else if (range.start().isValid()) {
modifyHtml() += i18n("
See: ");
const auto url = range.document.toUrl();
makeLink(QStringLiteral("%1 :%2")
.arg(url.fileName())
.arg(range.start().line() + 1),
url.toDisplayString(QUrl::PreferLocalFile), NavigationAction(url, range.start()));
}
modifyHtml() += QStringLiteral("
");
}
}
auto assistant = problem->solutionAssistant();
if (assistant && !assistant->actions().isEmpty()) {
modifyHtml() += QString::fromLatin1("").arg("#b3d4ff");
modifyHtml() += QStringLiteral("%1 | ").arg(htmlImg(QStringLiteral("dialog-ok-apply"), KIconLoader::Panel));
const int startIndex = m_assistantsActions.size();
int currentIndex = startIndex;
foreach (auto assistantAction, assistant->actions()) {
m_assistantsActions.append(assistantAction);
if (currentIndex != startIndex)
modifyHtml() += " ";
makeLink(i18n("Solution (%1)", currentIndex + 1), KEY_INVOKE_ACTION( currentIndex ),
NavigationAction(KEY_INVOKE_ACTION( currentIndex )));
modifyHtml() += ": " + assistantAction->description().toHtmlEscaped();
++currentIndex;
}
modifyHtml() += " |
";
modifyHtml() += QStringLiteral("
");
}
}
QString ProblemNavigationContext::html(bool shorten)
{
- m_shorten = shorten;
+ AbstractNavigationContext::html(shorten);
clear();
m_assistantsActions.clear();
int problemIndex = 0;
foreach (auto problem, m_problems) {
html(problem);
if (++problemIndex != m_problems.size())
modifyHtml() += "
";
}
return currentHtml();
}
NavigationContextPointer ProblemNavigationContext::executeKeyAction(QString key)
{
if (key.startsWith(QLatin1String("invoke_action_"))) {
const int index = key.replace(QLatin1String("invoke_action_"), QString()).toInt();
executeAction(index);
}
return {};
}
void ProblemNavigationContext::executeAction(int index)
{
if (index < 0 || index >= m_assistantsActions.size())
return;
auto action = m_assistantsActions.at(index);
Q_ASSERT(action);
if (action) {
action->execute();
if ( topContext() )
DUChain::self()->updateContextForUrl(topContext()->url(), TopDUContext::ForceUpdate);
} else {
qCWarning(LANGUAGE()) << "No such action";
return;
}
}
diff --git a/language/duchain/navigation/usesnavigationcontext.cpp b/language/duchain/navigation/usesnavigationcontext.cpp
index cfe4ced7c..6acc35e32 100644
--- a/language/duchain/navigation/usesnavigationcontext.cpp
+++ b/language/duchain/navigation/usesnavigationcontext.cpp
@@ -1,62 +1,64 @@
/*
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( KDevelop::IndexedDeclaration declaration, AbstractNavigationContext* previousContext ) : AbstractNavigationContext(TopDUContextPointer(), previousContext), m_declaration(declaration) {
+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() += "" + fontSizePrefix(shorten);
- if( m_previousContext ) {
+ if(auto context = previousContext()) {
modifyHtml() += navigationHighlight(i18n("Uses of "));
- makeLink( m_previousContext->name(), m_previousContext->name(), NavigationAction(m_previousContext) );
+ 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) + "
";
return currentHtml();
}
QWidget* UsesNavigationContext::widget() const {
return m_widget;
}
diff --git a/plugins/contextbrowser/contextbrowser.cpp b/plugins/contextbrowser/contextbrowser.cpp
index f9b026b3c..cc2e2508d 100644
--- a/plugins/contextbrowser/contextbrowser.cpp
+++ b/plugins/contextbrowser/contextbrowser.cpp
@@ -1,1497 +1,1497 @@
/*
* This file is part of KDevelop
*
* Copyright 2007 David Nolden
*
* 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 "contextbrowser.h"
#include "contextbrowserview.h"
#include "browsemanager.h"
#include "debug.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
Q_LOGGING_CATEGORY(PLUGIN_CONTEXTBROWSER, "kdevplatform.plugins.contextbrowser")
using KTextEditor::Attribute;
using KTextEditor::View;
// Helper that follows the QObject::parent() chain, and returns the highest widget that has no parent.
QWidget* masterWidget(QWidget* w)
{
while(w && w->parent() && qobject_cast(w->parent()))
w = qobject_cast(w->parent());
return w;
}
namespace {
const unsigned int highlightingTimeout = 150;
const float highlightingZDepth = -5000;
const int maxHistoryLength = 30;
// Helper that determines the context to use for highlighting at a specific position
DUContext* contextForHighlightingAt(const KTextEditor::Cursor& position, TopDUContext* topContext)
{
DUContext* ctx = topContext->findContextAt(topContext->transformToLocalRevision(position));
while(ctx && ctx->parentContext()
&& (ctx->type() == DUContext::Template || ctx->type() == DUContext::Helper
|| ctx->localScopeIdentifier().isEmpty()))
{
ctx = ctx->parentContext();
}
return ctx;
}
///Duchain must be locked
DUContext* getContextAt(const QUrl& url, KTextEditor::Cursor cursor)
{
TopDUContext* topContext = DUChainUtils::standardContextForUrl(url);
if (!topContext) return nullptr;
return contextForHighlightingAt(KTextEditor::Cursor(cursor), topContext);
}
DeclarationPointer cursorDeclaration()
{
KTextEditor::View* view = ICore::self()->documentController()->activeTextDocumentView();
if (!view) {
return DeclarationPointer();
}
DUChainReadLocker lock;
Declaration *decl = DUChainUtils::declarationForDefinition(DUChainUtils::itemUnderCursor(view->document()->url(), KTextEditor::Cursor(view->cursorPosition())).declaration);
return DeclarationPointer(decl);
}
}
class ContextBrowserViewFactory: public KDevelop::IToolViewFactory
{
public:
explicit ContextBrowserViewFactory(ContextBrowserPlugin *plugin): m_plugin(plugin) {}
QWidget* create(QWidget *parent = nullptr) override
{
ContextBrowserView* ret = new ContextBrowserView(m_plugin, parent);
return ret;
}
Qt::DockWidgetArea defaultPosition() override
{
return Qt::BottomDockWidgetArea;
}
QString id() const override
{
return QStringLiteral("org.kdevelop.ContextBrowser");
}
private:
ContextBrowserPlugin *m_plugin;
};
KXMLGUIClient* ContextBrowserPlugin::createGUIForMainWindow( Sublime::MainWindow* window )
{
m_browseManager = new BrowseManager(this);
KXMLGUIClient* ret = KDevelop::IPlugin::createGUIForMainWindow(window);
connect(ICore::self()->documentController(), &IDocumentController::documentJumpPerformed, this, &ContextBrowserPlugin::documentJumpPerformed);
m_previousButton = new QToolButton();
m_previousButton->setToolTip(i18n("Go back in context history"));
m_previousButton->setAutoRaise(true);
m_previousButton->setPopupMode(QToolButton::MenuButtonPopup);
m_previousButton->setIcon(QIcon::fromTheme(QStringLiteral("go-previous")));
m_previousButton->setEnabled(false);
m_previousButton->setFocusPolicy(Qt::NoFocus);
m_previousMenu = new QMenu();
m_previousButton->setMenu(m_previousMenu);
connect(m_previousButton.data(), &QToolButton::clicked, this, &ContextBrowserPlugin::historyPrevious);
connect(m_previousMenu.data(), &QMenu::aboutToShow, this, &ContextBrowserPlugin::previousMenuAboutToShow);
m_nextButton = new QToolButton();
m_nextButton->setToolTip(i18n("Go forward in context history"));
m_nextButton->setAutoRaise(true);
m_nextButton->setPopupMode(QToolButton::MenuButtonPopup);
m_nextButton->setIcon(QIcon::fromTheme(QStringLiteral("go-next")));
m_nextButton->setEnabled(false);
m_nextButton->setFocusPolicy(Qt::NoFocus);
m_nextMenu = new QMenu();
m_nextButton->setMenu(m_nextMenu);
connect(m_nextButton.data(), &QToolButton::clicked, this, &ContextBrowserPlugin::historyNext);
connect(m_nextMenu.data(), &QMenu::aboutToShow, this, &ContextBrowserPlugin::nextMenuAboutToShow);
IQuickOpen* quickOpen = KDevelop::ICore::self()->pluginController()->extensionForPlugin(QStringLiteral("org.kdevelop.IQuickOpen"));
if(quickOpen) {
m_outlineLine = quickOpen->createQuickOpenLine(QStringList(), QStringList() << i18n("Outline"), IQuickOpen::Outline);
m_outlineLine->setDefaultText(i18n("Outline..."));
m_outlineLine->setToolTip(i18n("Navigate outline of active document, click to browse."));
}
connect(m_browseManager, &BrowseManager::startDelayedBrowsing,
this, &ContextBrowserPlugin::startDelayedBrowsing);
connect(m_browseManager, &BrowseManager::stopDelayedBrowsing,
this, &ContextBrowserPlugin::stopDelayedBrowsing);
connect(m_browseManager, &BrowseManager::invokeAction,
this, &ContextBrowserPlugin::invokeAction);
m_toolbarWidget = toolbarWidgetForMainWindow(window);
m_toolbarWidgetLayout = new QHBoxLayout;
m_toolbarWidgetLayout->setSizeConstraint(QLayout::SetMaximumSize);
m_previousButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
m_nextButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
m_toolbarWidgetLayout->setMargin(0);
m_toolbarWidgetLayout->addWidget(m_previousButton);
if (m_outlineLine) {
m_toolbarWidgetLayout->addWidget(m_outlineLine);
m_outlineLine->setMaximumWidth(600);
connect(ICore::self()->documentController(), &IDocumentController::documentClosed, m_outlineLine.data(), &IQuickOpenLine::clear);
}
m_toolbarWidgetLayout->addWidget(m_nextButton);
if(m_toolbarWidget->children().isEmpty())
m_toolbarWidget->setLayout(m_toolbarWidgetLayout);
connect(ICore::self()->documentController(), &IDocumentController::documentActivated,
this, &ContextBrowserPlugin::documentActivated);
return ret;
}
void ContextBrowserPlugin::createActionsForMainWindow(Sublime::MainWindow* window, QString& xmlFile,
KActionCollection& actions)
{
xmlFile = QStringLiteral("kdevcontextbrowser.rc");
QAction* sourceBrowseMode = actions.addAction(QStringLiteral("source_browse_mode"));
sourceBrowseMode->setText( i18n("Source &Browse Mode") );
sourceBrowseMode->setIcon( QIcon::fromTheme(QStringLiteral("arrow-up")) );
sourceBrowseMode->setCheckable(true);
connect(sourceBrowseMode, &QAction::triggered, m_browseManager, &BrowseManager::setBrowsing);
QAction* previousContext = actions.addAction(QStringLiteral("previous_context"));
previousContext->setText( i18n("&Previous Visited Context") );
previousContext->setIcon( QIcon::fromTheme(QStringLiteral("go-previous-context") ) );
actions.setDefaultShortcut( previousContext, Qt::META | Qt::Key_Left );
QObject::connect(previousContext, &QAction::triggered, this, &ContextBrowserPlugin::previousContextShortcut);
QAction* nextContext = actions.addAction(QStringLiteral("next_context"));
nextContext->setText( i18n("&Next Visited Context") );
nextContext->setIcon( QIcon::fromTheme(QStringLiteral("go-next-context") ) );
actions.setDefaultShortcut( nextContext, Qt::META | Qt::Key_Right );
QObject::connect(nextContext, &QAction::triggered, this, &ContextBrowserPlugin::nextContextShortcut);
QAction* previousUse = actions.addAction(QStringLiteral("previous_use"));
previousUse->setText( i18n("&Previous Use") );
previousUse->setIcon( QIcon::fromTheme(QStringLiteral("go-previous-use")) );
actions.setDefaultShortcut( previousUse, Qt::META | Qt::SHIFT | Qt::Key_Left );
QObject::connect(previousUse, &QAction::triggered, this, &ContextBrowserPlugin::previousUseShortcut);
QAction* nextUse = actions.addAction(QStringLiteral("next_use"));
nextUse->setText( i18n("&Next Use") );
nextUse->setIcon( QIcon::fromTheme(QStringLiteral("go-next-use")) );
actions.setDefaultShortcut( nextUse, Qt::META | Qt::SHIFT | Qt::Key_Right );
QObject::connect(nextUse, &QAction::triggered, this, &ContextBrowserPlugin::nextUseShortcut);
QWidgetAction* outline = new QWidgetAction(this);
outline->setText(i18n("Context Browser"));
QWidget* w = toolbarWidgetForMainWindow(window);
w->setHidden(false);
outline->setDefaultWidget(w);
actions.addAction(QStringLiteral("outline_line"), outline);
// Add to the actioncollection so one can set global shortcuts for the action
actions.addAction(QStringLiteral("find_uses"), m_findUses);
}
void ContextBrowserPlugin::nextContextShortcut()
{
// TODO: cleanup
historyNext();
}
void ContextBrowserPlugin::previousContextShortcut()
{
// TODO: cleanup
historyPrevious();
}
K_PLUGIN_FACTORY_WITH_JSON(ContextBrowserFactory, "kdevcontextbrowser.json", registerPlugin();)
ContextBrowserPlugin::ContextBrowserPlugin(QObject *parent, const QVariantList&)
: KDevelop::IPlugin(QStringLiteral("kdevcontextbrowser"), parent)
, m_viewFactory(new ContextBrowserViewFactory(this))
, m_nextHistoryIndex(0)
, m_textHintProvider(this)
{
core()->uiController()->addToolView(i18n("Code Browser"), m_viewFactory);
connect( core()->documentController(), &IDocumentController::textDocumentCreated, this, &ContextBrowserPlugin::textDocumentCreated );
connect( DUChain::self(), &DUChain::updateReady, this, &ContextBrowserPlugin::updateReady);
connect( ColorCache::self(), &ColorCache::colorsGotChanged, this, &ContextBrowserPlugin::colorSetupChanged );
connect( DUChain::self(), &DUChain::declarationSelected,
this, &ContextBrowserPlugin::declarationSelectedInUI );
m_updateTimer = new QTimer(this);
m_updateTimer->setSingleShot(true);
connect( m_updateTimer, &QTimer::timeout, this, &ContextBrowserPlugin::updateViews );
//Needed global action for the context-menu extensions
m_findUses = new QAction(i18n("Find Uses"), this);
connect(m_findUses, &QAction::triggered, this, &ContextBrowserPlugin::findUses);
}
ContextBrowserPlugin::~ContextBrowserPlugin()
{
///TODO: QObject inheritance should suffice?
delete m_nextMenu;
delete m_previousMenu;
delete m_toolbarWidgetLayout;
delete m_previousButton;
delete m_outlineLine;
delete m_nextButton;
}
void ContextBrowserPlugin::unload()
{
core()->uiController()->removeToolView(m_viewFactory);
}
KDevelop::ContextMenuExtension ContextBrowserPlugin::contextMenuExtension(KDevelop::Context* context)
{
KDevelop::ContextMenuExtension menuExt = KDevelop::IPlugin::contextMenuExtension( context );
KDevelop::DeclarationContext *codeContext = dynamic_cast(context);
if (!codeContext)
return menuExt;
DUChainReadLocker lock(DUChain::lock());
if(!codeContext->declaration().data())
return menuExt;
qRegisterMetaType("KDevelop::IndexedDeclaration");
menuExt.addAction(KDevelop::ContextMenuExtension::NavigationGroup, m_findUses);
return menuExt;
}
void ContextBrowserPlugin::showUses(const DeclarationPointer& declaration)
{
QMetaObject::invokeMethod(this, "showUsesDelayed", Qt::QueuedConnection,
Q_ARG(KDevelop::DeclarationPointer, declaration));
}
void ContextBrowserPlugin::showUsesDelayed(const DeclarationPointer& declaration)
{
DUChainReadLocker lock;
Declaration* decl = declaration.data();
if(!decl) {
return;
}
QWidget* toolView = ICore::self()->uiController()->findToolView(i18n("Code Browser"), m_viewFactory, KDevelop::IUiController::CreateAndRaise);
if(!toolView) {
return;
}
ContextBrowserView* view = dynamic_cast(toolView);
Q_ASSERT(view);
view->allowLockedUpdate();
view->setDeclaration(decl, decl->topContext(), true);
//We may get deleted while the call to acceptLink, so make sure we don't crash in that case
QPointer widget = dynamic_cast(view->navigationWidget());
if(widget && widget->context()) {
- NavigationContextPointer nextContext = widget->context()->execute(
+ auto nextContext = widget->context()->execute(
NavigationAction(declaration, KDevelop::NavigationAction::ShowUses));
if(widget) {
widget->setContext( nextContext );
}
}
}
void ContextBrowserPlugin::findUses()
{
showUses(cursorDeclaration());
}
ContextBrowserHintProvider::ContextBrowserHintProvider(ContextBrowserPlugin* plugin)
: m_plugin(plugin)
{
}
QString ContextBrowserHintProvider::textHint(View* view, const KTextEditor::Cursor& cursor)
{
m_plugin->m_mouseHoverCursor = KTextEditor::Cursor(cursor);
if(!view) {
qCWarning(PLUGIN_CONTEXTBROWSER) << "could not cast to view";
}else{
m_plugin->m_mouseHoverDocument = view->document()->url();
m_plugin->m_updateViews << view;
}
m_plugin->m_updateTimer->start(1); // triggers updateViews()
m_plugin->showToolTip(view, cursor);
return QString();
}
void ContextBrowserPlugin::stopDelayedBrowsing() {
hideToolTip();
}
void ContextBrowserPlugin::invokeAction(int index)
{
if (!m_currentNavigationWidget)
return;
auto navigationWidget = qobject_cast(m_currentNavigationWidget);
if (!navigationWidget)
return;
// TODO: Add API in AbstractNavigation{Widget,Context}?
QMetaObject::invokeMethod(navigationWidget->context().data(), "executeAction", Q_ARG(int, index));
}
void ContextBrowserPlugin::startDelayedBrowsing(KTextEditor::View* view) {
if(!m_currentToolTip) {
showToolTip(view, view->cursorPosition());
}
}
void ContextBrowserPlugin::hideToolTip() {
if(m_currentToolTip) {
m_currentToolTip->deleteLater();
m_currentToolTip = nullptr;
m_currentNavigationWidget = nullptr;
m_currentToolTipProblems.clear();
m_currentToolTipDeclaration = {};
}
}
static QVector findProblemsUnderCursor(TopDUContext* topContext, KTextEditor::Cursor position)
{
QVector problems;
auto modelsData = ICore::self()->languageController()->problemModelSet()->models();
foreach (auto modelData, modelsData) {
foreach (auto problem, modelData.model->problems(topContext->url())) {
DocumentRange problemRange = problem->finalLocation();
if (problemRange.contains(position) || (problemRange.isEmpty() && problemRange.boundaryAtCursor(position)))
problems += problem;
}
}
return problems;
}
static QVector findProblemsCloseToCursor(TopDUContext* topContext, KTextEditor::Cursor position, KTextEditor::View* view)
{
QVector allProblems;
auto modelsData = ICore::self()->languageController()->problemModelSet()->models();
foreach (auto modelData, modelsData) {
foreach (auto problem, modelData.model->problems(topContext->url())) {
allProblems += problem;
}
}
if (allProblems.isEmpty())
return allProblems;
std::sort(allProblems.begin(), allProblems.end(),
[position](const KDevelop::IProblem::Ptr a, const KDevelop::IProblem::Ptr b) {
const auto aRange = a->finalLocation();
const auto bRange = b->finalLocation();
const auto aLineDistance = qMin(qAbs(aRange.start().line() - position.line()),
qAbs(aRange.end().line() - position.line()));
const auto bLineDistance = qMin(qAbs(bRange.start().line() - position.line()),
qAbs(bRange.end().line() - position.line()));
if (aLineDistance != bLineDistance) {
return aLineDistance < bLineDistance;
}
if (aRange.start().line() == bRange.start().line()) {
return qAbs(aRange.start().column() - position.column()) <
qAbs(bRange.start().column() - position.column());
}
return qAbs(aRange.end().column() - position.column()) <
qAbs(bRange.end().column() - position.column());
});
QVector closestProblems;
// Show problems, located on the same line
foreach (auto problem, allProblems) {
auto r = problem->finalLocation();
if (r.onSingleLine() && r.start().line() == position.line())
closestProblems += problem;
else
break;
}
// If not, only show it in case there's only whitespace
// between the current cursor position and the problem line
if (closestProblems.isEmpty()) {
foreach (auto problem, allProblems) {
auto r = problem->finalLocation();
KTextEditor::Range dist;
KTextEditor::Cursor bound(r.start().line(), 0);
if (position < r.start())
dist = KTextEditor::Range(position, bound);
else {
bound.setLine(r.end().line() + 1);
dist = KTextEditor::Range(bound, position);
}
if (view->document()->text(dist).trimmed().isEmpty())
closestProblems += problem;
else
break;
}
}
return closestProblems;
}
QWidget* ContextBrowserPlugin::navigationWidgetForPosition(KTextEditor::View* view, KTextEditor::Cursor position)
{
QUrl viewUrl = view->document()->url();
auto languages = ICore::self()->languageController()->languagesForUrl(viewUrl);
DUChainReadLocker lock(DUChain::lock());
foreach (const auto language, languages) {
auto widget = language->specialLanguageObjectNavigationWidget(viewUrl, KTextEditor::Cursor(position));
auto navigationWidget = qobject_cast(widget);
if(navigationWidget)
return navigationWidget;
}
TopDUContext* topContext = DUChainUtils::standardContextForUrl(view->document()->url());
if (topContext) {
// first pass: find problems under the cursor
const auto problems = findProblemsUnderCursor(topContext, position);
if (!problems.isEmpty()) {
if (problems == m_currentToolTipProblems && m_currentToolTip) {
return nullptr;
}
m_currentToolTipProblems = problems;
auto widget = new AbstractNavigationWidget;
auto context = new ProblemNavigationContext(problems);
context->setTopContext(TopDUContextPointer(topContext));
widget->setContext(NavigationContextPointer(context));
return widget;
}
}
auto declUnderCursor = DUChainUtils::itemUnderCursor(viewUrl, position).declaration;
Declaration* decl = DUChainUtils::declarationForDefinition(declUnderCursor);
if (decl && decl->kind() == Declaration::Alias) {
AliasDeclaration* alias = dynamic_cast(decl);
Q_ASSERT(alias);
DUChainReadLocker lock;
decl = alias->aliasedDeclaration().declaration();
}
if(decl) {
if(m_currentToolTipDeclaration == IndexedDeclaration(decl) && m_currentToolTip)
return nullptr;
m_currentToolTipDeclaration = IndexedDeclaration(decl);
return decl->context()->createNavigationWidget(decl, DUChainUtils::standardContextForUrl(viewUrl));
}
if (topContext) {
// second pass: find closest problem to the cursor
const auto problems = findProblemsCloseToCursor(topContext, position, view);
if (!problems.isEmpty()) {
if (problems == m_currentToolTipProblems && m_currentToolTip) {
return nullptr;
}
m_currentToolTipProblems = problems;
auto widget = new AbstractNavigationWidget;
// since the problem is not under cursor: show location
widget->setContext(NavigationContextPointer(new ProblemNavigationContext(problems, ProblemNavigationContext::ShowLocation)));
return widget;
}
}
return nullptr;
}
void ContextBrowserPlugin::showToolTip(KTextEditor::View* view, KTextEditor::Cursor position) {
ContextBrowserView* contextView = browserViewForWidget(view);
if(contextView && contextView->isVisible() && !contextView->isLocked())
return; // If the context-browser view is visible, it will care about updating by itself
auto navigationWidget = navigationWidgetForPosition(view, position);
if(navigationWidget) {
// If we have an invisible context-view, assign the tooltip navigation-widget to it.
// If the user makes the context-view visible, it will instantly contain the correct widget.
if(contextView && !contextView->isLocked())
contextView->setNavigationWidget(navigationWidget);
if(m_currentToolTip) {
m_currentToolTip->deleteLater();
m_currentToolTip = nullptr;
m_currentNavigationWidget = nullptr;
}
KDevelop::NavigationToolTip* tooltip = new KDevelop::NavigationToolTip(view, view->mapToGlobal(view->cursorToCoordinate(position)) + QPoint(20, 40), navigationWidget);
KTextEditor::Range itemRange;
{
DUChainReadLocker lock;
auto viewUrl = view->document()->url();
itemRange = DUChainUtils::itemUnderCursor(viewUrl, position).range;
}
tooltip->setHandleRect(KTextEditorHelpers::getItemBoundingRect(view, itemRange));
tooltip->resize( navigationWidget->sizeHint() + QSize(10, 10) );
QObject::connect( view, &KTextEditor::View::verticalScrollPositionChanged,
this, &ContextBrowserPlugin::hideToolTip );
QObject::connect( view, &KTextEditor::View::horizontalScrollPositionChanged,
this, &ContextBrowserPlugin::hideToolTip );
qCDebug(PLUGIN_CONTEXTBROWSER) << "tooltip size" << tooltip->size();
m_currentToolTip = tooltip;
m_currentNavigationWidget = navigationWidget;
ActiveToolTip::showToolTip(tooltip);
if ( ! navigationWidget->property("DoNotCloseOnCursorMove").toBool() ) {
connect(view, &View::cursorPositionChanged,
this, &ContextBrowserPlugin::hideToolTip, Qt::UniqueConnection);
} else {
disconnect(view, &View::cursorPositionChanged,
this, &ContextBrowserPlugin::hideToolTip);
}
}else{
qCDebug(PLUGIN_CONTEXTBROWSER) << "not showing tooltip, no navigation-widget";
}
}
void ContextBrowserPlugin::clearMouseHover() {
m_mouseHoverCursor = KTextEditor::Cursor::invalid();
m_mouseHoverDocument.clear();
}
Attribute::Ptr ContextBrowserPlugin::highlightedUseAttribute(KTextEditor::View* view) const {
if( !m_highlightAttribute ) {
m_highlightAttribute = Attribute::Ptr( new Attribute() );
m_highlightAttribute->setDefaultStyle(KTextEditor::dsNormal);
m_highlightAttribute->setForeground(m_highlightAttribute->selectedForeground());
m_highlightAttribute->setBackgroundFillWhitespace(true);
auto iface = qobject_cast(view);
auto background = iface->configValue(QStringLiteral("search-highlight-color")).value();
m_highlightAttribute->setBackground(background);
}
return m_highlightAttribute;
}
void ContextBrowserPlugin::colorSetupChanged() {
m_highlightAttribute = Attribute::Ptr();
}
Attribute::Ptr ContextBrowserPlugin::highlightedSpecialObjectAttribute(KTextEditor::View* view) const {
return highlightedUseAttribute(view);
}
void ContextBrowserPlugin::addHighlight( View* view, KDevelop::Declaration* decl ) {
if( !view || !decl ) {
qCDebug(PLUGIN_CONTEXTBROWSER) << "invalid view/declaration";
return;
}
ViewHighlights& highlights(m_highlightedRanges[view]);
KDevelop::DUChainReadLocker lock;
// Highlight the declaration
highlights.highlights << decl->createRangeMoving();
highlights.highlights.back()->setAttribute(highlightedUseAttribute(view));
highlights.highlights.back()->setZDepth(highlightingZDepth);
// Highlight uses
{
QMap< IndexedString, QList< KTextEditor::Range > > currentRevisionUses = decl->usesCurrentRevision();
for(QMap< IndexedString, QList< KTextEditor::Range > >::iterator fileIt = currentRevisionUses.begin(); fileIt != currentRevisionUses.end(); ++fileIt)
{
for(QList< KTextEditor::Range >::const_iterator useIt = (*fileIt).constBegin(); useIt != (*fileIt).constEnd(); ++useIt)
{
highlights.highlights << PersistentMovingRange::Ptr(new PersistentMovingRange(*useIt, fileIt.key()));
highlights.highlights.back()->setAttribute(highlightedUseAttribute(view));
highlights.highlights.back()->setZDepth(highlightingZDepth);
}
}
}
if( FunctionDefinition* def = FunctionDefinition::definition(decl) )
{
highlights.highlights << def->createRangeMoving();
highlights.highlights.back()->setAttribute(highlightedUseAttribute(view));
highlights.highlights.back()->setZDepth(highlightingZDepth);
}
}
Declaration* ContextBrowserPlugin::findDeclaration(View* view, const KTextEditor::Cursor& position, bool mouseHighlight)
{
Q_UNUSED(mouseHighlight);
Declaration* foundDeclaration = nullptr;
if(m_useDeclaration.data()) {
foundDeclaration = m_useDeclaration.data();
}else{
//If we haven't found a special language object, search for a use/declaration and eventually highlight it
foundDeclaration = DUChainUtils::declarationForDefinition( DUChainUtils::itemUnderCursor(view->document()->url(), position).declaration );
if (foundDeclaration && foundDeclaration->kind() == Declaration::Alias) {
AliasDeclaration* alias = dynamic_cast(foundDeclaration);
Q_ASSERT(alias);
DUChainReadLocker lock;
foundDeclaration = alias->aliasedDeclaration().declaration();
}
}
return foundDeclaration;
}
ContextBrowserView* ContextBrowserPlugin::browserViewForWidget(QWidget* widget)
{
foreach(ContextBrowserView* contextView, m_views) {
if(masterWidget(contextView) == masterWidget(widget)) {
return contextView;
}
}
return nullptr;
}
void ContextBrowserPlugin::updateForView(View* view)
{
bool allowHighlight = true;
if(view->selection())
{
// If something is selected, we unhighlight everything, so that we don't conflict with the
// kate plugin that highlights occurrences of the selected string, and also to reduce the
// overall amount of concurrent highlighting.
allowHighlight = false;
}
if(m_highlightedRanges[view].keep)
{
m_highlightedRanges[view].keep = false;
return;
}
// Clear all highlighting
m_highlightedRanges.clear();
// Re-highlight
ViewHighlights& highlights = m_highlightedRanges[view];
QUrl url = view->document()->url();
IDocument* activeDoc = core()->documentController()->activeDocument();
bool mouseHighlight = (url == m_mouseHoverDocument) && (m_mouseHoverCursor.isValid());
bool shouldUpdateBrowser = (mouseHighlight || (view == ICore::self()->documentController()->activeTextDocumentView() && activeDoc && activeDoc->textDocument() == view->document()));
KTextEditor::Cursor highlightPosition;
if (mouseHighlight)
highlightPosition = m_mouseHoverCursor;
else
highlightPosition = KTextEditor::Cursor(view->cursorPosition());
///Pick a language
ILanguageSupport* language = nullptr;
if(ICore::self()->languageController()->languagesForUrl(url).isEmpty()) {
qCDebug(PLUGIN_CONTEXTBROWSER) << "found no language for document" << url;
return;
}else{
language = ICore::self()->languageController()->languagesForUrl(url).front();
}
///Check whether there is a special language object to highlight (for example a macro)
KTextEditor::Range specialRange = language->specialLanguageObjectRange(url, highlightPosition);
ContextBrowserView* updateBrowserView = shouldUpdateBrowser ? browserViewForWidget(view) : nullptr;
if(specialRange.isValid())
{
// Highlight a special language object
if(allowHighlight)
{
highlights.highlights << PersistentMovingRange::Ptr(new PersistentMovingRange(specialRange, IndexedString(url)));
highlights.highlights.back()->setAttribute(highlightedSpecialObjectAttribute(view));
highlights.highlights.back()->setZDepth(highlightingZDepth);
}
if(updateBrowserView)
updateBrowserView->setSpecialNavigationWidget(language->specialLanguageObjectNavigationWidget(url, highlightPosition));
}else{
KDevelop::DUChainReadLocker lock( DUChain::lock(), 100 );
if(!lock.locked()) {
qCDebug(PLUGIN_CONTEXTBROWSER) << "Failed to lock du-chain in time";
return;
}
TopDUContext* topContext = DUChainUtils::standardContextForUrl(view->document()->url());
if (!topContext)
return;
DUContext* ctx = contextForHighlightingAt(highlightPosition, topContext);
if (!ctx)
return;
//Only update the history if this context is around the text cursor
if(core()->documentController()->activeDocument() && highlightPosition == KTextEditor::Cursor(view->cursorPosition()) && view->document() == core()->documentController()->activeDocument()->textDocument())
{
updateHistory(ctx, highlightPosition);
}
Declaration* foundDeclaration = findDeclaration(view, highlightPosition, mouseHighlight);
if( foundDeclaration ) {
m_lastHighlightedDeclaration = highlights.declaration = IndexedDeclaration(foundDeclaration);
if(allowHighlight)
addHighlight( view, foundDeclaration );
if(updateBrowserView)
updateBrowserView->setDeclaration(foundDeclaration, topContext);
}else{
if(updateBrowserView)
updateBrowserView->setContext(ctx);
}
}
}
void ContextBrowserPlugin::updateViews()
{
foreach( View* view, m_updateViews ) {
updateForView(view);
}
m_updateViews.clear();
m_useDeclaration = IndexedDeclaration();
}
void ContextBrowserPlugin::declarationSelectedInUI(const DeclarationPointer& decl)
{
m_useDeclaration = IndexedDeclaration(decl.data());
KTextEditor::View* view = core()->documentController()->activeTextDocumentView();
if(view)
m_updateViews << view;
if(!m_updateViews.isEmpty())
m_updateTimer->start(highlightingTimeout); // triggers updateViews()
}
void ContextBrowserPlugin::updateReady(const IndexedString& file, const ReferencedTopDUContext& /*topContext*/)
{
const auto url = file.toUrl();
for(QMap< View*, ViewHighlights >::iterator it = m_highlightedRanges.begin(); it != m_highlightedRanges.end(); ++it) {
if(it.key()->document()->url() == url) {
if(!m_updateViews.contains(it.key())) {
qCDebug(PLUGIN_CONTEXTBROWSER) << "adding view for update";
m_updateViews << it.key();
// Don't change the highlighted declaration after finished parse-jobs
(*it).keep = true;
}
}
}
if(!m_updateViews.isEmpty())
m_updateTimer->start(highlightingTimeout);
}
void ContextBrowserPlugin::textDocumentCreated( KDevelop::IDocument* document )
{
Q_ASSERT(document->textDocument());
connect( document->textDocument(), &KTextEditor::Document::viewCreated, this, &ContextBrowserPlugin::viewCreated );
foreach( View* view, document->textDocument()->views() )
viewCreated( document->textDocument(), view );
}
void ContextBrowserPlugin::documentActivated( IDocument* doc )
{
if (m_outlineLine)
m_outlineLine->clear();
if (View* view = doc->activeTextView())
{
cursorPositionChanged(view, view->cursorPosition());
}
}
void ContextBrowserPlugin::viewDestroyed( QObject* obj )
{
m_highlightedRanges.remove(static_cast(obj));
m_updateViews.remove(static_cast(obj));
}
void ContextBrowserPlugin::selectionChanged( View* view )
{
clearMouseHover();
m_updateViews.insert(view);
m_updateTimer->start(highlightingTimeout/2); // triggers updateViews()
}
void ContextBrowserPlugin::cursorPositionChanged( View* view, const KTextEditor::Cursor& newPosition )
{
if(view->document() == m_lastInsertionDocument && newPosition == m_lastInsertionPos)
{
//Do not update the highlighting while typing
m_lastInsertionDocument = nullptr;
m_lastInsertionPos = KTextEditor::Cursor();
if(m_highlightedRanges.contains(view))
m_highlightedRanges[view].keep = true;
}else{
if(m_highlightedRanges.contains(view))
m_highlightedRanges[view].keep = false;
}
clearMouseHover();
m_updateViews.insert(view);
m_updateTimer->start(highlightingTimeout/2); // triggers updateViews()
}
void ContextBrowserPlugin::textInserted(KTextEditor::Document* doc, const KTextEditor::Cursor& cursor, const QString& text)
{
m_lastInsertionDocument = doc;
m_lastInsertionPos = cursor + KTextEditor::Cursor(0, text.size());
}
void ContextBrowserPlugin::viewCreated( KTextEditor::Document* , View* v )
{
disconnect( v, &View::cursorPositionChanged, this, &ContextBrowserPlugin::cursorPositionChanged ); ///Just to make sure that multiple connections don't happen
connect( v, &View::cursorPositionChanged, this, &ContextBrowserPlugin::cursorPositionChanged );
connect( v, &View::destroyed, this, &ContextBrowserPlugin::viewDestroyed );
disconnect( v->document(), &KTextEditor::Document::textInserted, this, &ContextBrowserPlugin::textInserted);
connect(v->document(), &KTextEditor::Document::textInserted, this, &ContextBrowserPlugin::textInserted);
disconnect(v, &View::selectionChanged, this, &ContextBrowserPlugin::selectionChanged);
KTextEditor::TextHintInterface *iface = dynamic_cast(v);
if( !iface )
return;
iface->setTextHintDelay(highlightingTimeout);
iface->registerTextHintProvider(&m_textHintProvider);
}
void ContextBrowserPlugin::registerToolView(ContextBrowserView* view)
{
m_views << view;
}
void ContextBrowserPlugin::previousUseShortcut()
{
switchUse(false);
}
void ContextBrowserPlugin::nextUseShortcut()
{
switchUse(true);
}
KTextEditor::Range cursorToRange(KTextEditor::Cursor cursor) {
return KTextEditor::Range(cursor, cursor);
}
void ContextBrowserPlugin::switchUse(bool forward)
{
View* view = core()->documentController()->activeTextDocumentView();
if(view) {
KTextEditor::Document* doc = view->document();
KDevelop::DUChainReadLocker lock( DUChain::lock() );
KDevelop::TopDUContext* chosen = DUChainUtils::standardContextForUrl(doc->url());
if( chosen )
{
KTextEditor::Cursor cCurrent(view->cursorPosition());
KDevelop::CursorInRevision c = chosen->transformToLocalRevision(cCurrent);
Declaration* decl = nullptr;
//If we have a locked declaration, use that for jumping
foreach(ContextBrowserView* view, m_views) {
decl = view->lockedDeclaration().data(); ///@todo Somehow match the correct context-browser view if there is multiple
if(decl)
break;
}
if(!decl) //Try finding a declaration under the cursor
decl = DUChainUtils::itemUnderCursor(doc->url(), cCurrent).declaration;
if (decl && decl->kind() == Declaration::Alias) {
AliasDeclaration* alias = dynamic_cast(decl);
Q_ASSERT(alias);
DUChainReadLocker lock;
decl = alias->aliasedDeclaration().declaration();
}
if(decl) {
Declaration* target = nullptr;
if(forward)
//Try jumping from definition to declaration
target = DUChainUtils::declarationForDefinition(decl, chosen);
else if(decl->url().toUrl() == doc->url() && decl->range().contains(c))
//Try jumping from declaration to definition
target = FunctionDefinition::definition(decl);
if(target && target != decl) {
KTextEditor::Cursor jumpTo = target->rangeInCurrentRevision().start();
QUrl document = target->url().toUrl();
lock.unlock();
core()->documentController()->openDocument( document, cursorToRange(jumpTo) );
return;
}else{
//Always work with the declaration instead of the definition
decl = DUChainUtils::declarationForDefinition(decl, chosen);
}
}
if(!decl) {
//Pick the last use we have highlighted
decl = m_lastHighlightedDeclaration.data();
}
if(decl) {
KDevVarLengthArray usingFiles = DUChain::uses()->uses(decl->id());
if(DUChainUtils::contextHasUse(decl->topContext(), decl) && usingFiles.indexOf(decl->topContext()) == -1)
usingFiles.insert(0, decl->topContext());
if(decl->range().contains(c) && decl->url() == chosen->url()) {
//The cursor is directly on the declaration. Jump to the first or last use.
if(!usingFiles.isEmpty()) {
TopDUContext* top = (forward ? usingFiles[0] : usingFiles.back()).data();
if(top) {
QList useRanges = allUses(top, decl, true);
std::sort(useRanges.begin(), useRanges.end());
if(!useRanges.isEmpty()) {
QUrl url = top->url().toUrl();
KTextEditor::Range selectUse = chosen->transformFromLocalRevision(forward ? useRanges.first() : useRanges.back());
lock.unlock();
core()->documentController()->openDocument(url, cursorToRange(selectUse.start()));
}
}
}
return;
}
//Check whether we are within a use
QList localUses = allUses(chosen, decl, true);
std::sort(localUses.begin(), localUses.end());
for(int a = 0; a < localUses.size(); ++a) {
int nextUse = (forward ? a+1 : a-1);
bool pick = localUses[a].contains(c);
if(!pick && forward && a+1 < localUses.size() && localUses[a].end <= c && localUses[a+1].start > c) {
//Special case: We aren't on a use, but we are jumping forward, and are behind this and the next use
pick = true;
}
if(!pick && !forward && a-1 >= 0 && c < localUses[a].start && c >= localUses[a-1].end) {
//Special case: We aren't on a use, but we are jumping backward, and are in front of this use, but behind the previous one
pick = true;
}
if(!pick && a == 0 && c < localUses[a].start) {
if(!forward) {
//Will automatically jump to previous file
}else{
nextUse = 0; //We are before the first use, so jump to it.
}
pick = true;
}
if(!pick && a == localUses.size()-1 && c >= localUses[a].end) {
if(forward) {
//Will automatically jump to next file
}else{ //We are behind the last use, but moving backward. So pick the last use.
nextUse = a;
}
pick = true;
}
if(pick) {
//Make sure we end up behind the use
if(nextUse != a)
while(forward && nextUse < localUses.size() && (localUses[nextUse].start <= localUses[a].end || localUses[nextUse].isEmpty()))
++nextUse;
//Make sure we end up before the use
if(nextUse != a)
while(!forward && nextUse >= 0 && (localUses[nextUse].start >= localUses[a].start || localUses[nextUse].isEmpty()))
--nextUse;
//Jump to the next use
qCDebug(PLUGIN_CONTEXTBROWSER) << "count of uses:" << localUses.size() << "nextUse" << nextUse;
if(nextUse < 0 || nextUse == localUses.size()) {
qCDebug(PLUGIN_CONTEXTBROWSER) << "jumping to next file";
//Jump to the first use in the next using top-context
int indexInFiles = usingFiles.indexOf(chosen);
if(indexInFiles != -1) {
int nextFile = (forward ? indexInFiles+1 : indexInFiles-1);
qCDebug(PLUGIN_CONTEXTBROWSER) << "current file" << indexInFiles << "nextFile" << nextFile;
if(nextFile < 0 || nextFile >= usingFiles.size()) {
//Open the declaration, or the definition
if(nextFile >= usingFiles.size()) {
Declaration* definition = FunctionDefinition::definition(decl);
if(definition)
decl = definition;
}
QUrl u = decl->url().toUrl();
KTextEditor::Range range = decl->rangeInCurrentRevision();
range.setEnd(range.start());
lock.unlock();
core()->documentController()->openDocument(u, range);
return;
}else{
TopDUContext* nextTop = usingFiles[nextFile].data();
QUrl u = nextTop->url().toUrl();
QList nextTopUses = allUses(nextTop, decl, true);
std::sort(nextTopUses.begin(), nextTopUses.end());
if(!nextTopUses.isEmpty()) {
KTextEditor::Range range = chosen->transformFromLocalRevision(forward ? nextTopUses.front() : nextTopUses.back());
range.setEnd(range.start());
lock.unlock();
core()->documentController()->openDocument(u, range);
}
return;
}
}else{
qCDebug(PLUGIN_CONTEXTBROWSER) << "not found own file in use list";
}
}else{
QUrl url = chosen->url().toUrl();
KTextEditor::Range range = chosen->transformFromLocalRevision(localUses[nextUse]);
range.setEnd(range.start());
lock.unlock();
core()->documentController()->openDocument(url, range);
return;
}
}
}
}
}
}
}
void ContextBrowserPlugin::unRegisterToolView(ContextBrowserView* view)
{
m_views.removeAll(view);
}
// history browsing
QWidget* ContextBrowserPlugin::toolbarWidgetForMainWindow( Sublime::MainWindow* window )
{
//TODO: support multiple windows (if that ever gets revived)
if (!m_toolbarWidget) {
m_toolbarWidget = new QWidget(window);
}
return m_toolbarWidget;
}
void ContextBrowserPlugin::documentJumpPerformed( KDevelop::IDocument* newDocument,
const KTextEditor::Cursor& newCursor,
KDevelop::IDocument* previousDocument,
const KTextEditor::Cursor& previousCursor) {
DUChainReadLocker lock(DUChain::lock());
/*TODO: support multiple windows if that ever gets revived
if(newDocument && newDocument->textDocument() && newDocument->textDocument()->activeView() && masterWidget(newDocument->textDocument()->activeView()) != masterWidget(this))
return;
*/
if(previousDocument && previousCursor.isValid()) {
qCDebug(PLUGIN_CONTEXTBROWSER) << "updating jump source";
DUContext* context = getContextAt(previousDocument->url(), previousCursor);
if(context) {
updateHistory(context, KTextEditor::Cursor(previousCursor), true);
}else{
//We just want this place in the history
m_history.resize(m_nextHistoryIndex); // discard forward history
m_history.append(HistoryEntry(DocumentCursor(IndexedString(previousDocument->url()), KTextEditor::Cursor(previousCursor))));
++m_nextHistoryIndex;
}
}
qCDebug(PLUGIN_CONTEXTBROWSER) << "new doc: " << newDocument << " new cursor: " << newCursor;
if(newDocument && newCursor.isValid()) {
qCDebug(PLUGIN_CONTEXTBROWSER) << "updating jump target";
DUContext* context = getContextAt(newDocument->url(), newCursor);
if(context) {
updateHistory(context, KTextEditor::Cursor(newCursor), true);
}else{
//We just want this place in the history
m_history.resize(m_nextHistoryIndex); // discard forward history
m_history.append(HistoryEntry(DocumentCursor(IndexedString(newDocument->url()), KTextEditor::Cursor(newCursor))));
++m_nextHistoryIndex;
if (m_outlineLine) m_outlineLine->clear();
}
}
}
void ContextBrowserPlugin::updateButtonState()
{
m_nextButton->setEnabled( m_nextHistoryIndex < m_history.size() );
m_previousButton->setEnabled( m_nextHistoryIndex >= 2 );
}
void ContextBrowserPlugin::historyNext() {
if(m_nextHistoryIndex >= m_history.size()) {
return;
}
openDocument(m_nextHistoryIndex); // opening the document at given position
// will update the widget for us
++m_nextHistoryIndex;
updateButtonState();
}
void ContextBrowserPlugin::openDocument(int historyIndex) {
Q_ASSERT_X(historyIndex >= 0, "openDocument", "negative history index");
Q_ASSERT_X(historyIndex < m_history.size(), "openDocument", "history index out of range");
DocumentCursor c = m_history[historyIndex].computePosition();
if (c.isValid() && !c.document.str().isEmpty()) {
disconnect(ICore::self()->documentController(), &IDocumentController::documentJumpPerformed, this, &ContextBrowserPlugin::documentJumpPerformed);
ICore::self()->documentController()->openDocument(c.document.toUrl(), c);
connect(ICore::self()->documentController(), &IDocumentController::documentJumpPerformed, this, &ContextBrowserPlugin::documentJumpPerformed);
KDevelop::DUChainReadLocker lock( KDevelop::DUChain::lock() );
updateDeclarationListBox(m_history[historyIndex].context.data());
}
}
void ContextBrowserPlugin::historyPrevious() {
if(m_nextHistoryIndex < 2) {
return;
}
--m_nextHistoryIndex;
openDocument(m_nextHistoryIndex-1); // opening the document at given position
// will update the widget for us
updateButtonState();
}
QString ContextBrowserPlugin::actionTextFor(int historyIndex) const
{
const HistoryEntry& entry = m_history.at(historyIndex);
QString actionText = entry.context.data() ? entry.context.data()->scopeIdentifier(true).toString() : QString();
if(actionText.isEmpty())
actionText = entry.alternativeString;
if(actionText.isEmpty())
actionText = QStringLiteral("");
actionText += QLatin1String(" @ ");
QString fileName = entry.absoluteCursorPosition.document.toUrl().fileName();
actionText += QStringLiteral("%1:%2").arg(fileName).arg(entry.absoluteCursorPosition.line()+1);
return actionText;
}
/*
inline QDebug operator<<(QDebug debug, const ContextBrowserPlugin::HistoryEntry &he)
{
DocumentCursor c = he.computePosition();
debug << "\n\tHistoryEntry " << c.line << " " << c.document.str();
return debug;
}
*/
void ContextBrowserPlugin::nextMenuAboutToShow() {
QList indices;
for(int a = m_nextHistoryIndex; a < m_history.size(); ++a) {
indices << a;
}
fillHistoryPopup(m_nextMenu, indices);
}
void ContextBrowserPlugin::previousMenuAboutToShow() {
QList indices;
for(int a = m_nextHistoryIndex-2; a >= 0; --a) {
indices << a;
}
fillHistoryPopup(m_previousMenu, indices);
}
void ContextBrowserPlugin::fillHistoryPopup(QMenu* menu, const QList& historyIndices) {
menu->clear();
KDevelop::DUChainReadLocker lock( KDevelop::DUChain::lock() );
foreach(int index, historyIndices) {
QAction* action = new QAction(actionTextFor(index), menu);
action->setData(index);
menu->addAction(action);
connect(action, &QAction::triggered, this, &ContextBrowserPlugin::actionTriggered);
}
}
bool ContextBrowserPlugin::isPreviousEntry(KDevelop::DUContext* context,
const KTextEditor::Cursor& /*position*/) const
{
if (m_nextHistoryIndex == 0) return false;
Q_ASSERT(m_nextHistoryIndex <= m_history.count());
const HistoryEntry& he = m_history.at(m_nextHistoryIndex-1);
KDevelop::DUChainReadLocker lock( KDevelop::DUChain::lock() ); // is this necessary??
Q_ASSERT(context);
return IndexedDUContext(context) == he.context;
}
void ContextBrowserPlugin::updateHistory(KDevelop::DUContext* context, const KTextEditor::Cursor& position, bool force)
{
qCDebug(PLUGIN_CONTEXTBROWSER) << "updating history";
if(m_outlineLine && m_outlineLine->isVisible())
updateDeclarationListBox(context);
if(!context || (!context->owner() && !force)) {
return; //Only add history-entries for contexts that have owners, which in practice should be functions and classes
//This keeps the history cleaner
}
if (isPreviousEntry(context, position)) {
if(m_nextHistoryIndex) {
HistoryEntry& he = m_history[m_nextHistoryIndex-1];
he.setCursorPosition(position);
}
return;
} else { // Append new history entry
m_history.resize(m_nextHistoryIndex); // discard forward history
m_history.append(HistoryEntry(IndexedDUContext(context), position));
++m_nextHistoryIndex;
updateButtonState();
if(m_history.size() > (maxHistoryLength + 5)) {
m_history = m_history.mid(m_history.size() - maxHistoryLength);
m_nextHistoryIndex = m_history.size();
}
}
}
void ContextBrowserPlugin::updateDeclarationListBox(DUContext* context) {
if(!context || !context->owner()) {
qCDebug(PLUGIN_CONTEXTBROWSER) << "not updating box";
m_listUrl = IndexedString(); ///@todo Compute the context in the document here
if (m_outlineLine) m_outlineLine->clear();
return;
}
Declaration* decl = context->owner();
m_listUrl = context->url();
Declaration* specialDecl = SpecializationStore::self().applySpecialization(decl, decl->topContext());
FunctionType::Ptr function = specialDecl->type();
QString text = specialDecl->qualifiedIdentifier().toString();
if(function)
text += function->partToString(KDevelop::FunctionType::SignatureArguments);
if(m_outlineLine && !m_outlineLine->hasFocus())
{
m_outlineLine->setText(text);
m_outlineLine->setCursorPosition(0);
}
qCDebug(PLUGIN_CONTEXTBROWSER) << "updated" << text;
}
void ContextBrowserPlugin::actionTriggered() {
QAction* action = qobject_cast(sender());
Q_ASSERT(action); Q_ASSERT(action->data().type() == QVariant::Int);
int historyPosition = action->data().toInt();
// qCDebug(PLUGIN_CONTEXTBROWSER) << "history pos" << historyPosition << m_history.size() << m_history;
if(historyPosition >= 0 && historyPosition < m_history.size()) {
m_nextHistoryIndex = historyPosition + 1;
openDocument(historyPosition);
updateButtonState();
}
}
void ContextBrowserPlugin::doNavigate(NavigationActionType action)
{
KTextEditor::View* view = qobject_cast(sender());
if(!view) {
qCWarning(PLUGIN_CONTEXTBROWSER) << "sender is not a view";
return;
}
KTextEditor::CodeCompletionInterface* iface = qobject_cast(view);
if(!iface || iface->isCompletionActive())
return; // If code completion is active, the actions should be handled by the completion widget
QWidget* widget = m_currentNavigationWidget.data();
if(!widget || !widget->isVisible())
{
ContextBrowserView* contextView = browserViewForWidget(view);
if(contextView)
widget = contextView->navigationWidget();
}
if(widget)
{
AbstractNavigationWidget* navWidget = qobject_cast(widget);
if (navWidget)
{
switch(action) {
case Accept:
navWidget->accept();
break;
case Back:
navWidget->back();
break;
case Left:
navWidget->previous();
break;
case Right:
navWidget->next();
break;
case Up:
navWidget->up();
break;
case Down:
navWidget->down();
break;
}
}
}
}
void ContextBrowserPlugin::navigateAccept() {
doNavigate(Accept);
}
void ContextBrowserPlugin::navigateBack() {
doNavigate(Back);
}
void ContextBrowserPlugin::navigateDown() {
doNavigate(Down);
}
void ContextBrowserPlugin::navigateLeft() {
doNavigate(Left);
}
void ContextBrowserPlugin::navigateRight() {
doNavigate(Right);
}
void ContextBrowserPlugin::navigateUp() {
doNavigate(Up);
}
//BEGIN HistoryEntry
ContextBrowserPlugin::HistoryEntry::HistoryEntry(KDevelop::DocumentCursor pos) : absoluteCursorPosition(pos) {
}
ContextBrowserPlugin::HistoryEntry::HistoryEntry(IndexedDUContext ctx, const KTextEditor::Cursor& cursorPosition) : context(ctx) {
//Use a position relative to the context
setCursorPosition(cursorPosition);
if(ctx.data())
alternativeString = ctx.data()->scopeIdentifier(true).toString();
if(!alternativeString.isEmpty())
alternativeString += i18n("(changed)"); //This is used when the context was deleted in between
}
DocumentCursor ContextBrowserPlugin::HistoryEntry::computePosition() const {
KDevelop::DUChainReadLocker lock( KDevelop::DUChain::lock() );
DocumentCursor ret;
if(context.data()) {
ret = DocumentCursor(context.data()->url(), relativeCursorPosition);
ret.setLine(ret.line() + context.data()->range().start.line);
}else{
ret = absoluteCursorPosition;
}
return ret;
}
void ContextBrowserPlugin::HistoryEntry::setCursorPosition(const KTextEditor::Cursor& cursorPosition) {
KDevelop::DUChainReadLocker lock( KDevelop::DUChain::lock() );
if(context.data()) {
absoluteCursorPosition = DocumentCursor(context.data()->url(), cursorPosition);
relativeCursorPosition = cursorPosition;
relativeCursorPosition.setLine(relativeCursorPosition.line() - context.data()->range().start.line);
}
}
// kate: space-indent on; indent-width 2; tab-width 4; replace-tabs on; auto-insert-doxygen on
#include "contextbrowser.moc"