diff --git a/interfaces/iuicontroller.h b/interfaces/iuicontroller.h
index 6ee0faebb7..fe7d07ff03 100644
--- a/interfaces/iuicontroller.h
+++ b/interfaces/iuicontroller.h
@@ -1,184 +1,174 @@
/***************************************************************************
* Copyright 2007 Alexander Dymo *
* *
* 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 Library General Public *
* License along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
***************************************************************************/
#ifndef KDEVPLATFORM_IUICONTROLLER_H
#define KDEVPLATFORM_IUICONTROLLER_H
#include "interfacesexport.h"
#include
#include
class QAction;
template
class QExplicitlySharedDataPointer;
namespace KParts {
class MainWindow;
}
namespace Sublime{
class Controller;
class View;
class Area;
}
namespace KDevelop {
class IDocument;
class IAssistant;
class KDEVPLATFORMINTERFACES_EXPORT IToolViewFactory {
public:
virtual ~IToolViewFactory() {}
/**
* called to create a new widget for this toolview
* @param parent the parent to use as parent for the widget
* @returns the new widget for the toolview
*/
virtual QWidget* create(QWidget *parent = 0) = 0;
/**
* @returns the identifier of this toolview. The identifier
* is used to remember which areas the tool view should appear
* in, and must never change.
*/
virtual QString id() const = 0;
/**
* @returns the default position where this toolview should appear
*/
virtual Qt::DockWidgetArea defaultPosition() = 0;
/**
* Fetch a list of actions to add to the toolbar of the toolview @p view
* @param view the view to which the actions should be added
* @returns a list of actions to be added to the toolbar
*/
virtual QList toolBarActions( QWidget* viewWidget ) const { return viewWidget->actions(); }
/**
* Fetch a list of actions to be shown in the context menu of the toolview @p view.
* The default implementation will return all actions of @p viewWidget.
*
* @param view the view for which the context menu should be shown
* @returns a list of actions to be shown in the context menu
*/
virtual QList contextMenuActions( QWidget* viewWidget ) const { return viewWidget->actions(); }
/**
* called when a new view is created from this template
* @param view the new sublime view that is being shown
*/
virtual void viewCreated(Sublime::View* view);
/**
* @returns if multiple tool views can by created by this factory in the same area.
*/
virtual bool allowMultiple() const { return false; }
};
/**
*
* Allows to access various parts of the user-interface, like the toolviews or the mainwindow
*/
class KDEVPLATFORMINTERFACES_EXPORT IUiController {
public:
virtual ~IUiController();
enum SwitchMode {
ThisWindow /**< indicates that the area switch should be in the this window */,
NewWindow /**< indicates that the area switch should be using a new window */
};
enum FindFlags {
None = 0,
Create = 1, ///The tool-view is created if it doesn't exist in the current area yet
Raise = 2, ///The tool-view is raised if it was found/created
CreateAndRaise = Create | Raise ///The tool view is created and raised
};
virtual void switchToArea(const QString &areaName, SwitchMode switchMode) = 0;
virtual void addToolView(const QString &name, IToolViewFactory *factory, FindFlags state = Create) = 0;
virtual void removeToolView(IToolViewFactory *factory) = 0;
/** Makes sure that this tool-view exists in the current area, raises it, and returns the contained widget
* Returns zero on failure */
virtual QWidget* findToolView(const QString& name, IToolViewFactory *factory, FindFlags flags = CreateAndRaise) = 0;
/**
* Makes sure that the toolview that contains the widget @p toolViewWidget is visible to the user.
*/
virtual void raiseToolView(QWidget* toolViewWidget) = 0;
/** @return active mainwindow or 0 if no such mainwindow is active.*/
virtual KParts::MainWindow *activeMainWindow() = 0;
/*! @p status must implement KDevelop::IStatus */
virtual void registerStatus(QObject* status) = 0;
- /**
- * Shows an assistant popup at bottom within the current central widget
- * @p assistant the assistant that will be shown in a popup */
- virtual void popUpAssistant(const QExplicitlySharedDataPointer& assistant) = 0;
-
- /**
- * Hides the assistant if it is currently being shown
- */
- virtual void hideAssistant() = 0;
-
/**
* This is meant to be used by IDocument subclasses to initialize the
* Sublime::Document.
*/
virtual Sublime::Controller* controller() = 0;
/** Shows an error message in the status bar.
*
* Unlike all other functions in this class, this function is thread-safe.
* You can call it from the background.
*
* @p message The message
* @p timeout The timeout in seconds how long to show the message */
virtual void showErrorMessage(const QString& message, int timeout = 1) = 0;
/** @return area for currently active sublime mainwindow or 0 if
no sublime mainwindow is active.*/
virtual Sublime::Area *activeArea() = 0;
/**
* Widget which is currently responsible for consuming special events in the UI
* (such as shortcuts)
*
* @sa IToolViewActionListener
* @return QWidget implementing the IToolViewActionListener interface
*/
virtual QWidget* activeToolViewActionListener() const = 0;
/**
* @returns all areas in the shell
*
* @note there will be one per mainwindow, of each type, plus the default ones.
*/
virtual QList allAreas() const = 0;
protected:
IUiController();
};
}
#endif
diff --git a/language/CMakeLists.txt b/language/CMakeLists.txt
index bbe6456098..2065f837bf 100644
--- a/language/CMakeLists.txt
+++ b/language/CMakeLists.txt
@@ -1,398 +1,399 @@
add_definitions(-DTRANSLATION_DOMAIN=\"kdevplatform\")
# Check whether malloc_trim(3) is supported.
include(CheckIncludeFile)
include(CheckSymbolExists)
check_include_file("malloc.h" HAVE_MALLOC_H)
check_symbol_exists(malloc_trim "malloc.h" HAVE_MALLOC_TRIM)
add_subdirectory(highlighting/tests)
add_subdirectory(duchain/tests)
add_subdirectory(backgroundparser/tests)
add_subdirectory(codegen/tests)
add_subdirectory(util/tests)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/language-features.h.cmake
${CMAKE_CURRENT_BINARY_DIR}/language-features.h )
set(KDevPlatformLanguage_LIB_SRCS
assistant/staticassistantsmanager.cpp
assistant/renameaction.cpp
assistant/renameassistant.cpp
assistant/renamefileaction.cpp
assistant/staticassistant.cpp
editor/persistentmovingrangeprivate.cpp
editor/persistentmovingrange.cpp
editor/modificationrevisionset.cpp
editor/modificationrevision.cpp
backgroundparser/backgroundparser.cpp
backgroundparser/parsejob.cpp
backgroundparser/documentchangetracker.cpp
backgroundparser/parseprojectjob.cpp
backgroundparser/urlparselock.cpp
duchain/specializationstore.cpp
duchain/codemodel.cpp
duchain/duchain.cpp
duchain/waitforupdate.cpp
duchain/duchainpointer.cpp
duchain/ducontext.cpp
duchain/indexedducontext.cpp
duchain/indexedtopducontext.cpp
duchain/localindexedducontext.cpp
duchain/indexeddeclaration.cpp
duchain/localindexeddeclaration.cpp
duchain/topducontext.cpp
duchain/topducontextdynamicdata.cpp
duchain/topducontextutils.cpp
duchain/functiondefinition.cpp
duchain/declaration.cpp
duchain/classmemberdeclaration.cpp
duchain/classfunctiondeclaration.cpp
duchain/classdeclaration.cpp
duchain/use.cpp
duchain/forwarddeclaration.cpp
duchain/duchainbase.cpp
duchain/duchainlock.cpp
duchain/identifier.cpp
duchain/parsingenvironment.cpp
duchain/abstractfunctiondeclaration.cpp
duchain/functiondeclaration.cpp
duchain/stringhelpers.cpp
duchain/namespacealiasdeclaration.cpp
duchain/aliasdeclaration.cpp
duchain/dumpdotgraph.cpp
duchain/duchainutils.cpp
duchain/declarationid.cpp
duchain/definitions.cpp
duchain/uses.cpp
duchain/importers.cpp
duchain/duchaindumper.cpp
duchain/duchainregister.cpp
duchain/persistentsymboltable.cpp
duchain/instantiationinformation.cpp
duchain/problem.cpp
duchain/types/typesystem.cpp
duchain/types/typeregister.cpp
duchain/types/typerepository.cpp
duchain/types/identifiedtype.cpp
duchain/types/abstracttype.cpp
duchain/types/integraltype.cpp
duchain/types/functiontype.cpp
duchain/types/structuretype.cpp
duchain/types/pointertype.cpp
duchain/types/referencetype.cpp
duchain/types/delayedtype.cpp
duchain/types/arraytype.cpp
duchain/types/indexedtype.cpp
duchain/types/enumerationtype.cpp
duchain/types/constantintegraltype.cpp
duchain/types/enumeratortype.cpp
duchain/types/typeutils.cpp
duchain/types/typealiastype.cpp
duchain/types/unsuretype.cpp
duchain/types/containertypes.cpp
duchain/builders/dynamiclanguageexpressionvisitor.cpp
duchain/navigation/problemnavigationcontext.cpp
duchain/navigation/abstractnavigationwidget.cpp
duchain/navigation/abstractnavigationcontext.cpp
duchain/navigation/usesnavigationcontext.cpp
duchain/navigation/abstractdeclarationnavigationcontext.cpp
duchain/navigation/abstractincludenavigationcontext.cpp
duchain/navigation/useswidget.cpp
duchain/navigation/usescollector.cpp
interfaces/abbreviations.cpp
interfaces/iastcontainer.cpp
interfaces/ilanguagesupport.cpp
interfaces/quickopendataprovider.cpp
interfaces/iquickopen.cpp
interfaces/editorcontext.cpp
interfaces/codecontext.cpp
interfaces/icreateclasshelper.cpp
interfaces/icontextbrowser.cpp
codecompletion/codecompletion.cpp
codecompletion/codecompletionworker.cpp
codecompletion/codecompletionmodel.cpp
codecompletion/codecompletionitem.cpp
codecompletion/codecompletioncontext.cpp
codecompletion/codecompletionitemgrouper.cpp
codecompletion/codecompletionhelper.cpp
codecompletion/normaldeclarationcompletionitem.cpp
codegen/applychangeswidget.cpp
codegen/coderepresentation.cpp
codegen/documentchangeset.cpp
codegen/duchainchangeset.cpp
codegen/utilities.cpp
codegen/codedescription.cpp
codegen/basicrefactoring.cpp
codegen/progressdialogs/refactoringdialog.cpp
util/setrepository.cpp
util/includeitem.cpp
util/navigationtooltip.cpp
util/debug.cpp
highlighting/colorcache.cpp
highlighting/configurablecolors.cpp
highlighting/codehighlighting.cpp
checks/dataaccessrepository.cpp checks/dataaccess.cpp
checks/controlflowgraph.cpp checks/controlflownode.cpp
classmodel/classmodel.cpp
classmodel/classmodelnode.cpp
classmodel/classmodelnodescontroller.cpp
classmodel/allclassesfolder.cpp
classmodel/documentclassesfolder.cpp
classmodel/projectfolder.cpp
)
set(grantlee_LIB_SRCS
codegen/templatesmodel.cpp
codegen/templateclassgenerator.cpp
codegen/sourcefiletemplate.cpp
codegen/templaterenderer.cpp
codegen/templateengine.cpp
codegen/archivetemplateloader.cpp
)
if (Grantlee5_FOUND)
list(APPEND KDevPlatformLanguage_LIB_SRCS ${grantlee_LIB_SRCS})
endif()
ki18n_wrap_ui(KDevPlatformLanguage_LIB_SRCS
codegen/basicrefactoring.ui
codegen/progressdialogs/refactoringdialog.ui)
kdevplatform_add_library(KDevPlatformLanguage SOURCES ${KDevPlatformLanguage_LIB_SRCS})
target_include_directories(KDevPlatformLanguage PRIVATE ${Boost_INCLUDE_DIRS})
target_link_libraries(KDevPlatformLanguage LINK_PUBLIC
KF5::ThreadWeaver
KDev::Interfaces
KDev::Serialization
LINK_PRIVATE
KF5::GuiAddons
KF5::TextEditor
KF5::Parts
KF5::Archive
+ KF5::IconThemes
KDev::Util
KDev::Project
)
if (Grantlee5_FOUND)
target_link_libraries(KDevPlatformLanguage LINK_PRIVATE Grantlee5::Templates)
endif()
install(FILES
assistant/renameaction.h
assistant/renameassistant.h
assistant/staticassistant.h
assistant/staticassistantsmanager.h
DESTINATION ${KDE_INSTALL_INCLUDEDIR}/kdevplatform/language/assistant COMPONENT Devel
)
install(FILES
interfaces/ilanguagesupport.h
interfaces/icodehighlighting.h
interfaces/quickopendataprovider.h
interfaces/quickopenfilter.h
interfaces/iquickopen.h
interfaces/codecontext.h
interfaces/editorcontext.h
interfaces/iastcontainer.h
interfaces/icreateclasshelper.h
interfaces/icontextbrowser.h
interfaces/abbreviations.h
DESTINATION ${KDE_INSTALL_INCLUDEDIR}/kdevplatform/language/interfaces COMPONENT Devel
)
install(FILES
editor/persistentmovingrange.h
editor/documentrange.h
editor/documentcursor.h
editor/cursorinrevision.h
editor/rangeinrevision.h
editor/modificationrevision.h
editor/modificationrevisionset.h
DESTINATION ${KDE_INSTALL_INCLUDEDIR}/kdevplatform/language/editor COMPONENT Devel
)
install(FILES
backgroundparser/backgroundparser.h
backgroundparser/parsejob.h
backgroundparser/parseprojectjob.h
backgroundparser/urlparselock.h
backgroundparser/documentchangetracker.h
DESTINATION ${KDE_INSTALL_INCLUDEDIR}/kdevplatform/language/backgroundparser COMPONENT Devel
)
install(FILES
util/navigationtooltip.h
util/setrepository.h
util/basicsetrepository.h
util/includeitem.h
util/debuglanguageparserhelper.h
util/kdevhash.h
DESTINATION ${KDE_INSTALL_INCLUDEDIR}/kdevplatform/language/util COMPONENT Devel
)
install(FILES
duchain/parsingenvironment.h
duchain/duchain.h
duchain/codemodel.h
duchain/ducontext.h
duchain/ducontextdata.h
duchain/topducontext.h
duchain/topducontextutils.h
duchain/topducontextdata.h
duchain/declaration.h
duchain/declarationdata.h
duchain/classmemberdeclaration.h
duchain/classmemberdeclarationdata.h
duchain/classfunctiondeclaration.h
duchain/classdeclaration.h
duchain/functiondefinition.h
duchain/use.h
duchain/forwarddeclaration.h
duchain/duchainbase.h
duchain/duchainpointer.h
duchain/duchainlock.h
duchain/identifier.h
duchain/abstractfunctiondeclaration.h
duchain/functiondeclaration.h
duchain/stringhelpers.h
duchain/safetycounter.h
duchain/namespacealiasdeclaration.h
duchain/aliasdeclaration.h
duchain/dumpdotgraph.h
duchain/duchainutils.h
duchain/duchaindumper.h
duchain/declarationid.h
duchain/appendedlist.h
duchain/duchainregister.h
duchain/persistentsymboltable.h
duchain/instantiationinformation.h
duchain/specializationstore.h
duchain/persistentsetmap.h
duchain/indexedducontext.h
duchain/indexedtopducontext.h
duchain/localindexedducontext.h
duchain/indexeddeclaration.h
duchain/localindexeddeclaration.h
duchain/definitions.h
duchain/problem.h
DESTINATION ${KDE_INSTALL_INCLUDEDIR}/kdevplatform/language/duchain COMPONENT Devel
)
install(FILES
duchain/types/unsuretype.h
duchain/types/identifiedtype.h
duchain/types/typesystem.h
duchain/types/typeregister.h
duchain/types/typerepository.h
duchain/types/typepointer.h
duchain/types/typesystemdata.h
duchain/types/abstracttype.h
duchain/types/integraltype.h
duchain/types/functiontype.h
duchain/types/structuretype.h
duchain/types/pointertype.h
duchain/types/referencetype.h
duchain/types/delayedtype.h
duchain/types/arraytype.h
duchain/types/indexedtype.h
duchain/types/enumerationtype.h
duchain/types/constantintegraltype.h
duchain/types/enumeratortype.h
duchain/types/alltypes.h
duchain/types/typeutils.h
duchain/types/typealiastype.h
duchain/types/containertypes.h
DESTINATION ${KDE_INSTALL_INCLUDEDIR}/kdevplatform/language/duchain/types COMPONENT Devel
)
install(FILES
duchain/builders/abstractcontextbuilder.h
duchain/builders/abstractdeclarationbuilder.h
duchain/builders/abstracttypebuilder.h
duchain/builders/abstractusebuilder.h
duchain/builders/dynamiclanguageexpressionvisitor.h
DESTINATION ${KDE_INSTALL_INCLUDEDIR}/kdevplatform/language/duchain/builders COMPONENT Devel
)
install(FILES
codecompletion/codecompletion.h
codecompletion/codecompletionworker.h
codecompletion/codecompletionmodel.h
codecompletion/codecompletionitem.h
codecompletion/codecompletioncontext.h
codecompletion/codecompletionitemgrouper.h
codecompletion/codecompletionhelper.h
codecompletion/normaldeclarationcompletionitem.h
codecompletion/abstractincludefilecompletionitem.h
codecompletion/codecompletiontesthelper.h
DESTINATION ${KDE_INSTALL_INCLUDEDIR}/kdevplatform/language/codecompletion COMPONENT Devel
)
install(FILES
codegen/applychangeswidget.h
codegen/astchangeset.h
codegen/duchainchangeset.h
codegen/documentchangeset.h
codegen/coderepresentation.h
codegen/utilities.h
codegen/templatesmodel.h
codegen/templaterenderer.h
codegen/templateengine.h
codegen/sourcefiletemplate.h
codegen/templateclassgenerator.h
codegen/codedescription.h
codegen/basicrefactoring.h
DESTINATION ${KDE_INSTALL_INCLUDEDIR}/kdevplatform/language/codegen COMPONENT Devel
)
install(FILES
duchain/navigation/usesnavigationcontext.h
duchain/navigation/abstractnavigationcontext.h
duchain/navigation/abstractdeclarationnavigationcontext.h
duchain/navigation/abstractincludenavigationcontext.h
duchain/navigation/abstractnavigationwidget.h
duchain/navigation/navigationaction.h
duchain/navigation/useswidget.h
duchain/navigation/usescollector.h
DESTINATION ${KDE_INSTALL_INCLUDEDIR}/kdevplatform/language/duchain/navigation COMPONENT Devel
)
install(FILES
highlighting/codehighlighting.h
highlighting/colorcache.h
highlighting/configurablecolors.h
DESTINATION ${KDE_INSTALL_INCLUDEDIR}/kdevplatform/language/highlighting COMPONENT Devel
)
install(FILES
checks/dataaccess.h
checks/dataaccessrepository.h
checks/controlflowgraph.h
checks/controlflownode.h
DESTINATION ${KDE_INSTALL_INCLUDEDIR}/kdevplatform/language/checks COMPONENT Devel
)
install(FILES
classmodel/classmodel.h
classmodel/classmodelnode.h
classmodel/classmodelnodescontroller.h
classmodel/allclassesfolder.h
classmodel/documentclassesfolder.h
classmodel/projectfolder.h
DESTINATION ${KDE_INSTALL_INCLUDEDIR}/kdevplatform/language/classmodel COMPONENT Devel
)
diff --git a/language/assistant/renameassistant.cpp b/language/assistant/renameassistant.cpp
index 048f2d1aca..dfc7fb8e0b 100644
--- a/language/assistant/renameassistant.cpp
+++ b/language/assistant/renameassistant.cpp
@@ -1,223 +1,238 @@
/*
Copyright 2010 Olivier de Gaalon
Copyright 2014 Kevin Funk
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "renameassistant.h"
#include "renameaction.h"
#include "renamefileaction.h"
#include "util/debug.h"
#include "../codegen/basicrefactoring.h"
#include "../codegen/documentchangeset.h"
#include "../duchain/duchain.h"
#include "../duchain/duchainlock.h"
#include "../duchain/duchainutils.h"
#include "../duchain/declaration.h"
#include "../duchain/functiondefinition.h"
#include "../duchain/classfunctiondeclaration.h"
#include
#include
#include
#include
#include
using namespace KDevelop;
namespace {
bool rangesConnect(const KTextEditor::Range& firstRange, const KTextEditor::Range& secondRange)
{
return !firstRange.intersect(secondRange + KTextEditor::Range(0, -1, 0, +1)).isEmpty();
}
-Declaration* getDeclarationForChangedRange(KTextEditor::View* view, const KTextEditor::Range& changed)
+Declaration* getDeclarationForChangedRange(KTextEditor::Document* doc, const KTextEditor::Range& changed)
{
const KTextEditor::Cursor cursor(changed.start());
- Declaration* declaration = DUChainUtils::itemUnderCursor(view->document()->url(), cursor);
+ Declaration* declaration = DUChainUtils::itemUnderCursor(doc->url(), cursor);
//If it's null we could be appending, but there's a case where appending gives a wrong decl
//and not a null declaration ... "type var(init)", so check for that too
if (!declaration || !rangesConnect(declaration->rangeInCurrentRevision(), changed)) {
- declaration = DUChainUtils::itemUnderCursor(view->document()->url(), KTextEditor::Cursor(cursor.line(), cursor.column()-1));
+ declaration = DUChainUtils::itemUnderCursor(doc->url(), KTextEditor::Cursor(cursor.line(), cursor.column()-1));
}
//In this case, we may either not have a decl at the cursor, or we got a decl, but are editing its use.
//In either of those cases, give up and return 0
if (!declaration || !rangesConnect(declaration->rangeInCurrentRevision(), changed)) {
return 0;
}
return declaration;
}
}
struct RenameAssistant::Private
{
Private(RenameAssistant* qq)
: q(qq)
, m_isUseful(false)
, m_renameFile(false)
{
}
void reset()
{
q->doHide();
q->clearActions();
m_oldDeclarationName = Identifier();
m_newDeclarationRange.reset();
m_oldDeclarationUses.clear();
m_isUseful = false;
m_renameFile = false;
}
RenameAssistant* q;
KDevelop::Identifier m_oldDeclarationName;
QString m_newDeclarationName;
KDevelop::PersistentMovingRange::Ptr m_newDeclarationRange;
QVector m_oldDeclarationUses;
bool m_isUseful;
bool m_renameFile;
+ KTextEditor::Cursor m_lastChangedLocation;
+ QPointer m_lastChangedDocument = nullptr;
};
RenameAssistant::RenameAssistant(ILanguageSupport* supportedLanguage)
: StaticAssistant(supportedLanguage)
, d(new Private(this))
{
}
RenameAssistant::~RenameAssistant()
{
}
QString RenameAssistant::title() const
{
return tr("Rename");
}
bool RenameAssistant::isUseful() const
{
return d->m_isUseful;
}
-void RenameAssistant::textChanged(KTextEditor::View* view, const KTextEditor::Range& invocationRange, const QString& removedText)
+void RenameAssistant::textChanged(KTextEditor::Document* doc, const KTextEditor::Range& invocationRange, const QString& removedText)
{
clearActions();
+ d->m_lastChangedLocation = invocationRange.end();
+ d->m_lastChangedDocument = doc;
if (!supportedLanguage()->refactoring()) {
qCWarning(LANGUAGE) << "Refactoring not supported. Aborting.";
return;
}
- if (!view)
+ if (!doc)
return;
//If the inserted text isn't valid for a variable name, consider the editing ended
QRegExp validDeclName("^[0-9a-zA-Z_]*$");
- if (removedText.isEmpty() && !validDeclName.exactMatch(view->document()->text(invocationRange))) {
+ if (removedText.isEmpty() && !validDeclName.exactMatch(doc->text(invocationRange))) {
d->reset();
return;
}
- const QUrl url = view->document()->url();
+ const QUrl url = doc->url();
const IndexedString indexedUrl(url);
DUChainReadLocker lock;
//If we've stopped editing m_newDeclarationRange or switched the view,
// reset and see if there's another declaration being edited
if (!d->m_newDeclarationRange.data() || !rangesConnect(d->m_newDeclarationRange->range(), invocationRange)
|| d->m_newDeclarationRange->document() != indexedUrl) {
d->reset();
- Declaration* declAtCursor = getDeclarationForChangedRange(view, invocationRange);
+ Declaration* declAtCursor = getDeclarationForChangedRange(doc, invocationRange);
if (!declAtCursor) {
// not editing a declaration
return;
}
if (supportedLanguage()->refactoring()->shouldRenameUses(declAtCursor)) {
QMap< IndexedString, QList > declUses = declAtCursor->uses();
if (declUses.isEmpty()) {
// new declaration has no uses
return;
}
for(QMap< IndexedString, QList< RangeInRevision > >::const_iterator it = declUses.constBegin();
it != declUses.constEnd(); ++it)
{
foreach(const RangeInRevision range, it.value()) {
KTextEditor::Range currentRange = declAtCursor->transformFromLocalRevision(range);
- if(currentRange.isEmpty() || view->document()->text(currentRange) != declAtCursor->identifier().identifier().str()) {
+ if(currentRange.isEmpty() || doc->text(currentRange) != declAtCursor->identifier().identifier().str()) {
return; // One of the uses is invalid. Maybe the replacement has already been performed.
}
}
}
d->m_oldDeclarationUses = RevisionedFileRanges::convert(declUses);
} else if (supportedLanguage()->refactoring()->shouldRenameFile(declAtCursor)) {
d->m_renameFile = true;
} else {
// not a valid declaration
return;
}
d->m_oldDeclarationName = declAtCursor->identifier();
KTextEditor::Range newRange = declAtCursor->rangeInCurrentRevision();
if (removedText.isEmpty() && newRange.intersect(invocationRange).isEmpty()) {
newRange = newRange.encompass(invocationRange); //if text was added to the ends, encompass it
}
d->m_newDeclarationRange = new PersistentMovingRange(newRange, indexedUrl, true);
}
//Unfortunately this happens when you make a selection including one end of the decl's range and replace it
if (removedText.isEmpty() && d->m_newDeclarationRange->range().intersect(invocationRange).isEmpty()) {
d->m_newDeclarationRange = new PersistentMovingRange(
d->m_newDeclarationRange->range().encompass(invocationRange), indexedUrl, true);
}
- d->m_newDeclarationName = view->document()->text(d->m_newDeclarationRange->range());
+ d->m_newDeclarationName = doc->text(d->m_newDeclarationRange->range());
if (d->m_newDeclarationName == d->m_oldDeclarationName.toString()) {
d->reset();
return;
}
if (d->m_renameFile && supportedLanguage()->refactoring()->newFileName(url, d->m_newDeclarationName) == url.fileName()) {
// no change, don't do anything
return;
}
d->m_isUseful = true;
IAssistantAction::Ptr action;
if (d->m_renameFile) {
action = new RenameFileAction(supportedLanguage()->refactoring(), url, d->m_newDeclarationName);
} else {
action = new RenameAction(d->m_oldDeclarationName, d->m_newDeclarationName, d->m_oldDeclarationUses);
}
connect(action.data(), &IAssistantAction::executed, this, [&] { d->reset(); });
addAction(action);
emit actionsChanged();
}
+KTextEditor::Range KDevelop::RenameAssistant::displayRange() const
+{
+ if ( !d->m_lastChangedDocument ) {
+ return {};
+ }
+ auto range = d->m_lastChangedDocument->wordRangeAt(d->m_lastChangedLocation);
+ qDebug() << "range:" << range;
+ return range;
+}
+
+
#include "moc_renameassistant.cpp"
diff --git a/language/assistant/renameassistant.h b/language/assistant/renameassistant.h
index bcbe61c8b4..f3fdf05804 100644
--- a/language/assistant/renameassistant.h
+++ b/language/assistant/renameassistant.h
@@ -1,55 +1,57 @@
/*
Copyright 2010 Olivier de Gaalon
Copyright 2014 Kevin Funk
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KDEVPLATFORM_RENAMEEASSISTANT_H
#define KDEVPLATFORM_RENAMEEASSISTANT_H
#include
#include
#include
#include
#include "renameaction.h"
namespace KTextEditor {
class View;
}
namespace KDevelop {
class KDEVPLATFORMLANGUAGE_EXPORT RenameAssistant : public StaticAssistant
{
Q_OBJECT
public:
explicit RenameAssistant(ILanguageSupport* supportedLanguage);
~RenameAssistant() override;
- void textChanged(KTextEditor::View* view, const KTextEditor::Range& invocationRange, const QString& removedText = QString()) override;
+ void textChanged(KTextEditor::Document* doc, const KTextEditor::Range& invocationRange,
+ const QString& removedText = QString()) override;
bool isUseful() const override;
+ KTextEditor::Range displayRange() const override;
QString title() const override;
private:
struct Private;
QScopedPointer const d;
};
}
#endif // KDEVPLATFORM_RENAMEEASSISTANT_H
diff --git a/language/assistant/staticassistant.h b/language/assistant/staticassistant.h
index 9846604b6b..0a8179436e 100644
--- a/language/assistant/staticassistant.h
+++ b/language/assistant/staticassistant.h
@@ -1,94 +1,105 @@
/*
* Copyright 2014 Kevin Funk
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* 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, see .
*
*/
#ifndef KDEVPLATFORM_STATICASSISTANT_H
#define KDEVPLATFORM_STATICASSISTANT_H
#include
+#include
+#include
#include
+#include
+
namespace KTextEditor {
class Document;
class View;
class Range;
}
namespace KDevelop {
class ILanguageSupport;
/**
* @brief This class serves as a base for long-living assistants
*
* Normally, an assistant only lives for a short time, and is indirectly owned by a KDevelop::Problem.
* Static assistants are owned by the language support plugins and exist as long as the language plugin is loaded.
* They are not created by KDevelop::Problem instances, instead, they check for problematic code for themselves,
* by tracking document changes via the textChanged() method.
*
* Note that static assistants are not bound to a single document/view.
* Instead the current document/view we're looking at is passed via the textChanged() method.
*
* Register instances of this class view StaticAssistantsManager::registerAssistant
*
* @sa textChanged()
* @sa StaticAssistantsManager::registerAssistant
*/
class KDEVPLATFORMLANGUAGE_EXPORT StaticAssistant : public IAssistant
{
Q_OBJECT
public:
using Ptr = QExplicitlySharedDataPointer;
explicit StaticAssistant(ILanguageSupport* supportedLanguage);
~StaticAssistant() override;
/**
* Language this static assistant supports
*
* textChanged() will only be called for documents with language equal to this assistant's language
* @sa textChanged(0)
*/
ILanguageSupport* supportedLanguage() const;
/**
* Invoked whenever text inside a view was changed by the user
*
* Reimplement in subclass
*/
- virtual void textChanged(KTextEditor::View* view, const KTextEditor::Range& invocationRange,
+ virtual void textChanged(KTextEditor::Document* doc, const KTextEditor::Range& invocationRange,
const QString& removedText = QString()) = 0;
/**
* Whether it's worth showing this assistant to the user
*
* Reimplement in subclass
*/
virtual bool isUseful() const = 0;
+ /**
+ * The range the assistant should be displayed in.
+ */
+ virtual KTextEditor::Range displayRange() const = 0;
+
+ virtual void updateReady(const IndexedString&, const KDevelop::ReferencedTopDUContext&) { }
+
private:
struct Private;
QScopedPointer const d;
};
}
#endif // KDEVPLATFORM_STATICASSISTANT_H
diff --git a/language/assistant/staticassistantsmanager.cpp b/language/assistant/staticassistantsmanager.cpp
index 755789246b..29ccdc1113 100644
--- a/language/assistant/staticassistantsmanager.cpp
+++ b/language/assistant/staticassistantsmanager.cpp
@@ -1,321 +1,171 @@
/*
Copyright 2009 David Nolden
Copyright 2014 Kevin Funk
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
- along with this library; see the file COPYING.LIB. If not, write to
+ 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 "staticassistantsmanager.h"
+#include
#include "util/debug.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace KDevelop;
using namespace KTextEditor;
struct StaticAssistantsManager::Private
{
Private(StaticAssistantsManager* qq)
: q(qq)
- {
- connect(DUChain::self(), &DUChain::updateReady,
- q, [this] (const IndexedString& url, const ReferencedTopDUContext& topContext) {
- updateReady(url, topContext);
- });
- }
-
- void eventuallyStartAssistant();
- void startAssistant(KDevelop::IAssistant::Ptr assistant);
- void checkAssistantForProblems(KDevelop::TopDUContext* top);
+ { }
+ void updateReady(const IndexedString& document, const KDevelop::ReferencedTopDUContext& topContext);
void documentLoaded(KDevelop::IDocument*);
- void textInserted(Document* document, const Cursor& cursor, const QString& text);
- void textRemoved(Document* document, const Range& cursor, const QString& removedText);
- void updateReady(const IndexedString& document, const ReferencedTopDUContext& topContext);
- void documentActivated(KDevelop::IDocument*);
- void cursorPositionChanged(KTextEditor::View*, const KTextEditor::Cursor&);
- void timeout();
+ void textInserted(KTextEditor::Document* document, const Cursor& cursor, const QString& text);
+ void textRemoved(KTextEditor::Document* document, const Range& cursor, const QString& removedText);
StaticAssistantsManager* q;
- QPointer m_currentView;
- KTextEditor::Cursor m_assistantStartedAt;
- KDevelop::IndexedString m_currentDocument;
- QExplicitlySharedDataPointer m_activeAssistant;
- QList m_registeredAssistants;
- bool m_activeProblemAssistant = false;
- QTimer* m_timer;
-
- SafeDocumentPointer m_eventualDocument;
- KTextEditor::Range m_eventualRange;
- QString m_eventualRemovedText;
- QMetaObject::Connection m_cursorPositionChangeConnection;
+ QVector m_registeredAssistants;
};
StaticAssistantsManager::StaticAssistantsManager(QObject* parent)
: QObject(parent)
, d(new Private(this))
{
- d->m_timer = new QTimer(this);
- d->m_timer->setSingleShot(true);
- d->m_timer->setInterval(400);
- connect(d->m_timer, &QTimer::timeout, this, [&] { d->timeout(); });
-
connect(KDevelop::ICore::self()->documentController(),
&IDocumentController::documentLoaded,
this, [&] (IDocument* document) { d->documentLoaded(document); });
- connect(KDevelop::ICore::self()->documentController(),
- &IDocumentController::documentActivated,
- this, [&] (IDocument* doc) { d->documentActivated(doc); });
-
foreach (IDocument* document, ICore::self()->documentController()->openDocuments()) {
d->documentLoaded(document);
}
}
StaticAssistantsManager::~StaticAssistantsManager()
{
}
-QExplicitlySharedDataPointer StaticAssistantsManager::activeAssistant()
-{
- return d->m_activeAssistant;
-}
-
void StaticAssistantsManager::registerAssistant(const StaticAssistant::Ptr assistant)
{
if (d->m_registeredAssistants.contains(assistant))
return;
d->m_registeredAssistants << assistant;
}
void StaticAssistantsManager::unregisterAssistant(const StaticAssistant::Ptr assistant)
{
d->m_registeredAssistants.removeOne(assistant);
}
-QList StaticAssistantsManager::registeredAssistants() const
+QVector StaticAssistantsManager::registeredAssistants() const
{
return d->m_registeredAssistants;
}
void StaticAssistantsManager::Private::documentLoaded(IDocument* document)
{
if (document->textDocument()) {
- connect(document->textDocument(),
- &Document::textInserted, q,
- [&] (Document* document, const Cursor& cursor, const QString& text) { textInserted(document, cursor, text); });
- connect(document->textDocument(),
- &Document::textRemoved, q,
- [&] (Document* document, const Range& range, const QString& removedText) { textRemoved(document, range, removedText); });
+ auto doc = document->textDocument();
+ connect(doc, &KTextEditor::Document::textInserted, q,
+ [&] (KTextEditor::Document* doc, const Cursor& cursor, const QString& text) {
+ textInserted(doc, cursor, text);
+ });
+ connect(doc, &KTextEditor::Document::textRemoved, q,
+ [&] (KTextEditor::Document* doc, const Range& range, const QString& removedText) {
+ textRemoved(doc, range, removedText);
+ });
}
}
-void StaticAssistantsManager::hideAssistant()
+void StaticAssistantsManager::Private::textInserted(Document* doc, const Cursor& cursor, const QString& text)
{
- d->m_activeAssistant = QExplicitlySharedDataPointer();
- d->m_activeProblemAssistant = false;
- emit activeAssistantChanged();
-}
-
-void StaticAssistantsManager::Private::textInserted(Document* document, const Cursor& cursor, const QString& text)
-{
- m_eventualDocument = document;
- m_eventualRange = Range(cursor, text.size());
- m_eventualRemovedText.clear();
- QMetaObject::invokeMethod(q, "eventuallyStartAssistant", Qt::QueuedConnection);
+ Q_FOREACH ( auto assistant, m_registeredAssistants ) {
+ auto range = Range(cursor, cursor+Cursor(0, text.size()));
+ assistant->textChanged(doc, range, {});
+ }
}
-void StaticAssistantsManager::Private::textRemoved(Document* document, const Range& range,
+void StaticAssistantsManager::Private::textRemoved(Document* doc, const Range& range,
const QString& removedText)
{
- m_eventualDocument = document;
- m_eventualRange = range;
- m_eventualRemovedText = removedText;
- QMetaObject::invokeMethod(q, "eventuallyStartAssistant", Qt::QueuedConnection);
+ Q_FOREACH ( auto assistant, m_registeredAssistants ) {
+ assistant->textChanged(doc, range, removedText);
+ }
}
-void StaticAssistantsManager::Private::eventuallyStartAssistant()
+void StaticAssistantsManager::notifyAssistants(const IndexedString& url, const KDevelop::ReferencedTopDUContext& context)
{
- if (!m_eventualDocument) {
- return;
+ Q_FOREACH ( auto assistant, d->m_registeredAssistants ) {
+ assistant->updateReady(url, context);
}
+}
+QVector KDevelop::StaticAssistantsManager::problemsForContext(const KDevelop::ReferencedTopDUContext& top)
+{
View* view = ICore::self()->documentController()->activeTextDocumentView();
- if (!view) {
- return;
- }
- if (view->document() != m_eventualDocument) {
- qWarning(LANGUAGE) << "Active view does not belong to document of last observed change!";
- return;
+ if (!view || !top || IndexedString(view->document()->url()) != top->url()) {
+ return {};
}
- auto language = ICore::self()->languageController()->languagesForUrl(m_eventualDocument.data()->url()).value(0);
+ auto doc = top->url();
+ auto language = ICore::self()->languageController()->languagesForUrl(doc.toUrl()).value(0);
if (!language) {
- return;
+ return {};
}
+ auto ret = QVector();
qCDebug(LANGUAGE) << "Trying to find assistants for language" << language->name();
- foreach (const auto& assistant, m_registeredAssistants) {
+ foreach (const auto& assistant, d->m_registeredAssistants) {
if (assistant->supportedLanguage() != language)
continue;
- // notify assistant about editor changes
- assistant->textChanged(view, m_eventualRange, m_eventualRemovedText);
-
if (assistant->isUseful()) {
- startAssistant(IAssistant::Ptr(assistant.data()));
- break;
+ qDebug() << "assistant is now useful:" << assistant.data();
+
+ auto p = new KDevelop::StaticAssistantProblem();
+ auto range = assistant->displayRange();
+ qDebug() << "range:" << range;
+ p->setFinalLocation(DocumentRange(doc, range));
+ p->setSource(KDevelop::IProblem::SemanticAnalysis);
+ p->setSeverity(KDevelop::IProblem::Warning);
+ p->setDescription(assistant->title());
+ p->setSolutionAssistant(IAssistant::Ptr(assistant.data()));
+
+ ret.append(KDevelop::Problem::Ptr(p));
}
}
-
- // optimize, esp. for setText() calls as done in e.g. reformat source
- // only start the assitant once for multiple textRemoved/textInserted signals
- m_eventualDocument.clear();
- m_eventualRange = Range::invalid();
- m_eventualRemovedText.clear();
+ return ret;
}
-void StaticAssistantsManager::Private::startAssistant(IAssistant::Ptr assistant)
-{
- if (assistant == m_activeAssistant) {
- return;
- }
-
- qCDebug(LANGUAGE()) << "Starting assistant:" << assistant->title();
-
- if (m_activeAssistant) {
- m_activeAssistant->doHide();
- }
-
- if (!m_currentView)
- return;
-
- m_activeAssistant = assistant;
- if (m_activeAssistant) {
- connect(m_activeAssistant.data(), &IAssistant::hide, q, &StaticAssistantsManager::hideAssistant, Qt::UniqueConnection);
- ICore::self()->uiController()->popUpAssistant(IAssistant::Ptr(m_activeAssistant.data()));
-
- m_assistantStartedAt = m_currentView.data()->cursorPosition();
- }
-
- emit q->activeAssistantChanged();
-}
-void StaticAssistantsManager::Private::updateReady(const IndexedString& url, const ReferencedTopDUContext& topContext)
-{
- if (ICore::self()->shuttingDown()) {
- return;
- }
-
- if (url != m_currentDocument) {
- return;
- }
-
- if (m_activeAssistant) {
- if (m_activeProblemAssistant) {
- m_activeAssistant->doHide(); //Hide the assistant, as we will create a new one if the problem is still there
- } else {
- return;
- }
- }
-
- DUChainReadLocker lock(DUChain::lock(), 300);
- if (!lock.locked()) {
- return;
- }
-
- if (topContext) {
- checkAssistantForProblems(topContext);
- }
-}
-
-void StaticAssistantsManager::Private::cursorPositionChanged(View*, const Cursor& pos)
-{
- if (m_activeAssistant && m_assistantStartedAt.isValid()
- && abs(m_assistantStartedAt.line() - pos.line()) >= 1)
- {
- m_activeAssistant->doHide();
- }
-
- m_timer->start();
-}
-
-void StaticAssistantsManager::Private::documentActivated(IDocument* doc)
-{
- if (doc) {
- m_currentDocument = IndexedString(doc->url());
- }
-
- if (m_currentView) {
- QObject::disconnect(m_cursorPositionChangeConnection);
- m_currentView.clear();
- }
-
- m_currentView = ICore::self()->documentController()->activeTextDocumentView();
-
- if (m_currentView) {
- m_cursorPositionChangeConnection = connect(m_currentView.data(),
- &View::cursorPositionChanged, q,
- [&] (View* v, const Cursor& pos) { cursorPositionChanged(v, pos); });
- }
-}
-
-void StaticAssistantsManager::Private::checkAssistantForProblems(TopDUContext* top)
-{
- foreach (ProblemPointer problem, top->problems()) {
- if (m_currentView && m_currentView.data()->cursorPosition().line() == problem->range().start.line) {
- IAssistant::Ptr solution = problem->solutionAssistant();
- if(solution) {
- startAssistant(solution);
- m_activeProblemAssistant = true;
- break;
- }
- }
- }
-}
-
-void StaticAssistantsManager::Private::timeout()
-{
- if (!m_activeAssistant && m_currentView) {
- DUChainReadLocker lock(DUChain::lock(), 300);
- if (!lock.locked()) {
- return;
- }
-
- TopDUContext* top = DUChainUtils::standardContextForUrl(m_currentDocument.toUrl());
- if (top) {
- checkAssistantForProblems(top);
- }
- }
-}
#include "moc_staticassistantsmanager.cpp"
diff --git a/language/assistant/staticassistantsmanager.h b/language/assistant/staticassistantsmanager.h
index 3862422d63..5212c93180 100644
--- a/language/assistant/staticassistantsmanager.h
+++ b/language/assistant/staticassistantsmanager.h
@@ -1,79 +1,76 @@
/*
Copyright 2009 David Nolden
Copyright 2014 Kevin Funk
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KDEVPLATFORM_STATICASSISTANTSMANAGER_H
#define KDEVPLATFORM_STATICASSISTANTSMANAGER_H
#include
#include
#include "staticassistant.h"
#include
+#include
+#include
+#include
#include
#include
#include
class QTimer;
typedef QPointer SafeDocumentPointer;
namespace KDevelop {
class IDocument;
class DUContext;
class TopDUContext;
/**
* @brief Class managing instances of StaticAssistant
*
* Invokes the appropriate methods on registered StaticAssistant instances, such as StaticAssistant::textChanged
*
* @sa StaticAssistant::textChanged
*/
class KDEVPLATFORMLANGUAGE_EXPORT StaticAssistantsManager : public QObject
{
Q_OBJECT
public:
explicit StaticAssistantsManager(QObject* parent = nullptr);
~StaticAssistantsManager() override;
- QExplicitlySharedDataPointer activeAssistant();
-
void registerAssistant(const StaticAssistant::Ptr assistant);
void unregisterAssistant(const StaticAssistant::Ptr assistant);
- QList registeredAssistants() const;
-
-public slots:
- void hideAssistant();
+ QVector registeredAssistants() const;
+ void notifyAssistants(const IndexedString& url, const KDevelop::ReferencedTopDUContext& context);
-signals:
- void activeAssistantChanged();
+ QVector problemsForContext(const ReferencedTopDUContext& top);
private:
struct Private;
QScopedPointer const d;
- Q_PRIVATE_SLOT(d, void eventuallyStartAssistant())
};
}
#endif // KDEVPLATFORM_STATICASSISTANTSMANAGER_H
diff --git a/language/duchain/navigation/abstractdeclarationnavigationcontext.cpp b/language/duchain/navigation/abstractdeclarationnavigationcontext.cpp
index 92c98dd1bb..c9281cfcb7 100644
--- a/language/duchain/navigation/abstractdeclarationnavigationcontext.cpp
+++ b/language/duchain/navigation/abstractdeclarationnavigationcontext.cpp
@@ -1,764 +1,765 @@
/*
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() : 0)), previousContext), m_declaration(decl), m_fullBackwardSearch(false)
{
//Jump from definition to declaration if possible
FunctionDefinition* definition = dynamic_cast(m_declaration.data());
if(definition && definition->declaration())
m_declaration = DeclarationPointer(definition->declaration());
}
QString AbstractDeclarationNavigationContext::name() const
{
if(m_declaration.data())
return prettyQualifiedIdentifier(m_declaration).toString();
else
return declarationName(m_declaration);
}
QString AbstractDeclarationNavigationContext::html(bool shorten)
{
clear();
m_shorten = shorten;
modifyHtml() += "" + fontSizePrefix(shorten);
addExternalHtml(m_prefix);
if(!m_declaration.data()) {
modifyHtml() += i18n("
lost declaration
");
return currentHtml();
}
if( m_previousContext ) {
QString link = createLink( m_previousContext->name(), m_previousContext->name(), NavigationAction(m_previousContext) );
modifyHtml() += navigationHighlight(i18n("Back to %1
", link));
}
QExplicitlySharedDataPointer doc;
if( !shorten ) {
doc = ICore::self()->documentationController()->documentationForDeclaration(m_declaration.data());
const AbstractFunctionDeclaration* function = dynamic_cast(m_declaration.data());
if( function ) {
htmlFunction();
} else if( m_declaration->isTypeAlias() || m_declaration->type() || m_declaration->kind() == Declaration::Instance ) {
if( m_declaration->isTypeAlias() )
modifyHtml() += importantHighlight(QStringLiteral("typedef "));
if(m_declaration->type())
modifyHtml() += i18n("enumerator ");
AbstractType::Ptr useType = m_declaration->abstractType();
if(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);
if(auto integralType = 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() ) {
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(m_declaration->type()) {
EnumerationType::Ptr enumeration = m_declaration->type();
modifyHtml() += i18n("enumeration %1 ", identifierHighlight(m_declaration->identifier().toString().toHtmlEscaped(), m_declaration));
}
if(m_declaration->isForwardDeclaration()) {
ForwardDeclaration* forwardDec = static_cast(m_declaration.data());
Declaration* resolved = forwardDec->resolve(m_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();
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();
if( identifier.count() > 1 ) {
if( m_declaration->context() && m_declaration->context()->owner() )
{
Declaration* decl = 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( 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);
if( !access.isEmpty() )
modifyHtml() += labelHighlight(i18n("Access: %1 ", propertyHighlight(access.toHtmlEscaped())));
///@todo Enumerations
QString detailsHtml;
QStringList details = declarationDetails(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);
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()) {
modifyHtml() += labelHighlight(i18n("Status: %1 ", propertyHighlight(i18n("Deprecated"))));
}
modifyHtml() += QStringLiteral("
");
if(!shorten)
htmlAdditionalNavigation();
if( !shorten ) {
if(dynamic_cast(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 );
modifyHtml() += QStringLiteral(" ");
//modifyHtml() += "
";
if(!dynamic_cast(m_declaration.data())) {
if( FunctionDefinition* definition = FunctionDefinition::definition(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(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));
}
QByteArray declarationComment = m_declaration->comment();
if( !shorten && (!declarationComment.isEmpty() || doc) ) {
- modifyHtml() += QStringLiteral("
");
+ modifyHtml() += QStringLiteral("");
if(doc) {
QString comment = doc->description();
connect(doc.data(), &IDocumentation::descriptionChanged, this, &AbstractDeclarationNavigationContext::contentsChanged);
if(!comment.isEmpty()) {
- modifyHtml() += "
" + commentHighlight(comment);
+ 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("
");
+ modifyHtml() += QStringLiteral("
");
}
}
if(!shorten && doc) {
- modifyHtml() += "
" + i18n("Show documentation for ");
+ modifyHtml() += "" + i18n("Show documentation for ");
makeLink(prettyQualifiedName(m_declaration),
m_declaration, NavigationAction::ShowDocumentation);
+ modifyHtml() += "
";
}
//modifyHtml() += "
";
addExternalHtml(m_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());
Q_ASSERT(function);
const ClassFunctionDeclaration* classFunDecl = dynamic_cast(m_declaration.data());
const FunctionType::Ptr type = 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);
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());
}
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);
}
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());
if(classFunDecl) {
Declaration* overridden = DUChainUtils::getOverridden(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);
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();
if(classDecl) {
uint maxAllowedSteps = 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);
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;
clear();
}
return NavigationContextPointer(this);
}
void AbstractDeclarationNavigationContext::htmlClass()
{
StructureType::Ptr klass = m_declaration->abstractType().cast();
Q_ASSERT(klass);
ClassDeclaration* classDecl = dynamic_cast(klass->declaration(m_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()) ) {
//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 );
} else {
qCDebug(LANGUAGE) << "could not resolve declaration:" << idType->declarationId().isDirect() << idType->qualifiedIdentifier().toString() << "in top-context" << m_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() );
const IdentifiedType* idType = dynamic_cast( target.data() );
qCDebug(LANGUAGE) << "making type-links for" << type->toString();
if( idType && idType->declaration(m_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;
}
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/abstractnavigationcontext.cpp b/language/duchain/navigation/abstractnavigationcontext.cpp
index 87baf17697..0300f66fda 100644
--- a/language/duchain/navigation/abstractnavigationcontext.cpp
+++ b/language/duchain/navigation/abstractnavigationcontext.cpp
@@ -1,493 +1,499 @@
/*
Copyright 2007 David Nolden
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "abstractnavigationcontext.h"
#include
#include
#include "abstractdeclarationnavigationcontext.h"
#include "abstractnavigationwidget.h"
#include "usesnavigationcontext.h"
#include "../../../interfaces/icore.h"
#include "../../../interfaces/idocumentcontroller.h"
#include "../functiondeclaration.h"
#include "../namespacealiasdeclaration.h"
#include "../types/functiontype.h"
#include "../types/structuretype.h"
#include "util/debug.h"
#include
#include
#include
namespace KDevelop {
void AbstractNavigationContext::setTopContext(KDevelop::TopDUContextPointer context) {
m_topContext = context;
}
KDevelop::TopDUContextPointer AbstractNavigationContext::topContext() const {
return m_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)
{
}
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) {
//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 str = name.toHtmlEscaped();
if( m_linkCount == m_selectedLink )
str = "" + str + "";
QString ret = "" + str + "";
if( m_selectedLink == m_linkCount )
m_selectedLinkAction = action;
++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();
}
NavigationContextPointer AbstractNavigationContext::executeLink (QString link)
{
if(!m_links.contains(link))
return NavigationContextPointer(this);
return execute(m_links[link]);
}
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);
if( ctx && ctx->declaration() == action.decl )
return NavigationContextPointer( m_previousContext );
return AbstractNavigationContext::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:
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;
}
NavigationContextPointer AbstractNavigationContext::registerChild( AbstractNavigationContext* context ) {
m_children << NavigationContextPointer(context);
return 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;
return ret;
} else {
return NavigationContextPointer(this);
}
}
const int lineJump = 3;
void AbstractNavigationContext::down() {
//Make sure link-count is valid
if( m_linkCount == -1 )
html();
int fromLine = m_currentPositionLine;
if(m_selectedLink >= 0 && m_selectedLink < m_linkCount) {
if(fromLine == -1)
fromLine = m_linkLines[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;
return;
}
}
}
if(fromLine == -1)
fromLine = 0;
m_currentPositionLine = fromLine + lineJump;
if(m_currentPositionLine > m_currentLine)
m_currentPositionLine = m_currentLine;
}
void AbstractNavigationContext::up() {
//Make sure link-count is valid
if( m_linkCount == -1 )
html();
int fromLine = m_currentPositionLine;
if(m_selectedLink >= 0 && m_selectedLink < m_linkCount) {
if(fromLine == -1)
fromLine = m_linkLines[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;
return;
}
}
}
if(fromLine == -1)
fromLine = m_currentLine;
m_currentPositionLine = fromLine - lineJump;
if(m_currentPositionLine < 0)
m_currentPositionLine = 0;
}
void AbstractNavigationContext::nextLink()
{
//Make sure link-count is valid
if( m_linkCount == -1 )
html();
m_currentPositionLine = -1;
if( m_linkCount > 0 )
m_selectedLink = (m_selectedLink+1) % m_linkCount;
}
void AbstractNavigationContext::previousLink()
{
//Make sure link-count is valid
if( m_linkCount == -1 )
html();
m_currentPositionLine = -1;
if( m_linkCount > 0 ) {
--m_selectedLink;
if( m_selectedLink < 0 )
m_selectedLink += m_linkCount;
}
Q_ASSERT(m_selectedLink >= 0);
}
+int AbstractNavigationContext::linkCount() const
+{
+ return m_linkCount;
+}
+
void AbstractNavigationContext::setPrefixSuffix( const QString& prefix, const QString& suffix ) {
m_prefix = prefix;
m_suffix = suffix;
}
NavigationContextPointer AbstractNavigationContext::back() {
if(m_previousContext)
return NavigationContextPointer(m_previousContext);
else
return NavigationContextPointer(this);
}
NavigationContextPointer AbstractNavigationContext::accept() {
if( m_selectedLink >= 0 && m_selectedLink < m_linkCount )
{
NavigationAction action = m_intLinks[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) ) {
qCDebug(LANGUAGE) << "Executed unregistered link " << link << endl;
return NavigationContextPointer(this);
}
return execute(m_links[link]);
}
NavigationAction AbstractNavigationContext::currentAction() const {
return 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;
return QString();
}
bool AbstractNavigationContext::alreadyComputed() const {
return !m_currentText.isEmpty();
}
bool AbstractNavigationContext::isWidgetMaximized() const
{
return true;
}
QWidget* AbstractNavigationContext::widget() const {
return 0;
}
///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;
if(line.indexOf(newLineRegExp) != -1) {
++m_currentLine;
if(m_currentLine == m_currentPositionLine) {
m_currentText += QStringLiteral(" <-> "); // ><-> is <->
}
}
}
}
QString AbstractNavigationContext::currentHtml() const {
+
return 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 ce581c5d78..337ed6bae1 100644
--- a/language/duchain/navigation/abstractnavigationcontext.h
+++ b/language/duchain/navigation/abstractnavigationcontext.h
@@ -1,191 +1,194 @@
/*
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)
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 KDEVPLATFORMLANGUAGE_EXPORT AbstractNavigationContext : public QObject, public QSharedData
{
Q_OBJECT
public:
explicit AbstractNavigationContext( KDevelop::TopDUContextPointer topContext = KDevelop::TopDUContextPointer(), AbstractNavigationContext* previousContext = nullptr );
~AbstractNavigationContext() override {
}
void nextLink();
void previousLink();
+
+ int linkCount() const;
+
void up();
void down();
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.
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;
NavigationContextPointer executeLink(QString link);
NavigationContextPointer execute(const NavigationAction& action);
Q_SIGNALS:
void contentsChanged();
protected:
/// Returns the html font-size prefix (aka. or similar) for the given mode
QString fontSizePrefix(bool shorten) const;
/// Returns the html font-size suffix (aka. or similar) for the given mode
QString fontSizeSuffix(bool shorten) const;
virtual void setPreviousContext(AbstractNavigationContext* previous);
struct TextHandler {
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;
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
};
}
Q_DECLARE_OPERATORS_FOR_FLAGS(KDevelop::Colorizer::Formatting);
#endif
diff --git a/language/duchain/navigation/abstractnavigationwidget.cpp b/language/duchain/navigation/abstractnavigationwidget.cpp
index bb6d01dcc1..c1eaeb43c8 100644
--- a/language/duchain/navigation/abstractnavigationwidget.cpp
+++ b/language/duchain/navigation/abstractnavigationwidget.cpp
@@ -1,291 +1,301 @@
/*
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 "../declaration.h"
#include "../ducontext.h"
#include "../duchain.h"
#include "../duchainlock.h"
#include "util/debug.h"
namespace KDevelop {
AbstractNavigationWidget::AbstractNavigationWidget()
: m_browser(0), m_currentWidget(0)
{
setPalette( QApplication::palette() );
setFocusPolicy(Qt::NoFocus);
resize(100, 100);
}
const int maxNavigationWidgetWidth = 580;
QSize AbstractNavigationWidget::sizeHint() const
{
if(m_browser) {
updateIdealSize();
QSize ret = QSize(qMin(m_idealTextSize.width(), maxNavigationWidgetWidth), qMin(m_idealTextSize.height(), 300));
if(m_idealTextSize.height()>=300) { //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(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;
// 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 );
m_browser->setOpenLinks(false);
m_browser->setOpenExternalLinks(false);
QVBoxLayout* layout = new QVBoxLayout;
layout->addWidget(m_browser);
layout->setMargin(0);
setLayout(layout);
connect( m_browser.data(), &QTextBrowser::anchorClicked, this, &AbstractNavigationWidget::anchorClicked );
foreach(QWidget* w, findChildren())
w->setContextMenuPolicy(Qt::NoContextMenu);
}
AbstractNavigationWidget::~AbstractNavigationWidget() {
if(m_currentWidget)
layout()->removeWidget(m_currentWidget);
}
void AbstractNavigationWidget::setContext(NavigationContextPointer context, int initBrows)
{
if(m_browser == 0)
initBrowser(initBrows);
if(!context) {
qCDebug(LANGUAGE) << "no new context created";
return;
}
if(context == m_context && (!context || context->alreadyComputed()))
return;
if (!m_startContext)
m_startContext = m_context;
bool wasInitial = (m_context == m_startContext);
m_context = context;
update();
emit contextChanged(wasInitial, m_context == m_startContext);
emit sizeHintChanged();
}
void AbstractNavigationWidget::updateIdealSize() const {
if(m_context && !m_idealTextSize.isValid()) {
QTextDocument doc;
doc.setHtml(m_currentText);
if(doc.idealWidth() > maxNavigationWidgetWidth) {
doc.setTextWidth(maxNavigationWidgetWidth);
m_idealTextSize.setWidth(maxNavigationWidgetWidth);
}else{
m_idealTextSize.setWidth(doc.idealWidth());
}
m_idealTextSize.setHeight(doc.size().height());
}
}
void AbstractNavigationWidget::update() {
setUpdatesEnabled(false);
Q_ASSERT( m_context );
QString html = m_context->html();
if(!html.isEmpty()) {
int scrollPos = m_browser->verticalScrollBar()->value();
+ // TODO: Only show that the first time, or the first few times this context is shown?
+ html += QStringLiteral("");
+ if (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;
+
m_idealTextSize = QSize();
QSize hint = sizeHint();
if(hint.height() >= m_idealTextSize.height()) {
m_browser->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
}else{
m_browser->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
}
m_browser->verticalScrollBar()->setValue(scrollPos);
m_browser->scrollToAnchor(QStringLiteral("currentPosition"));
m_browser->show();
}else{
m_browser->hide();
}
if(m_currentWidget) {
layout()->removeWidget(m_currentWidget);
m_currentWidget->setParent(0);
}
m_currentWidget = m_context->widget();
m_browser->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_browser->setMaximumHeight(10000);
if(m_currentWidget) {
if (m_currentWidget->metaObject()
->indexOfSignal(QMetaObject::normalizedSignature("navigateDeclaration(KDevelop::IndexedDeclaration)")) != -1)
{
connect(m_currentWidget, SIGNAL(navigateDeclaration(KDevelop::IndexedDeclaration)),
this, SLOT(navigateDeclaration(KDevelop::IndexedDeclaration)));
}
layout()->addWidget(m_currentWidget);
if(m_context->isWidgetMaximized()) {
//Leave unused room to the widget
m_browser->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
m_browser->setMaximumHeight(m_idealTextSize.height());
}
}
setUpdatesEnabled(true);
}
NavigationContextPointer AbstractNavigationWidget::context() {
return m_context;
}
void AbstractNavigationWidget::navigateDeclaration(KDevelop::IndexedDeclaration decl) {
DUChainReadLocker lock( DUChain::lock() );
setContext(m_context->accept(decl));
}
void AbstractNavigationWidget::anchorClicked(const QUrl& url) {
DUChainReadLocker lock( DUChain::lock() );
//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;
NavigationContextPointer nextContext = m_context->acceptLink(url.toString());
if(thisPtr)
setContext( nextContext );
}
void AbstractNavigationWidget::next() {
DUChainReadLocker lock( DUChain::lock() );
Q_ASSERT( m_context );
m_context->nextLink();
update();
}
void AbstractNavigationWidget::previous() {
DUChainReadLocker lock( DUChain::lock() );
Q_ASSERT( m_context );
m_context->previousLink();
update();
}
void AbstractNavigationWidget::accept() {
DUChainReadLocker lock( DUChain::lock() );
Q_ASSERT( m_context );
QPointer thisPtr(this);
NavigationContextPointer oldContext = m_context;
NavigationContextPointer nextContext = m_context->accept();
if(thisPtr)
setContext( nextContext );
}
void AbstractNavigationWidget::back() {
DUChainReadLocker lock( DUChain::lock() );
QPointer thisPtr(this);
NavigationContextPointer oldContext = m_context;
NavigationContextPointer nextContext = m_context->back();
if(thisPtr)
setContext( nextContext );
}
void AbstractNavigationWidget::up() {
DUChainReadLocker lock( DUChain::lock() );
m_context->up();
update();
}
void AbstractNavigationWidget::down() {
DUChainReadLocker lock( DUChain::lock() );
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();
return;
}
}
diff --git a/language/duchain/navigation/problemnavigationcontext.cpp b/language/duchain/navigation/problemnavigationcontext.cpp
index a292f722e4..6a0d306d7a 100644
--- a/language/duchain/navigation/problemnavigationcontext.cpp
+++ b/language/duchain/navigation/problemnavigationcontext.cpp
@@ -1,128 +1,222 @@
/*
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
+#include
+
+#include "util/debug.h"
#include
#include
+#include
+#include
#include
#include
#include
#include
#include
-#include
#include
+#include
+
using namespace KDevelop;
-ProblemNavigationContext::ProblemNavigationContext(const IProblem::Ptr& problem)
+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");
+ }
+ 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 IProblem::Ptr& problem, const Flags flags)
: m_problem(problem)
+ , m_flags(flags)
, m_widget(nullptr)
{
- QExplicitlySharedDataPointer< IAssistant > solution = problem->solutionAssistant();
- if(solution && !solution->actions().isEmpty()) {
- m_widget = new QWidget;
- QHBoxLayout* layout = new QHBoxLayout(m_widget);
- RichTextPushButton* button = new RichTextPushButton;
-// button->setPopupMode(QToolButton::InstantPopup);
- if(!solution->title().isEmpty())
- button->setHtml(i18n("Solve: %1", solution->title()));
- else
- button->setHtml(i18n("Solve"));
-
- QMenu* menu = new QMenu;
- menu->setFocusPolicy(Qt::NoFocus);
- foreach(IAssistantAction::Ptr action, solution->actions()) {
- menu->addAction(action->toKAction(this));
- }
- button->setMenu(menu);
-
- layout->addWidget(button);
- layout->setAlignment(button, Qt::AlignLeft);
- m_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
- }
}
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::html(bool shorten)
{
clear();
m_shorten = shorten;
- modifyHtml() += QStringLiteral("");
+ auto iconPath = iconForSeverity(m_problem->severity());
- modifyHtml() += i18n("Problem in %1:
", m_problem->sourceString());
- modifyHtml() += m_problem->description().toHtmlEscaped();
+ modifyHtml() += QStringLiteral("
");
+
+ modifyHtml() += QStringLiteral("%1 | ").arg(htmlImg(iconPath, KIconLoader::Panel));
+
+ // BEGIN: right column
+ modifyHtml() += QStringLiteral("");
+
+ modifyHtml() += i18n("Problem in %1", m_problem->sourceString());
modifyHtml() += QStringLiteral(" ");
- modifyHtml() += "" + m_problem->explanation().toHtmlEscaped() + "";
+
+ if (m_flags & ShowLocation) {
+ const auto duchainProblem = dynamic_cast(m_problem.data());
+ if (duchainProblem) {
+ modifyHtml() += labelHighlight(i18n("Location: "));
+ makeLink(QStringLiteral("%1 :%2")
+ .arg(duchainProblem->finalLocation().document.toUrl().fileName())
+ .arg(duchainProblem->rangeInCurrentRevision().start().line() + 1),
+ QString(),
+ NavigationAction(duchainProblem->finalLocation().document.toUrl(), duchainProblem->finalLocation().start())
+ );
+ modifyHtml() += QStringLiteral(" ");
+ }
+ }
+
+ modifyHtml() += m_problem->description().toHtmlEscaped();
+ if ( !m_problem->explanation().isEmpty() ) {
+ modifyHtml() += "" + m_problem->explanation().toHtmlEscaped() + " ";
+ }
+
+ modifyHtml() += QStringLiteral(" | ");
+ // END: right column
+
+ modifyHtml() += QStringLiteral("
");
const QVector diagnostics = m_problem->diagnostics();
if (!diagnostics.isEmpty()) {
- modifyHtml() += QStringLiteral("
");
DUChainReadLocker lock;
for (auto diagnostic : diagnostics) {
+ modifyHtml() += QStringLiteral("");
modifyHtml() += labelHighlight(QStringLiteral("%1: ").arg(diagnostic->severityString()));
modifyHtml() += diagnostic->description();
const DocumentRange range = diagnostic->finalLocation();
Declaration* declaration = DUChainUtils::itemUnderCursor(range.document.toUrl(), range.start());
if (declaration) {
- modifyHtml() += i18n("
See: ");
+ 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: ");
+ 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("
");
+ modifyHtml() += QStringLiteral("
");
}
}
- modifyHtml() += QStringLiteral("");
+ if (!m_cachedAssistant) {
+ m_cachedAssistant = m_problem->solutionAssistant();
+ }
+ auto assistant = m_cachedAssistant;
+ if (assistant && !assistant->actions().isEmpty()) {
+ modifyHtml() += QString::fromLatin1("").arg("#b3d4ff");
+
+ modifyHtml() += QStringLiteral("%1 | ").arg(htmlImg(QStringLiteral("dialog-ok-apply"), KIconLoader::Panel));
+ int index = 0;
+ foreach (auto assistantAction, assistant->actions()) {
+ if (index != 0) {
+ modifyHtml() += " ";
+ }
+ makeLink(i18n("Solution (%1)", index + 1), KEY_INVOKE_ACTION(index),
+ NavigationAction(KEY_INVOKE_ACTION(index)));
+ modifyHtml() += ": " + assistantAction->description().toHtmlEscaped();
+ ++index;
+ }
+ modifyHtml() += " |
";
+ modifyHtml() += QStringLiteral("
");
+ }
+
return currentHtml();
}
+
+NavigationContextPointer ProblemNavigationContext::executeKeyAction(QString key)
+{
+ auto assistant = m_cachedAssistant;
+ if (!assistant)
+ return {};
+ if (key.startsWith(QLatin1String("invoke_action_"))) {
+ const auto index = key.replace(QLatin1String("invoke_action_"), QString()).toInt();
+ executeAction(index);
+ }
+
+ return {};
+}
+
+void ProblemNavigationContext::executeAction(int index)
+{
+ auto assistant = m_problem->solutionAssistant();
+ if (!assistant)
+ return;
+
+ auto action = assistant->actions().value(index);
+ 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/problemnavigationcontext.h b/language/duchain/navigation/problemnavigationcontext.h
index 02bac5fb93..35c3318a0c 100644
--- a/language/duchain/navigation/problemnavigationcontext.h
+++ b/language/duchain/navigation/problemnavigationcontext.h
@@ -1,49 +1,64 @@
/*
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.
*/
#ifndef KDEVPLATFORM_PROBLEMNAVIGATIONCONTEXT_H
#define KDEVPLATFORM_PROBLEMNAVIGATIONCONTEXT_H
#include
+#include
+
#include
#include
#include
namespace KDevelop {
class KDEVPLATFORMLANGUAGE_EXPORT ProblemNavigationContext : public AbstractNavigationContext
{
Q_OBJECT
public:
- explicit ProblemNavigationContext(const IProblem::Ptr& problem);
+ enum Flag {
+ NoFlag = 0,
+ ShowLocation = 1 << 0,
+ };
+ Q_DECLARE_FLAGS(Flags, Flag)
+
+ explicit ProblemNavigationContext(const IProblem::Ptr& problem, const Flags flags = {});
~ProblemNavigationContext() override;
QString name() const override;
QString html(bool shorten = false) override;
QWidget* widget() const override;
bool isWidgetMaximized() const override;
+ NavigationContextPointer executeKeyAction(QString key) override;
+
+public slots:
+ void executeAction(int index); // TODO: Add API in base class?
+
private:
IProblem::Ptr m_problem;
+ Flags m_flags;
QPointer m_widget;
+ IAssistant::Ptr m_cachedAssistant; // cache assistant, calling IAssistant::solutionAssistant() might be expensive
};
}
#endif // KDEVPLATFORM_PROBLEMNAVIGATIONCONTEXT_H
diff --git a/language/duchain/problem.h b/language/duchain/problem.h
index 3bb556766f..ae58876b4e 100644
--- a/language/duchain/problem.h
+++ b/language/duchain/problem.h
@@ -1,247 +1,261 @@
/* This file is part of KDevelop
Copyright 2007 Hamish Rodda
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KDEVPLATFORM_PROBLEM_H
#define KDEVPLATFORM_PROBLEM_H
#include
#include
#include "../editor/documentrange.h"
#include
#include "duchainbase.h"
#include
#include "indexedtopducontext.h"
#include
+#include
namespace KDevelop
{
class IAssistant;
class Problem;
using ProblemPointer = QExplicitlySharedDataPointer;
/**
* Represents a problem only by its index within the top-context
*
* Fixme: share code with the other LocalIndexed* classes
*/
class KDEVPLATFORMLANGUAGE_EXPORT LocalIndexedProblem
{
public:
LocalIndexedProblem(const ProblemPointer& problem, const TopDUContext* top);
explicit LocalIndexedProblem(uint index = 0)
: m_index(index)
{}
/**
* \note Duchain must be read locked
*/
ProblemPointer data(const TopDUContext* top) const;
bool operator==(const LocalIndexedProblem& rhs) const
{
return m_index == rhs.m_index;
}
bool isValid() const
{
return m_index;
}
/**
* Index of the Declaration within the top context
*/
uint localIndex() const
{
return m_index;
}
private:
uint m_index;
};
KDEVPLATFORMLANGUAGE_EXPORT DECLARE_LIST_MEMBER_HASH(ProblemData, diagnostics, LocalIndexedProblem)
class KDEVPLATFORMLANGUAGE_EXPORT ProblemData : public DUChainBaseData
{
public:
ProblemData()
: source(IProblem::Unknown)
, severity(IProblem::Error)
{
initializeAppendedLists();
}
ProblemData(const ProblemData& rhs)
: DUChainBaseData(rhs)
, source(rhs.source)
, severity(rhs.severity)
, url(rhs.url)
, description(rhs.description)
, explanation(rhs.explanation)
{
initializeAppendedLists();
copyListsFrom(rhs);
}
~ProblemData()
{
freeAppendedLists();
}
IProblem::Source source;
IProblem::Severity severity;
IndexedString url;
IndexedString description;
IndexedString explanation;
START_APPENDED_LISTS_BASE(ProblemData, DUChainBaseData);
APPENDED_LIST_FIRST(ProblemData, LocalIndexedProblem, diagnostics);
END_APPENDED_LISTS(ProblemData, diagnostics);
};
/**
* An object representing a problem in preprocessing, parsing, definition-use chain compilation, etc.
*
* You should always use ProblemPointer, because Problem may be subclassed.
* The subclass would be lost while copying.
*
* @warning Access to problems must be serialized through DUChainLock.
*/
class KDEVPLATFORMLANGUAGE_EXPORT Problem : public DUChainBase, public IProblem
{
public:
using Ptr = QExplicitlySharedDataPointer;
Problem();
explicit Problem(ProblemData& data);
~Problem();
Source source() const override;
void setSource(IProblem::Source source) override;
/**
* Returns a string version of the problem source
*/
QString sourceString() const override;
TopDUContext* topContext() const override;
KDevelop::IndexedString url() const override;
/**
* Location where this problem occurred
* @warning Must only be called from the foreground
* */
DocumentRange finalLocation() const override;
void setFinalLocation(const DocumentRange& location) override;
/**
* Returns child diagnostics of this particular problem
*
* Example:
* @code
* void foo(unsigned int);
* void foo(const char*);
* int main() { foo(0); }
* @endcode
*
* => foo(0) is ambigous. This will give us a ProblemPointer pointing to 'foo(0)'.
*
* Additionally, @p diagnostics may return the two locations to the ambiguous overloads,
* with descriptions such as 'test.cpp:1: candidate : ...'
*/
void clearDiagnostics() override;
QVector diagnostics() const override;
void setDiagnostics(const QVector &diagnostics) override;
void addDiagnostic(const IProblem::Ptr &diagnostic) override;
/**
* A brief description of the problem.
*/
QString description() const override;
void setDescription(const QString& description) override;
/**
* A (detailed) explanation of why the problem occurred.
*/
QString explanation() const override;
void setExplanation(const QString& explanation) override;
/**
* Get the severity of this problem.
* This is used for example to decide for a highlighting color.
*
* @see setSeverity()
*/
Severity severity() const override;
/**
* Set the severity of this problem.
*/
void setSeverity(Severity severity) override;
/**
* Returns a string representation of the severity.
*/
QString severityString() const override;
/**
* If this problem can be solved, this may return an assistant for the solution.
*/
virtual QExplicitlySharedDataPointer solutionAssistant() const override;
enum {
Identity = 15
};
/**
* Returns a string representation of this problem, useful for debugging.
*/
virtual QString toString() const;
private:
void rebuildDynamicData(DUContext* parent, uint ownIndex) override;
Q_DISABLE_COPY(Problem)
DUCHAIN_DECLARE_DATA(Problem)
friend class TopDUContext;
friend class TopDUContextDynamicData;
friend class LocalIndexedProblem;
//BEGIN dynamic data
TopDUContextPointer m_topContext;
mutable QList m_diagnostics;
uint m_indexInTopContext;
//END dynamic data
};
+class KDEVPLATFORMLANGUAGE_EXPORT StaticAssistantProblem : public KDevelop::Problem {
+ public:
+ KDevelop::IAssistant::Ptr solutionAssistant() const override {
+ return m_solution;
+ }
+ void setSolutionAssistant(KDevelop::IAssistant::Ptr p) {
+ m_solution = p;
+ }
+
+ private:
+ KDevelop::IAssistant::Ptr m_solution;
+};
+
}
Q_DECLARE_TYPEINFO(KDevelop::LocalIndexedProblem, Q_MOVABLE_TYPE);
KDEVPLATFORMLANGUAGE_EXPORT QDebug operator<<(QDebug s, const KDevelop::Problem& problem);
KDEVPLATFORMLANGUAGE_EXPORT QDebug operator<<(QDebug s, const KDevelop::ProblemPointer& problem);
#endif // KDEVPLATFORM_PROBLEM_H
diff --git a/plugins/contextbrowser/browsemanager.cpp b/plugins/contextbrowser/browsemanager.cpp
index bd615f0a0c..7cd7831724 100644
--- a/plugins/contextbrowser/browsemanager.cpp
+++ b/plugins/contextbrowser/browsemanager.cpp
@@ -1,331 +1,341 @@
/*
* This file is part of KDevelop
*
* Copyright 2008 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 "browsemanager.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "contextbrowserview.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "contextbrowser.h"
#include "debug.h"
using namespace KDevelop;
using namespace KTextEditor;
EditorViewWatcher::EditorViewWatcher(QObject* parent)
: QObject(parent)
{
connect(ICore::self()->documentController(), &IDocumentController::textDocumentCreated, this, &EditorViewWatcher::documentCreated);
foreach(KDevelop::IDocument* document, ICore::self()->documentController()->openDocuments())
documentCreated(document);
}
void EditorViewWatcher::documentCreated( KDevelop::IDocument* document ) {
KTextEditor::Document* textDocument = document->textDocument();
if(textDocument) {
connect(textDocument, &Document::viewCreated, this, &EditorViewWatcher::viewCreated);
foreach(KTextEditor::View* view, textDocument->views()) {
Q_ASSERT(view->parentWidget());
addViewInternal(view);
}
}
}
void EditorViewWatcher::addViewInternal(KTextEditor::View* view) {
m_views << view;
viewAdded(view);
connect(view, &View::destroyed, this, &EditorViewWatcher::viewDestroyed);
}
void EditorViewWatcher::viewAdded(KTextEditor::View*) {
}
void EditorViewWatcher::viewDestroyed(QObject* view) {
m_views.removeAll(static_cast(view));
}
void EditorViewWatcher::viewCreated(KTextEditor::Document* /*doc*/, KTextEditor::View* view) {
Q_ASSERT(view->parentWidget());
addViewInternal(view);
}
QList EditorViewWatcher::allViews() {
return m_views;
}
void BrowseManager::eventuallyStartDelayedBrowsing() {
avoidMenuAltFocus();
if(m_browsingByKey == Qt::Key_Alt && m_browsingStartedInView)
emit startDelayedBrowsing(m_browsingStartedInView);
}
BrowseManager::BrowseManager(ContextBrowserPlugin* controller)
: QObject(controller)
, m_plugin(controller)
, m_browsing(false)
, m_browsingByKey(0)
, m_watcher(this)
{
m_delayedBrowsingTimer = new QTimer(this);
m_delayedBrowsingTimer->setSingleShot(true);
connect(m_delayedBrowsingTimer, &QTimer::timeout, this, &BrowseManager::eventuallyStartDelayedBrowsing);
foreach(KTextEditor::View* view, m_watcher.allViews())
viewAdded(view);
}
KTextEditor::View* viewFromWidget(QWidget* widget) {
if(!widget)
return 0;
KTextEditor::View* view = qobject_cast(widget);
if(view)
return view;
else
return viewFromWidget(widget->parentWidget());
}
BrowseManager::JumpLocation BrowseManager::determineJumpLoc(KTextEditor::Cursor textCursor, const QUrl& viewUrl) const {
// @todo find out why this is needed, fix the code in kate
if (textCursor.column() > 0) {
textCursor.setColumn(textCursor.column() - 1);
}
// Step 1: Look for a special language object(Macro, included header, etc.)
foreach (const auto& language, ICore::self()->languageController()->languagesForUrl(viewUrl)) {
auto jumpTo = language->specialLanguageObjectJumpCursor(viewUrl, textCursor);
if (jumpTo.first.isValid() && jumpTo.second.isValid()) {
return {jumpTo};
}
}
// Step 2: Look for a declaration/use
DUChainReadLocker lock;
// Jump to definition by default, unless a definition itself was selected,
// in which case jump to declaration.
if (auto selectedDeclaration = DUChainUtils::itemUnderCursor(viewUrl, textCursor)) {
auto jumpDestination = selectedDeclaration;
if (selectedDeclaration->isDefinition()) {
// A definition was clicked directly - jump to declaration instead.
if (auto declaration = DUChainUtils::declarationForDefinition(selectedDeclaration)) {
jumpDestination = declaration;
}
} else if (selectedDeclaration == DUChainUtils::declarationForDefinition(selectedDeclaration)) {
// Clicked the declaration - jump to definition
if (auto definition = FunctionDefinition::definition(selectedDeclaration)) {
jumpDestination = definition;
}
}
return {{jumpDestination->url().toUrl(), jumpDestination->rangeInCurrentRevision().start()}};
}
return {};
}
bool BrowseManager::eventFilter(QObject * watched, QEvent * event) {
QWidget* widget = qobject_cast(watched);
Q_ASSERT(widget);
QKeyEvent* keyEvent = dynamic_cast(event);
const int browseKey = Qt::Key_Control;
const int magicModifier = Qt::Key_Alt;
KTextEditor::View* view = viewFromWidget(widget);
//Eventually start key-browsing
if(keyEvent && (keyEvent->key() == browseKey || keyEvent->key() == magicModifier) && !m_browsingByKey && keyEvent->type() == QEvent::KeyPress) {
m_delayedBrowsingTimer->start(300); // always start the timer, to get consistent behavior regarding the ALT key and the menu activation
m_browsingByKey = keyEvent->key();
if(!view) {
return false;
}
if(keyEvent->key() == magicModifier) {
if(dynamic_cast(view) && dynamic_cast(view)->isCompletionActive())
{
//Completion is active.
avoidMenuAltFocus();
}else{
m_browsingStartedInView = view;
}
}
}
+ if (keyEvent && m_browsingByKey && m_browsingStartedInView && keyEvent->type() == QEvent::KeyPress) {
+ if (keyEvent->key() >= Qt::Key_1 && keyEvent->key() <= Qt::Key_9) {
+ // user wants to trigger an action in the code browser
+ const int index = keyEvent->key() - Qt::Key_1;
+ emit invokeAction(index);
+ stopDelayedBrowsing();
+ return true;
+ }
+ }
+
if(!view) {
return false;
}
QFocusEvent* focusEvent = dynamic_cast(event);
//Eventually stop key-browsing
if((keyEvent && m_browsingByKey && keyEvent->key() == m_browsingByKey && keyEvent->type() == QEvent::KeyRelease)
|| (focusEvent && focusEvent->lostFocus()) || event->type() == QEvent::WindowDeactivate) {
m_browsingByKey = 0;
emit stopDelayedBrowsing();
}
QMouseEvent* mouseEvent = dynamic_cast(event);
if(mouseEvent) {
if (mouseEvent->type() == QEvent::MouseButtonPress && mouseEvent->button() == Qt::XButton1) {
m_plugin->historyPrevious();
return true;
}
if (mouseEvent->type() == QEvent::MouseButtonPress && mouseEvent->button() == Qt::XButton2) {
m_plugin->historyNext();
return true;
}
}
if(!m_browsing && !m_browsingByKey) {
resetChangedCursor();
return false;
}
if(mouseEvent) {
QPoint coordinatesInView = widget->mapTo(view, mouseEvent->pos());
KTextEditor::Cursor textCursor = view->coordinatesToCursor(coordinatesInView);
if (textCursor.isValid()) {
JumpLocation jumpTo = determineJumpLoc(textCursor, view->document()->url());
if (jumpTo.isValid()) {
if(mouseEvent->button() == Qt::LeftButton) {
if(mouseEvent->type() == QEvent::MouseButtonPress) {
m_buttonPressPosition = textCursor;
// view->setCursorPosition(textCursor);
// return false;
}else if(mouseEvent->type() == QEvent::MouseButtonRelease && textCursor == m_buttonPressPosition) {
ICore::self()->documentController()->openDocument(jumpTo.url, jumpTo.cursor);
// event->accept();
// return true;
}
}else if(mouseEvent->type() == QEvent::MouseMove) {
//Make the cursor a "hand"
setHandCursor(widget);
return false;
}
}
}
resetChangedCursor();
}
return false;
}
void BrowseManager::resetChangedCursor() {
QMap, QCursor> cursors = m_oldCursors;
m_oldCursors.clear();
for(QMap, QCursor>::iterator it = cursors.begin(); it != cursors.end(); ++it)
if(it.key())
it.key()->setCursor(QCursor(Qt::IBeamCursor));
}
void BrowseManager::setHandCursor(QWidget* widget) {
if(m_oldCursors.contains(widget))
return; //Nothing to do
m_oldCursors[widget] = widget->cursor();
widget->setCursor(QCursor(Qt::PointingHandCursor));
}
void BrowseManager::avoidMenuAltFocus() {
// send an invalid key event to the main menu bar. The menu bar will
// stop listening when observing another key than ALT between the press
// and the release.
QKeyEvent event1(QEvent::KeyPress, 0, Qt::NoModifier);
QApplication::sendEvent(ICore::self()->uiController()->activeMainWindow()->menuBar(), &event1);
QKeyEvent event2(QEvent::KeyRelease, 0, Qt::NoModifier);
QApplication::sendEvent(ICore::self()->uiController()->activeMainWindow()->menuBar(), &event2);
}
void BrowseManager::applyEventFilter(QWidget* object, bool install) {
if(install)
object->installEventFilter(this);
else
object->removeEventFilter(this);
foreach(QObject* child, object->children())
if(qobject_cast(child))
applyEventFilter(qobject_cast(child), install);
}
void BrowseManager::viewAdded(KTextEditor::View* view) {
applyEventFilter(view, true);
//We need to listen for cursorPositionChanged, to clear the shift-detector. The problem: Kate listens for the arrow-keys using shortcuts,
//so those keys are not passed to the event-filter
// can't use new signal/slot syntax here, these signals are only defined in KateView
// TODO: should we really depend on kate internals here?
connect(view, SIGNAL(navigateLeft()), m_plugin, SLOT(navigateLeft()));
connect(view, SIGNAL(navigateRight()), m_plugin, SLOT(navigateRight()));
connect(view, SIGNAL(navigateUp()), m_plugin, SLOT(navigateUp()));
connect(view, SIGNAL(navigateDown()), m_plugin, SLOT(navigateDown()));
connect(view, SIGNAL(navigateAccept()), m_plugin, SLOT(navigateAccept()));
connect(view, SIGNAL(navigateBack()), m_plugin, SLOT(navigateBack()));
}
void Watcher::viewAdded(KTextEditor::View* view) {
m_manager->viewAdded(view);
}
void BrowseManager::setBrowsing(bool enabled) {
if(enabled == m_browsing)
return;
m_browsing = enabled;
//This collects all the views
if(enabled) {
qCDebug(PLUGIN_CONTEXTBROWSER) << "Enabled browsing-mode";
}else{
qCDebug(PLUGIN_CONTEXTBROWSER) << "Disabled browsing-mode";
resetChangedCursor();
}
}
Watcher::Watcher(BrowseManager* manager)
: EditorViewWatcher(manager), m_manager(manager) {
foreach(KTextEditor::View* view, allViews())
m_manager->applyEventFilter(view, true);
}
diff --git a/plugins/contextbrowser/browsemanager.h b/plugins/contextbrowser/browsemanager.h
index e7594a9504..2ccf313126 100644
--- a/plugins/contextbrowser/browsemanager.h
+++ b/plugins/contextbrowser/browsemanager.h
@@ -1,125 +1,126 @@
/*
* This file is part of KDevelop
*
* Copyright 2008 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.
*/
#ifndef KDEVPLATFORM_PLUGIN_BROWSEMANAGER_H
#define KDEVPLATFORM_PLUGIN_BROWSEMANAGER_H
#include
#include
#include
#include
#include
#include
#include
class QWidget;
namespace KTextEditor {
class View;
class Document;
}
namespace KDevelop {
class IDocument;
}
class EditorViewWatcher : public QObject {
Q_OBJECT
public:
///@param sameWindow If this is true, only views that are child of the same window as the given widget are registered
explicit EditorViewWatcher(QObject* parent = nullptr);
QList allViews();
private:
///Called for every added view. Reimplement this to catch them.
virtual void viewAdded(KTextEditor::View*);
private slots:
void viewDestroyed(QObject* view);
void viewCreated(KTextEditor::Document*, KTextEditor::View*);
void documentCreated( KDevelop::IDocument* document );
private:
void addViewInternal(KTextEditor::View* view);
QList m_views;
};
class ContextBrowserPlugin;
class BrowseManager;
class Watcher : public EditorViewWatcher {
Q_OBJECT
public:
explicit Watcher(BrowseManager* manager);
void viewAdded(KTextEditor::View*) override;
private:
BrowseManager* m_manager;
};
/**
* Integrates the context-browser with the editor views, by listening for navigation events, and implementing html-like source browsing
*/
class BrowseManager : public QObject {
Q_OBJECT
public:
explicit BrowseManager(ContextBrowserPlugin* controller);
void viewAdded(KTextEditor::View* view);
//Installs/uninstalls the event-filter
void applyEventFilter(QWidget* object, bool install);
Q_SIGNALS:
//Emitted when browsing was started using the magic-modifier
void startDelayedBrowsing(KTextEditor::View* view);
void stopDelayedBrowsing();
+ void invokeAction(int index);
public slots:
///Enabled/disables the browsing mode
void setBrowsing(bool);
private slots:
void eventuallyStartDelayedBrowsing();
private:
struct JumpLocation
{
QUrl url;
KTextEditor::Cursor cursor;
JumpLocation(const QPair& pair = {})
: url(pair.first)
, cursor(pair.second)
{}
bool isValid() const { return url.isValid() && cursor.isValid(); }
};
void resetChangedCursor();
JumpLocation determineJumpLoc(KTextEditor::Cursor textCursor, const QUrl& viewUrl) const;
void setHandCursor(QWidget* widget);
void avoidMenuAltFocus();
bool eventFilter(QObject * watched, QEvent * event) override ;
ContextBrowserPlugin* m_plugin;
bool m_browsing;
int m_browsingByKey; //Whether the browsing was started because of a key
Watcher m_watcher;
//Maps widgets to their previously set cursors
QMap, QCursor> m_oldCursors;
QTimer* m_delayedBrowsingTimer;
QPointer m_browsingStartedInView;
KTextEditor::Cursor m_buttonPressPosition;
};
#endif
diff --git a/plugins/contextbrowser/contextbrowser.cpp b/plugins/contextbrowser/contextbrowser.cpp
index 0f215cf4a8..629f261bf4 100644
--- a/plugins/contextbrowser/contextbrowser.cpp
+++ b/plugins/contextbrowser/contextbrowser.cpp
@@ -1,1357 +1,1471 @@
/*
* 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
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 0;
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())));
return DeclarationPointer(decl);
}
}
class ContextBrowserViewFactory: public KDevelop::IToolViewFactory
{
public:
ContextBrowserViewFactory(ContextBrowserPlugin *plugin): m_plugin(plugin) {}
QWidget* create(QWidget *parent = 0) 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->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->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)
{
KDEV_USE_EXTENSION_INTERFACE( IContextBrowser )
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( 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::ExtensionGroup, 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(
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) {
qWarning() << "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 = 0;
m_currentNavigationWidget = 0;
+ m_currentToolTipProblem = {};
+ m_currentToolTipDeclaration = {};
}
}
-void ContextBrowserPlugin::showToolTip(KTextEditor::View* view, KTextEditor::Cursor position) {
+static ProblemPointer findProblemUnderCursor(const TopDUContext* topContext, KTextEditor::Cursor position)
+{
+ foreach (auto problem, topContext->problems()) {
+ if (problem->rangeInCurrentRevision().contains(position)) {
+ return problem;
+ }
+ }
- 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
+ return {};
+}
+
+static ProblemPointer findProblemCloseToCursor(const TopDUContext* topContext, KTextEditor::Cursor position, KTextEditor::View* view)
+{
+ auto problems = topContext->problems();
+ if (problems.isEmpty())
+ return {};
+
+ auto closestProblem = std::min_element(problems.constBegin(), problems.constEnd(),
+ [position](const ProblemPointer& a, const ProblemPointer& b) {
+ const auto aRange = a->rangeInCurrentRevision();
+ const auto bRange = b->rangeInCurrentRevision();
+
+ 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());
+ });
+
+ auto r = (*closestProblem)->rangeInCurrentRevision();
+ if (!r.contains(position)) {
+ if (r.start().line() == position.line() || r.end().line() == position.line()) {
+ // problem is on the same line, let's use it
+ return *closestProblem;
+ }
+ // if not, only show it in case there's only whitespace between the current cursor pos and the problem
+ auto dist = position < r.start() ? KTextEditor::Range(position, r.start()) : KTextEditor::Range(r.end(), position);
+ auto textBetween = view->document()->text(dist);
+ auto isSpace = std::all_of(textBetween.begin(), textBetween.end(), [](QChar c) { return c.isSpace(); });
+ if (!isSpace) {
+ return {};
+ }
+ }
+
+ return *closestProblem;
+}
+
+QWidget* ContextBrowserPlugin::navigationWidgetForPosition(KTextEditor::View* view, KTextEditor::Cursor position)
+{
QUrl viewUrl = view->document()->url();
auto languages = ICore::self()->languageController()->languagesForUrl(viewUrl);
- QWidget* navigationWidget = 0;
- {
- DUChainReadLocker lock(DUChain::lock());
- foreach (const auto language, languages) {
- auto widget = language->specialLanguageObjectNavigationWidget(viewUrl, KTextEditor::Cursor(position));
- navigationWidget = qobject_cast(widget);
- if(navigationWidget)
- break;
- }
+ 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;
+ }
- if(!navigationWidget) {
- Declaration* decl = DUChainUtils::declarationForDefinition( DUChainUtils::itemUnderCursor(viewUrl, KTextEditor::Cursor(position)) );
- if (decl && decl->kind() == Declaration::Alias) {
- AliasDeclaration* alias = dynamic_cast(decl);
- Q_ASSERT(alias);
- DUChainReadLocker lock;
- decl = alias->aliasedDeclaration().declaration();
+ TopDUContext* topContext = DUChainUtils::standardContextForUrl(view->document()->url());
+ if (topContext) {
+ // first pass: find problems under the cursor
+ const auto problem = findProblemUnderCursor(topContext, position);
+ if (problem) {
+ if (problem == m_currentToolTipProblem && m_currentToolTip) {
+ return nullptr;
}
- if(decl) {
- if(m_currentToolTipDeclaration == IndexedDeclaration(decl) && m_currentToolTip)
- return;
- m_currentToolTipDeclaration = IndexedDeclaration(decl);
- navigationWidget = decl->context()->createNavigationWidget(decl, DUChainUtils::standardContextForUrl(viewUrl));
+
+ m_currentToolTipProblem = problem;
+ auto widget = new AbstractNavigationWidget;
+ auto context = new ProblemNavigationContext(problem);
+ context->setTopContext(TopDUContextPointer(topContext));
+ widget->setContext(NavigationContextPointer(context));
+ return widget;
+ }
+ }
+
+ auto declUnderCursor = DUChainUtils::itemUnderCursor(viewUrl, position);
+ 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 problem = findProblemCloseToCursor(topContext, position, view);
+ if (problem) {
+ if (problem == m_currentToolTipProblem && m_currentToolTip) {
+ return nullptr;
}
+
+ m_currentToolTipProblem = problem;
+ auto widget = new AbstractNavigationWidget;
+ // since the problem is not under cursor: show location
+ widget->setContext(NavigationContextPointer(new ProblemNavigationContext(problem, 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 = 0;
m_currentNavigationWidget = 0;
}
KDevelop::NavigationToolTip* tooltip = new KDevelop::NavigationToolTip(view, view->mapToGlobal(view->cursorToCoordinate(position)) + QPoint(20, 40), navigationWidget);
KTextEditor::Range itemRange;
{
DUChainReadLocker lock;
- itemRange = DUChainUtils::itemRangeUnderCursor(viewUrl, KTextEditor::Cursor(position));
+ auto viewUrl = view->document()->url();
+ itemRange = DUChainUtils::itemRangeUnderCursor(viewUrl, position);
}
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 highlightedUseAttribute() {
static Attribute::Ptr standardAttribute = Attribute::Ptr();
if( !standardAttribute ) {
standardAttribute= Attribute::Ptr( new Attribute() );
standardAttribute->setBackgroundFillWhitespace(true);
// mixing (255, 255, 0, 100) with white yields this:
standardAttribute->setBackground(QColor(251, 250, 150));
// force a foreground color to overwrite default Kate highlighting, i.e. of Q_OBJECT or similar
// foreground color could change, hence apply it everytime
standardAttribute->setForeground(QColor(0, 0, 0, 255)); //Don't use alpha here, as kate uses the alpha only to blend with the document background color
}
return standardAttribute;
}
Attribute::Ptr highlightedSpecialObjectAttribute() {
static Attribute::Ptr standardAttribute = Attribute::Ptr();
if( !standardAttribute ) {
standardAttribute = Attribute::Ptr( new Attribute() );
standardAttribute->setBackgroundFillWhitespace(true);
// mixing (90, 255, 0, 100) with white yields this:
standardAttribute->setBackground(QColor(190, 255, 155));
// force a foreground color to overwrite default Kate highlighting, i.e. of Q_OBJECT or similar
// foreground color could change, hence apply it everytime
standardAttribute->setForeground(QColor(0, 0, 0, 255)); //Don't use alpha here, as kate uses the alpha only to blend with the document background color
}
return standardAttribute;
}
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());
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());
highlights.highlights.back()->setZDepth(highlightingZDepth);
}
}
}
if( FunctionDefinition* def = FunctionDefinition::definition(decl) )
{
highlights.highlights << def->createRangeMoving();
highlights.highlights.back()->setAttribute(highlightedUseAttribute());
highlights.highlights.back()->setZDepth(highlightingZDepth);
}
}
Declaration* ContextBrowserPlugin::findDeclaration(View* view, const KTextEditor::Cursor& position, bool mouseHighlight)
{
Q_UNUSED(mouseHighlight);
Declaration* foundDeclaration = 0;
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) );
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 0;
}
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) : 0;
if(specialRange.isValid())
{
// Highlight a special language object
if(allowHighlight)
{
highlights.highlights << PersistentMovingRange::Ptr(new PersistentMovingRange(specialRange, IndexedString(url)));
highlights.highlights.back()->setAttribute(highlightedSpecialObjectAttribute());
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 = 0;
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 = 0;
//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);
if (decl && decl->kind() == Declaration::Alias) {
AliasDeclaration* alias = dynamic_cast(decl);
Q_ASSERT(alias);
DUChainReadLocker lock;
decl = alias->aliasedDeclaration().declaration();
}
if(decl) {
Declaration* target = 0;
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) {
qWarning() << "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"
diff --git a/plugins/contextbrowser/contextbrowser.h b/plugins/contextbrowser/contextbrowser.h
index 260694cb09..3c616a18fd 100644
--- a/plugins/contextbrowser/contextbrowser.h
+++ b/plugins/contextbrowser/contextbrowser.h
@@ -1,271 +1,275 @@
/*
* 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.
*/
#ifndef KDEVPLATFORM_PLUGIN_CONTEXTBROWSERPLUGIN_H
#define KDEVPLATFORM_PLUGIN_CONTEXTBROWSERPLUGIN_H
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
+#include
#include
#include
#include
#include
class QHBoxLayout;
class QMenu;
class QToolButton;
namespace Sublime {
class MainWindow;
}
namespace KDevelop {
class IDocument;
class DUContext;
class TopDUContext;
class ReferencedTopDUContext;
class DUChainBase;
class AbstractNavigationWidget;
}
namespace KTextEditor {
class Document;
class View;
}
class ContextBrowserViewFactory;
class ContextBrowserView;
class ContextBrowserPlugin;
class BrowseManager;
class ContextBrowserHintProvider : public KTextEditor::TextHintProvider
{
public:
explicit ContextBrowserHintProvider(ContextBrowserPlugin* plugin);
QString textHint(KTextEditor::View* view, const KTextEditor::Cursor& position) override;
private:
ContextBrowserPlugin* m_plugin;
};
QWidget* masterWidget(QWidget* w);
struct ViewHighlights
{
ViewHighlights() : keep(false) {
}
// Whether the same highlighting should be kept highlighted (usually during typing)
bool keep;
// The declaration that is highlighted for this view
KDevelop::IndexedDeclaration declaration;
// Highlighted ranges. Those may also be contained by different views.
QList highlights;
};
class ContextBrowserPlugin : public KDevelop::IPlugin, public KDevelop::IContextBrowser
{
Q_OBJECT
Q_INTERFACES( KDevelop::IContextBrowser )
public:
explicit ContextBrowserPlugin(QObject *parent, const QVariantList & = QVariantList() );
~ContextBrowserPlugin() override;
void unload() override;
void registerToolView(ContextBrowserView* view);
void unRegisterToolView(ContextBrowserView* view);
KDevelop::ContextMenuExtension contextMenuExtension(KDevelop::Context*) override;
KXMLGUIClient* createGUIForMainWindow( Sublime::MainWindow* window ) override;
///duchain must be locked
///@param force When this is true, the history-entry is added, no matter whether the context is "interesting" or not
void updateHistory(KDevelop::DUContext* context, const KTextEditor::Cursor& cursorPosition,
bool force = false);
void updateDeclarationListBox(KDevelop::DUContext* context);
void showUses(const KDevelop::DeclarationPointer& declaration) override;
public Q_SLOTS:
void showUsesDelayed(const KDevelop::DeclarationPointer& declaration);
void previousContextShortcut();
void nextContextShortcut();
void startDelayedBrowsing(KTextEditor::View* view);
void stopDelayedBrowsing();
+ void invokeAction(int index);
void previousUseShortcut();
void nextUseShortcut();
void declarationSelectedInUI(const KDevelop::DeclarationPointer& decl);
void updateReady(const KDevelop::IndexedString& url, const KDevelop::ReferencedTopDUContext& topContext);
void textDocumentCreated( KDevelop::IDocument* document );
void documentActivated( KDevelop::IDocument* );
void viewDestroyed( QObject* obj );
void cursorPositionChanged( KTextEditor::View* view, const KTextEditor::Cursor& newPosition );
void viewCreated( KTextEditor::Document* , KTextEditor::View* );
void updateViews();
void hideToolTip();
void findUses();
void textInserted(KTextEditor::Document* doc, const KTextEditor::Cursor& cursor, const QString& text);
void selectionChanged(KTextEditor::View*);
void historyNext();
void historyPrevious();
private slots:
// history browsing
void documentJumpPerformed( KDevelop::IDocument* newDocument,
const KTextEditor::Cursor& newCursor,
KDevelop::IDocument* previousDocument,
const KTextEditor::Cursor& previousCursor);
void nextMenuAboutToShow();
void previousMenuAboutToShow();
void actionTriggered();
void navigateLeft();
void navigateRight();
void navigateUp();
void navigateDown();
void navigateAccept();
void navigateBack();
private:
QWidget* toolbarWidgetForMainWindow(Sublime::MainWindow* window);
void createActionsForMainWindow(Sublime::MainWindow* window, QString& xmlFile,
KActionCollection& actions) override;
+ QWidget* navigationWidgetForPosition(KTextEditor::View* view, KTextEditor::Cursor position);
void switchUse(bool forward);
void clearMouseHover();
void addHighlight( KTextEditor::View* view, KDevelop::Declaration* decl );
/** helper for updateBrowserView().
* Tries to find a 'specialLanguageObject' (eg macro) in @p view under cursor @c.
* If found returns true and sets @p pickedLanguage to the language this object belongs to */
KDevelop::Declaration* findDeclaration(KTextEditor::View* view, const KTextEditor::Cursor&, bool mouseHighlight);
void updateForView(KTextEditor::View* view);
// history browsing
bool isPreviousEntry(KDevelop::DUContext*, const KTextEditor::Cursor& cursor) const;
QString actionTextFor(int historyIndex) const;
void updateButtonState();
void openDocument(int historyIndex);
void fillHistoryPopup(QMenu* menu, const QList& historyIndices);
enum NavigationActionType {
Accept,
Back,
Down,
Up,
Left,
Right
};
void doNavigate(NavigationActionType action);
private:
// Returns the currently active and visible context browser view that belongs
// to the same context (mainwindow and area) as the given widget
ContextBrowserView* browserViewForWidget(QWidget* widget);
void showToolTip(KTextEditor::View* view, KTextEditor::Cursor position);
QTimer* m_updateTimer;
//Contains the range, the old attribute, and the attribute it was replaced with
QSet m_updateViews;
QMap m_highlightedRanges;
//Holds a list of all active context browser tool views
QList m_views;
//Used to override the next declaration that will be highlighted
KDevelop::IndexedDeclaration m_useDeclaration;
KDevelop::IndexedDeclaration m_lastHighlightedDeclaration;
QUrl m_mouseHoverDocument;
KTextEditor::Cursor m_mouseHoverCursor;
ContextBrowserViewFactory* m_viewFactory;
QPointer m_currentToolTip;
QPointer m_currentNavigationWidget;
KDevelop::IndexedDeclaration m_currentToolTipDeclaration;
+ KDevelop::Problem::Ptr m_currentToolTipProblem;
QAction* m_findUses;
QPointer m_lastInsertionDocument;
KTextEditor::Cursor m_lastInsertionPos;
// outline toolbar
QPointer m_outlineLine;
QPointer m_toolbarWidgetLayout;
QPointer m_toolbarWidget;
// history browsing
struct HistoryEntry {
//Duchain must be locked
HistoryEntry(KDevelop::IndexedDUContext ctx = KDevelop::IndexedDUContext(), const KTextEditor::Cursor& cursorPosition = KTextEditor::Cursor());
HistoryEntry(KDevelop::DocumentCursor pos);
//Duchain must be locked
void setCursorPosition(const KTextEditor::Cursor& cursorPosition);
//Duchain does not need to be locked
KDevelop::DocumentCursor computePosition() const;
KDevelop::IndexedDUContext context;
KDevelop::DocumentCursor absoluteCursorPosition;
KTextEditor::Cursor relativeCursorPosition; //Cursor position relative to the start line of the context
QString alternativeString;
};
QVector m_history;
QPointer m_previousButton;
QPointer m_nextButton;
QPointer m_previousMenu, m_nextMenu;
QList m_listDeclarations;
KDevelop::IndexedString m_listUrl;
BrowseManager* m_browseManager;
//Used to not record jumps triggered by the context-browser as history entries
QPointer m_focusBackWidget;
int m_nextHistoryIndex;
friend class ContextBrowserHintProvider;
ContextBrowserHintProvider m_textHintProvider;
};
#endif // KDEVPLATFORM_PLUGIN_CONTEXTBROWSERPLUGIN_H
// kate: space-indent on; indent-width 2; tab-width 4; replace-tabs on; auto-insert-doxygen on
diff --git a/plugins/problemreporter/problemhighlighter.cpp b/plugins/problemreporter/problemhighlighter.cpp
index cfa134876e..e7b39c7dfc 100644
--- a/plugins/problemreporter/problemhighlighter.cpp
+++ b/plugins/problemreporter/problemhighlighter.cpp
@@ -1,260 +1,206 @@
/*
* KDevelop Problem Reporter
*
* Copyright 2008 Hamish Rodda
* Copyright 2008-2009 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 "problemhighlighter.h"
#include
#include
-#include
#include