diff --git a/documentation/documentationview.cpp b/documentation/documentationview.cpp index 739cd13092..a927ab65ae 100644 --- a/documentation/documentationview.cpp +++ b/documentation/documentationview.cpp @@ -1,346 +1,356 @@ /* Copyright 2009 Aleix Pol Gonzalez Copyright 2010 Benjamin Port 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 "documentationview.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "documentationfindwidget.h" #include "debug.h" Q_LOGGING_CATEGORY(DOCUMENTATION, "kdevplatform.documentation") using namespace KDevelop; DocumentationView::DocumentationView(QWidget* parent, ProvidersModel* model) : QWidget(parent), mProvidersModel(model) { setWindowIcon(QIcon::fromTheme(QStringLiteral("documentation"), windowIcon())); setWindowTitle(i18n("Documentation")); setLayout(new QVBoxLayout(this)); layout()->setMargin(0); layout()->setSpacing(0); //TODO: clean this up, simply use addAction as that will create a toolbar automatically // use custom QAction's with createWidget for mProviders and mIdentifiers mActions = new KToolBar(this); // set window title so the QAction from QToolBar::toggleViewAction gets a proper name set mActions->setWindowTitle(i18n("Documentation Tool Bar")); mActions->setToolButtonStyle(Qt::ToolButtonIconOnly); int iconSize = style()->pixelMetric(QStyle::PM_SmallIconSize); mActions->setIconSize(QSize(iconSize, iconSize)); mFindDoc = new DocumentationFindWidget; mFindDoc->hide(); mBack = mActions->addAction(QIcon::fromTheme(QStringLiteral("go-previous")), i18n("Back")); mForward = mActions->addAction(QIcon::fromTheme(QStringLiteral("go-next")), i18n("Forward")); mFind = mActions->addAction(QIcon::fromTheme(QStringLiteral("edit-find")), i18n("Find"), mFindDoc, SLOT(startSearch())); mActions->addSeparator(); mActions->addAction(QIcon::fromTheme(QStringLiteral("go-home")), i18n("Home"), this, SLOT(showHome())); mProviders = new QComboBox(mActions); mIdentifiers = new QLineEdit(mActions); mIdentifiers->setClearButtonEnabled(true); mIdentifiers->setCompleter(new QCompleter(mIdentifiers)); // mIdentifiers->completer()->setCompletionMode(QCompleter::UnfilteredPopupCompletion); mIdentifiers->completer()->setCaseSensitivity(Qt::CaseInsensitive); /* vertical size policy should be left to the style. */ mIdentifiers->setSizePolicy(QSizePolicy::Expanding, mIdentifiers->sizePolicy().verticalPolicy()); connect(mIdentifiers, &QLineEdit::returnPressed, this, &DocumentationView::changedSelection); connect(mIdentifiers->completer(), static_cast(&QCompleter::activated), this, &DocumentationView::changeProvider); QWidget::setTabOrder(mProviders, mIdentifiers); mActions->addWidget(mProviders); mActions->addWidget(mIdentifiers); mBack->setEnabled(false); mForward->setEnabled(false); connect(mBack, &QAction::triggered, this, &DocumentationView::browseBack); connect(mForward, &QAction::triggered, this, &DocumentationView::browseForward); mCurrent = mHistory.end(); layout()->addWidget(mActions); layout()->addWidget(new QWidget(this)); layout()->addWidget(mFindDoc); QMetaObject::invokeMethod(this, "initialize", Qt::QueuedConnection); } void DocumentationView::initialize() { mProviders->setModel(mProvidersModel); connect(mProviders, static_cast(&QComboBox::activated), this, &DocumentationView::changedProvider); foreach (IDocumentationProvider* p, mProvidersModel->providers()) { // can't use new signal/slot syntax here, IDocumentation is not a QObject connect(dynamic_cast(p), SIGNAL(addHistory(KDevelop::IDocumentation::Ptr)), this, SLOT(addHistory(KDevelop::IDocumentation::Ptr))); } connect(mProvidersModel, &ProvidersModel::providersChanged, this, &DocumentationView::emptyHistory); if (mProvidersModel->rowCount() > 0) { changedProvider(0); } } void DocumentationView::browseBack() { mCurrent--; mBack->setEnabled(mCurrent != mHistory.begin()); mForward->setEnabled(true); updateView(); } void DocumentationView::browseForward() { mCurrent++; mForward->setEnabled(mCurrent+1 != mHistory.end()); mBack->setEnabled(true); updateView(); } void DocumentationView::showHome() { auto prov = mProvidersModel->provider(mProviders->currentIndex()); showDocumentation(prov->homePage()); } void DocumentationView::changedSelection() { changeProvider(mIdentifiers->completer()->currentIndex()); } void DocumentationView::changeProvider(const QModelIndex& idx) { if (idx.isValid()) { IDocumentationProvider* prov = mProvidersModel->provider(mProviders->currentIndex()); auto doc = prov->documentationForIndex(idx); if (doc) { showDocumentation(doc); } } } void DocumentationView::showDocumentation(const IDocumentation::Ptr& doc) { qCDebug(DOCUMENTATION) << "showing" << doc->name(); addHistory(doc); updateView(); } void DocumentationView::addHistory(const IDocumentation::Ptr& doc) { mBack->setEnabled(!mHistory.isEmpty()); mForward->setEnabled(false); // clear all history following the current item, unless we're already // at the end (otherwise this code crashes when history is empty, which // happens when addHistory is first called on startup to add the // homepage) if (mCurrent+1 < mHistory.end()) { mHistory.erase(mCurrent+1, mHistory.end()); } mHistory.append(doc); mCurrent = mHistory.end()-1; // NOTE: we assume an existing widget was used to navigate somewhere // but this history entry actually contains the new info for the // title... this is ugly and should be refactored somehow if (mIdentifiers->completer()->model() == (*mCurrent)->provider()->indexModel()) { mIdentifiers->setText((*mCurrent)->name()); } } void DocumentationView::emptyHistory() { mHistory.clear(); mCurrent = mHistory.end(); mBack->setEnabled(false); mForward->setEnabled(false); if (mProviders->count() > 0) { mProviders->setCurrentIndex(0); changedProvider(0); } } void DocumentationView::updateView() { mProviders->setCurrentIndex(mProvidersModel->rowForProvider((*mCurrent)->provider())); mIdentifiers->completer()->setModel((*mCurrent)->provider()->indexModel()); mIdentifiers->setText((*mCurrent)->name()); QLayoutItem* lastview = layout()->takeAt(1); Q_ASSERT(lastview); if (lastview->widget()->parent() == this) { lastview->widget()->deleteLater(); } delete lastview; mFindDoc->setEnabled(false); QWidget* w = (*mCurrent)->documentationWidget(mFindDoc, this); Q_ASSERT(w); QWidget::setTabOrder(mIdentifiers, w); mFind->setEnabled(mFindDoc->isEnabled()); if (!mFindDoc->isEnabled()) { mFindDoc->hide(); } QLayoutItem* findWidget = layout()->takeAt(1); layout()->addWidget(w); layout()->addItem(findWidget); } void DocumentationView::changedProvider(int row) { mIdentifiers->completer()->setModel(mProvidersModel->provider(row)->indexModel()); mIdentifiers->clear(); showHome(); } ////////////// ProvidersModel ////////////////// ProvidersModel::ProvidersModel(QObject* parent) : QAbstractListModel(parent) , mProviders(ICore::self()->documentationController()->documentationProviders()) { connect(ICore::self()->pluginController(), &IPluginController::unloadingPlugin, this, &ProvidersModel::unloaded); connect(ICore::self()->pluginController(), &IPluginController::pluginLoaded, this, &ProvidersModel::loaded); connect(ICore::self()->documentationController(), &IDocumentationController::providersChanged, this, &ProvidersModel::reloadProviders); } void ProvidersModel::reloadProviders() { beginResetModel(); mProviders = ICore::self()->documentationController()->documentationProviders(); + + std::sort(mProviders.begin(), mProviders.end(), + [](const KDevelop::IDocumentationProvider* a, const KDevelop::IDocumentationProvider* b) { + return a->name() < b->name(); + }); + endResetModel(); emit providersChanged(); } QVariant ProvidersModel::data(const QModelIndex& index, int role) const { if (index.row() >= mProviders.count() || index.row() < 0) return QVariant(); QVariant ret; switch (role) { case Qt::DisplayRole: ret = provider(index.row())->name(); break; case Qt::DecorationRole: ret = provider(index.row())->icon(); break; } return ret; } -void ProvidersModel::removeProviders(const QList&prov) +void ProvidersModel::addProvider(IDocumentationProvider* provider) { - if (prov.isEmpty()) + if (!provider || mProviders.contains(provider)) return; - int idx = mProviders.indexOf(prov.first()); + int pos = 0; + while (pos < mProviders.size() && mProviders[pos]->name() < provider->name()) + ++pos; - if (idx >= 0) { - beginRemoveRows(QModelIndex(), idx, idx + prov.count() - 1); - for(int i = 0, c = prov.count(); i < c; ++i) - mProviders.removeAt(idx); - endRemoveRows(); - emit providersChanged(); - } + beginInsertRows(QModelIndex(), pos, pos); + mProviders.insert(pos, provider); + endInsertRows(); + + emit providersChanged(); +} + +void ProvidersModel::removeProvider(IDocumentationProvider* provider) +{ + int pos; + if (!provider || (pos = mProviders.indexOf(provider)) < 0) + return; + + beginRemoveRows(QModelIndex(), pos, pos); + mProviders.removeAt(pos); + endRemoveRows(); + + emit providersChanged(); } void ProvidersModel::unloaded(IPlugin* plugin) { - IDocumentationProvider* provider = plugin->extension(); - if (provider) - removeProviders({provider}); + removeProvider(plugin->extension()); IDocumentationProviderProvider* providerProvider = plugin->extension(); if (providerProvider) { - removeProviders(providerProvider->providers()); + foreach(IDocumentationProvider* provider, providerProvider->providers()) + removeProvider(provider); } } void ProvidersModel::loaded(IPlugin* plugin) { - IDocumentationProvider* provider = plugin->extension(); - if (provider && !mProviders.contains(provider)) { - beginInsertRows(QModelIndex(), 0, 0); - mProviders.append(provider); - endInsertRows(); - emit providersChanged(); - } + addProvider(plugin->extension()); IDocumentationProviderProvider* providerProvider = plugin->extension(); if (providerProvider) { - beginInsertRows(QModelIndex(), 0, providerProvider->providers().count()-1); - mProviders.append(providerProvider->providers()); - endInsertRows(); - emit providersChanged(); + foreach(IDocumentationProvider* provider, providerProvider->providers()) + addProvider(provider); } } int ProvidersModel::rowCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : mProviders.count(); } int ProvidersModel::rowForProvider(IDocumentationProvider* provider) { return mProviders.indexOf(provider); } IDocumentationProvider* ProvidersModel::provider(int pos) const { return mProviders[pos]; } QList ProvidersModel::providers() { return mProviders; } diff --git a/documentation/documentationview.h b/documentation/documentationview.h index 14a420aaaa..c7a0822767 100644 --- a/documentation/documentationview.h +++ b/documentation/documentationview.h @@ -1,100 +1,101 @@ /* Copyright 2009 Aleix Pol Gonzalez Copyright 2010 Benjamin Port 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_DOCUMENTATIONVIEW_H #define KDEVPLATFORM_DOCUMENTATIONVIEW_H #include #include #include #include #include "documentationexport.h" namespace KDevelop { class IPlugin; class DocumentationFindWidget; } class QModelIndex; class QLineEdit; class ProvidersModel; class QComboBox; class KDEVPLATFORMDOCUMENTATION_EXPORT DocumentationView : public QWidget { Q_OBJECT public: DocumentationView(QWidget* parent, ProvidersModel* m); void showDocumentation(const KDevelop::IDocumentation::Ptr& doc); public slots: void initialize(); void addHistory(const KDevelop::IDocumentation::Ptr& doc); void emptyHistory(); void browseForward(); void browseBack(); void changedSelection(); void changedProvider(int); void changeProvider(const QModelIndex &); void showHome(); private: void updateView(); KToolBar* mActions; QAction* mForward; QAction* mBack; QAction* mFind; QLineEdit* mIdentifiers; QList< KDevelop::IDocumentation::Ptr > mHistory; QList< KDevelop::IDocumentation::Ptr >::iterator mCurrent; QComboBox* mProviders; ProvidersModel* mProvidersModel; KDevelop::DocumentationFindWidget* mFindDoc; }; class KDEVPLATFORMDOCUMENTATION_EXPORT ProvidersModel : public QAbstractListModel { Q_OBJECT public: explicit ProvidersModel(QObject* parent = nullptr); QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex& idx = QModelIndex()) const override; QList providers(); KDevelop::IDocumentationProvider* provider(int pos) const; int rowForProvider(KDevelop::IDocumentationProvider* provider); public slots: void unloaded(KDevelop::IPlugin* p); void loaded(KDevelop::IPlugin* p); void reloadProviders(); private: - void removeProviders(const QList &provider); + void addProvider(KDevelop::IDocumentationProvider* provider); + void removeProvider(KDevelop::IDocumentationProvider* provider); QList mProviders; signals: void providersChanged(); }; #endif // KDEVPLATFORM_DOCUMENTATIONVIEW_H diff --git a/interfaces/ibuddydocumentfinder.h b/interfaces/ibuddydocumentfinder.h index 59ef367fbb..621d02977b 100644 --- a/interfaces/ibuddydocumentfinder.h +++ b/interfaces/ibuddydocumentfinder.h @@ -1,143 +1,143 @@ /*************************************************************************** * Copyright 2011 Yannick Motta * * Martin Heide * * * * 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_IBUDDYDOCUMENTFINDER_H #define KDEVPLATFORM_IBUDDYDOCUMENTFINDER_H #include #include #include #include "interfacesexport.h" namespace KDevelop { /** * @short Implement this to add buddy document functionality to your language plugin. * * It enables the DocumentController (shell) to find related documents * (normally declaration/definition files, like foo.h and foo.cpp). * The DocumentController will position tabs of buddy documents next to * each other if the option UISettings.TabBarArrangeBuddies is 1. * The implementation also determines the order of the tabs in the tabbar. * * For finding a buddy document, the DocumentController addresses the * IBuddyDocumentFinder object that is registerd for this mimetype: * * This class provides static "registry" functions to handle a specific buddy finding * method (i.e. an object of class @class IBuddyDocumentFinder) for different * mimetypes. Like this, you can have for example an IBuddyDocumentFinder * for C++ files which considers foo.cpp and foo.h as buddies, and an * IBuddyDocumentFinder for Ada files which considers bar.adb and bar.ads as * buddies. * Like this, the concept of buddy documents is extensible for every language * for which support is added to KDevelop. * * If you want to add @class IBuddyDocumentFinder functionality to your language * plugin, your main class will inherit from IBuddyDocumentFinder (or have an * attribute of this type). Then in your constructor, call * @code addFinder(mimetype, this) @endcode * for each mimetype that your plugin supports. It is no problem to register the * same IBuddyDocumentFinder for several mimetypes. * * In the same way, in your destructor, you'll call * @code removeFinder(mimetype, this) @endcode * for each supported mimetype, to avoid that your IBuddyDocumentFinder object * is used beyond its lifetime. * * After you have registered it, your IBuddyDocumentFinder implementation will be found by * @code finderForMimeType(mimetype) @endcode * For example, the shell's DocumentController calls this function with idoc->mimeType() * in order to find a buddy document of a particular IDocument *idoc. */ class KDEVPLATFORMINTERFACES_EXPORT IBuddyDocumentFinder { public: virtual ~IBuddyDocumentFinder(){} /** * Called to determine if two document URLs should be considered as related. * * @return true, if the two documents are buddies. * For example, a C++ implementation would return true for * areBuddies(QUrl::fromLocalFile("...../foo.h"), QUrl::fromLocalFile("...../foo.cpp")). */ virtual bool areBuddies(const QUrl& url1, const QUrl& url2) = 0; /** - * Called to determine the order of two documents in the tabbar. + * @brief Called to determine the order of two documents in the tabbar. * * Example: a C++ implementation that wants to place the tab of the .h - * file left of the .cpp tab must return true for - * buddyOrder(QUrl::fromLocalFile("...../foo.h"), QUrl::fromLocalFile("...../foo.cpp")) - * and false for - * buddyOrder(QUrl::fromLocalFile("...../foo.cpp"), QUrl::fromLocalFile("...../foo.h")). + * file left of the .cpp tab must return true for
+ * buddyOrder(QUrl::fromLocalFile("foo.h"), QUrl::fromLocalFile("foo.cpp"))
+ * and false for
+ * buddyOrder(QUrl::fromLocalFile("foo.cpp"), QUrl::fromLocalFile("foo.h")) * - * @param url1 @param url2: two documents which are buddies, - * this means areBuddies(url1,url2) returned true. - * @return true, if url1's tab should be placed left of url2's tab. - * false, for the inverse. + * accepts two documents which are buddies, + * this means areBuddies(url1,url2) returned true. + * @return true if url1's tab should be placed left of url2's tab and + * false for the inverse. */ virtual bool buddyOrder(const QUrl& url1, const QUrl& url2) = 0; /** * Returns a list of QUrls of potential buddies of the document * provided by @p url. * * The urls are potential buddies and it is not ensured that the files * really exist. * * @returns list of potential buddy documents or an empty list * if non are available. */ virtual QVector getPotentialBuddies(const QUrl& url) const = 0; /** - * Registers a IBuddyDocumentFinder object for a mimetype. + * @brief Registers a IBuddyDocumentFinder object for a mimetype. * - * To be called in the constructor of language plugins. - * Afterwards, finderForMimeType(@param mimeType) will return @param finder, - * as long as the entry is not overwritten by another call to addFinder. + * @details To be called in the constructor of language plugins. + * Afterwards, finderForMimeType( @p mimeType ) will return @p finder , + * as long as the entry is not overwritten by another call to addFinder. */ static void addFinder(const QString& mimeType, IBuddyDocumentFinder* finder); /** - * Un-registers a IBuddyDocumentFinder object for a mimetype. + * @brief Un-registers a IBuddyDocumentFinder object for a mimetype. * - * To be called in the destructor of language plugins. - * Afterwards, finderForMimeType(@param mimeType) will return 0, until a new - * entry for this mimetype is created by addFinder(). + * @details To be called in the destructor of language plugins. + * Afterwards, finderForMimeType( @p mimeType ) will return 0, until a new + * entry for this mimetype is created by addFinder(). */ static void removeFinder(const QString& mimeType); /** * Returns the registered IBuddyDocumentFinder for this mimetype, or 0. * * Used in the DocumentController (shell). */ static IBuddyDocumentFinder* finderForMimeType(const QString& mimeType); private: struct Private; }; } #endif diff --git a/interfaces/idocumentcontroller.h b/interfaces/idocumentcontroller.h index a66fe9c91b..b8a41f031f 100644 --- a/interfaces/idocumentcontroller.h +++ b/interfaces/idocumentcontroller.h @@ -1,231 +1,233 @@ /*************************************************************************** * 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_IDOCUMENTCONTROLLER_H #define KDEVPLATFORM_IDOCUMENTCONTROLLER_H #include #include #include #include #include #include "interfacesexport.h" #include "idocument.h" namespace KTextEditor { class View; } namespace KDevelop { class ICore; class KDEVPLATFORMINTERFACES_EXPORT IDocumentFactory { public: virtual ~IDocumentFactory() {} virtual IDocument* create(const QUrl&, ICore* ) = 0; }; /** * * Allows to access the open documents and also open new ones * * @class IDocumentController */ class KDEVPLATFORMINTERFACES_EXPORT IDocumentController: public QObject { Q_OBJECT public: enum DocumentActivation { DefaultMode = 0, /**Activate document and create a view if no other flags passed.*/ DoNotActivate = 1, /**Don't activate the Document.*/ DoNotCreateView = 2, /**Don't create and show the view for the Document.*/ DoNotFocus = 4 /**Don't change the keyboard focus.*/ }; Q_DECLARE_FLAGS(DocumentActivationParams, DocumentActivation) explicit IDocumentController(QObject *parent); /** * Finds the first document object corresponding to a given url. * * @param url The Url of the document. * @return The corresponding document, or null if not found. */ Q_SCRIPTABLE virtual KDevelop::IDocument* documentForUrl( const QUrl & url ) const = 0; /// @return The list of all open documents Q_SCRIPTABLE virtual QList openDocuments() const = 0; /** * Returns the curently active or focused document. * * @return The active document. */ Q_SCRIPTABLE virtual KDevelop::IDocument* activeDocument() const = 0; Q_SCRIPTABLE virtual void activateDocument( KDevelop::IDocument * document, const KTextEditor::Range& range = KTextEditor::Range::invalid() ) = 0; virtual void registerDocumentForMimetype( const QString&, KDevelop::IDocumentFactory* ) = 0; Q_SCRIPTABLE virtual bool saveAllDocuments(KDevelop::IDocument::DocumentSaveMode mode = KDevelop::IDocument::Default) = 0; Q_SCRIPTABLE virtual bool saveSomeDocuments(const QList& list, KDevelop::IDocument::DocumentSaveMode mode = KDevelop::IDocument::Default) = 0; Q_SCRIPTABLE virtual bool saveAllDocumentsForWindow(KParts::MainWindow* mw, IDocument::DocumentSaveMode mode, bool currentAreaOnly = false) = 0; /// Opens a text document containing the @p data text. Q_SCRIPTABLE virtual KDevelop::IDocument* openDocumentFromText( const QString& data ) = 0; virtual IDocumentFactory* factory(const QString& mime) const = 0; Q_SCRIPTABLE virtual KTextEditor::Document* globalTextEditorInstance()=0; /** * @returns the KTextEditor::View of the current document, in case it is a text document */ virtual KTextEditor::View* activeTextDocumentView() const = 0; public Q_SLOTS: /** * Opens a new or existing document. * * @param url The full Url of the document to open. * @param cursor The location information, if applicable. + * @param activationParams Indicates whether to fully activate the document. */ KDevelop::IDocument* openDocument( const QUrl &url, const KTextEditor::Cursor& cursor, DocumentActivationParams activationParams = nullptr, const QString& encoding = {}); /** * Opens a new or existing document. * * @param url The full Url of the document to open. * @param range The range of text to select, if applicable. + * @param activationParams Indicates whether to fully activate the document * @param buddy Optional buddy document. If 0, the registered IBuddyDocumentFinder * for the URL's mimetype will be queried to find a fitting buddy. * If a buddy was found (or passed) @p url will be opened next * to its buddy. * * @return The opened document */ virtual KDevelop::IDocument* openDocument( const QUrl &url, const KTextEditor::Range& range = KTextEditor::Range::invalid(), DocumentActivationParams activationParams = nullptr, const QString& encoding = {}, IDocument* buddy = nullptr) = 0; /** * Opens a document from the IDocument instance. * * @param doc The IDocument to add * @param range The location information, if applicable. * @param activationParams Indicates whether to fully activate the document. * @param buddy Optional buddy document. If 0, the registered IBuddyDocumentFinder * for the Documents mimetype will be queried to find a fitting buddy. * If a buddy was found (or passed) @p url will be opened next * to its buddy. */ virtual Q_SCRIPTABLE bool openDocument(IDocument* doc, const KTextEditor::Range& range = KTextEditor::Range::invalid(), DocumentActivationParams activationParams = nullptr, IDocument* buddy = nullptr) = 0; /** * Opens a new or existing document. * * @param url The full Url of the document to open. * @param prefName The name of the preferred KPart to open that document */ virtual KDevelop::IDocument* openDocument( const QUrl &url, const QString& prefName ) = 0; virtual bool closeAllDocuments() = 0; Q_SIGNALS: /// Emitted when the document has been activated. void documentActivated( KDevelop::IDocument* document ); /** * Emitted whenever the active cursor jumps from one document+cursor to another, * as e.g. caused by a call to openDocument(..). * * This is also emitted when a document is only activated. Then previousDocument is zero. */ void documentJumpPerformed( KDevelop::IDocument* newDocument, KTextEditor::Cursor newCursor, KDevelop::IDocument* previousDocument, KTextEditor::Cursor previousCursor); /// Emitted when a document has been saved. void documentSaved( KDevelop::IDocument* document ); /** * Emitted when a document has been opened. * * NOTE: The document may not be loaded from disk/network at this point. * NOTE: No views exist for the document at the time this signal is emitted. */ void documentOpened( KDevelop::IDocument* document ); /** * Emitted when a document has been loaded. * * NOTE: No views exist for the document at the time this signal is emitted. */ void documentLoaded( KDevelop::IDocument* document ); /** * Emitted when a text document has been loaded, and the text document created. * * NOTE: no views exist for the document at the time this signal is emitted. */ void textDocumentCreated( KDevelop::IDocument* document ); /// Emitted when a document has been closed. void documentClosed( KDevelop::IDocument* document ); /** * This is emitted when the document state(the relationship * between the file in the editor and the file stored on disk) changes. */ void documentStateChanged( KDevelop::IDocument* document ); /// This is emitted when the document content changed. void documentContentChanged( KDevelop::IDocument* document ); /** * Emitted when a document has been loaded, but before documentLoaded(..) is emitted. * * This allows parts of kdevplatform to prepare data-structures that can be used by other parts * during documentLoaded(..). */ void documentLoadedPrepare( KDevelop::IDocument* document ); /// Emitted when a document url has changed. void documentUrlChanged( KDevelop::IDocument* document ); friend class IDocument; }; } // namespace KDevelop Q_DECLARE_OPERATORS_FOR_FLAGS(KDevelop::IDocumentController::DocumentActivationParams) #endif diff --git a/interfaces/iplugin.h b/interfaces/iplugin.h index 1e09f12c79..73985c98cb 100644 --- a/interfaces/iplugin.h +++ b/interfaces/iplugin.h @@ -1,286 +1,285 @@ /* This file is part of the KDE project Copyright 1999-2001 Bernd Gehrmann Copyright 2004,2007 Alexander Dymo Copyright 2006 Adam Treat Copyright 2007 Andreas Pakulat 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_IPLUGIN_H #define KDEVPLATFORM_IPLUGIN_H #include #include #include #include "interfacesexport.h" namespace Sublime { class MainWindow; } /** * This macro adds an extension interface to register with the extension manager * Call this macro for all interfaces your plugin implements in its constructor */ #define KDEV_USE_EXTENSION_INTERFACE(Extension) \ addExtension(QByteArray::fromRawData(qobject_interface_iid(), \ static_cast(strlen(qobject_interface_iid())))); namespace KDevelop { class ICore; class ConfigPage; class Context; class ContextMenuExtension; struct ProjectConfigOptions; /** * The base class for all KDevelop plugins. * * Plugin is a component which is loaded into KDevelop shell at startup or by * request. Each plugin should have corresponding .desktop file with a * description. The .desktop file template looks like: * @code * [Desktop Entry] * Type=Service * Exec=blubb * Name= * GenericName= * Comment= * Icon= * ServiceTypes=KDevelop/Plugin * X-KDE-Library= * X-KDE-PluginInfo-Name= * X-KDE-PluginInfo-Author= * X-KDE-PluginInfo-Version= * X-KDE-PluginInfo-License= * X-KDE-PluginInfo-Category= * X-KDevelop-Version= * X-KDevelop-Category= * X-KDevelop-Mode=GUI * X-KDevelop-LoadMode= * X-KDevelop-Language= * X-KDevelop-SupportedMimeTypes= * X-KDevelop-Interfaces= * X-KDevelop-IOptional= * X-KDevelop-IRequired= * @endcode * Description of parameters in .desktop file: * - Name is a translatable name of a plugin, it is used in the plugin * selection list (required); * - GenericName is a translatable generic name of a plugin, it should * describe in general what the plugin can do (required); * - Comment is a short description about the plugin (optional); * - Icon is a plugin icon (preferred); * X-KDE-librarythis is the name of the .so file to load for this plugin (required); * - X-KDE-PluginInfo-Name is a non-translatable user-readable plugin * identifier used in KTrader queries (required); * - X-KDE-PluginInfo-Author is a non-translateable name of the plugin * author (optional); * - X-KDE-PluginInfo-Version is version number of the plugin (optional); * - X-KDE-PluginInfo-License is a license (optional). can be: GPL, * LGPL, BSD, Artistic, QPL or Custom. If this property is not set, license is * considered as unknown; * - X-KDE-PluginInfo-Category is used to categorize plugins (optional). can be: * Core, Project Management, Version Control, Utilities, Documentation, * Language Support, Debugging, Other * If this property is not set, "Other" is assumed * - X-KDevelop-Version is the KDevPlatform API version this plugin * works with (required); * - X-KDevelop-Interfaces is a list of extension interfaces that this * plugin implements (optional); * - X-KDevelop-IRequired is a list of extension interfaces that this * plugin depends on (optional); A list entry can also be of the form @c interface@pluginname, * in which case a plugin of the given name is required which implements the interface. * - X-KDevelop-IOptional is a list of extension interfaces that this * plugin will use if they are available (optional); * - X-KDevelop-Language is the name of the language the plugin provides * support for (optional); * - X-KDevelop-SupportedMimeTypes is a list of mimetypes that the * language-parser in this plugin supports (optional); * - X-KDevelop-Mode is either GUI or NoGUI to indicate whether a plugin can run * with the GUI components loaded or not (required); * - X-KDevelop-Category is a scope of a plugin (see below for * explanation) (required); * - X-KDevelop-LoadMode can be set to AlwaysOn in which case the plugin will * never be unloaded even if requested via the API. (optional); * * Plugin scope can be either: * - Global * - Project * . * Global plugins are plugins which require only the shell to be loaded and do not operate on * the Project interface and/or do not use project wide information.\n * Core plugins are global plugins which offer some important "core" functionality and thus * are not selectable by user in plugin configuration pages.\n * Project plugins require a project to be loaded and are usually loaded/unloaded along with * the project. * If your plugin uses the Project interface and/or operates on project-related * information then this is a project plugin. * * * @sa Core class documentation for information about features available to * plugins from shell applications. */ class KDEVPLATFORMINTERFACES_EXPORT IPlugin: public QObject, public KXMLGUIClient { Q_OBJECT public: /**Constructs a plugin. * @param componentName The component name for this plugin. * @param parent The parent object for the plugin. */ IPlugin(const QString &componentName, QObject *parent); /**Destructs a plugin.*/ ~IPlugin() override; /** * Signal the plugin that it should cleanup since it will be unloaded soon. */ Q_SCRIPTABLE virtual void unload(); /** * Provides access to the ICore implementation */ Q_SCRIPTABLE ICore *core() const; Q_SCRIPTABLE QVector extensions() const; template Extension* extension() { const auto extensionIID = QByteArray::fromRawData(qobject_interface_iid(), static_cast(strlen(qobject_interface_iid()))); if (extensions().contains(extensionIID)) { return qobject_cast(this); } return nullptr; } /** * Ask the plugin for a ContextActionContainer, which contains actions * that will be merged into the context menu. * @param context the context describing where the context menu was requested * @returns a container describing which actions to merge into which context menu part */ virtual ContextMenuExtension contextMenuExtension( KDevelop::Context* context ); /** * Can create a new KXMLGUIClient, and set it up correctly with all the plugins per-window GUI actions. * * The caller owns the created object, including all contained actions. The object is destroyed as soon as * the mainwindow is closed. * * The default implementation calls the convenience function @ref createActionsForMainWindow and uses it to fill a custom KXMLGUIClient. * * Only override this version if you need more specific features of KXMLGUIClient, or other special per-window handling. * * @param window The main window the actions are created for */ virtual KXMLGUIClient* createGUIForMainWindow( Sublime::MainWindow* window ); /** * This function allows allows setting up plugin actions conveniently. Unless createGUIForMainWindow was overridden, * this is called for each new mainwindow, and the plugin should add its actions to @p actions, and write its KXMLGui xml file * into @p xmlFile. * * @param window The main window the actions are created for * @param xmlFile Change this to the xml file that needed for merging the actions into the GUI * @param actions Add your actions here. A new set of actions has to be created for each mainwindow. */ virtual void createActionsForMainWindow( Sublime::MainWindow* window, QString& xmlFile, KActionCollection& actions ); /** * This function is necessary because there is no proper way to signal errors from plugin constructor. * @returns True if the plugin has encountered an error (and therefore has an error description), * false otherwise. */ bool hasError() const; /** * Description of the last encountered error, of an empty string if none. */ QString errorDescription() const; /** * Set a @p description of the errror encountered. An empty error * description implies no error in the plugin. */ void setErrorDescription(QString const& description); /** * Get the global config page with the \p number, config pages from 0 to * configPages()-1 are available if configPages() > 0. * * @param number index of config page - * @param options The options used to initialize the ProjectConfigPage * @param parent parent widget for config page * @return newly created config page or NULL, if the number is out of bounds, default implementation returns NULL. * This config page should inherit from ProjectConfigPage, but it is not a strict requirement. * The default implementation returns @c nullptr. * @see perProjectConfigPages(), ProjectConfigPage */ virtual ConfigPage* configPage(int number, QWidget *parent); /** * Get the number of available config pages for global settings. * @return number of global config pages. The default implementation returns zero. * @see configPage() */ virtual int configPages() const; /** * Get the number of available config pages for per project settings. * @return number of per project config pages. The default implementation returns zero. * @see perProjectConfigPage() */ virtual int perProjectConfigPages() const; /** * Get the per project config page with the \p number, config pages from 0 to * perProjectConfigPages()-1 are available if perProjectConfigPages() > 0. * * @param number index of config page * @param options The options used to initialize the ProjectConfigPage * @param parent parent widget for config page * @return newly created config page or NULL, if the number is out of bounds, default implementation returns NULL. * This config page should inherit from ProjectConfigPage, but it is not a strict requirement. * The default implementation returns @c nullptr. * @see perProjectConfigPages(), ProjectConfigPage */ virtual ConfigPage* perProjectConfigPage(int number, const KDevelop::ProjectConfigOptions& options, QWidget* parent); protected: void addExtension( const QByteArray& ); /** * Initialize the XML GUI State. */ virtual void initializeGuiState(); private: friend class IPluginPrivate; class IPluginPrivate* const d; }; } #endif diff --git a/interfaces/iproblem.h b/interfaces/iproblem.h index a3f2b93f27..5e736acf2f 100644 --- a/interfaces/iproblem.h +++ b/interfaces/iproblem.h @@ -1,123 +1,123 @@ /* * Copyright 2015 Laszlo Kis-Adam * * 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 IPROBLEM_H #define IPROBLEM_H #include #include #include #include namespace KDevelop { class IAssistant; /// Interface for the Problem classes class IProblem : public QSharedData { public: typedef QExplicitlySharedDataPointer Ptr; /// The source of the problem. That is which tool / which part found this problem. enum Source { Unknown, Disk, Preprocessor, Lexer, Parser, DUChainBuilder, SemanticAnalysis, ToDo, Plugin /// The source is a problem checker plugin }; /// Severity of the problem. That is, how serious is the found problem. enum Severity { NoSeverity = 0, Error = 1, Warning = 2, Hint = 4 }; Q_DECLARE_FLAGS(Severities, Severity) IProblem(){} virtual ~IProblem(){} /// Returns the source of the problem virtual Source source() const = 0; /// Sets the source of the problem virtual void setSource(Source source) = 0; /// Returns a string containing the source of the problem virtual QString sourceString() const = 0; /// Returns the location of the problem (path, line, column) virtual KDevelop::DocumentRange finalLocation() const = 0; /// Sets the location of the problem (path, line, column) virtual void setFinalLocation(const KDevelop::DocumentRange& location) = 0; /// Returns the short description of the problem. virtual QString description() const = 0; /// Sets the short description of the problem virtual void setDescription(const QString& description) = 0; /// Returns the detailed explanation of the problem. virtual QString explanation() const = 0; /// Sets the detailed explanation of the problem virtual void setExplanation(const QString& explanation) = 0; /// Returns the severity of the problem virtual Severity severity() const = 0; /// Sets the severity of the problem virtual void setSeverity(Severity severity) = 0; /// Returns a string containing the severity of the problem virtual QString severityString() const = 0; /// Returns the diagnostics of the problem. virtual QVector diagnostics() const = 0; /// Sets the diagnostics of the problem virtual void setDiagnostics(const QVector &diagnostics) = 0; /// Adds a diagnostic line to the problem virtual void addDiagnostic(const Ptr &diagnostic) = 0; /// Clears all diagnostics virtual void clearDiagnostics() = 0; /// Returns a solution assistant for the problem, if applicable that is. virtual QExplicitlySharedDataPointer solutionAssistant() const = 0; }; Q_DECLARE_OPERATORS_FOR_FLAGS(IProblem::Severities) } -Q_DECLARE_METATYPE(KDevelop::IProblem::Ptr); +Q_DECLARE_METATYPE(KDevelop::IProblem::Ptr) #endif diff --git a/interfaces/iuicontroller.h b/interfaces/iuicontroller.h index fe7d07ff03..5fd7155faa 100644 --- a/interfaces/iuicontroller.h +++ b/interfaces/iuicontroller.h @@ -1,174 +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 + * @param viewWidget 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 + * @param viewWidget 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; /** * 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/interfaces/launchconfigurationpage.h b/interfaces/launchconfigurationpage.h index 73b8106951..e320891278 100644 --- a/interfaces/launchconfigurationpage.h +++ b/interfaces/launchconfigurationpage.h @@ -1,89 +1,89 @@ /* This file is part of KDevelop Copyright 2009 Andreas Pakulat 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_LAUNCHCONFIGURATIONPAGE_H #define KDEVPLATFORM_LAUNCHCONFIGURATIONPAGE_H #include "interfacesexport.h" #include class KConfigGroup; namespace KDevelop { class IProject; /** * Provides a configuration page for a launch configuration, the interface * allows the actual dialog to easily load/save the configuration and show some title/icon */ class KDEVPLATFORMINTERFACES_EXPORT LaunchConfigurationPage : public QWidget { Q_OBJECT public: explicit LaunchConfigurationPage( QWidget* parent ); /** * Allows the page to load values from an existing launch configuration * @param cfg the configuration to load from */ virtual void loadFromConfiguration( const KConfigGroup& cfg, KDevelop::IProject* project = nullptr ) = 0; /** * Allows the page to save values to an existing launch configuration * @param cfg the configuration to save to */ - virtual void saveToConfiguration( KConfigGroup, KDevelop::IProject* project = nullptr ) const = 0; + virtual void saveToConfiguration( KConfigGroup cfg, KDevelop::IProject* project = nullptr ) const = 0; /** * A title for displaying in the GUI * @returns the title of the page */ virtual QString title() const = 0; /** * an icon for the GUI * @returns an icon suitable for display in the GUI */ virtual QIcon icon() const = 0; Q_SIGNALS: void changed(); }; /** * A simple factory class to create certain launch config pages * this is used to create config pages only when they're needed */ class KDEVPLATFORMINTERFACES_EXPORT LaunchConfigurationPageFactory { public: virtual ~LaunchConfigurationPageFactory() {} /** * create a new launch config page widget using the given @p parent * @param parent the parent widget to be used * @returns a new launch config page */ virtual LaunchConfigurationPage* createWidget( QWidget* parent ) = 0; }; } #endif diff --git a/interfaces/launchconfigurationtype.h b/interfaces/launchconfigurationtype.h index e1efbf38b4..d44b52d0ba 100644 --- a/interfaces/launchconfigurationtype.h +++ b/interfaces/launchconfigurationtype.h @@ -1,152 +1,152 @@ /* This file is part of KDevelop Copyright 2009 Andreas Pakulat 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_LAUNCHCONFIGURATIONTYPE_H #define KDEVPLATFORM_LAUNCHCONFIGURATIONTYPE_H #include "interfacesexport.h" #include #include class QMenu; class QIcon; class QUrl; class KConfigGroup; namespace KDevelop { class IProject; class ILaunchConfiguration; class ProjectBaseItem; class ILauncher; class LaunchConfigurationPageFactory; /** * Launch configuration types are used to be able to create * new launch configurations. Each launch configuration has a * specific type, which specifies which launchers can be used * for the configuration as well as which config pages are needed * to setup the launch configuration */ class KDEVPLATFORMINTERFACES_EXPORT LaunchConfigurationType : public QObject { Q_OBJECT public: LaunchConfigurationType(); ~LaunchConfigurationType() override; /** * Provide a unique identifier for the type * among other things this will be used to create a config group in launch * configurations for the pages of this config type * @returns a unique identifier for this launch configuration type */ virtual QString id() const = 0; /** * Provide a user visible name for the type * @returns a translatable string for the type */ virtual QString name() const = 0; /** * Add @p starter to this configuration type * @param starter the launcher that can start configurations of this type */ void addLauncher( ILauncher* starter ); /** * remove @p starter from this configuration type * @param starter the launcher that should not start configurations of this type */ void removeLauncher( ILauncher* starter ); /** * Access all launchers that are usable with this type * @returns a list of launchers that can be used with configurations of this type */ QList launchers() const; /** * Convenience method to access a launcher given its @p id * @param id the id of the launcher to be found * @returns the launcher with the given id or 0 if there's no such launcher in this configuration type */ ILauncher* launcherForId( const QString& id ); /** * Provide a list of widgets to configure a launch configuration for this type * @returns a list of factories to create config pages from. */ virtual QList configPages() const = 0; /** * Provide an icon for this launch configuration type * @returns an icon to be used for representing launch configurations of this type */ virtual QIcon icon() const = 0; /** * Check whether this launch configuration type can launch the given project item * @param item the project tree item to test * @returns true if this configuration type can launch the given item, false otherwise */ virtual bool canLaunch( KDevelop::ProjectBaseItem* item ) const = 0; /** * Configure the given launch configuration to execute the selected item * @param config the configuration to setup * @param item the item to launch */ virtual void configureLaunchFromItem( KConfigGroup config, KDevelop::ProjectBaseItem* item ) const = 0; /** * Configure the given launch configuration to execute the selected item * @param config the configuration to setup - * @param item the item to launch + * @param args argument list */ virtual void configureLaunchFromCmdLineArguments( KConfigGroup config, const QStringList &args ) const = 0; /** * Check whether this launch configuration type can launch the given file * @param file the file to test launchability * @returns true if this configuration type can launch the given file, false otherwise */ virtual bool canLaunch( const QUrl& file ) const = 0; /** * Returns a menu that will be added to the UI where the interface will be * able to add any suggestion it needs, like default targets. */ virtual QMenu* launcherSuggestions() { return nullptr; } signals: void signalAddLaunchConfiguration(KDevelop::ILaunchConfiguration* launch); private: class LaunchConfigurationTypePrivate* const d; }; } #endif diff --git a/language/codecompletion/codecompletionitem.h b/language/codecompletion/codecompletionitem.h index 1d83e438b3..cacd3a88d0 100644 --- a/language/codecompletion/codecompletionitem.h +++ b/language/codecompletion/codecompletionitem.h @@ -1,152 +1,152 @@ /* * KDevelop Generic Code Completion Support * * Copyright 2007-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_KDEV_CODECOMPLETIONITEM_H #define KDEVPLATFORM_KDEV_CODECOMPLETIONITEM_H #include #include "../duchain/duchainpointer.h" #include "codecompletioncontext.h" namespace KTextEditor { class CodeCompletionModel; class Range; class Cursor; } class QModelIndex; namespace KDevelop { class CodeCompletionModel; struct CompletionTreeNode; class CompletionTreeItem; class IndexedType; class KDEVPLATFORMLANGUAGE_EXPORT CompletionTreeElement : public QSharedData { public: CompletionTreeElement(); virtual ~CompletionTreeElement(); CompletionTreeElement* parent() const; ///Reparenting is not supported. This is only allowed if parent() is still zero. void setParent(CompletionTreeElement*); int rowInParent() const; int columnInParent() const; ///Each element is either a node, or an item. CompletionTreeNode* asNode(); CompletionTreeItem* asItem(); template T* asItem() { return dynamic_cast(this); } template const T* asItem() const { return dynamic_cast(this); } const CompletionTreeNode* asNode() const; const CompletionTreeItem* asItem() const; private: CompletionTreeElement* m_parent; int m_rowInParent; }; struct KDEVPLATFORMLANGUAGE_EXPORT CompletionTreeNode : public CompletionTreeElement { CompletionTreeNode(); ~CompletionTreeNode() override; KTextEditor::CodeCompletionModel::ExtraItemDataRoles role; QVariant roleValue; ///Will append the child, and initialize it correctly to create a working tree-structure void appendChild(QExplicitlySharedDataPointer); void appendChildren(QList >); void appendChildren(QList >); ///@warning Do not manipulate this directly, that's bad for consistency. Use appendChild instead. QList > children; }; class KDEVPLATFORMLANGUAGE_EXPORT CompletionTreeItem : public CompletionTreeElement { public: ///Execute the completion item. The default implementation does nothing. virtual void execute(KTextEditor::View* view, const KTextEditor::Range& word); ///Should return normal completion data, @see KTextEditor::CodeCompletionModel ///The default implementation returns "unimplemented", so re-implement it! ///The duchain is not locked when this is called ///Navigation-widgets should be registered to the model, then it will care about the interaction. virtual QVariant data(const QModelIndex& index, int role, const CodeCompletionModel* model) const; ///Should return the inheritance-depth. The completion-items don't need to return it through the data() function. virtual int inheritanceDepth() const; ///Should return the argument-hint depth. The completion-items don't need to return it through the data() function. virtual int argumentHintDepth() const; ///The default-implementation calls DUChainUtils::completionProperties virtual KTextEditor::CodeCompletionModel::CompletionProperties completionProperties() const; ///If this item represents a Declaration, this should return the declaration. ///The default-implementation returns zero. virtual DeclarationPointer declaration() const; ///Should return the types should be used for matching items against this one when it's an argument hint. ///The matching against all types should be done, and the best one will be used as final match result. virtual QList typeForArgumentMatching() const; ///Should return whether this completion-items data changes with input done by the user during code-completion. ///Returning true is very expensive. virtual bool dataChangedWithInput() const; }; -///A custom-group node, that can be used as-is. Just create it, and call appendChild to add group items. -///The items in the group will be shown in the completion-list with a group-header that contains the given name + ///A custom-group node, that can be used as-is. Just create it, and call appendChild to add group items. + ///The items in the group will be shown in the completion-list with a group-header that contains the given name struct KDEVPLATFORMLANGUAGE_EXPORT CompletionCustomGroupNode : public CompletionTreeNode { - ///@param inheritanceDepth @see KTextEditor::CodeCompletionModel::GroupRole + ///@param inheritanceDepth @ref KTextEditor::CodeCompletionModel::GroupRole CompletionCustomGroupNode(QString groupName, int inheritanceDepth = 700); int inheritanceDepth; }; typedef QExplicitlySharedDataPointer CompletionTreeItemPointer; typedef QExplicitlySharedDataPointer CompletionTreeElementPointer; } Q_DECLARE_METATYPE(KDevelop::CompletionTreeElementPointer); #endif diff --git a/language/codegen/applychangeswidget.h b/language/codegen/applychangeswidget.h index 3a91223737..dfa2f50422 100644 --- a/language/codegen/applychangeswidget.h +++ b/language/codegen/applychangeswidget.h @@ -1,69 +1,69 @@ /* Copyright 2008 Aleix Pol * Copyright 2009 Ramón Zarazúa * * 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) 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_APPLYCHANGESWIDGET_H #define KDEVPLATFORM_APPLYCHANGESWIDGET_H #include #include namespace KTextEditor { class Document; } namespace KDevelop { class IndexedString; class ApplyChangesWidgetPrivate; class KDEVPLATFORMLANGUAGE_EXPORT ApplyChangesWidget : public QDialog { Q_OBJECT public: explicit ApplyChangesWidget(QWidget* parent=nullptr); ~ApplyChangesWidget() override; void setInformation(const QString& info); bool hasDocuments() const; KTextEditor::Document* document() const; - ///@param modified may be an artifial code representation (@ref KDevelop::InsertArtificialCodeRepresentation) + ///@param original may be an artifial code representation @ref KDevelop::InsertArtificialCodeRepresentation void addDocuments(const IndexedString & original); ///This will save all the modified files into their originals bool applyAllChanges(); ///Update the comparison view fo @p index, in case changes have been done directly to the opened document. ///@param index the index to update, -1 for current index void updateDiffView(int index = -1); public Q_SLOTS: ///Called to signal a change to the currently viewed index void indexChanged(int); private: ApplyChangesWidgetPrivate * d; }; } #endif diff --git a/language/codegen/codegenerator.h b/language/codegen/codegenerator.h index 8e1b1e03f5..35b9f0f9de 100644 --- a/language/codegen/codegenerator.h +++ b/language/codegen/codegenerator.h @@ -1,252 +1,252 @@ /* 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_CODEGENERATOR_H #define KDEVPLATFORM_CODEGENERATOR_H #include "language/editor/documentrange.h" #include #include "../duchain/topducontext.h" #include "../duchain/duchain.h" #include "language/interfaces/iastcontainer.h" #include "util/debug.h" namespace KDevelop { template class AstChangeSet; class DUContext; class DUChainChangeSet; class DocumentChangeSet; /** * \short Base class for generic code generators * * CodeGeneratorBase provides an api for a step-by-step process to * create and/or refactor code. * * This class should be used as a superclass only when du-chain level * changes are made, since this level knows nothing about the * language-specific AST. For more complex changes that require knowledge * about the language AST, use CodeGenerator * * \see Refactoring * \see CodeGenerator * \author Hamish Rodda */ class KDEVPLATFORMLANGUAGE_EXPORT CodeGeneratorBase { public: CodeGeneratorBase(); virtual ~CodeGeneratorBase(); enum State { Precondition, UserInput, Processing, Review, Finished }; /** * Check whether the preconditions of this generation are met at the given \a context and * \a position. * \returns true if conditions are met and the generator can progress, otherwise false if * the conditions are not met. Use setErrorText to specify the nature of the Error. */ virtual bool checkPreconditions(DUContext* context, const DocumentRange& position) = 0; /** * Gather information required from the user for this generator. * * \returns true if all of the information is retrieved, otherwise false, Use setErrorText * to specify the nature of the Error. */ virtual bool gatherInformation() = 0; /** * Do final condition checking and perform the code generation. * * \returns true if code generation was successful, false otherwise. Use setErrorText to * specify the nature of the Error. */ virtual bool process() = 0; const QString & errorText() const; // Implementation from kJob bool execute(); /** * @brief Indicates that this generation should not expect interaction with the user/ * Most probable scenarios are: Testing, and a generator that is being used by another one * @param context If not NULL, the custom context to use, instead of user selection - * @param position If not NULL, the cursom range to use instead of user selection + * @param range If not NULL, the cursom range to use instead of user selection * @note If this function is called, then gather information will not be called. * Derived classes should provide an alternative way of setting up the generator. */ void autoGenerate(DUContext * context, const DocumentRange * range); /** * \return The Document Change set to add a single Change, it is more addicient than creating a local DocumentChangeSet and merging it */ DocumentChangeSet & documentChangeSet(); protected: /** * Generate text edits from duchain change set. * This generator now owns the changeset, and will delete it. * * You may call this method multiple times to edit different files. */ void addChangeSet(DUChainChangeSet* duChainChange); void addChangeSet(DocumentChangeSet & docChangeSet); /** * Accessor for KJob's KJob::setErrorText. */ void setErrorText(const QString & error); /** * Inform the derived class if this generation is being performed without user interaction */ bool autoGeneration() const; /** * Clean up all the change sets that this generator is in charge of */ void clearChangeSets(); private: class CodeGeneratorPrivate * const d; bool displayChanges(); }; /** * \brief Base class for Ast aware code generators * * This class provides convenience for adding AstChangeSet, storing * the IAstContainer from the TopDUContext, and in general managing * Code generators that manipulate the AST * * \see CodeGeneratorBase * \author Ramón Zarazúa */ template class CodeGenerator : public CodeGeneratorBase { public: ~CodeGenerator() { clearChangeSets(); } protected: /// Convenience definition of the TopAstNode that is contained by this AstContainer typedef typename AstContainer::TopAstNode TopAstNode; typedef AstChangeSet LanguageChangeSet; /** * Query an AST of a particular file */ TopAstNode * ast(const IndexedString & file) { return astContainer(file)->topAstNode(); } TopAstNode * ast(const TopDUContext & context) { return astContainer(context)->topAstNode(); } typename AstContainer::Ptr astContainer(const IndexedString & file) { if(!m_AstContainers.contains(file)) { qCDebug(LANGUAGE) << "Ast requested for: " << file.str(); TopDUContext * context = DUChain::self()->waitForUpdate(file, KDevelop::TopDUContext::AST).data(); Q_ASSERT(context); m_AstContainers[file] = AstContainer::Ptr::template staticCast( context->ast() ); } return m_AstContainers[file]; } typename AstContainer::Ptr astContainer(const TopDUContext & context) { return astContainer(context.url()); } /** * Generate text edits from duchain / ast change set. * * You may call this method multiple times to edit different files. */ void addChangeSet(DUChainChangeSet * duChainChange) { CodeGeneratorBase::addChangeSet(duChainChange); } void addChangeSet(DocumentChangeSet & doc) { CodeGeneratorBase::addChangeSet(doc); } /** * Generate text edits from duchain / ast change set. * * You may call this method multiple times to edit different files. */ void addChangeSet(LanguageChangeSet * astChange); void clearChangeSets() { CodeGeneratorBase::clearChangeSets(); } /** * Inform the derived class if this generation is being performed without user interaction */ bool autoGeneration() const { return CodeGeneratorBase::autoGeneration(); } /** * Accessor for KJob's KJob::setErrorText. */ void setErrorText(const QString & error) { CodeGeneratorBase::setErrorText(error); } private: typedef QMap AstContainerMap; AstContainerMap m_AstContainers; }; } #endif // KDEVPLATFORM_CODEGENERATOR_H diff --git a/language/codegen/templaterenderer.h b/language/codegen/templaterenderer.h index ed5bdba0da..fef4b7d660 100644 --- a/language/codegen/templaterenderer.h +++ b/language/codegen/templaterenderer.h @@ -1,208 +1,208 @@ /* * This file is part of KDevelop * Copyright 2012 Miha Čančula * * 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_TEMPLATERENDERER_H #define KDEVPLATFORM_TEMPLATERENDERER_H #include #include class QUrl; namespace KDevelop { class TemplateEngine; class SourceFileTemplate; class DocumentChangeSet; /** * @brief Convenience class for rendering multiple templates with the same context * * The TemplateRenderer provides easy access to common template operations. * Internally, it encapsulates a Grantlee::Engine and a Grantlee::Context. * * It is used by adding a set of variables, and then renderering a template string * @code * TemplateRenderer renderer; * renderer.addVariable("greeting", "Hello"); * renderer.addVariable("target", "World"); * QString text = renderer.render("{{ greeting }}, {{ target }}!"); * // text == "Hello, World!" * @endcode * * If you wish to include other templates using the Grantlee {% include %} tag, * make sure TemplateRenderer can find those template by using * addTemplateDirectories() and addArchive(). This adds everything in the specified * directories or archive files to the list of files search for inclusion. * * Directories named "kdevcodegen/templates" in the "data" resource type are always included in the search path, * there is no need to add them explicitly. Additionally, TemplateRenderer adds the "lib" resource directories * to the Grantlee plugin search path, so plugins installed there will be available to templates. * **/ class KDEVPLATFORMLANGUAGE_EXPORT TemplateRenderer { public: /** * Policy for working with empty lines **/ enum EmptyLinesPolicy { /** * Keep empty lines as they are in the rendered output. * The output from the template is returned unmodified. */ KeepEmptyLines, /** * If the template output has more than one line, the renderer * performs a smart trim on the rendered output. * @li single empty lines are removed * @li two or more consecutive empty lines are compressed into a single empty line * @li a single empty line is kept at the end */ TrimEmptyLines, /** * Removes all empty lines from the template output, and appends a newline at the end if needed. */ RemoveEmptyLines }; TemplateRenderer(); virtual ~TemplateRenderer(); /** * Adds @p variables to the Grantlee::Context passed to each template. * * If the context already contains a variables with the same name as a key in @p variables, * it is overwritten. * **/ void addVariables(const QVariantHash& variables); /** * Adds variable with name @p name and value @p value to the Grantlee::Context passed to each template. * * If the context already contains a variables with the same @p name, it is overwritten. * **/ void addVariable(const QString& name, const QVariant& value); /** * Returns the current variables defined for this renderer * * @sa addVariable(), addVariables() */ QVariantHash variables() const; /** * @brief Renders a single template * * Any rendering errors are reported by errorString(). * If there were no errors, errorString() will return an empty string. * * @param content the template content * @param name (optional) the name of this template * @return the rendered template **/ QString render(const QString& content, const QString& name = QString()) const; /** * @brief Renders a single template from a file * * Any rendering errors are reported by errorString(). * If there were no errors, errorString() will return an empty string. * * @param url the URL of the file from which to load the template * @param name (optional) the name of this template * @return the rendered template **/ QString renderFile(const QUrl& url, const QString& name = QString()) const; /** * @brief Renders a list of templates * * This is a convenience method, suitable if you have to render a large number of templates * with the same context. * - * @param content the template contents + * @param contents the template contents * @return the rendered templates **/ QStringList render(const QStringList& contents) const; /** * @brief Sets the policy for empty lines in the rendered output * * The default is KeepEmptyLines, where the template output is return unmodified. * * @param policy policy for empty lines in the rendered output * @sa EmptyLinesPolicy */ void setEmptyLinesPolicy(EmptyLinesPolicy policy); /** * Returns the currently set policy for empty lines in the rendered output * @sa EmptyLinesPolicy, setEmptyLinesPolicy() */ EmptyLinesPolicy emptyLinesPolicy() const; /** * @brief Renders all templates in the archive represented by @p fileTemplate * * Output files are saved to corresponding URLs in @p fileUrls * * For each output file, TemplateRenderer add two variables named @c output_file_x * and @c output_file_x_absolute, where @c x is replaced * with the file name specified in the template description file. * The variable name is entirely lowercase and cleaned by replacing * all non-alphanumerical characters with underscores. * For example, if the file is named "Public Header" in * the description file, the variable will be @c output_file_public_heder. * * As their name suggests, @c output_file_x contains the relative path from baseUrl() to the URL of the * x's output location, while @c output_file_x_absolute contains x's absolute output URL. * Both are available to templates as strings. * * @param fileTemplate the source file template to render * @param baseUrl the base URL used for calculating relative output file URLs * @param fileUrls maps output file identifiers to desired destination URLs * @return KDevelop::DocumentChangeSet */ DocumentChangeSet renderFileTemplate(const KDevelop::SourceFileTemplate& fileTemplate, const QUrl& baseUrl, const QHash& fileUrls); /** * Returns the error string from the last call to render(), renderFile() or renderFileTemplate(). * If the last render was successful and produced no errors, this function returns an empty string. * * @return the last error string **/ QString errorString() const; private: class TemplateRendererPrivate* const d; }; } #endif // KDEVPLATFORM_TEMPLATERENDERER_H diff --git a/language/codegen/templatesmodel.cpp b/language/codegen/templatesmodel.cpp index 2f36db3e8a..2e6d7463ec 100644 --- a/language/codegen/templatesmodel.cpp +++ b/language/codegen/templatesmodel.cpp @@ -1,431 +1,431 @@ /* This file is part of KDevelop Copyright 2007 Alexander Dymo Copyright 2012 Miha Čančula 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. */ #include "templatesmodel.h" #include "util/debug.h" #include #include #include #include #include #include #include #include #include #include #include using namespace KDevelop; class KDevelop::TemplatesModelPrivate { public: TemplatesModelPrivate(const QString& typePrefix); QString typePrefix; QStringList searchPaths; QMap templateItems; /** * Extracts description files from all available template archives and saves them to a location * determined by descriptionResourceSuffix(). **/ void extractTemplateDescriptions(); /** * Creates a model item for the template @p name in category @p category * * @param name the name of the new template * @param category the category of the new template * @param parent the parent item * @return the created item **/ QStandardItem *createItem(const QString& name, const QString& category, QStandardItem* parent); enum ResourceType { Description, Template, Preview }; QString resourceFilter(ResourceType type, const QString &suffix = QString()) { QString filter = typePrefix; switch(type) { case Description: filter += QLatin1String("template_descriptions/"); break; case Template: filter += QLatin1String("templates/"); break; case Preview: filter += QLatin1String("template_previews/"); break; } return filter + suffix; } }; TemplatesModelPrivate::TemplatesModelPrivate(const QString& _typePrefix) : typePrefix(_typePrefix) { if (!typePrefix.endsWith('/')) { typePrefix.append('/'); } } TemplatesModel::TemplatesModel(const QString& typePrefix, QObject* parent) : QStandardItemModel(parent) , d(new TemplatesModelPrivate(typePrefix)) { const QStringList dataPaths = {QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)}; foreach(const QString& dataPath, dataPaths) { addDataPath(dataPath); } } TemplatesModel::~TemplatesModel() { delete d; } void TemplatesModel::refresh() { clear(); d->templateItems.clear(); d->templateItems[QString()] = invisibleRootItem(); d->extractTemplateDescriptions(); QStringList templateArchives; foreach(const QString& archivePath, d->searchPaths) { const QStringList files = QDir(archivePath).entryList(QDir::Files); foreach(const QString& file, files) { templateArchives.append(archivePath + file); } } QStringList templateDescriptions; const QStringList templatePaths = {QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) +'/'+ d->resourceFilter(TemplatesModelPrivate::Description)}; foreach(const QString& templateDescription, templatePaths) { const QStringList files = QDir(templateDescription).entryList(QDir::Files); foreach(const QString& file, files) { templateDescriptions.append(templateDescription + file); } } foreach (const QString &templateDescription, templateDescriptions) { QFileInfo fi(templateDescription); bool archiveFound = false; foreach( const QString& templateArchive, templateArchives ) { if( QFileInfo(templateArchive).baseName() == fi.baseName() ) { archiveFound = true; KConfig templateConfig(templateDescription); KConfigGroup general(&templateConfig, "General"); QString name = general.readEntry("Name"); QString category = general.readEntry("Category"); QString comment = general.readEntry("Comment"); QStandardItem *templateItem = d->createItem(name, category, invisibleRootItem()); templateItem->setData(templateDescription, DescriptionFileRole); templateItem->setData(templateArchive, ArchiveFileRole); templateItem->setData(comment, CommentRole); if (general.hasKey("Icon")) { QString icon = QStandardPaths::locate(QStandardPaths::GenericDataLocation, d->resourceFilter(TemplatesModelPrivate::Preview, general.readEntry("Icon"))); if (QFile::exists(icon)) { templateItem->setData(icon, IconNameRole); } } } } if (!archiveFound) { // Template file doesn't exist anymore, so remove the description // saves us the extra lookups for templateExists on the next run QFile(templateDescription).remove(); } } } QStandardItem *TemplatesModelPrivate::createItem(const QString& name, const QString& category, QStandardItem* parent) { QStringList path = category.split('/'); QStringList currentPath; foreach (const QString& entry, path) { currentPath << entry; if (!templateItems.contains(currentPath.join(QLatin1Char('/')))) { QStandardItem *item = new QStandardItem(entry); item->setEditable(false); parent->appendRow(item); templateItems[currentPath.join(QLatin1Char('/'))] = item; parent = item; } else { parent = templateItems[currentPath.join(QLatin1Char('/'))]; } } QStandardItem *templateItem = new QStandardItem(name); templateItem->setEditable(false); parent->appendRow(templateItem); return templateItem; } void TemplatesModelPrivate::extractTemplateDescriptions() { QStringList templateArchives; searchPaths << QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, resourceFilter(Template), QStandardPaths::LocateDirectory); searchPaths.removeDuplicates(); foreach(const QString &archivePath, searchPaths) { const QStringList files = QDir(archivePath).entryList(QDir::Files); foreach(const QString& file, files) { if(file.endsWith(QLatin1String(".zip")) || file.endsWith(QLatin1String(".tar.bz2"))) { QString archfile = archivePath + file; templateArchives.append(archfile); } } } QString localDescriptionsDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) +'/'+ resourceFilter(Description); QDir dir(localDescriptionsDir); if(!dir.exists()) dir.mkpath(QStringLiteral(".")); foreach (const QString &archName, templateArchives) { qCDebug(LANGUAGE) << "processing template" << archName; QScopedPointer templateArchive; if (QFileInfo(archName).completeSuffix() == QLatin1String("zip")) { templateArchive.reset(new KZip(archName)); } else { templateArchive.reset(new KTar(archName)); } if (templateArchive->open(QIODevice::ReadOnly)) { /* * This class looks for template description files in the following order * * - "basename.kdevtemplate" * - "*.kdevtemplate" * - "basename.desktop" * - "*.desktop" * * This is done because application templates can contain .desktop files used by the application * so the kdevtemplate suffix must have priority. */ QFileInfo templateInfo(archName); const KArchiveEntry *templateEntry = templateArchive->directory()->entry(templateInfo.baseName() + ".kdevtemplate"); if (!templateEntry || !templateEntry->isFile()) { /* * First, if the .kdevtemplate file is not found by name, * we check all the files in the archive for any .kdevtemplate file * * This is needed because kde-files.org renames downloaded files */ foreach (const QString& entryName, templateArchive->directory()->entries()) { if (entryName.endsWith(QLatin1String(".kdevtemplate"))) { templateEntry = templateArchive->directory()->entry(entryName); break; } } } if (!templateEntry || !templateEntry->isFile()) { templateEntry = templateArchive->directory()->entry(templateInfo.baseName() + ".desktop"); } if (!templateEntry || !templateEntry->isFile()) { foreach (const QString& entryName, templateArchive->directory()->entries()) { if (entryName.endsWith(QLatin1String(".desktop"))) { templateEntry = templateArchive->directory()->entry(entryName); break; } } } if (!templateEntry || !templateEntry->isFile()) { qCDebug(LANGUAGE) << "template" << archName << "does not contain .kdevtemplate or .desktop file"; continue; } const KArchiveFile *templateFile = static_cast(templateEntry); qCDebug(LANGUAGE) << "copy template description to" << localDescriptionsDir; templateFile->copyTo(localDescriptionsDir); /* * Rename the extracted description * so that its basename matches the basename of the template archive */ QFileInfo descriptionInfo(localDescriptionsDir + templateEntry->name()); QString destinationName = localDescriptionsDir + templateInfo.baseName() + '.' + descriptionInfo.suffix(); QFile::rename(descriptionInfo.absoluteFilePath(), destinationName); KConfig config(destinationName); KConfigGroup group(&config, "General"); if (group.hasKey("Icon")) { const KArchiveEntry* iconEntry = templateArchive->directory()->entry(group.readEntry("Icon")); if (iconEntry && iconEntry->isFile()) { const KArchiveFile* iconFile = static_cast(iconEntry); const QString saveDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) +'/'+ resourceFilter(Preview); iconFile->copyTo(saveDir); QFileInfo iconInfo(saveDir + templateEntry->name()); QFile::rename(iconInfo.absoluteFilePath(), saveDir + templateInfo.baseName() + '.' + iconInfo.suffix()); } } } else { - qCDebug(LANGUAGE) << "could not open template" << archName; + qCWarning(LANGUAGE) << "could not open template" << archName; } } } QModelIndexList TemplatesModel::templateIndexes(const QString& fileName) const { QFileInfo info(fileName); QString description = QStandardPaths::locate(QStandardPaths::GenericDataLocation, d->resourceFilter(TemplatesModelPrivate::Description, info.baseName() + ".kdevtemplate")); if (description.isEmpty()) { description = QStandardPaths::locate(QStandardPaths::GenericDataLocation, d->resourceFilter(TemplatesModelPrivate::Description, info.baseName() + ".desktop")); } QModelIndexList indexes; if (!description.isEmpty()) { KConfig templateConfig(description); KConfigGroup general(&templateConfig, "General"); QStringList categories = general.readEntry("Category").split('/'); QStringList levels; foreach (const QString& category, categories) { levels << category; indexes << d->templateItems[levels.join(QString('/'))]->index(); } if (!indexes.isEmpty()) { QString name = general.readEntry("Name"); QStandardItem* categoryItem = d->templateItems[levels.join(QString('/'))]; for (int i = 0; i < categoryItem->rowCount(); ++i) { QStandardItem* templateItem = categoryItem->child(i); if (templateItem->text() == name) { indexes << templateItem->index(); break; } } } } return indexes; } QString TemplatesModel::typePrefix() const { return d->typePrefix; } void TemplatesModel::addDataPath(const QString& path) { QString realpath = path + d->resourceFilter(TemplatesModelPrivate::Template); d->searchPaths.append(realpath); } QString TemplatesModel::loadTemplateFile(const QString& fileName) { QString saveLocation = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) +'/'+ d->resourceFilter(TemplatesModelPrivate::Template); QDir dir(saveLocation); if(!dir.exists()) dir.mkpath(QStringLiteral(".")); QFileInfo info(fileName); QString destination = saveLocation + info.baseName(); QMimeType mimeType = QMimeDatabase().mimeTypeForFile(fileName); qCDebug(LANGUAGE) << "Loaded file" << fileName << "with type" << mimeType.name(); if (mimeType.name() == QLatin1String("application/x-desktop")) { qCDebug(LANGUAGE) << "Loaded desktop file" << info.absoluteFilePath() << ", compressing"; #ifdef Q_WS_WIN destination += ".zip"; KZip archive(destination); #else destination += QLatin1String(".tar.bz2"); KTar archive(destination, QStringLiteral("application/x-bzip")); #endif //Q_WS_WIN archive.open(QIODevice::WriteOnly); QDir dir(info.absoluteDir()); QDir::Filters filter = QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot; foreach (const QFileInfo& entry, dir.entryInfoList(filter)) { if (entry.isFile()) { archive.addLocalFile(entry.absoluteFilePath(), entry.fileName()); } else if (entry.isDir()) { archive.addLocalDirectory(entry.absoluteFilePath(), entry.fileName()); } } archive.close(); } else { qCDebug(LANGUAGE) << "Copying" << fileName << "to" << saveLocation; QFile::copy(fileName, saveLocation + info.fileName()); } refresh(); return destination; } diff --git a/language/codegen/utilities.h b/language/codegen/utilities.h index 00f9e62d19..07668a8e90 100644 --- a/language/codegen/utilities.h +++ b/language/codegen/utilities.h @@ -1,74 +1,75 @@ /* Copyright 2009 Ramón Zarazúa 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_CODEGEN_UTILITIES_H +#define KDEVPLATFORM_CODEGEN_UTILITIES_H + #include #include -#ifndef KDEVPLATFORM_CODEGEN_UTILITIES_H -#define KDEVPLATFORM_CODEGEN_UTILITIES_H namespace KDevelop { class IndexedString; class DUContext; class Declaration; namespace CodeGenUtils { /*! * A validator object that verifies if a string would be an acceptable identifier * If inserted into the given context, including if it conflicts with any other identifier */ class KDEVPLATFORMLANGUAGE_EXPORT IdentifierValidator : public QValidator { Q_OBJECT public: explicit IdentifierValidator( DUContext * context); ~IdentifierValidator() override; State validate(QString & input, int &) const override; private: DUContext * m_context; }; /** * @brief Search for the file that contains the implementation of a specified type * * Search for the file that contains the implementation of @p targetClass. For languages that * allow implementation of a type through multiple files, the file with the most implementations of * class methods will be chosen, if a tie is found, then the file with the most uses will be chosen. * Else the file that contains the declaration is chosen. * * @note If called with a Forward declaration, the real declaration will be searched for. * * @return The file that matched best */ KDEVPLATFORMLANGUAGE_EXPORT IndexedString fetchImplementationFileForClass(const Declaration & targetClass); } } #endif //KDEVPLATFORM_CODEGEN_UTILITIES_H diff --git a/language/duchain/aliasdeclaration.h b/language/duchain/aliasdeclaration.h index ea201ac6f1..ce07504196 100644 --- a/language/duchain/aliasdeclaration.h +++ b/language/duchain/aliasdeclaration.h @@ -1,96 +1,95 @@ /* This file is part of KDevelop Copyright 2008 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDEVPLATFORM_ALIASDECLARATION_H #define KDEVPLATFORM_ALIASDECLARATION_H #include "classmemberdeclaration.h" #include "classmemberdeclarationdata.h" #include "duchainpointer.h" #include "declarationdata.h" namespace KDevelop { class KDEVPLATFORMLANGUAGE_EXPORT AliasDeclarationData : public ClassMemberDeclarationData { public: AliasDeclarationData() {} AliasDeclarationData( const AliasDeclarationData& rhs ) : ClassMemberDeclarationData( rhs ) { m_aliasedDeclaration = rhs.m_aliasedDeclaration; } IndexedDeclaration m_aliasedDeclaration; }; /** * An alias declaration maps one declaration to another. * While searching in the duchain, an AliasDeclaration is transparently * replaced by its aliased declaration. */ class KDEVPLATFORMLANGUAGE_EXPORT AliasDeclaration : public ClassMemberDeclaration { public: /// Copy constructor \param rhs declaration to copy AliasDeclaration(const AliasDeclaration& rhs); /** * Constructs an AliasDeclaration. The default value for isNamespaceAlias is true. * - * \param url url of the document where this occurred * \param range range of the alias declaration's identifier * \param context context in which this declaration occurred */ AliasDeclaration(const RangeInRevision& range, DUContext* context); explicit AliasDeclaration(AliasDeclarationData& data); /// Destructor virtual ~AliasDeclaration(); /** * An AliasDeclaration cannot have a type, so setAbstractType does nothing here. * * \param type ignored type */ virtual void setAbstractType(AbstractType::Ptr type) override; /** * Set the declaration that is aliased by this declaration. * * \param decl the declaration that this declaration references */ void setAliasedDeclaration(const IndexedDeclaration& decl); /** * Access the declaration that is aliased by this declaration. * * \returns the aliased declaration */ IndexedDeclaration aliasedDeclaration() const; virtual QString toString() const override; enum { Identity = 6 }; private: virtual Declaration* clonePrivate() const override; DUCHAIN_DECLARE_DATA(AliasDeclaration) }; } #endif // KDEVPLATFORM_FUNCTIONDECLARATION_H diff --git a/language/duchain/builders/abstractcontextbuilder.h b/language/duchain/builders/abstractcontextbuilder.h index 6c77d86cb2..8e58293110 100644 --- a/language/duchain/builders/abstractcontextbuilder.h +++ b/language/duchain/builders/abstractcontextbuilder.h @@ -1,671 +1,671 @@ /*************************************************************************** * This file is part of KDevelop * * Copyright 2008 Andreas Pakulat * * Copyright 2006 Roberto Raggi * * Copyright 2006-2008 Hamish Rodda * * * * 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_ABSTRACTABSTRACTCONTEXTBUILDER_H #define KDEVPLATFORM_ABSTRACTABSTRACTCONTEXTBUILDER_H #include #include #include #include "../topducontext.h" #include "../duchainpointer.h" #include "../duchainlock.h" #include "../duchain.h" #include "../ducontext.h" #include "../identifier.h" #include "../parsingenvironment.h" #include #include namespace KDevelop { /** * \short Abstract definition-use chain context builder class * * The AbstractContextBuilder is a convenience class template for creating customized * definition-use chain context builders from an AST. It simplifies: * - use of your editor integrator * - creating or modifying an existing DUContext tree * - following a DUContext tree for second and subsequent passes, if required * - opening and closing DUContext instances * - tracking which DUContext instances are still present when recompiling, and removing DUContexts which no longer exist in the source code. * * \author Hamish Rodda \ */ template class AbstractContextBuilder { public: /// Constructor. AbstractContextBuilder() : m_compilingContexts( false ) , m_recompiling( false ) , m_lastContext( 0 ) { } /// Destructor. Deletes the editor integrator, if one was created specifically for this builder only. virtual ~AbstractContextBuilder() { } /** * Entry point for building a definition-use chain with this builder. * * This function determines whether we are updating a chain, or creating a new one. If we are * creating a new chain, a new TopDUContext is created and registered with DUChain. * * \param url Url of the document being parsed. * \param node AST node to start building from. * \param updateContext TopDUContext to update if a duchain was previously created for this url, otherwise pass a null pointer. * * \returns the newly created or updated TopDUContext pointer. */ virtual ReferencedTopDUContext build( const IndexedString& url, T* node, ReferencedTopDUContext updateContext = ReferencedTopDUContext() ) { m_compilingContexts = true; m_url = url; ReferencedTopDUContext top; { DUChainWriteLocker lock( DUChain::lock() ); top = updateContext.data(); if( top ) { m_recompiling = true; Q_ASSERT(top->type() == DUContext::Global); Q_ASSERT(DUChain::self()->chainForIndex(top->ownIndex()) == top); } else { top = newTopContext( RangeInRevision( CursorInRevision( 0, 0 ), CursorInRevision( INT_MAX, INT_MAX ) ) ); DUChain::self()->addDocumentChain( top ); top->setType( DUContext::Global ); } setEncountered( top ); setContextOnNode( node, top ); } supportBuild( node, top ); m_compilingContexts = false; return top; } protected: /** * Support another builder by tracking the current context. * @param context the context to use. Must be set when the given node has no context. When it has one attached, this parameter is not needed. */ virtual void supportBuild( T* node, DUContext* context = 0 ) { if (!context) context = contextFromNode(node); Q_ASSERT(context); openContext( context ); startVisiting(node); closeContext(); Q_ASSERT(m_contextStack.isEmpty()); } /** * Entry point to your visitor. Reimplement and call the appropriate visit function. * * \param node AST node to visit. */ virtual void startVisiting( T* node ) = 0; /** * Associate a \a context with a given AST \a node. Once called on a \a node, the * contextFromNode() function should return this \a context when called. * * \param node AST node to associate * \param context DUContext to associate */ virtual void setContextOnNode( T* node, DUContext* context ) = 0; /** * Retrieve an associated DUContext from the given \a node. Used on second and * subsequent passes of the context builder (for supporting other builds) * * \param node AST node which was previously associated * \returns the DUContext which was previously associated */ virtual DUContext* contextFromNode( T* node ) = 0; /** * Retrieves a text range from the given nodes. * * As editor integrators have to be extended to determine ranges from AST nodes, * this function must be reimplemented to allow generic retrieving of rangs from nodes. * * \param fromNode the AST node to start from (on the start boundary) * \param toNode the AST node to end at (on the end boundary) * * \returns the text range encompassing the given AST node(s) */ virtual RangeInRevision editorFindRange( T* fromNode, T* toNode ) = 0; /** * Retrieve a text range for the given nodes. This is a special function required * by c++ support as a different range may need to be retrieved depending on * whether macros are involved. It is not usually required to implement this * function separately to editorFindRange() for other languages. * * \param fromNode the AST node to start from (on the start boundary) * \param toNode the AST node to end at (on the end boundary) * * \returns the text range encompassing the given AST node(s) */ virtual RangeInRevision editorFindRangeForContext( T* fromNode, T* toNode ) { return editorFindRange(fromNode, toNode); } /** * Determine the QualifiedIdentifier which corresponds to the given ast \a node. * * \param node ast node which represents an identifier * \return the qualified identifier determined from \a node */ virtual QualifiedIdentifier identifierForNode( NameT* node ) = 0; /** * Create a new DUContext from the given \a range. * * This exists so that you can create custom DUContext subclasses for your * language if you need to. * * \param range range for the new context to encompass * \returns the newly created context */ virtual DUContext* newContext(const RangeInRevision& range) { return new DUContext(range, currentContext()); } /** * Create a new TopDUContext from the given \a range. * * This exists so that you can create custom TopDUContext subclasses for your * language if you need to. * * \returns the newly created context */ virtual TopDUContext* newTopContext(const RangeInRevision& range, ParsingEnvironmentFile* file = 0) { return new TopDUContext(m_url, range, file); } /// Determine the currently open context. \returns the current context. inline DUContext* currentContext() const { return m_contextStack.top(); } /// Determine the last closed context. \returns the last closed context. inline DUContext* lastContext() const { return m_lastContext; } /// Clears the last closed context. inline void clearLastContext() { m_lastContext = 0; } inline void setLastContext(DUContext* context) { m_lastContext = context; } TopDUContext* topContext() const { return currentContext()->topContext(); } /** * Determine if we are recompiling an existing definition-use chain, or if * a new chain is being created from scratch. * * \returns true if an existing duchain is being updated, otherwise false. */ inline bool recompiling() const { return m_recompiling; } /** * Tell the context builder whether we are recompiling an existing definition-use chain, or if * a new chain is being created from scratch. * * \param recomp set to true if an existing duchain is being updated, otherwise false. */ inline void setRecompiling(bool recomp) { m_recompiling = recomp; } /** * Determine whether this pass will create DUContext instances. * * On the first pass of definition-use chain compiling, DUContext instances * are created to represent contexts in the source code. These contexts are * associated with their AST nodes at the time (see setContextOnNode()). * * On second and subsequent passes, the contexts already exist and thus can be * retrieved through contextFromNode(). * * \returns true if compiling contexts (ie. 1st pass), otherwise false. */ inline bool compilingContexts() const { return m_compilingContexts; } /** * Sets whether we need to create ducontexts, ie. if this is the first pass. * * \sa compilingContexts() */ inline void setCompilingContexts(bool compilingContexts) { m_compilingContexts = compilingContexts; } /** * Create child contexts for only a portion of the document. * * \param node The AST node which corresponds to the context to parse * \param parent The DUContext which encompasses the \a node. * \returns The DUContext which was reparsed, ie. \a parent. */ DUContext* buildSubContexts( T *node, DUContext* parent ) { // m_compilingContexts = true; // m_recompiling = false; setContextOnNode( node, parent ); { openContext( contextFromNode( node ) ); startVisiting( node ); closeContext(); } m_compilingContexts = false; if ( contextFromNode( node ) == parent ) { qDebug() << "Error in AbstractContextBuilder::buildSubContexts(...): du-context was not replaced with new one"; DUChainWriteLocker lock( DUChain::lock() ); deleteContextOnNode( node ); } return contextFromNode( node ); } /** * Delete the DUContext which is associated with the given \a node, * and remove the association. * * \param node Node which is associated with the context to delete. */ void deleteContextOnNode( T* node ) { delete contextFromNode( node ); setContextOnNode( node, 0 ); } /** * Open a context, and create / update it if necessary. * * \param rangeNode The range which encompasses the context. * \param type The type of context to open. * \param identifier The range which encompasses the name of this context, if one exists. * \returns the opened context. */ DUContext* openContext( T* rangeNode, DUContext::ContextType type, NameT* identifier = 0) { if ( m_compilingContexts ) { DUContext* ret = openContextInternal( editorFindRangeForContext( rangeNode, rangeNode ), type, identifier ? identifierForNode( identifier ) : QualifiedIdentifier() ); setContextOnNode( rangeNode, ret ); return ret; } else { openContext( contextFromNode(rangeNode) ); return currentContext(); } } /** * Open a context, and create / update it if necessary. * * \param node The range to associate with the context. * \param range A custom range which the context should encompass. * \param type The type of context to open. * \param identifier The range which encompasses the name of this context, if one exists. * \returns the opened context. */ DUContext* openContext(T* node, const RangeInRevision& range, DUContext::ContextType type, NameT* identifier = 0) { if (m_compilingContexts) { DUContext* ret = openContextInternal(range, type, identifier ? identifierForNode(identifier) : QualifiedIdentifier()); setContextOnNode( node, ret ); return ret; } else { openContext( contextFromNode(node) ); return currentContext(); } } /** * Open a context, and create / update it if necessary. * * \param node The range to associate with the context. * \param range A custom range which the context should encompass. * \param type The type of context to open. - * \param identifier The identifier for this context + * \param id The identifier for this context * \returns the opened context. */ DUContext* openContext(T* node, const RangeInRevision& range, DUContext::ContextType type, const QualifiedIdentifier& id) { if (m_compilingContexts) { DUContext* ret = openContextInternal(range, type, id); setContextOnNode( node, ret ); return ret; } else { openContext( contextFromNode(node) ); return currentContext(); } } /** * Open a context, and create / update it if necessary. * * \param rangeNode The range which encompasses the context. * \param type The type of context to open. * \param identifier The identifier which corresponds to the context. * \returns the opened context. */ DUContext* openContext( T* rangeNode, DUContext::ContextType type, const QualifiedIdentifier& identifier ) { if ( m_compilingContexts ) { DUContext* ret = openContextInternal( editorFindRangeForContext( rangeNode, rangeNode ), type, identifier ); setContextOnNode( rangeNode, ret ); return ret; } else { openContext( contextFromNode(rangeNode) ); return currentContext(); } } /** * Open a context, and create / update it if necessary. * * \param fromRange The range which starts the context. * \param toRange The range which ends the context. * \param type The type of context to open. * \param identifier The identifier which corresponds to the context. * \returns the opened context. */ DUContext* openContext( T* fromRange, T* toRange, DUContext::ContextType type, const QualifiedIdentifier& identifier = QualifiedIdentifier() ) { if ( m_compilingContexts ) { DUContext* ret = openContextInternal( editorFindRangeForContext( fromRange, toRange ), type, identifier ); setContextOnNode( fromRange, ret ); return ret; } else { openContext( contextFromNode(fromRange) ); return currentContext(); } } /** * Open a newly created or previously existing context. * * The open context is put on the context stack, and becomes the new * currentContext(). * * \warning When you call this, you also have to open a range! If you want to re-use * the range associated to the context, use injectContext * * \param newContext Context to open. */ virtual void openContext( DUContext* newContext ) { m_contextStack.push( newContext ); m_nextContextStack.push( 0 ); } /** * This can be used to temporarily change the current context. - * \param range The range that will be used as new current range, or zero(then the range associated to the context is used) + * \param ctx The context to be injected * */ void injectContext( DUContext* ctx ) { openContext( ctx ); } /** * Use this to close the context previously injected with injectContext. * */ void closeInjectedContext() { m_contextStack.pop(); m_nextContextStack.pop(); } /** * Close the current DUContext. When recompiling, this function will remove any * contexts that were not encountered in this passing run. * \note The DUChain write lock is already held here. */ virtual void closeContext() { { DUChainWriteLocker lock( DUChain::lock() ); //Remove all slaves that were not encountered while parsing if(m_compilingContexts) currentContext()->cleanIfNotEncountered( m_encountered ); setEncountered( currentContext() ); m_lastContext = currentContext(); } m_contextStack.pop(); m_nextContextStack.pop(); } /** * Remember that a specific item has been encoutered while parsing. * All items that are not encountered will be deleted at some stage. * * \param item duchain item that was encountered. * */ void setEncountered( DUChainBase* item ) { m_encountered.insert( item ); } /** * Determine whether the given \a item is in the set of encountered items. * * @return true if the \a item has been encountered, otherwise false. * */ bool wasEncountered( DUChainBase* item ) { return m_encountered.contains( item ); } /** * Set the current identifier to \a id. * * \param id the new current identifier. */ void setIdentifier( const QString& id ) { m_identifier = Identifier( id ); m_qIdentifier.push( m_identifier ); } /** * Determine the current identifier. * \returns the current identifier. */ QualifiedIdentifier qualifiedIdentifier() const { return m_qIdentifier; } /** * Clears the current identifier. */ void clearQualifiedIdentifier() { m_qIdentifier.clear(); } /** * Retrieve the current context stack. This function is not expected * to be used often and may be phased out. * * \todo Audit whether access to the context stack is still required, and provide * replacement functionality if possible. */ const Stack& contextStack() const { return m_contextStack; } /** * Access the index of the child context which has been encountered. * * \todo further delineate the role of this function and rename / document better. * \todo make private again? */ int& nextContextIndex() { return m_nextContextStack.top(); } /** * Open a context, either creating it if it does not exist, or referencing a previously existing * context if already encountered in a previous duchain parse run (when recompiling()). * * \param range The range of the context. * \param type The type of context to create. * \param identifier The identifier which corresponds to the context. * \returns the opened context. */ virtual DUContext* openContextInternal( const RangeInRevision& range, DUContext::ContextType type, const QualifiedIdentifier& identifier ) { Q_ASSERT( m_compilingContexts ); DUContext* ret = 0L; { if ( recompiling() ) { DUChainReadLocker readLock( DUChain::lock() ); const QVector& childContexts = currentContext()->childContexts(); int currentIndex = nextContextIndex(); const auto indexedIdentifier = IndexedQualifiedIdentifier(identifier); for ( ; currentIndex < childContexts.count(); ++currentIndex ) { DUContext* child = childContexts.at( currentIndex ); RangeInRevision childRange = child->range(); if (child->type() != type) { continue; } // We cannot update a contexts local scope identifier, that will break many other parts, like e.g. // the CodeModel of child contexts or declarations. // For unnamed child-ranges, we still do range-comparison, because we cannot distinguish them in other ways if ((!identifier.isEmpty() && child->indexedLocalScopeIdentifier() == indexedIdentifier) || (identifier.isEmpty() && child->indexedLocalScopeIdentifier().isEmpty() && !childRange.isEmpty() && childRange == range)) { // Match ret = child; readLock.unlock(); DUChainWriteLocker writeLock( DUChain::lock() ); ret->clearImportedParentContexts(); ++currentIndex; break; } } if(ret) nextContextIndex() = currentIndex; //If we had a match, jump forward to that position ///@todo We should also somehow make sure we don't get quadratic worst-case effort while updating. } if ( !ret ) { DUChainWriteLocker writeLock( DUChain::lock() ); ret = newContext( range ); ret->setType( type ); if ( !identifier.isEmpty() ) ret->setLocalScopeIdentifier( identifier ); setInSymbolTable( ret ); }else{ DUChainWriteLocker writeLock( DUChain::lock() ); Q_ASSERT(ret->localScopeIdentifier() == identifier); if(ret->parentContext()) ret->setRange( range ); } } m_encountered.insert( ret ); openContext( ret ); return ret; } ///This function should call context->setInSymbolTable(..) with an appropriate decision. The duchain is write-locked when this is called. virtual void setInSymbolTable(DUContext* context) { if(!context->parentContext()->inSymbolTable()) { context->setInSymbolTable(false); return; } DUContext::ContextType type = context->type(); context->setInSymbolTable(type == DUContext::Class || type == DUContext::Namespace || type == DUContext::Global || type == DUContext::Helper || type == DUContext::Enum); } /// @returns the current url/path ot the document we are parsing IndexedString document() const { return m_url; } private: Identifier m_identifier; IndexedString m_url; QualifiedIdentifier m_qIdentifier; bool m_compilingContexts : 1; bool m_recompiling : 1; Stack m_nextContextStack; DUContext* m_lastContext; //Here all valid declarations/uses/... will be collected QSet m_encountered; Stack m_contextStack; }; } #endif diff --git a/language/duchain/builders/abstractdeclarationbuilder.h b/language/duchain/builders/abstractdeclarationbuilder.h index 45040b46d1..c57f1359e5 100644 --- a/language/duchain/builders/abstractdeclarationbuilder.h +++ b/language/duchain/builders/abstractdeclarationbuilder.h @@ -1,215 +1,212 @@ /* This file is part of KDevelop Copyright 2006-2008 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 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_ABSTRACTDECLARATIONBUILDER_H #define KDEVPLATFORM_ABSTRACTDECLARATIONBUILDER_H #include #include "../classfunctiondeclaration.h" #include "../forwarddeclaration.h" #include "../types/identifiedtype.h" #include "../functiondeclaration.h" namespace KDevelop { class Declaration; /** * A class which iterates the AST to extract definitions of types. */ template class AbstractDeclarationBuilder : public LanguageSpecificDeclarationBuilderBase { protected: /// Determine if there is currently a declaration open. \returns true if a declaration is open, otherwise false. inline bool hasCurrentDeclaration() const { return !m_declarationStack.isEmpty(); } /// Access the current declaration. \returns the current declaration, or null if there is no current declaration. inline Declaration* currentDeclaration() const { return m_declarationStack.isEmpty() ? 0 : m_declarationStack.top(); } /// Access the current declaration, casted to type \a DeclarationType. \returns the current declaration if one exists and is an instance of the given \a DeclarationType. template inline DeclarationType* currentDeclaration() const { return m_declarationStack.isEmpty() ? 0 : dynamic_cast(m_declarationStack.top()); } /// Access the current comment. \returns the current comment, or an empty string if none exists. inline const QByteArray& comment() const { return m_lastComment; } /// Set the current \a comment. \param comment the new comment. inline void setComment(const QByteArray& comment) { m_lastComment = comment; } /// Clears the current comment. inline void clearComment() { m_lastComment.clear(); } enum DeclarationFlags { NoFlags = 0x0, DeclarationIsDefinition = 0x1 }; /** - * Register a new declaration with the definition-use chain. - * Returns the new declaration created. - * \param name When this is zero, the identifier given through customName is used. - * \param range provide a valid AST node here if name is null. - * \param isFunction whether the new declaration is a function. - * \param isForward whether the new declaration is a forward declaration. - * \param isDefinition whether the new declaration is also a definition. + * Register a new declaration with the definition-use chain + * \param name When this is zero, the identifier given through customName is used + * \param range provide a valid AST node here if name is null + * \param flags equal to DeclarationIsDefinition whether the new declaration is also a definition + * \return the new declaration created */ template DeclarationT* openDeclaration(NameT* name, T* range, DeclarationFlags flags = NoFlags) { DUChainWriteLocker lock(DUChain::lock()); RangeInRevision newRange = this->editorFindRange(name ? name : range, name ? name : range); QualifiedIdentifier id = this->identifierForNode(name); return openDeclaration(id, newRange, flags); } /** * \copydoc * * \param id the identifier of the new declaration. * \param newRange the range which the identifier for the new declaration occupies. - * \param isFunction whether the new declaration is a function. - * \param isForward whether the new declaration is a forward declaration. - * \param isDefinition whether the new declaration is also a definition. + * \param flags equal to DeclarationIsDefinition whether the new declaration is also a definition + * \return the new declaration created */ template DeclarationT* openDeclaration(const QualifiedIdentifier& id, const RangeInRevision& newRange, DeclarationFlags flags = NoFlags) { Identifier localId; if(!id.isEmpty()) { localId = id.last(); } DeclarationT* declaration = 0; if (LanguageSpecificDeclarationBuilderBase::recompiling()) { // Seek a matching declaration QList declarations = LanguageSpecificDeclarationBuilderBase::currentContext()->findLocalDeclarations(localId, CursorInRevision::invalid(), this->topContext(), AbstractType::Ptr(), DUContext::NoFiltering); foreach( Declaration* dec, declarations ) { if( LanguageSpecificDeclarationBuilderBase::wasEncountered(dec) ) continue; if (dec->range() == newRange && (localId == dec->identifier() || (localId.isUnique() && dec->identifier().isUnique())) && typeid(*dec) == typeid(DeclarationT) //&& extraDeclarationComparisons() ) { // Match declaration = dynamic_cast(dec); break; } } } if (!declaration) { declaration = new DeclarationT(newRange, LanguageSpecificDeclarationBuilderBase::currentContext()); if (flags & DeclarationIsDefinition) declaration->setDeclarationIsDefinition(true); declaration->setIdentifier(localId); } declaration->setComment(m_lastComment); m_lastComment.clear(); LanguageSpecificDeclarationBuilderBase::setEncountered(declaration); openDeclarationInternal(declaration); return declaration; } /// Convenience function. Same as openDeclaration(), but creates the declaration as a definition. template DeclarationT* openDefinition(NameT* name, T* range) { return openDeclaration(name, range, DeclarationIsDefinition); } /// Convenience function. Same as openDeclaration(), but creates the declaration as a definition. template DeclarationT* openDefinition(const QualifiedIdentifier& id, const RangeInRevision& newRange) { return openDeclaration(id, newRange, DeclarationIsDefinition); } /// Internal function to open the given \a declaration by pushing it onto the declaration stack. /// Provided for subclasses who don't want to use the generic openDeclaration() functions. void openDeclarationInternal(Declaration* declaration) { m_declarationStack.push(declaration); } /// Convenience function. Same as openDeclaration(), but creates a forward declaration. ForwardDeclaration* openForwardDeclaration(NameT* name, T* range) { return openDeclaration(name, range); } /// Set the internal context of a declaration; for example, a class declaration's internal context /// is the context inside the brackets: class ClassName { ... } void eventuallyAssignInternalContext() { if (LanguageSpecificDeclarationBuilderBase::lastContext()) { DUChainWriteLocker lock(DUChain::lock()); if( dynamic_cast(currentDeclaration()) ) Q_ASSERT( !static_cast(currentDeclaration())->isConstructor() || currentDeclaration()->context()->type() == DUContext::Class ); if(LanguageSpecificDeclarationBuilderBase::lastContext() && (LanguageSpecificDeclarationBuilderBase::lastContext()->type() == DUContext::Class || LanguageSpecificDeclarationBuilderBase::lastContext()->type() == DUContext::Other || LanguageSpecificDeclarationBuilderBase::lastContext()->type() == DUContext::Function || LanguageSpecificDeclarationBuilderBase::lastContext()->type() == DUContext::Template || LanguageSpecificDeclarationBuilderBase::lastContext()->type() == DUContext::Enum || (LanguageSpecificDeclarationBuilderBase::lastContext()->type() == DUContext::Namespace && currentDeclaration()->kind() == Declaration::Namespace) ) ) { if( !LanguageSpecificDeclarationBuilderBase::lastContext()->owner() || !LanguageSpecificDeclarationBuilderBase::wasEncountered(LanguageSpecificDeclarationBuilderBase::lastContext()->owner()) ) { //if the context is already internalContext of another declaration, leave it alone currentDeclaration()->setInternalContext(LanguageSpecificDeclarationBuilderBase::lastContext()); LanguageSpecificDeclarationBuilderBase::clearLastContext(); } } } } /// Close a declaration. Virtual to allow subclasses to perform customisations to declarations. virtual void closeDeclaration() { m_declarationStack.pop(); } /// Abort a declaration, deleting it. void abortDeclaration() { delete m_declarationStack.pop(); } private: Stack m_declarationStack; QByteArray m_lastComment; }; } #endif // KDEVPLATFORM_ABSTRACTDECLARATIONBUILDER_H diff --git a/language/duchain/builders/abstractusebuilder.h b/language/duchain/builders/abstractusebuilder.h index 3636702794..4b61565091 100644 --- a/language/duchain/builders/abstractusebuilder.h +++ b/language/duchain/builders/abstractusebuilder.h @@ -1,220 +1,220 @@ /* This file is part of KDevelop Copyright 2006-2008 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 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_ABSTRACTUSEBUILDER_H #define KDEVPLATFORM_ABSTRACTUSEBUILDER_H #include "../declaration.h" #include "../use.h" #include "../topducontext.h" #include "../duchain.h" #include "../duchainlock.h" #include namespace KDevelop { /** * \short Abstract definition-use chain use builder class * * The AbstractUseBuilder is a convenience class template for creating customized * definition-use chain use builders from an AST. It simplifies: * - use of your editor integrator * - creating or modifying existing \ref Use "Uses" * * \author Hamish Rodda \ */ template class AbstractUseBuilder: public LanguageSpecificUseBuilderBase { public: /// Constructor. template AbstractUseBuilder(ParseSession* session) : LanguageSpecificUseBuilderBase(session), m_finishContext(true) { } AbstractUseBuilder() : m_finishContext(true) { } /** * Iterate an existing duchain, and add, remove or modify uses as determined * from the ast. * * \param node AST node to start visiting. */ void buildUses(T *node) { TopDUContext* top = dynamic_cast(this->contextFromNode(node)); if (top) { DUChainWriteLocker lock(DUChain::lock()); top->clearUsedDeclarationIndices(); if(top->features() & TopDUContext::AllDeclarationsContextsAndUses) LanguageSpecificUseBuilderBase::setRecompiling(true); } LanguageSpecificUseBuilderBase::supportBuild(node); } protected: - /** - * Register a new use at the AST node \a name. - * - * \param node AST node which both represents a use and the identifier for the declaration which is being used. - */ struct ContextUseTracker { QVector createUses; }; + /** + * Register a new use at the AST node @p name. + * + * @param name AST node which both represents a use and the identifier for the declaration which is being used. + */ void newUse(NameT* name) { QualifiedIdentifier id = identifierForNode(name); RangeInRevision newRange = editorFindRange(name, name); DUChainReadLocker lock(DUChain::lock()); QList declarations = LanguageSpecificUseBuilderBase::currentContext()->findDeclarations(id, newRange.start); foreach (Declaration* declaration, declarations) if (!declaration->isForwardDeclaration()) { declarations.clear(); declarations.append(declaration); break; } // If we don't break, there's no non-forward declaration lock.unlock(); newUse( name, newRange, !declarations.isEmpty() ? DeclarationPointer(declarations.first()) : DeclarationPointer() ); } ///@todo Work this over! We must not pass around "Declaration*" values if the duchain is not locked. /** * Register a new use for a \a declaration with a \a node. * * \param node Node which encompasses the use. - * \param decl Declaration which is being used. May be null when a declaration cannot be found for the use. + * \param declaration Declaration which is being used. May be null when a declaration cannot be found for the use. */ void newUse(T* node, const KDevelop::DeclarationPointer& declaration) { newUse(node, this->editorFindRange(node, node), declaration); } /** * Register a new use. * * \param newRange Text range which encompasses the use. - * \param decl Declaration which is being used. May be null when a declaration cannot be found for the use. + * \param _declaration Declaration which is being used. May be null when a declaration cannot be found for the use. */ void newUse(T* node, const RangeInRevision& newRange, const DeclarationPointer& _declaration) { DUChainWriteLocker lock(DUChain::lock()); Declaration* declaration = _declaration.data(); if(!declaration) return; // The declaration was deleted in the meantime int declarationIndex = LanguageSpecificUseBuilderBase::currentContext()->topContext()->indexForUsedDeclaration(declaration); int contextUpSteps = 0; //We've got to use the stack here, and not parentContext(), because the order may be different { /* * We need to find a context that this use fits into, which must not necessarily be the current one. * The reason are macros like SOME_MACRO(SomeClass), where SomeClass is expanded to be within a * sub-context that comes from the macro. That sub-context will have a very small range, and will most * probably not be the range of the actual "SomeClass" text, so the "SomeClass" use has to be moved * into the context that surrounds the SOME_MACRO invocation. * */ DUContext* newContext = LanguageSpecificUseBuilderBase::currentContext(); while (!newContext->range().contains(newRange) && contextUpSteps < (LanguageSpecificUseBuilderBase::contextStack().size()-1)) { ++contextUpSteps; newContext = LanguageSpecificUseBuilderBase::contextStack()[LanguageSpecificUseBuilderBase::contextStack().size()-1-contextUpSteps]; } if (contextUpSteps) { m_finishContext = false; openContext(newContext); m_finishContext = true; currentUseTracker() = m_trackerStack.at(m_trackerStack.size()-contextUpSteps-2); } if (LanguageSpecificUseBuilderBase::m_mapAst) LanguageSpecificUseBuilderBase::editor()->parseSession()->mapAstUse( node, qMakePair(DUContextPointer(newContext), newRange)); currentUseTracker().createUses << KDevelop::Use(newRange, declarationIndex); } if (contextUpSteps) { Q_ASSERT(m_contexts[m_trackerStack.size()-contextUpSteps-2] == LanguageSpecificUseBuilderBase::currentContext()); m_trackerStack[m_trackerStack.size()-contextUpSteps-2] = currentUseTracker(); m_finishContext = false; closeContext(); m_finishContext = true; } } /** * Reimplementation of openContext, to track which uses should be assigned to which context. */ virtual void openContext(KDevelop::DUContext* newContext) { LanguageSpecificUseBuilderBase::openContext(newContext); ContextUseTracker newTracker; m_trackerStack.push(newTracker); m_contexts.push(newContext); } /** * Reimplementation of closeContext, to track which uses should be assigned to which context. */ virtual void closeContext() { if(m_finishContext) { DUChainWriteLocker lock(DUChain::lock()); this->currentContext()->deleteUses(); ContextUseTracker& tracker(currentUseTracker()); for(int a = 0; a < tracker.createUses.size(); ++a) { this->currentContext()->createUse(tracker.createUses[a].m_declarationIndex, tracker.createUses[a].m_range); } } LanguageSpecificUseBuilderBase::closeContext(); m_trackerStack.pop(); m_contexts.pop(); } private: inline ContextUseTracker& currentUseTracker() { return m_trackerStack.top(); } Stack m_trackerStack; Stack m_contexts; //Whether not encountered uses should be deleted during closeContext() bool m_finishContext; }; } #endif // KDEVPLATFORM_ABSTRACTUSEBUILDER_H diff --git a/language/duchain/declaration.h b/language/duchain/declaration.h index 27270bdb7c..7f6d1a04da 100644 --- a/language/duchain/declaration.h +++ b/language/duchain/declaration.h @@ -1,572 +1,570 @@ /* This file is part of KDevelop Copyright 2006 Hamish Rodda Copyright 2007-2008 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDEVPLATFORM_DECLARATION_H #define KDEVPLATFORM_DECLARATION_H #include #include #include "types/abstracttype.h" #include "duchainbase.h" #include "instantiationinformation.h" #include "identifier.h" #include "indexeddeclaration.h" class QByteArray; namespace KDevelop { class AbstractType; class DUContext; class IndexedString; class DeclarationData; class DeclarationId; class Declaration; class IndexedTopDUContext; class TopDUContext; /** * \short Represents a single declaration in a definition-use chain. * * \note A du-context can be freely edited as long as it's parent-context is zero. * In the moment the parent-context is set, the context may only be edited when it * is allowed to edited it's top-level context (@see TopLevelContext::inDUChain()) */ class KDEVPLATFORMLANGUAGE_EXPORT Declaration : public DUChainBase { public: /// Access types enum AccessPolicy : quint8 { Public /**< a public declaration */, Protected /**< a protected declaration */, Private /**< a private declaration */, DefaultAccess /** TypePtr type() const { return TypePtr::dynamicCast(abstractType()); } /** * Access this declaration's type. * * \note You should not compare or permanently store instances of AbstractType::Ptr. Use IndexedType instead. * \returns this declaration's type, or null if none has been assigned. */ AbstractType::Ptr abstractType() const; /** * Set this declaration's type. * * \param type the type to assign. */ template void setType(TypePtr type) { setAbstractType(AbstractType::Ptr::staticCast(type)); } /** * Set this declaration's \a type. * * \param type this declaration's new type. */ virtual void setAbstractType(AbstractType::Ptr type); /** * Return an indexed form of this declaration's type. * Should be preferred, this is the fastest way, and the correct way for doing equality-comparsion. * * \returns the declaration's type. */ IndexedType indexedType() const; /** * Set this declaration's \a identifier. * * \param identifier this declaration's new identifier */ void setIdentifier(const Identifier& identifier); /** * Access this declaration's \a identifier. * * \returns this declaration's identifier. */ Identifier identifier() const; /** * Access this declaration's \a identifier. * * \return this declaration's identifier in indexed form. This is faster than identifier(), because it * equals the internal representation. Use this for example to do equality-comparison. */ const IndexedIdentifier& indexedIdentifier() const; /** * Determine the global qualified identifier of this declaration. * * \note This function is expensive, equalQualifiedIdentifier() is preferred if you * just want to compare equality. */ QualifiedIdentifier qualifiedIdentifier() const; /** * Compares the qualified identifier of this declaration with the other one, without needing to compute it. * This is more efficient than comparing the results of qualifiedIdentifier(). * * \param rhs declaration to compare identifiers with * \returns true if the identifiers are equal, otherwise false. */ bool equalQualifiedIdentifier(const Declaration* rhs) const; /** * Returns the kind of this declaration. @see Kind */ Kind kind() const; /** * Set the kind. * * \param kind new kind */ void setKind(Kind kind); /** * Returns the comment associated to this declaration in the source-code, * or an invalid string if there is none. * * Stored in utf-8 encoding. */ QByteArray comment() const; /** * Sets the comment for this declaration. * * @note Should be utf-8 encoded. */ void setComment(const QByteArray& str); /** * Sets the comment for this declaration. */ void setComment(const QString& str); /** * Access whether this declaration is in the symbol table. * * \returns true if this declaration is in the symbol table, otherwise false. */ bool inSymbolTable() const; /** * Adds or removes this declaration to/from the symbol table. * * \param inSymbolTable true to add this declaration to the symbol table, false to remove it. */ virtual void setInSymbolTable(bool inSymbolTable); /** * Equivalence operator. * * \param other Other declaration to compare. * \returns true if the declarations are equal, otherwise false. */ bool operator==(const Declaration& other) const; /** * Determine this declaration as a string. \returns this declaration as a string. */ virtual QString toString() const; /** * Returns a map of files to use-ranges. * * The key of the returned map is an url of a file. The value * is a list with all use-ranges of this declaration in that file. * * \note The ranges are in the documents local revision, * use \c DUChainUtils::transformFromRevision or \c usesCurrentRevision() * * \note The uses are unique, no 2 uses are returend that have the same range within the same file. * * \note This is a non-trivial operation and hence expensive. */ QMap > uses() const; /** * Determines whether the declaration has any uses or not. * * Cheaper than calling uses(). */ bool hasUses() const; /** * Returns a map of files to use-ranges. * * The key of the returned map is an url of a file. The value * is a list with all use-ranges of this declaration in that file. * * \note The uses are unique, no 2 uses are returend that have the same range within the same file. * * \warning This must be called only from within the foreground, or with the foreground lock locked. * * \note This is a non-trivial operation and hence expensive. */ QMap > usesCurrentRevision() const; /** * This hash-value should differentiate between multiple different * declarations that have the same qualifiedIdentifier, but should have a different * identity, and thus own Definitions and own Uses assigned. * * Affected by function-arguments, whether this is a template-declaration, etc.. */ virtual uint additionalIdentity() const; /** * TODO document * */ virtual IndexedInstantiationInformation specialization() const; /** * \see DeclarationId * * \param forceDirect When this is true, the DeclarationId is force to be direct, * and can be resolved without a symbol-table and top-context. * The same goes for Declarations that have \c alwaysForceDirect() * set to true. */ virtual DeclarationId id(bool forceDirect = false) const; /** * Returns an index that uniquely identifies this declaration within its surrounding top-context. * * That index can be passed to \c TopDUContext::declarationFromIndex(index) to get the declaration. * This is only valid when the declaration is not a specialization (\c specialization() returns 0), * and if it is not anonymous in its context. * * \note for this to be valid, allocateOwnIndex() must have been called first. * \note the highest big of the index is always zero! * \returns the index of the declaration within its TopDUContext. */ uint ownIndex() const; /** * Whether this declaration has been inserted anonymously into its parent-context */ bool isAnonymous() const; /** * Clear the index for this declaration in the top context that was allocated with allocateOwnIndex(). */ void clearOwnIndex(); /** * Create an index to this declaration from the topContext(). Needed to be able to retrieve ownIndex(). */ void allocateOwnIndex(); /** * Returns a clone of this declaration, with the difference that the returned declaration * has no context set, i.e. \c context() returns zero. * * The declaration will not be registered anywhere, so you must care about its deletion. * * This declaration's text-range will be referenced from the clone, so the clone must not * live longer than the original. */ Declaration* clone() const; /** * Signalized that among multiple possible specializations, this one should be used in the UI from now on. * * Currently mainly used in C++ for template support. The default-implementation registers the current * specialization of this declaration to SpecializationStore if it is nonzero. */ virtual void activateSpecialization(); enum { Identity = 7 }; protected: /** * Constructor for copy constructors in subclasses. * * \param dd data to copy. - * \param url document url in which this object is located. * \param range text range which this object covers. */ Declaration( DeclarationData & dd, const RangeInRevision& range ); /** * Returns true if this declaration is being currently destroyed persistently, * which means that it should eventually deregister itself from persistent storage facilities. * * Only call this from destructors. */ bool persistentlyDestroying() const; DUCHAIN_DECLARE_DATA(Declaration) private: /** * Sub-classes should implement this and should copy as much information into the clone as possible without breaking the du-chain. * Sub-classes should also implement a public copy-constructor that can be used for cloning by sub-classes. * * \note You do not have to implement this for your language if you are not going to use it(the du-chain itself does not and should not depend on it). * */ virtual Declaration* clonePrivate() const; void updateCodeModel(); void rebuildDynamicData(DUContext* parent, uint ownIndex) override; friend class DUContext; friend class IndexedDeclaration; friend class LocalIndexedDeclaration; friend class TopDUContextDynamicData; DUContext* m_context; TopDUContext* m_topContext; int m_indexInTopContext; }; } #endif // KDEVPLATFORM_DECLARATION_H diff --git a/language/duchain/declarationid.h b/language/duchain/declarationid.h index 775c3cf860..37bddeb494 100644 --- a/language/duchain/declarationid.h +++ b/language/duchain/declarationid.h @@ -1,210 +1,210 @@ /* This file is part of KDevelop Copyright 2008 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDEVPLATFORM_DECLARATION_ID_H #define KDEVPLATFORM_DECLARATION_ID_H #include "indexeddeclaration.h" #include "identifier.h" #include "instantiationinformation.h" #include //krazy:excludeall=dpointer class QString; namespace KDevelop { class Declaration; class TopDUContext; /** * \short Allows clearly identifying a Declaration. * * DeclarationId is needed to uniquely address Declarations that are in another top-context, * because there may be multiple parsed versions of a file. * * There are two forms of DeclarationId, one indirect and one direct. The direct form * holds a reference to the Declaration instance, whereas the indirect form stores the qualified * identifier and an additional index to disambiguate instances of multiple declarations with the same * identifier. * * Both forms also have a specialization index. It can be used in a language-specific way to pick other * versions of the declaration. When the declaration is found, Declaration::specialize() is called on * the found declaration with this value, and the returned value is the actually found declaration. * * \note This only works when the Declaration is in the symbol table. * */ class KDEVPLATFORMLANGUAGE_EXPORT DeclarationId { public: /** * Constructor for indirect access to a declaration. The resulting DeclarationId will not * have a direct reference to the Declaration, but will look it up as needed. * * \param id Identifier for this declaration id. * \param additionalId Additional index to disambiguate * \param specialization Specialization index (see class documentation). */ explicit DeclarationId(const IndexedQualifiedIdentifier& id = IndexedQualifiedIdentifier(), uint additionalId = 0, const IndexedInstantiationInformation& specialization = IndexedInstantiationInformation()); /** * Constructor for direct access to a declaration. The resulting DeclarationId will * directly reference the Declaration * * \param decl Declaration to reference. * \param specialization Specialization index (see class documentation). */ explicit DeclarationId(const IndexedDeclaration& decl, const IndexedInstantiationInformation& specialization = IndexedInstantiationInformation()); DeclarationId(const DeclarationId& rhs); ~DeclarationId(); DeclarationId& operator=(const DeclarationId& rhs); /** * Equality operator. * * \param rhs declaration identifier to compare. * \returns true if equal, otherwise false. */ bool operator==(const DeclarationId& rhs) const { if(m_isDirect != rhs.m_isDirect) return false; if(!m_isDirect) return m_indirectData.identifier == rhs.m_indirectData.identifier && m_indirectData.additionalIdentity == rhs.m_indirectData.additionalIdentity && m_specialization == rhs.m_specialization; else return m_directData == rhs.m_directData && m_specialization == rhs.m_specialization; } /** * Not equal operator. * * \param rhs declaration identifier to compare. * \returns true if not equal, otherwise false. */ bool operator!=(const DeclarationId& rhs) const { return !operator==(rhs); } /** * Determine whether this declaration identifier references a valid declaration. */ bool isValid() const { return (m_isDirect && m_directData.isValid()) || m_indirectData.identifier.isValid(); } /** * Hash function for this declaration identifier. * * \warning This may return different hashes for the same declaration, * depending on whether the id is direct or indirect, * and thus you cannot compare hashes for declaration equality (use operator==() instead) */ uint hash() const { if(m_isDirect) return KDevHash() << m_directData.hash() << m_specialization.index(); else return KDevHash() << m_indirectData.identifier.getIndex() << m_indirectData.additionalIdentity << m_specialization.index(); } /** * Retrieve the declaration, from the perspective of \a context. * In order to be retrievable, the declaration must be in the symbol table. * * \param context Context in which to search for the Declaration. * \param instantiateIfRequired Whether the declaration should be instantiated if required * \returns the referenced Declaration, or null if none was found. * */ Declaration* getDeclaration(const TopDUContext* context, bool instantiateIfRequired = true) const; /** * Same as getDeclaration(..), but returns all matching declarations if there are multiple. * This also returns found forward-declarations. */ KDevVarLengthArray getDeclarations(const TopDUContext* context) const; /** * Set the specialization index (see class documentation). * - * \param specializationIndex the new specialization index. + * \param spec the new specialization index. */ void setSpecialization(const IndexedInstantiationInformation& spec); /** * Retrieve the specialization index (see class documentation). * * \returns the specialization index. */ IndexedInstantiationInformation specialization() const; /** * Determine whether this DeclarationId directly references a Declaration by indices, * or if it uses identifiers and other data to reference the Declaration. * * \returns true if direct, false if indirect. */ bool isDirect() const; /** * Return the qualified identifier for this declaration. * * \warning This is relatively expensive, and not 100% correct in all cases(actually a top-context would be needed to resolve this correctly), * so avoid using this, except for debugging purposes. */ QualifiedIdentifier qualifiedIdentifier() const; private: /// An indirect reference to the declaration, which uses the symbol-table for lookup. Should be preferred for all /// declarations that are in the symbol-table struct Indirect { IndexedQualifiedIdentifier identifier; /// Hash from signature, or similar. Used to disambiguate multiple declarations of the same name. uint additionalIdentity; Indirect& operator=(const Indirect& rhs) = default; }; union { Indirect m_indirectData; IndexedDeclaration m_directData; }; bool m_isDirect; // Can be used in a language-specific way to pick other versions of the declaration. // When the declaration is found, pickSpecialization is called on the found declaration // with this value, and the returned value is the actually found declaration. IndexedInstantiationInformation m_specialization; }; inline uint qHash(const KDevelop::DeclarationId& id) { return id.hash(); } } Q_DECLARE_TYPEINFO(KDevelop::DeclarationId, Q_MOVABLE_TYPE); #endif diff --git a/language/duchain/definitions.h b/language/duchain/definitions.h index fdf882f42b..0d6149bba8 100644 --- a/language/duchain/definitions.h +++ b/language/duchain/definitions.h @@ -1,62 +1,63 @@ /* This file is part of KDevelop Copyright 2008 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_DEFINITIONS_H +#define KDEVPLATFORM_DEFINITIONS_H + #include #include -#ifndef KDEVPLATFORM_DEFINITIONS_H -#define KDEVPLATFORM_DEFINITIONS_H class QTextStream; namespace KDevelop { class Declaration; class IndexedDeclaration; class DeclarationId; class TopDUContext; /** * Global mapping of one Declaration-Ids to multiple Definitions, protected through DUChainLock. * */ class KDEVPLATFORMLANGUAGE_EXPORT Definitions { public: /// Constructor. Definitions(); /// Destructor. ~Definitions(); /** - * Assigns @param definition to the given @param id. + * Assigns @param definition to the given @p id * */ void addDefinition(const DeclarationId& id, const IndexedDeclaration& definition); void removeDefinition(const DeclarationId& id, const IndexedDeclaration& definition); - ///Gets all the known definitions assigned to @param id. + ///Gets all the known definitions assigned to @p id KDevVarLengthArray definitions(const DeclarationId& id) const; /// Dump contents of the definitions repository to stream @p out void dump(const QTextStream& out); private: class DefinitionsPrivate* d; }; } #endif diff --git a/language/duchain/duchain.h b/language/duchain/duchain.h index 56162d4ef4..a523a0aaa1 100644 --- a/language/duchain/duchain.h +++ b/language/duchain/duchain.h @@ -1,315 +1,315 @@ /* This file is part of KDevelop Copyright 2006-2008 Hamish Rodda Copyright 2007-2008 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDEVPLATFORM_DUCHAIN_H #define KDEVPLATFORM_DUCHAIN_H #include #include "topducontext.h" #include "parsingenvironment.h" class QUrl; namespace KDevelop { class IDocument; class TopDUContext; class DUChainLock; class ParsingEnvironmentManager; class ParsingEnvironment; class ParsingEnvironmentFile; typedef QExplicitlySharedDataPointer ParsingEnvironmentFilePointer; class Definitions; class Uses; /** * \short Holds references to all top level source file contexts. * * The DUChain is a global static class which manages the definition-use * chains. It performs the following functions: * \li registers chains with addDocumentChain() and deregisters with removeDocumentChain() * \li allows querying for existing chains * \li watches text editors, registering and deregistering them with the BackgroundParser when files * are opened and closed. */ class KDEVPLATFORMLANGUAGE_EXPORT DUChain : public QObject { Q_OBJECT public: /** * Initializes common static item repositories. * Must be called once for multi threaded applications to work reliably. */ static void initialize(); /** * Return a list of all chains available */ Q_SCRIPTABLE QList allChains() const; /** * Makes sure the standard-context for the given url is up-to-date. * This may trigger a parsing in background, so a QObject can be given that will be notified * asyonchronously once the update is ready. * If the context is already up to date, the given QObject is notified directly. * * @param document Document to update - * @param features The requested features. If you want to force a full update of the context, give TopDUContext::ForceUpdate. + * @param minFeatures The requested features. If you want to force a full update of the context, give TopDUContext::ForceUpdate. * If you want to force an update including all imports, use TopDUContext::ForceUpdateRecursive. * @param notifyReady An optional pointer to a QObject that should contain a slot * "void updateReady(KDevelop::IndexedString url, KDevelop::ReferencedTopDUContext topContext)". * The notification is guaranteed to be called once for each call to updateContextForUrl. The given top-context * may be invalid if the update failed. A queued connection is used if a re-parse has to be done. The duchain * will _not_ be locked when updateReady is called. * @param priority An optional priority for the job. The lower the value, the higher it's priority. * @note The duchain must _not_ be locked when this is called! */ Q_SCRIPTABLE void updateContextForUrl(const IndexedString& document, TopDUContext::Features minFeatures, QObject* notifyReady = nullptr, int priority = 1) const; /** * Convenience-function similar to updateContextForUrl that blocks this thread until the update of the given document is ready, * and returns the top-context. * @param document The document to update - * @param features The requested features. If you want to force a full update of the context, give TopDUContext::ForceUpdate. + * @param minFeatures The requested features. If you want to force a full update of the context, give TopDUContext::ForceUpdate. * If you want to force an update including all imports, use TopDUContext::ForceUpdateRecursive. * @return The up-to-date top-context, or zero if the update failed * * @note The duchain must _not_ be locked when this is called! * */ KDevelop::ReferencedTopDUContext waitForUpdate(const KDevelop::IndexedString& document, KDevelop::TopDUContext::Features minFeatures, bool proxyContext = false); /** * Return any chain for the given document * If available, the version accepting IndexedString should be used instead of this, for performance reasons. * When no fitting chain is in memory, one may be loaded from disk. * * @note The duchain must be at least read-locked locked when this is called! * */ Q_SCRIPTABLE TopDUContext* chainForDocument(const QUrl& document, bool proxyContext = false) const; Q_SCRIPTABLE TopDUContext* chainForDocument(const IndexedString& document, bool proxyContext = false) const; /** * Return all chains for the given document that are currently in memory. * This does not load any chains from disk. * */ Q_SCRIPTABLE QList chainsForDocument(const QUrl& document) const; /** * Return all chains for the given document that are currently in memory. * This does not load any chains from disk. * Should be preferred over the QUrl version. * */ Q_SCRIPTABLE QList chainsForDocument(const IndexedString& document) const; /** * Find a chain that fits into the given environment. If no fitting chain is found, 0 is returned. * When no fitting chain is in memory, one may be loaded from disk. * @param proxyContext If this is true, only contexts are found that have an ParsingEnvironmentFile that has the proxy-flag set. Else, only content-contexts will be returned. * * @note The duchain must be at least read-locked locked when this is called! * */ Q_SCRIPTABLE TopDUContext* chainForDocument(const QUrl& document, const ParsingEnvironment* environment, bool proxyContext = false) const; /** * Find a chain that fits into the given environment. If no fitting chain is found, 0 is returned. * When no fitting chain is in memory, one may be loaded from disk. * @param proxyContext If this is true, only contexts are found that have an ParsingEnvironmentFile that has the proxy-flag set. Else, only content-contexts will be returned. * * Prefer this over the QUrl version. * * @note The duchain must be at least read-locked locked when this is called! * */ Q_SCRIPTABLE TopDUContext* chainForDocument(const IndexedString& document, const ParsingEnvironment* environment, bool proxyContext = false) const; /** * Find the environment-file of a chain that fits into the given environment. If no fitting chain is found, 0 is returned. * When no fitting chain is in memory, one may be loaded from disk. * * This should be preferred over chainForDocument when only the environment-info is needed, because the TopDUContext is not loaded in this function. * ** @param proxyContext If this is true, only contexts are found that have an ParsingEnvironmentFile that has the proxy-flag set. Else, only content-contexts will be returned. * * Prefer this over the QUrl version. * * @note The duchain must be at least read-locked locked when this is called! * */ Q_SCRIPTABLE ParsingEnvironmentFilePointer environmentFileForDocument(const IndexedString& document, const ParsingEnvironment* environment, bool proxyContext = false) const; Q_SCRIPTABLE ParsingEnvironmentFilePointer environmentFileForDocument(IndexedTopDUContext topContext) const; /** * Returns the list of the environment-infos of all versions of the given document. */ Q_SCRIPTABLE QList allEnvironmentFiles(const IndexedString& document); ///Returns the top-context that has the given index assigned, or zero if it doesn't exist. @see TopDUContext::ownIndex ///The duchain must be read-locked when this is called ///This function is inlined because it is called in a very high frequency Q_SCRIPTABLE inline TopDUContext* chainForIndex(uint index) { if(m_deleted) return nullptr; { QMutexLocker lock(&chainsByIndexLock); if(chainsByIndex.size() > index) { TopDUContext* top = chainsByIndex[index]; if(top) return top; } } //Load the top-context return loadChain(index); } ///Returns the url for the given top-context index if available. This does have some cost, so avoid it when possible. Q_SCRIPTABLE IndexedString urlForIndex(uint index) const; /// Only used for debugging at the moment Q_SCRIPTABLE QList documents() const; /// Only used for debugging at the moment /// Prefer that over the QUrl version for performance reasons Q_SCRIPTABLE QList indexedDocuments() const; /** * Registers a new definition-use \a chain for the given \a document. */ Q_SCRIPTABLE void addDocumentChain(TopDUContext* chain); /// Returns true if the global duchain instance has already been deleted Q_SCRIPTABLE static bool deleted(); /// Returns the global static instance. Q_SCRIPTABLE static DUChain* self(); /// Returns the structure that manages mapping between definitions and declarations Q_SCRIPTABLE static Definitions* definitions(); /// Returns the structure that manages mapping between declarations, and which top level contexts contain uses of them. static Uses* uses(); /** * Retrieve the read write lock for the entire definition-use chain. * To call non-const methods, you must be holding a write lock. * * Evaluations made prior to holding a lock (including which objects * exist) must be verified once the lock is held, as they may have changed * or been deleted. * * \threadsafe */ Q_SCRIPTABLE static DUChainLock* lock(); /// Returns whether the top-context with the given index is currently loaded in memory Q_SCRIPTABLE bool isInMemory(uint topContextIndex) const; /** * Changes the environment attached to the given top-level context, and updates the management-structures to reflect that * */ Q_SCRIPTABLE void updateContextEnvironment( TopDUContext* context, ParsingEnvironmentFile* file ); ///Allocates a new identity for a new top-context, no lock needed. The returned value is never zero static uint newTopContextIndex(); ///If you call this, the persistent disk-storage structure will stay unaffected, and no duchain cleanup will be done. ///Call this from within tests. void disablePersistentStorage(bool disable = true); ///Stores the whole duchain and all its repositories in the current state to disk ///The duchain must not be locked in any way void storeToDisk(); ///Compares the whole duchain and all its repositories in the current state to disk ///When the comparison fails, debug-output will show why ///The duchain must not be locked when calling this ///@return true If the current memory state equals the disk state, else false bool compareToDisk(); Q_SIGNALS: ///Is emitted when the declaration has been selected somewhere in the user-interface, for example in the completion-list void declarationSelected(const KDevelop::DeclarationPointer& decl); /** * This signal is emitted whenever the DUChain data associated with @p url was updated. * * You can connect to this signal to get notified when the DUChain for a given file was updated. */ void updateReady(const KDevelop::IndexedString& url, const KDevelop::ReferencedTopDUContext& topContext); public Q_SLOTS: ///Removes the given top-context from the duchain, and deletes it. void removeDocumentChain(KDevelop::TopDUContext* document); ///Emits the declarationSelected signal, so other parties can notice it. void emitDeclarationSelected(const KDevelop::DeclarationPointer& decl); /** * Call this after you have modified the DUChain data associated with the file @p url. * * This triggers an emit of the @c updateReady signal. */ void emitUpdateReady(const KDevelop::IndexedString& url, const KDevelop::ReferencedTopDUContext& topContext); /** * Shutdown and cleanup the DUChain. */ void shutdown(); private Q_SLOTS: void documentActivated(KDevelop::IDocument* doc); void documentLoadedPrepare(KDevelop::IDocument* document); void documentRenamed(KDevelop::IDocument* document); void documentClosed(KDevelop::IDocument*); private: TopDUContext* loadChain(uint index); //These two are exported here so that the extremely frequently called chainForIndex(..) can be inlined static bool m_deleted; static std::vector chainsByIndex; static QMutex chainsByIndexLock; /// Increases the reference-count for the given top-context. The result: It will not be unloaded. /// Do this to prevent KDevelop from unloading a top-context that you plan to use. Don't forget calling unReferenceToContext again, /// else the top-context will stay in memory for ever. void refCountUp(TopDUContext* top); /// Decreases the reference-count for the given top-context. When it reaches zero, KDevelop is free to unload it at any time, /// also invalidating all the contained declarations and contexts. void refCountDown(TopDUContext* top); void addToEnvironmentManager( TopDUContext * chain ); void removeFromEnvironmentManager( TopDUContext * chain ); DUChain(); ~DUChain() override; friend class DUChainPrivate; friend class ReferencedTopDUContext; }; } #endif // KDEVPLATFORM_DUCHAIN_H diff --git a/language/duchain/duchainbase.h b/language/duchain/duchainbase.h index c7cae377df..1ab9fc831e 100644 --- a/language/duchain/duchainbase.h +++ b/language/duchain/duchainbase.h @@ -1,221 +1,219 @@ /* This file is part of KDevelop Copyright 2006 Hamish Rodda Copyright 2007/2008 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDEVPLATFORM_DUCHAINBASE_H #define KDEVPLATFORM_DUCHAINBASE_H #include #include "appendedlist.h" #include "duchainpointer.h" #include #include namespace KTextEditor { class Cursor; class Range; } namespace KDevelop { class PersistentMovingRange; class DUContext; class TopDUContext; class DUChainBase; class IndexedString; ///Use this to declare the data functions in your DUChainBase based class. @warning Behind this macro, the access will be "public". #define DUCHAIN_DECLARE_DATA(Class) \ inline class Class##Data* d_func_dynamic() { makeDynamic(); return reinterpret_cast(d_ptr); } \ inline const class Class##Data* d_func() const { return reinterpret_cast(d_ptr); } \ public: typedef Class ## Data Data; private: #define DUCHAIN_D(Class) const Class##Data * const d = d_func() #define DUCHAIN_D_DYNAMIC(Class) Class##Data * const d = d_func_dynamic() ///@note When a data-item is stored on disk, no destructors of contained items will be called while destruction. ///DUChainBase assumes that each item that has constant data, is stored on disk. ///However the destructor is called even on constant items, when they have been replaced with a dynamic item. ///This tries to keep constructor/destructor count consistency persistently, which allows doing static reference-counting ///using contained classes in their constructor/destructors(For example the class Utils::StorableSet). ///This means that the data of all items that are stored to disk _MUST_ be made constant before their destruction. ///This also means that every item that is "semantically" deleted, _MUST_ have dynamic data before its destruction. ///This also means that DUChainBaseData based items should never be cloned using memcpy, but rather always using the copy-constructor, ///even if both sides are constant. class KDEVPLATFORMLANGUAGE_EXPORT DUChainBaseData { public: DUChainBaseData() : classId(0) { initializeAppendedLists(); } DUChainBaseData(const DUChainBaseData& rhs) : m_range(rhs.m_range), classId(rhs.classId) { initializeAppendedLists(); } ~DUChainBaseData() { freeAppendedLists(); } RangeInRevision m_range; APPENDED_LISTS_STUB(DUChainBaseData) quint16 classId; bool isDynamic() const { return m_dynamic; } /** * Internal setup for the data structure. * * This must be called from actual class that belongs to this data(not parent classes), and the class must have the * "Identity" enumerator with a unique identity. Do NOT call this in copy-constructors! */ template void setClassId(T*) { static_assert(T::Identity < std::numeric_limits::max(), "Class ID out of bounds"); classId = T::Identity; } uint classSize() const; ///This is called whenever the data-object is being deleted memory-wise, but not semantically(Which means it stays on disk) ///Implementations of parent-classes must always be called void freeDynamicData() { } ///Used to decide whether a constructed item should create constant data. ///The default is "false", so dynamic data is created by default. ///This is stored thread-locally. static bool& shouldCreateConstantData(); ///Returns whether initialized objects should be created as dynamic objects static bool appendedListDynamicDefault() { return !shouldCreateConstantData(); } }; /** * Base class for definition-use chain objects. * * This class provides a thread safe pointer type to reference duchain objects * while the DUChain mutex is not held (\see DUChainPointer) */ class KDEVPLATFORMLANGUAGE_EXPORT DUChainBase { public: /** * Constructor. * - * \param url url of the document where this occurred * \param range range of the alias declaration's identifier */ explicit DUChainBase(const RangeInRevision& range); /// Destructor virtual ~DUChainBase(); /** * Determine the top context to which this object belongs. */ virtual TopDUContext* topContext() const; /** * Returns a special pointer that can be used to track the existence of a du-chain object across locking-cycles. * @see DUChainPointerData * */ const QExplicitlySharedDataPointer& weakPointer() const; virtual IndexedString url() const; enum { Identity = 1 }; ///After this was called, the data-pointer is dynamic. It is cloned if needed. void makeDynamic(); explicit DUChainBase( DUChainBaseData& dd ); ///This must only be used to change the storage-location or storage-kind(dynamic/constant) of the data, but ///the data must always be equal! virtual void setData(DUChainBaseData*, bool constructorCalled = true); ///Returns the range assigned to this object, in the document revision when this document was last parsed. RangeInRevision range() const; ///Changes the range assigned to this object, in the document revision when this document is parsed. void setRange(const RangeInRevision& range); ///Returns the range assigned to this object, transformed into the current revision of the document. ///@warning This must only be called from the foreground thread, or with the foreground lock acquired. KTextEditor::Range rangeInCurrentRevision() const; ///Returns the range assigned to this object, transformed into the current revision of the document. ///The returned object is unique at each call, so you can use it and change it in whatever way you want. ///@warning This must only be called from the foreground thread, or with the foreground lock acquired. PersistentMovingRange::Ptr createRangeMoving() const; ///Transforms the given cursor in the current document revision to its according position ///in the parsed document containing this duchain object. The resulting cursor will be directly comparable to the non-translated ///range() members in the duchain, but only for one duchain locking cycle. ///@warning This must only be called from the foreground thread, or with the foreground lock acquired. CursorInRevision transformToLocalRevision(const KTextEditor::Cursor& cursor) const; ///Transforms the given range in the current document revision to its according position ///in the parsed document containing this duchain object. The resulting cursor will be directly comparable to the non-translated ///range() members in the duchain, but only for one duchain locking cycle. ///@warning This must only be called from the foreground thread, or with the foreground lock acquired. RangeInRevision transformToLocalRevision(const KTextEditor::Range& range) const; KTextEditor::Cursor transformFromLocalRevision(const CursorInRevision& cursor) const; KTextEditor::Range transformFromLocalRevision(const RangeInRevision& range) const; protected: /** * Creates a duchain object that uses the data of the given one, and will not delete it on destruction. * The data will be shared, and this object must be deleted before the given one is. */ DUChainBase( DUChainBase& rhs ); /** * Constructor for copy constructors in subclasses. * * \param dd data to use. - * \param url document url in which this object is located. * \param range text range which this object covers. */ DUChainBase( DUChainBaseData& dd, const RangeInRevision& range ); ///Called after loading to rebuild the dynamic data. If this is a context, this should recursively work on all sub-contexts. virtual void rebuildDynamicData(DUContext* parent, uint ownIndex); /// Data pointer that is shared across all the inheritance hierarchy DUChainBaseData* d_ptr; private: mutable QExplicitlySharedDataPointer m_ptr; public: DUCHAIN_DECLARE_DATA(DUChainBase) }; } #endif // KDEVPLATFORM_DUCHAINBASE_H diff --git a/language/duchain/duchainregister.h b/language/duchain/duchainregister.h index 8b3dfa7410..a549b26d88 100644 --- a/language/duchain/duchainregister.h +++ b/language/duchain/duchainregister.h @@ -1,210 +1,210 @@ /* This file is part of KDevelop Copyright 2008 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDEVPLATFORM_DUCHAINREGISTER_H #define KDEVPLATFORM_DUCHAINREGISTER_H #include "duchainbase.h" namespace KDevelop { class DUChainBase; class DUChainBaseData; ///This class is purely internal and doesn't need to be documented. It brings a "fake" type-info ///to classes that don't have type-info in the normal C++ way. ///Never use this directly, use the REGISTER_DUCHAIN_ITEM macro instead. class KDEVPLATFORMLANGUAGE_EXPORT DUChainBaseFactory { public: virtual DUChainBase* create(DUChainBaseData* data) const = 0; virtual void callDestructor(DUChainBaseData* data) const = 0; virtual void freeDynamicData(DUChainBaseData* data) const = 0; virtual void copy(const DUChainBaseData& from, DUChainBaseData& to, bool constant) const = 0; virtual DUChainBaseData* cloneData(const DUChainBaseData& data) const = 0; virtual uint dynamicSize(const DUChainBaseData& data) const = 0; virtual ~DUChainBaseFactory() { } }; ///Never use this directly, use the REGISTER_DUCHAIN_ITEM macro instead. template class DUChainItemFactory : public DUChainBaseFactory { public: DUChainBase* create(DUChainBaseData* data) const override { return new T(*static_cast(data)); } void copy(const DUChainBaseData& from, DUChainBaseData& to, bool constant) const override { Q_ASSERT(from.classId == T::Identity); bool& isConstant = DUChainBaseData::shouldCreateConstantData(); const bool previousConstant = isConstant; if (previousConstant != constant) { isConstant = constant; } new (&to) Data(static_cast(from)); //Call the copy constructor to initialize the target if (previousConstant != constant) { isConstant = previousConstant; } } void callDestructor(DUChainBaseData* data) const override { Q_ASSERT(data->classId == T::Identity); static_cast(data)->~Data(); } void freeDynamicData(DUChainBaseData* data) const override { Q_ASSERT(data->classId == T::Identity); static_cast(data)->freeDynamicData(); } uint dynamicSize(const DUChainBaseData& data) const override { Q_ASSERT(data.classId == T::Identity); return static_cast(data).dynamicSize(); } DUChainBaseData* cloneData(const DUChainBaseData& data) const override { Q_ASSERT(data.classId == T::Identity); return new Data(static_cast(data)); } }; /** * \short A class which registers data types and creates factories for them. * * DUChainItemSystem is a global static class which allows you to register new * DUChainBase subclasses for creation. */ class KDEVPLATFORMLANGUAGE_EXPORT DUChainItemSystem { public: /** * Register a new DUChainBase subclass. */ template void registerTypeClass() { if(m_factories.size() <= T::Identity) { m_factories.resize(T::Identity+1); m_dataClassSizes.resize(T::Identity+1); } Q_ASSERT_X(!m_factories[T::Identity], Q_FUNC_INFO, "This identity is already registered"); m_factories[T::Identity] = new DUChainItemFactory(); m_dataClassSizes[T::Identity] = sizeof(Data); } /** * Unregister an DUChainBase subclass. */ template void unregisterTypeClass() { Q_ASSERT(m_factories.size() > T::Identity); Q_ASSERT(m_factories[T::Identity]); delete m_factories[T::Identity]; m_factories[T::Identity] = 0; m_dataClassSizes[T::Identity] = 0; } /** * Create an DUChainBase for the given data. The returned type must be put into a DUChainBase::Ptr immediately. * Can return null if no type-factory is available for the given data (for example when a language-part is not loaded) */ DUChainBase* create(DUChainBaseData* data) const; ///Creates a dynamic copy of the given data DUChainBaseData* cloneData(const DUChainBaseData& data) const; /** * This just calls the correct constructor on the target. The target must be big enough to hold all the data. * If constant is true, it must be as big as dynamicSize(from). */ void copy(const DUChainBaseData& from, DUChainBaseData& to, bool constant) const; ///Calls the dynamicSize(..) member on the given data, in the most special class. Since we cannot use virtual functions, this is the only way. uint dynamicSize(const DUChainBaseData& data) const; ///Returns the size of the derived class, not including dynamic data. ///Returns zero if the class is not known. uint dataClassSize(const DUChainBaseData& data) const; ///Calls the destructor, but does not delete anything. This is needed because the data classes must not contain virtual members. ///This should only be called when a duchain data-pointer is semantically deleted, eg. when it does not persist on disk. void callDestructor(DUChainBaseData* data) const; ///Does not call the destructor, but frees all special data associated to dynamic data(the appendedlists stuff) ///This needs to be called whenever a dynamic duchain data-pointer is being deleted. void freeDynamicData(DUChainBaseData* data) const; /// Access the static DUChainItemSystem instance. static DUChainItemSystem& self(); private: ~DUChainItemSystem(); QVector m_factories; QVector m_dataClassSizes; }; template struct DUChainType {}; /// Use this in the header to declare DUChainType #define DUCHAIN_DECLARE_TYPE(Type) \ namespace KDevelop { \ template<> struct DUChainType { \ static void registerType(); \ static void unregisterType(); \ }; \ } /// Use this in the source file to define functions in DUChainType #define DUCHAIN_DEFINE_TYPE_WITH_DATA(Type, Data) \ void KDevelop::DUChainType::registerType() { DUChainItemSystem::self().registerTypeClass(); } \ void KDevelop::DUChainType::unregisterType() { DUChainItemSystem::self().unregisterTypeClass(); } #define DUCHAIN_DEFINE_TYPE(Type) \ DUCHAIN_DEFINE_TYPE_WITH_DATA(Type, Type##Data) -/// Register @param T to DUChainItemSystem +/// Register @p T to DUChainItemSystem template void duchainRegisterType() { DUChainType::registerType(); } -/// Unregister @param T to DUChainItemSystem +/// Unregister @p T to DUChainItemSystem template void duchainUnregisterType() { DUChainType::unregisterType(); } /// Helper class to register an DUChainBase subclass. /// /// Just use the REGISTER_TYPE(YourTypeClass) macro in your code, and you're done. template struct DUChainItemRegistrator { DUChainItemRegistrator() { DUChainItemSystem::self().registerTypeClass(); } ~DUChainItemRegistrator() { DUChainItemSystem::self().unregisterTypeClass(); } }; ///You must add this into your source-files for every DUChainBase based class ///For this to work, the class must have an "Identity" enumerator. ///It should be a unique value, but as small as possible, because a buffer at least as big as that number is created internally. #define REGISTER_DUCHAIN_ITEM(Class) KDevelop::DUChainItemRegistrator register ## Class #define REGISTER_DUCHAIN_ITEM_WITH_DATA(Class, Data) KDevelop::DUChainItemRegistrator register ## Class } #endif diff --git a/language/duchain/duchainutils.h b/language/duchain/duchainutils.h index 1f550b7528..f71c7512a3 100644 --- a/language/duchain/duchainutils.h +++ b/language/duchain/duchainutils.h @@ -1,132 +1,132 @@ /* * DUChain Utilities * * Copyright 2007 Hamish Rodda * Copyright 2007-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. */ #ifndef KDEVPLATFORM_DUCHAINUTILS_H #define KDEVPLATFORM_DUCHAINUTILS_H #include #include #include #include #include class QIcon; namespace KTextEditor { class Cursor; } namespace KDevelop { class Declaration; class DUChainBase; class DUContext; class IndexedString; class TopDUContext; class IndexedDeclaration; /** * A namespace which contains convenience utilities for navigating definition-use chains. */ namespace DUChainUtils { KDEVPLATFORMLANGUAGE_EXPORT KTextEditor::CodeCompletionModel::CompletionProperties completionProperties(const Declaration* dec); KDEVPLATFORMLANGUAGE_EXPORT QIcon iconForProperties(KTextEditor::CodeCompletionModel::CompletionProperties p); KDEVPLATFORMLANGUAGE_EXPORT QIcon iconForDeclaration(const Declaration* dec); /** Asks the language-plugins for standard-contexts for the given url, and returns one if available. * If there is no language-plugin registered for the given url, it will just try to get any top-context for the file from the du-chain. * NOTE: The DUChain needs to be read or write locked when you call this. - * @param proxyContext Whether the returned context should be a proxy context. When no proxy-context is found, a normal context is returned. + * @param preferProxyContext Whether the returned context should be a proxy context. When no proxy-context is found, a normal context is returned. * * FIXME: this should operate on IndexedString */ KDEVPLATFORMLANGUAGE_EXPORT KDevelop::TopDUContext* standardContextForUrl(const QUrl& url, bool preferProxyContext = false); /** * Returns the content-context associated to the given proxy-contex. * Returns the same context if it is not a proxy-context. * Returns zero if no content-context could be acquired. * */ KDEVPLATFORMLANGUAGE_EXPORT TopDUContext* contentContextFromProxyContext(TopDUContext* top); struct KDEVPLATFORMLANGUAGE_EXPORT ItemUnderCursor { Declaration* declaration; // found declaration (either declared/defined or used) DUContext* context; // context in which the declaration, definition, or use was found KTextEditor::Range range; // range of the declaration/definition/use }; /** Returns 1. the Declaration/Definition either declared or used under the cursor, * or zero; and 2. the context in which the declaration, definition, or use was found. * DUChain must be locked. * Must only be called from the foreground or with the foreground lock held. */ KDEVPLATFORMLANGUAGE_EXPORT ItemUnderCursor itemUnderCursor(const QUrl& url, const KTextEditor::Cursor& cursor); /**If the given declaration is a definition, and has a real declaration *attached, returns that declarations. Else returns the given argument. */ KDEVPLATFORMLANGUAGE_EXPORT Declaration* declarationForDefinition(Declaration* definition, TopDUContext* topContext = 0); ///Returns the first declaration in the given line. Searches the given context and all sub-contexts. ///Must only be called from the foreground or with the foreground lock held. KDEVPLATFORMLANGUAGE_EXPORT Declaration* declarationInLine(const KTextEditor::Cursor& cursor, KDevelop::DUContext* ctx); class KDEVPLATFORMLANGUAGE_EXPORT DUChainItemFilter { public: virtual bool accept(Declaration* decl) = 0; //Should return whether processing should be deepened into the given context virtual bool accept(DUContext* ctx) = 0; virtual ~DUChainItemFilter(); }; ///walks a context, all its sub-contexts, and all its declarations in exactly the order they appear in in the file. ///Re-implement DUChainItemFilter to do something with the items. KDEVPLATFORMLANGUAGE_EXPORT void collectItems( DUContext* context, DUChainItemFilter& filter ); KDEVPLATFORMLANGUAGE_EXPORT DUContext* getArgumentContext(Declaration* decl); ///Uses the persistent symbol table to find all occurrences of this declaration, based on its identifier. ///The result should be filtered to make sure that the declaration is actually useful to you. KDEVPLATFORMLANGUAGE_EXPORT QList collectAllVersions(Declaration* decl); ///If the given declaration is a class, this gets all classes that inherit this one ///@param collectVersions If this is true, the persistent symbol table is used to first find all registered /// versions of this class, and then get the inheriters from them all together. This is needed for C++. ///@param maxAllowedSteps The maximum of steps allowed. If this is zero in the end, this means the search has been stopped with the max. reached /// If you really want _all_ inheriters, you should initialize it with a very large value. KDEVPLATFORMLANGUAGE_EXPORT QList getInheriters(const Declaration* decl, uint& maxAllowedSteps, bool collectVersions = true); - ///Gets all functions that override the function @param overriddenDeclaration, starting the search at @param currentClass + ///Gets all functions that override the function @p overriddenDeclaration, starting the search at @p currentClass ///@param maxAllowedSteps The maximum of steps allowed. If this is zero in the end, this means the search has been stopped with the max. reached KDEVPLATFORMLANGUAGE_EXPORT QList getOverriders(const Declaration* currentClass, const Declaration* overriddenDeclaration, uint& maxAllowedSteps); ///Returns whether the given context or any of its child-contexts contain a use of the given declaration. This is relatively expensive. KDEVPLATFORMLANGUAGE_EXPORT bool contextHasUse(DUContext* context, Declaration* declaration); ///Returns the total count of uses of the given declaration under the given context KDEVPLATFORMLANGUAGE_EXPORT uint contextCountUses(DUContext* context, Declaration* declaration); ///Returns the declaration that is overridden by the given one, or zero. KDEVPLATFORMLANGUAGE_EXPORT Declaration* getOverridden(const Declaration* decl); ///If the given declaration is a function-declaration, this follows the context-structure up to the function-context that contains the arguments, ///and returns it. KDEVPLATFORMLANGUAGE_EXPORT DUContext* getFunctionContext(Declaration* decl); KDEVPLATFORMLANGUAGE_EXPORT QVector allProblemsForContext(ReferencedTopDUContext top); } } #endif // KDEVPLATFORM_DUCHAINUTILS_H diff --git a/language/duchain/ducontext.h b/language/duchain/ducontext.h index 2475132b5f..11f086cb48 100644 --- a/language/duchain/ducontext.h +++ b/language/duchain/ducontext.h @@ -1,954 +1,954 @@ /* This file is part of KDevelop Copyright 2006 Hamish Rodda Copyright 2007-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_DUCONTEXT_H #define KDEVPLATFORM_DUCONTEXT_H #include #include #include #include #include #include "identifier.h" #include "duchainbase.h" #include "types/abstracttype.h" #include "duchainpointer.h" #include "declarationid.h" #include "indexedducontext.h" #include "navigation/abstractnavigationwidget.h" class QWidget; namespace KDevelop { class Declaration; class DUChain; class Use; class TopDUContext; class DUContext; class DUContextData; class KDEVPLATFORMLANGUAGE_EXPORT DUChainVisitor { public: virtual void visit(DUContext* context) = 0; virtual void visit(Declaration* declaration) = 0; virtual ~DUChainVisitor(); }; typedef DUChainPointer DUContextPointer; /** * A single context in source code, represented as a node in a * directed acyclic graph. * * Access to context objects must be serialised by holding the * chain lock, ie. DUChain::lock(). * * NOTE: A du-context can be freely edited as long as it's parent-context is zero. * In the moment the parent-context is set, the context may only be edited when it * is allowed to edited it's top-level context(@see TopLevelContext::inDUChain() * * @todo change child relationships to a linked list within the context? */ class KDEVPLATFORMLANGUAGE_EXPORT DUContext : public DUChainBase { friend class Use; friend class Declaration; friend class DeclarationData; friend class DUContextData; friend class DUContextDynamicData; friend class Definition; friend class VisibleDeclarationIterator; public: /** * Constructor. No convenience methods, as the initialisation order is important, * * @param anonymous Whether the context should be added as an anonymous context to the parent. That way the context can never be found through any of the parent's member-functions. * * If the parent is in the symbol table and the context is not anonymous, it will also be added to the symbol table. You nead a write-lock to the DUChain then */ explicit DUContext(const RangeInRevision& range, DUContext* parent = nullptr, bool anonymous = false); explicit DUContext(DUContextData&); /** * Destructor. Will delete all child contexts which are defined within * the same file as this context. */ ~DUContext() override; enum ContextType : quint8 { Global /**< A context that declares functions, namespaces or classes */, Namespace /**< A context that declares namespace members */, Class /**< A context that declares class members */, Function /**< A context that declares function-arguments */, Template /**< A context that declares template-parameters */, Enum /**< A context that contains a list of enumerators */, Helper /**< A helper context. This context is treated specially during search: * when searching within the imports of a context, and that context's parent * is a context of type DUContext::Helper, then the upwards search is continued * into that helper(The decision happens in shouldSearchInParent) */, Other /**< Represents executable code, like for example within a compound-statement */ }; enum SearchFlag { NoSearchFlags = 0 /**< Searching for everything */, InImportedParentContext = 1 /**< Internal, do not use from outside */, OnlyContainerTypes = 2 /**< Not implemented yet */, DontSearchInParent = 4 /**< IF this flag is set, findDeclarations(..) will not search for the identifier in parent-contexts(which does not include imported parent-contexts) */, NoUndefinedTemplateParams = 8 /**< For languages that support templates(like C++). If this is set, the search should fail as soon as undefined template-parameters are involved. */, DirectQualifiedLookup = 16 /**< When this flag is used, the searched qualified identifier should NOT be split up into it's components and looked up one by one. Currently only plays a role in C++ specific parts. */, NoFiltering = 32 /**< Should be set when no filtering at all is wished, not even filtering that is natural for the underlying language(For example in C++, constructors are filtered out be default) */, OnlyFunctions = 64 /**< When this is given, only function-declarations are returned. In case of C++, this also means that constructors can be retrieved, while normally they are filtered out. */, NoImportsCheck = 128 /**< With this parameter, a global search will return all matching items, from all contexts, not only from imported ones. */, NoSelfLookUp = 256 /**< With this parameter, the special-treatment during search that allows finding the context-class by its name is disabled. */, DontResolveAliases = 512 /**< Disables the resolution of alias declarations in the returned list*/, LastSearchFlag = 1024 }; Q_DECLARE_FLAGS(SearchFlags, SearchFlag) ContextType type() const; void setType(ContextType type); /** * If this context was opened by a declaration or definition, this returns that item. * * The returned declaration/definition will have this context set as @c internalContext() */ Declaration* owner() const; /** * Sets the declaration/definition, and also updates it's internal context (they are strictly paired together). * * The declaration has to be part of the same top-context. */ void setOwner(Declaration* decl); /** * Calculate the depth of this context, from the top level context in the file. */ int depth() const; /** * Find the top context. */ TopDUContext* topContext() const override; /** * Visits all duchain objects in the whole duchain. * * Classes that hold a unique link to duchain objects like instantiations * have to pass the visitor over to those classes. * */ virtual void visit(DUChainVisitor& visitor); /** - * Find the context which most specifically covers @a position. + * Find the context which most specifically covers @p position. * * The search is recursive, so the most specific context is found. * - * @param includeRightBorder When this is true, contexts will also be found that - * have the position on their right border. + * @param includeBorders When this is true, contexts will also be found that + * have the position on their borders. * * @warning This uses the ranges in the local revision of the document (at last parsing time). * Use DUChainBase::transformToLocalRevision to transform the cursor into that revision first. */ DUContext* findContextAt(const CursorInRevision& position, bool includeBorders = false) const; /** - * Find a child declaration that has a rang that covers the given @a position. + * Find a child declaration that has a rang that covers the given @p position. * * The search is local, not recursive. * * @warning This uses the ranges in the local revision of the document (at last parsing time). * Use DUChainBase::transformToLocalRevision to transform the cursor into that revision first. */ Declaration* findDeclarationAt(const CursorInRevision& position) const; /** * Find the context which most specifically covers @a range. * * @warning This uses the ranges in the local revision of the document (at last parsing time). * Use DUChainBase::transformToLocalRevision to transform the cursor into that revision first. */ DUContext* findContextIncluding(const RangeInRevision& range) const; /** * Calculate the fully qualified scope identifier. */ QualifiedIdentifier scopeIdentifier(bool includeClasses = false) const; /** * Returns true if this context has the same scope identifier as the given one. * * @note This is much more efficient than computing the identifiers through @c scopeIdentifier(..) * and comparing them */ bool equalScopeIdentifier(const DUContext* rhs) const; /** * Scope identifier, used to qualify the identifiers occurring in each context. * * This is the part relative to the parent context. */ QualifiedIdentifier localScopeIdentifier() const; /** * Same as @c localScopeIdentifier(), but faster. */ IndexedQualifiedIdentifier indexedLocalScopeIdentifier() const; /** * Scope identifier, used to qualify the identifiers occurring in each context * This must not be called once this context has children. */ void setLocalScopeIdentifier(const QualifiedIdentifier& identifier); /** * Returns whether this context is listed in the symbol table (Namespaces and classes) */ bool inSymbolTable() const; /** * Move this object into/out of the symbol table. * * @note You need to have a duchain write lock, unless this is a TopDUContext. */ void setInSymbolTable(bool inSymbolTable); /** * Returns the immediate parent context of this context. */ DUContext* parentContext() const; /** * Represents an imported parent context. */ struct KDEVPLATFORMLANGUAGE_EXPORT Import { /** * @note DUChain must be read-locked when this is called */ Import(DUContext* context, const DUContext* importer, const CursorInRevision& position = CursorInRevision::invalid()); Import() : position(CursorInRevision::invalid()) { } Import(const DeclarationId& id, const CursorInRevision& position = CursorInRevision::invalid()); bool operator==(const Import& rhs) const { return m_context == rhs.m_context && m_declaration == rhs.m_declaration; } /** * @param topContext The top-context from where to start searching. * This is important to find the correct imports * in the case of templates or similar structures. */ DUContext* context(const TopDUContext* topContext, bool instantiateIfRequired = true) const; /** * Returns the top-context index, if this import is not a specialization import. */ uint topContextIndex() const { return m_context.topContextIndex(); } IndexedDUContext indexedContext() const { return m_context; } /** * Returns true if this import is direct. * * That is, the import is not referred to by its identifier, * but rather directly by its index. */ bool isDirect() const; /** * If this import is indirect, returns the imported declaration-id */ DeclarationId indirectDeclarationId() const { return m_declaration; } CursorInRevision position; private: //Either we store m_declaration, or m_context. That way we can resolve specialized contexts. ///@todo Compress using union DeclarationId m_declaration; IndexedDUContext m_context; }; /** * Returns the list of imported parent contexts for this context. * * @warning The list may contain objects that are not valid any more, * i.e. data() returns zero, @see addImportedParentContext) * @warning The import structure may contain loops if this is a TopDUContext, * so be careful when traversing the tree. * @note This is expensive. */ virtual QVector importedParentContexts() const; /** * If the given context is directly imported into this one, and * @c addImportedParentContext(..) was called with a valid cursor, * this will return that position. Otherwise an invalid cursor is returned. */ virtual CursorInRevision importPosition(const DUContext* target) const; /** * Returns true if this context imports @param origin at any depth, else false. */ virtual bool imports(const DUContext* origin, const CursorInRevision& position = CursorInRevision::invalid()) const; /** * Adds an imported context. * * @param anonymous If this is true, the import will not be registered at the imported context. * This allows du-chain contexts importing without having a write-lock. * @param position Position where the context is imported. This is mainly important in C++ with included files. * * If the context is already imported, only the position is updated. * * @note Be sure to have set the text location first, so that the chain is sorted correctly. */ virtual void addImportedParentContext(DUContext* context, const CursorInRevision& position = CursorInRevision::invalid(), bool anonymous = false, bool temporary = false); /** * Adds an imported context, which may be indirect. * * @warning This is only allowed if this context is _NOT_ a top-context. * @warning When using this mechanism, this context will not be registered as importer to the other one. * @warning The given import _must_ be indirect. * * @return true if the import was already imported before, else false. */ bool addIndirectImport(const DUContext::Import& import); /** * Removes a child context. */ virtual void removeImportedParentContext(DUContext* context); /** * Clear all imported parent contexts. */ virtual void clearImportedParentContexts(); /** * If this is set to true, all declarations that are added to this context * will also be visible in the parent-context. * * They will be visible in the parent using @c findDeclarations(...) and * @c findLocalDeclarations(...), but will not be in the list of @c localDeclarations(...). */ void setPropagateDeclarations(bool propagate); bool isPropagateDeclarations() const; /** * Returns the list of contexts importing this context. * * @note Very expensive, since the importers top-contexts need to be loaded. */ virtual QVector importers() const; /** * Returns the list of indexed importers. * * Cheap, because nothing needs to be loaded. */ KDevVarLengthArray indexedImporters() const; /** * Returns the list of immediate child contexts for this context. * * @note This is expensive. */ QVector childContexts() const; /** * Clears and deletes all child contexts recursively. * * This will not cross file boundaries. */ void deleteChildContextsRecursively(); /** * Resort the child contexts by their range. * * You must call this when you manually change the range of child contexts in a way * that could break the internal range sorting. */ void resortChildContexts(); /** * Returns true if this declaration is accessible through the du-chain, * and thus cannot be edited without a du-chain write lock */ virtual bool inDUChain() const; /** * Retrieve the context which is specialized with the given * @a specialization as seen from the given @a topContext. * * @param specialization the specialization index (see DeclarationId) * @param topContext the top context representing the perspective from which to specialize. * if @p topContext is zero, only already existing specializations are returned, * and if none exists, zero is returned. * @param upDistance upwards distance in the context-structure of the * given specialization-info. This allows specializing children. */ virtual DUContext* specialize(const IndexedInstantiationInformation& specialization, const TopDUContext* topContext, int upDistance = 0); /** - * Searches for and returns a declaration with a given @a identifier in this context, which - * is currently active at the given text @a position, with the given type @a dataType. + * Searches for and returns a declaration with a given @p identifier in this context, which + * is currently active at the given text @p position, with the given type @p dataType. * In fact, only items are returned that are declared BEFORE that position. * * @param identifier the identifier of the definition to search for - * @param location the text position to search for + * @param position the text position to search for * @param topContext the top-context from where a completion is triggered. * This is needed so delayed types (templates in C++) can be resolved in the correct context. - * @param type the type to match, or null for no type matching. + * @param dataType the type to match, or null for no type matching. * * @returns the requested declaration if one was found, otherwise null. * * @warning this may return declarations which are not in this tree, you may need to lock them too... */ QList findDeclarations(const QualifiedIdentifier& identifier, const CursorInRevision& position = CursorInRevision::invalid(), const AbstractType::Ptr& dataType = AbstractType::Ptr(), const TopDUContext* topContext = nullptr, SearchFlags flags = NoSearchFlags) const; /** * Searches for and returns a declaration with a given @a identifier in this context, which * is currently active at the given text @a position. * * @param identifier the identifier of the definition to search for * @param topContext the top-context from where a completion is triggered. * This is needed so delayed types(templates in C++) can be resolved in the correct context. - * @param location the text position to search for + * @param position the text position to search for * * @returns the requested declaration if one was found, otherwise null. * * @warning this may return declarations which are not in this tree, you may need to lock them too... * * @overload */ QList findDeclarations(const IndexedIdentifier& identifier, const CursorInRevision& position = CursorInRevision::invalid(), const TopDUContext* topContext = nullptr, SearchFlags flags = NoSearchFlags) const; /** * Prefer the version above for speed reasons. */ QList findDeclarations(const Identifier& identifier, const CursorInRevision& position = CursorInRevision::invalid(), const TopDUContext* topContext = nullptr, SearchFlags flags = NoSearchFlags) const; /** * Returns the type of any @a identifier defined in this context, or * null if one is not found. * * Does not search imported parent-contexts(like base-classes). */ QList findLocalDeclarations(const IndexedIdentifier& identifier, const CursorInRevision& position = CursorInRevision::invalid(), const TopDUContext* topContext = nullptr, const AbstractType::Ptr& dataType = AbstractType::Ptr(), SearchFlags flags = NoSearchFlags) const; /** * Prefer the version above for speed reasons. */ QList findLocalDeclarations(const Identifier& identifier, const CursorInRevision& position = CursorInRevision::invalid(), const TopDUContext* topContext = nullptr, const AbstractType::Ptr& dataType = AbstractType::Ptr(), SearchFlags flags = NoSearchFlags) const; /** * Clears all local declarations. * * Does not delete the declaration; the caller assumes ownership. */ QVector clearLocalDeclarations(); /** * Clears all local declarations. * * Deletes these declarations, as the context has ownership. */ void deleteLocalDeclarations(); /** * Returns all local declarations * * @param source A source-context that is needed to instantiate template-declarations in some cases. * If it is zero, that signalizes that missing members should not be instantiated. */ virtual QVector localDeclarations(const TopDUContext* source = nullptr) const; /** * Resort the local declarations by their range. * * You must call this when you manually change the range of declarations in a way * that could break the internal range sorting. */ void resortLocalDeclarations(); /** - * Searches for the most specific context for the given cursor @a position in the given @a url. + * Searches for the most specific context for the given cursor @p position in the given @p url. * - * @param location the text position to search for + * @param position the text position to search for * @param parent the parent context to search from (this is mostly an internal detail, but if you only * want to search in a subbranch of the chain, you may specify the parent here) * * @returns the requested context if one was found, otherwise null. */ DUContext* findContext(const CursorInRevision& position, DUContext* parent = nullptr) const; /** * Iterates the tree to see if the provided @a context is a subcontext of this context. * * @returns true if @a context is a subcontext, otherwise false. */ bool parentContextOf(DUContext* context) const; /** - * Return a list of all reachable declarations for a given cursor @a position in a given @a url. + * Return a list of all reachable declarations for a given cursor @p position in a given @p url. * - * @param location the text position to search for + * @param position the text position to search for * @param topContext the top-context from where a completion is triggered. * This is needed so delayed types(templates in C++) can be resolved * in the correct context. * @param searchInParents should declarations from parent-contexts be listed? * If false, only declarations from this and imported contexts will be returned. * * The returned declarations are paired together with their inheritance-depth, * which is the count of steps to into other contexts that were needed to find the declaration. * Declarations reached through a namespace- or global-context are offsetted by 1000. * * This also includes Declarations from sub-contexts that were propagated upwards * using @c setPropagateDeclarations(true). * * @returns the requested declarations, if any were active at that location. * Declarations propagated into this context(@c setPropagateDeclarations) are included. */ QList< QPair > allDeclarations(const CursorInRevision& position, const TopDUContext* topContext, bool searchInParents = true) const; /** * Delete and remove all slaves (uses, declarations, definitions, contexts) that are not in the given set. */ void cleanIfNotEncountered(const QSet& encountered); /** * Uses: * A "Use" represents any position in a document where a Declaration is used literally. * For efficiency, since there can be many many uses, they are managed efficiently by * TopDUContext and DUContext. In TopDUContext, the used declarations are registered * and assigned a "Declaration-Index" while calling TopDUContext::indexForUsedDeclaration. * From such a declaration-index, the declaration can be retrieved back by calling * @c TopDUContext::usedDeclarationForIndex. * * The actual uses are stored within DUContext, where each use consists of a range and * the declaration-index of the used declaration. * */ /** * Return a vector of all uses which occur in this context. * * To get the actual declarations, use @c TopDUContext::usedDeclarationForIndex(..) * with the declarationIndex. */ const Use* uses() const; /** * Returns the count of uses that can be accessed through @c uses() */ int usesCount() const; /** * Determines whether the given declaration has uses or not */ static bool declarationHasUses(Declaration* decl); /** * Find the use which encompasses @a position, if one exists. * @return The local index of the use, or -1 */ int findUseAt(const CursorInRevision& position) const; /** * @note The change must not break the ordering */ void changeUseRange(int useIndex, const RangeInRevision& range); /** - * Assigns the declaration represented by @param declarationIndex - * to the use with index @param useIndex. + * Assigns the declaration represented by @p declarationIndex + * to the use with index @p useIndex. */ void setUseDeclaration(int useIndex, int declarationIndex); /** - * Creates a new use of the declaration given through @param declarationIndex. + * Creates a new use of the declaration given through @p declarationIndex. * The index must be retrieved through @c TopDUContext::indexForUsedDeclaration(..). * * @param range The range of the use * @param insertBefore A hint where in the vector of uses to insert the use. * Must be correct so the order is preserved(ordered by position), * or -1 to automatically choose the position. * * @return Local index of the created use */ int createUse(int declarationIndex, const RangeInRevision& range, int insertBefore = -1); /** - * Deletes the use number @param index. + * Deletes the use number @p index. * * @param index is the position in the vector of uses, not a used declaration index. */ void deleteUse(int index); /** * Clear and delete all uses in this context. */ virtual void deleteUses(); /** * Recursively delete all uses in this context and all its child-contexts */ virtual void deleteUsesRecursively(); /** * Can be specialized by languages to create a navigation/information-widget. * * Ideally, the widget would be based on @c KDevelop::QuickOpenEmbeddedWidgetInterface * for user-interaction within the quickopen list. * * The returned widget will be owned by the caller. * * @param decl A member-declaration of this context the navigation-widget should be created for. * Zero to create a widget for this context. * @param topContext Top-context from where the navigation-widget is triggered. * In C++, this is needed to resolve forward-declarations. * @param htmlPrefix Html-formatted text that should be prepended before any information shown by this widget * @param htmlSuffix Html-formatted text that should be appended to any information shown by this widget * * Can return zero which disables the navigation widget. * * If you setProperty("DoNotCloseOnCursorMove", true) on the widget returned, * then the widget will not close when the cursor moves in the document, which * enables you to change the document contents from the widget without immediately closing the widget. */ virtual QWidget* createNavigationWidget(Declaration* decl = nullptr, TopDUContext* topContext = nullptr, const QString& htmlPrefix = QString(), const QString& htmlSuffix = QString(), AbstractNavigationWidget::DisplayHints hints = AbstractNavigationWidget::NoHints) const; enum { Identity = 2 }; /** * Represents multiple qualified identifiers in a way that is better * to manipulate and allows applying namespace-aliases or -imports easily. * * A SearchItem generally represents a tree of identifiers, and represents * all the qualified identifiers that can be constructed by walking * along the tree starting at an arbitrary root-node into the depth using the "next" pointers. * * The insertion order in the hierarchy determines the order of the represented list. */ struct KDEVPLATFORMLANGUAGE_EXPORT SearchItem : public QSharedData { typedef QExplicitlySharedDataPointer Ptr; typedef KDevVarLengthArray PtrList; /** - * Constructs a representation of the given @param id qualified identifier, - * starting at its index @param start. + * Constructs a representation of the given @p id qualified identifier, + * starting at its index @p start. * * @param nextItem is set as next item to the last item in the chain */ SearchItem(const QualifiedIdentifier& id, const Ptr& nextItem = Ptr(), int start = 0); /** - * Constructs a representation of the given @param id qualified identifier, - * starting at its index @param start. + * Constructs a representation of the given @p id qualified identifier, + * starting at its index @p start. * - * @param nextItem is set as next item to the last item in the chain + * @param nextItems is set as next item to the last item in the chain */ SearchItem(const QualifiedIdentifier& id, const PtrList& nextItems, int start = 0); SearchItem(bool explicitlyGlobal, const IndexedIdentifier& id, const PtrList& nextItems); SearchItem(bool explicitlyGlobal, const IndexedIdentifier& id, const Ptr& nextItem); bool isEmpty() const; bool hasNext() const; /** * Appends the given item to every item that can be reached from this item, * and not only to the end items. * * The effect to search is that the given item is searched with all prefixes * contained in this earch-item prepended. * * @warning This changes all contained sub-nodes, but they can be shared with * other SearchItem trees. You should not use this on SearchItem trees * that have shared nodes with other trees. * * @note These functions ignore explicitly global items. */ void addToEachNode(const Ptr& item); void addToEachNode(const PtrList& items); /** * Returns true if the given identifier matches one of the identifiers * represented by this SearchItem. Does not respect the explicitlyGlobal flag */ bool match(const QualifiedIdentifier& id, int offset = 0) const; /** * @note expensive */ QList toList(const QualifiedIdentifier& prefix = QualifiedIdentifier()) const; void addNext(const Ptr& other); bool isExplicitlyGlobal; IndexedIdentifier identifier; PtrList next; }; ///@todo Should be protected, moved here temporarily until I have figured ///out why the gcc 4.1.3 fails in cppducontext.h:212, which should work (within kdevelop) /// Declaration search implementation + typedef QList DeclarationList; + /** * This is a more complex interface to the declaration search engine. * * Always prefer @c findDeclarations(..) when possible. * * Advantage of this interface: * - You can search multiple identifiers at one time. * However, those should be aliased identifiers for one single item, because * search might stop as soon as one item is found. * - The source top-context is needed to correctly resolve template-parameters * * @param position A valid position, if in doubt use textRange().end() * * @warning @p position must be valid! * * @param depth Depth of the search in parents. This is used to prevent endless * recursions in endless import loops. * * * @return whether the search was successful. If it is false, it had to be stopped * for special reasons (like some flags) */ - typedef QList DeclarationList; - virtual bool findDeclarationsInternal(const SearchItem::PtrList& identifiers, const CursorInRevision& position, const AbstractType::Ptr& dataType, DeclarationList& ret, const TopDUContext* source, SearchFlags flags, uint depth ) const; /** * Returns the qualified identifier @p id with all aliases (for example namespace imports) applied * * @example: If the namespace 'Foo' is imported, and id is 'Bar', * then the returned list is 'Bar' and 'Foo::Bar' */ QList fullyApplyAliases(const QualifiedIdentifier& id, const TopDUContext* source) const; protected: /** * After one scope was searched, this function is asked whether more * results should be collected. Override it, for example to collect overloaded functions. * - * The default-implementation returns true as soon as decls is not empty. + * The default-implementation returns true as soon as @p decls is not empty. */ virtual bool foundEnough( const DeclarationList& decls , SearchFlags flags ) const; /** * Merges definitions and their inheritance-depth up all branches of the * definition-use chain into one hash. * * This includes declarations propagated from sub-contexts. * - * @param hadUrls is used to count together all contexts that already were + * @param hadContexts is used to count together all contexts that already were * visited, so they are not visited again. */ virtual void mergeDeclarationsInternal(QList< QPair >& definitions, const CursorInRevision& position, QHash& hadContexts, const TopDUContext* source, bool searchInParents = true, int currentDepth = 0) const; void findLocalDeclarationsInternal(const Identifier& identifier, const CursorInRevision & position, const AbstractType::Ptr& dataType, DeclarationList& ret, const TopDUContext* source, SearchFlags flags ) const; virtual void findLocalDeclarationsInternal(const IndexedIdentifier& identifier, const CursorInRevision & position, const AbstractType::Ptr& dataType, DeclarationList& ret, const TopDUContext* source, SearchFlags flags ) const; /** * Applies namespace-imports and namespace-aliases and returns * possible absolute identifiers that need to be searched. * * @param targetIdentifiers will be filled with all identifiers that should * be searched for, instead of identifier. * @param onlyImports if this is true, namespace-aliases will not be respected, * but only imports. This is faster. */ void applyAliases(const SearchItem::PtrList& identifiers, SearchItem::PtrList& targetIdentifiers, const CursorInRevision& position, bool canBeNamespace, bool onlyImports = false) const; /** * Applies the aliases that need to be applied when moving the search * from this context up to the parent-context. * * The default-implementation adds a set of identifiers with the own local * identifier prefixed, if this is a namespace. * * For C++, this is needed when searching out of a namespace, so the item * can be found within that namespace in another place. */ virtual void applyUpwardsAliases(SearchItem::PtrList& identifiers, const TopDUContext* source) const; DUContext(DUContextData& dd, const RangeInRevision& range, DUContext* parent = nullptr, bool anonymous = false); /** * Just uses the data from the given context. Doesn't copy or change anything, * and the data will not be deleted on this contexts destruction. */ DUContext(DUContext& useDataFrom); /** * Whether this context, or any of its parent contexts, has been inserte * anonymously into the du-chain * * @see DUContext::DUContext */ bool isAnonymous() const; /** * This is called whenever the search needs to do the decision whether it * should be continued in the parent context. * * It is not called when the DontSearchInParent flag is set. Else this should * be overridden to do language-specific logic. * * The default implementation returns false if the flag InImportedParentContext is set. */ virtual bool shouldSearchInParent(SearchFlags flags) const; private: void rebuildDynamicData(DUContext* parent, uint ownIndex) override; friend class TopDUContext; friend class IndexedDUContext; friend class LocalIndexedDUContext; friend class TopDUContextDynamicData; DUCHAIN_DECLARE_DATA(DUContext) class DUContextDynamicData* m_dynamicData; }; /** * This is the identifier that can be used to search namespace-import declarations, * and should be used to store namespace-imports. * * It is stored statically for performance-reasons, so it doesn't need to be * constructed every time it is used. * * @see NamespaceAliasDeclaration. */ KDEVPLATFORMLANGUAGE_EXPORT const Identifier& globalImportIdentifier(); /** * This is the identifier that can be used to search namespace-alias declarations. * * It is stored statically for performance-reasons, so it doesn't need to be * constructed every time it is used. * * @see NamespaceAliasDeclaration. */ KDEVPLATFORMLANGUAGE_EXPORT const Identifier& globalAliasIdentifier(); /** * This is the identifier that can be used to search namespace-import declarations, * and should be used to store namespace-imports. * * It is stored statically for performance-reasons, so it doesn't need to be * constructed every time it is used. * * @see NamespaceAliasDeclaration. */ KDEVPLATFORMLANGUAGE_EXPORT const IndexedIdentifier& globalIndexedImportIdentifier(); /** * This is the identifier that can be used to search namespace-alias declarations. * * It is stored statically for performance-reasons, so it doesn't need to be * constructed every time it is used. * * @see NamespaceAliasDeclaration. */ KDEVPLATFORMLANGUAGE_EXPORT const IndexedIdentifier& globalIndexedAliasIdentifier(); /** - * Collects all uses of the given @param declarationIndex + * Collects all uses of the given @p declarationIndex */ KDEVPLATFORMLANGUAGE_EXPORT QList allUses(DUContext* context, int declarationIndex, bool noEmptyRanges = false); } Q_DECLARE_TYPEINFO(KDevelop::DUContext::Import, Q_MOVABLE_TYPE); KDEVPLATFORMLANGUAGE_EXPORT QDebug operator<<(QDebug dbg, const KDevelop::DUContext::Import& import); #endif // KDEVPLATFORM_DUCONTEXT_H diff --git a/language/duchain/dumpdotgraph.h b/language/duchain/dumpdotgraph.h index 85ac59d2ee..95f417ace6 100644 --- a/language/duchain/dumpdotgraph.h +++ b/language/duchain/dumpdotgraph.h @@ -1,47 +1,45 @@ /*************************************************************************** Copyright 2007 David Nolden ***************************************************************************/ /*************************************************************************** * * * 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) any later version. * * * ***************************************************************************/ #ifndef KDEVPLATFORM_DUMPDOTGRAPH_H #define KDEVPLATFORM_DUMPDOTGRAPH_H #include #include #include #include namespace KDevelop { class TopDUContext; class DUContext; /** * A helper-class for debugging, that nicely visualizes the whole structure of a du-context. * */ class KDEVPLATFORMLANGUAGE_EXPORT DumpDotGraph { Q_DISABLE_COPY(DumpDotGraph) public: DumpDotGraph(); ~DumpDotGraph(); /** * The context, it's, and if it is not a top-context also all contexts importing it we be drawn. * Parent-contexts will not be respected, so if you want the whole structure, you will need to pass the top-context. * @param shortened if this is given sub-items like declarations, definitions, child-contexts, etc. will not be shown as separate nodes - * @param isMaster must always be true when called from outside - * @param allFiles is for internal use only. * */ QString dotGraph(KDevelop::DUContext* context, bool shortened = false); private: class DumpDotGraphPrivate* const d; }; } #endif // KDEVPLATFORM_DUMPDOTGRAPH_H diff --git a/language/duchain/forwarddeclaration.h b/language/duchain/forwarddeclaration.h index 7b103d7d7a..4fec0b2ffd 100644 --- a/language/duchain/forwarddeclaration.h +++ b/language/duchain/forwarddeclaration.h @@ -1,92 +1,91 @@ /* This file is part of KDevelop Copyright 2006-2008 Hamish Rodda Copyright 2007-2008 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDEVPLATFORM_FORWARDDECLARATION_H #define KDEVPLATFORM_FORWARDDECLARATION_H #include "declaration.h" #include "declarationdata.h" namespace KDevelop { class KDEVPLATFORMLANGUAGE_EXPORT ForwardDeclarationData : public DeclarationData { public: ForwardDeclarationData() { } ForwardDeclarationData( const ForwardDeclarationData& rhs ) : DeclarationData( rhs ) { } }; /** * Represents a forward declaration */ class KDEVPLATFORMLANGUAGE_EXPORT ForwardDeclaration : public Declaration { public: /** * Constructor. * * If \a context is in the symbol table, the declaration will automatically be added into the symbol table. * - * \param url url of the document where this occurred * \param range range of the alias declaration's identifier - * \param parentContext context in which this declaration occurred + * \param context context in which this declaration occurred * */ ForwardDeclaration(const RangeInRevision& range, DUContext* context); explicit ForwardDeclaration(ForwardDeclarationData& data); ///Copy-constructor for cloning ForwardDeclaration(const ForwardDeclaration& rhs); /// Destructor. virtual ~ForwardDeclaration(); virtual bool isForwardDeclaration() const override; /** * Resolved the forward-declaration using the given import-trace. * The topcontext is needed for correct functionality, and may only be * zero when the declaration is resolved starting from the top-context * the forward-declaration is contained in. * * If this forward-declaration has a type assigned that is not derived from ForwardDeclarationType, * and that is derived from IdentifiedType, the declaration of that type is returned here. * */ virtual Declaration* resolve(const TopDUContext* topContext) const; virtual DUContext * logicalInternalContext(const TopDUContext* topContext) const override; virtual QString toString() const override; enum { Identity = 10 }; typedef Declaration BaseClass; private: virtual Declaration* clonePrivate() const override; DUCHAIN_DECLARE_DATA(ForwardDeclaration) }; } #endif // KDEVPLATFORM_DECLARATION_H diff --git a/language/duchain/navigation/abstractincludenavigationcontext.h b/language/duchain/navigation/abstractincludenavigationcontext.h index ac6e59dae3..0f9cf00cc0 100644 --- a/language/duchain/navigation/abstractincludenavigationcontext.h +++ b/language/duchain/navigation/abstractincludenavigationcontext.h @@ -1,80 +1,80 @@ /* 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_ABSTRACTINCLUDENAVIGATIONCONTEXT_H #define KDEVPLATFORM_ABSTRACTINCLUDENAVIGATIONCONTEXT_H #include "abstractnavigationcontext.h" #include "../../util/includeitem.h" #include "../../duchain/parsingenvironment.h" #include namespace KDevelop { /** * Abstract navigation context for file includes. * * Example usage: * \code * namespace LANG { * class IncludeNavigationContext : public AbstractIncludeNavigationContext * { * public: * IncludeNavigationContext(const IncludeItem& item, TopDuContextPointer topContext) * : AbstractIncludeNavigationContext(item, topContext, KDevelop::LANGParsingEnvironment) {} * protected: * virtual void getFileInfo(KDevelop::TopDUContext* duchain) * { * // write language dependent stuff via modifyHtml() * } * }; * } * \endcode */ class KDEVPLATFORMLANGUAGE_EXPORT AbstractIncludeNavigationContext : public AbstractNavigationContext { Q_OBJECT public: AbstractIncludeNavigationContext(const IncludeItem& item, TopDUContextPointer topContext, const ParsingEnvironmentType& type); QString html(bool shorten) override; QString name() const override; protected: /// Overwrite this to add language dependent information for a given file. /// By default only "included by" and "includes" /// NOTE: You should always append a newline (
) if you write anything. virtual void getFileInfo(KDevelop::TopDUContext* duchain); ///Should return true if this declaration should be shown, and false if not ///The duchain is locked when this is called virtual bool filterDeclaration(Declaration* decl); private: /// Only environments with this type will be considered ParsingEnvironmentType m_type; - ///@param first must initially be true typedef QPair IdentifierPair; + ///@param first must initially be true void addDeclarationsFromContext(KDevelop::DUContext* ctx, bool& first, QList& addedDeclarations, const QString& indent = {} ); IncludeItem m_item; }; } #endif // KDEVPLATFORM_ABSTRACTINCLUDENAVIGATIONCONTEXT_H diff --git a/language/duchain/navigation/abstractnavigationcontext.h b/language/duchain/navigation/abstractnavigationcontext.h index 1f924505ad..3f7dceeba8 100644 --- a/language/duchain/navigation/abstractnavigationcontext.h +++ b/language/duchain/navigation/abstractnavigationcontext.h @@ -1,194 +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 + /// Returns the html font-size prefix (aka. <small> or similar) for the given mode QString fontSizePrefix(bool shorten) const; - /// Returns the html font-size suffix (aka. or similar) for the given mode + /// Returns the html font-size suffix (aka. <small> 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/topducontext.h b/language/duchain/topducontext.h index 2e50164724..c08bce6f5b 100644 --- a/language/duchain/topducontext.h +++ b/language/duchain/topducontext.h @@ -1,376 +1,376 @@ /* This file is part of KDevelop Copyright 2006 Hamish Rodda Copyright 2007-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_TOPDUCONTEXT_H #define KDEVPLATFORM_TOPDUCONTEXT_H #include "ducontext.h" #include #include template< class T > class QExplicitlySharedDataPointer; namespace KDevelop { class IAstContainer; class QualifiedIdentifier; class DUChain; class ParsingEnvironmentFile; class TopDUContextData; class TopDUContextLocalPrivate; class IndexedTopDUContext; // class TopDUContextDynamicData; class Problem; class DeclarationChecker; class TopDUContext; struct KDEVPLATFORMLANGUAGE_EXPORT RecursiveImportRepository { static Utils::BasicSetRepository* repository(); }; ///Maps an imported top-context to a pair: ///1. The distance to the top-context, and 2. The next step towards the top-context ///in the chain. typedef QHash > RecursiveImports; typedef DUChainPointer TopDUContextPointer; typedef QExplicitlySharedDataPointer ProblemPointer; ///KDevelop can unload unused top-context at any time. To prevent unloading, ///keep a ReferencedTopDUContext. class KDEVPLATFORMLANGUAGE_EXPORT ReferencedTopDUContext { public: ReferencedTopDUContext(TopDUContext* context = 0); ReferencedTopDUContext(const ReferencedTopDUContext& rhs); ~ReferencedTopDUContext(); ReferencedTopDUContext& operator=(const ReferencedTopDUContext& rhs); inline TopDUContext* data() const { return m_topContext; } inline operator TopDUContext*() const { return m_topContext; } inline bool operator==(const ReferencedTopDUContext& rhs) const { return m_topContext == rhs.m_topContext; } inline bool operator!=(const ReferencedTopDUContext& rhs) const { return m_topContext != rhs.m_topContext; } inline TopDUContext* operator->() const { return m_topContext; } inline uint hash() const { return (uint)(((quint64)m_topContext) * 37); } private: TopDUContext* m_topContext; }; /** * The top context in a definition-use chain for one source file. * * Implements SymbolTable lookups and locking for the chain. * * Contexts and Classes can only be found through TopDUContext if they are in the symbol table. * @see DUContext::setInSymbolTable, Declaration::setInSymbolTable * * \todo move the registration with DUChain here * * @warning Do not delete top-contexts directly, use DUChain::removeDocumentChain instead. */ class KDEVPLATFORMLANGUAGE_EXPORT TopDUContext : public DUContext { public: explicit TopDUContext(const IndexedString& url, const RangeInRevision& range, ParsingEnvironmentFile* file = 0); explicit TopDUContext(TopDUContextData& data); TopDUContext* topContext() const override; ///Returns an indexed representation of this top-context. Indexed representations stay valid even if the top-context is unloaded. IndexedTopDUContext indexed() const; uint ownIndex() const; IndexedString url() const override; /** * @see ParsingEnvironmentFile * May return zero if no file was set. * */ QExplicitlySharedDataPointer parsingEnvironmentFile() const; /// Returns true if this object is being deleted, otherwise false. bool deleting() const; /// Returns true if this object is registered in the du-chain. If it is not, all sub-objects(context, declarations, etc.) can be changed virtual bool inDUChain() const override; /// This flag is only used by DUChain, never change it from outside. void setInDuChain(bool); /// Whether this top-context has a stored version on disk bool isOnDisk() const; /** * Returns a list of all problems encountered while parsing this top-context. * Does not include the problems of imported contexts. * */ QList problems() const; /** * Add a parsing-problem to this context. * * \note you must be holding a write lock when you access this function. * */ void addProblem(const ProblemPointer& problem); /** * Clear the list of problems * * \note you must be holding a write lock when you access this function. */ void clearProblems(); /** * Set the list of problems, replacing all existing ones. * * \note you must be holding a write lock when you access this function. */ void setProblems(const QList& pointers); /** * Determine if this chain imports another chain recursively. * * This uses the imports-cache for speedup if it is available, thus it is not necessarily 100% correct * if the cache is not up-to-date. * * \note you must be holding a read but not a write chain lock when you access this function. */ virtual bool imports(const DUContext* origin, const CursorInRevision& position) const override; enum { Identity = 4 }; enum Features : quint16 { ///Top-context features standard that can be requested from the duchain, and that are stored in the features() member. Empty = 0, //Only the top-context structure (imports etc.) is built, but no declarations and no contexts SimplifiedVisibleDeclarationsAndContexts = 2, //The top-context should only contain publically simplified accessible declarations and contexts, without doing type look-up, //without extended information like function-argument declarations, etc., imported contexts can be parsed with 'Empty' features //This flag essentially leads to a ctags-like processing level. VisibleDeclarationsAndContexts = SimplifiedVisibleDeclarationsAndContexts + 4, //Default: The top-context should only contain publically accessible declarations and contexts AllDeclarationsAndContexts = VisibleDeclarationsAndContexts + 8, //The top-context should also contain non-public declarations and contexts, but no uses AllDeclarationsContextsAndUses = 16 + AllDeclarationsAndContexts, //The top-context should contain uses and all declarations + contexts AST = 32, //Signalizes that the ast() should be filled AllDeclarationsContextsUsesAndAST = AST | AllDeclarationsContextsAndUses, //Convenience flag, combining AST and AllDeclarationsContextsAndUses ///Additional update-flags that have a special meaning during updating, but are not set stored into a top-context Recursive = 64, //Request the given features on all recursively imported contexts. Only the features are applied recursively (including AST) ForceUpdate = 128, //Enforce updating the top-context ForceUpdateRecursive = ForceUpdate | 256, //Enforce updating the top-context and all its imports ///You can define own language-dependent features behind this flag LastFeature = 512 }; ///Returns the currently active features of this top-context. The features will include AST if ast() is valid. Features features() const; ///Set the features of this top-context. These features are ignored: AST, ForceUpdate, and ForceUpdateRecursive. void setFeatures(Features); /** * Retrieves or creates a local index that is to be used for referencing the given @param declaration * in local uses. Also registers this context as a user of the declaration. * @param create If this is false, only already registered indices will be returned. * If the declaration is not registered, std::numeric_limits::max() is returned * * The duchain must be write-locked if create is true, else it must at least be read-locked. * */ int indexForUsedDeclaration(Declaration* declaration, bool create = true); /** - * Tries to retrieve the used declaration @param declarationIndex - * @param context must be the context where the use happened + * Tries to retrieve the used declaration + * @param declarationIndex The index of the declaration which have to be retrieved * */ Declaration* usedDeclarationForIndex(unsigned int declarationIndex) const; /** * You can use this before you rebuild all uses. This does not affect any uses directly, * it only invalidates the mapping of declarationIndices to Declarations. * * usedDeclarationForIndex(..) must not be called until the use has gotten a new index through * indexForUsedDeclaration(..). * */ void clearUsedDeclarationIndices(); /** * Recursively deletes all contained uses, declaration-indices, etc. */ virtual void deleteUsesRecursively() override; /** * Returns the AST Container, that contains the AST created during parsing. * This is only created if you request the AST feature for parsing. * It may be discarded at any time. Every update without the AST feature will discard it. * The actual contents is language-specific. * * @todo Figure out logic to get rid of AST when it is not needed/useful */ QExplicitlySharedDataPointer ast() const; /** * Sets the AST Container. */ void setAst(QExplicitlySharedDataPointer ast); /** * Utility function to clear the AST Container */ void clearAst(); ///@param temporary If this is true, importers of this context will not be notified of the new imports. This greatly increases performance while removing the context, ///but creates in inconsistent import-structure. Therefore it is only suitable for temporary imports. These imports will not be visible from contexts that import this one. ///When this top-context does not own its private data, the import is added locally only to this context, not into the shared data. virtual void addImportedParentContext(DUContext* context, const CursorInRevision& position = CursorInRevision(), bool anonymous=false, bool temporary=false) override; ///Use this for mass-adding of imported contexts, it is faster than adding them individually. ///@param temporary If this is true, importers of this context will not be notified of the new imports. This greatly increases performance while removing the context, ///but creates in inconsistent import-structure. Therefore it is only suitable for temporary imports. These imports will not be visible from contexts that import this one. ///When this top-context does not own its private data, the import is added locally only to this context, not into the shared data. virtual void addImportedParentContexts(const QList >& contexts, bool temporary=false); ///When this top-context does not own its private data, the import is removed locally only from this context, not from the shared data. virtual void removeImportedParentContext(DUContext* context) override; ///Use this for mass-removing of imported contexts, it is faster than removing them individually. ///When this top-context does not own its private data, the import is removed locally only from this context, not from the shared data. virtual void removeImportedParentContexts(const QList& contexts); ///When this top-context does not own its private data, only the local imports of this context are removed, not those from the shared data. virtual void clearImportedParentContexts() override; typedef Utils::StorableSet IndexedRecursiveImports; virtual QVector importedParentContexts() const override; virtual QVector importers() const override; ///Returns all currently loade importers virtual QList loadedImporters() const; virtual CursorInRevision importPosition(const DUContext* target) const override; ///Returns the set of all recursively imported top-contexts. If import-caching is used, this returns the cached set. ///The list also contains this context itself. This set is used to determine declaration-visibility from within this top-context. const IndexedRecursiveImports& recursiveImportIndices() const; /** * Updates the cache of recursive imports. When you call this, from that moment on the set returned by recursiveImportIndices() is fixed, until * you call it again to update them. If your language has a very complex often-changing import-structure, * like for example in the case of C++, it is recommended to call this during while parsing, instead of using * the expensive builtin implicit mechanism. * Note that if you use caching, you _must_ call this before you see any visibility-effect after adding imports. * * Using import-caching has another big advantage: A top-context can be loaded without loading all its imports. * * Note: This is relatively expensive since it requires loading all imported contexts. * * When this is called, the top-context must already be registered in the duchain. */ void updateImportsCache(); bool usingImportsCache() const; virtual bool findDeclarationsInternal(const SearchItem::PtrList& identifiers, const CursorInRevision& position, const AbstractType::Ptr& dataType, DeclarationList& ret, const TopDUContext* source, SearchFlags flags, uint depth) const override; protected: void setParsingEnvironmentFile(ParsingEnvironmentFile*); /** * Does the same as DUContext::updateAliases, except that it uses the symbol-store, and processes the whole identifier. * @param canBeNamespace whether the searched identifier may be a namespace. * If this is true, namespace-aliasing is applied to the last elements of the identifiers. * */ template void applyAliases( const SearchItem::PtrList& identifiers, Acceptor& accept, const CursorInRevision& position, bool canBeNamespace ) const; protected: virtual ~TopDUContext(); void clearFeaturesSatisfied(); void rebuildDynamicData(DUContext* parent, uint ownIndex) override; //Must be called after all imported top-contexts were loaded into the du-chain void rebuildDynamicImportStructure(); struct AliasChainElement; struct FindDeclarationsAcceptor; struct DeclarationChecker; struct ApplyAliasesBuddyInfo; template bool applyAliases( const QualifiedIdentifier& previous, const SearchItem::Ptr& identifier, Acceptor& acceptor, const CursorInRevision& position, bool canBeNamespace, ApplyAliasesBuddyInfo* buddy, uint recursionDepth ) const; //Same as imports, without the slow access-check, for internal usage bool importsPrivate(const DUContext * origin, const CursorInRevision& position) const; DUCHAIN_DECLARE_DATA(TopDUContext) ///Called by DUChain::removeDocumentChain to destroy this top-context. void deleteSelf(); //Most of these classes need access to m_dynamicData friend class DUChain; friend class DUChainPrivate; friend class TopDUContextData; friend class TopDUContextLocalPrivate; friend class TopDUContextDynamicData; friend class Declaration; friend class DUContext; friend class Problem; friend class IndexedDeclaration; friend class IndexedDUContext; friend class LocalIndexedDeclaration; friend class LocalIndexedDUContext; friend class LocalIndexedProblem; friend class DeclarationId; friend class ParsingEnvironmentFile; TopDUContextLocalPrivate* m_local; class TopDUContextDynamicData* m_dynamicData; }; /** * Returns all uses of the given declaration within this top-context and all sub-contexts * */ KDEVPLATFORMLANGUAGE_EXPORT QList allUses(TopDUContext* context, Declaration* declaration, bool noEmptyRanges = false); inline uint qHash(const ReferencedTopDUContext& ctx) { return ctx.hash(); } } Q_DECLARE_METATYPE(KDevelop::ReferencedTopDUContext) #endif // KDEVPLATFORM_TOPDUCONTEXT_H diff --git a/language/duchain/topducontextdynamicdata.h b/language/duchain/topducontextdynamicdata.h index fd7cbc1aed..5df7a3272d 100644 --- a/language/duchain/topducontextdynamicdata.h +++ b/language/duchain/topducontextdynamicdata.h @@ -1,184 +1,185 @@ -class QFile; -/* This is part of KDevelop +/* This is part of KDevelop Copyright 2008 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDEVPLATFORM_TOPDUCONTEXTDYNAMICDATA_H #define KDEVPLATFORM_TOPDUCONTEXTDYNAMICDATA_H #include #include #include #include #include #include "problem.h" +class QFile; + namespace KDevelop { class TopDUContext; class DUContext; class Declaration; class IndexedString; class IndexedDUContext; class DUChainBaseData; typedef QPair ArrayWithPosition; ///This class contains dynamic data of a top-context, and also the repository that contains all the data within this top-context. class TopDUContextDynamicData { public: explicit TopDUContextDynamicData(TopDUContext* topContext); ~TopDUContextDynamicData(); void clear(); /** * Allocates an index for the given declaration in this top-context. * The returned index is never zero. - * @param anonymous whether the declaration is temporary. If it is, it will be stored separately, not stored to disk, + * @param temporary whether the declaration is temporary. If it is, it will be stored separately, not stored to disk, * and a duchain write-lock is not needed. Else, you need a write-lock when calling this. */ uint allocateDeclarationIndex(Declaration* decl, bool temporary); Declaration* getDeclarationForIndex(uint index) const; bool isDeclarationForIndexLoaded(uint index) const; void clearDeclarationIndex(Declaration* decl); /** * Allocates an index for the given context in this top-context. * The returned index is never zero. - * @param anonymous whether the context is temporary. If it is, it will be stored separately, not stored to disk, + * @param temporary whether the context is temporary. If it is, it will be stored separately, not stored to disk, * and a duchain write-lock is not needed. Else, you need a write-lock when calling this. */ uint allocateContextIndex(DUContext* ctx, bool temporary); DUContext* getContextForIndex(uint index) const; bool isContextForIndexLoaded(uint index) const; void clearContextIndex(DUContext* ctx); /** * Allocates an index for the given problem in this top-context. * The returned index is never zero. */ uint allocateProblemIndex(ProblemPointer problem); ProblemPointer getProblemForIndex(uint index) const; void clearProblems(); ///Stores this top-context to disk void store(); ///Stores all remainings of this top-context that are on disk. The top-context will be fully dynamic after this. void deleteOnDisk(); ///Whether this top-context is on disk(Either has been loaded, or has been stored) bool isOnDisk() const; ///Loads the top-context from disk, or returns zero on failure. The top-context will not be registered anywhere, and will have no ParsingEnvironmentFile assigned. ///Also loads all imported contexts. The Declarations/Contexts will be correctly initialized, and put into the symbol tables if needed. static TopDUContext* load(uint topContextIndex); ///Loads only the url out of the data stored on disk for the top-context. static IndexedString loadUrl(uint topContextIndex); static bool fileExists(uint topContextIndex); ///Loads only the list of importers out of the data stored on disk for the top-context. static QList loadImporters(uint topContextIndex); static QList loadImports(uint topContextIndex); bool isTemporaryContextIndex(uint index) const; bool isTemporaryDeclarationIndex(uint index) const ; bool m_deleting; ///Flag used during destruction struct ItemDataInfo { uint dataOffset; /// Offset of the data uint parentContext; /// Parent context of the data (0 means the global context) }; private: bool hasChanged() const; void unmap(); //Converts away from an mmap opened file to a data array QString filePath() const; void loadData() const; const char* pointerInData(uint offset) const; ItemDataInfo writeDataInfo(const ItemDataInfo& info, const DUChainBaseData* data, uint& totalDataOffset); TopDUContext* m_topContext; template struct DUChainItemStorage { DUChainItemStorage(TopDUContextDynamicData* data); ~DUChainItemStorage(); void clearItems(); bool itemsHaveChanged() const; void storeData(uint& currentDataOffset, const QList& oldData); Item getItemForIndex(uint index) const; void clearItemIndex(const Item& item, const uint index); uint allocateItemIndex(const Item& item, const bool temporary); void deleteOnDisk(); bool isItemForIndexLoaded(uint index) const; void loadData(QFile* file) const; void writeData(QFile* file); //May contain zero items if they were deleted mutable QVector items; mutable QVector offsets; QVector temporaryItems; TopDUContextDynamicData* const data; }; DUChainItemStorage m_contexts; DUChainItemStorage m_declarations; DUChainItemStorage m_problems; //For temporary declarations that will not be stored to disk, like template instantiations mutable QList m_data; mutable QList m_topContextData; bool m_onDisk; mutable bool m_dataLoaded; mutable QFile* m_mappedFile; mutable uchar* m_mappedData; mutable size_t m_mappedDataSize; mutable bool m_itemRetrievalForbidden; }; } Q_DECLARE_TYPEINFO(KDevelop::TopDUContextDynamicData::ItemDataInfo, Q_PRIMITIVE_TYPE); #endif diff --git a/language/interfaces/ilanguagesupport.h b/language/interfaces/ilanguagesupport.h index 9ac7f36906..b59cbc8155 100644 --- a/language/interfaces/ilanguagesupport.h +++ b/language/interfaces/ilanguagesupport.h @@ -1,172 +1,172 @@ /*************************************************************************** * Copyright 2007 Alexander Dymo * * Copyright 2014 Kevin Funk * * * * 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_ILANGUAGESUPPORT_H #define KDEVPLATFORM_ILANGUAGESUPPORT_H #include #include #include #include "interfaces/isourceformatter.h" class QReadWriteLock; namespace KTextEditor { class Cursor; class Range; class Document; } namespace KDevelop { class BasicRefactoring; class IndexedString; class ParseJob; class ILanguageSupportPrivate; class TopDUContext; class ICodeHighlighting; class ICreateClassHelper; class KDEVPLATFORMLANGUAGE_EXPORT ILanguageSupport { public: ILanguageSupport(); virtual ~ILanguageSupport(); /** @return the name of the language.*/ virtual QString name() const = 0; /** @return the parse job that is used by background parser to parse given @p url.*/ virtual ParseJob *createParseJob(const IndexedString &url) = 0; /** - * Only important for languages that can parse multiple different versions of a file, like C++ due to the preprocessor. + * Only important for languages that can parse multiple different versions of a file, like C++ due to the preprocessor. * The default-implementation for other languages is "return DUChain::chainForDocument(url);" * * @param proxyContext Whether the returned context should be a proxy-contexts. In C++, a proxy-contexts has no direct content. * It mainly just imports an actual content-context, and it holds all the imports. It can also represent * multiple different versions of the same content in the eyes of the preprocessor. Also, a proxy-context may contain the problem- * descriptions of preprocessor problems. * The proxy-context should be preferred whenever the problem-list is required, or for deciding whether a document needs to be updated * (only the proxy-context knows about all the dependencies, since it contains most of the imports) * * @warning The DUChain must be locked before calling this, @see KDevelop::DUChainReadLocker * - * @return the standard context used by this language for the given @param url. - * */ + * @return the standard context used by this language for the given @p url + **/ virtual TopDUContext *standardContext(const QUrl& url, bool proxyContext = false); /** * Should return a code-highlighting instance for this language, or zero. */ virtual ICodeHighlighting* codeHighlighting() const; /** * Should return a BasicRefactoring instance that controls the language-agnostic refactoring rules, or zero */ virtual BasicRefactoring* refactoring() const; /** * Should return a class creating helper for this language, or zero. * * If zero is returned, a default class helper will be created. * Reimplementing this method is therefore not necessary to have classes created in this language. * */ virtual ICreateClassHelper* createClassHelper() const; /** * Every thread that does background-parsing should read-lock its language's parse-mutex while parsing. * Any other thread may write-lock the parse-mutex in order to wait for all parsing-threads to finish the parsing. * The parse-mutex only needs to be locked while working on the du-chain, not while preprocessing or reading. * Tip: use QReadLocker for read-locking. * The duchain must always be unlocked when you try to lock a parseLock! */ virtual QReadWriteLock* parseLock() const; /** * The following functions are used to allow navigation-features, tooltips, etc. for non-duchain language objects. * In C++, they are used to allow highlighting and navigation of macro-uses. * */ /**Should return the local range within the given url that belongs to the - *special language-object that contains @param position, or (QUrl(), KTextEditor::Range:invalid()) */ + *special language-object that contains @param position , or (QUrl(), KTextEditor::Range:invalid()) */ virtual KTextEditor::Range specialLanguageObjectRange(const QUrl& url, const KTextEditor::Cursor& position); /**Should return the source-range and source-document that the *special language-object that contains @param position refers to, or KTextEditor::Range:invalid(). */ virtual QPair specialLanguageObjectJumpCursor(const QUrl& url, const KTextEditor::Cursor& position); /**Should return a navigation-widget for the *special language-object that contains @param position refers, or 0. *If you setProperty("DoNotCloseOnCursorMove", true) on the widget returned, *then the widget will not close when the cursor moves in the document, which *enables you to change the document contents from the widget without immediately closing the widget.*/ virtual QWidget* specialLanguageObjectNavigationWidget(const QUrl& url, const KTextEditor::Cursor& position); /**Should return a tiny piece of code which makes it possible for KDevelop to derive the indentation *settings from an automatic source formatter. Example for C++: "class C{\n class D {\n void c() {\n int m;\n }\n }\n};\n" *The sample must be completely unindented (no line must start with leading whitespace), *and it must contain at least 4 indentation levels! *The default implementation returns an empty string.*/ virtual QString indentationSample() const; /** * Can return a list of source formatting items for this language. * For example, if your language wants to use the CustomScript engine with * a specific executable, return an item with "customscript" as the engine * and a style describing your options as the style (in this case, especially * the command to execute in the "content" member). * Multiple items can be returned. Make sure to set the mime type(s) of your language * on the returned items. */ virtual SourceFormatterItemList sourceFormatterItems() const; enum ReparseDelaySpecialValues { DefaultDelay = -1, NoUpdateRequired = -2 }; /** * @brief Enables the language to control how long the background parser waits until a changed document is reparsed. * * You can return DefaultDelay to use the default delay, or NoUpdateRequired to indicate that * this change does not require a re-parse at all. * * The default implementation returns DefaultDelay if the change was not whitespace-only, * and NoUpdateRequired otherwise. * * @param doc the document which was modified * @param changedRange the range which was modified * @param changedText the text which was inserted or removed * @param removal whether text was removed or inserted * @return int duration in ms to wait until re-parsing or a value of the ReparseDelaySpecialValues enum. */ virtual int suggestedReparseDelayForChange(KTextEditor::Document* doc, const KTextEditor::Range& changedRange, const QString& changedText, bool removal) const; private: QScopedPointer const d; }; } Q_DECLARE_INTERFACE( KDevelop::ILanguageSupport, "org.kdevelop.ILanguageSupport") #endif diff --git a/language/interfaces/iquickopen.h b/language/interfaces/iquickopen.h index 855a8ec9a4..3a04fb9d41 100644 --- a/language/interfaces/iquickopen.h +++ b/language/interfaces/iquickopen.h @@ -1,109 +1,109 @@ /* This file is part of the KDE project Copyright 2006 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 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_IQUICKOPEN_H #define KDEVPLATFORM_IQUICKOPEN_H #include #include #include class QStringList; namespace KDevelop { class QuickOpenDataProviderBase; class IndexedString; class KDEVPLATFORMLANGUAGE_EXPORT IQuickOpenLine : public QLineEdit { Q_OBJECT public: /** * Returns a non-zero declaration if it has been explicitly selected * and executed through the quickopen line * \code IndexedDeclaration selectedDeclaration() = 0; \endcode * * Returns a non-empty string if the file has been explicitly selected * and executed through the quickopen line * \code IndexedString selectedFile() = 0; \endcode */ virtual void setDefaultText(const QString& text) = 0; Q_SIGNALS: void itemSelected(); }; /** * Interface to quickopen */ class KDEVPLATFORMLANGUAGE_EXPORT IQuickOpen { public: virtual ~IQuickOpen(); /** * Shows the quickopen dialog with the entries of specified types * Default types are: Files, Functions, Classes * There might be other quick open providers with custom items. * Note, the item name has to be translated, for example i18n("Files") should be passed. */ virtual void showQuickOpen( const QStringList &types ) = 0; /** * Registers a new provider under a specified name. * There may be multiple providers with the same type/scope, they will be used simultaneously in that case. * type and scope will be shown in the GUI, so they should be translated. - * @param scope Different scopes supported by this data-provider, Examples: "Project", "Imports", etc. + * @param scopes Different scopes supported by this data-provider, Examples: "Project", "Imports", etc. * @param type Types of the provided data, Examples: "Files", "Functions", "Classes", etc. * @param provider The provider. It does not need to be explicitly removed before its destruction. */ virtual void registerProvider( const QStringList& scopes, const QStringList& type, QuickOpenDataProviderBase* provider ) = 0; /** * Remove provider. * @param provider The provider to remove * @return Whether a provider was removed. If false, the provider was not attached. */ virtual bool removeProvider( QuickOpenDataProviderBase* provider ) = 0; /** * Queries a set of files merged from all active data-providers that implement QuickOpenFileSetInterface. * This should not be queried by data-providers that implement QuickOpenFileSetInterface during their * initialization(set() and enableData()) */ virtual QSet fileSet() const = 0; enum QuickOpenType { Standard, Outline }; virtual IQuickOpenLine* createQuickOpenLine(const QStringList& scopes, const QStringList& types, QuickOpenType type = Standard) = 0; }; } Q_DECLARE_INTERFACE(KDevelop::IQuickOpen, "org.kdevelop.IQuickOpen") #endif // KDEVPLATFORM_IQUICKOPEN_H diff --git a/outputview/ioutputview.h b/outputview/ioutputview.h index 6e937f8d70..1bd346f344 100644 --- a/outputview/ioutputview.h +++ b/outputview/ioutputview.h @@ -1,184 +1,184 @@ /* KDevelop Output View * * Copyright 2006-2007 Andreas Pakulat * * 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) 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_IOUTPUTVIEW_H #define KDEVPLATFORM_IOUTPUTVIEW_H #include #include #include #include "outputviewexport.h" class QString; class QAbstractItemModel; class QModelIndex; class QAbstractItemDelegate; class QAction; /** @author Andreas Pakulat */ namespace KDevelop { class KDEVPLATFORMOUTPUTVIEW_EXPORT IOutputView { public: enum Behaviour { AllowUserClose = 0x1 /**< allow the user to close the view */, AlwaysShowView = 0x2 /**< always show the view */, AutoScroll = 0x4 /**< automatically scroll the view */ }; Q_DECLARE_FLAGS(Behaviours, Behaviour) enum Option { NoOptions = 0x0, ShowItemsButton = 0x1 /**< show the two buttons (select and focus) */, AddFilterAction = 0x2 /**< add a filter action */ }; Q_DECLARE_FLAGS(Options, Option) enum ViewType { OneView = 0 /**< there's only one outputview, newly registered outputs will replace existing ones */, HistoryView = 1 /**< The toolview will have a history with forward/backward buttons */, MultipleView = 2 /**< show multiples outputs in a toolview at the same time */ }; enum StandardToolView { BuildView = 0 /**< the standard outputview for building output */, RunView = 1 /**< the standard outputview for running apps */, DebugView = 2 /**< the standard outputview for debugging apps */, TestView = 4 /**< the standard outputview for verbose test output */, VcsView = 8 /**< the standard outputview for VCS commands */ }; virtual ~IOutputView(); /** * fetch the identifier for one of the standard toolviews * This will automatically create the toolview if it doesn't exist yet * @param view the standard toolview to get the identifier for * @returns the identifier for the standard toolview */ virtual int standardToolView( StandardToolView view ) = 0; /** * Register a new toolview for output with the given title, behaviour and type * If there already exists a toolview with this title and type return the existing id * @param title the Title to be displayed on the toolview * @param type the type of view that should be created * @param icon the icon of the toolview * @param option the options of the toolview * @param actionList list of actions adding to the toolbar * @returns an toolview id that identifies the new view and is used in the other * methods */ virtual int registerToolView( const QString& title, ViewType type = OneView, const QIcon& icon = QIcon(), Options option = ShowItemsButton, const QList& actionList = QList()) = 0; /** * Register a new output view in a given toolview. How this new view is created depends * on the type of the toolview. * @param toolviewId the id of the toolview, created by registerToolView * @param title the title to use for the new output in the toolview * @param behaviour the Behaviour of the output * @returns the id of the output to supply to the other methods */ virtual int registerOutputInToolView( int toolviewId, const QString& title, Behaviours behaviour = AllowUserClose ) = 0; /** * Raise a given view */ virtual void raiseOutput( int outputId ) = 0; virtual void scrollOutputTo( int outputId, const QModelIndex& ) = 0; /** * Sets the model of the registered output identified by @p outputId to @p model. * * Does nothing if the id doesn't exist. The output view takes ownership of the model. * * NOTE: Do not reuse the same model for different views. */ virtual void setModel( int outputId, QAbstractItemModel* model ) = 0; /** * Sets the item delegate of the registered output identified by @p outputId to @p delegate. * * Does nothing if the id doesn't exist. The output view takes ownership of the delegate. * * NOTE: Do not reuse the same delegate for different views. */ virtual void setDelegate( int outputId, QAbstractItemDelegate* model ) = 0; /** * Sets a @p title for the specified @p outputIds */ virtual void setTitle( int outputId, const QString& title ) = 0; /** * remove a toolview, don't forget to emit toolViewRemoved when you implement this * - * @param id identifies the view to remove + * @param toolviewId identifies the view to remove */ virtual void removeToolView( int toolviewId ) = 0; /** * remove an output view from a toolview. Don't forget to emit outputRemoved * when you implement this. * @param outputId the id of the outputview to remove */ virtual void removeOutput( int outputId ) = 0; Q_SIGNALS: /** * emitted after a toolview was removed * * @param toolviewId identifies the removed toolview */ void toolViewRemoved( int toolviewId ); /** * emitted after a toolview was removed * * @param toolviewId identifies the removed toolview * @param outputId identifies the removed output */ void outputRemoved( int toolviewId, int outputId ); }; } // namespace KDevelop Q_DECLARE_OPERATORS_FOR_FLAGS(KDevelop::IOutputView::Behaviours) Q_DECLARE_OPERATORS_FOR_FLAGS(KDevelop::IOutputView::Options) Q_DECLARE_METATYPE(KDevelop::IOutputView::StandardToolView) Q_DECLARE_INTERFACE( KDevelop::IOutputView, "org.kdevelop.IOutputView" ) #endif diff --git a/plugins/contextbrowser/browsemanager.h b/plugins/contextbrowser/browsemanager.h index 2ccf313126..072d97a7d3 100644 --- a/plugins/contextbrowser/browsemanager.h +++ b/plugins/contextbrowser/browsemanager.h @@ -1,126 +1,125 @@ /* * 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 + ///Installs/uninstalls the event-filter void applyEventFilter(QWidget* object, bool install); Q_SIGNALS: - //Emitted when browsing was started using the magic-modifier + ///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/externalscript/externalscriptplugin.h b/plugins/externalscript/externalscriptplugin.h index 645a9c8b64..ed2a9856fd 100644 --- a/plugins/externalscript/externalscriptplugin.h +++ b/plugins/externalscript/externalscriptplugin.h @@ -1,104 +1,104 @@ /* This plugin is part of KDevelop. Copyright (C) 2010 Milian Wolff This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef KDEVPLATFORM_PLUGIN_EXTERNALSCRIPTPLUGIN_H #define KDEVPLATFORM_PLUGIN_EXTERNALSCRIPTPLUGIN_H #include #include #include #include #include class ExternalScriptItem; class QStandardItem; class QStandardItemModel; class ExternalScriptPlugin : public KDevelop::IPlugin { Q_OBJECT Q_CLASSINFO( "D-Bus Interface", "org.kdevelop.ExternalScriptPlugin" ) public: explicit ExternalScriptPlugin( QObject *parent, const QVariantList &args = QVariantList() ); ~ExternalScriptPlugin() override; void unload() override; KDevelop::ContextMenuExtension contextMenuExtension(KDevelop::Context* context) override; static ExternalScriptPlugin* self(); /** * @return The model storing all external scripts managed by this plugin. * @NOTE: always append() items, never insert in the middle! */ QStandardItemModel* model() const; /** * Executes @p script. */ void execute(ExternalScriptItem* item, const QUrl &url) const; /** * Executes @p script. */ void execute(ExternalScriptItem* item) const; /** * Returns config group to store all settings for this plugin in. */ KConfigGroup getConfig() const; void saveItem(const ExternalScriptItem* item); public slots: void executeScriptFromActionData() const; /** * Executes the command (Used by the shell-integration) * */ Q_SCRIPTABLE bool executeCommand(QString command, QString workingDirectory) const; /** * Executes the command synchronously and returns the output text (Used by the shell-integration) * */ Q_SCRIPTABLE QString executeCommandSync(QString command, QString workingDirectory) const; private slots: void rowsRemoved( const QModelIndex& parent, int start, int end ); void rowsInserted( const QModelIndex& parent, int start, int end ); void executeScriptFromContextMenu() const; private: - /// @param item row in the model for the item to save + /// @param row row in the model for the item to save void saveItemForRow( int row ); QStandardItemModel* m_model; QList m_urls; static ExternalScriptPlugin* m_self; class ExternalScriptViewFactory *m_factory; }; #endif // KDEVPLATFORM_PLUGIN_EXTERNALSCRIPTPLUGIN_H // kate: indent-mode cstyle; space-indent on; indent-width 2; replace-tabs on; diff --git a/plugins/filetemplates/templateoptionspage.h b/plugins/filetemplates/templateoptionspage.h index 898e1b88ca..5eb7298499 100644 --- a/plugins/filetemplates/templateoptionspage.h +++ b/plugins/filetemplates/templateoptionspage.h @@ -1,83 +1,84 @@ /* This file is part of KDevelop Copyright 2012 Miha Čančula 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_PLUGIN_TEMPLATEOPTIONSPAGE_H #define KDEVPLATFORM_PLUGIN_TEMPLATEOPTIONSPAGE_H #include #include namespace KDevelop { class TemplateRenderer; class SourceFileTemplate; class TemplateClassAssistant; /** * @brief Shows a page for configuring options specified by a class template * * Templates can include a file that specify configuration options. * These can be set by the user before creating the class and are passed to the template. * * @sa SourceFileTemplate::customOptions() **/ class TemplateOptionsPage : public QWidget { Q_OBJECT Q_PROPERTY(QVariantHash templateOptions READ templateOptions) public: /** * @brief Create a new template options page * * @param parent the parent template class assistant * @param f window flags, passed to QWidget **/ explicit TemplateOptionsPage(QWidget* parent, Qt::WindowFlags f = nullptr); /** * Destructor **/ ~TemplateOptionsPage() override; /** - * Parses the XML-formatted .kcfg file contents and creates the UI for setting template options. + * Parses template archive file and creates the UI for setting template options. * - * @param contents the file contents + * @param fileTemplate The template archive file + * @param renderer A Grantlee wrapper used to render all the templates **/ void load(const SourceFileTemplate& fileTemplate, TemplateRenderer* renderer); /** * @property templateOptions * * The user-set options. Keys are the options' names, and values are their values. * **/ QVariantHash templateOptions() const; private: class TemplateOptionsPagePrivate* const d; }; } #endif // KDEVPLATFORM_PLUGIN_TEMPLATEOPTIONSPAGE_H diff --git a/plugins/grepview/grepviewplugin.cpp b/plugins/grepview/grepviewplugin.cpp index ff84cdd2f1..a0837a9282 100644 --- a/plugins/grepview/grepviewplugin.cpp +++ b/plugins/grepview/grepviewplugin.cpp @@ -1,232 +1,232 @@ /*************************************************************************** * Copyright 1999-2001 by Bernd Gehrmann * * bernd@kdevelop.org * * Copyright 2007 Dukju Ahn * * Copyright 2010 Benjamin Port * * Copyright 2010 Julien Desgats * * 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) any later version. * * * ***************************************************************************/ #include "grepviewplugin.h" #include "grepdialog.h" #include "grepoutputmodel.h" #include "grepoutputdelegate.h" #include "grepjob.h" #include "grepoutputview.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 Q_LOGGING_CATEGORY(PLUGIN_GREPVIEW, "kdevplatform.plugins.grepview") static QString patternFromSelection(const KDevelop::IDocument* doc) { if (!doc) return QString(); QString pattern; KTextEditor::Range range = doc->textSelection(); if( range.isValid() ) { pattern = doc->textDocument()->text( range ); } if( pattern.isEmpty() ) { pattern = doc->textWord(); } // Before anything, this removes line feeds from the // beginning and the end. int len = pattern.length(); if (len > 0 && pattern[0] == '\n') { pattern.remove(0, 1); len--; } if (len > 0 && pattern[len-1] == '\n') pattern.truncate(len-1); return pattern; } GrepViewPlugin::GrepViewPlugin( QObject *parent, const QVariantList & ) : KDevelop::IPlugin( QStringLiteral("kdevgrepview"), parent ), m_currentJob(0) { setXMLFile(QStringLiteral("kdevgrepview.rc")); QDBusConnection::sessionBus().registerObject( QStringLiteral("/org/kdevelop/GrepViewPlugin"), this, QDBusConnection::ExportScriptableSlots ); QAction*action = actionCollection()->addAction(QStringLiteral("edit_grep")); action->setText(i18n("Find/Replace in Fi&les...")); actionCollection()->setDefaultShortcut( action, QKeySequence(QStringLiteral("Ctrl+Alt+F")) ); connect(action, &QAction::triggered, this, &GrepViewPlugin::showDialogFromMenu); action->setToolTip( i18n("Search for expressions over several files") ); action->setWhatsThis( i18n("Opens the 'Find/Replace in files' dialog. There you " "can enter a regular expression which is then " "searched for within all files in the directories " "you specify. Matches will be displayed, you " "can switch to a match directly. You can also do replacement.") ); action->setIcon(QIcon::fromTheme(QStringLiteral("edit-find"))); // instantiate delegate, it's supposed to be deleted via QObject inheritance new GrepOutputDelegate(this); m_factory = new GrepOutputViewFactory(this); core()->uiController()->addToolView(i18n("Find/Replace in Files"), m_factory); } GrepOutputViewFactory* GrepViewPlugin::toolViewFactory() const { return m_factory; } GrepViewPlugin::~GrepViewPlugin() { } void GrepViewPlugin::unload() { core()->uiController()->removeToolView(m_factory); } -void GrepViewPlugin::startSearch(QString pattern, QString directory, bool showOptions) +void GrepViewPlugin::startSearch(QString pattern, QString directory, bool show) { m_directory = directory; - showDialog(false, pattern, showOptions); + showDialog(false, pattern, show); } KDevelop::ContextMenuExtension GrepViewPlugin::contextMenuExtension(KDevelop::Context* context) { KDevelop::ContextMenuExtension extension = KDevelop::IPlugin::contextMenuExtension(context); if( context->type() == KDevelop::Context::ProjectItemContext ) { KDevelop::ProjectItemContext* ctx = dynamic_cast( context ); QList items = ctx->items(); // verify if there is only one folder selected if ((items.count() == 1) && (items.first()->folder())) { QAction* action = new QAction( i18n( "Find/Replace in This Folder..." ), this ); action->setIcon(QIcon::fromTheme(QStringLiteral("edit-find"))); m_contextMenuDirectory = items.at(0)->folder()->path().toLocalFile(); connect( action, &QAction::triggered, this, &GrepViewPlugin::showDialogFromProject); extension.addAction( KDevelop::ContextMenuExtension::ExtensionGroup, action ); } } if ( context->type() == KDevelop::Context::EditorContext ) { KDevelop::EditorContext *econtext = dynamic_cast(context); if ( econtext->view()->selection() ) { QAction* action = new QAction(QIcon::fromTheme(QStringLiteral("edit-find")), i18n("&Find/Replace in Files..."), this); connect(action, &QAction::triggered, this, &GrepViewPlugin::showDialogFromMenu); extension.addAction(KDevelop::ContextMenuExtension::ExtensionGroup, action); } } if(context->type() == KDevelop::Context::FileContext) { KDevelop::FileContext *fcontext = dynamic_cast(context); // TODO: just stat() or QFileInfo().isDir() for local files? should be faster than mime type checking QMimeType mimetype = QMimeDatabase().mimeTypeForUrl(fcontext->urls().at(0)); static const QMimeType directoryMime = QMimeDatabase().mimeTypeForName(QStringLiteral("inode/directory")); if (mimetype == directoryMime) { QAction* action = new QAction( i18n( "Find/Replace in This Folder..." ), this ); action->setIcon(QIcon::fromTheme(QStringLiteral("edit-find"))); m_contextMenuDirectory = fcontext->urls().at(0).toLocalFile(); connect( action, &QAction::triggered, this, &GrepViewPlugin::showDialogFromProject); extension.addAction( KDevelop::ContextMenuExtension::ExtensionGroup, action ); } } return extension; } -void GrepViewPlugin::showDialog(bool setLastUsed, QString pattern, bool showOptions) +void GrepViewPlugin::showDialog(bool setLastUsed, QString pattern, bool show) { GrepDialog* dlg = new GrepDialog( this, core()->uiController()->activeMainWindow() ); KDevelop::IDocument* doc = core()->documentController()->activeDocument(); if(!pattern.isEmpty()) { dlg->setPattern(pattern); } else if(!setLastUsed) { QString pattern = patternFromSelection(doc); if (!pattern.isEmpty()) { dlg->setPattern( pattern ); } } //if directory is empty then use a default value from the config file. if (!m_directory.isEmpty()) { dlg->setSearchLocations(m_directory); } - if(showOptions) + if(show) dlg->show(); else{ dlg->startSearch(); dlg->deleteLater(); } } void GrepViewPlugin::showDialogFromMenu() { showDialog(); } void GrepViewPlugin::showDialogFromProject() { rememberSearchDirectory(m_contextMenuDirectory); showDialog(); } void GrepViewPlugin::rememberSearchDirectory(QString const & directory) { m_directory = directory; } GrepJob* GrepViewPlugin::newGrepJob() { if(m_currentJob != 0) { m_currentJob->kill(); } m_currentJob = new GrepJob(); connect(m_currentJob, &GrepJob::finished, this, &GrepViewPlugin::jobFinished); return m_currentJob; } GrepJob* GrepViewPlugin::grepJob() { return m_currentJob; } void GrepViewPlugin::jobFinished(KJob* job) { if(job == m_currentJob) { emit grepJobFinished(); m_currentJob = 0; } } diff --git a/plugins/grepview/grepviewplugin.h b/plugins/grepview/grepviewplugin.h index 61ae56a491..d4d34e4f64 100644 --- a/plugins/grepview/grepviewplugin.h +++ b/plugins/grepview/grepviewplugin.h @@ -1,67 +1,67 @@ /*************************************************************************** * Copyright 1999-2001 by Bernd Gehrmann * * bernd@kdevelop.org * * Copyright 2010 Julien Desgats * * * * 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) any later version. * * * ***************************************************************************/ #ifndef GREPVIEWPART_H_ #define GREPVIEWPART_H_ #include #include #include class KJob; class GrepJob; class GrepOutputViewFactory; class GrepViewPlugin : public KDevelop::IPlugin { Q_OBJECT public: explicit GrepViewPlugin( QObject *parent, const QVariantList & = QVariantList() ); ~GrepViewPlugin() override; void unload() override; void rememberSearchDirectory(QString const & directory); KDevelop::ContextMenuExtension contextMenuExtension(KDevelop::Context* context) override; - void showDialog(bool setLastUsed = false, QString pattern = QString(), bool showOptions = true); - + void showDialog(bool setLastUsed = false, QString pattern = QString(), bool show = true); + /** * Returns a new instance of GrepJob. Since the plugin supports only one job at the same time, * previous job, if any, is killed before creating a new job. */ GrepJob *newGrepJob(); GrepJob *grepJob(); GrepOutputViewFactory* toolViewFactory() const; public Q_SLOTS: ///@param pattern the pattern to search ///@param directory the directory, or a semicolon-separated list of files - ///@param showDIalog whether the search dialog should be shown. if false, - /// the parameters of the last search will be used. - Q_SCRIPTABLE void startSearch(QString pattern, QString directory, bool showOptions); + ///@param show whether the search dialog should be shown. if false, + /// the parameters of the last search will be used. + Q_SCRIPTABLE void startSearch(QString pattern, QString directory, bool show); Q_SIGNALS: Q_SIGNAL void grepJobFinished(); private Q_SLOTS: void showDialogFromMenu(); void showDialogFromProject(); void jobFinished(KJob *job); private: GrepJob *m_currentJob; QString m_directory; QString m_contextMenuDirectory; GrepOutputViewFactory* m_factory; }; #endif diff --git a/plugins/projectmanagerview/projectmodelitemdelegate.cpp b/plugins/projectmanagerview/projectmodelitemdelegate.cpp index 84a923e9f1..094178423a 100644 --- a/plugins/projectmanagerview/projectmodelitemdelegate.cpp +++ b/plugins/projectmanagerview/projectmodelitemdelegate.cpp @@ -1,160 +1,160 @@ /* This file is part of KDevelop Copyright 2013 Aleix Pol 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. */ #include "projectmodelitemdelegate.h" #include "vcsoverlayproxymodel.h" #include ProjectModelItemDelegate::ProjectModelItemDelegate(QObject* parent) : QItemDelegate(parent) {} static QIcon::Mode IconMode( QStyle::State state ) { if (!(state & QStyle::State_Enabled)) { return QIcon::Disabled; } else if (state & QStyle::State_Selected) { return QIcon::Selected; } else { return QIcon::Normal; } } static QIcon::State IconState(QStyle::State state) { return (state & QStyle::State_Open) ? QIcon::On : QIcon::Off; } void ProjectModelItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& opt, const QModelIndex& index) const { // Qt5.5 HiDPI Fix part (1/2) // This fix is based on how Qt5.5's QItemDelegate::paint implementation deals with the same issue // Unfortunately, there doesn't seem to be a clean way to use the base implementation // and have the added functionality this class provides QPixmap decoData; QRect decorationRect; QIcon icon; - QIcon::Mode mode; - QIcon::State state; + QIcon::Mode mode = QIcon::Mode::Disabled; + QIcon::State state = QIcon::State::Off; { QVariant value; value = index.data(Qt::DecorationRole); if (value.isValid()) { decoData = decoration(opt, value); if (value.type() == QVariant::Icon) { icon = qvariant_cast(value); mode = IconMode(opt.state); state = IconState(opt.state); QSize size = icon.actualSize( opt.decorationSize, mode, state ); decorationRect = QRect(QPoint(0, 0), size); } else { decorationRect = QRect(QPoint(0, 0), decoData.size()); } } else { decorationRect = QRect(); } } QRect checkRect; //unused in practice QRect spaceLeft = opt.rect; spaceLeft.setLeft(decorationRect.right()); QString displayData = index.data(Qt::DisplayRole).toString(); QRect displayRect = textRectangle(painter, spaceLeft, opt.font, displayData); displayRect.setLeft(spaceLeft.left()); QRect branchNameRect(displayRect.topRight(), opt.rect.bottomRight()); doLayout(opt, &checkRect, &decorationRect, &displayRect, false); branchNameRect.setLeft(branchNameRect.left() + displayRect.left()); branchNameRect.setTop(displayRect.top()); drawStyledBackground(painter, opt); // drawCheck(painter, opt, checkRect, checkState); // Qt5.5 HiDPI Fix part (2/2) // use the QIcon from above if possible if (!icon.isNull()) { icon.paint(painter, decorationRect, opt.decorationAlignment, mode, state ); } else { drawDecoration(painter, opt, decorationRect, decoData); } drawDisplay(painter, opt, displayRect, displayData); /// FIXME: this can apparently trigger a nested eventloop, see /// https://bugs.kde.org/show_bug.cgi?id=355099 QString branchNameData = index.data(VcsOverlayProxyModel::VcsStatusRole).toString(); drawBranchName(painter, opt, branchNameRect, branchNameData); drawFocus(painter, opt, displayRect); } void ProjectModelItemDelegate::drawBranchName(QPainter* painter, const QStyleOptionViewItem& option, const QRect& rect, const QString& branchName) const { QString text = option.fontMetrics.elidedText(branchName, Qt::ElideRight, rect.width()); bool selected = option.state & QStyle::State_Selected; QPalette::ColorGroup colorGroup = selected ? QPalette::Active : QPalette::Disabled; painter->save(); painter->setPen(option.palette.color(colorGroup, QPalette::Text)); painter->drawText(rect, text); painter->restore(); } void ProjectModelItemDelegate::drawStyledBackground(QPainter* painter, const QStyleOptionViewItem& option) const { QStyleOptionViewItemV4 opt(option); QStyle *style = opt.widget->style(); style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, opt.widget); } void ProjectModelItemDelegate::drawDisplay(QPainter* painter, const QStyleOptionViewItem& option, const QRect& rect, const QString& text) const { QPalette::ColorGroup cg = option.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled; if (option.state & QStyle::State_Editing) { painter->save(); painter->setPen(option.palette.color(cg, QPalette::Text)); painter->drawRect(rect.adjusted(0, 0, -1, -1)); painter->restore(); } if(text.isEmpty()) { return; } if (cg == QPalette::Normal && !(option.state & QStyle::State_Active)) { cg = QPalette::Inactive; } if (option.state & QStyle::State_Selected) { painter->setPen(option.palette.color(cg, QPalette::HighlightedText)); } else { painter->setPen(option.palette.color(cg, QPalette::Text)); } QFontMetrics fm(painter->fontMetrics()); painter->drawText(rect, fm.elidedText(text, Qt::ElideRight, rect.width())); } diff --git a/plugins/welcomepage/qml/area_code.qml b/plugins/welcomepage/qml/area_code.qml index 32c6bd0ba3..f48b2bde65 100644 --- a/plugins/welcomepage/qml/area_code.qml +++ b/plugins/welcomepage/qml/area_code.qml @@ -1,110 +1,105 @@ /* KDevelop * * Copyright 2011 Aleix Pol * * 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) 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. */ import QtQuick 2.0 import QtQuick.Controls 1.3 import QtQuick.Layouts 1.2 StandardBackground { id: root state: "develop" tools: ColumnLayout { spacing: 10 RowLayout { Layout.fillWidth: true Layout.preferredHeight: parent.width/4 Layout.maximumHeight: parent.width/4 Image { id: icon Layout.fillHeight: true Layout.alignment: Qt.AlignHCenter horizontalAlignment: Image.AlignHCenter verticalAlignment: Image.AlignVCenter - sourceSize { - width: icon.height - height: icon.height - } - source: "image://icon/kdevelop" smooth: true fillMode: Image.PreserveAspectFit } Label { Layout.fillWidth: true Layout.fillHeight: true verticalAlignment: Text.AlignVCenter text: "KDevelop" fontSizeMode: Text.Fit font { pointSize: icon.height/3 weight: Font.ExtraLight } } } Item { Layout.fillWidth: true Layout.fillHeight: true } ColumnLayout { Layout.fillWidth: true Heading { Layout.fillWidth: true text: i18n("Need Help?") } Link { text: i18n("KDevelop.org") iconName: "applications-webbrowsers" onClicked: { Qt.openUrlExternally("https://kdevelop.org") } } Link { text: i18n("Learn about KDevelop") iconName: "applications-webbrowsers" onClicked: Qt.openUrlExternally("https://userbase.kde.org/KDevelop") } Link { text: i18n("Join KDevelop's team!") iconName: "applications-webbrowsers" onClicked: Qt.openUrlExternally("https://kdevelop.org/contribute-kdevelop") } Link { text: i18n("Handbook") iconName: "applications-webbrowsers" onClicked: kdev.retrieveMenuAction("help/help_contents").trigger() } } } Develop { anchors { fill: parent leftMargin: root.marginLeft+root.margins } } } diff --git a/project/interfaces/iprojectfilemanager.h b/project/interfaces/iprojectfilemanager.h index 256c0b2a9d..d6590b2139 100644 --- a/project/interfaces/iprojectfilemanager.h +++ b/project/interfaces/iprojectfilemanager.h @@ -1,180 +1,180 @@ /* This file is part of KDevelop Copyright 2004 Roberto Raggi Copyright 2006 Matt Rogers Copyright 2006 Hamish Rodda Copyright 2007 Andreas Pakulat 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_IPROJECTFILEMANAGER_H #define KDEVPLATFORM_IPROJECTFILEMANAGER_H #include #include #include #include class KJob; namespace KDevelop { class IProject; class ProjectBaseItem; class ProjectFolderItem; class ProjectFileItem; /** * @short An interface to project file management * * FileManager is the class you want to implement for integrating * a project manager in KDevelop. For build systems, implement its * child class, BuildManager. * * These classes \e do \e not cause files, folders etc. to be created * or removed on disk. They simply read from and write to the file(s) * which describe the structure (eg. CMakeLists.txt for cmake, Makefile.am for automake, etc). * * @author Roberto Raggi, Matt Rogers, Hamish Rodda, Milian Wolff */ class KDEVPLATFORMPROJECT_EXPORT IProjectFileManager { public: virtual ~IProjectFileManager(); /** Features the file manager supports */ enum Feature { None = 0 , ///< This manager supports nothing Folders = 1 << 0, ///< Folders are supported by the manager Targets = 1 << 1, ///< Targets are supported by the manager Files = 1 << 2 ///< Files are supported by the manager }; Q_DECLARE_FLAGS( Features, Feature ) /** * @return the Features supported by the filemanager */ virtual Features features() const = 0; /** * This method initialize the model item @arg dom * @return The list of the sub folders */ virtual QList parse(ProjectFolderItem *dom) = 0; /** * This method creates the root item from the file @arg fileName * @return The created item */ virtual ProjectFolderItem *import(IProject *project) = 0; /** - * This method creates an import job for the given @arg item + * @brief This method creates an import job for the given @p item * - * The default implementation should be suitable for most needs, - * it'll create an instance of @class ImportProjectJob + * @details The default implementation should be suitable for most needs, + * it'll create an instance of class @ref ImportProjectJob * * @return a job that imports the project */ virtual KJob* createImportJob(ProjectFolderItem* item); /** * Add a folder to the project and create it on disk. * * Adds the folder specified by @p folder to @p parent and modifies the * underlying build system if needed */ virtual ProjectFolderItem* addFolder(const Path& folder, ProjectFolderItem* parent) = 0; /** * Add a file to a folder and create it on disk. * * Adds the file specified by @p file to the folder @p parent and modifies * the underlying build system if needed. The file is not added to a target */ virtual ProjectFileItem* addFile(const Path& file, ProjectFolderItem *parent) = 0; /** * Remove files or folders from the project and delete them from disk * * Removes the files or folders specified by @p items and * modifies the underlying build system if needed. * * Note: Do not attempt to remove subitems along with their parents */ virtual bool removeFilesAndFolders(const QList &items) = 0; /** * Move files and folders within a given project * * Moves the files or folders specified by @p items to @p newParent and * modifies the underlying build system as needed * * Note: Do not attempt to move subitems along with their parents */ virtual bool moveFilesAndFolders(const QList< KDevelop::ProjectBaseItem* > &items, KDevelop::ProjectFolderItem* newParent) = 0; /** * Copy files and folders within a given project * * Copies the files or folders specified by @p items to @p newParent and * modifies the underlying build system as needed * * Note: Do not attempt to copy subitems along with their parents */ virtual bool copyFilesAndFolders(const Path::List &items, KDevelop::ProjectFolderItem* newParent) = 0; /** * Rename a file in the project * * Renames the file specified by @p oldFile to @p newPath */ virtual bool renameFile(ProjectFileItem* file, const Path& newPath) = 0; /** * Rename a folder in the project * * Renames the folder specified by @p oldFile to @p newPath */ virtual bool renameFolder(ProjectFolderItem* oldFolder, const Path& newPath) = 0; /** * Reload an item in the project * * Reloads the item specified by @p item */ virtual bool reload(ProjectFolderItem* item) = 0; Q_SIGNALS: void folderAdded(KDevelop::ProjectFolderItem* folder); void folderRemoved(KDevelop::ProjectFolderItem* folder); void folderRenamed(const KDevelop::Path& oldFolder, KDevelop::ProjectFolderItem* newFolder); void fileAdded(KDevelop::ProjectFileItem* file); void fileRemoved(KDevelop::ProjectFileItem* file); void fileRenamed(const KDevelop::Path& oldFile, KDevelop::ProjectFileItem* newFile); }; } Q_DECLARE_OPERATORS_FOR_FLAGS( KDevelop::IProjectFileManager::Features ) Q_DECLARE_INTERFACE( KDevelop::IProjectFileManager, "org.kdevelop.IProjectFileManager") #endif diff --git a/serialization/itemrepository.h b/serialization/itemrepository.h index 821046705f..a557da690f 100644 --- a/serialization/itemrepository.h +++ b/serialization/itemrepository.h @@ -1,2253 +1,2252 @@ /* Copyright 2008 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDEVPLATFORM_ITEMREPOSITORY_H #define KDEVPLATFORM_ITEMREPOSITORY_H #include #include #include #include #include #include "referencecounting.h" #include "abstractitemrepository.h" #include "repositorymanager.h" #include "itemrepositoryregistry.h" //#define DEBUG_MONSTERBUCKETS // #define DEBUG_ITEMREPOSITORY_LOADING // #define ifDebugInfiniteRecursion(x) x #define ifDebugInfiniteRecursion(x) // #define ifDebugLostSpace(x) x #define ifDebugLostSpace(x) // #define DEBUG_INCORRECT_DELETE //Makes sure that all items stay reachable through the basic hash // #define DEBUG_ITEM_REACHABILITY ///@todo Dynamic bucket hash size #ifdef DEBUG_ITEM_REACHABILITY #define ENSURE_REACHABLE(bucket) Q_ASSERT(allItemsReachable(bucket)); #define IF_ENSURE_REACHABLE(x) x #else #define ENSURE_REACHABLE(bucket) #define IF_ENSURE_REACHABLE(x) #endif #define ITEMREPOSITORY_USE_MMAP_LOADING //Assertion macro that prevents warnings if debugging is disabled //Only use it to verify values, it should not call any functions, since else the function will even be called in release mode #ifdef QT_NO_DEBUG #define VERIFY(X) if(!(X)) {qWarning() << "Failed to verify expression" << #X;} #else #define VERIFY(X) Q_ASSERT(X) #endif ///When this is uncommented, a 64-bit test-value is written behind the area an item is allowed to write into before ///createItem(..) is called, and an assertion triggers when it was changed during createItem(), which means createItem wrote too long. ///The problem: This temporarily overwrites valid data in the following item, so it will cause serious problems if that data is accessed ///during the call to createItem(). // #define DEBUG_WRITING_EXTENTS class TestItemRepository; namespace KDevelop { /** * This file implements a generic bucket-based indexing repository, that can be used for example to index strings. * * All you need to do is define your item type that you want to store into the repository, and create a request item * similar to ExampleItemRequest that compares and fills the defined item type. * * For example the string repository uses "unsigned short" as item-type, uses that actual value to store the length of the string, * and uses the space behind to store the actual string content. * * @see AbstractItemRepository * @see ItemRepository * * @see ExampleItem * @see ExampleItemRequest * * @see typerepository.h * @see stringrepository.h * @see indexedstring.h */ enum { ItemRepositoryBucketSize = 1<<16, ItemRepositoryBucketLimit = 1<<16 }; /** * Buckets are the memory-units that are used to store the data in an ItemRepository. * * About monster buckets: Normally a bucket has a size of 64kb, but when an item is * allocated that is larger than that, a "monster bucket" is allocated, which spans the * space of multiple buckets. */ template class Bucket { public: enum { AdditionalSpacePerItem = 2 }; enum { ObjectMapSize = ((ItemRepositoryBucketSize / ItemRequest::AverageSize) * 3) / 2 + 1, MaxFreeItemsForHide = 0, //When less than this count of free items in one buckets is reached, the bucket is removed from the global list of buckets with free items MaxFreeSizeForHide = fixedItemSize ? fixedItemSize : 0, //Only when the largest free size is smaller then this, the bucket is taken from the free list MinFreeItemsForReuse = 10,//When this count of free items in one bucket is reached, consider re-assigning them to new requests MinFreeSizeForReuse = ItemRepositoryBucketSize/20 //When the largest free item is bigger then this, the bucket is automatically added to the free list }; enum { NextBucketHashSize = ObjectMapSize, //Affects the average count of bucket-chains that need to be walked in ItemRepository::index. Must be a multiple of ObjectMapSize DataSize = sizeof(char) + sizeof(unsigned int) * 3 + ItemRepositoryBucketSize + sizeof(short unsigned int) * (ObjectMapSize + NextBucketHashSize + 1) }; enum { CheckStart = 0xff00ff1, CheckEnd = 0xfafcfb }; Bucket() : m_monsterBucketExtent(0) , m_available(0) , m_data(0) , m_mappedData(0) , m_objectMap(0) , m_largestFreeItem(0) , m_freeItemCount(0) , m_nextBucketHash(0) , m_dirty(false) , m_changed(false) , m_lastUsed(0) { } ~Bucket() { if(m_data != m_mappedData) { delete[] m_data; delete[] m_nextBucketHash; delete[] m_objectMap; } } void initialize(int monsterBucketExtent) { if(!m_data) { m_monsterBucketExtent = monsterBucketExtent; m_available = ItemRepositoryBucketSize; m_data = new char[ItemRepositoryBucketSize + monsterBucketExtent * DataSize]; memset(m_data, 0, (ItemRepositoryBucketSize + monsterBucketExtent * DataSize) * sizeof(char)); //The bigger we make the map, the lower the probability of a clash(and thus bad performance). However it increases memory usage. m_objectMap = new short unsigned int[ObjectMapSize]; memset(m_objectMap, 0, ObjectMapSize * sizeof(short unsigned int)); m_nextBucketHash = new short unsigned int[NextBucketHashSize]; memset(m_nextBucketHash, 0, NextBucketHashSize * sizeof(short unsigned int)); m_changed = true; m_dirty = false; m_lastUsed = 0; } } template void readValue(char*& from, T& to) { to = *reinterpret_cast(from); from += sizeof(T); } void initializeFromMap(char* current) { if(!m_data) { char* start = current; readValue(current, m_monsterBucketExtent); Q_ASSERT(current - start == 4); readValue(current, m_available); m_objectMap = reinterpret_cast(current); current += sizeof(short unsigned int) * ObjectMapSize; m_nextBucketHash = reinterpret_cast(current); current += sizeof(short unsigned int) * NextBucketHashSize; readValue(current, m_largestFreeItem); readValue(current, m_freeItemCount); readValue(current, m_dirty); m_data = current; m_mappedData = current; m_changed = false; m_lastUsed = 0; VERIFY(current - start == (DataSize - ItemRepositoryBucketSize)); } } void store(QFile* file, size_t offset) { if(!m_data) return; if(static_cast(file->size()) < offset + (1+m_monsterBucketExtent)*DataSize) file->resize(offset + (1+m_monsterBucketExtent)*DataSize); file->seek(offset); file->write((char*)&m_monsterBucketExtent, sizeof(unsigned int)); file->write((char*)&m_available, sizeof(unsigned int)); file->write((char*)m_objectMap, sizeof(short unsigned int) * ObjectMapSize); file->write((char*)m_nextBucketHash, sizeof(short unsigned int) * NextBucketHashSize); file->write((char*)&m_largestFreeItem, sizeof(short unsigned int)); file->write((char*)&m_freeItemCount, sizeof(unsigned int)); file->write((char*)&m_dirty, sizeof(bool)); file->write(m_data, ItemRepositoryBucketSize + m_monsterBucketExtent * DataSize); if(static_cast(file->pos()) != offset + (1+m_monsterBucketExtent)*DataSize) { KMessageBox::error(0, i18n("Failed writing to %1, probably the disk is full", file->fileName())); abort(); } m_changed = false; #ifdef DEBUG_ITEMREPOSITORY_LOADING { file->flush(); file->seek(offset); uint available, freeItemCount, monsterBucketExtent; short unsigned int largestFree; bool dirty; short unsigned int* m = new short unsigned int[ObjectMapSize]; short unsigned int* h = new short unsigned int[NextBucketHashSize]; file->read((char*)&monsterBucketExtent, sizeof(unsigned int)); char* d = new char[ItemRepositoryBucketSize + monsterBucketExtent * DataSize]; file->read((char*)&available, sizeof(unsigned int)); file->read((char*)m, sizeof(short unsigned int) * ObjectMapSize); file->read((char*)h, sizeof(short unsigned int) * NextBucketHashSize); file->read((char*)&largestFree, sizeof(short unsigned int)); file->read((char*)&freeItemCount, sizeof(unsigned int)); file->read((char*)&dirty, sizeof(bool)); file->read(d, ItemRepositoryBucketSize); Q_ASSERT(monsterBucketExtent == m_monsterBucketExtent); Q_ASSERT(available == m_available); Q_ASSERT(memcmp(d, m_data, ItemRepositoryBucketSize + monsterBucketExtent * DataSize) == 0); Q_ASSERT(memcmp(m, m_objectMap, sizeof(short unsigned int) * ObjectMapSize) == 0); Q_ASSERT(memcmp(h, m_nextBucketHash, sizeof(short unsigned int) * NextBucketHashSize) == 0); Q_ASSERT(m_largestFreeItem == largestFree); Q_ASSERT(m_freeItemCount == freeItemCount); Q_ASSERT(m_dirty == dirty); Q_ASSERT(static_cast(file->pos()) == offset + DataSize + m_monsterBucketExtent * DataSize); delete[] d; delete[] m; delete[] h; } #endif } inline char* data() { return m_data; } inline uint dataSize() const { return ItemRepositoryBucketSize + m_monsterBucketExtent * DataSize; } //Tries to find the index this item has in this bucket, or returns zero if the item isn't there yet. unsigned short findIndex(const ItemRequest& request) const { m_lastUsed = 0; unsigned short localHash = request.hash() % ObjectMapSize; unsigned short index = m_objectMap[localHash]; unsigned short follower = 0; //Walk the chain of items with the same local hash while(index && (follower = followerIndex(index)) && !(request.equals(itemFromIndex(index)))) index = follower; if(index && request.equals(itemFromIndex(index))) { return index; //We have found the item } return 0; } //Tries to get the index within this bucket, or returns zero. Will put the item into the bucket if there is room. //Created indices will never begin with 0xffff____, so you can use that index-range for own purposes. unsigned short index(const ItemRequest& request, unsigned int itemSize) { m_lastUsed = 0; unsigned short localHash = request.hash() % ObjectMapSize; unsigned short index = m_objectMap[localHash]; unsigned short insertedAt = 0; unsigned short follower = 0; //Walk the chain of items with the same local hash while(index && (follower = followerIndex(index)) && !(request.equals(itemFromIndex(index)))) index = follower; if(index && request.equals(itemFromIndex(index))) return index; //We have found the item ifDebugLostSpace( Q_ASSERT(!lostSpace()); ) prepareChange(); unsigned int totalSize = itemSize + AdditionalSpacePerItem; if(m_monsterBucketExtent) { ///This is a monster-bucket. Other rules are applied here. Only one item can be allocated, and that must be bigger than the bucket data Q_ASSERT(totalSize > ItemRepositoryBucketSize); Q_ASSERT(m_available); m_available = 0; insertedAt = AdditionalSpacePerItem; setFollowerIndex(insertedAt, 0); Q_ASSERT(m_objectMap[localHash] == 0); m_objectMap[localHash] = insertedAt; if(markForReferenceCounting) enableDUChainReferenceCounting(m_data, dataSize()); request.createItem(reinterpret_cast(m_data + insertedAt)); if(markForReferenceCounting) disableDUChainReferenceCounting(m_data); return insertedAt; } //The second condition is needed, else we can get problems with zero-length items and an overflow in insertedAt to zero if(totalSize > m_available || (!itemSize && totalSize == m_available)) { //Try finding the smallest freed item that can hold the data unsigned short currentIndex = m_largestFreeItem; unsigned short previousIndex = 0; unsigned short freeChunkSize = 0; ///@todo Achieve this without full iteration while(currentIndex && freeSize(currentIndex) > itemSize) { unsigned short follower = followerIndex(currentIndex); if(follower && freeSize(follower) >= itemSize) { //The item also fits into the smaller follower, so use that one previousIndex = currentIndex; currentIndex = follower; }else{ //The item fits into currentIndex, but not into the follower. So use currentIndex freeChunkSize = freeSize(currentIndex) - itemSize; //We need 2 bytes to store the free size if(freeChunkSize != 0 && freeChunkSize < AdditionalSpacePerItem+2) { //we can not manage the resulting free chunk as a separate item, so we cannot use this position. //Just pick the biggest free item, because there we can be sure that //either we can manage the split, or we cannot do anything at all in this bucket. freeChunkSize = freeSize(m_largestFreeItem) - itemSize; if(freeChunkSize == 0 || freeChunkSize >= AdditionalSpacePerItem+2) { previousIndex = 0; currentIndex = m_largestFreeItem; }else{ currentIndex = 0; } } break; } } if(!currentIndex || freeSize(currentIndex) < (totalSize-AdditionalSpacePerItem)) return 0; if(previousIndex) setFollowerIndex(previousIndex, followerIndex(currentIndex)); else m_largestFreeItem = followerIndex(currentIndex); --m_freeItemCount; //Took one free item out of the chain ifDebugLostSpace( Q_ASSERT((uint)lostSpace() == (uint)(freeSize(currentIndex) + AdditionalSpacePerItem)); ) if(freeChunkSize) { Q_ASSERT(freeChunkSize >= AdditionalSpacePerItem+2); unsigned short freeItemSize = freeChunkSize - AdditionalSpacePerItem; unsigned short freeItemPosition; //Insert the resulting free chunk into the list of free items, so we don't lose it if(isBehindFreeSpace(currentIndex)) { //Create the free item at the beginning of currentIndex, so it can be merged with the free space in front freeItemPosition = currentIndex; currentIndex += freeItemSize + AdditionalSpacePerItem; }else{ //Create the free item behind currentIndex freeItemPosition = currentIndex + itemSize + AdditionalSpacePerItem; } setFreeSize(freeItemPosition, freeItemSize); insertFreeItem(freeItemPosition); } insertedAt = currentIndex; Q_ASSERT((bool)m_freeItemCount == (bool)m_largestFreeItem); }else{ //We have to insert the item insertedAt = ItemRepositoryBucketSize - m_available; insertedAt += AdditionalSpacePerItem; //Room for the prepended follower-index m_available -= totalSize; } ifDebugLostSpace( Q_ASSERT(lostSpace() == totalSize); ) Q_ASSERT(!index || !followerIndex(index)); Q_ASSERT(!m_objectMap[localHash] || index); if(index) setFollowerIndex(index, insertedAt); setFollowerIndex(insertedAt, 0); if(m_objectMap[localHash] == 0) m_objectMap[localHash] = insertedAt; #ifdef DEBUG_CREATEITEM_EXTENTS char* borderBehind = m_data + insertedAt + (totalSize-AdditionalSpacePerItem); quint64 oldValueBehind = 0; if(m_available >= 8) { oldValueBehind = *(quint64*)borderBehind; *((quint64*)borderBehind) = 0xfafafafafafafafaLLU; } #endif //Last thing we do, because createItem may recursively do even more transformation of the repository if(markForReferenceCounting) enableDUChainReferenceCounting(m_data, dataSize()); request.createItem(reinterpret_cast(m_data + insertedAt)); if(markForReferenceCounting) disableDUChainReferenceCounting(m_data); #ifdef DEBUG_CREATEITEM_EXTENTS if(m_available >= 8) { //If this assertion triggers, then the item writes a bigger range than it advertised in Q_ASSERT(*((quint64*)borderBehind) == 0xfafafafafafafafaLLU); *((quint64*)borderBehind) = oldValueBehind; } #endif Q_ASSERT(itemFromIndex(insertedAt)->hash() == request.hash()); Q_ASSERT(itemFromIndex(insertedAt)->itemSize() == itemSize); ifDebugLostSpace( if(lostSpace()) qDebug() << "lost space:" << lostSpace(); Q_ASSERT(!lostSpace()); ) return insertedAt; } ///@param modulo Returns whether this bucket contains an item with (hash % modulo) == (item.hash % modulo) /// The default-parameter is the size of the next-bucket hash that is used by setNextBucketForHash and nextBucketForHash - /// @param modulo MUST be a multiple of ObjectMapSize, because (b-a) | (x * h1) => (b-a) | h2, where a|b means a is a multiple of b. - /// This this allows efficiently computing the clashes using the local object map hash. + ///@note modulo MUST be a multiple of ObjectMapSize, because (b-a) | (x * h1) => (b-a) | h2, where a|b means a is a multiple of b. + /// This this allows efficiently computing the clashes using the local object map hash. bool hasClashingItem(uint hash, uint modulo) { Q_ASSERT(modulo % ObjectMapSize == 0); m_lastUsed = 0; uint hashMod = hash % modulo; unsigned short localHash = hash % ObjectMapSize; unsigned short currentIndex = m_objectMap[localHash]; if(currentIndex == 0) return false; while(currentIndex) { uint currentHash = itemFromIndex(currentIndex)->hash(); Q_ASSERT(currentHash % ObjectMapSize == localHash); if(currentHash % modulo == hashMod) return true; //Clash currentIndex = followerIndex(currentIndex); } return false; } void countFollowerIndexLengths(uint& usedSlots, uint& lengths, uint& slotCount, uint& longestInBucketFollowerChain) { for(uint a = 0; a < ObjectMapSize; ++a) { unsigned short currentIndex = m_objectMap[a]; ++slotCount; uint length = 0; if(currentIndex) { ++usedSlots; while(currentIndex) { ++length; ++lengths; currentIndex = followerIndex(currentIndex); } if(length > longestInBucketFollowerChain) { // qDebug() << "follower-chain at" << a << ":" << length; longestInBucketFollowerChain = length; } } } } //Returns whether the given item is reachabe within this bucket, through its hash. bool itemReachable(const Item* item, uint hash) const { unsigned short localHash = hash % ObjectMapSize; unsigned short currentIndex = m_objectMap[localHash]; while(currentIndex) { if(itemFromIndex(currentIndex) == item) return true; currentIndex = followerIndex(currentIndex); } return false; } template void deleteItem(unsigned short index, unsigned int hash, Repository& repository) { ifDebugLostSpace( Q_ASSERT(!lostSpace()); ) m_lastUsed = 0; prepareChange(); unsigned int size = itemFromIndex(index)->itemSize(); //Step 1: Remove the item from the data-structures that allow finding it: m_objectMap unsigned short localHash = hash % ObjectMapSize; unsigned short currentIndex = m_objectMap[localHash]; unsigned short previousIndex = 0; //Fix the follower-link by setting the follower of the previous item to the next one, or updating m_objectMap while(currentIndex != index) { previousIndex = currentIndex; currentIndex = followerIndex(currentIndex); //If this assertion triggers, the deleted item was not registered under the given hash Q_ASSERT(currentIndex); } Q_ASSERT(currentIndex == index); if(!previousIndex) //The item was directly in the object map m_objectMap[localHash] = followerIndex(index); else setFollowerIndex(previousIndex, followerIndex(index)); Item* item = const_cast(itemFromIndex(index)); if(markForReferenceCounting) enableDUChainReferenceCounting(m_data, dataSize()); ItemRequest::destroy(item, repository); if(markForReferenceCounting) disableDUChainReferenceCounting(m_data); memset(item, 0, size); //For debugging, so we notice the data is wrong if(m_monsterBucketExtent) { ///This is a monster-bucket. Make it completely empty again. Q_ASSERT(!m_available); m_available = ItemRepositoryBucketSize; //Items are always inserted into monster-buckets at a fixed position Q_ASSERT(currentIndex == AdditionalSpacePerItem); Q_ASSERT(m_objectMap[localHash] == 0); }else{ ///Put the space into the free-set setFreeSize(index, size); //Try merging the created free item to other free items around, else add it into the free list insertFreeItem(index); if(m_freeItemCount == 1 && freeSize(m_largestFreeItem) + m_available == ItemRepositoryBucketSize) { //Everything has been deleted, there is only free space left. Make the bucket empty again, //so it can later also be used as a monster-bucket. m_available = ItemRepositoryBucketSize; m_freeItemCount = 0; m_largestFreeItem = 0; } } Q_ASSERT((bool)m_freeItemCount == (bool)m_largestFreeItem); ifDebugLostSpace( Q_ASSERT(!lostSpace()); ) #ifdef DEBUG_INCORRECT_DELETE //Make sure the item cannot be found any more { unsigned short localHash = hash % ObjectMapSize; unsigned short currentIndex = m_objectMap[localHash]; while(currentIndex && currentIndex != index) { previousIndex = currentIndex; currentIndex = followerIndex(currentIndex); } Q_ASSERT(!currentIndex); //The item must not be found } #endif // Q_ASSERT(canAllocateItem(size)); } ///@warning The returned item may be in write-protected memory, so never try doing a const_cast and changing some data /// If you need to change something, use dynamicItemFromIndex ///@warning When using multi-threading, mutex() must be locked as long as you use the returned data inline const Item* itemFromIndex(unsigned short index) const { m_lastUsed = 0; return reinterpret_cast(m_data+index); } bool isEmpty() const { return m_available == ItemRepositoryBucketSize; } ///Returns true if this bucket has no nextBucketForHash links bool noNextBuckets() const { for(int a = 0; a < NextBucketHashSize; ++a) if(m_nextBucketHash[a]) return false; return true; } uint available() const { return m_available; } uint usedMemory() const { return ItemRepositoryBucketSize - m_available; } template bool visitAllItems(Visitor& visitor) const { m_lastUsed = 0; for(uint a = 0; a < ObjectMapSize; ++a) { uint currentIndex = m_objectMap[a]; while(currentIndex) { //Get the follower early, so there is no problems when the current //index is removed if(!visitor(reinterpret_cast(m_data+currentIndex))) return false; currentIndex = followerIndex(currentIndex); } } return true; } ///Returns whether something was changed template int finalCleanup(Repository& repository) { int changed = 0; while(m_dirty) { m_dirty = false; for(uint a = 0; a < ObjectMapSize; ++a) { uint currentIndex = m_objectMap[a]; while(currentIndex) { //Get the follower early, so there is no problems when the current //index is removed const Item* item = reinterpret_cast(m_data+currentIndex); if(!ItemRequest::persistent(item)) { changed += item->itemSize(); deleteItem(currentIndex, item->hash(), repository); m_dirty = true; //Set to dirty so we re-iterate break; } currentIndex = followerIndex(currentIndex); } } } return changed; } unsigned short nextBucketForHash(uint hash) const { m_lastUsed = 0; return m_nextBucketHash[hash % NextBucketHashSize]; } void setNextBucketForHash(unsigned int hash, unsigned short bucket) { m_lastUsed = 0; prepareChange(); m_nextBucketHash[hash % NextBucketHashSize] = bucket; } uint freeItemCount() const { return m_freeItemCount; } short unsigned int totalFreeItemsSize() const { short unsigned int ret = 0; short unsigned int currentIndex = m_largestFreeItem; while(currentIndex) { ret += freeSize(currentIndex); currentIndex = followerIndex(currentIndex); } return ret; } //Size of the largest item that could be inserted into this bucket short unsigned int largestFreeSize() const { short unsigned int ret = 0; if(m_largestFreeItem) ret = freeSize(m_largestFreeItem); if(m_available > (uint)(AdditionalSpacePerItem + (uint)ret)) { ret = m_available - AdditionalSpacePerItem; Q_ASSERT(ret == (m_available - AdditionalSpacePerItem)); } return ret; } bool canAllocateItem(unsigned int size) const { short unsigned int currentIndex = m_largestFreeItem; while(currentIndex) { short unsigned int currentFree = freeSize(currentIndex); if(currentFree < size) return false; //Either we need an exact match, or 2 additional bytes to manage the resulting gap if(size == currentFree || currentFree - size >= AdditionalSpacePerItem + 2) return true; currentIndex = followerIndex(currentIndex); } return false; } void tick() const { ++m_lastUsed; } //How many ticks ago the item was last used int lastUsed() const { return m_lastUsed; } //Whether this bucket was changed since it was last stored bool changed() const { return m_changed; } void prepareChange() { m_changed = true; m_dirty = true; makeDataPrivate(); } bool dirty() const { return m_dirty; } ///Returns the count of following buckets that were merged onto this buckets data array int monsterBucketExtent() const { return m_monsterBucketExtent; } //Counts together the space that is neither accessible through m_objectMap nor through the free items uint lostSpace() { if(m_monsterBucketExtent) return 0; uint need = ItemRepositoryBucketSize - m_available; uint found = 0; for(uint a = 0; a < ObjectMapSize; ++a) { uint currentIndex = m_objectMap[a]; while(currentIndex) { found += reinterpret_cast(m_data+currentIndex)->itemSize() + AdditionalSpacePerItem; currentIndex = followerIndex(currentIndex); } } uint currentIndex = m_largestFreeItem; while(currentIndex) { found += freeSize(currentIndex) + AdditionalSpacePerItem; currentIndex = followerIndex(currentIndex); } return need-found; } private: void makeDataPrivate() { if(m_mappedData == m_data) { short unsigned int* oldObjectMap = m_objectMap; short unsigned int* oldNextBucketHash = m_nextBucketHash; m_data = new char[ItemRepositoryBucketSize + m_monsterBucketExtent * DataSize]; m_objectMap = new short unsigned int[ObjectMapSize]; m_nextBucketHash = new short unsigned int[NextBucketHashSize]; memcpy(m_data, m_mappedData, ItemRepositoryBucketSize + m_monsterBucketExtent * DataSize); memcpy(m_objectMap, oldObjectMap, ObjectMapSize * sizeof(short unsigned int)); memcpy(m_nextBucketHash, oldNextBucketHash, NextBucketHashSize * sizeof(short unsigned int)); } } ///Merges the given index item, which must have a freeSize() set, to surrounding free items, and inserts the result. ///The given index itself should not be in the free items chain yet. ///Returns whether the item was inserted somewhere. void insertFreeItem(unsigned short index) { //If the item-size is fixed, we don't need to do any management. Just keep a list of free items. Items of other size will never be requested. if(!fixedItemSize) { unsigned short currentIndex = m_largestFreeItem; unsigned short previousIndex = 0; while(currentIndex) { Q_ASSERT(currentIndex != index); #ifndef QT_NO_DEBUG unsigned short currentFreeSize = freeSize(currentIndex); #endif ///@todo Achieve this without iterating through all items in the bucket(This is very slow) //Merge behind index if(currentIndex == index + freeSize(index) + AdditionalSpacePerItem) { //Remove currentIndex from the free chain, since it's merged backwards into index if(previousIndex && followerIndex(currentIndex)) Q_ASSERT(freeSize(previousIndex) >= freeSize(followerIndex(currentIndex))); if(previousIndex) setFollowerIndex(previousIndex, followerIndex(currentIndex)); else m_largestFreeItem = followerIndex(currentIndex); --m_freeItemCount; //One was removed //currentIndex is directly behind index, touching its space. Merge them. setFreeSize(index, freeSize(index) + AdditionalSpacePerItem + freeSize(currentIndex)); //Recurse to do even more merging insertFreeItem(index); return; } //Merge before index if(index == currentIndex + freeSize(currentIndex) + AdditionalSpacePerItem) { if(previousIndex && followerIndex(currentIndex)) Q_ASSERT(freeSize(previousIndex) >= freeSize(followerIndex(currentIndex))); //Remove currentIndex from the free chain, since insertFreeItem wants //it not to be in the chain, and it will be inserted in another place if(previousIndex) setFollowerIndex(previousIndex, followerIndex(currentIndex)); else m_largestFreeItem = followerIndex(currentIndex); --m_freeItemCount; //One was removed //index is directly behind currentIndex, touching its space. Merge them. setFreeSize(currentIndex, freeSize(currentIndex) + AdditionalSpacePerItem + freeSize(index)); //Recurse to do even more merging insertFreeItem(currentIndex); return; } previousIndex = currentIndex; currentIndex = followerIndex(currentIndex); #ifndef QT_NO_DEBUG if(currentIndex) Q_ASSERT(freeSize(currentIndex) <= currentFreeSize); #endif } } insertToFreeChain(index); } ///Only inserts the item in the correct position into the free chain. index must not be in the chain yet. void insertToFreeChain(unsigned short index) { if(!fixedItemSize) { ///@todo Use some kind of tree to find the correct position in the chain(This is very slow) //Insert the free item into the chain opened by m_largestFreeItem unsigned short currentIndex = m_largestFreeItem; unsigned short previousIndex = 0; unsigned short size = freeSize(index); while(currentIndex && freeSize(currentIndex) > size) { Q_ASSERT(currentIndex != index); //must not be in the chain yet previousIndex = currentIndex; currentIndex = followerIndex(currentIndex); } if(currentIndex) Q_ASSERT(freeSize(currentIndex) <= size); setFollowerIndex(index, currentIndex); if(previousIndex) { Q_ASSERT(freeSize(previousIndex) >= size); setFollowerIndex(previousIndex, index); } else //This item is larger than all already registered free items, or there are none. m_largestFreeItem = index; }else{ Q_ASSERT(freeSize(index) == fixedItemSize); //When all items have the same size, just prepent to the front. setFollowerIndex(index, m_largestFreeItem); m_largestFreeItem = index; } ++m_freeItemCount; } ///Returns true if the given index is right behind free space, and thus can be merged to the free space. bool isBehindFreeSpace(unsigned short index) const { ///@todo Without iteration! unsigned short currentIndex = m_largestFreeItem; while(currentIndex) { if(index == currentIndex + freeSize(currentIndex) + AdditionalSpacePerItem) return true; currentIndex = followerIndex(currentIndex); } return false; } ///@param index the index of an item @return The index of the next item in the chain of items with a same local hash, or zero inline unsigned short followerIndex(unsigned short index) const { Q_ASSERT(index >= 2); return *reinterpret_cast(m_data+(index-2)); } void setFollowerIndex(unsigned short index, unsigned short follower) { Q_ASSERT(index >= 2); *reinterpret_cast(m_data+(index-2)) = follower; } // Only returns the current value if the item is actually free inline unsigned short freeSize(unsigned short index) const { return *reinterpret_cast(m_data+index); } //Convenience function to set the free-size, only for freed items void setFreeSize(unsigned short index, unsigned short size) { *reinterpret_cast(m_data+index) = size; } int m_monsterBucketExtent; //If this is a monster-bucket, this contains the count of follower-buckets that belong to this one unsigned int m_available; char* m_data; //Structure of the data: (2 byte), (item.size() byte) char* m_mappedData; //Read-only memory-mapped data. If this equals m_data, m_data must not be written short unsigned int* m_objectMap; //Points to the first object in m_data with (hash % ObjectMapSize) == index. Points to the item itself, so subtract 1 to get the pointer to the next item with same local hash. short unsigned int m_largestFreeItem; //Points to the largest item that is currently marked as free, or zero. That one points to the next largest one through followerIndex unsigned int m_freeItemCount; unsigned short* m_nextBucketHash; bool m_dirty; //Whether the data was changed since the last finalCleanup bool m_changed; //Whether this bucket was changed since it was last stored to disk mutable int m_lastUsed; //How many ticks ago this bucket was last accessed }; template struct Locker { //This is a dummy that does nothing template Locker(const T& /*t*/) { } }; template<> struct Locker { Locker(QMutex* mutex) : m_mutex(mutex) { m_mutex->lock(); } ~Locker() { m_mutex->unlock(); } QMutex* m_mutex; }; ///This object needs to be kept alive as long as you change the contents of an item ///stored in the repository. It is needed to correctly track the reference counting ///within disk-storage. ///@warning You can not freely copy this around, when you create a copy, the copy source /// becomes invalid template class DynamicItem { public: DynamicItem(Item* i, void* start, uint size) : m_item(i), m_start(start) { if(markForReferenceCounting) enableDUChainReferenceCounting(m_start, size); // qDebug() << "enabling" << i << "to" << (void*)(((char*)i)+size); } ~DynamicItem() { if(m_start) { // qDebug() << "destructor-disabling" << m_item; if(markForReferenceCounting) disableDUChainReferenceCounting(m_start); } } DynamicItem(const DynamicItem& rhs) : m_item(rhs.m_item), m_start(rhs.m_start) { // qDebug() << "stealing" << m_item; Q_ASSERT(rhs.m_start); rhs.m_start = 0; } Item* operator->() { return m_item; } Item* m_item; private: mutable void* m_start; DynamicItem& operator=(const DynamicItem&); }; ///@param Item @see ExampleItem ///@param ItemRequest @see ExampleReqestItem ///@param fixedItemSize When this is true, all inserted items must have the same size. /// This greatly simplifies and speeds up the task of managing free items within the buckets. ///@param markForReferenceCounting Whether the data within the repository should be marked for reference-counting. /// This costs a bit of performance, but must be enabled if there may be data in the repository /// that does on-disk reference counting, like IndexedString, IndexedIdentifier, etc. ///@param threadSafe Whether class access should be thread-safe. Disabling this is dangerous when you do multi-threading. /// You have to make sure that mutex() is locked whenever the repository is accessed. template class ItemRepository : public AbstractItemRepository { typedef Locker ThisLocker; typedef Bucket MyBucket; enum { //Must be a multiple of Bucket::ObjectMapSize, so Bucket::hasClashingItem can be computed //Must also be a multiple of Bucket::NextBucketHashSize, for the same reason.(Currently those are same) bucketHashSize = (targetBucketHashSize / MyBucket::ObjectMapSize) * MyBucket::ObjectMapSize }; enum { BucketStartOffset = sizeof(uint) * 7 + sizeof(short unsigned int) * bucketHashSize //Position in the data where the bucket array starts }; public: ///@param registry May be zero, then the repository will not be registered at all. Else, the repository will register itself to that registry. /// If this is zero, you have to care about storing the data using store() and/or close() by yourself. It does not happen automatically. /// For the global standard registry, the storing/loading is triggered from within duchain, so you don't need to care about it. ItemRepository(const QString& repositoryName, ItemRepositoryRegistry* registry = &globalItemRepositoryRegistry(), uint repositoryVersion = 1, AbstractRepositoryManager* manager = 0) : m_ownMutex(QMutex::Recursive) , m_mutex(&m_ownMutex) , m_repositoryName(repositoryName) , m_registry(registry) , m_file(0) , m_dynamicFile(0) , m_repositoryVersion(repositoryVersion) , m_manager(manager) { m_unloadingEnabled = true; m_metaDataChanged = true; m_buckets.resize(10); m_buckets.fill(0); memset(m_firstBucketForHash, 0, bucketHashSize * sizeof(short unsigned int)); m_statBucketHashClashes = m_statItemCount = 0; m_currentBucket = 1; //Skip the first bucket, we won't use it so we have the zero indices for special purposes if(m_registry) m_registry->registerRepository(this, m_manager); } ~ItemRepository() { if(m_registry) m_registry->unRegisterRepository(this); close(); } ///Unloading of buckets is enabled by default. Use this to disable it. When unloading is enabled, the data ///gotten from must only itemFromIndex must not be used for a long time. void setUnloadingEnabled(bool enabled) { m_unloadingEnabled = enabled; } ///Returns the index for the given item. If the item is not in the repository yet, it is inserted. ///The index can never be zero. Zero is reserved for your own usage as invalid - ///@param dynamic will be applied to the dynamic data of the found item + ///@param request Item to retrieve the index from unsigned int index(const ItemRequest& request) { ThisLocker lock(m_mutex); const uint hash = request.hash(); const uint size = request.itemSize(); // Bucket indexes tracked while walking the bucket chain for this request hash unsigned short bucketInChainWithSpace = 0; unsigned short lastBucketWalked = 0; const ushort foundIndexInBucket = walkBucketChain(hash, [&](ushort bucketIdx, const MyBucket* bucketPtr) { lastBucketWalked = bucketIdx; const ushort found = bucketPtr->findIndex(request); if (!found && !bucketInChainWithSpace && bucketPtr->canAllocateItem(size)) { bucketInChainWithSpace = bucketIdx; } return found; }); if (foundIndexInBucket) { // 'request' is already present, return the existing index return createIndex(lastBucketWalked, foundIndexInBucket); } /* * Disclaimer: Writer of comment != writer of code, believe with caution * * Requested item does not yet exist. Need to find a place to put it... * * First choice is to place it in an existing bucket in the chain for the request hash * Second choice is to find an existing bucket anywhere with enough space * Otherwise use m_currentBucket (the latest unused bucket) * * If the chosen bucket fails to allocate the item, merge buckets to create a monster (dragon?) * * Finally, if the first option failed or the selected bucket failed to allocate, add the * bucket which the item ended up in to the chain of buckets for the request's hash */ m_metaDataChanged = true; const bool pickedBucketInChain = bucketInChainWithSpace; int useBucket = bucketInChainWithSpace; int reOrderFreeSpaceBucketIndex = -1; if (!pickedBucketInChain) { //Try finding an existing bucket with deleted space to store the data into for(int a = 0; a < m_freeSpaceBuckets.size(); ++a) { MyBucket* bucketPtr = bucketForIndex(m_freeSpaceBuckets[a]); Q_ASSERT(bucketPtr->largestFreeSize()); if(bucketPtr->canAllocateItem(size)) { //The item fits into the bucket. useBucket = m_freeSpaceBuckets[a]; reOrderFreeSpaceBucketIndex = a; break; } } if (!useBucket) { useBucket = m_currentBucket; } } else { reOrderFreeSpaceBucketIndex = m_freeSpaceBuckets.indexOf(useBucket); } //The item isn't in the repository yet, find a new bucket for it while(1) { if(useBucket >= m_buckets.size()) { if(m_buckets.size() >= 0xfffe) { //We have reserved the last bucket index 0xffff for special purposes //the repository has overflown. qWarning() << "Found no room for an item in" << m_repositoryName << "size of the item:" << request.itemSize(); return 0; }else{ //Allocate new buckets m_buckets.resize(m_buckets.size() + 10); } } MyBucket* bucketPtr = m_buckets.at(useBucket); if(!bucketPtr) { initializeBucket(useBucket); bucketPtr = m_buckets.at(useBucket); } ENSURE_REACHABLE(useBucket); Q_ASSERT_X(!bucketPtr->findIndex(request), Q_FUNC_INFO, "found item in unexpected bucket, ensure your ItemRequest::equals method is correct. Note: For custom AbstractType's e.g. ensure you have a proper equals() override"); unsigned short indexInBucket = bucketPtr->index(request, size); //If we could not allocate the item in an empty bucket, then we need to create a monster-bucket that //can hold the data. if(bucketPtr->isEmpty() && !indexInBucket) { ///@todo Move this compound statement into an own function uint totalSize = size + MyBucket::AdditionalSpacePerItem; Q_ASSERT((totalSize > ItemRepositoryBucketSize)); useBucket = 0; //The item did not fit in, we need a monster-bucket(Merge consecutive buckets) ///Step one: Search whether we can merge multiple empty buckets in the free-list into one monster-bucket int rangeStart = -1; int rangeEnd = -1; for(int a = 0; a < m_freeSpaceBuckets.size(); ++a) { MyBucket* bucketPtr = bucketForIndex(m_freeSpaceBuckets[a]); if(bucketPtr->isEmpty()) { //This bucket is a candidate for monster-bucket merging int index = (int)m_freeSpaceBuckets[a]; if(rangeEnd != index) { rangeStart = index; rangeEnd = index+1; }else{ ++rangeEnd; } if(rangeStart != rangeEnd) { uint extent = rangeEnd - rangeStart - 1; uint totalAvailableSpace = bucketForIndex(rangeStart)->available() + MyBucket::DataSize * (rangeEnd - rangeStart - 1); if(totalAvailableSpace > totalSize) { Q_ASSERT(extent); ///We can merge these buckets into one monster-bucket that can hold the data Q_ASSERT((uint)m_freeSpaceBuckets[a-extent] == (uint)rangeStart); m_freeSpaceBuckets.remove(a-extent, extent+1); useBucket = rangeStart; convertMonsterBucket(rangeStart, extent); break; } } } } if(!useBucket) { //Create a new monster-bucket at the end of the data int needMonsterExtent = (totalSize - ItemRepositoryBucketSize) / MyBucket::DataSize + 1; Q_ASSERT(needMonsterExtent); if(m_currentBucket + needMonsterExtent + 1 > m_buckets.size()) { m_buckets.resize(m_buckets.size() + 10 + needMonsterExtent + 1); } useBucket = m_currentBucket; convertMonsterBucket(useBucket, needMonsterExtent); m_currentBucket += 1 + needMonsterExtent; Q_ASSERT(m_currentBucket < ItemRepositoryBucketLimit); Q_ASSERT(m_buckets[m_currentBucket - 1 - needMonsterExtent] && m_buckets[m_currentBucket - 1 - needMonsterExtent]->monsterBucketExtent() == needMonsterExtent); } Q_ASSERT(useBucket); bucketPtr = bucketForIndex(useBucket); indexInBucket = bucketPtr->index(request, size); Q_ASSERT(indexInBucket); } if(indexInBucket) { ++m_statItemCount; const int previousBucketNumber = lastBucketWalked; unsigned short* const bucketHashPosition = m_firstBucketForHash + (hash % bucketHashSize); if(!(*bucketHashPosition)) { Q_ASSERT(!previousBucketNumber); (*bucketHashPosition) = useBucket; ENSURE_REACHABLE(useBucket); } else if(!pickedBucketInChain && previousBucketNumber && previousBucketNumber != useBucket) { //Should happen rarely ++m_statBucketHashClashes; ///Debug: Detect infinite recursion ifDebugInfiniteRecursion(Q_ASSERT(walkBucketLinks(*bucketHashPosition, hash, previousBucketNumber));) //Find the position where the paths of useBucket and *bucketHashPosition intersect, and insert useBucket //there. That way, we don't create loops. QPair intersect = hashChainIntersection(*bucketHashPosition, useBucket, hash); Q_ASSERT(m_buckets[previousBucketNumber]->nextBucketForHash(hash) == 0); if(!intersect.second) { ifDebugInfiniteRecursion(Q_ASSERT(!walkBucketLinks(*bucketHashPosition, hash, useBucket));) ifDebugInfiniteRecursion(Q_ASSERT(!walkBucketLinks(useBucket, hash, previousBucketNumber));) Q_ASSERT(m_buckets[previousBucketNumber]->nextBucketForHash(hash) == 0); m_buckets[previousBucketNumber]->setNextBucketForHash(hash, useBucket); ENSURE_REACHABLE(useBucket); ENSURE_REACHABLE(previousBucketNumber); ifDebugInfiniteRecursion(Q_ASSERT(walkBucketLinks(*bucketHashPosition, hash, useBucket));) } else if(intersect.first) { ifDebugInfiniteRecursion(Q_ASSERT(bucketForIndex(intersect.first)->nextBucketForHash(hash) == intersect.second);) ifDebugInfiniteRecursion(Q_ASSERT(!walkBucketLinks(*bucketHashPosition, hash, useBucket));) ifDebugInfiniteRecursion(Q_ASSERT(walkBucketLinks(*bucketHashPosition, hash, intersect.second));) ifDebugInfiniteRecursion(Q_ASSERT(walkBucketLinks(*bucketHashPosition, hash, intersect.first));) ifDebugInfiniteRecursion(Q_ASSERT(bucketForIndex(intersect.first)->nextBucketForHash(hash) == intersect.second);) ifDebugInfiniteRecursion(Q_ASSERT(walkBucketLinks(useBucket, hash, intersect.second));) ifDebugInfiniteRecursion(Q_ASSERT(!walkBucketLinks(useBucket, hash, intersect.first));) bucketForIndex(intersect.first)->setNextBucketForHash(hash, useBucket); ENSURE_REACHABLE(useBucket); ENSURE_REACHABLE(intersect.second); ifDebugInfiniteRecursion(Q_ASSERT(walkBucketLinks(*bucketHashPosition, hash, useBucket));) ifDebugInfiniteRecursion(Q_ASSERT(walkBucketLinks(*bucketHashPosition, hash, intersect.second));) } else { //State: intersect.first == 0 && intersect.second != 0. This means that whole compleet //chain opened by *bucketHashPosition with the hash-value is also following useBucket, //so useBucket can just be inserted at the top ifDebugInfiniteRecursion(Q_ASSERT(!walkBucketLinks(*bucketHashPosition, hash, useBucket));) ifDebugInfiniteRecursion(Q_ASSERT(walkBucketLinks(useBucket, hash, *bucketHashPosition));) unsigned short oldStart = *bucketHashPosition; *bucketHashPosition = useBucket; ENSURE_REACHABLE(useBucket); ENSURE_REACHABLE(oldStart); Q_UNUSED(oldStart); } } if(reOrderFreeSpaceBucketIndex != -1) updateFreeSpaceOrder(reOrderFreeSpaceBucketIndex); return createIndex(useBucket, indexInBucket); }else{ //This should never happen when we picked a bucket for re-use Q_ASSERT(!pickedBucketInChain); Q_ASSERT(reOrderFreeSpaceBucketIndex == -1); Q_ASSERT(useBucket == m_currentBucket); if(!bucketForIndex(useBucket)->isEmpty()) putIntoFreeList(useBucket, bucketPtr); ++m_currentBucket; Q_ASSERT(m_currentBucket < ItemRepositoryBucketLimit); useBucket = m_currentBucket; } } //We haven't found a bucket that already contains the item, so append it to the current bucket qWarning() << "Found no bucket for an item in" << m_repositoryName; return 0; } ///Returns zero if the item is not in the repository yet unsigned int findIndex(const ItemRequest& request) { ThisLocker lock(m_mutex); return walkBucketChain(request.hash(), [this, &request](ushort bucketIdx, const MyBucket* bucketPtr) { const ushort indexInBucket = bucketPtr->findIndex(request); return indexInBucket ? createIndex(bucketIdx, indexInBucket) : 0u; }); } ///Deletes the item from the repository. void deleteItem(unsigned int index) { verifyIndex(index); ThisLocker lock(m_mutex); m_metaDataChanged = true; const uint hash = itemFromIndex(index)->hash(); const ushort bucket = (index >> 16); //Apart from removing the item itself, we may have to recreate the nextBucketForHash link, so we need the previous bucket MyBucket* previousBucketPtr = nullptr; MyBucket* const bucketPtr = walkBucketChain(hash, [bucket, &previousBucketPtr](ushort bucketIdx, MyBucket* bucketPtr) -> MyBucket* { if (bucket != bucketIdx) { previousBucketPtr = bucketPtr; return nullptr; } return bucketPtr; // found bucket, stop looking }); //Make sure the index was reachable through the hash chain Q_ASSERT(bucketPtr); --m_statItemCount; bucketPtr->deleteItem(index, hash, *this); /** * Now check whether the link root/previousBucketNumber -> bucket is still needed. */ if (!previousBucketPtr) { // This bucket is linked in the m_firstBucketForHash array, find the next clashing bucket in the chain // There may be items in the chain that clash only with MyBucket::NextBucketHashSize, skipped here m_firstBucketForHash[hash % bucketHashSize] = walkBucketChain(hash, [hash](ushort bucketIdx, MyBucket *bucketPtr){ if (bucketPtr->hasClashingItem(hash, bucketHashSize)) { return bucketIdx; } return static_cast(0); }); } else if(!bucketPtr->hasClashingItem(hash, MyBucket::NextBucketHashSize)) { // TODO: Skip clashing items reachable from m_firstBucketForHash // (see note in usePermissiveModuloWhenRemovingClashLinks() test) ENSURE_REACHABLE(bucket); previousBucketPtr->setNextBucketForHash(hash, bucketPtr->nextBucketForHash(hash)); Q_ASSERT(m_buckets[bucketPtr->nextBucketForHash(hash)] != previousBucketPtr); } ENSURE_REACHABLE(bucket); if(bucketPtr->monsterBucketExtent()) { //Convert the monster-bucket back to multiple normal buckets, and put them into the free list uint newBuckets = bucketPtr->monsterBucketExtent()+1; Q_ASSERT(bucketPtr->isEmpty()); if (!previousBucketPtr) { // see https://bugs.kde.org/show_bug.cgi?id=272408 // the monster bucket will be deleted and new smaller ones created // the next bucket for this hash is invalid anyways as done above // but calling the below unconditionally leads to other issues... bucketPtr->setNextBucketForHash(hash, 0); } convertMonsterBucket(bucket, 0); for(uint created = bucket; created < bucket + newBuckets; ++created) { putIntoFreeList(created, bucketForIndex(created)); #ifdef DEBUG_MONSTERBUCKETS Q_ASSERT(m_freeSpaceBuckets.indexOf(created) != -1); #endif } }else{ putIntoFreeList(bucket, bucketPtr); } } ///This returns an editable version of the item. @warning: Never change an entry that affects the hash, ///or the equals(..) function. That would completely destroy the consistency. ///@param index The index. It must be valid(match an existing item), and nonzero. ///@warning If you use this, make sure you lock mutex() before calling, /// and hold it until you're ready using/changing the data.. typedef DynamicItem MyDynamicItem; MyDynamicItem dynamicItemFromIndex(unsigned int index) { verifyIndex(index); ThisLocker lock(m_mutex); unsigned short bucket = (index >> 16); MyBucket* bucketPtr = m_buckets.at(bucket); if(!bucketPtr) { initializeBucket(bucket); bucketPtr = m_buckets.at(bucket); } bucketPtr->prepareChange(); unsigned short indexInBucket = index & 0xffff; return MyDynamicItem(const_cast(bucketPtr->itemFromIndex(indexInBucket)), bucketPtr->data(), bucketPtr->dataSize()); } ///This returns an editable version of the item. @warning: Never change an entry that affects the hash, ///or the equals(..) function. That would completely destroy the consistency. ///@param index The index. It must be valid(match an existing item), and nonzero. ///@warning If you use this, make sure you lock mutex() before calling, /// and hold it until you're ready using/changing the data.. ///@warning If you change contained complex items that depend on reference-counting, you /// must use dynamicItemFromIndex(..) instead of dynamicItemFromIndexSimple(..) Item* dynamicItemFromIndexSimple(unsigned int index) { verifyIndex(index); ThisLocker lock(m_mutex); unsigned short bucket = (index >> 16); MyBucket* bucketPtr = m_buckets.at(bucket); if(!bucketPtr) { initializeBucket(bucket); bucketPtr = m_buckets.at(bucket); } bucketPtr->prepareChange(); unsigned short indexInBucket = index & 0xffff; return const_cast(bucketPtr->itemFromIndex(indexInBucket)); } ///@param index The index. It must be valid(match an existing item), and nonzero. - ///@param dynamic will be applied to the item. const Item* itemFromIndex(unsigned int index) const { verifyIndex(index); ThisLocker lock(m_mutex); unsigned short bucket = (index >> 16); const MyBucket* bucketPtr = m_buckets.at(bucket); if(!bucketPtr) { initializeBucket(bucket); bucketPtr = m_buckets.at(bucket); } unsigned short indexInBucket = index & 0xffff; return bucketPtr->itemFromIndex(indexInBucket); } struct Statistics { Statistics() : loadedBuckets(-1), currentBucket(-1), usedMemory(-1), loadedMonsterBuckets(-1), usedSpaceForBuckets(-1), freeSpaceInBuckets(-1), lostSpace(-1), freeUnreachableSpace(-1), hashClashedItems(-1), totalItems(-1), hashSize(-1), hashUse(-1), averageInBucketHashSize(-1), averageInBucketUsedSlotCount(-1), averageInBucketSlotChainLength(-1), longestInBucketChain(-1), longestNextBucketChain(-1), totalBucketFollowerSlots(-1), averageNextBucketForHashSequenceLength(-1) { } uint loadedBuckets; uint currentBucket; uint usedMemory; uint loadedMonsterBuckets; uint usedSpaceForBuckets; uint freeSpaceInBuckets; uint lostSpace; uint freeUnreachableSpace; uint hashClashedItems; uint totalItems; uint emptyBuckets; uint hashSize; //How big the hash is uint hashUse; //How many slots in the hash are used uint averageInBucketHashSize; uint averageInBucketUsedSlotCount; float averageInBucketSlotChainLength; uint longestInBucketChain; uint longestNextBucketChain; uint totalBucketFollowerSlots; //Total count of used slots in the nextBucketForHash structure float averageNextBucketForHashSequenceLength; //Average sequence length of a nextBucketForHash sequence(If not empty) QString print() const { QString ret; ret += QStringLiteral("loaded buckets: %1 current bucket: %2 used memory: %3 loaded monster buckets: %4").arg(loadedBuckets).arg(currentBucket).arg(usedMemory).arg(loadedMonsterBuckets); ret += QStringLiteral("\nbucket hash clashed items: %1 total items: %2").arg(hashClashedItems).arg(totalItems); ret += QStringLiteral("\nused space for buckets: %1 free space in buckets: %2 lost space: %3").arg(usedSpaceForBuckets).arg(freeSpaceInBuckets).arg(lostSpace); ret += QStringLiteral("\nfree unreachable space: %1 empty buckets: %2").arg(freeUnreachableSpace).arg(emptyBuckets); ret += QStringLiteral("\nhash size: %1 hash slots used: %2").arg(hashSize).arg(hashUse); ret += QStringLiteral("\naverage in-bucket hash size: %1 average in-bucket used hash slot count: %2 average in-bucket slot chain length: %3 longest in-bucket follower chain: %4").arg(averageInBucketHashSize).arg(averageInBucketUsedSlotCount).arg(averageInBucketSlotChainLength).arg(longestInBucketChain); ret += QStringLiteral("\ntotal count of used next-bucket-for-hash slots: %1 average next-bucket-for-hash sequence length: %2 longest next-bucket chain: %3").arg(totalBucketFollowerSlots).arg(averageNextBucketForHashSequenceLength).arg(longestNextBucketChain); return ret; } operator QString() const { return print(); } }; QString printStatistics() const override { return statistics().print(); } Statistics statistics() const { Statistics ret; uint loadedBuckets = 0; for(int a = 0; a < m_buckets.size(); ++a) if(m_buckets[a]) ++loadedBuckets; #ifdef DEBUG_MONSTERBUCKETS for(int a = 0; a < m_freeSpaceBuckets.size(); ++a) { if(a > 0) { uint prev = a-1; uint prevLargestFree = bucketForIndex(m_freeSpaceBuckets[prev])->largestFreeSize(); uint largestFree = bucketForIndex(m_freeSpaceBuckets[a])->largestFreeSize(); Q_ASSERT( (prevLargestFree < largestFree) || (prevLargestFree == largestFree && m_freeSpaceBuckets[prev] < m_freeSpaceBuckets[a]) ); } } #endif ret.hashSize = bucketHashSize; ret.hashUse = 0; for(uint a = 0; a < bucketHashSize; ++a) if(m_firstBucketForHash[a]) ++ret.hashUse; ret.emptyBuckets = 0; uint loadedMonsterBuckets = 0; for(int a = 0; a < m_buckets.size(); ++a) if(m_buckets[a] && m_buckets[a]->monsterBucketExtent()) loadedMonsterBuckets += m_buckets[a]->monsterBucketExtent()+1; uint usedBucketSpace = MyBucket::DataSize * m_currentBucket; uint freeBucketSpace = 0, freeUnreachableSpace = 0; uint lostSpace = 0; //Should be zero, else something is wrong uint totalInBucketHashSize = 0, totalInBucketUsedSlotCount = 0, totalInBucketChainLengths = 0; uint bucketCount = 0; ret.totalBucketFollowerSlots = 0; ret.averageNextBucketForHashSequenceLength = 0; ret.longestNextBucketChain = 0; ret.longestInBucketChain = 0; for(int a = 1; a < m_currentBucket+1; ++a) { MyBucket* bucket = bucketForIndex(a); if(bucket) { ++bucketCount; bucket->countFollowerIndexLengths(totalInBucketUsedSlotCount, totalInBucketChainLengths, totalInBucketHashSize, ret.longestInBucketChain); for(uint aa = 0; aa < MyBucket::NextBucketHashSize; ++aa) { uint length = 0; uint next = bucket->nextBucketForHash(aa); if(next) { ++ret.totalBucketFollowerSlots; while(next) { ++length; ++ret.averageNextBucketForHashSequenceLength; next = bucketForIndex(next)->nextBucketForHash(aa); } } if(length > ret.longestNextBucketChain) { // qDebug() << "next-bucket-chain in" << a << aa << ":" << length; ret.longestNextBucketChain = length; } } uint bucketFreeSpace = bucket->totalFreeItemsSize() + bucket->available(); freeBucketSpace += bucketFreeSpace; if(m_freeSpaceBuckets.indexOf(a) == -1) freeUnreachableSpace += bucketFreeSpace; if(bucket->isEmpty()) { ++ret.emptyBuckets; Q_ASSERT(!bucket->monsterBucketExtent()); #ifdef DEBUG_MONSTERBUCKETS Q_ASSERT(m_freeSpaceBuckets.contains(a)); #endif } lostSpace += bucket->lostSpace(); a += bucket->monsterBucketExtent(); } } if(ret.totalBucketFollowerSlots) ret.averageNextBucketForHashSequenceLength /= ret.totalBucketFollowerSlots; ret.loadedBuckets = loadedBuckets; ret.currentBucket = m_currentBucket; ret.usedMemory = usedMemory(); ret.loadedMonsterBuckets = loadedMonsterBuckets; ret.hashClashedItems = m_statBucketHashClashes; ret.totalItems = m_statItemCount; ret.usedSpaceForBuckets = usedBucketSpace; ret.freeSpaceInBuckets = freeBucketSpace; ret.lostSpace = lostSpace; ret.freeUnreachableSpace = freeUnreachableSpace; ret.averageInBucketHashSize = totalInBucketHashSize / bucketCount; ret.averageInBucketUsedSlotCount = totalInBucketUsedSlotCount / bucketCount; ret.averageInBucketSlotChainLength = float(totalInBucketChainLengths) / totalInBucketUsedSlotCount; //If m_statBucketHashClashes is high, the bucket-hash needs to be bigger return ret; } uint usedMemory() const { uint used = 0; for(int a = 0; a < m_buckets.size(); ++a) { if(m_buckets[a]) { used += m_buckets[a]->usedMemory(); } } return used; } ///This can be used to safely iterate through all items in the repository ///@param visitor Should be an instance of a class that has a bool operator()(const Item*) member function, /// that returns whether more items are wanted. ///@param onlyInMemory If this is true, only items are visited that are currently in memory. template void visitAllItems(Visitor& visitor, bool onlyInMemory = false) const { ThisLocker lock(m_mutex); for(int a = 1; a <= m_currentBucket; ++a) { if(!onlyInMemory || m_buckets.at(a)) { if(bucketForIndex(a) && !bucketForIndex(a)->visitAllItems(visitor)) return; } } } ///Synchronizes the state on disk to the one in memory, and does some memory-management. ///Should be called on a regular basis. Can be called centrally from the global item repository registry. virtual void store() override { QMutexLocker lock(m_mutex); if(m_file) { if(!m_file->open( QFile::ReadWrite ) || !m_dynamicFile->open( QFile::ReadWrite )) { qFatal("cannot re-open repository file for storing"); return; } for(int a = 0; a < m_buckets.size(); ++a) { if(m_buckets[a]) { if(m_buckets[a]->changed()) { storeBucket(a); } if(m_unloadingEnabled) { const int unloadAfterTicks = 2; if(m_buckets[a]->lastUsed() > unloadAfterTicks) { delete m_buckets[a]; m_buckets[a] = 0; }else{ m_buckets[a]->tick(); } } } } if(m_metaDataChanged) { Q_ASSERT(m_dynamicFile); m_file->seek(0); m_file->write((char*)&m_repositoryVersion, sizeof(uint)); uint hashSize = bucketHashSize; m_file->write((char*)&hashSize, sizeof(uint)); uint itemRepositoryVersion = staticItemRepositoryVersion(); m_file->write((char*)&itemRepositoryVersion, sizeof(uint)); m_file->write((char*)&m_statBucketHashClashes, sizeof(uint)); m_file->write((char*)&m_statItemCount, sizeof(uint)); const uint bucketCount = static_cast(m_buckets.size()); m_file->write((char*)&bucketCount, sizeof(uint)); m_file->write((char*)&m_currentBucket, sizeof(uint)); m_file->write((char*)m_firstBucketForHash, sizeof(short unsigned int) * bucketHashSize); Q_ASSERT(m_file->pos() == BucketStartOffset); m_dynamicFile->seek(0); const uint freeSpaceBucketsSize = static_cast(m_freeSpaceBuckets.size()); m_dynamicFile->write((char*)&freeSpaceBucketsSize, sizeof(uint)); m_dynamicFile->write((char*)m_freeSpaceBuckets.data(), sizeof(uint) * freeSpaceBucketsSize); } //To protect us from inconsistency due to crashes. flush() is not enough. We need to close. m_file->close(); m_dynamicFile->close(); Q_ASSERT(!m_file->isOpen()); Q_ASSERT(!m_dynamicFile->isOpen()); } } ///This mutex is used for the thread-safe locking when threadSafe is true. Even if threadSafe is false, it is ///always locked before storing to or loading from disk. ///@warning If threadSafe is false, and you sometimes call store() from within another thread(As happens in duchain), /// you must always make sure that this mutex is locked before you access this repository. /// Else you will get crashes and inconsistencies. /// In KDevelop This means: Make sure you _always_ lock this mutex before accessing the repository. QMutex* mutex() const { return m_mutex; } ///With this, you can replace the internal mutex with another one. void setMutex(QMutex* mutex) { m_mutex = mutex; } virtual QString repositoryName() const override { return m_repositoryName; } private: uint createIndex(ushort bucketIndex, ushort indexInBucket) { //Combine the index in the bucket, and the bucket number into one index const uint index = (bucketIndex << 16) + indexInBucket; verifyIndex(index); return index; } /** - * Walks through all buckets clashing with @param hash + * Walks through all buckets clashing with @p hash * * Will return the value returned by the lambda, returning early if truthy */ template auto walkBucketChain(unsigned int hash, const Visitor& visitor) const -> decltype(visitor(0, nullptr)) { unsigned short bucketIndex = m_firstBucketForHash[hash % bucketHashSize]; while (bucketIndex) { auto* bucketPtr = m_buckets.at(bucketIndex); if (!bucketPtr) { initializeBucket(bucketIndex); bucketPtr = m_buckets.at(bucketIndex); } if (auto visitResult = visitor(bucketIndex, bucketPtr)) { return visitResult; } bucketIndex = bucketPtr->nextBucketForHash(hash); } return {}; } ///Makes sure the order within m_freeSpaceBuckets is correct, after largestFreeSize has been changed for m_freeSpaceBuckets[index]. ///If too few space is free within the given bucket, it is removed from m_freeSpaceBuckets. void updateFreeSpaceOrder(uint index) { m_metaDataChanged = true; unsigned int* freeSpaceBuckets = m_freeSpaceBuckets.data(); Q_ASSERT(index < static_cast(m_freeSpaceBuckets.size())); MyBucket* bucketPtr = bucketForIndex(freeSpaceBuckets[index]); unsigned short largestFreeSize = bucketPtr->largestFreeSize(); if(largestFreeSize == 0 || (bucketPtr->freeItemCount() <= MyBucket::MaxFreeItemsForHide && largestFreeSize <= MyBucket::MaxFreeSizeForHide)) { //Remove the item from freeSpaceBuckets m_freeSpaceBuckets.remove(index); }else{ while(1) { int prev = index-1; int next = index+1; if(prev >= 0 && (bucketForIndex(freeSpaceBuckets[prev])->largestFreeSize() > largestFreeSize || (bucketForIndex(freeSpaceBuckets[prev])->largestFreeSize() == largestFreeSize && freeSpaceBuckets[index] < freeSpaceBuckets[prev])) ) { //This item should be behind the successor, either because it has a lower largestFreeSize, or because the index is lower uint oldPrevValue = freeSpaceBuckets[prev]; freeSpaceBuckets[prev] = freeSpaceBuckets[index]; freeSpaceBuckets[index] = oldPrevValue; index = prev; }else if(next < m_freeSpaceBuckets.size() && (bucketForIndex(freeSpaceBuckets[next])->largestFreeSize() < largestFreeSize || (bucketForIndex(freeSpaceBuckets[next])->largestFreeSize() == largestFreeSize && freeSpaceBuckets[index] > freeSpaceBuckets[next]))) { //This item should be behind the successor, either because it has a higher largestFreeSize, or because the index is higher uint oldNextValue = freeSpaceBuckets[next]; freeSpaceBuckets[next] = freeSpaceBuckets[index]; freeSpaceBuckets[index] = oldNextValue; index = next; }else { break; } } } } ///Does conversion from monster-bucket to normal bucket and from normal bucket to monster-bucket ///The bucket @param bucketNumber must already be loaded and empty. the "extent" buckets behind must also be loaded, ///and also be empty. ///The created buckets are not registered anywhere. When converting from monster-bucket to normal bucket, ///oldExtent+1 normal buckets are created, that must be registered somewhere. ///@warning During conversion, all the touched buckets are deleted and re-created ///@param extent When this is zero, the bucket is converted from monster-bucket to normal bucket. /// When it is nonzero, it is converted to a monster-bucket. MyBucket* convertMonsterBucket(int bucketNumber, int extent) { Q_ASSERT(bucketNumber); MyBucket* bucketPtr = m_buckets.at(bucketNumber); if(!bucketPtr) { initializeBucket(bucketNumber); bucketPtr = m_buckets.at(bucketNumber); } if(extent) { //Convert to monster-bucket #ifdef DEBUG_MONSTERBUCKETS for(int index = bucketNumber; index < bucketNumber + 1 + extent; ++index) { Q_ASSERT(bucketPtr->isEmpty()); Q_ASSERT(!bucketPtr->monsterBucketExtent()); Q_ASSERT(m_freeSpaceBuckets.indexOf(index) == -1); } #endif for(int index = bucketNumber; index < bucketNumber + 1 + extent; ++index) deleteBucket(index); m_buckets[bucketNumber] = new MyBucket(); m_buckets[bucketNumber]->initialize(extent); #ifdef DEBUG_MONSTERBUCKETS for(uint index = bucketNumber+1; index < bucketNumber + 1 + extent; ++index) { Q_ASSERT(!m_buckets[index]); } #endif }else{ Q_ASSERT(bucketPtr->monsterBucketExtent()); Q_ASSERT(bucketPtr->isEmpty()); const int oldExtent = bucketPtr->monsterBucketExtent(); deleteBucket(bucketNumber); //Delete the monster-bucket for(int index = bucketNumber; index < bucketNumber + 1 + oldExtent; ++index) { Q_ASSERT(!m_buckets[index]); m_buckets[index] = new MyBucket(); m_buckets[index]->initialize(0); Q_ASSERT(!m_buckets[index]->monsterBucketExtent()); } } return m_buckets[bucketNumber]; } MyBucket* bucketForIndex(short unsigned int index) const { MyBucket* bucketPtr = m_buckets.at(index); if(!bucketPtr) { initializeBucket(index); bucketPtr = m_buckets.at(index); } return bucketPtr; } virtual bool open(const QString& path) override { QMutexLocker lock(m_mutex); close(); //qDebug() << "opening repository" << m_repositoryName << "at" << path; QDir dir(path); m_file = new QFile(dir.absoluteFilePath( m_repositoryName )); m_dynamicFile = new QFile(dir.absoluteFilePath( m_repositoryName + QLatin1String("_dynamic") )); if(!m_file->open( QFile::ReadWrite ) || !m_dynamicFile->open( QFile::ReadWrite ) ) { delete m_file; m_file = 0; delete m_dynamicFile; m_dynamicFile = 0; return false; } m_metaDataChanged = true; if(m_file->size() == 0) { m_file->resize(0); m_file->write((char*)&m_repositoryVersion, sizeof(uint)); uint hashSize = bucketHashSize; m_file->write((char*)&hashSize, sizeof(uint)); uint itemRepositoryVersion = staticItemRepositoryVersion(); m_file->write((char*)&itemRepositoryVersion, sizeof(uint)); m_statBucketHashClashes = m_statItemCount = 0; m_file->write((char*)&m_statBucketHashClashes, sizeof(uint)); m_file->write((char*)&m_statItemCount, sizeof(uint)); m_buckets.resize(10); m_buckets.fill(0); uint bucketCount = m_buckets.size(); m_file->write((char*)&bucketCount, sizeof(uint)); memset(m_firstBucketForHash, 0, bucketHashSize * sizeof(short unsigned int)); m_currentBucket = 1; //Skip the first bucket, we won't use it so we have the zero indices for special purposes m_file->write((char*)&m_currentBucket, sizeof(uint)); m_file->write((char*)m_firstBucketForHash, sizeof(short unsigned int) * bucketHashSize); //We have completely initialized the file now if(m_file->pos() != BucketStartOffset) { KMessageBox::error(0, i18n("Failed writing to %1, probably the disk is full", m_file->fileName())); abort(); } const uint freeSpaceBucketsSize = 0; m_dynamicFile->write((char*)&freeSpaceBucketsSize, sizeof(uint)); m_freeSpaceBuckets.clear(); }else{ m_file->close(); bool res = m_file->open( QFile::ReadOnly ); //Re-open in read-only mode, so we create a read-only m_fileMap VERIFY(res); //Check that the version is correct uint storedVersion = 0, hashSize = 0, itemRepositoryVersion = 0; m_file->read((char*)&storedVersion, sizeof(uint)); m_file->read((char*)&hashSize, sizeof(uint)); m_file->read((char*)&itemRepositoryVersion, sizeof(uint)); m_file->read((char*)&m_statBucketHashClashes, sizeof(uint)); m_file->read((char*)&m_statItemCount, sizeof(uint)); if(storedVersion != m_repositoryVersion || hashSize != bucketHashSize || itemRepositoryVersion != staticItemRepositoryVersion()) { qDebug() << "repository" << m_repositoryName << "version mismatch in" << m_file->fileName() << ", stored: version " << storedVersion << "hashsize" << hashSize << "repository-version" << itemRepositoryVersion << " current: version" << m_repositoryVersion << "hashsize" << bucketHashSize << "repository-version" << staticItemRepositoryVersion(); delete m_file; m_file = 0; delete m_dynamicFile; m_dynamicFile = 0; return false; } m_metaDataChanged = false; uint bucketCount = 0; m_file->read((char*)&bucketCount, sizeof(uint)); m_buckets.resize(bucketCount); m_file->read((char*)&m_currentBucket, sizeof(uint)); m_file->read((char*)m_firstBucketForHash, sizeof(short unsigned int) * bucketHashSize); Q_ASSERT(m_file->pos() == BucketStartOffset); uint freeSpaceBucketsSize = 0; m_dynamicFile->read((char*)&freeSpaceBucketsSize, sizeof(uint)); m_freeSpaceBuckets.resize(freeSpaceBucketsSize); m_dynamicFile->read((char*)m_freeSpaceBuckets.data(), sizeof(uint) * freeSpaceBucketsSize); } m_fileMapSize = 0; m_fileMap = 0; #ifdef ITEMREPOSITORY_USE_MMAP_LOADING if(m_file->size() > BucketStartOffset){ m_fileMap = m_file->map(BucketStartOffset, m_file->size() - BucketStartOffset); Q_ASSERT(m_file->isOpen()); Q_ASSERT(m_file->size() >= BucketStartOffset); if(m_fileMap){ m_fileMapSize = m_file->size() - BucketStartOffset; }else{ qWarning() << "mapping" << m_file->fileName() << "FAILED!"; } } #endif //To protect us from inconsistency due to crashes. flush() is not enough. m_file->close(); m_dynamicFile->close(); return true; } ///@warning by default, this does not store the current state to disk. virtual void close(bool doStore = false) override { if(doStore) store(); if(m_file) m_file->close(); delete m_file; m_file = 0; m_fileMap = 0; m_fileMapSize = 0; if(m_dynamicFile) m_dynamicFile->close(); delete m_dynamicFile; m_dynamicFile = 0; qDeleteAll(m_buckets); m_buckets.clear(); memset(m_firstBucketForHash, 0, bucketHashSize * sizeof(short unsigned int)); } struct AllItemsReachableVisitor { AllItemsReachableVisitor(ItemRepository* rep) : repository(rep) { } bool operator()(const Item* item) { return repository->itemReachable(item); } ItemRepository* repository; }; //Returns whether the given item is reachable through its hash bool itemReachable(const Item* item) const { const uint hash = item->hash(); return walkBucketChain(hash, [=](ushort /*bucketIndex*/, const MyBucket* bucketPtr) { return bucketPtr->itemReachable(item, hash); }); } //Returns true if all items in the given bucket are reachable through their hashes bool allItemsReachable(unsigned short bucket) { if(!bucket) return true; MyBucket* bucketPtr = bucketForIndex(bucket); AllItemsReachableVisitor visitor(this); return bucketPtr->visitAllItems(visitor); } virtual int finalCleanup() override { ThisLocker lock(m_mutex); int changed = 0; for(int a = 1; a <= m_currentBucket; ++a) { MyBucket* bucket = bucketForIndex(a); if(bucket && bucket->dirty()) { ///@todo Faster dirty check, without loading bucket changed += bucket->finalCleanup(*this); } a += bucket->monsterBucketExtent(); //Skip buckets that are attached as tail to monster-buckets } return changed; } inline void initializeBucket(int bucketNumber) const { Q_ASSERT(bucketNumber); #ifdef DEBUG_MONSTERBUCKETS for(uint offset = 1; offset < 5; ++offset) { int test = bucketNumber - offset; if(test >= 0 && m_buckets[test]) { Q_ASSERT(m_buckets[test]->monsterBucketExtent() < offset); } } #endif if(!m_buckets[bucketNumber]) { m_buckets[bucketNumber] = new MyBucket(); bool doMMapLoading = (bool)m_fileMap; uint offset = ((bucketNumber-1) * MyBucket::DataSize); if(m_file && offset < m_fileMapSize && doMMapLoading && *reinterpret_cast(m_fileMap + offset) == 0) { // qDebug() << "loading bucket mmap:" << bucketNumber; m_buckets[bucketNumber]->initializeFromMap(reinterpret_cast(m_fileMap + offset)); } else if(m_file) { //Either memory-mapping is disabled, or the item is not in the existing memory-map, //so we have to load it the classical way. bool res = m_file->open( QFile::ReadOnly ); if(offset + BucketStartOffset < m_file->size()) { VERIFY(res); offset += BucketStartOffset; m_file->seek(offset); uint monsterBucketExtent; m_file->read((char*)(&monsterBucketExtent), sizeof(unsigned int));; m_file->seek(offset); ///FIXME: use the data here instead of copying it again in prepareChange QByteArray data = m_file->read((1+monsterBucketExtent) * MyBucket::DataSize); m_buckets[bucketNumber]->initializeFromMap(data.data()); m_buckets[bucketNumber]->prepareChange(); }else{ m_buckets[bucketNumber]->initialize(0); } m_file->close(); }else{ m_buckets[bucketNumber]->initialize(0); } }else{ m_buckets[bucketNumber]->initialize(0); } } ///Can only be called on empty buckets void deleteBucket(int bucketNumber) { Q_ASSERT(bucketForIndex(bucketNumber)->isEmpty()); Q_ASSERT(bucketForIndex(bucketNumber)->noNextBuckets()); delete m_buckets[bucketNumber]; m_buckets[bucketNumber] = 0; } //m_file must be opened void storeBucket(int bucketNumber) const { if(m_file && m_buckets[bucketNumber]) { m_buckets[bucketNumber]->store(m_file, BucketStartOffset + (bucketNumber-1) * MyBucket::DataSize); } } ///Returns whether @param mustFindBucket was found ///If mustFindBucket is zero, the whole chain is just walked. This is good for debugging for infinite recursion. bool walkBucketLinks(uint checkBucket, uint hash, uint mustFindBucket = 0) const { bool found = false; while(checkBucket) { if(checkBucket == mustFindBucket) found = true; checkBucket = bucketForIndex(checkBucket)->nextBucketForHash(hash); } return found || (mustFindBucket == 0); } ///Computes the bucket where the chains opened by the buckets @param mainHead and @param intersectorHead ///with hash @param hash meet each other. ///@return QPair hashChainIntersection(uint mainHead, uint intersectorHead, uint hash) const { uint previous = 0; uint current = mainHead; while(current) { ///@todo Make this more efficient if(walkBucketLinks(intersectorHead, hash, current)) return qMakePair(previous, current); previous = current; current = bucketForIndex(current)->nextBucketForHash(hash); } return qMakePair(0u, 0u); } void putIntoFreeList(unsigned short bucket, MyBucket* bucketPtr) { Q_ASSERT(!bucketPtr->monsterBucketExtent()); int indexInFree = m_freeSpaceBuckets.indexOf(bucket); if(indexInFree == -1 && (bucketPtr->freeItemCount() >= MyBucket::MinFreeItemsForReuse || bucketPtr->largestFreeSize() >= MyBucket::MinFreeSizeForReuse)) { //Add the bucket to the list of buckets from where to re-assign free space //We only do it when a specific threshold of empty items is reached, because that way items can stay "somewhat" semantically ordered. Q_ASSERT(bucketPtr->largestFreeSize()); int insertPos; for(insertPos = 0; insertPos < m_freeSpaceBuckets.size(); ++insertPos) { if(bucketForIndex(m_freeSpaceBuckets[insertPos])->largestFreeSize() > bucketPtr->largestFreeSize()) break; } m_freeSpaceBuckets.insert(insertPos, bucket); updateFreeSpaceOrder(insertPos); }else if(indexInFree != -1) { ///Re-order so the order in m_freeSpaceBuckets is correct(sorted by largest free item size) updateFreeSpaceOrder(indexInFree); } #ifdef DEBUG_MONSTERBUCKETS if(bucketPtr->isEmpty()) { Q_ASSERT(m_freeSpaceBuckets.contains(bucket)); } #endif } void verifyIndex(uint index) const { // We don't use zero indices Q_ASSERT(index); int bucket = (index >> 16); // nor zero buckets Q_ASSERT(bucket); Q_ASSERT_X(bucket < m_buckets.size(), Q_FUNC_INFO, qPrintable(QStringLiteral("index %1 gives invalid bucket number %2, current count is: %3") .arg(index) .arg(bucket) .arg(m_buckets.size()))); // don't trigger compile warnings in release mode Q_UNUSED(bucket); Q_UNUSED(index); } bool m_metaDataChanged; mutable QMutex m_ownMutex; mutable QMutex* m_mutex; QString m_repositoryName; mutable int m_currentBucket; //List of buckets that have free space available that can be assigned. Sorted by size: Smallest space first. Second order sorting: Bucket index QVector m_freeSpaceBuckets; mutable QVector m_buckets; uint m_statBucketHashClashes, m_statItemCount; //Maps hash-values modulo 1< * * 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_REFERENCECOUNTING_H #define KDEVPLATFORM_REFERENCECOUNTING_H #include "serializationexport.h" #include #include #include //When this is enabled, the duchain unloading is disabled as well, and you should start //with a cleared ~/.kdevduchain // #define TEST_REFERENCE_COUNTING namespace KDevelop { template const T* make_const(const T* argument) { return argument; } ///Since shouldDoDUChainReferenceCounting is called extremely often, we export some internals into the header here, ///so the reference-counting code can be inlined. KDEVPLATFORMSERIALIZATION_EXPORT extern bool doReferenceCounting; KDEVPLATFORMSERIALIZATION_EXPORT extern QMutex refCountingLock; KDEVPLATFORMSERIALIZATION_EXPORT extern QMap >* refCountingRanges; KDEVPLATFORMSERIALIZATION_EXPORT extern bool refCountingHasAdditionalRanges; KDEVPLATFORMSERIALIZATION_EXPORT extern void* refCountingFirstRangeStart; KDEVPLATFORMSERIALIZATION_EXPORT extern QPair refCountingFirstRangeExtent; KDEVPLATFORMSERIALIZATION_EXPORT void initReferenceCounting(); ///@internal The spin-lock ,must already be locked inline bool shouldDoDUChainReferenceCountingInternal(void* item) { auto it = make_const(refCountingRanges)->upperBound(item); if (it != refCountingRanges->constBegin()) { --it; return ((char*)it.key()) <= (char*)item && (char*)item < ((char*)it.key()) + it.value().first; } return false; } ///This is used by indexed items to decide whether they should do reference-counting inline bool shouldDoDUChainReferenceCounting(void* item) { if(!doReferenceCounting) //Fast path, no place has been marked for reference counting, 99% of cases return false; QMutexLocker lock(&refCountingLock); if(refCountingFirstRangeStart && (((char*)refCountingFirstRangeStart) <= (char*)item) && ((char*)item < ((char*)refCountingFirstRangeStart) + refCountingFirstRangeExtent.first)) return true; if(refCountingHasAdditionalRanges) return shouldDoDUChainReferenceCountingInternal(item); else return false; } ///Enable reference-counting for the given range ///You should only enable the reference-counting for the time it's really needed, ///and it always has to be enabled too when the items are deleted again, else ///it will lead to inconsistencies in the repository. ///@warning If you are not working on the duchain internal storage mechanism, you should ///not care about this stuff at all. ///@param start Position where to start the reference-counting ///@param size Size of the area in bytes KDEVPLATFORMSERIALIZATION_EXPORT void enableDUChainReferenceCounting(void* start, unsigned int size); ///Must be called as often as enableDUChainReferenceCounting, with the same ranges ///Must never be called for the same range twice, and not for overlapping ranges - ///@param start Position where to start the reference-counting - ///@param size Size of the area in bytes + ///@param start Position where the reference-counting was started KDEVPLATFORMSERIALIZATION_EXPORT void disableDUChainReferenceCounting(void* start); ///Use this as local variable within the object that maintains the reference-count, ///and use struct ReferenceCountManager { #ifndef TEST_REFERENCE_COUNTING inline void increase(uint& ref, uint /*targetId*/) { ++ref; } inline void decrease(uint& ref, uint /*targetId*/) { Q_ASSERT(ref); --ref; } #else ReferenceCountManager(); ~ReferenceCountManager(); ReferenceCountManager(const ReferenceCountManager& rhs); ReferenceCountManager& operator=(const ReferenceCountManager& rhs); void increase(uint& ref, uint targetId); void decrease(uint& ref, uint targetId); // bool hasReferenceCount() const; uint m_id; #endif }; } #endif diff --git a/shell/documentcontroller.h b/shell/documentcontroller.h index b28c129a72..3dd6815d26 100644 --- a/shell/documentcontroller.h +++ b/shell/documentcontroller.h @@ -1,183 +1,183 @@ /* This document is part of the KDE project Copyright 2002 Matthias Hoelzer-Kluepfel Copyright 2002 Bernd Gehrmann Copyright 2003 Roberto Raggi Copyright 2003 Hamish Rodda Copyright 2003 Harald Fernengel Copyright 2003 Jens Dagerbo Copyright 2005 Adam Treat Copyright 2004-2007 Alexander Dymo Copyright 2007 Andreas Pakulat 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 document COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDEVPLATFORM_DOCUMENTCONTROLLER_H #define KDEVPLATFORM_DOCUMENTCONTROLLER_H #include #include #include "shellexport.h" namespace KTextEditor { class View; } namespace Sublime { class Document; class Area; class AreaIndex; } namespace KDevelop { class ProjectFileItem; class IProject; class MainWindow; /** * \short Interface to control open documents. * The document controller manages open documents in the IDE. * Open documents are usually editors, GUI designers, html documentation etc. * * Please note that this interface gives access to documents and not to their views. * It is possible that more than 1 view is shown in KDevelop for a document. */ class KDEVPLATFORMSHELL_EXPORT DocumentController: public IDocumentController { Q_OBJECT Q_CLASSINFO( "D-Bus Interface", "org.kdevelop.DocumentController" ) public: /**Constructor. @param parent The parent object.*/ explicit DocumentController( QObject *parent = nullptr ); ~DocumentController() override; /**Finds the first document object corresponding to a given url. @param url The Url of the document. @return The corresponding document, or null if not found.*/ IDocument* documentForUrl( const QUrl & url ) const override; /**@return The list of open documents*/ QList openDocuments() const override; /**Refers to the document currently active or focused. @return The active document.*/ IDocument* activeDocument() const override; KTextEditor::View* activeTextDocumentView() const override; void activateDocument( IDocument * document, const KTextEditor::Range& range = KTextEditor::Range::invalid() ) override; void registerDocumentForMimetype( const QString&, KDevelop::IDocumentFactory* ) override; /// Request the document controller to save all documents. /// If the \a mode is not IDocument::Silent, ask the user which documents to save. /// Returns false if the user cancels the save dialog. bool saveAllDocuments(IDocument::DocumentSaveMode mode) override; bool saveAllDocumentsForWindow(KParts::MainWindow* mw, IDocument::DocumentSaveMode mode, bool currentAreaOnly = false) override; void initialize(); void cleanup(); virtual QStringList documentTypes() const; QString documentType(Sublime::Document* document) const; using IDocumentController::openDocument; /**checks that url is an url of empty document*/ static bool isEmptyDocumentUrl(const QUrl &url); static QUrl nextEmptyDocumentUrl(); IDocumentFactory* factory(const QString& mime) const override; bool openDocument(IDocument* doc, const KTextEditor::Range& range = KTextEditor::Range::invalid(), DocumentActivationParams activationParams = nullptr, IDocument* buddy = nullptr) override; KTextEditor::Document* globalTextEditorInstance() override; public Q_SLOTS: /**Opens a new or existing document. @param url The full Url of the document to open. If it is empty, a dialog to choose the document will be opened. @param range The location information, if applicable. @param activationParams Indicates whether to fully activate the document. @param buddy The buddy document @return The opened document */ Q_SCRIPTABLE IDocument* openDocument( const QUrl &url, const KTextEditor::Range& range = KTextEditor::Range::invalid(), DocumentActivationParams activationParams = nullptr, const QString& encoding = {}, IDocument* buddy = nullptr ) override; Q_SCRIPTABLE IDocument* openDocumentFromText( const QString& data ) override; - KDevelop::IDocument* openDocument( const QUrl &url, const QString& prefname ) override; + KDevelop::IDocument* openDocument( const QUrl &url, const QString& prefName ) override; virtual void closeDocument( const QUrl &url ); void fileClose(); void slotSaveAllDocuments(); bool closeAllDocuments() override; void closeAllOtherDocuments(); void reloadAllDocuments(); // DBUS-compatible versions of openDocument virtual Q_SCRIPTABLE bool openDocumentSimple( QString url, int line = -1, int column = 0 ); // Opens a list of documents, with optional split-view separators, like: "file1 / [ file2 - fil3 ]" (see kdevplatform_shell_environment.sh) virtual Q_SCRIPTABLE bool openDocumentsSimple( QStringList urls ); virtual Q_SCRIPTABLE bool openDocumentFromTextSimple( QString text ); // If 'target' is empty, returns the currently active document, or // the currently selected project-item if no document is active. // If 'target' is "[selection]", returns the path of the currently active selection. // If 'target' is the name of a project, returns the root-path of that project. // Whenever the returned path corresponds to a directory, a '/.' suffix is appended. Q_SCRIPTABLE QString activeDocumentPath(QString target = {}) const; // Returns all open documents in the current area Q_SCRIPTABLE QStringList activeDocumentPaths() const; void vcsAnnotateCurrentDocument(); private Q_SLOTS: virtual void slotOpenDocument(const QUrl &url); void notifyDocumentClosed(Sublime::Document* doc); private: bool openDocumentsWithSplitSeparators( Sublime::AreaIndex* index, QStringList urlsWithSeparators, bool& isFirstView ); QList visibleDocumentsInWindow(MainWindow* mw) const; QList documentsExclusivelyInWindow(MainWindow* mw, bool currentAreaOnly = false) const; QList modifiedDocuments(const QList& list) const; bool saveSomeDocuments(const QList& list, IDocument::DocumentSaveMode mode) override; void setupActions(); Q_PRIVATE_SLOT(d, void removeDocument(Sublime::Document*)) Q_PRIVATE_SLOT(d, void chooseDocument()) Q_PRIVATE_SLOT(d, void changeDocumentUrl(KDevelop::IDocument*)) friend struct DocumentControllerPrivate; struct DocumentControllerPrivate *d; }; } #endif diff --git a/shell/launchconfiguration.h b/shell/launchconfiguration.h index 8d80a0f670..891a736a8d 100644 --- a/shell/launchconfiguration.h +++ b/shell/launchconfiguration.h @@ -1,102 +1,102 @@ /* This file is part of KDevelop Copyright 2009 Andreas Pakulat 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_LAUNCHCONFIGURATION_H #define KDEVPLATFORM_LAUNCHCONFIGURATION_H #include #include #include "shellexport.h" class QString; namespace KDevelop { class LaunchConfigurationType; class IProject; /** * @copydoc KDevelop::ILaunchConfiguration */ class KDEVPLATFORMSHELL_EXPORT LaunchConfiguration : public QObject, public ILaunchConfiguration { Q_OBJECT public: explicit LaunchConfiguration( KConfigGroup, IProject* = nullptr, QObject* = nullptr ); ~LaunchConfiguration() override; static QString LaunchConfigurationNameEntry(); static QString LaunchConfigurationTypeEntry(); /** * Change the name of this launch configuration * @param name the new name for the launch configuration */ void setName( const QString& name ); /** * Changes the type of this launch configuration. Note that * this removes all existing config values from this configuration - * @param type the id of the new type + * @param typeId the id of the new type */ void setType( const QString& typeId ); /** * @copydoc KDevelop::ILaunchConfiguration::config() */ const KConfigGroup config() const override; /** * @copydoc KDevelop::ILaunchConfiguration::type() */ LaunchConfigurationType* type() const override; /** * @copydoc KDevelop::ILaunchConfiguration::name() */ QString name() const override; /** * @copydoc KDevelop::ILaunchConfiguration::project() */ IProject* project() const override; void save(); QString configGroupName() const; QString launcherForMode( const QString& mode ) const; void setLauncherForMode( const QString& mode, const QString& id ); KConfigGroup config() override; signals: void nameChanged( LaunchConfiguration* ); void typeChanged( LaunchConfigurationType* ); private: KConfigGroup baseGroup; IProject* m_project; LaunchConfigurationType* m_type; }; } #endif diff --git a/shell/launchconfigurationdialog.ui b/shell/launchconfigurationdialog.ui index 51e4ae27c5..b472ef0501 100644 --- a/shell/launchconfigurationdialog.ui +++ b/shell/launchconfigurationdialog.ui @@ -1,157 +1,157 @@ LaunchConfigurationDialog 0 0 643 530 Qt::Horizontal 1 0 false false Add New... QToolButton::InstantPopup Qt::ToolButtonTextBesideIcon Remove Selected Qt::ToolButtonTextBesideIcon Qt::Horizontal 10 21 - + 0 0 Debugger: 0 0 0 0 1 Name 2 0 0 QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok KMessageWidget QFrame
kmessagewidget.h
1
diff --git a/shell/plugincontroller.h b/shell/plugincontroller.h index d5900a3f45..cdcf16ca9b 100644 --- a/shell/plugincontroller.h +++ b/shell/plugincontroller.h @@ -1,185 +1,185 @@ /* This file is part of the KDE project Copyright 2007 Andreas Pakulat Copyright 2004, 2007 Alexander Dymo Copyright 2006 Matt Rogers 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_PLUGINCONTROLLER_H #define KDEVPLATFORM_PLUGINCONTROLLER_H #include #include #include "shellexport.h" namespace KDevelop { class Core; class CorePrivate; class IPlugin; class PluginControllerPrivate; /** * The KDevelop plugin controller. * The Plugin controller is responsible for querying, loading and unloading * available plugins. */ class KDEVPLATFORMSHELL_EXPORT PluginController: public IPluginController { Q_OBJECT friend class Core; friend class CorePrivate; public: explicit PluginController(Core *core); ~PluginController() override; /** * Get the plugin instance based on the ID. The ID should be whatever is * in X-KDE-PluginInfo-Name */ IPlugin* plugin( const QString& ); /** * Get the plugin info for a loaded plugin */ KPluginMetaData pluginInfo( const IPlugin* ) const override; /** * Find the KPluginMetaData structure for the given @p pluginId. */ KPluginMetaData infoForPluginId(const QString &pluginId) const override; /** * Get a list of currently loaded plugins */ QList loadedPlugins() const override; /** * Returns a uniquely specified plugin. If it isn't already loaded, it will be. * @param pluginName the name of the plugin, as given in the X-KDE-PluginInfo-Name property * @returns a pointer to the plugin instance or 0 */ IPlugin * loadPlugin( const QString & pluginName ) override; /** * @brief Unloads the plugin specified by @p plugin * * @param plugin The name of the plugin as specified by the * X-KDE-PluginInfo-Name key of the .desktop file for the plugin */ bool unloadPlugin( const QString & plugin ) override; enum PluginDeletion { Now, Later }; /** * retrieve all plugin infos */ QVector allPluginInfos() const; /** * loads not-yet-loaded plugins and unloads plugins * depending on the configuration in the session\ */ void updateLoadedPlugins(); /** * Queries for the plugin which supports given extension interface. * * All already loaded plugins will be queried and the first one to support the extension interface * will be returned. Any plugin can be an extension, only "ServiceTypes=..." entry is * required in .desktop file for that plugin. * * @param extension The extension interface - * @param pluginname The name of the plugin to load if multiple plugins for the extension exist, corresponds to the X-KDE-PluginInfo-Name + * @param pluginName The name of the plugin to load if multiple plugins for the extension exist, corresponds to the X-KDE-PluginInfo-Name * @return A KDevelop extension plugin for given service type or 0 if no plugin supports it */ IPlugin *pluginForExtension(const QString &extension, const QString &pluginName = {}, const QVariantMap& constraints = QVariantMap()) override; QList allPluginsForExtension(const QString &extension, const QVariantMap& constraints = QVariantMap()) override; QStringList allPluginNames(); QVector queryExtensionPlugins(const QString& extension, const QVariantMap& constraints = QVariantMap()) const override; QList queryPluginsForContextMenuExtensions( KDevelop::Context* context ) const override; QStringList projectPlugins(); void loadProjectPlugins(); void unloadProjectPlugins(); void resetToDefaults(); bool isEnabled(const KPluginMetaData& info) const; private: /** * Directly unload the given \a plugin, either deleting it now or \a deletion. * * \param plugin plugin to unload * \param deletion if true, delete the plugin later, if false, delete it now. */ bool unloadPlugin(IPlugin* plugin, PluginDeletion deletion); /** * @internal * * The internal method for loading plugins. * Called by @ref loadPlugin directly or through the queue for async plugin * loading. */ IPlugin* loadPluginInternal( const QString &pluginId ); /** * Check whether the plugin identified by @p info has unresolved dependencies. * * Assume a plugin depends on the interfaces Foo and Bar. Then, all available enabled * plugins are queried to check whether any fulfills the interfaces. If any of the * interfaces is not found, then it is inserted into @p missing and this method returns * true. Otherwise, @p missing is empty and this method returns true, indicating that all * dependencies can be fulfilled. * * @return true when there are unresolved dependencies, false otherwise. */ bool hasUnresolvedDependencies( const KPluginMetaData& info, QStringList& missing ) const; bool loadDependencies(const KPluginMetaData&, QString& failedPlugin); void loadOptionalDependencies(const KPluginMetaData& info); void cleanup(); virtual void initialize(); private: class PluginControllerPrivate* const d; }; } #endif diff --git a/shell/progresswidget/progressmanager.h b/shell/progresswidget/progressmanager.h index 440e35f5fe..b08bfcfdec 100644 --- a/shell/progresswidget/progressmanager.h +++ b/shell/progresswidget/progressmanager.h @@ -1,479 +1,478 @@ /*************************************************************************** * (C) 2004 Till Adam * * * * 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_PROGRESSMANAGER_H #define KDEVPLATFORM_PROGRESSMANAGER_H #include #include #include #include #include #include namespace Akonadi { class AgentInstance; } namespace KDevelop { class ProgressItem; class ProgressManager; typedef QMap ProgressItemMap; class ProgressItem : public QObject { Q_OBJECT friend class ProgressManager; public: /** * @return The id string which uniquely identifies the operation * represented by this item. */ const QString &id() const { return mId; } /** * @return The parent item of this one, if there is one. */ ProgressItem *parent() const { return mParent.data(); } /** * @return The user visible string to be used to represent this item. */ const QString &label() const { return mLabel; } /** * @param v Set the user visible string identifying this item. */ void setLabel( const QString &v ); /** * @return The string to be used for showing this item's current status. */ const QString &status() const { return mStatus; } /** * Set the string to be used for showing this item's current status. * @param v The status string. */ void setStatus( const QString &v ); /** * @return Whether this item can be canceled. */ bool canBeCanceled() const { return mCanBeCanceled; } /** * @return Whether this item uses secure communication * (Account uses ssl, for example.). */ bool usesCrypto() const { return mUsesCrypto; } /** * Set whether this item uses crypted communication, so listeners * can display a nice crypto icon. * @param v The value. */ void setUsesCrypto( bool v ); /** * @return whether this item uses a busy indicator instead of real progress display */ bool usesBusyIndicator() const { return mUsesBusyIndicator; } /** * Sets whether this item uses a busy indicator instead of real progress for its progress bar. * If it uses a busy indicator, you are still responsible for calling setProgress() from time to * time to update the busy indicator. */ void setUsesBusyIndicator( bool useBusyIndicator ); /** * @return The current progress value of this item in percent. */ unsigned int progress() const { return mProgress; } /** * Set the progress (percentage of completion) value of this item. * @param v The percentage value. */ void setProgress( unsigned int v ); /** * Tell the item it has finished. This will emit progressItemCompleted() * result in the destruction of the item after all slots connected to this * signal have executed. This is the only way to get rid of an item and * needs to be called even if the item is canceled. Don't use the item * after this has been called on it. */ void setComplete(); /** * Reset the progress value of this item to 0 and the status string to * the empty string. */ void reset() { setProgress( 0 ); setStatus( QString() ); mCompleted = 0; } void cancel(); // Often needed values for calculating progress. void setTotalItems( unsigned int v ) { mTotal = v; } unsigned int totalItems() const { return mTotal; } void setCompletedItems( unsigned int v ) { mCompleted = v; } void incCompletedItems( unsigned int v = 1 ) { mCompleted += v; } unsigned int completedItems() const { return mCompleted; } /** * Recalculate progress according to total/completed items and update. */ void updateProgress() { setProgress( mTotal? mCompleted * 100 / mTotal : 0 ); } void addChild( ProgressItem *kiddo ); void removeChild( ProgressItem *kiddo ); bool canceled() const { return mCanceled; } void setBusy( bool busy ); Q_SIGNALS: /** * Emitted when a new ProgressItem is added. - * @param The ProgressItem that was added. + * @param item The ProgressItem that was added. */ - void progressItemAdded( KDevelop::ProgressItem * ); + void progressItemAdded( KDevelop::ProgressItem* item ); /** * Emitted when the progress value of an item changes. - * @param The item which got a new value. - * @param The value, for convenience. + * @param item The item which got a new value. + * @param value The value, for convenience. */ - void progressItemProgress( KDevelop::ProgressItem *, unsigned int ); + void progressItemProgress( KDevelop::ProgressItem* item, unsigned int value ); /** * Emitted when a progress item was completed. The item will be * deleted afterwards, so slots connected to this are the last * chance to work with this item. - * @param The completed item. + * @param item The completed item. */ - void progressItemCompleted( KDevelop::ProgressItem * ); + void progressItemCompleted( KDevelop::ProgressItem* item ); /** * Emitted when an item was canceled. It will _not_ go away immediately, * only when the owner sets it complete, which will usually happen. Can be * used to visually indicate the canceled status of an item. Should be used * by the owner of the item to make sure it is set completed even if it is * canceled. There is a ProgressManager::slotStandardCancelHandler which * simply sets the item completed and can be used if no other work needs to * be done on cancel. - * @param The canceled item; + * @param item The canceled item; */ - void progressItemCanceled( KDevelop::ProgressItem * ); + void progressItemCanceled( KDevelop::ProgressItem* item ); /** * Emitted when the status message of an item changed. Should be used by * progress dialogs to update the status message for an item. - * @param The updated item. - * @param The new message. + * @param item The updated item. + * @param message The new message. */ - void progressItemStatus( KDevelop::ProgressItem *, const QString & ); + void progressItemStatus( KDevelop::ProgressItem* item, const QString& message ); /** * Emitted when the label of an item changed. Should be used by * progress dialogs to update the label of an item. - * @param The updated item. - * @param The new label. + * @param item The updated item. + * @param label The new label. */ - void progressItemLabel( KDevelop::ProgressItem *, const QString & ); + void progressItemLabel( KDevelop::ProgressItem* item, const QString& label ); /** * Emitted when the crypto status of an item changed. Should be used by * progress dialogs to update the crypto indicator of an item. - * @param The updated item. - * @param The new state. + * @param item The updated item. + * @param state The new state. */ - void progressItemUsesCrypto( KDevelop::ProgressItem *, bool ); + void progressItemUsesCrypto( KDevelop::ProgressItem* item, bool state ); /** * Emitted when the busy indicator state of an item changes. Should be used * by progress dialogs so that they can adjust the display of the progress bar * to the new mode. * @param item The updated item * @param value True if the item uses a busy indicator now, false otherwise */ void progressItemUsesBusyIndicator( KDevelop::ProgressItem *item, bool value ); protected: /* Only to be used by our good friend the ProgressManager */ ProgressItem( ProgressItem *parent, const QString &id, const QString &label, const QString &status, bool isCancellable, bool usesCrypto ); ~ProgressItem() override; private: QString mId; QString mLabel; QString mStatus; QPointer mParent; bool mCanBeCanceled; unsigned int mProgress; ProgressItemMap mChildren; unsigned int mTotal; unsigned int mCompleted; bool mWaitingForKids; bool mCanceled; bool mUsesCrypto; bool mUsesBusyIndicator; bool mCompletedCalled; }; struct ProgressManagerPrivate; /** * The ProgressManager singleton keeps track of all ongoing transactions * and notifies observers (progress dialogs) when their progress percent value * changes, when they are completed (by their owner), and when they are canceled. * Each ProgressItem emits those signals individually and the singleton * broadcasts them. Use the ::createProgressItem() statics to acquire an item * and then call ->setProgress( int percent ) on it every time you want to * update the item and ->setComplete() when the operation is done. This will * delete the item. Connect to the item's progressItemCanceled() signal to be * notified when the user cancels the transaction using one of the observing * progress dialogs or by calling item->cancel() in some other way. The owner * is responsible for calling setComplete() on the item, even if it is canceled. * Use the standardCancelHandler() slot if that is all you want to do on cancel. * * Note that if you request an item with a certain id and there is already * one with that id, there will not be a new one created but the existing * one will be returned. This is convenient for accessing items that are * needed regularly without the to store a pointer to them or to add child * items to parents by id. */ class ProgressManager : public QObject { Q_OBJECT friend struct ProgressManagerPrivate; public: ~ProgressManager() override; /** * @return The singleton instance of this class. */ static ProgressManager *instance(); /** - * Use this to acquire a unique id number which can be used to discern + * @return a unique id number which can be used to discern * an operation from all others going on at the same time. Use that - * number as the id string for your progressItem to ensure it is unique. - * @return + * number as the id string for your progressItem to ensure it is unique. */ static QString getUniqueID() { return QString::number( ++uID ); } /** * Creates a ProgressItem with a unique id and the given label. * This is the simplest way to acquire a progress item. It will not * have a parent and will be set to be cancellable and not using crypto. */ static ProgressItem *createProgressItem( const QString &label ) { return instance()->createProgressItemImpl( nullptr, getUniqueID(), label, QString(), true, false ); } /** * Creates a new progressItem with the given parent, id, label and initial * status. * * @param parent Specify an already existing item as the parent of this one. * @param id Used to identify this operation for cancel and progress info. * @param label The text to be displayed by progress handlers * @param status Additional text to be displayed for the item. * @param canBeCanceled can the user cancel this operation? * @param usesCrypto does the operation use secure transports (SSL) * Cancelling the parent will cancel the children as well (if they can be * canceled) and ongoing children prevent parents from finishing. * @return The ProgressItem representing the operation. */ static ProgressItem *createProgressItem( ProgressItem *parent, const QString &id, const QString &label, const QString &status = QString(), bool canBeCanceled = true, bool usesCrypto = false ) { return instance()->createProgressItemImpl( parent, id, label, status, canBeCanceled, usesCrypto ); } /** * Use this version if you have the id string of the parent and want to * add a subjob to it. */ static ProgressItem *createProgressItem( const QString &parent, const QString &id, const QString &label, const QString &status = QString(), bool canBeCanceled = true, bool usesCrypto = false ) { return instance()->createProgressItemImpl( parent, id, label, status, canBeCanceled, usesCrypto ); } /** * Version without a parent. */ static ProgressItem *createProgressItem( const QString &id, const QString &label, const QString &status = QString(), bool canBeCanceled = true, bool usesCrypto = false ) { return instance()->createProgressItemImpl( nullptr, id, label, status, canBeCanceled, usesCrypto ); } /** * Version for Akonadi agents. * This connects all the proper signals so that you do not have to * worry about updating the progress or reacting to progressItemCanceled(). */ static ProgressItem *createProgressItem( ProgressItem *parent, const Akonadi::AgentInstance &agent, const QString &id, const QString &label, const QString &status = QString(), bool canBeCanceled = true, bool usesCrypto = false ) { return instance()->createProgressItemForAgent( parent, agent, id, label, status, canBeCanceled, usesCrypto ); } /** * @return true when there are no more progress items. */ bool isEmpty() const { return mTransactions.isEmpty(); } /** * @return the only top level progressitem when there's only one. * Returns 0 if there is no item, or more than one top level item. * Since this is used to calculate the overall progress, it will also return * 0 if there is an item which uses a busy indicator, since that will invalidate * the overall progress. */ ProgressItem *singleItem() const; /** * Ask all listeners to show the progress dialog, because there is * something that wants to be shown. */ static void emitShowProgressDialog() { instance()->emitShowProgressDialogImpl(); } Q_SIGNALS: /** @see ProgressItem::progressItemAdded() */ void progressItemAdded( KDevelop::ProgressItem * ); /** @see ProgressItem::progressItemProgress() */ void progressItemProgress( KDevelop::ProgressItem *, unsigned int ); /** @see ProgressItem::progressItemCompleted() */ void progressItemCompleted( KDevelop::ProgressItem * ); /** @see ProgressItem::progressItemCanceled() */ void progressItemCanceled( KDevelop::ProgressItem * ); /** @see ProgressItem::progressItemStatus() */ void progressItemStatus( KDevelop::ProgressItem *, const QString & ); /** @see ProgressItem::progressItemLabel() */ void progressItemLabel( KDevelop::ProgressItem *, const QString & ); /** @see ProgressItem::progressItemUsesCrypto() */ void progressItemUsesCrypto( KDevelop::ProgressItem *, bool ); /** @see ProgressItem::progressItemUsesBusyIndicator */ void progressItemUsesBusyIndicator( KDevelop::ProgressItem*, bool ); /** * Emitted when an operation requests the listeners to be shown. * Use emitShowProgressDialog() to trigger it. */ void showProgressDialog(); public Q_SLOTS: /** * Calls setCompleted() on the item, to make sure it goes away. * Provided for convenience. * @param item the canceled item. */ void slotStandardCancelHandler( KDevelop::ProgressItem *item ); /** * Aborts all running jobs. Bound to "Esc" */ void slotAbortAll(); private Q_SLOTS: void slotTransactionCompleted( KDevelop::ProgressItem *item ); private: ProgressManager(); // prevent unsolicited copies ProgressManager( const ProgressManager & ); virtual ProgressItem *createProgressItemImpl( ProgressItem *parent, const QString &id, const QString &label, const QString &status, bool cancellable, bool usesCrypto ); virtual ProgressItem *createProgressItemImpl( const QString &parent, const QString &id, const QString &label, const QString &status, bool cancellable, bool usesCrypto ); ProgressItem *createProgressItemForAgent( ProgressItem *parent, const Akonadi::AgentInstance &instance, const QString &id, const QString &label, const QString &status, bool cancellable, bool usesCrypto ); void emitShowProgressDialogImpl(); QHash< QString, ProgressItem* > mTransactions; static unsigned int uID; }; } #endif // __KDevelop_PROGRESSMANAGER_H__ diff --git a/shell/sessioncontroller.cpp b/shell/sessioncontroller.cpp index 2c5d6eac5d..c9bf27498e 100644 --- a/shell/sessioncontroller.cpp +++ b/shell/sessioncontroller.cpp @@ -1,664 +1,664 @@ /* This file is part of KDevelop Copyright 2008 Andreas Pakulat Copyright 2010 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 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. */ #include "sessioncontroller.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 "session.h" #include "core.h" #include "uicontroller.h" #include "shellextension.h" #include "sessionlock.h" #include "sessionchooserdialog.h" #include "debug.h" #include #include #include #include #include namespace KDevelop { namespace { int argc = 0; char** argv = 0; }; void SessionController::setArguments(int _argc, char** _argv) { argc = _argc; argv = _argv; } static QStringList standardArguments() { QStringList ret; for(int a = 0; a < argc; ++a) { QString arg = QString::fromLocal8Bit(argv[a]); if(arg.startsWith(QLatin1String("-graphicssystem")) || arg.startsWith(QLatin1String("-style"))) { ret << '-' + arg; if(a+1 < argc) ret << QString::fromLocal8Bit(argv[a+1]); } } return ret; } class SessionControllerPrivate : public QObject { Q_OBJECT public: SessionControllerPrivate( SessionController* s ) : q(s) , activeSession(0) , grp(0) { } ~SessionControllerPrivate() override { } Session* findSessionForName( const QString& name ) const { foreach( Session* s, sessionActions.keys() ) { if( s->name() == name ) return s; } return 0; } Session* findSessionForId(QString idString) { QUuid id(idString); foreach( Session* s, sessionActions.keys() ) { if( s->id() == id) return s; } return 0; } void newSession() { qsrand(QDateTime::currentDateTimeUtc().toTime_t()); Session* session = new Session( QUuid::createUuid().toString() ); KProcess::startDetached(ShellExtension::getInstance()->binaryPath(), QStringList() << QStringLiteral("-s") << session->id().toString() << standardArguments()); delete session; #if 0 //Terminate this instance of kdevelop if the user agrees foreach(Sublime::MainWindow* window, Core::self()->uiController()->controller()->mainWindows()) window->close(); #endif } void deleteCurrentSession() { int choice = KMessageBox::warningContinueCancel(Core::self()->uiController()->activeMainWindow(), i18n("The current session and all contained settings will be deleted. The projects will stay unaffected. Do you really want to continue?")); if(choice == KMessageBox::Continue) { q->deleteSessionFromDisk(sessionLock); q->emitQuitSession(); } } void renameSession() { bool ok; auto newSessionName = QInputDialog::getText(Core::self()->uiController()->activeMainWindow(), i18n("Rename Session"), i18n("New Session Name:"), QLineEdit::Normal, q->activeSession()->name(), &ok); if (ok) { static_cast(q->activeSession())->setName(newSessionName); } q->updateXmlGuiActionList(); // resort } bool loadSessionExternally( Session* s ) { Q_ASSERT( s ); KProcess::startDetached(ShellExtension::getInstance()->binaryPath(), QStringList() << QStringLiteral("-s") << s->id().toString() << standardArguments()); return true; } TryLockSessionResult activateSession( Session* s ) { Q_ASSERT( s ); activeSession = s; TryLockSessionResult result = SessionController::tryLockSession( s->id().toString()); if( !result.lock ) { activeSession = 0; return result; } Q_ASSERT(s->id().toString() == result.lock->id()); sessionLock = result.lock; KConfigGroup grp = KSharedConfig::openConfig()->group( SessionController::cfgSessionGroup() ); grp.writeEntry( SessionController::cfgActiveSessionEntry(), s->id().toString() ); grp.sync(); if (Core::self()->setupFlags() & Core::NoUi) return result; QHash::iterator it = sessionActions.find(s); Q_ASSERT( it != sessionActions.end() ); (*it)->setCheckable(true); (*it)->setChecked(true); for(it = sessionActions.begin(); it != sessionActions.end(); ++it) { if(it.key() != s) (*it)->setCheckable(false); } return result; } void loadSessionFromAction(QAction* action) { auto session = action->data().value(); loadSessionExternally(session); } void addSession( Session* s ) { if (Core::self()->setupFlags() & Core::NoUi) { sessionActions[s] = 0; return; } QAction* a = new QAction( grp ); a->setText( s->description() ); a->setCheckable( false ); a->setData(QVariant::fromValue(s)); sessionActions[s] = a; q->actionCollection()->addAction( "session_"+s->id().toString(), a ); connect( s, &Session::sessionUpdated, this, &SessionControllerPrivate::sessionUpdated ); sessionUpdated( s ); } SessionController* q; QHash sessionActions; ISession* activeSession; QActionGroup* grp; ISessionLock::Ptr sessionLock; static QString sessionBaseDirectory() { return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) +'/'+ qApp->applicationName() + "/sessions/"; } QString ownSessionDirectory() const { Q_ASSERT(activeSession); return q->sessionDirectory( activeSession->id().toString() ); } private slots: void sessionUpdated( KDevelop::ISession* s ) { sessionActions[static_cast( s )]->setText( KStringHandler::rsqueeze(s->description()) ); } }; SessionController::SessionController( QObject *parent ) : QObject( parent ), d(new SessionControllerPrivate(this)) { setObjectName(QStringLiteral("SessionController")); setComponentName(QStringLiteral("kdevsession"), QStringLiteral("KDevSession")); setXMLFile(QStringLiteral("kdevsessionui.rc")); QDBusConnection::sessionBus().registerObject( QStringLiteral("/org/kdevelop/SessionController"), this, QDBusConnection::ExportScriptableSlots ); if (Core::self()->setupFlags() & Core::NoUi) return; QAction* action = actionCollection()->addAction( QStringLiteral("new_session"), this, SLOT(newSession()) ); action->setText( i18nc("@action:inmenu", "Start New Session") ); action->setToolTip( i18nc("@info:tooltip", "Start a new KDevelop instance with an empty session") ); action->setIcon(QIcon::fromTheme(QStringLiteral("window-new"))); action = actionCollection()->addAction( QStringLiteral("rename_session"), this, SLOT(renameSession()) ); action->setText( i18n("Rename Current Session...") ); action->setIcon(QIcon::fromTheme(QStringLiteral("edit-rename"))); action = actionCollection()->addAction( QStringLiteral("delete_session"), this, SLOT(deleteCurrentSession()) ); action->setText( i18n("Delete Current Session...") ); action->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete"))); action = actionCollection()->addAction( QStringLiteral("quit"), this, SIGNAL(quitSession()) ); action->setText( i18n("Quit") ); action->setMenuRole( QAction::NoRole ); // OSX: prevent QT from hiding this due to conflict with 'Quit KDevelop...' actionCollection()->setDefaultShortcut( action, Qt::CTRL | Qt::Key_Q ); action->setIcon(QIcon::fromTheme(QStringLiteral("application-exit"))); d->grp = new QActionGroup( this ); connect( d->grp, &QActionGroup::triggered, this, [&] (QAction* a) { d->loadSessionFromAction(a); } ); } SessionController::~SessionController() { delete d; } void SessionController::startNewSession() { d->newSession(); } void SessionController::cleanup() { if (d->activeSession) { Q_ASSERT(d->activeSession->id().toString() == d->sessionLock->id()); if (d->activeSession->isTemporary()) { deleteSessionFromDisk(d->sessionLock); } d->activeSession = 0; } d->sessionLock.clear(); qDeleteAll(d->sessionActions); d->sessionActions.clear(); } void SessionController::initialize( const QString& session ) { QDir sessiondir( SessionControllerPrivate::sessionBaseDirectory() ); foreach( const QString& s, sessiondir.entryList( QDir::AllDirs | QDir::NoDotAndDotDot ) ) { QUuid id( s ); if( id.isNull() ) continue; // Only create sessions for directories that represent proper uuid's Session* ses = new Session( id.toString(), this ); //Delete sessions that have no name and are empty if( ses->containedProjects().isEmpty() && ses->name().isEmpty() && (session.isEmpty() || (ses->id().toString() != session && ses->name() != session)) ) { TryLockSessionResult result = tryLockSession(s); if (result.lock) { deleteSessionFromDisk(result.lock); } delete ses; } else { d->addSession( ses ); } } loadDefaultSession( session ); updateXmlGuiActionList(); } ISession* SessionController::activeSession() const { return d->activeSession; } ISessionLock::Ptr SessionController::activeSessionLock() const { return d->sessionLock; } void SessionController::loadSession( const QString& nameOrId ) { d->loadSessionExternally( session( nameOrId ) ); } QList SessionController::sessionNames() const { QStringList l; foreach( const Session* s, d->sessionActions.keys() ) { l << s->name(); } return l; } QList< const KDevelop::Session* > SessionController::sessions() const { QList< const KDevelop::Session* > ret; foreach( const Session* s, d->sessionActions.keys() ) { ret << s; } return ret; } Session* SessionController::createSession( const QString& name ) { Session* s; if(name.startsWith('{')) { s = new Session( QUuid(name).toString() ); }else{ qsrand(QDateTime::currentDateTimeUtc().toTime_t()); s = new Session( QUuid::createUuid().toString() ); s->setName( name ); } d->addSession( s ); updateXmlGuiActionList(); return s; } void SessionController::deleteSession( const ISessionLock::Ptr& lock ) { Session* s = session(lock->id()); QHash::iterator it = d->sessionActions.find(s); Q_ASSERT( it != d->sessionActions.end() ); unplugActionList( QStringLiteral("available_sessions") ); actionCollection()->removeAction(*it); if (d->grp) { // happens in unit tests d->grp->removeAction(*it); plugActionList( QStringLiteral("available_sessions"), d->grp->actions() ); } if (s == d->activeSession) { d->activeSession = nullptr; } deleteSessionFromDisk(lock); emit sessionDeleted( s->id().toString() ); d->sessionActions.remove(s); delete s; } void SessionController::deleteSessionFromDisk( const ISessionLock::Ptr& lock ) { qCDebug(SHELL) << "Deleting session:" << lock->id(); static_cast(lock.data())->removeFromDisk(); ItemRepositoryRegistry::deleteRepositoryFromDisk( lock ); } void SessionController::loadDefaultSession( const QString& session ) { QString load = session; if (load.isEmpty()) { KConfigGroup grp = KSharedConfig::openConfig()->group( cfgSessionGroup() ); load = grp.readEntry( cfgActiveSessionEntry(), "default" ); } // Iteratively try to load the session, asking user what to do in case of failure // If showForceOpenDialog() returns empty string, stop trying Session* s = 0; do { s = this->session( load ); if( !s ) { s = createSession( load ); } TryLockSessionResult result = d->activateSession( s ); if( result.lock ) { Q_ASSERT(d->activeSession == s); Q_ASSERT(d->sessionLock = result.lock); break; } load = handleLockedSession( s->name(), s->id().toString(), result.runInfo ); } while( !load.isEmpty() ); } Session* SessionController::session( const QString& nameOrId ) const { Session* ret = d->findSessionForName( nameOrId ); if(ret) return ret; return d->findSessionForId( nameOrId ); } QString SessionController::cloneSession( const QString& nameOrid ) { Session* origSession = session( nameOrid ); qsrand(QDateTime::currentDateTimeUtc().toTime_t()); QUuid id = QUuid::createUuid(); auto copyJob = KIO::copy(QUrl::fromLocalFile(sessionDirectory(origSession->id().toString())), QUrl::fromLocalFile(sessionDirectory( id.toString()))); KJobWidgets::setWindow(copyJob, Core::self()->uiController()->activeMainWindow()); copyJob->exec(); Session* newSession = new Session( id.toString() ); newSession->setName( i18n( "Copy of %1", origSession->name() ) ); d->addSession(newSession); updateXmlGuiActionList(); return newSession->name(); } void SessionController::updateXmlGuiActionList() { unplugActionList( QStringLiteral("available_sessions") ); if (d->grp) { auto actions = d->grp->actions(); std::sort(actions.begin(), actions.end(), [](const QAction* lhs, const QAction* rhs) { auto s1 = lhs->data().value(); auto s2 = rhs->data().value(); return QString::localeAwareCompare(s1->description(), s2->description()) < 0; }); plugActionList(QStringLiteral("available_sessions"), actions); } } QString SessionController::cfgSessionGroup() { return QStringLiteral("Sessions"); } QString SessionController::cfgActiveSessionEntry() { return QStringLiteral("Active Session ID"); } QList SessionController::availableSessionInfo() { return availableSessionInfos().toList(); } SessionInfos SessionController::availableSessionInfos() { SessionInfos sessionInfos; foreach( const QString& sessionId, QDir( SessionControllerPrivate::sessionBaseDirectory() ).entryList( QDir::AllDirs ) ) { if( !QUuid( sessionId ).isNull() ) { sessionInfos << Session::parse( sessionId ); } } return sessionInfos; } QString SessionController::sessionDirectory(const QString& sessionId) { return SessionControllerPrivate::sessionBaseDirectory() + sessionId; } -TryLockSessionResult SessionController::tryLockSession(const QString& id) +TryLockSessionResult SessionController::tryLockSession(const QString& id, bool doLocking) { - return SessionLock::tryLockSession(id, true); + return SessionLock::tryLockSession(id, doLocking); } bool SessionController::isSessionRunning(const QString& id) { return sessionRunInfo(id).isRunning; } SessionRunInfo SessionController::sessionRunInfo(const QString& id) { return SessionLock::tryLockSession(id, false).runInfo; } QString SessionController::showSessionChooserDialog(QString headerText, bool onlyRunning) { ///FIXME: move this code into sessiondialog.cpp QListView* view = new QListView; QLineEdit* filter = new QLineEdit; filter->setClearButtonEnabled( true ); filter->setPlaceholderText(i18n("Search")); QStandardItemModel* model = new QStandardItemModel(view); QSortFilterProxyModel *proxy = new QSortFilterProxyModel(model); proxy->setSourceModel(model); proxy->setFilterKeyColumn( 1 ); proxy->setFilterCaseSensitivity( Qt::CaseInsensitive ); connect(filter, &QLineEdit::textChanged, proxy, &QSortFilterProxyModel::setFilterFixedString); SessionChooserDialog dialog(view, proxy, filter); view->setEditTriggers(QAbstractItemView::NoEditTriggers); QVBoxLayout layout(dialog.mainWidget()); if(!headerText.isEmpty()) { QLabel* heading = new QLabel(headerText); QFont font = heading->font(); font.setBold(true); heading->setFont(font); layout.addWidget(heading); } model->setColumnCount(3); model->setHeaderData(0, Qt::Horizontal,i18n("Identity")); model->setHeaderData(1, Qt::Horizontal, i18n("Contents")); model->setHeaderData(2, Qt::Horizontal,i18n("State")); view->setModel(proxy); view->setModelColumn(1); QHBoxLayout* filterLayout = new QHBoxLayout(); filterLayout->addWidget(new QLabel(i18n("Filter:"))); filterLayout->addWidget(filter); layout.addLayout(filterLayout); layout.addWidget(view); filter->setFocus(); int row = 0; QString defaultSession = KSharedConfig::openConfig()->group( cfgSessionGroup() ).readEntry( cfgActiveSessionEntry(), "default" ); foreach(const KDevelop::SessionInfo& si, KDevelop::SessionController::availableSessionInfos()) { if ( si.name.isEmpty() && si.projects.isEmpty() ) { continue; } bool running = KDevelop::SessionController::isSessionRunning(si.uuid.toString()); if(onlyRunning && !running) continue; model->setItem(row, 0, new QStandardItem(si.uuid.toString())); model->setItem(row, 1, new QStandardItem(si.description)); model->setItem(row, 2, new QStandardItem); ++row; } model->sort(1); if(!onlyRunning) { model->setItem(row, 0, new QStandardItem); model->setItem(row, 1, new QStandardItem(QIcon::fromTheme(QStringLiteral("window-new")), i18n("Create New Session"))); } dialog.updateState(); dialog.mainWidget()->layout()->setContentsMargins(0,0,0,0); const QModelIndex defaultSessionIndex = model->match(model->index(0, 0), Qt::DisplayRole, defaultSession, 1, Qt::MatchExactly).value(0); view->selectionModel()->setCurrentIndex(proxy->mapFromSource(defaultSessionIndex), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); view->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); ///@todo We need a way to get a proper size-hint from the view, but unfortunately, that only seems possible after the view was shown. dialog.resize(QSize(900, 600)); if(dialog.exec() != QDialog::Accepted) { return QString(); } QModelIndex selected = view->selectionModel()->currentIndex(); if (!selected.isValid()) return QString(); const QString selectedSessionId = selected.sibling(selected.row(), 0).data().toString(); if (selectedSessionId.isEmpty()) { // "Create New Session" item selected, return a fresh UUID qsrand(QDateTime::currentDateTimeUtc().toTime_t()); return QUuid::createUuid().toString(); } return selectedSessionId; } QString SessionController::handleLockedSession( const QString& sessionName, const QString& sessionId, const SessionRunInfo& runInfo ) { return SessionLock::handleLockedSession(sessionName, sessionId, runInfo); } QString SessionController::sessionDir() { if( !activeSession() ) return QString(); return d->ownSessionDirectory(); } QString SessionController::sessionName() { if(!activeSession()) return QString(); return activeSession()->description(); } } #include "sessioncontroller.moc" #include "moc_sessioncontroller.cpp" diff --git a/shell/sessioncontroller.h b/shell/sessioncontroller.h index f358540fa5..7f1a5eeb2e 100644 --- a/shell/sessioncontroller.h +++ b/shell/sessioncontroller.h @@ -1,180 +1,180 @@ /* This file is part of KDevelop Copyright 2008 Andreas Pakulat Copyright 2013 Milian Wolff 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_SESSIONCONTROLLER_H #define KDEVPLATFORM_SESSIONCONTROLLER_H #include "shellexport.h" #include "session.h" #include #include #include namespace KDevelop { struct SessionRunInfo { SessionRunInfo() : isRunning(false) , holderPid(-1) {} bool operator==(const SessionRunInfo& o) const { return isRunning == o.isRunning && holderPid == o.holderPid && holderApp == o.holderApp && holderHostname == o.holderHostname; } bool operator!=(const SessionRunInfo& o) const { return !(operator==(o)); } // if this is true, this session is currently running in an external process bool isRunning; // if the session is running, this contains the PID of its process qint64 holderPid; // if the session is running, this contains the name of its process QString holderApp; // if the session is running, this contains the host name where the process runs QString holderHostname; }; struct TryLockSessionResult { TryLockSessionResult(const ISessionLock::Ptr& _lock) : lock(_lock) {} TryLockSessionResult(const SessionRunInfo& _runInfo) : runInfo(_runInfo) {} // if this is non-null then the session was locked ISessionLock::Ptr lock; // otherwise this contains information about who is locking the session SessionRunInfo runInfo; }; class KDEVPLATFORMSHELL_EXPORT SessionController : public QObject, public KXMLGUIClient { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kdevelop.SessionController") public: explicit SessionController( QObject *parent = nullptr ); ~SessionController() override; void initialize( const QString& session ); void cleanup(); /// Returns whether the given session can be locked (i. e., is not locked currently). /// @param doLocking whether to really lock the session or just "dry-run" the locking process - static TryLockSessionResult tryLockSession(const QString& id); + static TryLockSessionResult tryLockSession(const QString& id, bool doLocking=true); /** * @return true when the given session is currently running, false otherwise */ static bool isSessionRunning(const QString& id); /** * @return information about whether the session @p id is running */ static SessionRunInfo sessionRunInfo(const QString& id); /// The application should call this on startup to tell the /// session-controller about the received arguments. /// Some of them may need to be passed to newly opened sessions. static void setArguments(int argc, char** argv); ///Finds a session by its name or by its UUID Session* session( const QString& nameOrId ) const; virtual ISession* activeSession() const; ISessionLock::Ptr activeSessionLock() const; QList sessionNames() const; Session* createSession( const QString& name ); QList sessions() const; void loadDefaultSession( const QString& session ); void startNewSession(); void loadSession( const QString& nameOrId ); void deleteSession( const ISessionLock::Ptr& lock ); static void deleteSessionFromDisk( const ISessionLock::Ptr& lock ); QString cloneSession( const QString& nameOrid ); /** * Path to session directory for the session with the given @p sessionId. */ static QString sessionDirectory( const QString& sessionId ); static QString cfgSessionGroup(); static QString cfgActiveSessionEntry(); static QT_DEPRECATED QList availableSessionInfo(); // use availableSessionInfos() static SessionInfos availableSessionInfos(); /** * Shows a dialog where the user can choose the session * @param headerText an additional text that will be shown at the top in a label * @param onlyRunning whether only currently running sessions should be shown * @return UUID on success, empty string in any other case */ static QString showSessionChooserDialog(QString headerText = QString(), bool onlyRunning = false); /// Should be called if session to be opened is locked. /// It attempts to bring existing instance's window up via a DBus call; if that succeeds, empty string is returned. /// Otherwise (if the app did not respond) it shows a dialog where the user may choose /// 1) to force-remove the lockfile and continue, - /// 2) to select another session via \ref showSessionChooserDialog, + /// 2) to select another session via @ref showSessionChooserDialog, /// 3) to quit the current (starting-up) instance. /// @param sessionName session name (for the message) - /// @param sessionId current session GUID (to return if user chooses force-removal) + /// @param currentSessionId current session GUID (to return if user chooses force-removal) /// @param runInfo the run information about the session /// @return new session GUID to try or an empty string if application startup shall be aborted static QString handleLockedSession( const QString& sessionName, const QString& currentSessionId, const SessionRunInfo& runInfo ); void updateXmlGuiActionList(); void emitQuitSession() { emit quitSession(); } public Q_SLOTS: // Returns the pretty name of the currently active session (used in the shell integration) virtual Q_SCRIPTABLE QString sessionName(); // Returns the directory associated to the active session (used in the shell integration) virtual Q_SCRIPTABLE QString sessionDir(); Q_SIGNALS: void sessionLoaded( ISession* ); void sessionDeleted( const QString& id); void quitSession(); private: Q_PRIVATE_SLOT( d, void newSession() ) Q_PRIVATE_SLOT( d, void deleteCurrentSession() ) Q_PRIVATE_SLOT( d, void renameSession() ) Q_PRIVATE_SLOT( d, void loadSessionFromAction( QAction* ) ) class SessionControllerPrivate* const d; }; } #endif diff --git a/sublime/document.h b/sublime/document.h index 2e0001373b..00ff6679e7 100644 --- a/sublime/document.h +++ b/sublime/document.h @@ -1,146 +1,146 @@ /*************************************************************************** * Copyright 2006-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_SUBLIMEDOCUMENT_H #define KDEVPLATFORM_SUBLIMEDOCUMENT_H #include #include #include "sublimeexport.h" class QIcon; class QWidget; namespace Sublime { class Area; class View; class Controller; /** @short Abstract base class for all Sublime documents Subclass from Document and implement createViewWidget() method to return a new widget for a view. */ class KDEVPLATFORMSUBLIME_EXPORT Document: public QObject { Q_OBJECT public: /**Creates a document and adds it to a @p controller.*/ Document(const QString &title, Controller *controller); ~Document() override; /**@return the new view for this document. @note it will not create a widget, just return a view object.*/ View *createView(); /**@return the list of all views in all areas for this document.*/ const QList &views() const; /**@return the controller for this document.*/ Controller *controller() const; - /**@return the document title.*/ enum TitleType { Normal, Extended}; + /**@return the document title.*/ virtual QString title(TitleType type = Normal) const; /**Set the document title.*/ void setTitle(const QString& newTitle); void setToolTip(const QString& newToolTip); QString toolTip() const; /**@return the type of document which can be written to config.*/ virtual QString documentType() const = 0; /**@return the specifics of this document which can be written to config.*/ virtual QString documentSpecifier() const = 0; /** * If the document is in a state where data may be lost while closking, * asks the user whether he really wants to close the document. * * This function may also take actions like saving the document before closing * if the user desires so. * * @return true if the document is allowed to be closed, otherwise false. * * The default implementation always returns true. * * */ virtual bool askForCloseFeedback(); /**Should try closing the document, eventually asking the user for feedback. * *If closing is successful, all views should be deleted, and the document itself *be scheduled for deletion using deleteLater(). * * @param silent If this is true, the user must not be asked. * * Returns whether closing was successful (The user did not push 'Cancel') */ virtual bool closeDocument(bool silent = false); void setStatusIcon(QIcon icon); /** * @return The status icon of the document. */ QIcon statusIcon() const; /** * @return The status icon of the document, or, if none is present, an icon * that resembles the document, i.e. based on its mime type. * @see defaultIcon() */ QIcon icon() const; /** * Optionally override this to return a default icon when no status * icon is set for the document. The default returns an invalid icon. */ virtual QIcon defaultIcon() const; Q_SIGNALS: /**Emitted when the document is about to be deleted but is still in valid state.*/ void aboutToDelete(Sublime::Document *doc); /**Emitted when the document's title is changed.*/ void titleChanged(Sublime::Document *doc); /**Emitted when the document status-icon has changed */ void statusIconChanged(Sublime::Document *doc); protected: /**Creates and returns the new view. Reimplement in subclasses to instantiate views of derived from Sublime::View classes.*/ virtual View *newView(Document *doc); /**Reimplement this to create and return the new widget to display this document in the view. This method is used by View class when it is asked for its widget.*/ virtual QWidget *createViewWidget(QWidget *parent = nullptr) = 0; /** Closes all views associated to this document */ virtual void closeViews(); private: struct DocumentPrivate *const d; friend struct DocumentPrivate; friend class View; }; } #endif diff --git a/tests/testfile.h b/tests/testfile.h index ddf49f4598..459cbb7989 100644 --- a/tests/testfile.h +++ b/tests/testfile.h @@ -1,167 +1,167 @@ /* This file is part of KDevelop Copyright 2010 Niko Sams Copyright 2011 Milian Wolff 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_TESTFILE_H #define KDEVPLATFORM_TESTFILE_H #include #include #include "testsexport.h" namespace KDevelop { class TestProject; /** * Helper file to parse a file using the full KDevelop architecture. * * The file will be added to the background parser, and eventually * parsed by a fitting language plugin, just like a normal file * would be in an actual KDevelop instance. * * Example usage: * \code * TestFile file("... * @endcode */ TestFile(const QString& contents, const QString& fileExtension, KDevelop::TestProject *project = nullptr, const QString& dir = QString()); /** * Create a temporary file from @p contents with the same file basename as - * @p other but with the given @p fileExtension. + * @p base but with the given @p fileExtension. * * @param fileExtension the new file extension without the dot. - * @param other a different TestFile which is used for this file's basename + * @param base a different TestFile which is used for this file's basename * * This can be used to create e.g. .cpp/.h file pairs: * * @code * TestFile header("...", "h"); * TestFile impl("...", "cpp", &header); * @endcode */ TestFile(const QString& contents, const QString& fileExtension, const TestFile* base); /** * Removes temporary file and cleans up. */ ~TestFile() override; /** * Returns the URL to this file. */ IndexedString url() const; /** * Trigger (re-)parsing of this file with given @p features and @p priority. * * @see KDevelop::DUChain::updateContextForUrl */ void parse(TopDUContext::Features features = TopDUContext::AllDeclarationsContextsAndUses, int priority = 1); /** * Convenience method: * Trigger parse and wait for the file to be parsed. Internally calls waitForParsed() * * @see waitForParsed() * @see parse() */ bool parseAndWait(TopDUContext::Features features = TopDUContext::AllDeclarationsContextsAndUses, int priority = 1, int timeout = 1000); /** * Blocks current thread and waits until the file has been parsed. * * If it has waited longer than @p timeout ms, we return false * and assume something went wrong. * * Otherwise true is returned, indicating parsing finished * within the timeout interval. */ bool waitForParsed(int timeout = 1000); /** * Check whether the file has been processed after the last call to @c parse(). */ bool isReady() const; /** * Returns the @c TopDUContext for the current file, if it has been successfully parsed. */ KDevelop::ReferencedTopDUContext topContext(); /** * Change the file contents to @p contents. * * Use this to test behavior of your parsing code over * file changes. */ void setFileContents(const QString& contents); /** * Read the files contents and return them. */ QString fileContents() const; /** * Set to true when you want to keep the DUChain data. * * By default the DUChain data is removed on destruction of the TestFile. */ void setKeepDUChainData(bool keep); bool keepDUChainData(); private: struct TestFilePrivate; TestFilePrivate* d; Q_PRIVATE_SLOT(d, void updateReady(const KDevelop::IndexedString& url, KDevelop::ReferencedTopDUContext topContext)) }; } #endif // KDEVPLATFORM_TESTFILE_H diff --git a/util/convenientfreelist.h b/util/convenientfreelist.h index 91eafe74cd..ce1248108d 100644 --- a/util/convenientfreelist.h +++ b/util/convenientfreelist.h @@ -1,743 +1,743 @@ /* This file is part of KDevelop Copyright 2008 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDEVPLATFORM_CONVENIENTFREELIST_H #define KDEVPLATFORM_CONVENIENTFREELIST_H #include #include #include "embeddedfreetree.h" #include "kdevvarlengtharray.h" namespace KDevelop { template class ConvenientEmbeddedSetIterator; template class ConvenientEmbeddedSetFilterIterator; ///A convenience-class for accessing the data in a set managed by the EmbeddedFreeTree algorithms. template class ConstantConvenientEmbeddedSet { public: ConstantConvenientEmbeddedSet() : m_data(0), m_dataSize(0), m_centralFreeItem(-1) { } ConstantConvenientEmbeddedSet(const Data* data, uint count, int centralFreeItem) : m_data(data), m_dataSize(count), m_centralFreeItem(centralFreeItem) { } ///Returns whether the item is contained in this set bool contains(const Data& data) const { return indexOf(data) != -1; } ///Returns the position of the item in the underlying array, or -1 if it is not contained int indexOf(const Data& data) const { EmbeddedTreeAlgorithms alg(m_data, m_dataSize, m_centralFreeItem); return alg.indexOf(data); } ///Returns the size of the underlying array uint dataSize() const { return m_dataSize; } uint countFreeItems() { EmbeddedTreeAlgorithms alg(m_data, m_dataSize, m_centralFreeItem); return alg.countFreeItems(); } void verify() { EmbeddedTreeAlgorithms alg(m_data, m_dataSize, m_centralFreeItem); alg.verifyTreeConsistent(); alg.verifyOrder(); } ///Returns the underlying array. That array may contain invalid/free items. const Data* data() const { return m_data; } - ///Returns the first valid index that has a data-value larger or equal to @param data. + ///Returns the first valid index that has a data-value larger or equal to @p data. ///Returns -1 if nothing is found. int lowerBound(const Data& data) const { EmbeddedTreeAlgorithms alg(m_data, m_dataSize, m_centralFreeItem); return alg.lowerBound(data, 0, m_dataSize); } - ///Returns the first valid index that has a data-value larger or equal to @param data, + ///Returns the first valid index that has a data-value larger or equal to @p data, ///and that is in the range [start, end). ///Returns -1 if nothing is found. int lowerBound(const Data& data, uint start, uint end) const { EmbeddedTreeAlgorithms alg(m_data, m_dataSize, m_centralFreeItem); return alg.lowerBound(data, start, end); } ///Finds a valid most central in the range [start, end). ///Returns -1 if no such item exists. int validMiddle(uint start, uint end) { if(end <= start) return -1; int firstTry = ((end-start)/2) + start; int thisTry = firstTry; while(thisTry < (int)end && Handler::isFree(m_data[thisTry])) ++thisTry; if(thisTry != (int)end) return thisTry; //Nothing find on right side of middle, try the other direction thisTry = firstTry-1; while(thisTry >= (int)start && Handler::isFree(m_data[thisTry])) --thisTry; if(thisTry >= (int)start) return thisTry; else return -1; } ///Returns the first valid item in the range [pos, end), or -1 int firstValidItem(int start, int end = -1) const { if(end == -1) end = (int)m_dataSize; for(; start < end; ++start) if(!Handler::isFree(m_data[start])) return start; return -1; } ///Returns the last valid item in the range [pos, end), or -1 int lastValidItem(int start = 0, int end = -1) const { if(end == -1) end = (int)m_dataSize; --end; for(; end >= start; --end) if(!Handler::isFree(m_data[end])) return end; return -1; } typedef ConvenientEmbeddedSetIterator Iterator; ConvenientEmbeddedSetIterator iterator() const; // protected: const Data* m_data; uint m_dataSize; int m_centralFreeItem; }; ///Convenient iterator that automatically skips invalid/free items in the array. template class ConvenientEmbeddedSetIterator : public ConstantConvenientEmbeddedSet { public: ConvenientEmbeddedSetIterator(const Data* data = 0, uint count = 0, int centralFreeItem = -1) : ConstantConvenientEmbeddedSet(data, count, centralFreeItem), m_pos(0) { //Move to first valid position moveToValid(); } ///Returns true of this iterator has a value to return operator bool() const { return m_pos != this->m_dataSize; } const Data* operator->() const { return &this->m_data[m_pos]; } const Data& operator*() const { return this->m_data[m_pos]; } ConvenientEmbeddedSetIterator& operator++() { ++m_pos; moveToValid(); return *this; } protected: inline void moveToValid() { while(this->m_pos < this->m_dataSize && (Handler::isFree(this->m_data[this->m_pos]))) ++this->m_pos; } uint m_pos; }; ///An iterator that allows efficient matching between two lists with different data type. ///Important: It must be possible to extract the data-type of the second list from the items in the first list ///The second list must be sorted by that data. ///The first list must primarily be sorted by that data, but is allowed to ///be sub-ordered by something else, and multiple items in the first list are allowed to match one item in the second. ///This iterator iterates through all items in the first list that have extracted key-data that is in represented in the second. template class ConvenientEmbeddedSetFilterIterator : public ConvenientEmbeddedSetIterator { public: ConvenientEmbeddedSetFilterIterator() : m_match(-1) { } ConvenientEmbeddedSetFilterIterator(const ConvenientEmbeddedSetIterator& base, const ConvenientEmbeddedSetIterator& rhs) : ConvenientEmbeddedSetIterator(base), m_rhs(rhs), m_match(-1) { boundStack.append( qMakePair( qMakePair(0u, this->m_dataSize), qMakePair(0u, rhs.m_dataSize) ) ); go(); } operator bool() const { return m_match != -1; } const Data* operator->() const { Q_ASSERT(m_match != -1); return &this->m_data[m_match]; } const Data& operator*() const { Q_ASSERT(m_match != -1); return this->m_data[m_match]; } ConvenientEmbeddedSetFilterIterator& operator++() { Q_ASSERT(m_match != -1); go(); return *this; } #define CHECK_BOUNDS Q_ASSERT(boundStack.back().first.first < 100000 && boundStack.back().first.second < 10000 && boundStack.back().second.first < 100000 && boundStack.back().second.second < 10000 ); private: void go() { m_match = -1; boundsUp: if(boundStack.isEmpty()) return; CHECK_BOUNDS QPair, QPair > currentBounds = boundStack.back(); boundStack.pop_back(); uint ownStart = currentBounds.first.first, ownEnd = currentBounds.first.second; uint rhsStart = currentBounds.second.first, rhsEnd = currentBounds.second.second; #if 0 //This code works, but it doesn't give a speedup int ownFirstValid = this->firstValidItem(ownStart, ownEnd), ownLastValid = this->lastValidItem(ownStart, ownEnd); int rhsFirstValid = m_rhs.firstValidItem(rhsStart, rhsEnd), rhsLastValid = m_rhs.lastValidItem(rhsStart, rhsEnd); if(ownFirstValid == -1 || rhsFirstValid == -1) goto boundsUp; Data2 ownFirstValidData = KeyExtractor::extract(this->m_data[ownFirstValid]); Data2 ownLastValidData = KeyExtractor::extract(this->m_data[ownLastValid]); Data2 commonStart = ownFirstValidData; Data2 commonLast = ownLastValidData; //commonLast is also still valid if(commonStart < m_rhs.m_data[rhsFirstValid]) commonStart = m_rhs.m_data[rhsFirstValid]; if(m_rhs.m_data[rhsLastValid] < commonLast) commonLast = m_rhs.m_data[rhsLastValid]; if(commonLast < commonStart) goto boundsUp; #endif while(true) { if(ownStart == ownEnd) goto boundsUp; int ownMiddle = this->validMiddle(ownStart, ownEnd); Q_ASSERT(ownMiddle < 100000); if(ownMiddle == -1) goto boundsUp; //No valid items in the range Data2 currentData2 = KeyExtractor::extract(this->m_data[ownMiddle]); Q_ASSERT(!Handler2::isFree(currentData2)); int bound = m_rhs.lowerBound(currentData2, rhsStart, rhsEnd); if(bound == -1) { //Release second half of the own range // Q_ASSERT(ownEnd > ownMiddle); ownEnd = ownMiddle; continue; } if(currentData2 == m_rhs.m_data[bound]) { //We have a match this->m_match = ownMiddle; //Append the ranges that need to be matched next, without the matched item boundStack.append( qMakePair( qMakePair( (uint)ownMiddle+1, ownEnd ), qMakePair((uint)bound, rhsEnd)) ); if(ownMiddle) boundStack.append( qMakePair( qMakePair( ownStart, (uint)ownMiddle ), qMakePair(rhsStart, (uint)bound+1)) ); return; } if(bound == m_rhs.firstValidItem(rhsStart)) { //The bound is the first valid item of the second range. //Discard left side and the matched left item, and continue. ownStart = ownMiddle+1; rhsStart = bound; continue; } //Standard: Split both sides into 2 ranges that will be checked next boundStack.append( qMakePair( qMakePair( (uint)ownMiddle+1, ownEnd ), qMakePair((uint)bound, rhsEnd)) ); // Q_ASSERT(ownMiddle <= ownEnd); ownEnd = ownMiddle; //We loose the item at 'middle' here, but that's fine, since it hasn't found a match. rhsEnd = bound+1; } } //Bounds that yet need to be matched. KDevVarLengthArray, QPair > > boundStack; ConvenientEmbeddedSetIterator m_rhs; int m_match; }; ///Filters a list-embedded set by a binary tree set as managed by the SetRepository data structures template class ConvenientEmbeddedSetTreeFilterIterator : public ConvenientEmbeddedSetIterator { public: ConvenientEmbeddedSetTreeFilterIterator() : m_match(-1) { } ///@param noFiltering whether the given input is pre-filtered. If this is true, base will be iterated without skipping any items. ConvenientEmbeddedSetTreeFilterIterator(const ConvenientEmbeddedSetIterator& base, const TreeSet& rhs, bool noFiltering = false) : ConvenientEmbeddedSetIterator(base), m_rhs(rhs), m_match(-1), m_noFiltering(noFiltering) { if(rhs.node().isValid()) { //Correctly initialize the initial bounds int ownStart = lowerBound(rhs.node().firstItem(), 0, this->m_dataSize); if(ownStart == -1) return; int ownEnd = lowerBound(rhs.node().lastItem(), ownStart, this->m_dataSize); if(ownEnd == -1) ownEnd = this->m_dataSize; else ownEnd += 1; boundStack.append( qMakePair( qMakePair((uint)ownStart, (uint)ownEnd), rhs.node() ) ); } go(); } operator bool() const { return m_match != -1; } const Data* operator->() const { Q_ASSERT(m_match != -1); return &this->m_data[m_match]; } const Data& operator*() const { Q_ASSERT(m_match != -1); return this->m_data[m_match]; } ConvenientEmbeddedSetTreeFilterIterator& operator++() { Q_ASSERT(m_match != -1); go(); return *this; } #define CHECK_BOUNDS Q_ASSERT(boundStack.back().first.first < 100000 && boundStack.back().first.second < 10000 && boundStack.back().second.first < 100000 && boundStack.back().second.second < 10000 ); private: void go() { if(m_noFiltering) { ++m_match; if((uint)m_match >= this->m_dataSize) m_match = -1; return; } if(m_match != -1) { //Match multiple items in this list to one in the tree m_match = this->firstValidItem(m_match+1, this->m_dataSize); if(m_match != -1 && KeyExtractor::extract(this->m_data[m_match]) == m_matchingTo) return; } m_match = -1; boundsUp: if(boundStack.isEmpty()) return; QPair, typename TreeSet::Node > currentBounds = boundStack.back(); boundStack.pop_back(); uint ownStart = currentBounds.first.first, ownEnd = currentBounds.first.second; typename TreeSet::Node currentNode = currentBounds.second; if(ownStart >= ownEnd) goto boundsUp; if(!currentNode.isValid()) goto boundsUp; while(true) { if(ownStart == ownEnd) goto boundsUp; if(currentNode.isFinalNode()) { // qCDebug(UTIL) << ownStart << ownEnd << "final node" << currentNode.start() * extractor_div_with << currentNode.end() * extractor_div_with; //Check whether the item is contained int bound = lowerBound(*currentNode, ownStart, ownEnd); // qCDebug(UTIL) << "bound:" << bound << (KeyExtractor::extract(this->m_data[bound]) == *currentNode); if(bound != -1 && KeyExtractor::extract(this->m_data[bound]) == *currentNode) { //Got a match m_match = bound; m_matchingTo = *currentNode; m_matchBound = ownEnd; return; }else{ //Mismatch goto boundsUp; } }else{ // qCDebug(UTIL) << ownStart << ownEnd << "node" << currentNode.start() * extractor_div_with << currentNode.end() * extractor_div_with; //This is not a final node, split up the search into the sub-nodes typename TreeSet::Node leftNode = currentNode.leftChild(); typename TreeSet::Node rightNode = currentNode.rightChild(); Q_ASSERT(leftNode.isValid()); Q_ASSERT(rightNode.isValid()); Data2 leftLastItem = leftNode.lastItem(); int rightSearchStart = lowerBound(rightNode.firstItem(), ownStart, ownEnd); if(rightSearchStart == -1) rightSearchStart = ownEnd; int leftSearchLast = lowerBound(leftLastItem, ownStart, rightSearchStart != -1 ? rightSearchStart : ownEnd); if(leftSearchLast == -1) leftSearchLast = rightSearchStart-1; bool recurseLeft = false; if(leftSearchLast > (int)ownStart) { recurseLeft = true; //There must be something in the range ownStart -> leftSearchLast that matches the range }else if((int)ownStart == leftSearchLast) { //Check if the one item item under leftSearchStart is contained in the range Data2 leftFoundStartData = KeyExtractor::extract(this->m_data[ownStart]); recurseLeft = leftFoundStartData < leftLastItem || leftFoundStartData == leftLastItem; } bool recurseRight = false; if(rightSearchStart < (int)ownEnd) recurseRight = true; if(recurseLeft && recurseRight) { //Push the right branch onto the stack, and work in the left one boundStack.append( qMakePair( qMakePair( (uint)rightSearchStart, ownEnd ), rightNode) ); } if(recurseLeft) { currentNode = leftNode; if(leftSearchLast != -1) ownEnd = leftSearchLast+1; }else if(recurseRight) { currentNode = rightNode; ownStart = rightSearchStart; }else{ goto boundsUp; } } } } - ///Returns the first valid index that has an extracted data-value larger or equal to @param data. + ///Returns the first valid index that has an extracted data-value larger or equal to @p data. ///Returns -1 if nothing is found. int lowerBound(const Data2& data, int start, int end) { int currentBound = -1; while(1) { if(start >= end) return currentBound; int center = (start + end)/2; //Skip free items, since they cannot be used for ordering for(; center < end; ) { if(!Handler::isFree(this->m_data[center])) break; ++center; } if(center == end) { end = (start + end)/2; //No non-free items found in second half, so continue search in the other }else{ Data2 centerData = KeyExtractor::extract(this->m_data[center]); //Even if the data equals we must continue searching to the left, since there may be multiple matching if(data == centerData || data < centerData) { currentBound = center; end = (start + end)/2; }else{ //Continue search in second half start = center+1; } } } } //Bounds that yet need to be matched. Always a range in the own vector, and a node that all items in the range are contained in KDevVarLengthArray, typename TreeSet::Node > > boundStack; TreeSet m_rhs; int m_match, m_matchBound; Data2 m_matchingTo; bool m_noFiltering; }; ///Same as above, except that it visits all filtered items with a visitor, instead of iterating over them. ///This is more efficient. The visiting is done directly from within the constructor. template class ConvenientEmbeddedSetTreeFilterVisitor : public ConvenientEmbeddedSetIterator { public: ConvenientEmbeddedSetTreeFilterVisitor() { } typedef QPair, typename TreeSet::Node > Bounds; struct Bound { inline Bound(uint s, uint e, const typename TreeSet::Node& n) : start(s), end(e), node(n) { } Bound() { } uint start; uint end; typename TreeSet::Node node; }; ///@param noFiltering whether the given input is pre-filtered. If this is true, base will be iterated without skipping any items. ConvenientEmbeddedSetTreeFilterVisitor(Visitor& visitor, const ConvenientEmbeddedSetIterator& base, const TreeSet& rhs, bool noFiltering = false) : ConvenientEmbeddedSetIterator(base), m_visitor(visitor), m_rhs(rhs), m_noFiltering(noFiltering) { if(m_noFiltering) { for(uint a = 0; a < this->m_dataSize; ++a) visitor(this->m_data[a]); return; } if(rhs.node().isValid()) { //Correctly initialize the initial bounds int ownStart = lowerBound(rhs.node().firstItem(), 0, this->m_dataSize); if(ownStart == -1) return; int ownEnd = lowerBound(rhs.node().lastItem(), ownStart, this->m_dataSize); if(ownEnd == -1) ownEnd = this->m_dataSize; else ownEnd += 1; go( Bound((uint)ownStart, (uint)ownEnd, rhs.node()) ); } } private: void go( Bound bound ) { KDevVarLengthArray bounds; while(true) { if(bound.start >= bound.end) goto nextBound; if(bound.node.isFinalNode()) { //Check whether the item is contained int b = lowerBound(*bound.node, bound.start, bound.end); if(b != -1) { const Data2& matchTo(*bound.node); if(KeyExtractor::extract(this->m_data[b]) == matchTo) { while(1) { m_visitor(this->m_data[b]); b = this->firstValidItem(b+1, this->m_dataSize); if(b < (int)this->m_dataSize && b != -1 && KeyExtractor::extract(this->m_data[b]) == matchTo) continue; else break; } } } goto nextBound; }else{ //This is not a final node, split up the search into the sub-nodes typename TreeSet::Node leftNode = bound.node.leftChild(); typename TreeSet::Node rightNode = bound.node.rightChild(); Q_ASSERT(leftNode.isValid()); Q_ASSERT(rightNode.isValid()); Data2 leftLastItem = leftNode.lastItem(); int rightSearchStart = lowerBound(rightNode.firstItem(), bound.start, bound.end); if(rightSearchStart == -1) rightSearchStart = bound.end; int leftSearchLast = lowerBound(leftLastItem, bound.start, rightSearchStart != -1 ? rightSearchStart : bound.end); if(leftSearchLast == -1) leftSearchLast = rightSearchStart-1; bool recurseLeft = false; if(leftSearchLast > (int)bound.start) { recurseLeft = true; //There must be something in the range bound.start -> leftSearchLast that matches the range }else if((int)bound.start == leftSearchLast) { //Check if the one item item under leftSearchStart is contained in the range Data2 leftFoundStartData = KeyExtractor::extract(this->m_data[bound.start]); recurseLeft = leftFoundStartData < leftLastItem || leftFoundStartData == leftLastItem; } bool recurseRight = false; if(rightSearchStart < (int)bound.end) recurseRight = true; if(recurseLeft && recurseRight) bounds.append( Bound(rightSearchStart, bound.end, rightNode) ); if(recurseLeft) { bound.node = leftNode; if(leftSearchLast != -1) bound.end = leftSearchLast+1; }else if(recurseRight) { bound.node = rightNode; bound.start = rightSearchStart; }else{ goto nextBound; } continue; } nextBound: if(bounds.isEmpty()) { return; }else{ bound = bounds.back(); bounds.pop_back(); } } } - ///Returns the first valid index that has an extracted data-value larger or equal to @param data. + ///Returns the first valid index that has an extracted data-value larger or equal to @p data. ///Returns -1 if nothing is found. int lowerBound(const Data2& data, int start, int end) { int currentBound = -1; while(1) { if(start >= end) return currentBound; int center = (start + end)/2; //Skip free items, since they cannot be used for ordering for(; center < end; ) { if(!Handler::isFree(this->m_data[center])) break; ++center; } if(center == end) { end = (start + end)/2; //No non-free items found in second half, so continue search in the other }else{ Data2 centerData = KeyExtractor::extract(this->m_data[center]); //Even if the data equals we must continue searching to the left, since there may be multiple matching if(data == centerData || data < centerData) { currentBound = center; end = (start + end)/2; }else{ //Continue search in second half start = center+1; } } } } //Bounds that yet need to be matched. Always a range in the own vector, and a node that all items in the range are contained in Visitor& m_visitor; TreeSet m_rhs; bool m_noFiltering; }; template ConvenientEmbeddedSetIterator ConstantConvenientEmbeddedSet::iterator() const { return ConvenientEmbeddedSetIterator(m_data, m_dataSize, m_centralFreeItem); } ///This is a simple set implementation based on the embedded free tree algorithms. ///The core advantage of the whole thing is that the wole set is represented by a consecutive ///memory-area, and thus can be stored or copied using a simple memcpy. ///However in many cases it's better using the algorithms directly in such cases. /// ///However even for normal tasks this implementation does have some advantages over std::set: ///- Many times faster iteration through contained data ///- Lower memory-usage if the objects are small, since there is no heap allocation overhead ///- Can be combined with other embedded-free-list based sets using algorithms in ConstantConvenientEmbeddedSet ///Disadvantages: ///- Significantly slower insertion template class ConvenientFreeListSet { public: typedef ConvenientEmbeddedSetIterator Iterator; ConvenientFreeListSet() : m_centralFree(-1) { } ///Re-construct a set from its components ConvenientFreeListSet(int centralFreeItem, QVector data) : m_data(data), m_centralFree(centralFreeItem) { } ///You can use this to store the set to disk and later give it together with data() to the constructor, thus reconstructing it. int centralFreeItem() const { return m_centralFree; } const QVector& data() const { return m_data; } void insert(const Data& item) { if(contains(item)) return; KDevelop::EmbeddedTreeAddItem add(m_data.data(), m_data.size(), m_centralFree, item); if((int)add.newItemCount() != (int)m_data.size()) { QVector newData; newData.resize(add.newItemCount()); add.transferData(newData.data(), newData.size()); m_data = newData; } } Iterator iterator() const { return Iterator(m_data.data(), m_data.size(), m_centralFree); } bool contains(const Data& item) const { KDevelop::EmbeddedTreeAlgorithms alg(m_data.data(), m_data.size(), m_centralFree); return alg.indexOf(Data(item)) != -1; } void remove(const Data& item) { KDevelop::EmbeddedTreeRemoveItem remove(m_data.data(), m_data.size(), m_centralFree, item); if((int)remove.newItemCount() != (int)m_data.size()) { QVector newData; newData.resize(remove.newItemCount()); remove.transferData(newData.data(), newData.size()); m_data = newData; } } private: int m_centralFree; QVector m_data; }; } #endif diff --git a/util/embeddedfreetree.h b/util/embeddedfreetree.h index b6df986fd6..03f749d111 100644 --- a/util/embeddedfreetree.h +++ b/util/embeddedfreetree.h @@ -1,889 +1,889 @@ /* This file is part of KDevelop Copyright 2008 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef EMBEDDED_FREE_TREE #define EMBEDDED_FREE_TREE #include "kdevvarlengtharray.h" #include #include #include #include //Uncomment this to search for tree-inconsistencies, however it's very expensive // #define DEBUG_FREEITEM_COUNT debugFreeItemCount(); verifyTreeConsistent(*m_centralFreeItem, 0, m_itemCount); #define DEBUG_FREEITEM_COUNT /** * This file implements algorithms that allow managing a sorted list of items, and managing "free" items * for reuse efficiently in that list. Among those free items a tree is built, and they are traversed * on insertion/removal to manage free items in the tree. * * There is specific needs on the embedded items: * - They must be markable "invalid", so after they are deleted they can stay in their place as invalid items. * - While they are invalid, they still must be able to hold 2 integers, needed for managing the tree of free items. * - One integer is needed for each list to hold a pointer to the central free item. * * Only these functions must be used to manipulate the lists, from the beginning up. First create an empty list * and initialize centralFreeItem with -1, and then you start adding items. * * Since the list is sorted, and each item can be contained only once, these lists actually represent a set. * * EmbeddedTreeAlgorithms implements an efficient "contains" function that uses binary search within the list. */ namespace KDevelop { ///Responsible for handling the items in the list ///This is an example. ItemHandler::rightChild(..) and ItemHandler::leftChild(..) must be values that must be able to hold the count of positive ///values that will be the maximum size of the list, and additionally -1. // template // class ExampleItemHandler { // public: // ExampleItemHandler(const Data& data) : m_data(data) { // } // int ItemHandler::rightChild() const { // Q_ASSERT(0); // } // int ItemHandler::leftChild() const { // Q_ASSERT(0); // } // void ItemHandler::setLeftChild(int child) { // Q_ASSERT(0); // } // void setRightChild(int child) { // Q_ASSERT(0); // } // bool operator<(const StandardItemHandler& rhs) const { // Q_ASSERT(0); // } // //Copies this item into the given one // void copyTo(Data& data) const { // data = m_data; // } // // static void createFreeItem(Data& data) { // data = Data(); // } // // bool isFree() const { // Q_ASSERT(0); // } // // const Data& data() { // } // // private: // const Data& m_data; // }; /** * Use this for several constant algorithms on sorted lists with free-trees * */ template class EmbeddedTreeAlgorithms { public: EmbeddedTreeAlgorithms(const Data* items, uint itemCount, const int& centralFreeItem) : m_items(items), m_itemCount(itemCount), m_centralFreeItem(¢ralFreeItem) { } ~EmbeddedTreeAlgorithms() { } ///Efficiently checks whether the item is contained in the set. ///If it is contained, returns the index. Else, returns -1. int indexOf(const Data& data) { return indexOf(data, 0, m_itemCount); } ///Searches the given item within the specified bounds. int indexOf(const Data& data, uint start, uint end) { while(1) { if(start >= end) return -1; int center = (start + end)/2; //Skip free items, since they cannot be used for ordering for(; center < (int)end; ) { if(!ItemHandler::isFree(m_items[center])) break; ++center; } if(center == (int)end) { end = (start + end)/2; //No non-free items found in second half, so continue search in the other }else{ if(ItemHandler::equals(data, m_items[center])) { return center; }else if(data < m_items[center]) { end = (start + end)/2; }else{ //Continue search in second half start = center+1; } } } } - ///Returns the first valid index that has a data-value larger or equal to @param data. + ///Returns the first valid index that has a data-value larger or equal to @p data. ///Returns -1 if nothing is found. int lowerBound(const Data& data, int start, int end) { int currentBound = -1; while(1) { if(start >= end) return currentBound; int center = (start + end)/2; //Skip free items, since they cannot be used for ordering for(; center < end; ) { if(!ItemHandler::isFree(m_items[center])) break; ++center; } if(center == end) { end = (start + end)/2; //No non-free items found in second half, so continue search in the other }else{ if(ItemHandler::equals(data, m_items[center])) { return center; }else if(data < m_items[center]) { currentBound = center; end = (start + end)/2; }else{ //Continue search in second half start = center+1; } } } } uint countFreeItems() const { return countFreeItemsInternal(*m_centralFreeItem); } uint countFreeItemsNaive() const { uint ret = 0; for(uint a = 0; a < m_itemCount; ++a) { if(ItemHandler::isFree(m_items[a])) ++ret; } return ret; } void verifyOrder() { Data last; for(uint a = 0; a < m_itemCount; ++a) { if(!ItemHandler::isFree(m_items[a])) { if(!ItemHandler::isFree(last)) Q_ASSERT(last < m_items[a]); last = m_items[a]; } } } void verifyTreeConsistent() { verifyTreeConsistentInternal(*m_centralFreeItem, 0, m_itemCount); Q_ASSERT(countFreeItems() == countFreeItemsNaive()); } private: void verifyTreeConsistentInternal(int position, int lowerBound, int upperBound) { if(position == -1) return; Q_ASSERT(lowerBound <= position && position < upperBound); verifyTreeConsistentInternal(ItemHandler::leftChild(m_items[position]), lowerBound, position); verifyTreeConsistentInternal(ItemHandler::rightChild(m_items[position]), position+1, upperBound); } uint countFreeItemsInternal(int item) const { if(item == -1) return 0; return 1 + countFreeItemsInternal(ItemHandler::leftChild(m_items[item])) + countFreeItemsInternal(ItemHandler::rightChild(m_items[item])); } const Data* m_items; uint m_itemCount; const int* m_centralFreeItem; }; /**Use this to add items. * The added item must not be in the set yet! * General usage: * - Construct the object * - Check if newItemCount() equals the previous item-count. If not, construct * a new list as large as newItemCount, and call object.transferData to transfer the data * into the new list. The new size must match the returned newItemCount. * - Either call object.apply(), or let it be called automatically by the destructor. * @param increaseFraction By what fraction the list is increased when it needs to. For example the size will be increased by 1/5 if it's 5. * @param rebuildIfInsertionMoreExpensive The structure is rebuilt completely when an insertion needs a moving around of more than rebuildIfInsertionMoreExpensive times the count of items needed to be moved in worst case in a fresh tree. * After rebuilding the tree, the free space is evenly distributed, and thus insertions require much less moving. * */ template class EmbeddedTreeAddItem { public: EmbeddedTreeAddItem(Data* items, uint itemCount, int& centralFreeItem, const Data& add) : m_add(add), m_items(items), m_itemCount(itemCount), m_centralFreeItem(¢ralFreeItem), m_applied(false), m_needResize(false) { m_needResize = !apply(); } ~EmbeddedTreeAddItem() { if(!m_applied) apply(true); } ///Check this to see whether a new item-count is needed. If this does not equal the given itemCount, then ///the data needs to be transferred into a new list using transferData uint newItemCount() const { if(!m_applied) { if(*m_centralFreeItem == -1) { uint realItemCount = m_itemCount - countFreeItems(*m_centralFreeItem); uint newItemCount = realItemCount + (realItemCount/increaseFraction); if(newItemCount <= m_itemCount) newItemCount = m_itemCount+1; return newItemCount; }else if(m_needResize) { uint realItemCount = m_itemCount - countFreeItems(*m_centralFreeItem); uint newItemCount = realItemCount + (realItemCount/increaseFraction); return newItemCount; } } return m_itemCount; } ///Transfers the data into a new item-list. The size of the new item-list must equal newItemCount() void transferData(Data* newItems, uint newCount, int* newCentralFree = 0) { DEBUG_FREEITEM_COUNT uint currentRealCount = m_itemCount - countFreeItems(*m_centralFreeItem); // Q_ASSERT(currentRealCount + (currentRealCount/increaseFraction) == newCount); //Create a new list where the items from m_items are put into newItems, with the free items evenly //distributed, and a clean balanced free-tree. uint newFreeCount = newCount - currentRealCount; volatile uint freeItemRaster; if(newFreeCount) freeItemRaster = newCount / newFreeCount; else { freeItemRaster = newCount+1; //No free items } ///@todo Do not iterate through all items, instead use the free-tree and memcpy for the ranges between free items. ///Ideally, even the free-tree would be built on-the-fly. Q_ASSERT(freeItemRaster); uint offset = 0; uint insertedValidCount = 0; for(uint a = 0; a < newCount; ++a) { //Create new free items at the end of their raster range if(a % freeItemRaster == (freeItemRaster-1)) { //We need to insert a free item ItemHandler::createFreeItem(newItems[a]); ++offset; }else{ ++insertedValidCount; while(ItemHandler::isFree(m_items[a-offset]) && a-offset < m_itemCount) --offset; Q_ASSERT(a-offset < m_itemCount); newItems[a] = m_items[a-offset]; // Q_ASSERT(!ItemHandler::isFree(newItems[a])); } } Q_ASSERT(insertedValidCount == m_itemCount - countFreeItems(*m_centralFreeItem)); // qCDebug(UTIL) << m_itemCount << newCount << offset; // Q_ASSERT(m_itemCount == newCount-offset); m_items = newItems; m_itemCount = newCount; if(newCentralFree) m_centralFreeItem = newCentralFree; *m_centralFreeItem = buildFreeTree(newFreeCount, freeItemRaster, freeItemRaster-1); // qCDebug(UTIL) << "count of new free items:" << newFreeCount; // Q_ASSERT(countFreeItems( *m_centralFreeItem ) == newFreeCount); DEBUG_FREEITEM_COUNT } ///Tries to put the item into the list. If the insertion would be too inefficient or is not possible, returns false, unless @param force is true bool apply(bool force = false) { if(m_applied) return true; if(*m_centralFreeItem == -1) { Q_ASSERT(!force); return false; } //Find the free item that is nearest to the target position in the item order int previousItem = -1; int currentItem = *m_centralFreeItem; int replaceCurrentWith = -1; //In currentLowerBound and currentUpperBound, we count the smallest contiguous range between free //items that the added items needs to be sorted into. If the range is empty, the item can just be inserted. int currentLowerBound = 0; int currentUpperBound = m_itemCount; //Now go down the chain, always into the items direction while(1) { QPair freeBounds = leftAndRightRealItems(currentItem); const Data& current(m_items[currentItem]); if(freeBounds.first != -1 && m_add < m_items[freeBounds.first]) { //Follow left child currentUpperBound = freeBounds.first+1; if(ItemHandler::leftChild(current) != -1) { //Continue traversing previousItem = currentItem; currentItem = ItemHandler::leftChild(current); }else{ replaceCurrentWith = ItemHandler::rightChild(current); break; } }else if(freeBounds.second != -1 && m_items[freeBounds.second] < m_add) { //Follow right child currentLowerBound = freeBounds.second; if(ItemHandler::rightChild(current) != -1) { //Continue traversing previousItem = currentItem; currentItem = ItemHandler::rightChild(current); }else{ replaceCurrentWith = ItemHandler::leftChild(current); break; } }else{ //We will use this item! So find a replacement for it in the tree, and update the structure force = true; currentLowerBound = currentUpperBound = currentItem; int leftReplaceCandidate = -1, rightReplaceCandidate = -1; if(ItemHandler::leftChild(current) != -1) leftReplaceCandidate = rightMostChild(ItemHandler::leftChild(current)); if(ItemHandler::rightChild(current) != -1) rightReplaceCandidate = leftMostChild(ItemHandler::rightChild(current)); ///@todo it's probably better using lowerBound and upperBound like in the "remove" version //Left and right bounds of all children of current int leftChildBound = leftMostChild(currentItem), rightChildBound = rightMostChild(currentItem); Q_ASSERT(leftChildBound != -1 && rightChildBound != -1); int childCenter = (leftChildBound + rightChildBound)/2; if(leftReplaceCandidate == -1 && rightReplaceCandidate == -1) { //We don't have a replace candidate, since there is no children Q_ASSERT(ItemHandler::leftChild(current) == -1); Q_ASSERT(ItemHandler::rightChild(current) == -1); }else if(rightReplaceCandidate == -1 || abs(leftReplaceCandidate - childCenter) < abs(rightReplaceCandidate - childCenter)) { //pick the left replacement, since it's more central Q_ASSERT(leftReplaceCandidate != -1); replaceCurrentWith = leftReplaceCandidate; Data& replaceWith(m_items[replaceCurrentWith]); if(replaceCurrentWith == ItemHandler::leftChild(current)) { //The left child of replaceWith can just stay as it is, and we just need to add the right child Q_ASSERT(ItemHandler::rightChild(replaceWith) == -1); }else{ takeRightMostChild(ItemHandler::leftChild(current)); //Since we'll be clearing the item, we have to put this childsomewhere else. // Either make it our new "left" child, or make it the new left children "rightmost" child. int addRightMostLeftChild = ItemHandler::leftChild(replaceWith); ItemHandler::setLeftChild(replaceWith, -1); Q_ASSERT(ItemHandler::leftChild(replaceWith) == -1); Q_ASSERT(ItemHandler::rightChild(replaceWith) == -1); if(ItemHandler::leftChild(current) != -1) { Q_ASSERT(rightMostChild(ItemHandler::leftChild(current)) != replaceCurrentWith); Q_ASSERT(ItemHandler::leftChild(current) == -1 || ItemHandler::leftChild(current) < replaceCurrentWith); ItemHandler::setLeftChild(replaceWith, ItemHandler::leftChild(current)); if(addRightMostLeftChild != -1) { int rightMostLeft = rightMostChild(ItemHandler::leftChild(replaceWith)); Q_ASSERT(rightMostLeft != -1); // Q_ASSERT(item(rightMostLeft).ItemHandler::rightChild() == -1); Q_ASSERT(rightMostLeft < addRightMostLeftChild); ItemHandler::setRightChild(m_items[rightMostLeft], addRightMostLeftChild); } }else{ Q_ASSERT(addRightMostLeftChild == -1 || addRightMostLeftChild < replaceCurrentWith); ItemHandler::setLeftChild(replaceWith, addRightMostLeftChild); } } Q_ASSERT(ItemHandler::rightChild(current) == -1 || replaceCurrentWith < ItemHandler::rightChild(current)); ItemHandler::setRightChild(replaceWith, ItemHandler::rightChild(current)); }else{ //pick the right replacement, since it's more central Q_ASSERT(rightReplaceCandidate != -1); replaceCurrentWith = rightReplaceCandidate; Data& replaceWith(m_items[replaceCurrentWith]); if(replaceCurrentWith == ItemHandler::rightChild(current)) { //The right child of replaceWith can just stay as it is, and we just need to add the left child Q_ASSERT(ItemHandler::leftChild(replaceWith) == -1); }else{ takeLeftMostChild(ItemHandler::rightChild(current)); //Since we'll be clearing the item, we have to put this childsomewhere else. // Either make it our new "right" child, or make it the new right children "leftmost" child. int addLeftMostRightChild = ItemHandler::rightChild(replaceWith); ItemHandler::setRightChild(replaceWith, -1); Q_ASSERT(ItemHandler::rightChild(replaceWith) == -1); Q_ASSERT(ItemHandler::leftChild(replaceWith) == -1); if(ItemHandler::rightChild(current) != -1) { Q_ASSERT(leftMostChild(ItemHandler::rightChild(current)) != replaceCurrentWith); Q_ASSERT(ItemHandler::rightChild(current) == -1 || replaceCurrentWith < ItemHandler::rightChild(current)); ItemHandler::setRightChild(replaceWith, ItemHandler::rightChild(current)); if(addLeftMostRightChild != -1) { int leftMostRight = leftMostChild(ItemHandler::rightChild(replaceWith)); Q_ASSERT(leftMostRight != -1); Q_ASSERT(ItemHandler::leftChild(m_items[leftMostRight]) == -1); Q_ASSERT(addLeftMostRightChild < leftMostRight); ItemHandler::setLeftChild(m_items[leftMostRight], addLeftMostRightChild); } }else{ Q_ASSERT(addLeftMostRightChild == -1 || replaceCurrentWith < addLeftMostRightChild); ItemHandler::setRightChild(replaceWith, addLeftMostRightChild); } } Q_ASSERT(ItemHandler::leftChild(current) == -1 || ItemHandler::leftChild(current) < replaceCurrentWith); ItemHandler::setLeftChild(replaceWith, ItemHandler::leftChild(current)); } break; } } //We can insert now //currentItem and previousItem are the two items that best enclose the target item // for(int a = currentLowerBound; a < currentUpperBound; ++a) { // Q_ASSERT(!ItemHandler::isFree(m_items[a])); // } Q_ASSERT(currentItem < currentLowerBound || currentItem >= currentUpperBound); //If the current item is on a border of the bounds, it needs to be inserted in the right position. //Else, the current position already is right, and we only need to copy it in. if(currentLowerBound < currentUpperBound && (currentItem == currentLowerBound-1 || currentItem == currentUpperBound)) { if(!insertSorted(m_add, currentItem, currentLowerBound, currentUpperBound, force)) { return false; } }else{ ItemHandler::copyTo(m_add, m_items[currentItem]); } m_applied = true; //First, take currentItem out of the chain, by replacing it with current.rightChild in the parent if(previousItem != -1) { Data& previous(m_items[previousItem]); if(ItemHandler::leftChild(previous) == currentItem) { Q_ASSERT(replaceCurrentWith == -1 || replaceCurrentWith < previousItem); ItemHandler::setLeftChild(previous, replaceCurrentWith); } else if(ItemHandler::rightChild(previous) == currentItem) { Q_ASSERT(replaceCurrentWith == -1 || previousItem < replaceCurrentWith); ItemHandler::setRightChild(previous, replaceCurrentWith); } else { Q_ASSERT(0); } } else { *m_centralFreeItem = replaceCurrentWith; } return true; DEBUG_FREEITEM_COUNT } private: void verifyTreeConsistent(int position, int lowerBound, int upperBound) { if(position == -1) return; Q_ASSERT(lowerBound <= position && position < upperBound); verifyTreeConsistent(ItemHandler::leftChild(m_items[position]), lowerBound, position); verifyTreeConsistent(ItemHandler::rightChild(m_items[position]), position+1, upperBound); } void debugFreeItemCount() { uint count = 0; for(uint a = 0; a < m_itemCount; ++a) { if(isFree(m_items[a])) ++count; } uint counted = countFreeItems(*m_centralFreeItem); Q_ASSERT(count == counted); Q_UNUSED(counted); } QPair leftAndRightRealItems(int pos) { Q_ASSERT(ItemHandler::isFree(m_items[pos])); int left = -1, right = -1; for(int a = pos-1; a >= 0; --a) { if(!ItemHandler::isFree(m_items[a])) { left = a; break; } } for(uint a = pos+1; a < m_itemCount; ++a) { if(!ItemHandler::isFree(m_items[a])) { right = a; break; } } return qMakePair(left, right); } int buildFreeTree(int count, uint raster, int start) { Q_ASSERT((start % raster) == (raster-1)); Q_ASSERT(count != 0); Q_ASSERT(count <= (int)m_itemCount); if(count == 1) { ItemHandler::createFreeItem(m_items[start]); return start; }else{ int central = start + (count / 2) * raster; int leftCount = count / 2; int midCount = 1; int rightCount = count - leftCount - midCount; Q_ASSERT(leftCount + midCount <= count); ItemHandler::createFreeItem(m_items[central]); Q_ASSERT(ItemHandler::isFree(m_items[central])); int leftFreeTree = buildFreeTree(leftCount, raster, start); Q_ASSERT(leftFreeTree == -1 || leftFreeTree < central); ItemHandler::setLeftChild(m_items[central], leftFreeTree ); if(rightCount > 0) { int rightFreeTree = buildFreeTree(rightCount, raster, central+raster); Q_ASSERT(rightFreeTree == -1 || central < rightFreeTree); ItemHandler::setRightChild(m_items[central], rightFreeTree ); } return central; } } uint countFreeItems(int item) const { if(item == -1) return 0; const Data& current(m_items[item]); return 1 + countFreeItems(ItemHandler::leftChild(current)) + countFreeItems(ItemHandler::rightChild(current)); } int leftMostChild(int pos) const { while(1) { if(ItemHandler::leftChild(m_items[pos]) != -1) pos = ItemHandler::leftChild(m_items[pos]); else return pos; } } int takeLeftMostChild(int pos) const { int parent = -1; while(1) { if(ItemHandler::leftChild(m_items[pos]) != -1) { parent = pos; pos = ItemHandler::leftChild(m_items[pos]); } else { ItemHandler::setLeftChild(m_items[parent], -1); return pos; } } } int rightMostChild(int pos) const { while(1) { if(ItemHandler::rightChild(m_items[pos]) != -1) pos = ItemHandler::rightChild(m_items[pos]); else return pos; } } int takeRightMostChild(int pos) const { int parent = -1; while(1) { if(ItemHandler::rightChild(m_items[pos]) != -1) { parent = pos; pos = ItemHandler::rightChild(m_items[pos]); } else { ItemHandler::setRightChild(m_items[parent], -1); return pos; } } } ///Maximum "moving" out of the way of items without forcing a complete rebuild of the list inline int maxMoveAround() const { return increaseFraction * rebuildIfInsertionMoreExpensive; } - ///Inserts the given data item into position pos, and updates the sorting - ///@param otherBound can be another empty item, that together with @param pos represents the closest enclosure of the target position + ///Inserts the given data item into position @p pos, and updates the sorting + ///@param data can be another empty item, that together with @p pos represents the closest enclosure of the target position ///@return Whether the item could be inserted. It is not inserted if bool insertSorted(const Data& data, int pos, int start, int end, bool force) { if(pos < start) start = pos; if(pos >= end) end = pos+1; /* for(int a = start; a < end; ++a) { if(a != pos) { Q_ASSERT(!ItemHandler::isFree(m_items[a])); } }*/ EmbeddedTreeAlgorithms alg(m_items, m_itemCount, *m_centralFreeItem); int bound = alg.lowerBound(data, start, end); //Now find the position that should be taken if(bound == -1) bound = end; //Now our item should end up right before bound int target; //bound cannot be pos, because pos is invalid Q_ASSERT(bound != pos); //Shuffle around the item at the free pos, so reference counting in constructors/destructors is not screwed up char backup[sizeof(Data)]; memcpy(backup, m_items+pos, sizeof(Data)); if(bound < pos) { if(!force && pos-bound > maxMoveAround()) { // qCDebug(UTIL) << "increasing because" << pos-bound << ">" << maxMoveAround() << "left free items:" << countFreeItems(*m_centralFreeItem) << "target free items:" << (m_itemCount-countFreeItems(*m_centralFreeItem))/increaseFraction; return false; } //Move [bound, pos) one to right, and insert at bound memmove(m_items+bound+1, m_items+bound, sizeof(Data)*(pos-bound)); target = bound; }else { Q_ASSERT(bound > pos); if(!force && bound-pos-1 > maxMoveAround()) { // qCDebug(UTIL) << "increasing because" << bound-pos-1 << ">" << maxMoveAround() << "left free items:" << countFreeItems(*m_centralFreeItem)<< "target free items:" << (m_itemCount-countFreeItems(*m_centralFreeItem))/increaseFraction; return false; } //Move (pos, bound) one to left, and insert at bound-1 memmove(m_items+pos, m_items+pos+1, sizeof(Data)*(bound-pos-1)); target = bound-1; } memcpy(m_items+target, backup, sizeof(Data)); ItemHandler::copyTo(data, m_items[target]); return true; } const Data& m_add; Data* m_items; uint m_itemCount; int* m_centralFreeItem; bool m_applied, m_needResize; }; /**Use this to add items. * The removed item must be in the set! * General usage: * - Construct the object * - Check if newItemCount() equals the previous item-count. If not, construct * a new list as large as newItemCount, and call object.transferData to transfer the data * into the new list. The new size must match the returned newItemCount. * However this may also be ignored if the memory-saving is not wanted in that moment. * */ template class EmbeddedTreeRemoveItem { public: EmbeddedTreeRemoveItem(Data* items, uint itemCount, int& centralFreeItem, const Data& remove) : m_remove(remove), m_items(items), m_itemCount(itemCount), m_centralFreeItem(¢ralFreeItem), m_insertedAtDepth(0) { apply(); } ~EmbeddedTreeRemoveItem() { } ///Check this to see whether a new item-count is needed. If this does not equal the given itemCount, then ///the data needs to be transferred into a new list using transferData uint newItemCount() const { uint maxFreeItems = ((m_itemCount / increaseFraction)*3)/2 + 1; //First we approximate the count of free items using the insertion depth if((1u << m_insertedAtDepth) >= maxFreeItems) { uint freeCount = countFreeItems(*m_centralFreeItem); if(freeCount > maxFreeItems || freeCount == m_itemCount) { return m_itemCount - freeCount; } } return m_itemCount; } ///Transfers the data into a new item-list. The size of the new item-list must equal newItemCount() void transferData(Data* newItems, uint newCount, int* newCentralFree = 0) { DEBUG_FREEITEM_COUNT //We only transfer into a new list when all the free items are used up //Create a list where only the non-free items exist uint offset = 0; for(uint a = 0; a < m_itemCount; ++a) { if(!ItemHandler::isFree(m_items[a])) { newItems[offset] = m_items[a]; ++offset; } } Q_ASSERT(offset == newCount); if(newCentralFree) m_centralFreeItem = newCentralFree; *m_centralFreeItem = -1; m_items = newItems; m_itemCount = newCount; DEBUG_FREEITEM_COUNT } private: void verifyTreeConsistent(int position, int lowerBound, int upperBound) { if(position == -1) return; Q_ASSERT(lowerBound <= position && position < upperBound); verifyTreeConsistent(ItemHandler::leftChild(m_items[position]), lowerBound, position); verifyTreeConsistent(ItemHandler::rightChild(m_items[position]), position+1, upperBound); } uint countFreeItems(int item) const { if(item == -1) return 0; const Data& current(m_items[item]); return 1 + countFreeItems(ItemHandler::leftChild(current)) + countFreeItems(ItemHandler::rightChild(current)); } int findItem(const Data& data, uint start, uint end) { EmbeddedTreeAlgorithms alg(m_items, m_itemCount, *m_centralFreeItem); return alg.indexOf(data, start, end); } void apply() { DEBUG_FREEITEM_COUNT int removeIndex = findItem(m_remove, 0, m_itemCount); Q_ASSERT(removeIndex != -1); Q_ASSERT(!ItemHandler::isFree(m_items[removeIndex])); //Find the free item that is nearest to the target position in the item order int currentItem = *m_centralFreeItem; int lowerBound = 0; //The minimum position the searched item can have int upperBound = m_itemCount; //The lowest known position the searched item can _not_ have if(*m_centralFreeItem == -1) { *m_centralFreeItem = removeIndex; Q_ASSERT(*m_centralFreeItem != -1); ItemHandler::createFreeItem(m_items[*m_centralFreeItem]); return; } //Now go down the chain, always into the items direction ///@todo make the structure better: Don't just put left/right child, but also swap when neede /// to balance the tree while(1) { Q_ASSERT(removeIndex != currentItem); Data& current(m_items[currentItem]); ++m_insertedAtDepth; if(removeIndex < currentItem) { upperBound = currentItem; //Follow left child if(ItemHandler::leftChild(current) != -1) { //Continue traversing currentItem = ItemHandler::leftChild(current); Q_ASSERT(currentItem >= lowerBound && currentItem < upperBound); }else{ //The to-be deleted item is before current, and can be added as left child to current int item = findItem(m_remove, lowerBound, upperBound); Q_ASSERT(item == removeIndex); ItemHandler::createFreeItem(m_items[item]); Q_ASSERT(item == -1 || item < currentItem); ItemHandler::setLeftChild(current, item); Q_ASSERT(item >= lowerBound && item < upperBound); break; } }else{ lowerBound = currentItem+1; //Follow right child if(ItemHandler::rightChild(current) != -1) { //Continue traversing currentItem = ItemHandler::rightChild(current); Q_ASSERT(currentItem >= lowerBound && currentItem < upperBound); }else{ //The to-be deleted item is behind current, and can be added as right child to current int item = findItem(m_remove, lowerBound, upperBound); Q_ASSERT(item == removeIndex); ItemHandler::createFreeItem(m_items[item]); Q_ASSERT(item == -1 || currentItem < item); ItemHandler::setRightChild(current, item); Q_ASSERT(item >= lowerBound && item < upperBound); break; } } } DEBUG_FREEITEM_COUNT } void debugFreeItemCount() { uint count = 0; for(uint a = 0; a < m_itemCount; ++a) { if(ItemHandler::isFree(m_items[a])) ++count; } uint counted = countFreeItems(*m_centralFreeItem); Q_ASSERT(count == counted); Q_UNUSED(counted); } const Data& m_remove; Data* m_items; uint m_itemCount; int* m_centralFreeItem; int m_insertedAtDepth; }; } #endif diff --git a/util/formattinghelpers.h b/util/formattinghelpers.h index 04a6bab520..83a1e0ea7a 100644 --- a/util/formattinghelpers.h +++ b/util/formattinghelpers.h @@ -1,47 +1,46 @@ /* This file is part of KDevelop * Copyright 2011 David Nolden 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) 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDEVPLATFORM_FORMATTINGHELPERS_H #define KDEVPLATFORM_FORMATTINGHELPERS_H #include "utilexport.h" #include class QString; namespace KDevelop { /** * Helps extracting a re-formatted version of a text fragment, within a specific left and right context. * The re-formatting must be an operation which only changes whitespace, and keeps whitespace boundaries * between identifiers intact. If this is not the case, the original text is returned. - * + * * @param formattedMergedText The re-formatted merged text: format(leftContext + text + rightContext) - * @param originalMergedText The original merged text: (leftContext + text + rightContext) * @param text The text fragment of which the re-formatted version will be returned * @param leftContext The left context of the text fragment * @param rightContext The right context of the text fragment * @param tabWidth The width of one tab, required while matching tabs vs. spaces * @param fuzzyCharacters Characters which are ignored in case of mismatches * * @return The re-formatted version of @p text * */ KDEVPLATFORMUTIL_EXPORT QString extractFormattedTextFromContext(const QString& formattedMergedText, const QString& text, const QString& leftContext, const QString& rightContext, int tabWidth = 4, const QString& fuzzyCharacters = QStringLiteral("{}()/*/")); } #endif // KDEVPLATFORM_FORMATTINGHELPERS_H diff --git a/util/pushvalue.h b/util/pushvalue.h index 41434bf31b..d33a2a8776 100644 --- a/util/pushvalue.h +++ b/util/pushvalue.h @@ -1,61 +1,61 @@ /* Copyright 2007-2008 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDEVPLATFORM_PUSHVALUE_H #define KDEVPLATFORM_PUSHVALUE_H #include /** * A simple helper-class that does the following: - * Backup the given reference-value given through @param ptr, - * replace it with the value given through @param push, + * Backup the given reference-value given through @p ptr, + * replace it with the value given through @p push, * restore the backed up value back on destruction. * * NOTE: This is _not_ a direct alias of QScopedValueRollback, * the behavior of the constructor is different: * PushValue will *always* push, PushPositiveValue will only * push if the value evaluates to true. QScopedValueRollback * will *always* push with the ctor that takes a new value, * and *never* with the ctor that just takes a ref. **/ template class PushValue : public QScopedValueRollback { public: PushValue(Value& ref, const Value& newValue = Value()) : QScopedValueRollback(ref, newValue) { } }; ///Only difference to PushValue: The value is only replaced if the new value is positive template class PushPositiveValue : public QScopedValueRollback { public: PushPositiveValue(Value& ref, const Value& newValue = Value()) : QScopedValueRollback(ref) { if (newValue) { ref = newValue; } } }; #endif diff --git a/vcs/vcsannotation.cpp b/vcs/vcsannotation.cpp index 7b94a91d1b..3179bb076e 100644 --- a/vcs/vcsannotation.cpp +++ b/vcs/vcsannotation.cpp @@ -1,210 +1,210 @@ /*************************************************************************** * This file is part of KDevelop * * Copyright 2007 Andreas Pakulat * * * * 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. * ***************************************************************************/ #include "vcsannotation.h" #include #include #include #include "vcsrevision.h" namespace KDevelop { class VcsAnnotationPrivate { public: QHash lines; QUrl location; }; class VcsAnnotationLinePrivate { public: QString author; QDateTime date; QString text; QString line; VcsRevision revision; QString message; int lineno; }; VcsAnnotationLine::VcsAnnotationLine() : d( new VcsAnnotationLinePrivate ) { d->lineno = -1; } VcsAnnotationLine::VcsAnnotationLine( const VcsAnnotationLine& rhs ) : d( new VcsAnnotationLinePrivate ) { d->author = rhs.d->author; d->line = rhs.d->line; d->revision = rhs.d->revision; d->lineno = rhs.d->lineno; d->date = rhs.d->date; d->text = rhs.d->text; d->message = rhs.d->message; } VcsAnnotationLine::~VcsAnnotationLine() { delete d; } int VcsAnnotationLine::lineNumber() const { return d->lineno; } QString VcsAnnotationLine::text() const { return d->text; } QString VcsAnnotationLine::author() const { return d->author; } VcsRevision VcsAnnotationLine::revision() const { return d->revision; } QDateTime VcsAnnotationLine::date() const { return d->date; } void VcsAnnotationLine::setLineNumber( int lineno ) { d->lineno = lineno; } void VcsAnnotationLine::setText( const QString& text ) { d->text = text; } void VcsAnnotationLine::setAuthor( const QString& author ) { d->author = author; } -void VcsAnnotationLine::setRevision( const VcsRevision& revision ) +void KDevelop::VcsAnnotationLine::setRevision( const KDevelop::VcsRevision& revision ) { d->revision = revision; } void VcsAnnotationLine::setDate( const QDateTime& date ) { d->date = date; } VcsAnnotationLine& VcsAnnotationLine::operator=( const VcsAnnotationLine& rhs) { if(this == &rhs) return *this; d->author = rhs.d->author; d->line = rhs.d->line; d->revision = rhs.d->revision; d->lineno = rhs.d->lineno; d->date = rhs.d->date; d->text = rhs.d->text; d->message = rhs.d->message; return *this; } QString VcsAnnotationLine::commitMessage() const { return d->message; } void VcsAnnotationLine::setCommitMessage ( const QString& msg ) { d->message = msg; } VcsAnnotation::VcsAnnotation() : d(new VcsAnnotationPrivate) { } VcsAnnotation::VcsAnnotation( const VcsAnnotation& rhs ) : d(new VcsAnnotationPrivate) { d->lines = rhs.d->lines; d->location = rhs.d->location; } VcsAnnotation::~VcsAnnotation() { delete d; } QUrl VcsAnnotation::location() const { return d->location; } int VcsAnnotation::lineCount() const { return d->lines.count(); } void VcsAnnotation::insertLine( int lineno, const VcsAnnotationLine& line ) { if( lineno < 0 ) { return; } d->lines.insert( lineno, line ); } void VcsAnnotation::setLocation(const QUrl& u) { d->location = u; } VcsAnnotationLine VcsAnnotation::line( int lineno ) const { return d->lines[lineno]; } VcsAnnotation& VcsAnnotation::operator=( const VcsAnnotation& rhs) { if(this == &rhs) return *this; d->location = rhs.d->location; d->lines = rhs.d->lines; return *this; } bool VcsAnnotation::containsLine( int lineno ) const { return d->lines.contains( lineno ); } } diff --git a/vcs/vcsannotation.h b/vcs/vcsannotation.h index 3a22e92164..a91c8866d9 100644 --- a/vcs/vcsannotation.h +++ b/vcs/vcsannotation.h @@ -1,161 +1,161 @@ /* This file is part of KDevelop * * Copyright 2007 Andreas Pakulat * Copyright 2007 Matthew Woehlke * * 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) 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_VCSANNOTATION_H #define KDEVPLATFORM_VCSANNOTATION_H #include "vcsexport.h" #include class QString; class QDateTime; class QUrl; namespace KDevelop { class VcsRevision; /** * Annotation information for a line of a version controlled file */ class KDEVPLATFORMVCS_EXPORT VcsAnnotationLine { public: VcsAnnotationLine(); VcsAnnotationLine( const VcsAnnotationLine& ); virtual ~VcsAnnotationLine(); /** * @return the line number of this annotation line */ int lineNumber() const; /** * @return the text of this line */ QString text() const; /** * @return the author that last changed this line */ QString author() const; /** * @return the revision this line was last changed */ VcsRevision revision() const; /** * @return the date of the last change to this line */ QDateTime date() const; /** * @return the commit message of the revision in this line */ QString commitMessage() const; /** * set the line number of this annotation line * @param lineno the line number */ void setLineNumber( int lineno ); /** * set the text of this annotation line * @param text the text of the line */ - void setText( const QString& ); + void setText( const QString& text ); /** * set the author of this annotation line * @param author the author of the last change */ - void setAuthor( const QString& ); + void setAuthor( const QString& author ); /** * set the revision of this annotation line * @param revision the revision of the last change */ - void setRevision( const VcsRevision& ); + void setRevision( const VcsRevision& revision ); /** * set the date of this annotation line * @param date the date of the last change */ - void setDate( const QDateTime& ); + void setDate( const QDateTime& date ); /** * set the commit message of the revision in this * line * @param msg the message of the commit */ void setCommitMessage( const QString& msg ); VcsAnnotationLine& operator=( const VcsAnnotationLine& rhs); private: class VcsAnnotationLinePrivate* d; }; /** * Annotations for a local file. * * This class lets the user fetch information for each line of a local file, * including date of last change, author of last change and revision of * last change to the line. */ class KDEVPLATFORMVCS_EXPORT VcsAnnotation { public: VcsAnnotation(); VcsAnnotation(const VcsAnnotation&); virtual ~VcsAnnotation(); /** * @return the local url of the file */ QUrl location() const; /** * @return the number of lines in the file */ int lineCount() const; /** * retrieve the annotation line for the given number */ VcsAnnotationLine line( int linenumber ) const; /** * insert a new line to list of lines using * the parameters * * @param lineno the line for which to insert the content * @param line the annotation line that should be inserted * */ - void insertLine( int lineno, const VcsAnnotationLine& ); + void insertLine( int lineno, const VcsAnnotationLine& line ); /** * @param location the location of the file */ - void setLocation( const QUrl& ); + void setLocation( const QUrl& location ); bool containsLine( int lineno ) const; VcsAnnotation& operator=( const VcsAnnotation& rhs); private: class VcsAnnotationPrivate* const d; }; } Q_DECLARE_METATYPE( KDevelop::VcsAnnotation ) Q_DECLARE_METATYPE( KDevelop::VcsAnnotationLine ) #endif diff --git a/vcs/vcslocation.h b/vcs/vcslocation.h index 6a3c5431ac..cb232d1ad3 100644 --- a/vcs/vcslocation.h +++ b/vcs/vcslocation.h @@ -1,163 +1,163 @@ /* This file is part of KDevelop * * Copyright 2007 Andreas Pakulat * Copyright 2007 Matthew Woehlke * * 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) 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_VCSLOCATION_H #define KDEVPLATFORM_VCSLOCATION_H #include #include #include class QVariant; #include "vcsexport.h" namespace KDevelop { /** * Denotes a local or repository location for a Vcs system. * * For the RepositoryLocation type, most of the information * is vcs-specific. */ class KDEVPLATFORMVCS_EXPORT VcsLocation { public: enum LocationType { LocalLocation = 0 /**< this is a local location */, RepositoryLocation = 1 /**< this is a repository location */ }; VcsLocation(); explicit VcsLocation( const QUrl& ); explicit VcsLocation( const QString& ); ~VcsLocation(); VcsLocation( const VcsLocation& ); VcsLocation& operator=( const VcsLocation& ); /** * @returns Local url if this location is a LocalLocation */ QUrl localUrl() const; /** * Returns a string for the repository, usually this identifies the server. * @returns a vcs-implementation-specific string identifying the server */ QString repositoryServer() const; /** * Returns the module or module path inside the server. * @returns a vcs-implementation-specific string identifying the module */ QString repositoryModule() const; /** * Identifies the tag which this location belongs to. * @returns a vcs-implementation-specific string identifying the tag */ QString repositoryTag() const; /** * Identifies the branch to which this location belongs to. * @returns a vcs-implementation-specific string identifying the branch */ QString repositoryBranch() const; /** * This can define a path relative to the module. This is used * when identifying a subdirectory or file inside a repository location * @returns a path relative to module */ QString repositoryPath() const; /** * @returns the type of this location */ LocationType type() const; /** * Set the local url for this location, automatically sets the type to LocalLocation * @param url the local url */ void setLocalUrl( const QUrl& url ); /** * Set the server string for this location, automatically sets the type to RepositoryLocation */ void setRepositoryServer( const QString& ); /** * Set the module for this location, automatically sets the type to RepositoryLocation */ void setRepositoryModule( const QString& ); /** * Set the branch string for this location, automatically sets the type to RepositoryLocation */ void setRepositoryBranch( const QString& ); /** * Set the tag string for this location, automatically sets the type to RepositoryLocation */ void setRepositoryTag( const QString& ); /** * Set the path for this location, automatically sets the type to RepositoryLocation */ void setRepositoryPath( const QString& ); /** * Allows to add vcs-specific data to this location. * Automatically sets the type to RepositoryLocation * @param data the vcs-specific data */ - void setUserData( const QVariant& ); + void setUserData( const QVariant& data ); /** * retrieve vcs-specific data */ QVariant userData() const; bool operator==( const KDevelop::VcsLocation& ); bool isValid() const; private: class VcsLocationPrivate* d; }; inline uint qHash( const KDevelop::VcsLocation& loc ) { if( loc.type() == KDevelop::VcsLocation::LocalLocation ) { return qHash(loc.localUrl()); }else { return qHash(loc.repositoryServer()); } } inline bool operator==( const KDevelop::VcsLocation& lhs, const KDevelop::VcsLocation& rhs ) { return( lhs.type() == rhs.type() && lhs.repositoryServer() == rhs.repositoryServer() && lhs.localUrl() == rhs.localUrl() ); } } Q_DECLARE_METATYPE( KDevelop::VcsLocation ) #endif diff --git a/vcs/vcsstatusinfo.h b/vcs/vcsstatusinfo.h index d0e8736cf6..107c69c392 100644 --- a/vcs/vcsstatusinfo.h +++ b/vcs/vcsstatusinfo.h @@ -1,102 +1,102 @@ /* This file is part of KDevelop * * Copyright 2007 Andreas Pakulat * Copyright 2007 Matthew Woehlke * * 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) 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_VCSSTATUSINFO_H #define KDEVPLATFORM_VCSSTATUSINFO_H #include #include #include "vcsexport.h" class QString; class QStringList; namespace KDevelop { /** * * Class that encapsulates status information * for one local url. * * The extendedState functions allow to transport * extended status information * * Note for VCS implementations: * If you want to use this class in queued signal/slot connections * you should call qRegisterMetaType() * in the constructor of the plugin class */ class KDEVPLATFORMVCS_EXPORT VcsStatusInfo { public: /** * Status of a local file */ enum State { ItemUnknown = 0 /**< No VCS information about a file is known (or file is not under VCS control). */, ItemUpToDate = 1 /**< Item was updated or it is already at up to date version. */, ItemAdded = 2 /**< Item was added to the repository but not committed. */, ItemModified = 3 /**< Item was modified locally. */, ItemDeleted = 4 /**< Item is scheduled to be deleted. */, ItemHasConflicts = 8 /**< Local version has conflicts that need to be resolved before commit. */, ItemUserState = 1000 /**< special states for individual vcs implementations should use this as base. */ }; VcsStatusInfo(); virtual ~VcsStatusInfo(); VcsStatusInfo(const VcsStatusInfo&); /** * retrieves the url of this status information item * @return the url */ QUrl url() const; /** * Change the url of this status information item * @param url the url */ - void setUrl( const QUrl& ); + void setUrl( const QUrl& url ); VcsStatusInfo::State state() const; void setState( VcsStatusInfo::State ); int extendedState() const; void setExtendedState( int ); VcsStatusInfo& operator=( const VcsStatusInfo& rhs); bool operator==( const KDevelop::VcsStatusInfo& rhs) const; bool operator!=( const KDevelop::VcsStatusInfo& rhs) const; private: class VcsStatusInfoPrivate* d; }; } Q_DECLARE_METATYPE( KDevelop::VcsStatusInfo ) KDEVPLATFORMVCS_EXPORT QDebug operator<<(QDebug s, const KDevelop::VcsStatusInfo& statusInfo); #endif