File indexing completed on 2024-05-05 04:39:48
0001 #include "filetemplatesplugin.h" 0002 #include "templateclassassistant.h" 0003 #include "templatepreviewtoolview.h" 0004 #include "debug.h" 0005 0006 #include <language/codegen/templatesmodel.h> 0007 #include <language/interfaces/editorcontext.h> 0008 #include <interfaces/icore.h> 0009 #include <interfaces/iproject.h> 0010 #include <interfaces/iuicontroller.h> 0011 #include <interfaces/context.h> 0012 #include <interfaces/contextmenuextension.h> 0013 #include <interfaces/idocumentcontroller.h> 0014 #include <interfaces/iselectioncontroller.h> 0015 #include <interfaces/iprojectcontroller.h> 0016 #include <project/projectmodel.h> 0017 #include <util/path.h> 0018 0019 #include <KActionCollection> 0020 #include <KConfigGroup> 0021 #include <KLocalizedString> 0022 #include <KPluginFactory> 0023 0024 #include <QAction> 0025 #include <QApplication> 0026 #include <QDir> 0027 #include <QIcon> 0028 0029 using namespace KDevelop; 0030 0031 K_PLUGIN_FACTORY_WITH_JSON(FileTemplatesFactory, "kdevfiletemplates.json", registerPlugin<FileTemplatesPlugin>();) 0032 0033 class TemplatePreviewFactory : public KDevelop::IToolViewFactory 0034 { 0035 public: 0036 explicit TemplatePreviewFactory(FileTemplatesPlugin* plugin) 0037 : KDevelop::IToolViewFactory() 0038 , m_plugin(plugin) 0039 { 0040 0041 } 0042 0043 QWidget* create(QWidget* parent = nullptr) override 0044 { 0045 return new TemplatePreviewToolView(m_plugin, parent); 0046 } 0047 0048 QString id() const override 0049 { 0050 return QStringLiteral("org.kdevelop.TemplateFilePreview"); 0051 } 0052 0053 Qt::DockWidgetArea defaultPosition() const override 0054 { 0055 return Qt::RightDockWidgetArea; 0056 } 0057 0058 private: 0059 FileTemplatesPlugin* m_plugin; 0060 }; 0061 0062 FileTemplatesPlugin::FileTemplatesPlugin(QObject* parent, const QVariantList& args) 0063 : IPlugin(QStringLiteral("kdevfiletemplates"), parent) 0064 { 0065 Q_UNUSED(args); 0066 0067 setXMLFile(QStringLiteral("kdevfiletemplates.rc")); 0068 QAction* action = actionCollection()->addAction(QStringLiteral("new_from_template")); 0069 action->setText(i18nc("@action", "New from Template...")); 0070 action->setIcon( QIcon::fromTheme( QStringLiteral("code-class") ) ); 0071 action->setWhatsThis(i18nc("@info:whatsthis", "Allows you to create new source code files, such as classes or unit tests, using templates." ) ); 0072 action->setToolTip( i18nc("@info:tooltip", "Create new files from a template" ) ); 0073 connect (action, &QAction::triggered, this, &FileTemplatesPlugin::createFromTemplate); 0074 0075 m_toolView = new TemplatePreviewFactory(this); 0076 core()->uiController()->addToolView(i18nc("@title:window", "Template Preview"), m_toolView); 0077 } 0078 0079 FileTemplatesPlugin::~FileTemplatesPlugin() 0080 { 0081 0082 } 0083 0084 void FileTemplatesPlugin::unload() 0085 { 0086 core()->uiController()->removeToolView(m_toolView); 0087 } 0088 0089 ContextMenuExtension FileTemplatesPlugin::contextMenuExtension(Context* context, QWidget* parent) 0090 { 0091 ContextMenuExtension ext; 0092 QUrl fileUrl; 0093 0094 if (context->type() == Context::ProjectItemContext) 0095 { 0096 auto* projectContext = static_cast<ProjectItemContext*>(context); 0097 QList<ProjectBaseItem*> items = projectContext->items(); 0098 if (items.size() != 1) 0099 { 0100 return ext; 0101 } 0102 0103 QUrl url; 0104 ProjectBaseItem* item = items.first(); 0105 if (item->folder()) 0106 { 0107 url = item->path().toUrl(); 0108 } 0109 else if (item->target()) 0110 { 0111 url = item->parent()->path().toUrl(); 0112 } 0113 if (url.isValid()) 0114 { 0115 auto* action = new QAction(i18nc("@action:inmenu", "Create from Template..."), parent); 0116 action->setIcon(QIcon::fromTheme(QStringLiteral("code-class"))); 0117 action->setData(url); 0118 connect(action, &QAction::triggered, this, &FileTemplatesPlugin::createFromTemplate); 0119 ext.addAction(ContextMenuExtension::FileGroup, action); 0120 } 0121 0122 if (item->file()) 0123 { 0124 fileUrl = item->path().toUrl(); 0125 } 0126 } 0127 else if (context->type() == Context::EditorContext) 0128 { 0129 auto* editorContext = static_cast<KDevelop::EditorContext*>(context); 0130 fileUrl = editorContext->url(); 0131 } 0132 0133 if (fileUrl.isValid() && determineTemplateType(fileUrl) != NoTemplate) 0134 { 0135 auto* action = new QAction(i18nc("@action:inmenu", "Show Template Preview"), parent); 0136 action->setIcon(QIcon::fromTheme(QStringLiteral("document-preview"))); 0137 action->setData(fileUrl); 0138 connect(action, &QAction::triggered, this, &FileTemplatesPlugin::previewTemplate); 0139 ext.addAction(ContextMenuExtension::ExtensionGroup, action); 0140 } 0141 0142 return ext; 0143 } 0144 0145 QString FileTemplatesPlugin::name() const 0146 { 0147 return i18n("File Templates"); 0148 } 0149 0150 QIcon FileTemplatesPlugin::icon() const 0151 { 0152 return QIcon::fromTheme(QStringLiteral("code-class")); 0153 } 0154 0155 QAbstractItemModel* FileTemplatesPlugin::templatesModel() const 0156 { 0157 if(!m_model) { 0158 auto* self = const_cast<FileTemplatesPlugin*>(this); 0159 m_model = new TemplatesModel(QStringLiteral("kdevfiletemplates"), self); 0160 } 0161 return m_model; 0162 } 0163 0164 QString FileTemplatesPlugin::knsConfigurationFile() const 0165 { 0166 return QStringLiteral("kdevfiletemplates.knsrc"); 0167 } 0168 0169 QStringList FileTemplatesPlugin::supportedMimeTypes() const 0170 { 0171 const QStringList types{ 0172 QStringLiteral("application/x-desktop"), 0173 QStringLiteral("application/x-bzip-compressed-tar"), 0174 QStringLiteral("application/zip"), 0175 }; 0176 return types; 0177 } 0178 0179 void FileTemplatesPlugin::reload() 0180 { 0181 templatesModel(); 0182 m_model->refresh(); 0183 } 0184 0185 void FileTemplatesPlugin::loadTemplate(const QString& fileName) 0186 { 0187 templatesModel(); 0188 m_model->loadTemplateFile(fileName); 0189 } 0190 0191 void FileTemplatesPlugin::createFromTemplate() 0192 { 0193 QUrl baseUrl; 0194 if (auto* action = qobject_cast<QAction*>(sender())) 0195 { 0196 baseUrl = action->data().toUrl(); 0197 } 0198 if (!baseUrl.isValid()) { 0199 // fall-back to currently active document's parent directory 0200 IDocument* doc = ICore::self()->documentController()->activeDocument(); 0201 if (doc && doc->url().isValid()) { 0202 baseUrl = doc->url().adjusted(QUrl::RemoveFilename); 0203 } 0204 } 0205 if (!baseUrl.isValid()) { 0206 // fall-back to currently selected project's or item's base directory 0207 auto* projectContext = dynamic_cast<ProjectItemContext*>(ICore::self()->selectionController()->currentSelection()); 0208 if (projectContext) { 0209 const QList<ProjectBaseItem*> items = projectContext->items(); 0210 if (items.size() == 1) { 0211 ProjectBaseItem* item = items.at(0); 0212 if (item->folder()) { 0213 baseUrl = item->path().toUrl(); 0214 } else if (item->target()) { 0215 baseUrl = item->parent()->path().toUrl(); 0216 } 0217 } 0218 } 0219 } 0220 if (!baseUrl.isValid()) { 0221 // fall back to base directory of currently open project, if there is only one 0222 const QList<IProject*> projects = ICore::self()->projectController()->projects(); 0223 if (projects.size() == 1) { 0224 baseUrl = projects.at(0)->path().toUrl(); 0225 } 0226 } 0227 if (!baseUrl.isValid()) { 0228 // last resort 0229 baseUrl = ICore::self()->projectController()->projectsBaseDirectory(); 0230 } 0231 auto* assistant = new TemplateClassAssistant(QApplication::activeWindow(), baseUrl); 0232 assistant->setAttribute(Qt::WA_DeleteOnClose); 0233 assistant->show(); 0234 } 0235 0236 FileTemplatesPlugin::TemplateType FileTemplatesPlugin::determineTemplateType(const QUrl& url) 0237 { 0238 QDir dir(url.toLocalFile()); 0239 0240 /* 0241 * Search for a description file in the url's directory. 0242 * If it is not found there, try cascading up a maximum of 5 directories. 0243 */ 0244 int level = 0; 0245 while (dir.cdUp() && level < 5) 0246 { 0247 const QStringList filters{QStringLiteral("*.kdevtemplate"), QStringLiteral("*.desktop")}; 0248 const auto entries = dir.entryList(filters); 0249 for (const QString& entry : entries) { 0250 qCDebug(PLUGIN_FILETEMPLATES) << "Trying entry" << entry; 0251 /* 0252 * This logic is not perfect, but it works for most cases. 0253 * 0254 * Project template description files usually have the suffix 0255 * ".kdevtemplate", so those are easy to find. For project templates, 0256 * all the files in the directory are template files. 0257 * 0258 * On the other hand, file templates use the generic suffix ".desktop". 0259 * Fortunately, those explicitly list input and output files, so we 0260 * only match the explicitly listed files 0261 */ 0262 if (entry.endsWith(QLatin1String(".kdevtemplate"))) 0263 { 0264 return ProjectTemplate; 0265 } 0266 0267 auto* config = new KConfig(dir.absoluteFilePath(entry), KConfig::SimpleConfig); 0268 KConfigGroup group = config->group("General"); 0269 0270 qCDebug(PLUGIN_FILETEMPLATES) << "General group keys:" << group.keyList(); 0271 0272 if (!group.hasKey("Name") || !group.hasKey("Category")) 0273 { 0274 continue; 0275 } 0276 0277 if (group.hasKey("Files")) 0278 { 0279 qCDebug(PLUGIN_FILETEMPLATES) << "Group has files " << group.readEntry("Files", QStringList()); 0280 const auto outputFiles = group.readEntry("Files", QStringList()); 0281 for (const QString& outputFile : outputFiles) { 0282 if (dir.absoluteFilePath(config->group(outputFile).readEntry("File")) == url.toLocalFile()) 0283 { 0284 return FileTemplate; 0285 } 0286 } 0287 } 0288 0289 if (group.hasKey("ShowFilesAfterGeneration")) 0290 { 0291 return ProjectTemplate; 0292 } 0293 } 0294 0295 ++level; 0296 } 0297 0298 return NoTemplate; 0299 } 0300 0301 void FileTemplatesPlugin::previewTemplate() 0302 { 0303 auto* action = qobject_cast<QAction*>(sender()); 0304 if (!action || !action->data().toUrl().isValid()) 0305 { 0306 return; 0307 } 0308 auto* preview = qobject_cast<TemplatePreviewToolView*>(core()->uiController()->findToolView(i18nc("@title:window", "Template Preview"), m_toolView)); 0309 if (!preview) 0310 { 0311 return; 0312 } 0313 0314 core()->documentController()->activateDocument(core()->documentController()->openDocument(action->data().toUrl())); 0315 } 0316 0317 #include "filetemplatesplugin.moc" 0318 #include "moc_filetemplatesplugin.cpp"