File indexing completed on 2024-04-28 04:37:16
0001 /* 0002 SPDX-FileCopyrightText: 2009 Aleix Pol Gonzalez <aleixpol@kde.org> 0003 SPDX-FileCopyrightText: 2010 Benjamin Port <port.benjamin@gmail.com> 0004 0005 SPDX-License-Identifier: LGPL-2.0-only 0006 */ 0007 0008 #include "documentationcontroller.h" 0009 #include "debug.h" 0010 0011 #include <interfaces/iplugin.h> 0012 #include <interfaces/idocumentationprovider.h> 0013 #include <interfaces/idocumentationproviderprovider.h> 0014 #include <interfaces/icore.h> 0015 #include <interfaces/iplugincontroller.h> 0016 #include <interfaces/iuicontroller.h> 0017 #include <shell/core.h> 0018 0019 #include <QAction> 0020 0021 #include <KActionCollection> 0022 #include <KParts/MainWindow> 0023 #include <KTextEditor/Document> 0024 #include <KTextEditor/View> 0025 0026 #include <interfaces/contextmenuextension.h> 0027 #include <interfaces/idocumentcontroller.h> 0028 0029 #include <language/interfaces/codecontext.h> 0030 #include <language/duchain/duchain.h> 0031 #include <language/duchain/duchainlock.h> 0032 #include <language/duchain/duchainutils.h> 0033 #include <language/duchain/types/identifiedtype.h> 0034 #include <language/duchain/types/typeutils.h> 0035 #include <documentation/documentationview.h> 0036 #include <documentation/standarddocumentationview.h> 0037 0038 using namespace KDevelop; 0039 0040 namespace { 0041 0042 /** 0043 * Return a "more useful" declaration that documentation providers can look-up 0044 * 0045 * @code 0046 * QPoint point; 0047 * ^-- cursor here 0048 * @endcode 0049 * 0050 * In this case, this method returns a Declaration pointer to the *type* 0051 * instead of a pointer to the instance, which is more useful when looking for help 0052 * 0053 * @return A more appropriate Declaration pointer or the given parameter @p decl 0054 */ 0055 Declaration* usefulDeclaration(Declaration* decl) 0056 { 0057 if (!decl) 0058 return nullptr; 0059 0060 // First: Attempt to find the declaration of a definition 0061 decl = DUChainUtils::declarationForDefinition(decl); 0062 0063 // Convenience feature: Retrieve the type declaration of instances, 0064 // it makes no sense to pass the declaration pointer of instances of types 0065 if (decl->kind() == Declaration::Instance) { 0066 AbstractType::Ptr type = TypeUtils::targetTypeKeepAliases(decl->abstractType(), decl->topContext()); 0067 auto* idType = dynamic_cast<IdentifiedType*>(type.data()); 0068 Declaration* idDecl = idType ? idType->declaration(decl->topContext()) : nullptr; 0069 if (idDecl) { 0070 decl = idDecl; 0071 } 0072 } 0073 return decl; 0074 } 0075 0076 } 0077 0078 class DocumentationViewFactory: public KDevelop::IToolViewFactory 0079 { 0080 public: 0081 DocumentationViewFactory() 0082 {} 0083 0084 QWidget* create(QWidget *parent = nullptr) override 0085 { 0086 if (!m_providersModel) { 0087 m_providersModel.reset(new ProvidersModel); 0088 } 0089 return new DocumentationView(parent, m_providersModel.data()); 0090 } 0091 0092 Qt::DockWidgetArea defaultPosition() const override 0093 { 0094 return Qt::RightDockWidgetArea; 0095 } 0096 QString id() const override { return QStringLiteral("org.kdevelop.DocumentationView"); } 0097 QList<QAction*> contextMenuActions(QWidget* viewWidget) const override 0098 { 0099 auto documentationViewWidget = qobject_cast<DocumentationView*>(viewWidget); 0100 Q_ASSERT(documentationViewWidget); 0101 return documentationViewWidget->contextMenuActions(); 0102 } 0103 0104 private: 0105 QScopedPointer<ProvidersModel> m_providersModel; 0106 }; 0107 0108 DocumentationController::DocumentationController(Core* core) 0109 : m_factory(new DocumentationViewFactory) 0110 { 0111 StandardDocumentationView::registerCustomUrlSchemes(); 0112 0113 m_showDocumentation = core->uiController()->activeMainWindow()->actionCollection()->addAction(QStringLiteral("showDocumentation")); 0114 m_showDocumentation->setText(i18nc("@action", "Show Documentation")); 0115 m_showDocumentation->setIcon(QIcon::fromTheme(QStringLiteral("documentation"))); 0116 connect(m_showDocumentation, &QAction::triggered, this, &DocumentationController::doShowDocumentation); 0117 0118 // registering the tool view here so it registered before the areas are restored 0119 // and thus also gets treated like the ones registered from plugins 0120 // cmp. comment about tool views in CorePrivate::initialize 0121 core->uiController()->addToolView(i18nc("@title:window", "Documentation"), m_factory); 0122 } 0123 0124 DocumentationController::~DocumentationController() 0125 { 0126 } 0127 0128 void DocumentationController::initialize() 0129 { 0130 } 0131 0132 0133 void KDevelop::DocumentationController::doShowDocumentation() 0134 { 0135 KTextEditor::View* view = ICore::self()->documentController()->activeTextDocumentView(); 0136 if(!view) 0137 return; 0138 0139 KDevelop::DUChainReadLocker lock( DUChain::lock() ); 0140 0141 Declaration* decl = usefulDeclaration(DUChainUtils::itemUnderCursor(view->document()->url(), KTextEditor::Cursor(view->cursorPosition())).declaration); 0142 auto documentation = documentationForDeclaration(decl); 0143 if (documentation) { 0144 showDocumentation(documentation); 0145 } 0146 } 0147 0148 KDevelop::ContextMenuExtension KDevelop::DocumentationController::contextMenuExtension(Context* context, QWidget* parent) 0149 { 0150 Q_UNUSED(parent); 0151 0152 ContextMenuExtension menuExt; 0153 0154 auto* ctx = dynamic_cast<DeclarationContext*>(context); 0155 if(ctx) { 0156 DUChainReadLocker lock(DUChain::lock()); 0157 if(!ctx->declaration().data()) 0158 return menuExt; 0159 0160 auto doc = documentationForDeclaration(ctx->declaration().data()); 0161 if (doc) { 0162 menuExt.addAction(ContextMenuExtension::ExtensionGroup, m_showDocumentation); 0163 } 0164 } 0165 0166 return menuExt; 0167 } 0168 0169 IDocumentation::Ptr DocumentationController::documentationForDeclaration(Declaration* decl) 0170 { 0171 if (!decl) 0172 return {}; 0173 0174 const auto documentationProviders = this->documentationProviders(); 0175 for (IDocumentationProvider* doc : documentationProviders) { 0176 qCDebug(SHELL) << "Documentation provider found:" << doc; 0177 auto ret = doc->documentationForDeclaration(decl); 0178 0179 qCDebug(SHELL) << "Documentation proposed: " << ret.data(); 0180 if (ret) { 0181 return ret; 0182 } 0183 } 0184 0185 return {}; 0186 } 0187 0188 IDocumentation::Ptr DocumentationController::documentation(const QUrl& url) const 0189 { 0190 const auto providers = this->documentationProviders(); 0191 for (const IDocumentationProvider* provider : providers) { 0192 IDocumentation::Ptr doc = provider->documentation(url); 0193 if (doc) { 0194 return doc; 0195 } 0196 } 0197 return {}; 0198 } 0199 0200 QList< IDocumentationProvider* > DocumentationController::documentationProviders() const 0201 { 0202 const QList<IPlugin*> plugins = ICore::self()->pluginController()->allPluginsForExtension(QStringLiteral("org.kdevelop.IDocumentationProvider")); 0203 const QList<IPlugin*> pluginsProvider = ICore::self()->pluginController()->allPluginsForExtension(QStringLiteral("org.kdevelop.IDocumentationProviderProvider")); 0204 0205 QList<IDocumentationProvider*> ret; 0206 for (IPlugin* p : pluginsProvider) { 0207 auto *docProvider=p->extension<IDocumentationProviderProvider>(); 0208 if (!docProvider) { 0209 qCWarning(SHELL) << "plugin" << p << "does not implement ProviderProvider extension, rerun kbuildsycoca5"; 0210 continue; 0211 } 0212 ret.append(docProvider->providers()); 0213 } 0214 0215 for (IPlugin* p : plugins) { 0216 auto *doc=p->extension<IDocumentationProvider>(); 0217 if (!doc) { 0218 qCWarning(SHELL) << "plugin" << p << "does not implement Provider extension, rerun kbuildsycoca5"; 0219 continue; 0220 } 0221 ret.append(doc); 0222 } 0223 0224 return ret; 0225 } 0226 0227 void KDevelop::DocumentationController::showDocumentation(const IDocumentation::Ptr& doc) 0228 { 0229 Q_ASSERT_X(doc, Q_FUNC_INFO, "Null documentation pointer is unsupported."); 0230 QWidget* w = ICore::self()->uiController()->findToolView(i18nc("@title:window", "Documentation"), m_factory, KDevelop::IUiController::CreateAndRaise); 0231 if(!w) { 0232 qCWarning(SHELL) << "Could not add documentation tool view"; 0233 return; 0234 } 0235 0236 auto* view = dynamic_cast<DocumentationView*>(w); 0237 if( !view ) { 0238 qCWarning(SHELL) << "Could not cast tool view" << w << "to DocumentationView class!"; 0239 return; 0240 } 0241 view->showDocumentation(doc); 0242 } 0243 0244 void DocumentationController::changedDocumentationProviders() 0245 { 0246 emit providersChanged(); 0247 } 0248 0249 #include "moc_documentationcontroller.cpp"