diff --git a/kdevplatform/language/codecompletion/abstractincludefilecompletionitem.h b/kdevplatform/language/codecompletion/abstractincludefilecompletionitem.h index 7cc17dfdfc..e3a5fdf821 100644 --- a/kdevplatform/language/codecompletion/abstractincludefilecompletionitem.h +++ b/kdevplatform/language/codecompletion/abstractincludefilecompletionitem.h @@ -1,96 +1,95 @@ /* * KDevelop Code Completion Support for file includes * * 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_ABSTRACTINCLUDEFILECOMPLETIONITEM_H #define KDEVPLATFORM_ABSTRACTINCLUDEFILECOMPLETIONITEM_H #include "codecompletionitem.h" #include #include "../util/includeitem.h" #include "../duchain/duchain.h" #include "../duchain/duchainlock.h" #include "codecompletionmodel.h" namespace KDevelop { //A completion item used for completing include-files template class AbstractIncludeFileCompletionItem : public CompletionTreeItem { public: AbstractIncludeFileCompletionItem(const IncludeItem& include) : includeItem(include) { } virtual QVariant data(const QModelIndex& index, int role, const KDevelop::CodeCompletionModel* model) const { DUChainReadLocker lock(DUChain::lock(), 500); if(!lock.locked()) { qDebug() << "Failed to lock the du-chain in time"; return QVariant(); } const IncludeItem& item( includeItem ); switch (role) { case CodeCompletionModel::IsExpandable: return QVariant(true); case CodeCompletionModel::ExpandingWidget: { NavigationWidget* nav = new NavigationWidget(item, model->currentTopContext()); - model->addNavigationWidget(this, nav); QVariant v; v.setValue((QWidget*)nav); return v; } case Qt::DisplayRole: switch (index.column()) { case CodeCompletionModel::Prefix: if(item.isDirectory) return QStringLiteral("directory"); else return QStringLiteral("file"); case CodeCompletionModel::Name: { return item.isDirectory ? (item.name + QLatin1Char('/')) : item.name; } } break; case CodeCompletionModel::ItemSelected: { return QVariant( NavigationWidget::shortDescription(item) ); } } return QVariant(); } virtual void execute(KTextEditor::View* view, const KTextEditor::Range& word) = 0; virtual int inheritanceDepth() const { return includeItem.pathNumber; } virtual int argumentHintDepth() const { return 0; } IncludeItem includeItem; }; } #endif // KDEVPLATFORM_ABSTRACTINCLUDEFILECOMPLETIONITEM_H diff --git a/kdevplatform/language/codecompletion/codecompletionitem.h b/kdevplatform/language/codecompletion/codecompletionitem.h index a6ecc14b48..3d35598a78 100644 --- a/kdevplatform/language/codecompletion/codecompletionitem.h +++ b/kdevplatform/language/codecompletion/codecompletionitem.h @@ -1,151 +1,150 @@ /* * 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" 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 struct KDEVPLATFORMLANGUAGE_EXPORT CompletionCustomGroupNode : public CompletionTreeNode { /// @param inheritanceDepth See KTextEditor::CodeCompletionModel::GroupRole explicit CompletionCustomGroupNode(const QString& groupName, int inheritanceDepth = 700); int inheritanceDepth; }; typedef QExplicitlySharedDataPointer CompletionTreeItemPointer; typedef QExplicitlySharedDataPointer CompletionTreeElementPointer; } Q_DECLARE_METATYPE(KDevelop::CompletionTreeElementPointer); #endif diff --git a/kdevplatform/language/codecompletion/codecompletionmodel.cpp b/kdevplatform/language/codecompletion/codecompletionmodel.cpp index 214bd17909..2420040ff3 100644 --- a/kdevplatform/language/codecompletion/codecompletionmodel.cpp +++ b/kdevplatform/language/codecompletion/codecompletionmodel.cpp @@ -1,440 +1,430 @@ /* * KDevelop Generic Code Completion Support * * Copyright 2006-2008 Hamish Rodda * 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. */ #include "codecompletionmodel.h" #include #include #include #include "../duchain/declaration.h" #include "../duchain/classfunctiondeclaration.h" #include "../duchain/ducontext.h" #include "../duchain/duchain.h" #include "../duchain/namespacealiasdeclaration.h" #include "../duchain/parsingenvironment.h" #include "../duchain/duchainlock.h" #include "../duchain/duchainbase.h" #include "../duchain/topducontext.h" #include "../duchain/duchainutils.h" #include "../interfaces/quickopendataprovider.h" #include "../interfaces/icore.h" #include "../interfaces/ilanguagecontroller.h" #include "../interfaces/icompletionsettings.h" #include #include "codecompletionworker.h" #include "codecompletioncontext.h" #include using namespace KTextEditor; //Multi-threaded completion creates some multi-threading related crashes, and sometimes shows the completions in the wrong position if the cursor was moved // #define SINGLE_THREADED_COMPLETION namespace KDevelop { class CompletionWorkerThread : public QThread { Q_OBJECT public: explicit CompletionWorkerThread(CodeCompletionModel* model) : QThread(model), m_model(model), m_worker(m_model->createCompletionWorker()) { Q_ASSERT(m_worker->parent() == nullptr); // Must be null, else we cannot change the thread affinity! m_worker->moveToThread(this); Q_ASSERT(m_worker->thread() == this); } ~CompletionWorkerThread() override { delete m_worker; } void run () override { //We connect directly, so we can do the pre-grouping within the background thread connect(m_worker, &CodeCompletionWorker::foundDeclarationsReal, m_model, &CodeCompletionModel::foundDeclarations, Qt::QueuedConnection); connect(m_model, &CodeCompletionModel::completionsNeeded, m_worker, static_cast&, const Cursor&, View*)>(&CodeCompletionWorker::computeCompletions), Qt::QueuedConnection); connect(m_model, &CodeCompletionModel::doSpecialProcessingInBackground, m_worker, &CodeCompletionWorker::doSpecialProcessing); exec(); } CodeCompletionModel* m_model; CodeCompletionWorker* m_worker; }; bool CodeCompletionModel::forceWaitForModel() { return m_forceWaitForModel; } void CodeCompletionModel::setForceWaitForModel(bool wait) { m_forceWaitForModel = wait; } CodeCompletionModel::CodeCompletionModel( QObject * parent ) : KTextEditor::CodeCompletionModel(parent) , m_forceWaitForModel(false) , m_fullCompletion(true) , m_mutex(new QMutex) , m_thread(nullptr) { qRegisterMetaType(); } void CodeCompletionModel::initialize() { if(!m_thread) { m_thread = new CompletionWorkerThread(this); #ifdef SINGLE_THREADED_COMPLETION m_thread->m_worker = createCompletionWorker(); #endif m_thread->start(); } } CodeCompletionModel::~CodeCompletionModel() { if(m_thread->m_worker) m_thread->m_worker->abortCurrentCompletion(); m_thread->quit(); m_thread->wait(); delete m_thread; delete m_mutex; } -void CodeCompletionModel::addNavigationWidget(const CompletionTreeElement* element, QWidget* widget) const -{ - Q_ASSERT(dynamic_cast(widget)); - m_navigationWidgets[element] = widget; -} - - bool CodeCompletionModel::fullCompletion() const { return m_fullCompletion; } KDevelop::CodeCompletionWorker* CodeCompletionModel::worker() const { return m_thread->m_worker; } void CodeCompletionModel::clear() { beginResetModel(); m_completionItems.clear(); - m_navigationWidgets.clear(); m_completionContext.reset(); endResetModel(); } void CodeCompletionModel::completionInvokedInternal(KTextEditor::View* view, const KTextEditor::Range& range, InvocationType invocationType, const QUrl& url) { Q_ASSERT(m_thread == worker()->thread()); Q_UNUSED(invocationType) DUChainReadLocker lock(DUChain::lock(), 400); if( !lock.locked() ) { qCDebug(LANGUAGE) << "could not lock du-chain in time"; return; } TopDUContext* top = DUChainUtils::standardContextForUrl( url ); if(!top) { qCDebug(LANGUAGE) << "================== NO CONTEXT FOUND ======================="; beginResetModel(); m_completionItems.clear(); - m_navigationWidgets.clear(); endResetModel(); qCDebug(LANGUAGE) << "Completion invoked for unknown context. Document:" << url << ", Known documents:" << DUChain::self()->documents(); return; } setCurrentTopContext(TopDUContextPointer(top)); RangeInRevision rangeInRevision = top->transformToLocalRevision(KTextEditor::Range(range)); qCDebug(LANGUAGE) << "completion invoked for context" << (DUContext*)top; if( top->parsingEnvironmentFile() && top->parsingEnvironmentFile()->modificationRevision() != ModificationRevision::revisionForFile(IndexedString(url.toString())) ) { qCDebug(LANGUAGE) << "Found context is not current."; } DUContextPointer thisContext; { qCDebug(LANGUAGE) << "apply specialization:" << range.start(); thisContext = SpecializationStore::self().applySpecialization(top->findContextAt(rangeInRevision.start), top); if ( thisContext ) { qCDebug(LANGUAGE) << "after specialization:" << thisContext->localScopeIdentifier().toString() << thisContext->rangeInCurrentRevision(); } else { thisContext = top; } qCDebug(LANGUAGE) << "context is set to" << thisContext.data(); } lock.unlock(); if(m_forceWaitForModel) emit waitForReset(); emit completionsNeeded(thisContext, range.start(), view); } void CodeCompletionModel::completionInvoked(KTextEditor::View* view, const KTextEditor::Range& range, InvocationType invocationType) { //If this triggers, initialize() has not been called after creation. Q_ASSERT(m_thread); KDevelop::ICompletionSettings::CompletionLevel level = KDevelop::ICore::self()->languageController()->completionSettings()->completionLevel(); if(level == KDevelop::ICompletionSettings::AlwaysFull || (invocationType != AutomaticInvocation && level == KDevelop::ICompletionSettings::MinimalWhenAutomatic)) m_fullCompletion = true; else m_fullCompletion = false; //Only use grouping in full completion mode setHasGroups(m_fullCompletion); Q_UNUSED(invocationType) if (!worker()) { qCWarning(LANGUAGE) << "Completion invoked on a completion model which has no code completion worker assigned!"; } beginResetModel(); - m_navigationWidgets.clear(); m_completionItems.clear(); endResetModel(); worker()->abortCurrentCompletion(); worker()->setFullCompletion(m_fullCompletion); QUrl url = view->document()->url(); completionInvokedInternal(view, range, invocationType, url); } void CodeCompletionModel::foundDeclarations(const QList>& items, const QExplicitlySharedDataPointer& completionContext) { m_completionContext = completionContext; if(m_completionItems.isEmpty() && items.isEmpty()) { if(m_forceWaitForModel) { // TODO KF5: Check if this actually works beginResetModel(); endResetModel(); //If we need to reset the model, reset it } return; //We don't need to reset, which is bad for target model } beginResetModel(); m_completionItems = items; endResetModel(); if(m_completionContext) { qCDebug(LANGUAGE) << "got completion-context with " << m_completionContext->ungroupedElements().size() << "ungrouped elements"; } } KTextEditor::CodeCompletionModelControllerInterface::MatchReaction CodeCompletionModel::matchingItem(const QModelIndex& /*matched*/) { return None; } void CodeCompletionModel::setCompletionContext(QExplicitlySharedDataPointer completionContext) { QMutexLocker lock(m_mutex); m_completionContext = completionContext; if(m_completionContext) { qCDebug(LANGUAGE) << "got completion-context with " << m_completionContext->ungroupedElements().size() << "ungrouped elements"; } } QExplicitlySharedDataPointer CodeCompletionModel::completionContext() const { QMutexLocker lock(m_mutex); return m_completionContext; } void CodeCompletionModel::executeCompletionItem(View* view, const KTextEditor::Range& word, const QModelIndex& index) const { //We must not lock the duchain at this place, because the items might rely on that CompletionTreeElement* element = static_cast(index.internalPointer()); if( !element || !element->asItem() ) return; element->asItem()->execute(view, word); } QExplicitlySharedDataPointer< KDevelop::CompletionTreeElement > CodeCompletionModel::itemForIndex(QModelIndex index) const { CompletionTreeElement* element = static_cast(index.internalPointer()); return QExplicitlySharedDataPointer< KDevelop::CompletionTreeElement >(element); } QVariant CodeCompletionModel::data(const QModelIndex& index, int role) const { if ( role == Qt::TextAlignmentRole && index.column() == 0 ) { return Qt::AlignRight; } auto element = static_cast(index.internalPointer()); if( !element ) return QVariant(); if( role == CodeCompletionModel::GroupRole ) { if( element->asNode() ) { return QVariant(element->asNode()->role); }else { qCDebug(LANGUAGE) << "Requested group-role from leaf tree element"; return QVariant(); } }else{ if( element->asNode() ) { if( role == CodeCompletionModel::InheritanceDepth ) { auto customGroupNode = dynamic_cast(element); if(customGroupNode) return QVariant(customGroupNode->inheritanceDepth); } if( role == element->asNode()->role ) { return element->asNode()->roleValue; } else { return QVariant(); } } } if(!element->asItem()) { qCWarning(LANGUAGE) << "Error in completion model"; return QVariant(); } //Navigation widget interaction is done here, the other stuff is done within the tree-elements switch (role) { case CodeCompletionModel::InheritanceDepth: return element->asItem()->inheritanceDepth(); case CodeCompletionModel::ArgumentHintDepth: return element->asItem()->argumentHintDepth(); case CodeCompletionModel::ItemSelected: { DeclarationPointer decl = element->asItem()->declaration(); if(decl) { DUChain::self()->emitDeclarationSelected(decl); } break; } } //In minimal completion mode, hide all columns except the "name" one if(!m_fullCompletion && role == Qt::DisplayRole && index.column() != Name && (element->asItem()->argumentHintDepth() == 0 || index.column() == Prefix)) return QVariant(); //In reduced completion mode, don't show information text with the selected items if(role == ItemSelected && (!m_fullCompletion || !ICore::self()->languageController()->completionSettings()->showMultiLineSelectionInformation())) return QVariant(); return element->asItem()->data(index, role, this); } KDevelop::TopDUContextPointer CodeCompletionModel::currentTopContext() const { return m_currentTopContext; } void CodeCompletionModel::setCurrentTopContext(const KDevelop::TopDUContextPointer& topContext) { m_currentTopContext = topContext; } QModelIndex CodeCompletionModel::index(int row, int column, const QModelIndex& parent) const { if( parent.isValid() ) { CompletionTreeElement* element = static_cast(parent.internalPointer()); CompletionTreeNode* node = element->asNode(); if( !node ) { qCDebug(LANGUAGE) << "Requested sub-index of leaf node"; return QModelIndex(); } if (row < 0 || row >= node->children.count() || column < 0 || column >= ColumnCount) return QModelIndex(); return createIndex(row, column, node->children[row].data()); } else { if (row < 0 || row >= m_completionItems.count() || column < 0 || column >= ColumnCount) return QModelIndex(); return createIndex(row, column, const_cast(m_completionItems[row].data())); } } QModelIndex CodeCompletionModel::parent ( const QModelIndex & index ) const { if(rowCount() == 0) return QModelIndex(); if( index.isValid() ) { CompletionTreeElement* element = static_cast(index.internalPointer()); if( element->parent() ) return createIndex( element->rowInParent(), element->columnInParent(), element->parent() ); } return QModelIndex(); } int CodeCompletionModel::rowCount ( const QModelIndex & parent ) const { if( parent.isValid() ) { CompletionTreeElement* element = static_cast(parent.internalPointer()); CompletionTreeNode* node = element->asNode(); if( !node ) return 0; return node->children.count(); }else{ return m_completionItems.count(); } } QString CodeCompletionModel::filterString(KTextEditor::View* view, const KTextEditor::Range& range, const KTextEditor::Cursor& position) { m_filterString = KTextEditor::CodeCompletionModelControllerInterface::filterString(view, range, position); return m_filterString; } } #include "moc_codecompletionmodel.cpp" #include "codecompletionmodel.moc" diff --git a/kdevplatform/language/codecompletion/codecompletionmodel.h b/kdevplatform/language/codecompletion/codecompletionmodel.h index b7f3e4c21e..e815de10bf 100644 --- a/kdevplatform/language/codecompletion/codecompletionmodel.h +++ b/kdevplatform/language/codecompletion/codecompletionmodel.h @@ -1,148 +1,144 @@ /* * KDevelop Generic Code Completion Support * * Copyright 2006-2008 Hamish Rodda * 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_CODECOMPLETIONMODEL_H #define KDEVPLATFORM_CODECOMPLETIONMODEL_H #include #include #include #include #include #include "../duchain/duchainpointer.h" #include #include "codecompletioncontext.h" #include "codecompletionitem.h" #include #include class QMutex; namespace KDevelop { class DUContext; class Declaration; class CodeCompletionWorker; class CompletionWorkerThread; class KDEVPLATFORMLANGUAGE_EXPORT CodeCompletionModel : public KTextEditor::CodeCompletionModel , public KTextEditor::CodeCompletionModelControllerInterface { Q_OBJECT Q_INTERFACES(KTextEditor::CodeCompletionModelControllerInterface) public: explicit CodeCompletionModel(QObject* parent); ~CodeCompletionModel() override; ///This MUST be called after the creation of this completion-model. ///If you use use the KDevelop::CodeCompletion helper-class, that one cares about it. virtual void initialize(); ///Entry-point for code-completion. This determines ome settings, clears the model, and then calls completionInvokedInternal for further processing. void completionInvoked(KTextEditor::View* view, const KTextEditor::Range& range, KTextEditor::CodeCompletionModel::InvocationType invocationType) override; QModelIndex index ( int row, int column, const QModelIndex & parent = QModelIndex() ) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; int rowCount ( const QModelIndex & parent = QModelIndex() ) const override; QModelIndex parent ( const QModelIndex & index ) const override; ///Use this to set whether the code-completion widget should wait for this model until it's shown ///This makes sense when the model takes some time but not too much time, to make the UI less flickering and ///annoying. ///The default is false ///@todo Remove this option, make it true by default, and make sure in CodeCompletionWorker that the whole thing cannot break void setForceWaitForModel(bool wait); bool forceWaitForModel(); ///Convenience-storage for use by the inherited completion model void setCompletionContext(QExplicitlySharedDataPointer completionContext); QExplicitlySharedDataPointer completionContext() const; ///Convenience-storage for use by the inherited completion model KDevelop::TopDUContextPointer currentTopContext() const; void setCurrentTopContext(const KDevelop::TopDUContextPointer& topContext); - ///Tracks navigation widget so they can be interactive with through the keyboard later on - void addNavigationWidget(const CompletionTreeElement* element, QWidget* widget) const; - ///Whether the completion should be fully detailed. If false, it should be simplifed, so no argument-hints, ///no expanding information, no type-information, etc. bool fullCompletion() const; MatchReaction matchingItem(const QModelIndex& matched) override; QString filterString(KTextEditor::View* view, const KTextEditor::Range& range, const KTextEditor::Cursor& position) override; void clear(); ///Returns the tree-element that belogns to the index, or zero QExplicitlySharedDataPointer itemForIndex(QModelIndex index) const; Q_SIGNALS: ///Connection from this completion-model into the background worker thread. You should emit this from within completionInvokedInternal. void completionsNeeded(KDevelop::DUContextPointer context, const KTextEditor::Cursor& position, KTextEditor::View* view); ///Additional signal that allows directly stepping into the worker-thread, bypassing computeCompletions(..) etc. ///doSpecialProcessing(data) will be executed in the background thread. void doSpecialProcessingInBackground(uint data); protected Q_SLOTS: ///Connection from the background-thread into the model: This is called when the background-thread is ready virtual void foundDeclarations(const QList>& item, const QExplicitlySharedDataPointer& completionContext); protected: ///Eventually override this, determine the context or whatever, and then emit completionsNeeded(..) to continue processing in the background tread. ///The default-implementation does this completely, so if you don't need to do anything special, you can just leave it. virtual void completionInvokedInternal(KTextEditor::View* view, const KTextEditor::Range& range, KTextEditor::CodeCompletionModel::InvocationType invocationType, const QUrl& url); void executeCompletionItem(KTextEditor::View* view, const KTextEditor::Range& word, const QModelIndex& index) const override; QExplicitlySharedDataPointer m_completionContext; typedef QPair > DeclarationContextPair; - mutable QMap > m_navigationWidgets; QList< QExplicitlySharedDataPointer > m_completionItems; /// Should create a completion-worker. The worker must have no parent object, /// because else its thread-affinity can not be changed. virtual CodeCompletionWorker* createCompletionWorker() = 0; friend class CompletionWorkerThread; CodeCompletionWorker* worker() const; private: bool m_forceWaitForModel; bool m_fullCompletion; QMutex* m_mutex; CompletionWorkerThread* m_thread; QString m_filterString; KDevelop::TopDUContextPointer m_currentTopContext; }; } #endif diff --git a/kdevplatform/language/codecompletion/normaldeclarationcompletionitem.cpp b/kdevplatform/language/codecompletion/normaldeclarationcompletionitem.cpp index e5edfa459b..46a35dd940 100644 --- a/kdevplatform/language/codecompletion/normaldeclarationcompletionitem.cpp +++ b/kdevplatform/language/codecompletion/normaldeclarationcompletionitem.cpp @@ -1,230 +1,229 @@ /* * 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. */ #include "normaldeclarationcompletionitem.h" #include "codecompletionmodel.h" #include "../duchain/duchainlock.h" #include "../duchain/duchain.h" #include "../duchain/classfunctiondeclaration.h" #include "../duchain/types/functiontype.h" #include "../duchain/types/enumeratortype.h" #include "../duchain/duchainutils.h" #include "../duchain/navigation/abstractdeclarationnavigationcontext.h" #include #include #include namespace KDevelop { const int NormalDeclarationCompletionItem::normalBestMatchesCount = 5; //If this is true, the return-values of argument-hints will be just written as "..." if they are too long const bool NormalDeclarationCompletionItem::shortenArgumentHintReturnValues = true; const int NormalDeclarationCompletionItem::maximumArgumentHintReturnValueLength = 30; const int NormalDeclarationCompletionItem::desiredTypeLength = 20; NormalDeclarationCompletionItem::NormalDeclarationCompletionItem(const KDevelop::DeclarationPointer& decl, QExplicitlySharedDataPointer context, int inheritanceDepth) : m_completionContext(context), m_declaration(decl), m_inheritanceDepth(inheritanceDepth) { } KDevelop::DeclarationPointer NormalDeclarationCompletionItem::declaration() const { return m_declaration; } QExplicitlySharedDataPointer< KDevelop::CodeCompletionContext > NormalDeclarationCompletionItem::completionContext() const { return m_completionContext; } int NormalDeclarationCompletionItem::inheritanceDepth() const { return m_inheritanceDepth; } int NormalDeclarationCompletionItem::argumentHintDepth() const { if( m_completionContext ) return m_completionContext->depth(); else return 0; } QString NormalDeclarationCompletionItem::declarationName() const { if (!m_declaration) { return QStringLiteral(""); } QString ret = m_declaration->identifier().toString(); if (ret.isEmpty()) return QStringLiteral(""); else return ret; } void NormalDeclarationCompletionItem::execute(KTextEditor::View* view, const KTextEditor::Range& word) { if( m_completionContext && m_completionContext->depth() != 0 ) return; //Do not replace any text when it is an argument-hint KTextEditor::Document* document = view->document(); QString newText; { KDevelop::DUChainReadLocker lock(KDevelop::DUChain::lock()); if(m_declaration) { newText = declarationName(); } else { qCDebug(LANGUAGE) << "Declaration disappeared"; return; } } document->replaceText(word, newText); KTextEditor::Range newRange = word; newRange.setEnd(KTextEditor::Cursor(newRange.end().line(), newRange.start().column() + newText.length())); executed(view, newRange); } QWidget* NormalDeclarationCompletionItem::createExpandingWidget(const KDevelop::CodeCompletionModel* model) const { Q_UNUSED(model); return nullptr; } bool NormalDeclarationCompletionItem::createsExpandingWidget() const { return false; } QString NormalDeclarationCompletionItem::shortenedTypeString(KDevelop::DeclarationPointer decl, int desiredTypeLength) const { Q_UNUSED(desiredTypeLength); return decl->abstractType()->toString(); } void NormalDeclarationCompletionItem::executed(KTextEditor::View* view, const KTextEditor::Range& word) { Q_UNUSED(view); Q_UNUSED(word); } QVariant NormalDeclarationCompletionItem::data(const QModelIndex& index, int role, const KDevelop::CodeCompletionModel* model) const { DUChainReadLocker lock(DUChain::lock(), 500); if(!lock.locked()) { qCDebug(LANGUAGE) << "Failed to lock the du-chain in time"; return QVariant(); } if(!m_declaration) return QVariant(); switch (role) { case Qt::DisplayRole: if (index.column() == CodeCompletionModel::Name) { return declarationName(); } else if(index.column() == CodeCompletionModel::Postfix) { if (FunctionType::Ptr functionType = m_declaration->type()) { // Retrieve const/volatile string return functionType->AbstractType::toString(); } } else if(index.column() == CodeCompletionModel::Prefix) { if(m_declaration->kind() == Declaration::Namespace) return QStringLiteral("namespace"); if (m_declaration->abstractType()) { if(EnumeratorType::Ptr enumerator = m_declaration->type()) { if(m_declaration->context()->owner() && m_declaration->context()->owner()->abstractType()) { if(!m_declaration->context()->owner()->identifier().isEmpty()) return shortenedTypeString(DeclarationPointer(m_declaration->context()->owner()), desiredTypeLength); else return QStringLiteral("enum"); } } if (FunctionType::Ptr functionType = m_declaration->type()) { ClassFunctionDeclaration* funDecl = dynamic_cast(m_declaration.data()); if (functionType->returnType()) { QString ret = shortenedTypeString(m_declaration, desiredTypeLength); if(shortenArgumentHintReturnValues && argumentHintDepth() && ret.length() > maximumArgumentHintReturnValueLength) return QStringLiteral("..."); else return ret; }else if(argumentHintDepth()) { return QString();//Don't show useless prefixes in the argument-hints }else if(funDecl && funDecl->isConstructor() ) return QStringLiteral(""); else if(funDecl && funDecl->isDestructor() ) return QStringLiteral(""); else return QStringLiteral(""); } else { return shortenedTypeString(m_declaration, desiredTypeLength); } } else { return QStringLiteral(""); } } else if (index.column() == CodeCompletionModel::Arguments) { if (m_declaration->isFunctionDeclaration()) { auto functionType = declaration()->type(); return functionType ? functionType->partToString(FunctionType::SignatureArguments) : QVariant(); } } break; case CodeCompletionModel::BestMatchesCount: return QVariant(normalBestMatchesCount); break; case CodeCompletionModel::IsExpandable: return QVariant(createsExpandingWidget()); case CodeCompletionModel::ExpandingWidget: { QWidget* nav = createExpandingWidget(model); Q_ASSERT(nav); - model->addNavigationWidget(this, nav); QVariant v; v.setValue(nav); return v; } case CodeCompletionModel::ScopeIndex: return static_cast(reinterpret_cast(m_declaration->context())); case CodeCompletionModel::CompletionRole: return (int)completionProperties(); case CodeCompletionModel::ItemSelected: { NavigationContextPointer ctx(new AbstractDeclarationNavigationContext(DeclarationPointer(m_declaration), TopDUContextPointer())); return ctx->html(true); } case Qt::DecorationRole: { if( index.column() == CodeCompletionModel::Icon ) { CodeCompletionModel::CompletionProperties p = completionProperties(); lock.unlock(); return DUChainUtils::iconForProperties(p); } break; } } return QVariant(); } }