diff --git a/kdevplatform/language/interfaces/ilanguagesupport.h b/kdevplatform/language/interfaces/ilanguagesupport.h --- a/kdevplatform/language/interfaces/ilanguagesupport.h +++ b/kdevplatform/language/interfaces/ilanguagesupport.h @@ -113,11 +113,12 @@ virtual QPair specialLanguageObjectJumpCursor(const QUrl& url, const KTextEditor::Cursor& position); /**Should return a navigation-widget for the - *special language-object that contains @p position refers, or 0. + *special language-object that contains @p position refers to as well as the range the object takes there, + *or nullptr and an invalid range. *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); + virtual QPair 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" diff --git a/kdevplatform/language/interfaces/ilanguagesupport.cpp b/kdevplatform/language/interfaces/ilanguagesupport.cpp --- a/kdevplatform/language/interfaces/ilanguagesupport.cpp +++ b/kdevplatform/language/interfaces/ilanguagesupport.cpp @@ -57,10 +57,11 @@ return QPair(QUrl(), KTextEditor::Cursor::invalid()); } -QWidget* ILanguageSupport::specialLanguageObjectNavigationWidget(const QUrl& url, const KTextEditor::Cursor& position) { +QPair ILanguageSupport::specialLanguageObjectNavigationWidget(const QUrl& url, const KTextEditor::Cursor& position) +{ Q_UNUSED(url) Q_UNUSED(position) - return nullptr; + return {nullptr, KTextEditor::Range::invalid()}; } ICodeHighlighting* ILanguageSupport::codeHighlighting() const { diff --git a/plugins/clang/clangsupport.h b/plugins/clang/clangsupport.h --- a/plugins/clang/clangsupport.h +++ b/plugins/clang/clangsupport.h @@ -68,7 +68,8 @@ KTextEditor::Range specialLanguageObjectRange(const QUrl &url, const KTextEditor::Cursor& position) override; QPair specialLanguageObjectJumpCursor(const QUrl &url, const KTextEditor::Cursor& position) override; - QWidget* specialLanguageObjectNavigationWidget(const QUrl &url, const KTextEditor::Cursor& position) override; + QPair specialLanguageObjectNavigationWidget(const QUrl& url, + const KTextEditor::Cursor& position) override; ClangIndex* index(); diff --git a/plugins/clang/clangsupport.cpp b/plugins/clang/clangsupport.cpp --- a/plugins/clang/clangsupport.cpp +++ b/plugins/clang/clangsupport.cpp @@ -342,24 +342,27 @@ return {{}, KTextEditor::Cursor::invalid()}; } -QWidget* ClangSupport::specialLanguageObjectNavigationWidget(const QUrl &url, const KTextEditor::Cursor& position) +QPair ClangSupport::specialLanguageObjectNavigationWidget(const QUrl& url, const KTextEditor::Cursor& position) { DUChainReadLocker lock; const QPair macroExpansion = macroExpansionForPosition(url, position); if (macroExpansion.first) { Declaration* declaration = macroExpansion.second.usedDeclaration(macroExpansion.first.data()); const MacroDefinition::Ptr macroDefinition(dynamic_cast(declaration)); Q_ASSERT(macroDefinition); auto rangeInRevision = macroExpansion.first->transformFromLocalRevision(macroExpansion.second.m_range.start); - return new ClangNavigationWidget(macroDefinition, DocumentCursor(IndexedString(url), rangeInRevision)); + return { + new ClangNavigationWidget(macroDefinition, DocumentCursor(IndexedString(url), rangeInRevision)), + macroExpansion.second.m_range.castToSimpleRange() + }; } const QPair import = importedContextForPosition(url, position); if (import.first) { - return import.first->createNavigationWidget(); + return {import.first->createNavigationWidget(), import.second}; } - return nullptr; + return {nullptr, KTextEditor::Range::invalid()}; } TopDUContext* ClangSupport::standardContext(const QUrl &url, bool /*proxyContext*/) diff --git a/plugins/cmake/cmakemanager.h b/plugins/cmake/cmakemanager.h --- a/plugins/cmake/cmakemanager.h +++ b/plugins/cmake/cmakemanager.h @@ -120,8 +120,8 @@ QString name() const override; KDevelop::ParseJob *createParseJob(const KDevelop::IndexedString &url) override; KDevelop::ICodeHighlighting* codeHighlighting() const override; - QWidget* specialLanguageObjectNavigationWidget(const QUrl &url, const KTextEditor::Cursor& position) override; - + QPair specialLanguageObjectNavigationWidget(const QUrl& url, const KTextEditor::Cursor& position) override; + // void addPending(const KDevelop::Path& path, CMakeFolderItem* folder); // CMakeFolderItem* takePending(const KDevelop::Path& path); // void addWatcher(KDevelop::IProject* p, const QString& path); @@ -156,8 +156,8 @@ CMakeFile fileInformation(KDevelop::ProjectBaseItem* item) const; void folderAdded(KDevelop::ProjectFolderItem* folder); - QString termAtPosition(const KTextEditor::Document* textDocument, - const KTextEditor::Cursor& position) const; + KTextEditor::Range termRangeAtPosition(const KTextEditor::Document* textDocument, + const KTextEditor::Cursor& position) const; private: QHash m_projects; diff --git a/plugins/cmake/cmakemanager.cpp b/plugins/cmake/cmakemanager.cpp --- a/plugins/cmake/cmakemanager.cpp +++ b/plugins/cmake/cmakemanager.cpp @@ -806,8 +806,8 @@ // return renameFileOrFolder(item, newPath); // } -QString CMakeManager::termAtPosition(const KTextEditor::Document* textDocument, - const KTextEditor::Cursor& position) const +KTextEditor::Range CMakeManager::termRangeAtPosition(const KTextEditor::Document* textDocument, + const KTextEditor::Cursor& position) const { const KTextEditor::Cursor step(0, 1); @@ -833,7 +833,7 @@ } if (parseState != AnyChar) { - return QString(); + return KTextEditor::Range::invalid(); } // undo step before last valid char start += step; @@ -848,47 +848,47 @@ end += step; } - return textDocument->text(KTextEditor::Range(start, end)); + return KTextEditor::Range(start, end); } -QWidget* CMakeManager::specialLanguageObjectNavigationWidget(const QUrl &url, const KTextEditor::Cursor& position) +QPair CMakeManager::specialLanguageObjectNavigationWidget(const QUrl& url, const KTextEditor::Cursor& position) { + KTextEditor::Range itemRange; + CMakeNavigationWidget* doc = nullptr; + KDevelop::TopDUContextPointer top= TopDUContextPointer(KDevelop::DUChain::self()->chainForDocument(url)); - Declaration *decl=nullptr; if(top) { int useAt=top->findUseAt(top->transformToLocalRevision(position)); if(useAt>=0) { Use u=top->uses()[useAt]; - decl=u.usedDeclaration(top->topContext()); + doc = new CMakeNavigationWidget(top, u.usedDeclaration(top->topContext())); + itemRange = u.m_range.castToSimpleRange(); } } - CMakeNavigationWidget* doc=nullptr; - if(decl) - { - doc=new CMakeNavigationWidget(top, decl); - } - else - { + if (!doc) { ICMakeDocumentation* docu=CMake::cmakeDocumentation(); if( docu ) { const auto* document = ICore::self()->documentController()->documentForUrl(url); const auto* textDocument = document->textDocument(); - const auto id = termAtPosition(textDocument, position); - if (!id.isEmpty()) { - IDocumentation::Ptr desc=docu->description(id, url); - if(desc) - { - doc=new CMakeNavigationWidget(top, desc); + itemRange = termRangeAtPosition(textDocument, position); + if (itemRange.isValid()) { + const auto id = textDocument->text(itemRange); + + if (!id.isEmpty()) { + IDocumentation::Ptr desc=docu->description(id, url); + if (desc) { + doc=new CMakeNavigationWidget(top, desc); + } } } } } - return doc; + return {doc, itemRange}; } QPair CMakeManager::cacheValue(KDevelop::IProject* /*project*/, const QString& /*id*/) const diff --git a/plugins/contextbrowser/contextbrowser.h b/plugins/contextbrowser/contextbrowser.h --- a/plugins/contextbrowser/contextbrowser.h +++ b/plugins/contextbrowser/contextbrowser.h @@ -177,7 +177,8 @@ QWidget* toolbarWidgetForMainWindow(Sublime::MainWindow* window); void createActionsForMainWindow(Sublime::MainWindow* window, QString& xmlFile, KActionCollection& actions) override; - QWidget* navigationWidgetForPosition(KTextEditor::View* view, KTextEditor::Cursor position); + QWidget* navigationWidgetForPosition(KTextEditor::View* view, KTextEditor::Cursor position, + KTextEditor::Range& itemRange); void switchUse(bool forward); void clearMouseHover(); diff --git a/plugins/contextbrowser/contextbrowser.cpp b/plugins/contextbrowser/contextbrowser.cpp --- a/plugins/contextbrowser/contextbrowser.cpp +++ b/plugins/contextbrowser/contextbrowser.cpp @@ -444,23 +444,36 @@ } } -static QVector findProblemsUnderCursor(TopDUContext* topContext, KTextEditor::Cursor position) +static QVector findProblemsUnderCursor(TopDUContext* topContext, KTextEditor::Cursor position, + KTextEditor::Range& handleRange) { QVector problems; + handleRange = KTextEditor::Range::invalid(); + const auto modelsData = ICore::self()->languageController()->problemModelSet()->models(); for (const auto& modelData : modelsData) { foreach (const auto& problem, modelData.model->problems(topContext->url())) { DocumentRange problemRange = problem->finalLocation(); - if (problemRange.contains(position) || (problemRange.isEmpty() && problemRange.boundaryAtCursor(position))) + if (problemRange.contains(position) || (problemRange.isEmpty() && problemRange.boundaryAtCursor(position))) { problems += problem; + // first? + if (!handleRange.isValid()) { + handleRange = problemRange; + } else { + handleRange.confineToRange(problemRange); + } + } } } return problems; } -static QVector findProblemsCloseToCursor(TopDUContext* topContext, KTextEditor::Cursor position, KTextEditor::View* view) +static QVector findProblemsCloseToCursor(const TopDUContext* topContext, KTextEditor::Cursor position, const KTextEditor::View* view, + KTextEditor::Range& handleRange) { + handleRange = KTextEditor::Range::invalid(); + QVector allProblems; const auto modelsData = ICore::self()->languageController()->problemModelSet()->models(); for (const auto& modelData : modelsData) { @@ -528,65 +541,81 @@ } } + if (!closestProblems.isEmpty()) { + auto it = closestProblems.constBegin(); + handleRange = (*it)->finalLocation(); + ++it; + for (auto end = closestProblems.constEnd(); it != end; ++it) { + handleRange.confineToRange((*it)->finalLocation()); + } + } + return closestProblems; } -QWidget* ContextBrowserPlugin::navigationWidgetForPosition(KTextEditor::View* view, KTextEditor::Cursor position) +QWidget* ContextBrowserPlugin::navigationWidgetForPosition(KTextEditor::View* view, KTextEditor::Cursor position, + KTextEditor::Range& itemRange) { QUrl viewUrl = view->document()->url(); const auto languages = ICore::self()->languageController()->languagesForUrl(viewUrl); DUChainReadLocker lock(DUChain::lock()); + for (const auto language : languages) { - auto widget = language->specialLanguageObjectNavigationWidget(viewUrl, KTextEditor::Cursor(position)); - auto navigationWidget = qobject_cast(widget); - if(navigationWidget) + auto widget = language->specialLanguageObjectNavigationWidget(viewUrl, position); + auto navigationWidget = qobject_cast(widget.first); + if (navigationWidget) { + itemRange = widget.second; return navigationWidget; + } } TopDUContext* topContext = DUChainUtils::standardContextForUrl(view->document()->url()); if (topContext) { // first pass: find problems under the cursor - const auto problems = findProblemsUnderCursor(topContext, position); + const auto problems = findProblemsUnderCursor(topContext, position, itemRange); if (!problems.isEmpty()) { if (problems == m_currentToolTipProblems && m_currentToolTip) { return nullptr; } m_currentToolTipProblems = problems; + auto widget = new AbstractNavigationWidget; auto context = new ProblemNavigationContext(problems); context->setTopContext(TopDUContextPointer(topContext)); widget->setContext(NavigationContextPointer(context)); return widget; } } - auto declUnderCursor = DUChainUtils::itemUnderCursor(viewUrl, position).declaration; + const auto itemUnderCursor = DUChainUtils::itemUnderCursor(viewUrl, position); + auto declUnderCursor = itemUnderCursor.declaration; Declaration* decl = DUChainUtils::declarationForDefinition(declUnderCursor); if (decl && decl->kind() == Declaration::Alias) { AliasDeclaration* alias = dynamic_cast(decl); Q_ASSERT(alias); - DUChainReadLocker lock; decl = alias->aliasedDeclaration().declaration(); } if(decl) { if(m_currentToolTipDeclaration == IndexedDeclaration(decl) && m_currentToolTip) return nullptr; m_currentToolTipDeclaration = IndexedDeclaration(decl); + itemRange = itemUnderCursor.range; return decl->context()->createNavigationWidget(decl, DUChainUtils::standardContextForUrl(viewUrl)); } if (topContext) { // second pass: find closest problem to the cursor - const auto problems = findProblemsCloseToCursor(topContext, position, view); + const auto problems = findProblemsCloseToCursor(topContext, position, view, itemRange); if (!problems.isEmpty()) { if (problems == m_currentToolTipProblems && m_currentToolTip) { return nullptr; } m_currentToolTipProblems = problems; + auto widget = new AbstractNavigationWidget; // since the problem is not under cursor: show location widget->setContext(NavigationContextPointer(new ProblemNavigationContext(problems, ProblemNavigationContext::ShowLocation))); @@ -602,7 +631,8 @@ if(contextView && contextView->isVisible() && !contextView->isLocked()) return; // If the context-browser view is visible, it will care about updating by itself - auto navigationWidget = navigationWidgetForPosition(view, position); + KTextEditor::Range itemRange = KTextEditor::Range::invalid(); + auto navigationWidget = navigationWidgetForPosition(view, position, itemRange); if(navigationWidget) { // If we have an invisible context-view, assign the tooltip navigation-widget to it. @@ -617,12 +647,11 @@ } KDevelop::NavigationToolTip* tooltip = new KDevelop::NavigationToolTip(view, view->mapToGlobal(view->cursorToCoordinate(position)) + QPoint(20, 40), navigationWidget); - KTextEditor::Range itemRange; - { - DUChainReadLocker lock; - auto viewUrl = view->document()->url(); - itemRange = DUChainUtils::itemUnderCursor(viewUrl, position).range; + if (!itemRange.isValid()) { + qCWarning(PLUGIN_CONTEXTBROWSER) << "Got navigationwidget with invalid itemrange"; + itemRange = KTextEditor::Range(position, 0); } + tooltip->setHandleRect(KTextEditorHelpers::itemBoundingRect(view, itemRange)); tooltip->resize( navigationWidget->sizeHint() + QSize(10, 10) ); QObject::connect( view, &KTextEditor::View::verticalScrollPositionChanged, @@ -798,7 +827,7 @@ highlights.highlights.back()->setZDepth(highlightingZDepth); } if(updateBrowserView) - updateBrowserView->setSpecialNavigationWidget(language->specialLanguageObjectNavigationWidget(url, highlightPosition)); + updateBrowserView->setSpecialNavigationWidget(language->specialLanguageObjectNavigationWidget(url, highlightPosition).first); }else{ KDevelop::DUChainReadLocker lock( DUChain::lock(), 100 ); if(!lock.locked()) { diff --git a/plugins/qmljs/kdevqmljsplugin.h b/plugins/qmljs/kdevqmljsplugin.h --- a/plugins/qmljs/kdevqmljsplugin.h +++ b/plugins/qmljs/kdevqmljsplugin.h @@ -40,7 +40,7 @@ KDevelop::BasicRefactoring* refactoring() const override; KDevelop::ContextMenuExtension contextMenuExtension(KDevelop::Context* context, QWidget* parent) override; - QWidget* specialLanguageObjectNavigationWidget(const QUrl& url, const KTextEditor::Cursor& position) override; + QPair specialLanguageObjectNavigationWidget(const QUrl& url, const KTextEditor::Cursor& position) override; private: KDevelop::ICodeHighlighting* m_highlighting; diff --git a/plugins/qmljs/kdevqmljsplugin.cpp b/plugins/qmljs/kdevqmljsplugin.cpp --- a/plugins/qmljs/kdevqmljsplugin.cpp +++ b/plugins/qmljs/kdevqmljsplugin.cpp @@ -177,24 +177,24 @@ )); } -QWidget* KDevQmlJsPlugin::specialLanguageObjectNavigationWidget(const QUrl& url, const KTextEditor::Cursor& position) +QPair KDevQmlJsPlugin::specialLanguageObjectNavigationWidget(const QUrl& url, const KTextEditor::Cursor& position) { IDocument* doc = ICore::self()->documentController()->documentForUrl(url); if ( doc && doc->textDocument() ) { // Check for a QML property, and construct a property preview widget // if the property key is listed in the supported properties. QPair property = parseProperty(doc->textDocument()->line(position.line()), position); if ( property.first.isValid() && property.second.isValid() ) { - Declaration* decl = DUChainUtils::itemUnderCursor(url, property.first.start()).declaration; + const auto itemUnderCursor = DUChainUtils::itemUnderCursor(url, property.first.start()); - return PropertyPreviewWidget::constructIfPossible( + return {PropertyPreviewWidget::constructIfPossible( doc->textDocument(), property.first, property.second, - decl, + itemUnderCursor.declaration, textFromDoc(doc, property.first), textFromDoc(doc, property.second) - ); + ), itemUnderCursor.range}; } } // Otherwise, display no special "navigation" widget. diff --git a/plugins/quickopen/quickopenplugin.cpp b/plugins/quickopen/quickopenplugin.cpp --- a/plugins/quickopen/quickopenplugin.cpp +++ b/plugins/quickopen/quickopenplugin.cpp @@ -624,7 +624,7 @@ const auto languages = ICore::self()->languageController()->languagesForUrl(url); for (const auto language : languages) { - QWidget* w = language->specialLanguageObjectNavigationWidget(url, KTextEditor::Cursor(view->cursorPosition())); + QWidget* w = language->specialLanguageObjectNavigationWidget(url, view->cursorPosition()).first; if (w) { return w; }