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