1 /*
2     SPDX-FileCopyrightText: 2010 Milian Wolff <mail@milianw.de>
3 
4     SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "codeutilsplugin.h"
8 
9 #include <QAction>
10 #include <QVariantList>
11 
12 #include <KLocalizedString>
13 #include <KPluginFactory>
14 #include <KActionCollection>
15 
16 #include <KTextEditor/Document>
17 #include <KTextEditor/View>
18 #include <QStandardPaths>
19 
20 #include <interfaces/icore.h>
21 #include <interfaces/idocumentcontroller.h>
22 #include <interfaces/context.h>
23 #include <interfaces/ilanguagecontroller.h>
24 
25 #include <language/duchain/duchainutils.h>
26 #include <language/duchain/declaration.h>
27 #include <language/duchain/duchainlock.h>
28 #include <language/duchain/abstractfunctiondeclaration.h>
29 #include <language/duchain/topducontext.h>
30 #include <language/duchain/types/functiontype.h>
31 #include <language/codegen/templaterenderer.h>
32 #include <language/codegen/codedescription.h>
33 #include <language/interfaces/ilanguagesupport.h>
34 #include <debug.h>
35 
36 
37 using namespace KDevelop;
38 using namespace KTextEditor;
39 
40 K_PLUGIN_FACTORY_WITH_JSON(CodeUtilsPluginFactory, "kdevcodeutils.json", registerPlugin<CodeUtilsPlugin>(); )
41 
CodeUtilsPlugin(QObject * parent,const QVariantList &)42 CodeUtilsPlugin::CodeUtilsPlugin ( QObject* parent, const QVariantList& )
43     : IPlugin ( QStringLiteral("kdevcodeutils"), parent )
44 {
45     setXMLFile( QStringLiteral("kdevcodeutils.rc") );
46 
47     QAction* action = actionCollection()->addAction( QStringLiteral("document_declaration") );
48     // i18n: action name; 'Document' is a verb
49     action->setText(i18nc("@action", "Document Declaration"));
50     actionCollection()->setDefaultShortcut(action, i18nc("default shortcut for \"Document Declaration\"", "Alt+Shift+d"));
51     connect( action, &QAction::triggered, this, &CodeUtilsPlugin::documentDeclaration );
52     action->setToolTip(i18nc("@info:tooltip", "Add Doxygen skeleton for declaration under cursor."));
53     // i18n: translate title same as the action name
54     action->setWhatsThis(i18nc("@info:whatthis",
55                                 "Adds a basic Doxygen comment skeleton in front of "
56                                 "the declaration under the cursor, e.g. with all the "
57                                 "parameter of a function." ) );
58     action->setIcon( QIcon::fromTheme( QStringLiteral("documentinfo") ) );
59 }
60 
documentDeclaration()61 void CodeUtilsPlugin::documentDeclaration()
62 {
63     View* view =  ICore::self()->documentController()->activeTextDocumentView();
64     if ( !view ) {
65         return;
66     }
67 
68     DUChainReadLocker lock;
69     TopDUContext* topCtx = DUChainUtils::standardContextForUrl(view->document()->url());
70     if ( !topCtx ) {
71         return;
72     }
73 
74     Declaration* dec = DUChainUtils::declarationInLine( KTextEditor::Cursor( view->cursorPosition() ),
75                                                         topCtx );
76     if ( !dec || dec->isForwardDeclaration() ) {
77         return;
78     }
79     // finally - we found the declaration :)
80     int line = dec->range().start.line;
81     Cursor insertPos( line, 0 );
82 
83     TemplateRenderer renderer;
84     renderer.setEmptyLinesPolicy(TemplateRenderer::TrimEmptyLines);
85     renderer.addVariable(QStringLiteral("brief"), i18n( "..." ));
86 
87     /*
88     QString indentation = textDoc->line( insertPos.line() );
89     if (!indentation.isEmpty()) {
90         int lastSpace = 0;
91         while (indentation.at(lastSpace).isSpace()) {
92             ++lastSpace;
93         }
94         indentation.truncate(lastSpace);
95     }
96     */
97 
98     if (dec->isFunctionDeclaration())
99     {
100         FunctionDescription description = FunctionDescription(DeclarationPointer(dec));
101         renderer.addVariable(QStringLiteral("function"), QVariant::fromValue(description));
102         qCDebug(PLUGIN_CODEUTILS) << "Found function" << description.name << "with" << description.arguments.size() << "arguments";
103     }
104 
105     lock.unlock();
106 
107     // TODO: Choose the template based on the language
108     QLatin1String templateName = QLatin1String("doxygen_cpp");
109     auto languages = core()->languageController()->languagesForUrl(view->document()->url());
110     if (!languages.isEmpty())
111     {
112         QString languageName = languages.first()->name();
113         if (languageName == QLatin1String("Php"))
114         {
115             templateName = QLatin1String("phpdoc_php");
116         }
117         else if (languageName == QLatin1String("Python"))
118         {
119             templateName = QLatin1String("rest_python");
120             // Python docstrings appear inside functions and classes, not above them
121             insertPos = Cursor(line+1, 0);
122         }
123     }
124 
125     const QString fileName = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("kdevcodeutils/templates/") + templateName + QLatin1String(".txt"));
126     if (fileName.isEmpty())
127     {
128         qCWarning(PLUGIN_CODEUTILS) << "No suitable template found" << fileName;
129         return;
130     }
131 
132     const QString comment = renderer.renderFile(QUrl::fromLocalFile(fileName));
133     view->insertTemplate(insertPos, comment);
134 }
135 
~CodeUtilsPlugin()136 CodeUtilsPlugin::~CodeUtilsPlugin()
137 {
138 }
139 
140 #include "codeutilsplugin.moc"
141