File indexing completed on 2024-04-21 15:24:07
0001 /* 0002 SPDX-FileCopyrightText: 2012 Milian Wolff <mail@milianw.de> 0003 0004 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 #include "phpdocsplugin.h" 0008 #include "phpdocsmodel.h" 0009 0010 #include <KPluginFactory> 0011 #include <KAboutData> 0012 #include <ksettings/Dispatcher> 0013 0014 #include <interfaces/idocumentation.h> 0015 #include <interfaces/icore.h> 0016 #include <interfaces/idocumentationcontroller.h> 0017 0018 #include <language/duchain/duchain.h> 0019 #include <language/duchain/declaration.h> 0020 #include <language/duchain/duchainlock.h> 0021 0022 #include <language/duchain/classdeclaration.h> 0023 #include <language/duchain/functiondeclaration.h> 0024 #include <language/duchain/classmemberdeclaration.h> 0025 #include <language/duchain/classfunctiondeclaration.h> 0026 0027 #include <QFile> 0028 0029 #include "phpdocsdebug.h" 0030 #include "phpdocumentation.h" 0031 #include "phpdocssettings.h" 0032 0033 using namespace KDevelop; 0034 0035 K_PLUGIN_FACTORY_WITH_JSON(PhpDocsFactory, "kdevphpdocs.json", registerPlugin<PhpDocsPlugin>();) 0036 0037 PhpDocsPlugin::PhpDocsPlugin(QObject* parent, const QVariantList& args) 0038 : IPlugin(QStringLiteral("kdevphpdocs"), parent) 0039 , m_model(new PhpDocsModel(this)) 0040 { 0041 Q_UNUSED(args); 0042 0043 readConfig(); 0044 0045 KSettings::Dispatcher::registerComponent( QStringLiteral("kdevphpdocs_config"), this, "readConfig" ); 0046 } 0047 0048 PhpDocsPlugin::~PhpDocsPlugin() 0049 { 0050 } 0051 0052 QString PhpDocsPlugin::name() const 0053 { 0054 return QStringLiteral("PHP"); 0055 } 0056 0057 QIcon PhpDocsPlugin::icon() const 0058 { 0059 static QIcon icon = QIcon::fromTheme(QStringLiteral("application-x-php")); 0060 return icon; 0061 } 0062 0063 void PhpDocsPlugin::readConfig() 0064 { 0065 // since PhpDocsSettings pointer in this plugin is distinct from the one in the KCM 0066 // we have to trigger reading manually 0067 PhpDocsSettings::self()->load(); 0068 } 0069 0070 ///TODO: possibly return multiple filenames (i.e. fallbacks) when doing local lookups 0071 QString PhpDocsPlugin::getDocumentationFilename( KDevelop::Declaration* dec, const bool& isLocal ) const 0072 { 0073 QString fname; 0074 0075 //TODO: most of the SPL stuff is not found for me in the deb package php-doc 0076 // => check newer documentation or give a fallback to ref.spl.html 0077 if ( ClassFunctionDeclaration* fdec = dynamic_cast<ClassFunctionDeclaration*>( dec ) ) { 0078 // class methods -> php.net/CLASS.METHOD 0079 // local: either CLASS.METHOD.html or function.CLASS-METHOD.html... really sucks :-/ 0080 // for now, use the latter... 0081 if ( dec->context() && dec->context()->type() == DUContext::Class && dec->context()->owner() ) { 0082 QString className = dec->context()->owner()->identifier().toString(); 0083 0084 if ( !isLocal ) { 0085 fname = className + '.' + fdec->identifier().toString(); 0086 } else { 0087 if ( fdec->isConstructor() ) { 0088 fname = QStringLiteral("construct"); 0089 } else if ( fdec->isDestructor() ) { 0090 fname = QStringLiteral("destruct"); 0091 } else { 0092 fname = fdec->identifier().toString(); 0093 } 0094 //TODO: CLASS.METHOD.html e.g. for xmlreader etc. pp. 0095 fname = "function." + className + '-' + fname; 0096 } 0097 } 0098 } else if ( dynamic_cast<ClassDeclaration*>(dec) ) { 0099 fname = "class." + dec->identifier().toString(); 0100 } else if ( dynamic_cast<FunctionDeclaration*>(dec) ) { 0101 fname = "function." + dec->identifier().toString(); 0102 } 0103 // check for superglobals / reserved variables 0104 else if ( dec->identifier() == Identifier(QStringLiteral("GLOBALS")) || 0105 dec->identifier() == Identifier(QStringLiteral("php_errormsg")) || 0106 dec->identifier() == Identifier(QStringLiteral("HTTP_RAW_POST_DATA")) || 0107 dec->identifier() == Identifier(QStringLiteral("http_response_header")) || 0108 dec->identifier() == Identifier(QStringLiteral("argc")) || 0109 dec->identifier() == Identifier(QStringLiteral("argv")) || 0110 dec->identifier() == Identifier(QStringLiteral("_GET")) || 0111 dec->identifier() == Identifier(QStringLiteral("_POST")) || 0112 dec->identifier() == Identifier(QStringLiteral("_FILES")) || 0113 dec->identifier() == Identifier(QStringLiteral("_REQUEST")) || 0114 dec->identifier() == Identifier(QStringLiteral("_SESSION")) || 0115 dec->identifier() == Identifier(QStringLiteral("_ENV")) || 0116 dec->identifier() == Identifier(QStringLiteral("_COOKIE")) ) { 0117 if ( isLocal ) { 0118 fname = QStringLiteral("reserved.variables.") + dec->identifier().toString().remove('_'); 0119 } else { 0120 fname = dec->identifier().toString(); 0121 } 0122 } 0123 0124 qCDebug(DOCS) << fname; 0125 0126 if ( !fname.isEmpty() && isLocal ) { 0127 fname = fname.toLower(); 0128 fname.replace('_', '-'); 0129 fname.append(".html"); 0130 } 0131 0132 return fname; 0133 } 0134 0135 IDocumentation::Ptr PhpDocsPlugin::documentationForDeclaration( Declaration* dec ) const 0136 { 0137 if ( dec ) { 0138 DUChainReadLocker lock( DUChain::lock() ); 0139 0140 // filter non global or non-php declarations 0141 if ( dec->topContext()->url() != m_model->internalFunctionFile() ) { 0142 return {}; 0143 } 0144 0145 QUrl url = PhpDocsSettings::phpDocLocation(); 0146 qCDebug(DOCS) << url; 0147 0148 QString file = getDocumentationFilename( dec, url.isLocalFile() ); 0149 if ( file.isEmpty() ) { 0150 qCDebug(DOCS) << "no documentation pattern found for" << dec->toString(); 0151 return {}; 0152 } 0153 0154 url.setPath( url.path() + '/' + file); 0155 if ( url.isLocalFile() && !QFile::exists( url.toLocalFile() ) ) { 0156 qCDebug(DOCS) << "bad path" << url << "for documentation of" << dec->toString() << " - aborting"; 0157 return {}; 0158 } 0159 0160 qCDebug(DOCS) << "php documentation located at " << url << "for" << dec->toString(); 0161 return documentationForUrl(url, dec->qualifiedIdentifier().toString()); 0162 } 0163 0164 return {}; 0165 } 0166 0167 IDocumentation::Ptr PhpDocsPlugin::documentation(const QUrl& url) const 0168 { 0169 if (url.toString().startsWith(PhpDocsSettings::phpDocLocation().toString())) { 0170 return documentationForUrl(url, QString()); 0171 } 0172 return {}; 0173 } 0174 0175 0176 QAbstractListModel* PhpDocsPlugin::indexModel() const 0177 { 0178 return m_model; 0179 } 0180 0181 IDocumentation::Ptr PhpDocsPlugin::documentationForIndex(const QModelIndex& index) const 0182 { 0183 return documentationForDeclaration(static_cast<Declaration*>( 0184 index.data(PhpDocsModel::DeclarationRole).value<DeclarationPointer>().data() 0185 )); 0186 } 0187 0188 void PhpDocsPlugin::loadUrl(const QUrl& url) const 0189 { 0190 qCDebug(DOCS) << "loading URL" << url; 0191 auto doc = documentationForUrl(url, QString()); 0192 ICore::self()->documentationController()->showDocumentation(doc); 0193 } 0194 0195 void PhpDocsPlugin::showDocumentation(const QUrl& url) 0196 { 0197 auto doc = documentationForUrl(url, url.toString()); 0198 ICore::self()->documentationController()->showDocumentation(doc); 0199 } 0200 0201 IDocumentation::Ptr PhpDocsPlugin::documentationForUrl(const QUrl& url, const QString& name, const QByteArray& description) const 0202 { 0203 return IDocumentation::Ptr(new PhpDocumentation( url, name, description, const_cast<PhpDocsPlugin*>(this))); 0204 } 0205 0206 IDocumentation::Ptr PhpDocsPlugin::homePage() const 0207 { 0208 QUrl url = PhpDocsSettings::phpDocLocation(); 0209 if ( url.isLocalFile() ) { 0210 url.setPath(url.path() + "/index.html"); 0211 } else { 0212 url.setPath(url.path() + "/manual"); 0213 } 0214 return documentationForUrl(url, i18n("PHP Documentation")); 0215 } 0216 0217 #include "phpdocsplugin.moc" 0218 0219 #include "moc_phpdocsplugin.cpp"