File indexing completed on 2024-04-21 04:35:56
0001 /* This file is part of KDevelop 0002 * 0003 * Copyright (C) 2014-2015 Miquel SabatĂ© SolĂ <mikisabate@gmail.com> 0004 * 0005 * This program is free software: you can redistribute it and/or modify 0006 * it under the terms of the GNU General Public License as published by 0007 * the Free Software Foundation, either version 3 of the License, or 0008 * (at your option) any later version. 0009 * 0010 * This program is distributed in the hope that it will be useful, 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0013 * GNU General Public License for more details. 0014 * 0015 * You should have received a copy of the GNU General Public License 0016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 0017 */ 0018 0019 #include <launcher.h> 0020 0021 #include <QtWidgets/QAction> 0022 0023 #include <KConfigGroup> 0024 #include <KLocalizedString> 0025 0026 #include <execute/iexecuteplugin.h> 0027 #include <interfaces/icore.h> 0028 #include <interfaces/idocumentcontroller.h> 0029 #include <interfaces/iruncontroller.h> 0030 #include <interfaces/ilanguagecontroller.h> 0031 #include <interfaces/ilaunchconfiguration.h> 0032 #include <interfaces/ilauncher.h> 0033 #include <interfaces/ilaunchmode.h> 0034 #include <interfaces/iplugincontroller.h> 0035 #include <interfaces/launchconfigurationtype.h> 0036 #include <language/duchain/duchainlock.h> 0037 #include <language/duchain/duchainutils.h> 0038 #include <language/duchain/topducontext.h> 0039 0040 #include <debug.h> 0041 #include <languagesupport.h> 0042 #include <rails/helpers.h> 0043 0044 using namespace ruby; 0045 using namespace KDevelop; 0046 0047 namespace { 0048 0049 /** 0050 * @internal Set up the launch configuration before the run occurs. 0051 * @param cfg the KConfigGroup for this launch. 0052 * @param document the currently active document. 0053 */ 0054 void setupBeforeRun(KConfigGroup &cfg, IDocument *document) 0055 { 0056 Path root = rails::Helpers::findRailsRoot(document->url()); 0057 0058 if (root.isValid()) { 0059 cfg.writeEntry("Working Directory", root.toLocalFile()); 0060 } else { 0061 cfg.writeEntry("Working Directory", root.parent().path()); 0062 } 0063 } 0064 0065 /** 0066 * @internal Find the method under the cursor in the given \p doc. It's 0067 * used by the runCurrentTestFunction() slot. 0068 */ 0069 QString findFunctionUnderCursor(KDevelop::IDocument *doc) 0070 { 0071 DUChainReadLocker lock; 0072 0073 const auto top = DUChainUtils::standardContextForUrl(doc->url()); 0074 if (!top) { 0075 return ""; 0076 } 0077 0078 const auto &cursor = doc->cursorPosition(); 0079 auto context = top->findContextAt(CursorInRevision( 0080 cursor.line(), cursor.column() 0081 )); 0082 if (!context) { 0083 return ""; 0084 } 0085 0086 qCDebug(KDEV_RUBY) << "CONTEXT ID" << context->localScopeIdentifier(); 0087 return context->localScopeIdentifier().toString(); 0088 } 0089 0090 } 0091 0092 Launcher::Launcher(LanguageSupport *support) 0093 : QObject(support) 0094 , m_file(nullptr) 0095 , m_function(nullptr) 0096 { 0097 m_support = support; 0098 } 0099 0100 void Launcher::setupActions(KActionCollection &actions) 0101 { 0102 QAction *action = actions.addAction("ruby_run_current_file"); 0103 action->setText(i18n("Run Current File")); 0104 action->setShortcut(Qt::META | Qt::Key_F9); 0105 connect(action, &QAction::triggered, this, &Launcher::runCurrentFile); 0106 0107 action = actions.addAction("ruby_run_current_test_function"); 0108 action->setText(i18n("Run Current Test Function")); 0109 action->setShortcut(Qt::META | Qt::SHIFT | Qt::Key_F9); 0110 connect(action, &QAction::triggered, this, &Launcher::runCurrentTest); 0111 } 0112 0113 ILaunchConfiguration * Launcher::launchConfiguration(const QString &name) 0114 { 0115 for (auto config : m_support->core()->runController()->launchConfigurations()) { 0116 if (config->name() == name) { 0117 return config; 0118 } 0119 } 0120 0121 auto executePlugin = m_support->core()->pluginController()-> 0122 pluginForExtension("org.kdevelop.IExecutePlugin")->extension<IExecutePlugin>(); 0123 auto type = m_support->core()->runController()->launchConfigurationTypeForId( 0124 executePlugin->nativeAppConfigTypeId()); 0125 0126 if (!type) { 0127 return nullptr; 0128 } 0129 0130 auto mode = m_support->core()->runController()->launchModeForId("execute"); 0131 if (!mode) { 0132 return nullptr; 0133 } 0134 0135 KDevelop::ILauncher *launcher = nullptr; 0136 for (auto l : type->launchers()) { 0137 if (l->supportedModes().contains(QStringLiteral("execute"))) { 0138 launcher = l; 0139 break; 0140 } 0141 } 0142 if (!launcher) { 0143 return nullptr; 0144 } 0145 0146 auto config = m_support->core()->runController()->createLaunchConfiguration( 0147 type, qMakePair(mode->id(), launcher->id()), nullptr, name); 0148 0149 KConfigGroup cfg = config->config(); 0150 cfg.writeEntry("isExecutable", true); 0151 cfg.writeEntry("Executable", "ruby"); 0152 cfg.sync(); 0153 0154 return config; 0155 } 0156 0157 void Launcher::runCurrentFile() 0158 { 0159 auto doc = ICore::self()->documentController()->activeDocument(); 0160 if (!doc) { 0161 return; 0162 } 0163 0164 // Get out if this is not a Ruby file. 0165 if (!ICore::self()->languageController()->languagesForUrl(doc->url()). 0166 contains(m_support)) { 0167 return; 0168 } 0169 0170 if (!m_file) { 0171 m_file = launchConfiguration(i18n("Current Ruby File")); 0172 if (!m_file) { 0173 return; 0174 } 0175 } 0176 0177 KConfigGroup cfg = m_file->config(); 0178 setupBeforeRun(cfg, doc); 0179 cfg.writeEntry("Arguments", QStringList() << doc->url().toLocalFile()); 0180 cfg.sync(); 0181 0182 m_support->core()->runController()-> 0183 execute(QStringLiteral("execute"), m_file); 0184 } 0185 0186 void Launcher::runCurrentTest() 0187 { 0188 auto doc = ICore::self()->documentController()->activeDocument(); 0189 if (!doc) { 0190 return; 0191 } 0192 0193 // Get out if this is not a Ruby file. 0194 if (!ICore::self()->languageController()->languagesForUrl(doc->url()). 0195 contains(m_support)) { 0196 return; 0197 } 0198 0199 if (!m_function) { 0200 m_function = launchConfiguration(i18n("Current Ruby Test Function")); 0201 if (!m_function) { 0202 return; 0203 } 0204 } 0205 0206 // Find function under the cursor (if any). 0207 const QString &¤tFunction = findFunctionUnderCursor(doc); 0208 if (currentFunction.isEmpty()) { 0209 return; 0210 } 0211 0212 KConfigGroup cfg = m_function->config(); 0213 setupBeforeRun(cfg, doc); 0214 QStringList args{ 0215 doc->url().toLocalFile(), 0216 "-n", 0217 currentFunction 0218 }; 0219 cfg.writeEntry("Arguments", args.join(" ")); 0220 cfg.sync(); 0221 0222 m_support->core()->runController()->execute( 0223 QStringLiteral("execute"), m_function); 0224 } 0225