File indexing completed on 2024-04-28 04:38:20

0001 /*
0002     SPDX-FileCopyrightText: 2010 Milian Wolff <mail@milianw.de>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "codeutilsplugin.h"
0008 
0009 #include <QAction>
0010 #include <QVariantList>
0011 
0012 #include <KLocalizedString>
0013 #include <KPluginFactory>
0014 #include <KActionCollection>
0015 
0016 #include <KTextEditor/Document>
0017 #include <KTextEditor/View>
0018 #include <QStandardPaths>
0019 
0020 #include <interfaces/icore.h>
0021 #include <interfaces/idocumentcontroller.h>
0022 #include <interfaces/context.h>
0023 #include <interfaces/ilanguagecontroller.h>
0024 
0025 #include <language/duchain/duchainutils.h>
0026 #include <language/duchain/declaration.h>
0027 #include <language/duchain/duchainlock.h>
0028 #include <language/duchain/abstractfunctiondeclaration.h>
0029 #include <language/duchain/topducontext.h>
0030 #include <language/duchain/types/functiontype.h>
0031 #include <language/codegen/templaterenderer.h>
0032 #include <language/codegen/codedescription.h>
0033 #include <language/interfaces/ilanguagesupport.h>
0034 #include <debug.h>
0035 
0036 
0037 using namespace KDevelop;
0038 using namespace KTextEditor;
0039 
0040 K_PLUGIN_FACTORY_WITH_JSON(CodeUtilsPluginFactory, "kdevcodeutils.json", registerPlugin<CodeUtilsPlugin>(); )
0041 
0042 CodeUtilsPlugin::CodeUtilsPlugin ( QObject* parent, const QVariantList& )
0043     : IPlugin ( QStringLiteral("kdevcodeutils"), parent )
0044 {
0045     setXMLFile( QStringLiteral("kdevcodeutils.rc") );
0046 
0047     QAction* action = actionCollection()->addAction( QStringLiteral("document_declaration") );
0048     // i18n: action name; 'Document' is a verb
0049     action->setText(i18nc("@action", "Document Declaration"));
0050     actionCollection()->setDefaultShortcut(action, i18nc("default shortcut for \"Document Declaration\"", "Alt+Shift+d"));
0051     connect( action, &QAction::triggered, this, &CodeUtilsPlugin::documentDeclaration );
0052     action->setToolTip(i18nc("@info:tooltip", "Add Doxygen skeleton for declaration under cursor."));
0053     // i18n: translate title same as the action name
0054     action->setWhatsThis(i18nc("@info:whatthis",
0055                                 "Adds a basic Doxygen comment skeleton in front of "
0056                                 "the declaration under the cursor, e.g. with all the "
0057                                 "parameter of a function." ) );
0058     action->setIcon( QIcon::fromTheme( QStringLiteral("documentinfo") ) );
0059 }
0060 
0061 void CodeUtilsPlugin::documentDeclaration()
0062 {
0063     View* view =  ICore::self()->documentController()->activeTextDocumentView();
0064     if ( !view ) {
0065         return;
0066     }
0067 
0068     DUChainReadLocker lock;
0069     TopDUContext* topCtx = DUChainUtils::standardContextForUrl(view->document()->url());
0070     if ( !topCtx ) {
0071         return;
0072     }
0073 
0074     Declaration* dec = DUChainUtils::declarationInLine( KTextEditor::Cursor( view->cursorPosition() ),
0075                                                         topCtx );
0076     if ( !dec || dec->isForwardDeclaration() ) {
0077         return;
0078     }
0079     // finally - we found the declaration :)
0080     int line = dec->range().start.line;
0081     Cursor insertPos( line, 0 );
0082 
0083     TemplateRenderer renderer;
0084     renderer.setEmptyLinesPolicy(TemplateRenderer::TrimEmptyLines);
0085     renderer.addVariable(QStringLiteral("brief"), i18n( "..." ));
0086 
0087     /*
0088     QString indentation = textDoc->line( insertPos.line() );
0089     if (!indentation.isEmpty()) {
0090         int lastSpace = 0;
0091         while (indentation.at(lastSpace).isSpace()) {
0092             ++lastSpace;
0093         }
0094         indentation.truncate(lastSpace);
0095     }
0096     */
0097 
0098     if (dec->isFunctionDeclaration())
0099     {
0100         FunctionDescription description = FunctionDescription(DeclarationPointer(dec));
0101         renderer.addVariable(QStringLiteral("function"), QVariant::fromValue(description));
0102         qCDebug(PLUGIN_CODEUTILS) << "Found function" << description.name << "with" << description.arguments.size() << "arguments";
0103     }
0104 
0105     lock.unlock();
0106 
0107     // TODO: Choose the template based on the language
0108     QLatin1String templateName = QLatin1String("doxygen_cpp");
0109     auto languages = core()->languageController()->languagesForUrl(view->document()->url());
0110     if (!languages.isEmpty())
0111     {
0112         QString languageName = languages.first()->name();
0113         if (languageName == QLatin1String("Php"))
0114         {
0115             templateName = QLatin1String("phpdoc_php");
0116         }
0117         else if (languageName == QLatin1String("Python"))
0118         {
0119             templateName = QLatin1String("rest_python");
0120             // Python docstrings appear inside functions and classes, not above them
0121             insertPos = Cursor(line+1, 0);
0122         }
0123     }
0124 
0125     const QString fileName = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("kdevcodeutils/templates/") + templateName + QLatin1String(".txt"));
0126     if (fileName.isEmpty())
0127     {
0128         qCWarning(PLUGIN_CODEUTILS) << "No suitable template found" << fileName;
0129         return;
0130     }
0131 
0132     const QString comment = renderer.renderFile(QUrl::fromLocalFile(fileName));
0133     view->insertTemplate(insertPos, comment);
0134 }
0135 
0136 CodeUtilsPlugin::~CodeUtilsPlugin()
0137 {
0138 }
0139 
0140 #include "codeutilsplugin.moc"
0141 #include "moc_codeutilsplugin.cpp"