diff --git a/app/main.cpp b/app/main.cpp index 646625b78c..4b6a8ba34e 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -1,784 +1,784 @@ /*************************************************************************** * Copyright 2003-2009 Alexander Dymo * * Copyright 2007 Ralf Habacker * * Copyright 2006-2007 Matt Rogers * * Copyright 2006-2007 Hamish Rodda * * Copyright 2005-2007 Adam Treat * * Copyright 2003-2007 Jens Dagerbo * * Copyright 2001-2002 Bernd Gehrmann * * Copyright 2001-2002 Matthias Hoelzer-Kluepfel * * Copyright 2003 Roberto Raggi * * Copyright 2010 Niko Sams * * Copyright 2015 Kevin Funk * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include #include "urlinfo.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kdevideextension.h" #if KDEVELOP_SINGLE_APP #include "qtsingleapplication.h" #endif #include #ifdef Q_OS_MAC #include #endif using namespace KDevelop; namespace { #if KDEVELOP_SINGLE_APP QString serializeOpenFilesMessage(const QVector &infos) { QByteArray message; QDataStream stream(&message, QIODevice::WriteOnly); stream << QByteArrayLiteral("open"); stream << infos; return QString::fromLatin1(message.toHex()); } #endif void openFiles(const QVector& infos) { foreach (const UrlInfo& info, infos) { if (!ICore::self()->documentController()->openDocument(info.url, info.cursor)) { qWarning() << i18n("Could not open %1", info.url.toDisplayString(QUrl::PreferLocalFile)); } } } } class KDevelopApplication: #if KDEVELOP_SINGLE_APP public SharedTools::QtSingleApplication #else public QApplication #endif { public: explicit KDevelopApplication(int &argc, char **argv, bool GUIenabled = true) #if KDEVELOP_SINGLE_APP : SharedTools::QtSingleApplication(QStringLiteral("KDevelop"), argc, argv) #else : QApplication(argc, argv, GUIenabled) #endif { Q_UNUSED(GUIenabled); connect(this, &QGuiApplication::saveStateRequest, this, &KDevelopApplication::saveState); } #if KDEVELOP_SINGLE_APP public Q_SLOTS: void remoteArguments(const QString &message, QObject *socket) { Q_UNUSED(socket); QByteArray ba = QByteArray::fromHex(message.toLatin1()); QDataStream stream(ba); QByteArray command; stream >> command; qCDebug(APP) << "Received remote command: " << command; if (command == "open") { QVector infos; stream >> infos; QVector files, directories; for (const auto& info : infos) if (info.isDirectory()) directories << info; else files << info; openFiles(files); for(const auto &urlinfo : directories) ICore::self()->projectController()->openProjectForUrl(urlinfo.url); } else { qCWarning(APP) << "Unknown remote command: " << command; } } void fileOpenRequested(const QString &file) { openFiles({UrlInfo(file)}); } #endif private Q_SLOTS: void saveState( QSessionManager& sm ) { if (KDevelop::Core::self() && KDevelop::Core::self()->sessionController()) { QString x11SessionId = QStringLiteral("%1_%2").arg(sm.sessionId()).arg(sm.sessionKey()); const auto activeSession = KDevelop::Core::self()->sessionController()->activeSession(); if (!activeSession) { qWarning() << "No active session, can't save state"; return; } QString kdevelopSessionId = activeSession->id().toString(); sm.setRestartCommand({QCoreApplication::applicationFilePath(), "-session", x11SessionId, "-s", kdevelopSessionId}); } } }; /// Tries to find a session identified by @p data in @p sessions. /// The @p data may be either a session's name or a string-representation of its UUID. /// @return pointer to the session or NULL if nothing appropriate has been found static const KDevelop::SessionInfo* findSessionInList( const SessionInfos& sessions, const QString& data ) { // We won't search a session without input data, since that could lead to false-positives // with unnamed sessions if( data.isEmpty() ) return nullptr; for( auto it = sessions.constBegin(); it != sessions.constEnd(); ++it ) { if ( ( it->name == data ) || ( it->uuid.toString() == data ) ) { const KDevelop::SessionInfo& sessionRef = *it; return &sessionRef; } } return nullptr; } /// Performs a DBus call to open the given @p files in the running kdev instance identified by @p pid /// Returns the exit status static int openFilesInRunningInstance(const QVector& files, qint64 pid) { const QString service = QStringLiteral("org.kdevelop.kdevelop-%1").arg(pid); QDBusInterface iface(service, QStringLiteral("/org/kdevelop/DocumentController"), QStringLiteral("org.kdevelop.DocumentController")); QStringList urls; bool errors_occured = false; foreach ( const UrlInfo& file, files ) { QDBusReply result = iface.call(QStringLiteral("openDocumentSimple"), file.url.toString(), file.cursor.line(), file.cursor.column()); if ( ! result.value() ) { QTextStream err(stderr); err << i18n("Could not open file '%1'.", file.url.toDisplayString(QUrl::PreferLocalFile)) << "\n"; errors_occured = true; } } // make the window visible QDBusMessage makeVisible = QDBusMessage::createMethodCall( service, QStringLiteral("/kdevelop/MainWindow"), QStringLiteral("org.kdevelop.MainWindow"), QStringLiteral("ensureVisible") ); QDBusConnection::sessionBus().asyncCall( makeVisible ); return errors_occured; } /// Performs a DBus call to open the given @p files in the running kdev instance identified by @p pid /// Returns the exit status static int openProjectInRunningInstance(const QVector& paths, qint64 pid) { const QString service = QStringLiteral("org.kdevelop.kdevelop-%1").arg(pid); QDBusInterface iface(service, QStringLiteral("/org/kdevelop/ProjectController"), QStringLiteral("org.kdevelop.ProjectController")); int errors = 0; foreach ( const UrlInfo& path, paths ) { QDBusReply result = iface.call(QStringLiteral("openProjectForUrl"), path.url.toString()); if ( !result.isValid() ) { QTextStream err(stderr); err << i18n("Could not open project '%1': %2", path.url.toDisplayString(QUrl::PreferLocalFile), result.error().message()) << "\n"; ++errors; } } // make the window visible QDBusMessage makeVisible = QDBusMessage::createMethodCall( service, QStringLiteral("/kdevelop/MainWindow"), QStringLiteral("org.kdevelop.MainWindow"), QStringLiteral("ensureVisible") ); QDBusConnection::sessionBus().asyncCall( makeVisible ); return errors; } /// Gets the PID of a running KDevelop instance, eventually asking the user if there is more than one. /// Returns -1 in case there are no running sessions. static qint64 getRunningSessionPid() { SessionInfos candidates; foreach( const KDevelop::SessionInfo& si, KDevelop::SessionController::availableSessionInfos() ) { if( KDevelop::SessionController::isSessionRunning(si.uuid.toString()) ) { candidates << si; } } if ( candidates.isEmpty() ) { return -1; } QString sessionUuid; if ( candidates.size() == 1 ) { sessionUuid = candidates.first().uuid.toString(); } else { const QString title = i18n("Select the session to open the document in"); sessionUuid = KDevelop::SessionController::showSessionChooserDialog(title, true); } return KDevelop::SessionController::sessionRunInfo(sessionUuid).holderPid; } static QString findSessionId(const SessionInfos& availableSessionInfos, const QString& session) { //If there is a session and a project with the same name, always open the session //regardless of the order encountered QString projectAsSession; foreach(const KDevelop::SessionInfo& si, availableSessionInfos) { if ( session == si.name || session == si.uuid.toString() ) { return si.uuid.toString(); } else if (projectAsSession.isEmpty()) { foreach(const QUrl& k, si.projects) { QString fn(k.fileName()); fn = fn.left(fn.indexOf('.')); if ( session == fn ) { projectAsSession = si.uuid.toString(); } } } } if (projectAsSession.isEmpty()) { QTextStream qerr(stderr); qerr << endl << i18n("Cannot open unknown session %1. See `--list-sessions` switch for available sessions or use `-n` to create a new one.", session) << endl; } return projectAsSession; } static qint64 findSessionPid(const QString &sessionId) { KDevelop::SessionRunInfo sessionInfo = KDevelop::SessionController::sessionRunInfo( sessionId ); return sessionInfo.holderPid; } int main( int argc, char *argv[] ) { QElapsedTimer timer; timer.start(); // TODO: Maybe generalize, add KDEVELOP_STANDALONE build option #if defined(Q_OS_WIN) || defined(Q_OS_MAC) qputenv("KDE_FORK_SLAVES", "1"); // KIO slaves will be forked off instead of being started via DBus #endif // Useful for valgrind runs, just `export KDEV_DISABLE_JIT=1` if (qEnvironmentVariableIsSet("KDEV_DISABLE_JIT")) { qputenv("KDEV_DISABLE_WELCOMEPAGE", "1"); qputenv("QT_ENABLE_REGEXP_JIT", "0"); } - KLocalizedString::setApplicationDomain("kdevelop"); QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); #ifdef Q_OS_MAC CFBundleRef mainBundle = CFBundleGetMainBundle(); if (mainBundle) { // get the application's Info Dictionary. For app bundles this would live in the bundle's Info.plist, // for regular executables it is obtained in another way. CFMutableDictionaryRef infoDict = (CFMutableDictionaryRef) CFBundleGetInfoDictionary(mainBundle); if (infoDict) { // Try to prevent App Nap on OS X. This can be tricky in practice, at least in 10.9 . CFDictionarySetValue(infoDict, CFSTR("NSAppSleepDisabled"), kCFBooleanTrue); CFDictionarySetValue(infoDict, CFSTR("NSSupportsAutomaticTermination"), kCFBooleanFalse); } } #endif //we can't use KCmdLineArgs as it doesn't allow arguments for the debugee //so lookup the --debug switch and eat everything behind by decrementing argc //debugArgs is filled with args after --debug QStringList debugArgs; QString debugeeName; { bool debugFound = false; int c = argc; for (int i=0; i < c; ++i) { if (debugFound) { debugArgs << argv[i]; } else if ((qstrcmp(argv[i], "--debug") == 0) || (qstrcmp(argv[i], "-d") == 0)) { if (argc > (i + 1)) { i++; } argc = i + 1; debugFound = true; } else if (QString(argv[i]).startsWith(QLatin1String("--debug="))) { argc = i + 1; debugFound = true; } } } KDevelopApplication app(argc, argv); + KLocalizedString::setApplicationDomain("kdevelop"); static const char description[] = I18N_NOOP( "The KDevelop Integrated Development Environment" ); KAboutData aboutData( QStringLiteral("kdevelop"), i18n( "KDevelop" ), QByteArray(VERSION), i18n(description), KAboutLicense::GPL, i18n("Copyright 1999-2017, The KDevelop developers"), QString(), QStringLiteral("https://www.kdevelop.org/")); aboutData.setDesktopFileName(QStringLiteral("org.kde.kdevelop")); aboutData.addAuthor( i18n("Kevin Funk"), i18n( "Co-maintainer, C++/Clang, QA, Windows Support" ), QStringLiteral("kfunk@kde.org") ); aboutData.addAuthor( i18n("Sven Brauch"), i18n( "Co-maintainer, AppImage, Python Support, User Interface improvements" ), QStringLiteral("svenbrauch@gmail.com") ); aboutData.addAuthor( i18n("Aleix Pol Gonzalez"), i18n( "CMake Support, Run Support, Kross Support" ), QStringLiteral("aleixpol@gmail.com") ); aboutData.addAuthor( i18n("Milian Wolff"), i18n( "C++/Clang, Generic manager, Webdevelopment Plugins, Snippets, Performance" ), QStringLiteral("mail@milianw.de") ); aboutData.addAuthor( i18n("Olivier JG"), i18n( "C++/Clang, DUChain, Bug Fixes" ), QStringLiteral("olivier.jg@gmail.com") ); aboutData.addAuthor( i18n("Andreas Pakulat"), i18n( "Architecture, VCS Support, Project Management Support, QMake Projectmanager" ), QStringLiteral("apaku@gmx.de") ); aboutData.addAuthor( i18n("Alexander Dymo"), i18n( "Architecture, Sublime UI, Ruby support" ), QStringLiteral("adymo@kdevelop.org") ); aboutData.addAuthor( i18n("David Nolden"), i18n( "Definition-Use Chain, C++ Support, Code Navigation, Code Completion, Coding Assistance, Refactoring" ), QStringLiteral("david.nolden.kdevelop@art-master.de") ); aboutData.addAuthor( i18n("Vladimir Prus"), i18n( "GDB integration" ), QStringLiteral("ghost@cs.msu.su") ); aboutData.addAuthor( i18n("Hamish Rodda"), i18n( "Text editor integration, definition-use chain" ), QStringLiteral("rodda@kde.org") ); aboutData.addAuthor( i18n("Amilcar do Carmo Lucas"), i18n( "Website admin, API documentation, Doxygen and autoproject patches" ), QStringLiteral("amilcar@kdevelop.org") ); aboutData.addAuthor( i18n("Niko Sams"), i18n( "GDB integration, Webdevelopment Plugins" ), QStringLiteral("niko.sams@gmail.com") ); aboutData.addCredit( i18n("Matt Rogers"), QString(), QStringLiteral("mattr@kde.org")); aboutData.addCredit( i18n("Cédric Pasteur"), i18n("astyle and indent support"), QStringLiteral("cedric.pasteur@free.fr") ); aboutData.addCredit( i18n("Evgeniy Ivanov"), i18n("Distributed VCS, Git, Mercurial"), QStringLiteral("powerfox@kde.ru") ); // QTest integration is separate in playground currently. //aboutData.addCredit( i18n("Manuel Breugelmanns"), i18n( "Veritas, QTest integration"), "mbr.nxi@gmail.com" ); aboutData.addCredit( i18n("Robert Gruber") , i18n( "SnippetPart, debugger and usability patches" ), QStringLiteral("rgruber@users.sourceforge.net") ); aboutData.addCredit( i18n("Dukju Ahn"), i18n( "Subversion plugin, Custom Make Manager, Overall improvements" ), QStringLiteral("dukjuahn@gmail.com") ); aboutData.addCredit( i18n("Harald Fernengel"), i18n( "Ported to Qt 3, patches, valgrind, diff and perforce support" ), QStringLiteral("harry@kdevelop.org") ); aboutData.addCredit( i18n("Roberto Raggi"), i18n( "C++ parser" ), QStringLiteral("roberto@kdevelop.org") ); aboutData.addCredit( i18n("The KWrite authors"), i18n( "Kate editor component" ), QStringLiteral("kwrite-devel@kde.org") ); aboutData.addCredit( i18n("Nokia Corporation/Qt Software"), i18n( "Designer code" ), QStringLiteral("qt-info@nokia.com") ); aboutData.addCredit( i18n("Contributors to older versions:"), QString(), QLatin1String("") ); aboutData.addCredit( i18n("Bernd Gehrmann"), i18n( "Initial idea, basic architecture, much initial source code" ), QStringLiteral("bernd@kdevelop.org") ); aboutData.addCredit( i18n("Caleb Tennis"), i18n( "KTabBar, bugfixes" ), QStringLiteral("caleb@aei-tech.com") ); aboutData.addCredit( i18n("Richard Dale"), i18n( "Java & Objective C support" ), QStringLiteral("Richard_Dale@tipitina.demon.co.uk") ); aboutData.addCredit( i18n("John Birch"), i18n( "Debugger frontend" ), QStringLiteral("jbb@kdevelop.org") ); aboutData.addCredit( i18n("Sandy Meier"), i18n( "PHP support, context menu stuff" ), QStringLiteral("smeier@kdevelop.org") ); aboutData.addCredit( i18n("Kurt Granroth"), i18n( "KDE application templates" ), QStringLiteral("kurth@granroth.org") ); aboutData.addCredit( i18n("Ian Reinhart Geiser"), i18n( "Dist part, bash support, application templates" ), QStringLiteral("geiseri@yahoo.com") ); aboutData.addCredit( i18n("Matthias Hoelzer-Kluepfel"), i18n( "Several components, htdig indexing" ), QStringLiteral("hoelzer@kde.org") ); aboutData.addCredit( i18n("Victor Roeder"), i18n( "Help with Automake manager and persistent class store" ), QStringLiteral("victor_roeder@gmx.de") ); aboutData.addCredit( i18n("Simon Hausmann"), i18n( "Help with KParts infrastructure" ), QStringLiteral("hausmann@kde.org") ); aboutData.addCredit( i18n("Oliver Kellogg"), i18n( "Ada support" ), QStringLiteral("okellogg@users.sourceforge.net") ); aboutData.addCredit( i18n("Jakob Simon-Gaarde"), i18n( "QMake projectmanager" ), QStringLiteral("jsgaarde@tdcspace.dk") ); aboutData.addCredit( i18n("Falk Brettschneider"), i18n( "MDI modes, QEditor, bugfixes" ), QStringLiteral("falkbr@kdevelop.org") ); aboutData.addCredit( i18n("Mario Scalas"), i18n( "PartExplorer, redesign of CvsPart, patches, bugs(fixes)" ), QStringLiteral("mario.scalas@libero.it") ); aboutData.addCredit( i18n("Jens Dagerbo"), i18n( "Replace, Bookmarks, FileList and CTags2 plugins. Overall improvements and patches" ), QStringLiteral("jens.dagerbo@swipnet.se") ); aboutData.addCredit( i18n("Julian Rockey"), i18n( "Filecreate part and other bits and patches" ), QStringLiteral("linux@jrockey.com") ); aboutData.addCredit( i18n("Ajay Guleria"), i18n( "ClearCase support" ), QStringLiteral("ajay_guleria@yahoo.com") ); aboutData.addCredit( i18n("Marek Janukowicz"), i18n( "Ruby support" ), QStringLiteral("child@t17.ds.pwr.wroc.pl") ); aboutData.addCredit( i18n("Robert Moniot"), i18n( "Fortran documentation" ), QStringLiteral("moniot@fordham.edu") ); aboutData.addCredit( i18n("Ka-Ping Yee"), i18n( "Python documentation utility" ), QStringLiteral("ping@lfw.org") ); aboutData.addCredit( i18n("Dimitri van Heesch"), i18n( "Doxygen wizard" ), QStringLiteral("dimitri@stack.nl") ); aboutData.addCredit( i18n("Hugo Varotto"), i18n( "Fileselector component" ), QStringLiteral("hugo@varotto-usa.com") ); aboutData.addCredit( i18n("Matt Newell"), i18n( "Fileselector component" ), QStringLiteral("newellm@proaxis.com") ); aboutData.addCredit( i18n("Daniel Engelschalt"), i18n( "C++ code completion, persistent class store" ), QStringLiteral("daniel.engelschalt@gmx.net") ); aboutData.addCredit( i18n("Stephane Ancelot"), i18n( "Patches" ), QStringLiteral("sancelot@free.fr") ); aboutData.addCredit( i18n("Jens Zurheide"), i18n( "Patches" ), QStringLiteral("jens.zurheide@gmx.de") ); aboutData.addCredit( i18n("Luc Willems"), i18n( "Help with Perl support" ), QStringLiteral("Willems.luc@pandora.be") ); aboutData.addCredit( i18n("Marcel Turino"), i18n( "Documentation index view" ), QStringLiteral("M.Turino@gmx.de") ); aboutData.addCredit( i18n("Yann Hodique"), i18n( "Patches" ), QStringLiteral("Yann.Hodique@lifl.fr") ); aboutData.addCredit( i18n("Tobias Gl\303\244\303\237er") , i18n( "Documentation Finder, qmake projectmanager patches, usability improvements, bugfixes ... " ), QStringLiteral("tobi.web@gmx.de") ); aboutData.addCredit( i18n("Andreas Koepfle") , i18n( "QMake project manager patches" ), QStringLiteral("koepfle@ti.uni-mannheim.de") ); aboutData.addCredit( i18n("Sascha Cunz") , i18n( "Cleanup and bugfixes for qEditor, AutoMake and much other stuff" ), QStringLiteral("mail@sacu.de") ); aboutData.addCredit( i18n("Zoran Karavla"), i18n( "Artwork for the ruby language" ), QStringLiteral("webmaster@the-error.net"), QStringLiteral("http://the-error.net") ); KAboutData::setApplicationData(aboutData); // set icon for shells which do not use desktop file metadata // but without setting replacing an existing icon with an empty one! QApplication::setWindowIcon(QIcon::fromTheme(QStringLiteral("kdevelop"), QApplication::windowIcon())); KCrash::initialize(); Kdelibs4ConfigMigrator migrator(QStringLiteral("kdevelop")); migrator.setConfigFiles({QStringLiteral("kdeveloprc")}); migrator.setUiFiles({QStringLiteral("kdevelopui.rc")}); migrator.migrate(); // High DPI support app.setAttribute(Qt::AA_UseHighDpiPixmaps, true); qCDebug(APP) << "Startup"; QCommandLineParser parser; parser.addVersionOption(); parser.addHelpOption(); aboutData.setupCommandLine(&parser); parser.addOption(QCommandLineOption{QStringList{"n", "new-session"}, i18n("Open KDevelop with a new session using the given name."), QStringLiteral("name")}); parser.addOption(QCommandLineOption{QStringList{"s", "open-session"}, i18n("Open KDevelop with the given session.\n" "You can pass either hash or the name of the session." ), QStringLiteral("session")}); parser.addOption(QCommandLineOption{QStringList{"rm", "remove-session"}, i18n("Delete the given session.\n" "You can pass either hash or the name of the session." ), QStringLiteral("session")}); parser.addOption(QCommandLineOption{QStringList{"ps", "pick-session"}, i18n("Shows all available sessions and lets you select one to open.")}); parser.addOption(QCommandLineOption{QStringList{"pss", "pick-session-shell"}, i18n("List all available sessions on shell and lets you select one to open.")}); parser.addOption(QCommandLineOption{QStringList{"l", "list-sessions"}, i18n("List available sessions and quit.")}); parser.addOption(QCommandLineOption{QStringList{"p", "project"}, i18n("Open KDevelop and load the given project."), QStringLiteral("project")}); parser.addOption(QCommandLineOption{QStringList{"d", "debug"}, i18n("Start debugging an application in KDevelop with the given debugger.\n" "The executable that should be debugged must follow - including arguments.\n" "Example: kdevelop --debug gdb myapp --foo bar"), QStringLiteral("debugger")}); // this is used by the 'kdevelop!' script to retrieve the pid of a KDEVELOP // instance. When this is called, then we should just print the PID on the // standard-output. If a session is specified through open-session, then // we should return the PID of that session. Otherwise, if only a single // session is running, then we should just return the PID of that session. // Otherwise, we should print a command-line session-chooser dialog ("--pss"), // which only shows the running sessions, and the user can pick one. parser.addOption(QCommandLineOption{QStringList{"pid"}}); parser.addPositionalArgument(QStringLiteral("files"), i18n( "Files to load" ), QStringLiteral("[FILE...]")); // The session-controller needs to arguments to eventually pass them to newly opened sessions KDevelop::SessionController::setArguments(argc, argv); parser.process(app); aboutData.processCommandLine(&parser); if(parser.isSet(QStringLiteral("list-sessions"))) { QTextStream qout(stdout); qout << endl << ki18n("Available sessions (use '-s HASH' or '-s NAME' to open a specific one):").toString() << endl << endl; qout << QStringLiteral("%1").arg(ki18n("Hash").toString(), -38) << '\t' << ki18n("Name: Opened Projects").toString() << endl; foreach(const KDevelop::SessionInfo& si, KDevelop::SessionController::availableSessionInfos()) { if ( si.name.isEmpty() && si.projects.isEmpty() ) { continue; } qout << si.uuid.toString() << '\t' << si.description; if(KDevelop::SessionController::isSessionRunning(si.uuid.toString())) qout << " " << i18n("[running]"); qout << endl; } return 0; } // Handle extra arguments, which stand for files to open QVector initialFiles; QVector initialDirectories; foreach (const QString &file, parser.positionalArguments()) { const UrlInfo info(file); if (info.isDirectory()) { initialDirectories.append(info); } else { initialFiles.append(info); } } const auto availableSessionInfos = KDevelop::SessionController::availableSessionInfos(); if ((!initialFiles.isEmpty() || !initialDirectories.isEmpty()) && !parser.isSet(QStringLiteral("new-session"))) { #if KDEVELOP_SINGLE_APP if (app.isRunning()) { bool success = app.sendMessage(serializeOpenFilesMessage(initialFiles << initialDirectories)); if (success) { return 0; } } #else qint64 pid = -1; if (parser.isSet(QStringLiteral("open-session"))) { const QString session = findSessionId(availableSessionInfos, parser.value(QStringLiteral("open-session"))); if (session.isEmpty()) { return 1; } else if (KDevelop::SessionController::isSessionRunning(session)) { pid = findSessionPid(session); } } else { pid = getRunningSessionPid(); } if ( pid > 0 ) { return openFilesInRunningInstance(initialFiles, pid) + openProjectInRunningInstance(initialDirectories, pid); } // else there are no running sessions, and the generated list of files will be opened below. #endif } // if empty, restart kdevelop with last active session, see SessionController::defaultSessionId QString session; uint nRunningSessions = 0; foreach(const KDevelop::SessionInfo& si, availableSessionInfos) if(KDevelop::SessionController::isSessionRunning(si.uuid.toString())) ++nRunningSessions; // also show the picker dialog when a pid shall be retrieved and multiple // sessions are running. if(parser.isSet(QStringLiteral("pss")) || (parser.isSet(QStringLiteral("pid")) && !parser.isSet(QStringLiteral("open-session")) && !parser.isSet(QStringLiteral("ps")) && nRunningSessions > 1)) { QTextStream qerr(stderr); SessionInfos candidates; foreach(const KDevelop::SessionInfo& si, availableSessionInfos) if( (!si.name.isEmpty() || !si.projects.isEmpty() || parser.isSet(QStringLiteral("pid"))) && (!parser.isSet(QStringLiteral("pid")) || KDevelop::SessionController::isSessionRunning(si.uuid.toString()))) candidates << si; if(candidates.size() == 0) { qerr << "no session available" << endl; return 1; } if(candidates.size() == 1 && parser.isSet(QStringLiteral("pid"))) { session = candidates[0].uuid.toString(); }else{ for(int i = 0; i < candidates.size(); ++i) qerr << "[" << i << "]: " << candidates[i].description << endl; int chosen; std::cin >> chosen; if(std::cin.good() && (chosen >= 0 && chosen < candidates.size())) { session = candidates[chosen].uuid.toString(); }else{ qerr << "invalid selection" << endl; return 1; } } } if(parser.isSet(QStringLiteral("ps"))) { bool onlyRunning = parser.isSet(QStringLiteral("pid")); session = KDevelop::SessionController::showSessionChooserDialog(i18n("Select the session you would like to use"), onlyRunning); if(session.isEmpty()) return 1; } if ( parser.isSet(QStringLiteral("debug")) ) { if ( debugArgs.isEmpty() ) { QTextStream qerr(stderr); qerr << endl << i18nc("@info:shell", "Specify the executable you want to debug.") << endl; return 1; } QFileInfo executableFileInfo(debugArgs.first()); if (!executableFileInfo.exists()) { executableFileInfo = QStandardPaths::findExecutable(debugArgs.first()); if (!executableFileInfo.exists()) { QTextStream qerr(stderr); qerr << endl << i18nc("@info:shell", "Specified executable does not exist.") << endl; return 1; } } debugArgs.first() = executableFileInfo.absoluteFilePath(); debugeeName = i18n("Debug %1", executableFileInfo.fileName()); session = debugeeName; } else if ( parser.isSet(QStringLiteral("new-session")) ) { session = parser.value(QStringLiteral("new-session")); foreach(const KDevelop::SessionInfo& si, availableSessionInfos) { if ( session == si.name ) { QTextStream qerr(stderr); qerr << endl << i18n("A session with the name %1 exists already. Use the -s switch to open it.", session) << endl; return 1; } } // session doesn't exist, we can create it } else if ( parser.isSet(QStringLiteral("open-session")) ) { session = findSessionId(availableSessionInfos, parser.value(QStringLiteral("open-session"))); if (session.isEmpty()) { return 1; } } else if ( parser.isSet(QStringLiteral("remove-session")) ) { session = parser.value(QStringLiteral("remove-session")); auto si = findSessionInList(availableSessionInfos, session); if (!si) { QTextStream qerr(stderr); qerr << endl << i18n("No session with the name %1 exists.", session) << endl; return 1; } auto sessionLock = KDevelop::SessionController::tryLockSession(si->uuid.toString()); if (!sessionLock.lock) { QTextStream qerr(stderr); qerr << endl << i18n("Could not lock session %1 for deletion.", session) << endl; return 1; } KDevelop::SessionController::deleteSessionFromDisk(sessionLock.lock); QTextStream qout(stdout); qout << endl << i18n("Session with name %1 was successfully removed.", session) << endl; return 0; } if(parser.isSet(QStringLiteral("pid"))) { if (session.isEmpty()) { // just pick the first running session foreach(const KDevelop::SessionInfo& si, availableSessionInfos) if(KDevelop::SessionController::isSessionRunning(si.uuid.toString())) session = si.uuid.toString(); } const KDevelop::SessionInfo* sessionData = findSessionInList(availableSessionInfos, session); if( !sessionData ) { qCritical() << "session not given or does not exist"; return 5; } const auto pid = findSessionPid(sessionData->uuid.toString()); if (pid > 0) { // Print the PID and we're ready std::cout << pid << std::endl; return 0; } else { qCritical() << sessionData->uuid.toString() << sessionData->name << "is not running"; return 5; } } KDevIDEExtension::init(); if(!Core::initialize(Core::Default, session)) return 5; // register a DBUS service for this process, so that we can open files in it from other invocations QDBusConnection::sessionBus().registerService(QStringLiteral("org.kdevelop.kdevelop-%1").arg(app.applicationPid())); Core* core = Core::self(); if (!QProcessEnvironment::systemEnvironment().contains(QStringLiteral("KDEV_DISABLE_WELCOMEPAGE"))) { core->pluginController()->loadPlugin(QStringLiteral("KDevWelcomePage")); } QStringList projectNames = parser.values(QStringLiteral("project")); foreach(const QString& projectName, projectNames) { QFileInfo info( projectName ); if( info.suffix() == QLatin1String("kdev4") ) { // make sure the project is not already opened by the session controller bool shouldOpen = true; Path path(info.absoluteFilePath()); foreach(KDevelop::IProject* project, core->projectController()->projects()) { if (project->projectFile() == path) { shouldOpen = false; break; } } if (shouldOpen) { core->projectController()->openProject( path.toUrl() ); } } } const QString debugStr = QStringLiteral("debug"); if ( parser.isSet(debugStr) ) { Q_ASSERT( !debugeeName.isEmpty() ); QString launchName = debugeeName; KDevelop::LaunchConfiguration* launch = nullptr; qCDebug(APP) << launchName; foreach (KDevelop::LaunchConfiguration *l, core->runControllerInternal()->launchConfigurationsInternal()) { qCDebug(APP) << l->name(); if (l->name() == launchName) { launch = l; } } KDevelop::LaunchConfigurationType *type = nullptr; foreach (KDevelop::LaunchConfigurationType *t, core->runController()->launchConfigurationTypes()) { qCDebug(APP) << t->id(); if (t->id() == QLatin1String("Native Application")) { type = t; break; } } if (!type) { QTextStream qerr(stderr); qerr << endl << i18n("Cannot find native launch configuration type") << endl; return 1; } if (launch && launch->type()->id() != QLatin1String("Native Application")) launch = nullptr; if (launch && launch->launcherForMode(debugStr) != parser.value(debugStr)) launch = nullptr; if (!launch) { qCDebug(APP) << launchName << "not found, creating a new one"; QPair launcher; launcher.first = debugStr; foreach (KDevelop::ILauncher *l, type->launchers()) { if (l->id() == parser.value(debugStr)) { if (l->supportedModes().contains(debugStr)) { launcher.second = l->id(); } } } if (launcher.second.isEmpty()) { QTextStream qerr(stderr); qerr << endl << i18n("Cannot find launcher %1", parser.value(debugStr)) << endl; return 1; } KDevelop::ILaunchConfiguration* ilaunch = core->runController()->createLaunchConfiguration(type, launcher, nullptr, launchName); launch = static_cast(ilaunch); } type->configureLaunchFromCmdLineArguments(launch->config(), debugArgs); launch->config().writeEntry("Break on Start", true); core->runControllerInternal()->setDefaultLaunch(launch); core->runControllerInternal()->execute(debugStr, launch); } else { openFiles(initialFiles); for(const auto &urlinfo: initialDirectories) core->projectController()->openProjectForUrl(urlinfo.url); } #if KDEVELOP_SINGLE_APP // Set up remote arguments. QObject::connect(&app, &SharedTools::QtSingleApplication::messageReceived, &app, &KDevelopApplication::remoteArguments); QObject::connect(&app, &SharedTools::QtSingleApplication::fileOpenRequest, &app, &KDevelopApplication::fileOpenRequested); #endif qCDebug(APP) << "Done startup" << "- took:" << timer.elapsed() << "ms"; timer.invalidate(); return app.exec(); } diff --git a/kdevplatform/documentation/documentationview.cpp b/kdevplatform/documentation/documentationview.cpp index 8b21b11861..2c6cccf8bd 100644 --- a/kdevplatform/documentation/documentationview.cpp +++ b/kdevplatform/documentation/documentationview.cpp @@ -1,398 +1,419 @@ /* Copyright 2009 Aleix Pol Gonzalez Copyright 2010 Benjamin Port This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "documentationview.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "documentationfindwidget.h" #include "debug.h" using namespace KDevelop; DocumentationView::DocumentationView(QWidget* parent, ProvidersModel* model) : QWidget(parent), mProvidersModel(model) { setWindowIcon(QIcon::fromTheme(QStringLiteral("documentation"), windowIcon())); setWindowTitle(i18n("Documentation")); setLayout(new QVBoxLayout(this)); layout()->setMargin(0); layout()->setSpacing(0); mFindDoc = new DocumentationFindWidget; mFindDoc->hide(); // insert placeholder widget at location of doc view layout()->addWidget(new QWidget(this)); layout()->addWidget(mFindDoc); setupActions(); mCurrent = mHistory.end(); setFocusProxy(mIdentifiers); QMetaObject::invokeMethod(this, "initialize", Qt::QueuedConnection); } QList DocumentationView::contextMenuActions() const { // TODO: also show providers return {mBack, mForward, mHomeAction, mSeparatorBeforeFind, mFind}; } void DocumentationView::setupActions() { // use custom QAction's with createWidget for mProviders and mIdentifiers mBack = new QAction(QIcon::fromTheme(QStringLiteral("go-previous")), i18n("Back"), this); mBack->setEnabled(false); connect(mBack, &QAction::triggered, this, &DocumentationView::browseBack); addAction(mBack); mForward = new QAction(QIcon::fromTheme(QStringLiteral("go-next")), i18n("Forward"), this); mForward->setEnabled(false); connect(mForward, &QAction::triggered, this, &DocumentationView::browseForward); addAction(mForward); mHomeAction = new QAction(QIcon::fromTheme(QStringLiteral("go-home")), i18n("Home"), this); + mHomeAction->setEnabled(false); connect(mHomeAction, &QAction::triggered, this, &DocumentationView::showHome); addAction(mHomeAction); mProviders = new QComboBox(this); auto providersAction = new QWidgetAction(this); providersAction->setDefaultWidget(mProviders); addAction(providersAction); mIdentifiers = new QLineEdit(this); + mIdentifiers->setEnabled(false); mIdentifiers->setClearButtonEnabled(true); mIdentifiers->setPlaceholderText(i18n("Search...")); mIdentifiers->setCompleter(new QCompleter(mIdentifiers)); // mIdentifiers->completer()->setCompletionMode(QCompleter::UnfilteredPopupCompletion); mIdentifiers->completer()->setCaseSensitivity(Qt::CaseInsensitive); /* vertical size policy should be left to the style. */ mIdentifiers->setSizePolicy(QSizePolicy::Expanding, mIdentifiers->sizePolicy().verticalPolicy()); connect(mIdentifiers->completer(), static_cast(&QCompleter::activated), this, &DocumentationView::changedSelection); connect(mIdentifiers, &QLineEdit::returnPressed, this, &DocumentationView::returnPressed); auto identifiersAction = new QWidgetAction(this); identifiersAction->setDefaultWidget(mIdentifiers); addAction(identifiersAction); mSeparatorBeforeFind = new QAction(this); mSeparatorBeforeFind->setSeparator(true); addAction(mSeparatorBeforeFind); mFind = new QAction(QIcon::fromTheme(QStringLiteral("edit-find")), i18n("Find in Text..."), this); mFind->setToolTip(i18n("Find in text of current documentation page.")); + mFind->setEnabled(false); connect(mFind, &QAction::triggered, mFindDoc, &DocumentationFindWidget::startSearch); addAction(mFind); auto closeFindBarShortcut = new QShortcut(QKeySequence(Qt::Key_Escape), this); closeFindBarShortcut->setContext(Qt::WidgetWithChildrenShortcut); connect(closeFindBarShortcut, &QShortcut::activated, mFindDoc, &QWidget::hide); } void DocumentationView::initialize() { mProviders->setModel(mProvidersModel); connect(mProviders, static_cast(&QComboBox::activated), this, &DocumentationView::changedProvider); foreach (IDocumentationProvider* p, mProvidersModel->providers()) { // can't use new signal/slot syntax here, IDocumentation is not a QObject connect(dynamic_cast(p), SIGNAL(addHistory(KDevelop::IDocumentation::Ptr)), this, SLOT(addHistory(KDevelop::IDocumentation::Ptr))); } connect(mProvidersModel, &ProvidersModel::providersChanged, this, &DocumentationView::emptyHistory); - if (mProvidersModel->rowCount() > 0) { + const bool hasProviders = (mProviders->count() > 0); + mHomeAction->setEnabled(hasProviders); + mIdentifiers->setEnabled(hasProviders); + if (hasProviders) { changedProvider(0); } } void DocumentationView::browseBack() { --mCurrent; mBack->setEnabled(mCurrent != mHistory.begin()); mForward->setEnabled(true); updateView(); } void DocumentationView::browseForward() { ++mCurrent; mForward->setEnabled(mCurrent+1 != mHistory.end()); mBack->setEnabled(true); updateView(); } void DocumentationView::showHome() { auto prov = mProvidersModel->provider(mProviders->currentIndex()); showDocumentation(prov->homePage()); } void DocumentationView::returnPressed() { // Exit if search text is empty. It's necessary because of empty // line edit text not leads to "empty" completer indexes. if (mIdentifiers->text().isEmpty()) return; // Exit if completer popup has selected item - in this case 'Return' // key press emits QCompleter::activated signal which is already connected. if (mIdentifiers->completer()->popup()->currentIndex().isValid()) return; // If user doesn't select any item in popup we will try to use the first row. if (mIdentifiers->completer()->setCurrentRow(0)) changedSelection(mIdentifiers->completer()->currentIndex()); } void DocumentationView::changedSelection(const QModelIndex& idx) { if (idx.isValid()) { // Skip view update if user try to show already opened documentation mIdentifiers->setText(idx.data(Qt::DisplayRole).toString()); if (mIdentifiers->text() == (*mCurrent)->name()) { return; } IDocumentationProvider* prov = mProvidersModel->provider(mProviders->currentIndex()); auto doc = prov->documentationForIndex(idx); if (doc) { showDocumentation(doc); } } } void DocumentationView::showDocumentation(const IDocumentation::Ptr& doc) { qCDebug(DOCUMENTATION) << "showing" << doc->name(); addHistory(doc); updateView(); } void DocumentationView::addHistory(const IDocumentation::Ptr& doc) { mBack->setEnabled(!mHistory.isEmpty()); mForward->setEnabled(false); // clear all history following the current item, unless we're already // at the end (otherwise this code crashes when history is empty, which // happens when addHistory is first called on startup to add the // homepage) if (mCurrent+1 < mHistory.end()) { mHistory.erase(mCurrent+1, mHistory.end()); } mHistory.append(doc); mCurrent = mHistory.end()-1; // NOTE: we assume an existing widget was used to navigate somewhere // but this history entry actually contains the new info for the // title... this is ugly and should be refactored somehow if (mIdentifiers->completer()->model() == (*mCurrent)->provider()->indexModel()) { mIdentifiers->setText((*mCurrent)->name()); } } void DocumentationView::emptyHistory() { mHistory.clear(); mCurrent = mHistory.end(); mBack->setEnabled(false); mForward->setEnabled(false); - if (mProviders->count() > 0) { + const bool hasProviders = (mProviders->count() > 0); + mHomeAction->setEnabled(hasProviders); + mIdentifiers->setEnabled(hasProviders); + if (hasProviders) { mProviders->setCurrentIndex(0); changedProvider(0); + } else { + updateView(); } } void DocumentationView::updateView() { - mProviders->setCurrentIndex(mProvidersModel->rowForProvider((*mCurrent)->provider())); - mIdentifiers->completer()->setModel((*mCurrent)->provider()->indexModel()); - mIdentifiers->setText((*mCurrent)->name()); - mIdentifiers->completer()->setCompletionPrefix((*mCurrent)->name()); + if (mCurrent != mHistory.end()) { + mProviders->setCurrentIndex(mProvidersModel->rowForProvider((*mCurrent)->provider())); + mIdentifiers->completer()->setModel((*mCurrent)->provider()->indexModel()); + mIdentifiers->setText((*mCurrent)->name()); + mIdentifiers->completer()->setCompletionPrefix((*mCurrent)->name()); + } else { + mIdentifiers->clear(); + } QLayoutItem* lastview = layout()->takeAt(0); Q_ASSERT(lastview); if (lastview->widget()->parent() == this) { lastview->widget()->deleteLater(); } delete lastview; mFindDoc->setEnabled(false); - QWidget* w = (*mCurrent)->documentationWidget(mFindDoc, this); - Q_ASSERT(w); - QWidget::setTabOrder(mIdentifiers, w); + QWidget* w; + if (mCurrent != mHistory.end()) { + w = (*mCurrent)->documentationWidget(mFindDoc, this); + Q_ASSERT(w); + QWidget::setTabOrder(mIdentifiers, w); + } else { + // placeholder widget at location of doc view + w = new QWidget(this); + } mFind->setEnabled(mFindDoc->isEnabled()); if (!mFindDoc->isEnabled()) { mFindDoc->hide(); } QLayoutItem* findWidget = layout()->takeAt(0); layout()->addWidget(w); layout()->addItem(findWidget); } void DocumentationView::changedProvider(int row) { mIdentifiers->completer()->setModel(mProvidersModel->provider(row)->indexModel()); mIdentifiers->clear(); showHome(); } ////////////// ProvidersModel ////////////////// ProvidersModel::ProvidersModel(QObject* parent) : QAbstractListModel(parent) , mProviders(ICore::self()->documentationController()->documentationProviders()) { connect(ICore::self()->pluginController(), &IPluginController::unloadingPlugin, this, &ProvidersModel::unloaded); connect(ICore::self()->pluginController(), &IPluginController::pluginLoaded, this, &ProvidersModel::loaded); connect(ICore::self()->documentationController(), &IDocumentationController::providersChanged, this, &ProvidersModel::reloadProviders); } void ProvidersModel::reloadProviders() { beginResetModel(); mProviders = ICore::self()->documentationController()->documentationProviders(); std::sort(mProviders.begin(), mProviders.end(), [](const KDevelop::IDocumentationProvider* a, const KDevelop::IDocumentationProvider* b) { return a->name() < b->name(); }); endResetModel(); emit providersChanged(); } QVariant ProvidersModel::data(const QModelIndex& index, int role) const { if (index.row() >= mProviders.count() || index.row() < 0) return QVariant(); QVariant ret; switch (role) { case Qt::DisplayRole: ret = provider(index.row())->name(); break; case Qt::DecorationRole: ret = provider(index.row())->icon(); break; } return ret; } void ProvidersModel::addProvider(IDocumentationProvider* provider) { if (!provider || mProviders.contains(provider)) return; int pos = 0; while (pos < mProviders.size() && mProviders[pos]->name() < provider->name()) ++pos; beginInsertRows(QModelIndex(), pos, pos); mProviders.insert(pos, provider); endInsertRows(); emit providersChanged(); } void ProvidersModel::removeProvider(IDocumentationProvider* provider) { int pos; if (!provider || (pos = mProviders.indexOf(provider)) < 0) return; beginRemoveRows(QModelIndex(), pos, pos); mProviders.removeAt(pos); endRemoveRows(); emit providersChanged(); } void ProvidersModel::unloaded(IPlugin* plugin) { removeProvider(plugin->extension()); IDocumentationProviderProvider* providerProvider = plugin->extension(); if (providerProvider) { foreach(IDocumentationProvider* provider, providerProvider->providers()) removeProvider(provider); } } void ProvidersModel::loaded(IPlugin* plugin) { addProvider(plugin->extension()); IDocumentationProviderProvider* providerProvider = plugin->extension(); if (providerProvider) { foreach(IDocumentationProvider* provider, providerProvider->providers()) addProvider(provider); } } int ProvidersModel::rowCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : mProviders.count(); } int ProvidersModel::rowForProvider(IDocumentationProvider* provider) { return mProviders.indexOf(provider); } IDocumentationProvider* ProvidersModel::provider(int pos) const { return mProviders[pos]; } QList ProvidersModel::providers() { return mProviders; } diff --git a/kdevplatform/project/abstractfilemanagerplugin.cpp b/kdevplatform/project/abstractfilemanagerplugin.cpp index 09960eabfe..de8b1d9bd4 100644 --- a/kdevplatform/project/abstractfilemanagerplugin.cpp +++ b/kdevplatform/project/abstractfilemanagerplugin.cpp @@ -1,666 +1,680 @@ /*************************************************************************** * This file is part of KDevelop * * Copyright 2010-2012 Milian Wolff * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "abstractfilemanagerplugin.h" #include "filemanagerlistjob.h" #include "projectmodel.h" #include "helper.h" #include #include #include +#ifdef TIME_IMPORT_JOB +#include +#endif #include #include #include #include #include #include #include #include "projectfiltermanager.h" #include "debug.h" #define ifDebug(x) using namespace KDevelop; //BEGIN Helper namespace { /** * Returns the parent folder item for a given item or the project root item if there is no parent. */ ProjectFolderItem* getParentFolder(ProjectBaseItem* item) { if ( item->parent() ) { return static_cast(item->parent()); } else { return item->project()->projectItem(); } } } //END Helper //BEGIN Private class KDevelop::AbstractFileManagerPluginPrivate { public: explicit AbstractFileManagerPluginPrivate(AbstractFileManagerPlugin* qq) : q(qq) { } AbstractFileManagerPlugin* q; /** * The just returned must be started in one way or another for this method * to have any affect. The job will then auto-delete itself upon completion. */ Q_REQUIRED_RESULT KIO::Job* eventuallyReadFolder(ProjectFolderItem* item); void addJobItems(FileManagerListJob* job, ProjectFolderItem* baseItem, const KIO::UDSEntryList& entries); void deleted(const QString &path); void created(const QString &path); void projectClosing(IProject* project); void jobFinished(KJob* job); /// Stops watching the given folder for changes, only useful for local files. void stopWatcher(ProjectFolderItem* folder); /// Continues watching the given folder for changes. void continueWatcher(ProjectFolderItem* folder); /// Common renaming function. bool rename(ProjectBaseItem* item, const Path& newPath); void removeFolder(ProjectFolderItem* folder); QHash m_watchers; QHash > m_projectJobs; QVector m_stoppedFolders; ProjectFilterManager m_filters; }; void AbstractFileManagerPluginPrivate::projectClosing(IProject* project) { if ( m_projectJobs.contains(project) ) { // make sure the import job does not live longer than the project // see also addLotsOfFiles test foreach( FileManagerListJob* job, m_projectJobs[project] ) { qCDebug(FILEMANAGER) << "killing project job:" << job; job->abort(); } m_projectJobs.remove(project); } +#ifdef TIME_IMPORT_JOB + QElapsedTimer timer; + if (m_watchers.contains(project)) { + timer.start(); + } +#endif delete m_watchers.take(project); +#ifdef TIME_IMPORT_JOB + if (timer.isValid()) { + qCDebug(FILEMANAGER) << "Deleting dir watcher took" << timer.elapsed() / 1000.0 << "seconds for project" << project->name(); + } +#endif m_filters.remove(project); } KIO::Job* AbstractFileManagerPluginPrivate::eventuallyReadFolder(ProjectFolderItem* item) { FileManagerListJob* listJob = new FileManagerListJob( item ); m_projectJobs[ item->project() ] << listJob; qCDebug(FILEMANAGER) << "adding job" << listJob << item << item->path() << "for project" << item->project(); q->connect( listJob, &FileManagerListJob::finished, q, [&] (KJob* job) { jobFinished(job); } ); q->connect( listJob, &FileManagerListJob::entries, q, [&] (FileManagerListJob* job, ProjectFolderItem* baseItem, const KIO::UDSEntryList& entries) { addJobItems(job, baseItem, entries); } ); return listJob; } void AbstractFileManagerPluginPrivate::jobFinished(KJob* job) { FileManagerListJob* gmlJob = qobject_cast(job); if (gmlJob) { ifDebug(qCDebug(FILEMANAGER) << job << gmlJob << gmlJob->item();) m_projectJobs[ gmlJob->item()->project() ].removeOne( gmlJob ); } else { // job emitted its finished signal from its destructor // ensure we don't keep a dangling point in our list foreach (auto jobs, m_projectJobs) { if (jobs.removeOne(reinterpret_cast(job))) { break; } } } } void AbstractFileManagerPluginPrivate::addJobItems(FileManagerListJob* job, ProjectFolderItem* baseItem, const KIO::UDSEntryList& entries) { if ( entries.empty() ) { return; } qCDebug(FILEMANAGER) << "reading entries of" << baseItem->path(); // build lists of valid files and folders with paths relative to the project folder Path::List files; Path::List folders; foreach ( const KIO::UDSEntry& entry, entries ) { QString name = entry.stringValue( KIO::UDSEntry::UDS_NAME ); if (name == QLatin1String(".") || name == QLatin1String("..")) { continue; } Path path(baseItem->path(), name); if ( !q->isValid( path, entry.isDir(), baseItem->project() ) ) { continue; } else { if ( entry.isDir() ) { if( entry.isLink() ) { const Path linkedPath = baseItem->path().cd(entry.stringValue( KIO::UDSEntry::UDS_LINK_DEST )); // make sure we don't end in an infinite loop if( linkedPath.isParentOf( baseItem->project()->path() ) || baseItem->project()->path().isParentOf( linkedPath ) || linkedPath == baseItem->project()->path() ) { continue; } } folders << path; } else { files << path; } } } ifDebug(qCDebug(FILEMANAGER) << "valid folders:" << folders;) ifDebug(qCDebug(FILEMANAGER) << "valid files:" << files;) // remove obsolete rows for ( int j = 0; j < baseItem->rowCount(); ++j ) { if ( ProjectFolderItem* f = baseItem->child(j)->folder() ) { // check if this is still a valid folder int index = folders.indexOf( f->path() ); if ( index == -1 ) { // folder got removed or is now invalid removeFolder(f); --j; } else { // this folder already exists in the view folders.remove( index ); // no need to add this item, but we still want to recurse into it job->addSubDir( f ); emit q->reloadedFolderItem( f ); } } else if ( ProjectFileItem* f = baseItem->child(j)->file() ) { // check if this is still a valid file int index = files.indexOf( f->path() ); if ( index == -1 ) { // file got removed or is now invalid ifDebug(qCDebug(FILEMANAGER) << "removing file:" << f << f->path();) baseItem->removeRow( j ); --j; } else { // this file already exists in the view files.remove( index ); emit q->reloadedFileItem( f ); } } } // add new rows foreach ( const Path& path, files ) { ProjectFileItem* file = q->createFileItem( baseItem->project(), path, baseItem ); if (file) { emit q->fileAdded( file ); } } foreach ( const Path& path, folders ) { ProjectFolderItem* folder = q->createFolderItem( baseItem->project(), path, baseItem ); if (folder) { emit q->folderAdded( folder ); job->addSubDir( folder ); } } } void AbstractFileManagerPluginPrivate::created(const QString& path_) { qCDebug(FILEMANAGER) << "created:" << path_; QFileInfo info(path_); ///FIXME: share memory with parent const Path path(path_); const IndexedString indexedPath(path.pathOrUrl()); const IndexedString indexedParent(path.parent().pathOrUrl()); QHashIterator it(m_watchers); while (it.hasNext()) { const auto p = it.next().key(); if ( !p->projectItem()->model() ) { // not yet finished with loading // FIXME: how should this be handled? see unit test continue; } if ( !q->isValid(path, info.isDir(), p) ) { continue; } if ( info.isDir() ) { bool found = false; foreach ( ProjectFolderItem* folder, p->foldersForPath(indexedPath) ) { // exists already in this project, happens e.g. when we restart the dirwatcher // or if we delete and remove folders consecutively https://bugs.kde.org/show_bug.cgi?id=260741 qCDebug(FILEMANAGER) << "force reload of" << path << folder; auto job = eventuallyReadFolder( folder ); job->start(); found = true; } if ( found ) { continue; } } else if (!p->filesForPath(indexedPath).isEmpty()) { // also gets triggered for kate's backup files continue; } foreach ( ProjectFolderItem* parentItem, p->foldersForPath(indexedParent) ) { if ( info.isDir() ) { ProjectFolderItem* folder = q->createFolderItem( p, path, parentItem ); if (folder) { emit q->folderAdded( folder ); auto job = eventuallyReadFolder( folder ); job->start(); } } else { ProjectFileItem* file = q->createFileItem( p, path, parentItem ); if (file) { emit q->fileAdded( file ); } } } } } void AbstractFileManagerPluginPrivate::deleted(const QString& path_) { if ( QFile::exists(path_) ) { // stopDirScan... return; } // ensure that the path is not inside a stopped folder foreach(const QString& folder, m_stoppedFolders) { if (path_.startsWith(folder)) { return; } } qCDebug(FILEMANAGER) << "deleted:" << path_; const Path path(QUrl::fromLocalFile(path_)); const IndexedString indexed(path.pathOrUrl()); QHashIterator it(m_watchers); while (it.hasNext()) { const auto p = it.next().key(); if (path == p->path()) { KMessageBox::error(qApp->activeWindow(), i18n("The base folder of project %1" " got deleted or moved outside of KDevelop.\n" "The project has to be closed.", p->name()), i18n("Project Folder Deleted") ); ICore::self()->projectController()->closeProject(p); continue; } if ( !p->projectItem()->model() ) { // not yet finished with loading // FIXME: how should this be handled? see unit test continue; } foreach ( ProjectFolderItem* item, p->foldersForPath(indexed) ) { removeFolder(item); } foreach ( ProjectFileItem* item, p->filesForPath(indexed) ) { emit q->fileRemoved(item); ifDebug(qCDebug(FILEMANAGER) << "removing file" << item;) item->parent()->removeRow(item->row()); } } } bool AbstractFileManagerPluginPrivate::rename(ProjectBaseItem* item, const Path& newPath) { if ( !q->isValid(newPath, true, item->project()) ) { int cancel = KMessageBox::warningContinueCancel( qApp->activeWindow(), i18n("You tried to rename '%1' to '%2', but the latter is filtered and will be hidden.\n" "Do you want to continue?", item->text(), newPath.lastPathSegment()), QString(), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), QStringLiteral("GenericManagerRenameToFiltered") ); if ( cancel == KMessageBox::Cancel ) { return false; } } foreach ( ProjectFolderItem* parent, item->project()->foldersForPath(IndexedString(newPath.parent().pathOrUrl())) ) { if ( parent->folder() ) { stopWatcher(parent); const Path source = item->path(); bool success = renameUrl( item->project(), source.toUrl(), newPath.toUrl() ); if ( success ) { item->setPath( newPath ); item->parent()->takeRow( item->row() ); parent->appendRow( item ); if (item->file()) { emit q->fileRenamed(source, item->file()); } else { Q_ASSERT(item->folder()); emit q->folderRenamed(source, item->folder()); } } continueWatcher(parent); return success; } } return false; } void AbstractFileManagerPluginPrivate::stopWatcher(ProjectFolderItem* folder) { if ( !folder->path().isLocalFile() ) { return; } Q_ASSERT(m_watchers.contains(folder->project())); const QString path = folder->path().toLocalFile(); m_watchers[folder->project()]->stopDirScan(path); m_stoppedFolders.append(path); } void AbstractFileManagerPluginPrivate::continueWatcher(ProjectFolderItem* folder) { if ( !folder->path().isLocalFile() ) { return; } auto watcher = m_watchers.value(folder->project(), nullptr); Q_ASSERT(watcher); const QString path = folder->path().toLocalFile(); if (!watcher->restartDirScan(path)) { // path wasn't being watched yet - can we be 100% certain of that will never happen? qCWarning(FILEMANAGER) << "Folder" << path << "in project" << folder->project()->name() << "wasn't yet being watched"; watcher->addDir(path); } const int idx = m_stoppedFolders.indexOf(path); if (idx != -1) { m_stoppedFolders.remove(idx); } } bool isChildItem(ProjectBaseItem* parent, ProjectBaseItem* child) { do { if (child == parent) { return true; } child = child->parent(); } while(child); return false; } void AbstractFileManagerPluginPrivate::removeFolder(ProjectFolderItem* folder) { ifDebug(qCDebug(FILEMANAGER) << "removing folder:" << folder << folder->path();) foreach(FileManagerListJob* job, m_projectJobs[folder->project()]) { if (isChildItem(folder, job->item())) { qCDebug(FILEMANAGER) << "killing list job for removed folder" << job << folder->path(); job->abort(); Q_ASSERT(!m_projectJobs.value(folder->project()).contains(job)); } else { job->removeSubDir(folder); } } folder->parent()->removeRow( folder->row() ); } //END Private //BEGIN Plugin AbstractFileManagerPlugin::AbstractFileManagerPlugin( const QString& componentName, QObject *parent, const QVariantList & /*args*/ ) : IProjectFileManager(), IPlugin( componentName, parent ), d(new AbstractFileManagerPluginPrivate(this)) { connect(core()->projectController(), &IProjectController::projectClosing, this, [&] (IProject* project) { d->projectClosing(project); }); } AbstractFileManagerPlugin::~AbstractFileManagerPlugin() = default; IProjectFileManager::Features AbstractFileManagerPlugin::features() const { return Features( Folders | Files ); } QList AbstractFileManagerPlugin::parse( ProjectFolderItem *item ) { // we are async, can't return anything here qCDebug(FILEMANAGER) << "note: parse will always return an empty list"; Q_UNUSED(item); return QList(); } ProjectFolderItem *AbstractFileManagerPlugin::import( IProject *project ) { ProjectFolderItem *projectRoot = createFolderItem( project, project->path(), nullptr ); emit folderAdded( projectRoot ); qCDebug(FILEMANAGER) << "imported new project" << project->name() << "at" << projectRoot->path(); ///TODO: check if this works for remote files when something gets changed through another KDE app if ( project->path().isLocalFile() ) { d->m_watchers[project] = new KDirWatch( project ); connect(d->m_watchers[project], &KDirWatch::created, this, [&] (const QString& path_) { d->created(path_); }); connect(d->m_watchers[project], &KDirWatch::deleted, this, [&] (const QString& path_) { d->deleted(path_); }); d->m_watchers[project]->addDir(project->path().toLocalFile(), KDirWatch::WatchSubDirs | KDirWatch:: WatchFiles ); } d->m_filters.add(project); return projectRoot; } KJob* AbstractFileManagerPlugin::createImportJob(ProjectFolderItem* item) { return d->eventuallyReadFolder(item); } bool AbstractFileManagerPlugin::reload( ProjectFolderItem* item ) { qCDebug(FILEMANAGER) << "reloading item" << item->path(); auto job = d->eventuallyReadFolder( item->folder() ); job->start(); return true; } ProjectFolderItem* AbstractFileManagerPlugin::addFolder( const Path& folder, ProjectFolderItem * parent ) { qCDebug(FILEMANAGER) << "adding folder" << folder << "to" << parent->path(); ProjectFolderItem* created = nullptr; d->stopWatcher(parent); if ( createFolder(folder.toUrl()) ) { created = createFolderItem( parent->project(), folder, parent ); if (created) { emit folderAdded(created); } } d->continueWatcher(parent); return created; } ProjectFileItem* AbstractFileManagerPlugin::addFile( const Path& file, ProjectFolderItem * parent ) { qCDebug(FILEMANAGER) << "adding file" << file << "to" << parent->path(); ProjectFileItem* created = nullptr; d->stopWatcher(parent); if ( createFile(file.toUrl()) ) { created = createFileItem( parent->project(), file, parent ); if (created) { emit fileAdded(created); } } d->continueWatcher(parent); return created; } bool AbstractFileManagerPlugin::renameFolder(ProjectFolderItem* folder, const Path& newPath) { qCDebug(FILEMANAGER) << "trying to rename a folder:" << folder->path() << newPath; return d->rename(folder, newPath); } bool AbstractFileManagerPlugin::renameFile(ProjectFileItem* file, const Path& newPath) { qCDebug(FILEMANAGER) << "trying to rename a file:" << file->path() << newPath; return d->rename(file, newPath); } bool AbstractFileManagerPlugin::removeFilesAndFolders(const QList &items) { bool success = true; foreach(ProjectBaseItem* item, items) { Q_ASSERT(item->folder() || item->file()); ProjectFolderItem* parent = getParentFolder(item); d->stopWatcher(parent); success &= removeUrl(parent->project(), item->path().toUrl(), true); if ( success ) { if (item->file()) { emit fileRemoved(item->file()); } else { Q_ASSERT(item->folder()); emit folderRemoved(item->folder()); } item->parent()->removeRow( item->row() ); } d->continueWatcher(parent); if ( !success ) break; } return success; } bool AbstractFileManagerPlugin::moveFilesAndFolders(const QList< ProjectBaseItem* >& items, ProjectFolderItem* newParent) { bool success = true; foreach(ProjectBaseItem* item, items) { Q_ASSERT(item->folder() || item->file()); ProjectFolderItem* oldParent = getParentFolder(item); d->stopWatcher(oldParent); d->stopWatcher(newParent); const Path oldPath = item->path(); const Path newPath(newParent->path(), item->baseName()); success &= renameUrl(oldParent->project(), oldPath.toUrl(), newPath. toUrl()); if ( success ) { if (item->file()) { emit fileRemoved(item->file()); } else { emit folderRemoved(item->folder()); } oldParent->removeRow( item->row() ); KIO::Job *readJob = d->eventuallyReadFolder(newParent); // reload first level synchronously, deeper levels will run async // this is required for code that expects the new item to exist after // this method finished readJob->exec(); } d->continueWatcher(oldParent); d->continueWatcher(newParent); if ( !success ) break; } return success; } bool AbstractFileManagerPlugin::copyFilesAndFolders(const Path::List& items, ProjectFolderItem* newParent) { bool success = true; foreach(const Path& item, items) { d->stopWatcher(newParent); success &= copyUrl(newParent->project(), item.toUrl(), newParent->path().toUrl()); if ( success ) { KIO::Job *readJob = d->eventuallyReadFolder(newParent); // reload first level synchronously, deeper levels will run async // this is required for code that expects the new item to exist after // this method finished readJob->exec(); } d->continueWatcher(newParent); if ( !success ) break; } return success; } bool AbstractFileManagerPlugin::isValid( const Path& path, const bool isFolder, IProject* project ) const { return d->m_filters.isValid( path, isFolder, project ); } ProjectFileItem* AbstractFileManagerPlugin::createFileItem( IProject* project, const Path& path, ProjectBaseItem* parent ) { return new ProjectFileItem( project, path, parent ); } ProjectFolderItem* AbstractFileManagerPlugin::createFolderItem( IProject* project, const Path& path, ProjectBaseItem* parent ) { return new ProjectFolderItem( project, path, parent ); } KDirWatch* AbstractFileManagerPlugin::projectWatcher( IProject* project ) const { return d->m_watchers.value( project, nullptr ); } //END Plugin #include "moc_abstractfilemanagerplugin.cpp" diff --git a/kdevplatform/shell/documentationcontroller.cpp b/kdevplatform/shell/documentationcontroller.cpp index 8e916486c2..c9320c098f 100644 --- a/kdevplatform/shell/documentationcontroller.cpp +++ b/kdevplatform/shell/documentationcontroller.cpp @@ -1,241 +1,241 @@ /* Copyright 2009 Aleix Pol Gonzalez Copyright 2010 Benjamin Port This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "documentationcontroller.h" #include "debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KDevelop; namespace { /** * Return a "more useful" declaration that documentation providers can look-up * * @code * QPoint point; * ^-- cursor here * @endcode * * In this case, this method returns a Declaration pointer to the *type* * instead of a pointer to the instance, which is more useful when looking for help * * @return A more appropriate Declaration pointer or the given parameter @p decl */ Declaration* usefulDeclaration(Declaration* decl) { if (!decl) return nullptr; // First: Attempt to find the declaration of a definition decl = DUChainUtils::declarationForDefinition(decl); // Convenience feature: Retrieve the type declaration of instances, // it makes no sense to pass the declaration pointer of instances of types if (decl->kind() == Declaration::Instance) { AbstractType::Ptr type = TypeUtils::targetTypeKeepAliases(decl->abstractType(), decl->topContext()); IdentifiedType* idType = dynamic_cast(type.data()); Declaration* idDecl = idType ? idType->declaration(decl->topContext()) : nullptr; if (idDecl) { decl = idDecl; } } return decl; } } class DocumentationViewFactory: public KDevelop::IToolViewFactory { public: DocumentationViewFactory() {} QWidget* create(QWidget *parent = nullptr) override { if (!m_providersModel) { m_providersModel.reset(new ProvidersModel); } return new DocumentationView(parent, m_providersModel.data()); } Qt::DockWidgetArea defaultPosition() override { return Qt::RightDockWidgetArea; } QString id() const override { return QStringLiteral("org.kdevelop.DocumentationView"); } QList contextMenuActions(QWidget* viewWidget) const override { auto documentationViewWidget = qobject_cast(viewWidget); Q_ASSERT(documentationViewWidget); return documentationViewWidget->contextMenuActions(); } private: QScopedPointer m_providersModel; }; DocumentationController::DocumentationController(Core* core) : m_factory(new DocumentationViewFactory) { m_showDocumentation = core->uiController()->activeMainWindow()->actionCollection()->addAction(QStringLiteral("showDocumentation")); m_showDocumentation->setText(i18n("Show Documentation")); m_showDocumentation->setIcon(QIcon::fromTheme(QStringLiteral("documentation"))); connect(m_showDocumentation, &QAction::triggered, this, &DocumentationController::doShowDocumentation); } DocumentationController::~DocumentationController() { } void DocumentationController::initialize() { - if(!documentationProviders().isEmpty() && !(Core::self()->setupFlags() & Core::NoUi)) { + if (!(Core::self()->setupFlags() & Core::NoUi)) { Core::self()->uiController()->addToolView(i18n("Documentation"), m_factory); } } void KDevelop::DocumentationController::doShowDocumentation() { KTextEditor::View* view = ICore::self()->documentController()->activeTextDocumentView(); if(!view) return; KDevelop::DUChainReadLocker lock( DUChain::lock() ); Declaration* decl = usefulDeclaration(DUChainUtils::itemUnderCursor(view->document()->url(), KTextEditor::Cursor(view->cursorPosition())).declaration); auto documentation = documentationForDeclaration(decl); if (documentation) { showDocumentation(documentation); } } KDevelop::ContextMenuExtension KDevelop::DocumentationController::contextMenuExtension(Context* context, QWidget* parent) { Q_UNUSED(parent); ContextMenuExtension menuExt; DeclarationContext* ctx = dynamic_cast(context); if(ctx) { DUChainReadLocker lock(DUChain::lock()); if(!ctx->declaration().data()) return menuExt; auto doc = documentationForDeclaration(ctx->declaration().data()); if (doc) { menuExt.addAction(ContextMenuExtension::ExtensionGroup, m_showDocumentation); } } return menuExt; } IDocumentation::Ptr DocumentationController::documentationForDeclaration(Declaration* decl) { if (!decl) return {}; foreach (IDocumentationProvider* doc, documentationProviders()) { qCDebug(SHELL) << "Documentation provider found:" << doc; auto ret = doc->documentationForDeclaration(decl); qCDebug(SHELL) << "Documentation proposed: " << ret.data(); if (ret) { return ret; } } return {}; } QList< IDocumentationProvider* > DocumentationController::documentationProviders() const { QList plugins=ICore::self()->pluginController()->allPluginsForExtension(QStringLiteral("org.kdevelop.IDocumentationProvider")); QList pluginsProvider=ICore::self()->pluginController()->allPluginsForExtension(QStringLiteral("org.kdevelop.IDocumentationProviderProvider")); QList ret; foreach(IPlugin* p, pluginsProvider) { IDocumentationProviderProvider *docProvider=p->extension(); if (!docProvider) { qCWarning(SHELL) << "plugin" << p << "does not implement ProviderProvider extension, rerun kbuildsycoca5"; continue; } ret.append(docProvider->providers()); } foreach(IPlugin* p, plugins) { IDocumentationProvider *doc=p->extension(); if (!doc) { qCWarning(SHELL) << "plugin" << p << "does not implement Provider extension, rerun kbuildsycoca5"; continue; } ret.append(doc); } return ret; } void KDevelop::DocumentationController::showDocumentation(const IDocumentation::Ptr& doc) { QWidget* w = ICore::self()->uiController()->findToolView(i18n("Documentation"), m_factory, KDevelop::IUiController::CreateAndRaise); if(!w) { qCWarning(SHELL) << "Could not add documentation tool view"; return; } DocumentationView* view = dynamic_cast(w); if( !view ) { qCWarning(SHELL) << "Could not cast tool view" << w << "to DocumentationView class!"; return; } view->showDocumentation(doc); } void DocumentationController::changedDocumentationProviders() { emit providersChanged(); } diff --git a/kdevplatform/shell/tests/kdevnonguiinterfaceplugin.json b/kdevplatform/shell/tests/kdevnonguiinterfaceplugin.json index deedff0c44..e5c60e11bb 100644 --- a/kdevplatform/shell/tests/kdevnonguiinterfaceplugin.json +++ b/kdevplatform/shell/tests/kdevnonguiinterfaceplugin.json @@ -1,36 +1,37 @@ { "KPlugin": { "Description": "This plugin is purely for unit-test", "Description[ca@valencia]": "Este connector és exclusivament per a unitats de proves", "Description[ca]": "Aquest connector és exclusivament per a unitats de proves", "Description[de]": "Die ist eine Modul nur für Unit-Tests", "Description[es]": "Este complemento es exclusivamente para pruebas unitarias", "Description[fi]": "Tämä liitännäinen on puhtaasti yksikkötestausta varten", "Description[gl]": "Este complemento é soamente para probas unitarias.", "Description[nl]": "Deze plugin is alleen voor eenheid-testen", "Description[pl]": "Ta wtyczka służy tylko do testów jednostek", "Description[pt]": "Este 'plugin' serve apenas para testes unitários", "Description[pt_BR]": "Este plugin é apenas para testes unitários", "Description[sk]": "Tento plugin je čisto na unit testy", "Description[sl]": "Ta vstavek je samo za preizkušanje enot", "Description[sv]": "Insticksprogrammet är enbart avsett för enhetstest", "Description[tr]": "Bu eklenti tamamen birim-testleri içindir", "Description[uk]": "Цей додаток призначено лише для тестування модулів", "Description[x-test]": "xxThis plugin is purely for unit-testxx", "Description[zh_CN]": "此插件只用于 unit 测试", "Id": "kdevnonguiinterface", "License": "LGPL", "Name": "KDevNonGuiInterface", "Name[bs]": "KDevNonGUI sučelje", "Name[nds]": "KDevKeenBöversiet", "Name[sv]": "KDevelop icke grafiskt användargränssnitt", "Name[x-test]": "xxKDevNonGuiInterfacexx", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Interfaces": [ "org.kdevelop.ITestNonGuiInterface" ], + "X-KDevelop-Category": "Global", "X-KDevelop-Mode": "NoGUI" } diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index bac6ae7bae..44f7b94295 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -1,150 +1,152 @@ # BEGIN: Analyzers add_subdirectory(cppcheck) -add_subdirectory(heaptrack) +if(UNIX AND NOT (APPLE OR CYGWIN)) + add_subdirectory(heaptrack) +endif() # END: Analyzers # BEGIN: Debuggers if(NOT WIN32) # TODO: Make compile under Windows add_subdirectory(debuggercommon) add_subdirectory(lldb) add_subdirectory(gdb) endif() # END: Debuggers # BEGIN: Documentation find_package(Qt5Help CONFIG) set_package_properties(Qt5Help PROPERTIES PURPOSE "The Help module for the Qt toolkit, needed for the qthelp plugin" URL "https://www.qt.io/" TYPE OPTIONAL) if(Qt5Help_FOUND) ecm_optional_add_subdirectory(qthelp) endif() ecm_optional_add_subdirectory(manpage) # END: Documentation # BEGIN: Formatters add_subdirectory(astyle) add_subdirectory(customscript) # END: Formatters # BEGIN: Languages ecm_optional_add_subdirectory(custom-definesandincludes) ecm_optional_add_subdirectory(qmljs) find_package(Clang 3.8) set(clangSearchHint "") if (NOT CLANG_FOUND) set(clangSearchHint "Please install a package providing libclang. Either pass -DLLVM_ROOT=/path/to/llvm-prefix or install the 'llvm-config' command-line utility for auto-detection.") endif() set_package_properties(Clang PROPERTIES DESCRIPTION "Clang libraries from the LLVM project. ${clangSearchHint}" PURPOSE "Used for KDevelop's C++/C support plugin." TYPE REQUIRED ) if (CLANG_FOUND) if (NOT CLANG_LIBCLANG_LIB) message(FATAL_ERROR "Could not find the Clang C library: libclang") endif() ecm_optional_add_subdirectory(clang) endif() # END: Languages # BEGIN: Project builders add_subdirectory(makebuilder) add_subdirectory(ninjabuilder) ecm_optional_add_subdirectory(cmakebuilder) if (KDevelop-PG-Qt_FOUND) ecm_optional_add_subdirectory(qmakebuilder) endif() # END: Project builders # BEGIN: Project managers ecm_optional_add_subdirectory(cmake) ecm_optional_add_subdirectory(custommake) ecm_optional_add_subdirectory(custom-buildsystem) if (KDevelop-PG-Qt_FOUND) ecm_optional_add_subdirectory(qmakemanager) endif() ecm_optional_add_subdirectory(genericprojectmanager) # END: Project managers # BEGIN: Runtimes add_subdirectory(android) if (UNIX) add_subdirectory(docker) add_subdirectory(flatpak) endif() # END: Runtimes # BEGIN: VCS ecm_optional_add_subdirectory(bazaar) ecm_optional_add_subdirectory(cvs) ecm_optional_add_subdirectory(git) ecm_optional_add_subdirectory(perforce) find_package(SubversionLibrary) set_package_properties(SubversionLibrary PROPERTIES PURPOSE "Support for Subversion integration" URL "http://subversion.tigris.org" TYPE OPTIONAL) if(SubversionLibrary_FOUND) ecm_optional_add_subdirectory(subversion) endif() add_subdirectory(vcschangesview) # END: VCS # BEGIN: Others add_subdirectory(appwizard) add_subdirectory(codeutils) add_subdirectory(contextbrowser) add_subdirectory(documentswitcher) add_subdirectory(documentview) add_subdirectory(execute) add_subdirectory(executescript) add_subdirectory(externalscript) add_subdirectory(filemanager) add_subdirectory(filetemplates) add_subdirectory(grepview) add_subdirectory(openwith) add_subdirectory(outlineview) add_subdirectory(patchreview) add_subdirectory(problemreporter) add_subdirectory(projectfilter) add_subdirectory(projectmanagerview) add_subdirectory(quickopen) add_subdirectory(sourceformatter) add_subdirectory(standardoutputview) add_subdirectory(switchtobuddy) add_subdirectory(testview) ecm_optional_add_subdirectory(classbrowser) ecm_optional_add_subdirectory(executeplasmoid) ecm_optional_add_subdirectory(ghprovider) ecm_optional_add_subdirectory(kdeprovider) ecm_optional_add_subdirectory(konsole) if (Qt5QuickWidgets_FOUND) add_subdirectory(welcomepage) endif() find_package(OktetaKastenControllers 0.3.1) set_package_properties(OktetaKastenControllers PROPERTIES PURPOSE "Required for building Okteta KDevelop plugin." URL "http://kde.org/" TYPE OPTIONAL) if (OktetaKastenControllers_FOUND) find_package( KastenControllers ) set_package_properties(KastenControllers PROPERTIES PURPOSE "Required for building Okteta KDevelop plugin." URL "http://kde.org/" TYPE OPTIONAL) endif() if (OktetaKastenControllers_FOUND AND KastenControllers_FOUND) add_subdirectory(okteta) endif() # END: Others diff --git a/plugins/astyle/kdevastyle.json b/plugins/astyle/kdevastyle.json index 958f4f94c0..9f02ba04ba 100644 --- a/plugins/astyle/kdevastyle.json +++ b/plugins/astyle/kdevastyle.json @@ -1,64 +1,65 @@ { "KPlugin": { "Authors": [ { "Name": "Cedric Pasteur, Matthias Hölzer-Klüpfel", "Name[x-test]": "xxCedric Pasteur, Matthias Hölzer-Klüpfelxx" } ], "Category": "Utilities", "Description": "A plugin for formatting of sourcecode according to a specified set of rules", "Description[bs]": "Dodatak za formatiranje izvornog koda prema određenom nizu pravila", "Description[ca@valencia]": "Un connector per a formatar el codi font d'acord a un conjunt especificat de regles", "Description[ca]": "Un connector per a formatar el codi font d'acord a un conjunt especificat de regles", "Description[de]": "Modul zum Formatieren von Quelltext nach bestimmten Vorschriften", "Description[es]": "Un complemento para formatear código fuente según el conjunto de reglas indicado", "Description[fi]": "Liitännäinen lähdekoodin muotoilemiseen määritellyn sääntöjoukon mukaan", "Description[fr]": "Un module pour mettre en forme le code source d'après un ensemble de règles spécifié", "Description[gl]": "Complemento para formatar código fonte segundo un conxunto de regras indicado.", "Description[it]": "Un'estensione per la formattazione del codice sorgente secondo uno specifico insieme di regole", "Description[nl]": "Een plugin om broncode te formatteren volgens een speciale set regels", "Description[pl]": "Wtyczka do formatowania kodu źródłowego zgodnie z podanymi regułami", "Description[pt]": "Um 'plugin' para formatar código-fonte de acordo com um dado conjunto de regras", "Description[pt_BR]": "Um plugin para formatar código-fonte de acordo com um conjunto de regras", "Description[sk]": "Modul pre formátovanie zdrojového kódu podľa špecifických pravidiel", "Description[sl]": "Vstavek za oblikovanje izvorne kode po določenih pravilih", "Description[sv]": "Ett insticksprogram för formatering av källkod enligt en angiven uppsättning regler", "Description[tr]": "Belli kurallara göre kaynak kodları biçimlendirmeye yarayan bir eklenti", "Description[uk]": "Додаток для форматування коду у відповідності до вказаного набору правил", "Description[x-test]": "xxA plugin for formatting of sourcecode according to a specified set of rulesxx", "Description[zh_CN]": "一个根据指定规则设定格式化源代码的插件", "Icon": "kdevelop", "Id": "kdevastyle", "License": "LGPL", "Name": "AStyle Formatter Backend", "Name[bs]": "AStyle formater pozadine", "Name[ca@valencia]": "Dorsal del formatador AStyle", "Name[ca]": "Dorsal del formatador AStyle", "Name[de]": "AStyle-Formatierer", "Name[es]": "Motor de formateo AStyle", "Name[fi]": "AStyle-muotoilijataustaohjelma", "Name[fr]": "Moteur de mise en forme AStyle", "Name[gl]": "Infraestrutura de formatación de AStyle", "Name[it]": "Backend formattatore AStyle", "Name[nl]": "Backend voor AStyle formatteerprogramma", "Name[pl]": "Silnik formatowania AStyle", "Name[pt]": "Infra-Estrutura de Formatação do Astyle", "Name[pt_BR]": "Formatador AStyle", "Name[sk]": "Backend formátovača AStyle", "Name[sl]": "Zaledje oblikovalnika AStyle", "Name[sv]": "Astyle-formateringsgränssnitt", "Name[tr]": "Astyle Biçimlendirici Arka Ucu", "Name[uk]": "Сервер форматування AStyle", "Name[x-test]": "xxAStyle Formatter Backendxx", "Name[zh_CN]": "AStyle 格式化器后端", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Interfaces": [ "org.kdevelop.ISourceFormatter" ], "X-KDevelop-LoadMode": "AlwaysOn", + "X-KDevelop-Category": "Global", "X-KDevelop-Mode": "NoGUI" } diff --git a/plugins/bazaar/kdevbazaar.json b/plugins/bazaar/kdevbazaar.json index 51ec2e09c0..34a157d148 100644 --- a/plugins/bazaar/kdevbazaar.json +++ b/plugins/bazaar/kdevbazaar.json @@ -1,70 +1,71 @@ { "Encoding": "UTF-8", "KPlugin": { "Authors": [ { "Email": "d82ks8djf82msd83hf8sc02lqb5gh5@gmail.com", "Name": "Maciej Poleski", "Name[x-test]": "xxMaciej Poleskixx" } ], "Category": "Version Control", "Description": "This plugin integrates Bazaar to KDevelop", "Description[ar]": "تكامل هذه الملحقة مطوّرك ببازار", "Description[ca@valencia]": "Este connector integra el Bazaar al KDevelop", "Description[ca]": "Aquest connector integra el Bazaar al KDevelop", "Description[cs]": "Tento modul integruje podporu pro Bazaar v KDevelop", "Description[de]": "Dieses Modul integriert Bazaar in KDevelop", "Description[es]": "Este complemento integra Bazaar en KDevelop", "Description[fr]": "Ce module intègre Bazaar dans KDevelop", "Description[it]": "Questa estensione integra Bazaar in KDevelop", "Description[nl]": "Deze plugin integreert Bazaar in KDevelop", "Description[pl]": "Ta wtyczka udostępnia Bazaar w KDevelopie", "Description[pt]": "Este 'plugin' integra o Bazaar no KDevelop", "Description[pt_BR]": "Esta extensão integra o Bazaar ao KDevelop", "Description[sk]": "Tento plugin integruje Bazaar do KDevelop.", "Description[sv]": "Insticksprogrammet integrerar Bazaar i KDevelop", "Description[uk]": "За допомогою цього додатка можна інтегрувати Bazaar до KDevelop", "Description[x-test]": "xxThis plugin integrates Bazaar to KDevelopxx", "Description[zh_CN]": "此插件将 Bazaar 整合到 KDevelop", "Icon": "bazaar", "Id": "kdevbazaar", "License": "GPL", "Name": "Bazaar Support", "Name[ar]": "دعم بازار", "Name[bg]": "Поддръжка на Bazaar", "Name[ca@valencia]": "Implementació de Bazaar", "Name[ca]": "Implementació de Bazaar", "Name[cs]": "Podpora Bazaar", "Name[da]": "Bazaar-understøttelse", "Name[de]": "Bazaar-Unterstützung", "Name[el]": "Υποστήριξη Bazaar", "Name[es]": "Implementación de Bazaar", "Name[et]": "Bazaari toetus", "Name[fr]": "Prise en charge de Bazaar", "Name[ga]": "Tacaíocht Bazaar", "Name[it]": "Supporto per Bazaar", "Name[kk]": "Bazaar қолдауы", "Name[nb]": "Bazaar-støtte", "Name[nds]": "Bazaar-Ünnerstütten", "Name[nl]": "Ondersteuning van Bazaar", "Name[pl]": "Obsługa Bazaar", "Name[pt]": "Suporte para o Bazaar", "Name[sk]": "Podpora Bazaar", "Name[sv]": "Bazaar-stöd", "Name[ug]": "Bazaar قوللىشى", "Name[uk]": "Підтримка Bazaar", "Name[x-test]": "xxBazaar Supportxx", "Name[zh_CN]": "Bazaar 支持", "Name[zh_TW]": "Bazaar 支援", "ServiceTypes": [ "KDevelop/Plugin" ], "Version": "1.0" }, "X-KDevelop-Interfaces": [ "org.kdevelop.IBasicVersionControl", "org.kdevelop.IDistributedVersionControl" ], + "X-KDevelop-Category": "Global", "X-KDevelop-Mode": "GUI" } diff --git a/plugins/clang/kdevclangsupport.json b/plugins/clang/kdevclangsupport.json index 5e97c379ef..a1e2645ddc 100644 --- a/plugins/clang/kdevclangsupport.json +++ b/plugins/clang/kdevclangsupport.json @@ -1,75 +1,76 @@ { "KPlugin": { "Description": "C/C++ Language Support (Clang-based)", "Description[ca@valencia]": "Implementació del llenguatge C/C++ (basat en Clang)", "Description[ca]": "Implementació del llenguatge C/C++ (basat en Clang)", "Description[cs]": "Podpora jazyka C/C++ (založeno na Clang)", "Description[de]": "Sprachunterstützung für C/C++ (Clang-basiert)", "Description[es]": "Implementación del lenguaje C/C++ (basado en Clang)", "Description[fi]": "C/C++-kielituki (Clang-pohjainen)", "Description[fr]": "Prise en charge du langage C/C++ (utilisant Clang)", "Description[gl]": "Compatibilidade con C e C++ (baseada en Clang)", "Description[it]": "Supporto al linguaggio C/C++ (basato su Clang)", "Description[nl]": "Ondersteuning voor de talen C/C++ (Clang-based)", "Description[pl]": "Obsługa języków C/C++ (na podstawie Clang)", "Description[pt]": "Suporte para a Linguagem C/C++ (baseado no Clang)", "Description[pt_BR]": "Suporte à linguagem C/C++ (baseado no Clang)", "Description[sk]": "Podpora jazyka C/C++ (založená na triedach)", "Description[sl]": "Podpora jeziku C/C++ (temelječa na Clang)", "Description[sv]": "C/C++ språkstöd (Clang-baserat)", "Description[tr]": "C/C++ Dil Desteği (Clang-tabanlı)", "Description[uk]": "Підтримка мови C/C++ на основі Clang", "Description[x-test]": "xxC/C++ Language Support (Clang-based)xx", "Description[zh_CN]": "C/C++ 语言支持 (基于 Clang)", "Icon": "text-x-c++src", "Id": "kdevclangsupport", "Name": "C++ Support", "Name[bs]": "C++ Podrška", "Name[ca@valencia]": "Implementació del C++", "Name[ca]": "Implementació del C++", "Name[cs]": "Podpora C++", "Name[de]": "Unterstützung für C++", "Name[es]": "Implementación de C++", "Name[fi]": "C++-tuki", "Name[fr]": "Prise en charge du C++", "Name[gl]": "Compatibilidade con C++", "Name[hu]": "C++ támogatás", "Name[it]": "Supporto per C++", "Name[nl]": "Ondersteuning voor C++", "Name[pl]": "Obsługa C++", "Name[pt]": "Suporte para o C++", "Name[pt_BR]": "Suporte à C++", "Name[ru]": "Поддержка C++", "Name[sk]": "Podpora C++", "Name[sl]": "Podpora za C++", "Name[sv]": "C++ stöd", "Name[tr]": "C++ Desteği", "Name[uk]": "Підтримка C++", "Name[x-test]": "xxC++ Supportxx", "Name[zh_CN]": "C++ 支持", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Interfaces": [ "ILanguageSupport" ], "X-KDevelop-Languages": [ "C", "C++", "OpenCL C", "CUDA C", "Objective-C" ], "X-KDevelop-LoadMode": "AlwaysOn", + "X-KDevelop-Category": "Global", "X-KDevelop-Mode": "NoGUI", "X-KDevelop-SupportedMimeTypes": [ "text/x-chdr", "text/x-c++hdr", "text/x-csrc", "text/x-c++src", "text/x-opencl-src", "text/vnd.nvidia.cuda.csrc", "text/x-objcsrc" ] } diff --git a/plugins/cmake/cmakeserverimportjob.cpp b/plugins/cmake/cmakeserverimportjob.cpp index f93ded0965..c40ba10d79 100644 --- a/plugins/cmake/cmakeserverimportjob.cpp +++ b/plugins/cmake/cmakeserverimportjob.cpp @@ -1,205 +1,205 @@ /* KDevelop CMake Support * * Copyright 2017 Aleix Pol Gonzalez * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #include "cmakeserverimportjob.h" #include "cmakeutils.h" #include "cmakeserver.h" #include #include #include #include #include #include #include #include #include #include "debug.h" static QString unescape(const QStringRef& input) { QString output; output.reserve(input.length()); bool isEscaped = false; for (auto it = input.data(), end = it + input.length(); it != end; ++it) { QChar c = *it; if (!isEscaped && c == '\\') { isEscaped = true; } else { output.append(c); isEscaped = false; } } return output; } static QHash processDefines(const QString &compileFlags, const QJsonArray &defines) { QHash ret; const auto& defineRx = MakeFileResolver::defineRegularExpression(); auto it = defineRx.globalMatch(compileFlags); while (it.hasNext()) { const auto match = it.next(); QString value; if (match.lastCapturedIndex() > 1) { value = unescape(match.capturedRef(match.lastCapturedIndex())); } ret[match.captured(1)] = value; } for (const QJsonValue& defineValue: defines) { const QString define = defineValue.toString(); const int eqIdx = define.indexOf(QLatin1Char('=')); if (eqIdx<0) { ret[define] = QString(); } else { ret[define.left(eqIdx)] = define.mid(eqIdx+1); } } return ret; } CMakeTarget::Type typeToEnum(const QJsonObject& target) { static const QHash s_types = { {QStringLiteral("EXECUTABLE"), CMakeTarget::Executable}, {QStringLiteral("STATIC_LIBRARY"), CMakeTarget::Library}, {QStringLiteral("MODULE_LIBRARY"), CMakeTarget::Library}, {QStringLiteral("SHARED_LIBRARY"), CMakeTarget::Library}, {QStringLiteral("OBJECT_LIBRARY"), CMakeTarget::Library}, {QStringLiteral("INTERFACE_LIBRARY"), CMakeTarget::Library} }; const auto value = target.value(QLatin1String("type")).toString(); return s_types.value(value, CMakeTarget::Custom); } void CMakeServerImportJob::processCodeModel(const QJsonObject &response, CMakeProjectData &data) { const auto configs = response.value(QStringLiteral("configurations")).toArray(); qCDebug(CMAKE) << "process response" << response; data.targets.clear(); data.compilationData.files.clear(); const auto rt = KDevelop::ICore::self()->runtimeController()->currentRuntime(); for (const auto &config: configs) { const auto projects = config.toObject().value(QStringLiteral("projects")).toArray(); for (const auto &project: projects) { const auto targets = project.toObject().value(QStringLiteral("targets")).toArray(); for (const auto &targetObject: targets) { const auto target = targetObject.toObject(); const KDevelop::Path targetDir = rt->pathInHost(KDevelop::Path(target.value(QStringLiteral("sourceDirectory")).toString())); data.targets[targetDir] += CMakeTarget { typeToEnum(target), target.value(QStringLiteral("name")).toString(), kTransform(target[QLatin1String("artifacts")].toArray(), [](const QJsonValue& val) { return KDevelop::Path(val.toString()); }) }; const auto fileGroups = target.value(QStringLiteral("fileGroups")).toArray(); for (const auto &fileGroupValue: fileGroups) { const auto fileGroup = fileGroupValue.toObject(); CMakeFile file; file.includes = kTransform(fileGroup.value(QStringLiteral("includePath")).toArray(), [](const QJsonValue& val) { return KDevelop::Path(val.toObject().value(QStringLiteral("path")).toString()); }); file.compileFlags = fileGroup.value(QStringLiteral("compileFlags")).toString(); file.defines = processDefines(file.compileFlags, fileGroup.value(QStringLiteral("defines")).toArray()); const auto sourcesArray = fileGroup.value(QStringLiteral("sources")).toArray(); const KDevelop::Path::List sources = kTransform(sourcesArray, [targetDir](const QJsonValue& val) { return KDevelop::Path(targetDir, val.toString()); }); for (const auto& source: sources) { // NOTE: we use the canonical file path to prevent issues with symlinks in the path // leading to lookup failures const auto localFile = rt->pathInHost(source); const auto canonicalFile = QFileInfo(source.toLocalFile()).canonicalFilePath(); const auto sourcePath = localFile.toLocalFile() == canonicalFile ? localFile : KDevelop::Path(canonicalFile); data.compilationData.files[sourcePath] = file; } qCDebug(CMAKE) << "registering..." << sources << file; } } } } } CMakeServerImportJob::CMakeServerImportJob(KDevelop::IProject* project, CMakeServer* server, QObject* parent) : KJob(parent) , m_server(server) , m_project(project) { connect(m_server.data(), &CMakeServer::disconnected, this, [this]() { setError(UnexpectedDisconnect); emitResult(); }); } void CMakeServerImportJob::start() { if (m_server->isServerAvailable()) doStart(); else connect(m_server.data(), &CMakeServer::connected, this, &CMakeServerImportJob::doStart); } void CMakeServerImportJob::doStart() { connect(m_server.data(), &CMakeServer::response, this, &CMakeServerImportJob::processResponse); m_server->handshake(m_project->path(), CMake::currentBuildDir(m_project)); } void CMakeServerImportJob::processResponse(const QJsonObject& response) { const auto responseType = response.value(QStringLiteral("type")); if (responseType == QLatin1String("reply")) { const auto inReplyTo = response.value(QStringLiteral("inReplyTo")); qCDebug(CMAKE) << "replying..." << inReplyTo; if (inReplyTo == QLatin1String("handshake")) { m_server->configure({}); } else if (inReplyTo == QLatin1String("configure")) { m_server->compute(); } else if (inReplyTo == QLatin1String("compute")) { m_server->codemodel(); } else if(inReplyTo == QLatin1String("codemodel")) { processCodeModel(response, m_data); m_data.m_testSuites = CMake::importTestSuites(CMake::currentBuildDir(m_project)); m_data.m_server = m_server; emitResult(); } else { - qCWarning(CMAKE) << "unhandled reply" << response; + qCDebug(CMAKE) << "unhandled reply" << response; } } else if(responseType == QLatin1String("error")) { setError(ErrorResponse); setErrorText(response.value(QStringLiteral("errorMessage")).toString()); qCWarning(CMAKE) << "error!!" << response; emitResult(); } else if (responseType == QLatin1String("progress")) { int progress = response.value(QStringLiteral("progressCurrent")).toInt(); int total = response.value(QStringLiteral("progressMaximum")).toInt(); if (progress >= 0 && total > 0) { setPercent(100.0 * progress / total); } } else if (responseType == QLatin1String("message") || responseType == QLatin1String("hello")) { // Known, but not used for anything currently. } else { - qCWarning(CMAKE) << "unhandled message" << response; + qCDebug(CMAKE) << "unhandled message" << response; } } diff --git a/plugins/cmake/cmakeutils.cpp b/plugins/cmake/cmakeutils.cpp index 5b9bd5fabe..9d6b6318d3 100644 --- a/plugins/cmake/cmakeutils.cpp +++ b/plugins/cmake/cmakeutils.cpp @@ -1,731 +1,727 @@ /* KDevelop CMake Support * * Copyright 2009 Andreas Pakulat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #include "cmakeutils.h" #include "cmakeprojectdata.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "icmakedocumentation.h" #include "cmakebuilddirchooser.h" #include "settings/cmakecachemodel.h" #include "debug.h" #include "cmakebuilderconfig.h" #include #include "parser/cmakelistsparser.h" using namespace KDevelop; namespace Config { namespace Old { static const QString currentBuildDirKey = QStringLiteral("CurrentBuildDir"); static const QString oldcmakeExecutableKey = QStringLiteral("CMake Binary"); // Todo: Remove at some point static const QString currentBuildTypeKey = QStringLiteral("CurrentBuildType"); static const QString currentInstallDirKey = QStringLiteral("CurrentInstallDir"); static const QString currentEnvironmentKey = QStringLiteral("CurrentEnvironment"); static const QString currentExtraArgumentsKey = QStringLiteral("Extra Arguments"); static const QString currentCMakeExecutableKey = QStringLiteral("Current CMake Binary"); static const QString projectRootRelativeKey = QStringLiteral("ProjectRootRelative"); static const QString projectBuildDirs = QStringLiteral("BuildDirs"); } static const QString buildDirIndexKey_ = QStringLiteral("Current Build Directory Index"); static const QString buildDirOverrideIndexKey = QStringLiteral("Temporary Build Directory Index"); static const QString buildDirCountKey = QStringLiteral("Build Directory Count"); //the used builddir will change for every runtime static QString buildDirIndexKey() { const QString currentRuntime = ICore::self()->runtimeController()->currentRuntime()->name(); return buildDirIndexKey_ + '-' + currentRuntime; } namespace Specific { static const QString buildDirPathKey = QStringLiteral("Build Directory Path"); // TODO: migrate to more generic & consistent key term "CMake Executable" // Support the old "CMake Binary" key too for backwards compatibility during // a reasonable transition period. Both keys are saved at least until 5.2.0 // is released. Import support for the old key will need to remain for a // considably longer period, ideally. static const QString cmakeBinaryKey = QStringLiteral("CMake Binary"); static const QString cmakeExecutableKey = QStringLiteral("CMake Executable"); static const QString cmakeBuildTypeKey = QStringLiteral("Build Type"); static const QString cmakeInstallDirKey = QStringLiteral("Install Directory"); static const QString cmakeEnvironmentKey = QStringLiteral("Environment Profile"); static const QString cmakeArgumentsKey = QStringLiteral("Extra Arguments"); static const QString buildDirRuntime = QStringLiteral("Runtime"); } static const QString groupNameBuildDir = QStringLiteral("CMake Build Directory %1"); static const QString groupName = QStringLiteral("CMake"); } // namespace Config namespace { KConfigGroup baseGroup( KDevelop::IProject* project ) { if (!project) return KConfigGroup(); return project->projectConfiguration()->group( Config::groupName ); } KConfigGroup buildDirGroup( KDevelop::IProject* project, int buildDirIndex ) { return baseGroup(project).group( Config::groupNameBuildDir.arg(buildDirIndex) ); } bool buildDirGroupExists( KDevelop::IProject* project, int buildDirIndex ) { return baseGroup(project).hasGroup( Config::groupNameBuildDir.arg(buildDirIndex) ); } QString readBuildDirParameter( KDevelop::IProject* project, const QString& key, const QString& aDefault, int buildDirectory ) { const int buildDirIndex = buildDirectory<0 ? CMake::currentBuildDirIndex(project) : buildDirectory; if (buildDirIndex >= 0) return buildDirGroup( project, buildDirIndex ).readEntry( key, aDefault ); else return aDefault; } void writeBuildDirParameter( KDevelop::IProject* project, const QString& key, const QString& value ) { int buildDirIndex = CMake::currentBuildDirIndex(project); if (buildDirIndex >= 0) { KConfigGroup buildDirGrp = buildDirGroup( project, buildDirIndex ); buildDirGrp.writeEntry( key, value ); } else { qCWarning(CMAKE) << "cannot write key" << key << "(" << value << ")" << "when no builddir is set!"; } } void writeProjectBaseParameter( KDevelop::IProject* project, const QString& key, const QString& value ) { KConfigGroup baseGrp = baseGroup(project); baseGrp.writeEntry( key, value ); } void setBuildDirRuntime( KDevelop::IProject* project, const QString& name) { writeBuildDirParameter(project, Config::Specific::buildDirRuntime, name); } QString buildDirRuntime( KDevelop::IProject* project, int builddir) { return readBuildDirParameter(project, Config::Specific::buildDirRuntime, QString(), builddir); } } // namespace namespace CMake { KDevelop::Path::List resolveSystemDirs(KDevelop::IProject* project, const QStringList& dirs) { const KDevelop::Path buildDir(CMake::currentBuildDir(project)); const KDevelop::Path installDir(CMake::currentInstallDir(project)); KDevelop::Path::List newList; newList.reserve(dirs.size()); foreach(const QString& s, dirs) { KDevelop::Path dir; if(s.startsWith(QLatin1String("#[bin_dir]"))) { dir = KDevelop::Path(buildDir, s); } else if(s.startsWith(QLatin1String("#[install_dir]"))) { dir = KDevelop::Path(installDir, s); } else { dir = KDevelop::Path(s); } // qCDebug(CMAKE) << "resolved" << s << "to" << d; if (!newList.contains(dir)) { newList.append(dir); } } return newList; } ///NOTE: when you change this, update @c defaultConfigure in cmakemanagertest.cpp bool checkForNeedingConfigure( KDevelop::IProject* project ) { const QString currentRuntime = ICore::self()->runtimeController()->currentRuntime()->name(); const KDevelop::Path builddir = currentBuildDir(project); const bool isValid = (buildDirRuntime(project, -1) == currentRuntime || buildDirRuntime(project, -1).isEmpty()) && builddir.isValid(); if( !isValid ) { CMakeBuildDirChooser bd; bd.setProject( project ); const auto builddirs = CMake::allBuildDirs(project); bd.setAlreadyUsed( builddirs ); bd.setShowAvailableBuildDirs(!builddirs.isEmpty()); bd.setCMakeExecutable(currentCMakeExecutable(project)); if( !bd.exec() ) { return false; } if (bd.reuseBuilddir()) { CMake::setCurrentBuildDirIndex( project, bd.alreadyUsedIndex() ); } else { QString newbuilddir = bd.buildFolder().toLocalFile(); int addedBuildDirIndex = buildDirCount( project ); // old count is the new index // Initialize the kconfig items with the values from the dialog, this ensures the settings // end up in the config file once the changes are saved qCDebug(CMAKE) << "adding to cmake config: new builddir index" << addedBuildDirIndex; qCDebug(CMAKE) << "adding to cmake config: builddir path " << bd.buildFolder(); qCDebug(CMAKE) << "adding to cmake config: installdir " << bd.installPrefix(); qCDebug(CMAKE) << "adding to cmake config: extra args" << bd.extraArguments(); qCDebug(CMAKE) << "adding to cmake config: build type " << bd.buildType(); qCDebug(CMAKE) << "adding to cmake config: cmake executable " << bd.cmakeExecutable(); qCDebug(CMAKE) << "adding to cmake config: environment "; CMake::setBuildDirCount( project, addedBuildDirIndex + 1 ); CMake::setCurrentBuildDirIndex( project, addedBuildDirIndex ); CMake::setCurrentBuildDir( project, bd.buildFolder() ); CMake::setCurrentInstallDir( project, bd.installPrefix() ); CMake::setCurrentExtraArguments( project, bd.extraArguments() ); CMake::setCurrentBuildType( project, bd.buildType() ); CMake::setCurrentCMakeExecutable(project, bd.cmakeExecutable()); CMake::setCurrentEnvironment( project, QString() ); } setBuildDirRuntime( project, currentRuntime ); return true; } else if( !QFile::exists( KDevelop::Path(builddir, QStringLiteral("CMakeCache.txt")).toLocalFile() ) || //TODO: maybe we could use the builder for that? !(QFile::exists( KDevelop::Path(builddir, QStringLiteral("Makefile")).toLocalFile() ) || QFile::exists( KDevelop::Path(builddir, QStringLiteral("build.ninja")).toLocalFile() ) ) ) { // User entered information already, but cmake hasn't actually been run yet. setBuildDirRuntime( project, currentRuntime ); return true; } setBuildDirRuntime( project, currentRuntime ); return false; } QHash enumerateTargets(const KDevelop::Path& targetsFilePath, const QString& sourceDir, const KDevelop::Path &buildDir) { const QString buildPath = buildDir.toLocalFile(); QHash targets; QFile targetsFile(targetsFilePath.toLocalFile()); if (!targetsFile.open(QIODevice::ReadOnly)) { qCDebug(CMAKE) << "Couldn't find the Targets file in" << targetsFile.fileName(); } QTextStream targetsFileStream(&targetsFile); const QRegularExpression rx(QStringLiteral("^(.*)/CMakeFiles/(.*).dir$")); while (!targetsFileStream.atEnd()) { const QString line = targetsFileStream.readLine(); auto match = rx.match(line); if (!match.isValid()) qCDebug(CMAKE) << "invalid match for" << line; const QString sourcePath = match.captured(1).replace(buildPath, sourceDir); targets[KDevelop::Path(sourcePath)].append(match.captured(2)); } return targets; } KDevelop::Path projectRoot(KDevelop::IProject* project) { if (!project) { return {}; } return project->path().cd(CMake::projectRootRelative(project)); } KDevelop::Path currentBuildDir( KDevelop::IProject* project, int builddir ) { return KDevelop::Path(readBuildDirParameter( project, Config::Specific::buildDirPathKey, QString(), builddir )); } KDevelop::Path commandsFile(KDevelop::IProject* project) { auto currentBuildDir = CMake::currentBuildDir(project); if (currentBuildDir.isEmpty()) { return {}; } return KDevelop::Path(currentBuildDir, QStringLiteral("compile_commands.json")); } KDevelop::Path targetDirectoriesFile(KDevelop::IProject* project) { auto currentBuildDir = CMake::currentBuildDir(project); if (currentBuildDir.isEmpty()) { return {}; } return KDevelop::Path(currentBuildDir, QStringLiteral("CMakeFiles/TargetDirectories.txt")); } QString currentBuildType( KDevelop::IProject* project, int builddir ) { return readBuildDirParameter( project, Config::Specific::cmakeBuildTypeKey, QStringLiteral("Release"), builddir ); } QString findExecutable() { auto cmake = QStandardPaths::findExecutable(QStringLiteral("cmake")); #ifdef Q_OS_WIN if (cmake.isEmpty()) cmake = QStandardPaths::findExecutable("cmake",{ "C:\\Program Files (x86)\\CMake\\bin", "C:\\Program Files\\CMake\\bin", "C:\\Program Files (x86)\\CMake 2.8\\bin", "C:\\Program Files\\CMake 2.8\\bin"}); #endif return cmake; } KDevelop::Path currentCMakeExecutable(KDevelop::IProject* project, int builddir) { const auto defaultCMakeExecutable = CMakeBuilderSettings::self()->cmakeExecutable().toLocalFile(); if (project) { // check for "CMake Executable" but for now also "CMake Binary", falling back to the default. auto projectCMakeExecutable = readBuildDirParameter( project, Config::Specific::cmakeExecutableKey, readBuildDirParameter( project, Config::Specific::cmakeBinaryKey, defaultCMakeExecutable, builddir), builddir ); if (projectCMakeExecutable != defaultCMakeExecutable) { QFileInfo info(projectCMakeExecutable); if (!info.isExecutable()) { projectCMakeExecutable = defaultCMakeExecutable; } } return KDevelop::Path(projectCMakeExecutable); } return KDevelop::Path(defaultCMakeExecutable); } KDevelop::Path currentInstallDir( KDevelop::IProject* project, int builddir ) { const QString defaultInstallDir = #ifdef Q_OS_WIN QStringLiteral("C:\\Program Files"); #else QStringLiteral("/usr/local"); #endif return KDevelop::Path(readBuildDirParameter( project, Config::Specific::cmakeInstallDirKey, defaultInstallDir, builddir )); } QString projectRootRelative( KDevelop::IProject* project ) { return baseGroup(project).readEntry( Config::Old::projectRootRelativeKey, "." ); } bool hasProjectRootRelative(KDevelop::IProject* project) { return baseGroup(project).hasKey( Config::Old::projectRootRelativeKey ); } QString currentExtraArguments( KDevelop::IProject* project, int builddir ) { return readBuildDirParameter( project, Config::Specific::cmakeArgumentsKey, QString(), builddir ); } void setCurrentInstallDir( KDevelop::IProject* project, const KDevelop::Path& path ) { writeBuildDirParameter( project, Config::Specific::cmakeInstallDirKey, path.toLocalFile() ); } void setCurrentBuildType( KDevelop::IProject* project, const QString& type ) { writeBuildDirParameter( project, Config::Specific::cmakeBuildTypeKey, type ); } void setCurrentCMakeExecutable(KDevelop::IProject* project, const KDevelop::Path& path) { // maintain compatibility with older versions for now writeBuildDirParameter(project, Config::Specific::cmakeBinaryKey, path.toLocalFile()); writeBuildDirParameter(project, Config::Specific::cmakeExecutableKey, path.toLocalFile()); } void setCurrentBuildDir( KDevelop::IProject* project, const KDevelop::Path& path ) { writeBuildDirParameter( project, Config::Specific::buildDirPathKey, path.toLocalFile() ); } void setProjectRootRelative( KDevelop::IProject* project, const QString& relative) { writeProjectBaseParameter( project, Config::Old::projectRootRelativeKey, relative ); } void setCurrentExtraArguments( KDevelop::IProject* project, const QString& string) { writeBuildDirParameter( project, Config::Specific::cmakeArgumentsKey, string ); } QString currentEnvironment(KDevelop::IProject* project, int builddir) { return readBuildDirParameter( project, Config::Specific::cmakeEnvironmentKey, QString(), builddir ); } int currentBuildDirIndex( KDevelop::IProject* project ) { KConfigGroup baseGrp = baseGroup(project); if ( baseGrp.hasKey( Config::buildDirOverrideIndexKey ) ) return baseGrp.readEntry( Config::buildDirOverrideIndexKey, 0 ); else if (baseGrp.hasKey(Config::buildDirIndexKey())) return baseGrp.readEntry( Config::buildDirIndexKey(), 0 ); else return baseGrp.readEntry( Config::buildDirIndexKey_, 0 ); // backwards compatibility } void setCurrentBuildDirIndex( KDevelop::IProject* project, int buildDirIndex ) { writeProjectBaseParameter( project, Config::buildDirIndexKey(), QString::number (buildDirIndex) ); } void setCurrentEnvironment( KDevelop::IProject* project, const QString& environment ) { writeBuildDirParameter( project, Config::Specific::cmakeEnvironmentKey, environment ); } void initBuildDirConfig( KDevelop::IProject* project ) { int buildDirIndex = currentBuildDirIndex( project ); if (buildDirCount(project) <= buildDirIndex ) setBuildDirCount( project, buildDirIndex + 1 ); } int buildDirCount( KDevelop::IProject* project ) { return baseGroup(project).readEntry( Config::buildDirCountKey, 0 ); } void setBuildDirCount( KDevelop::IProject* project, int count ) { writeProjectBaseParameter( project, Config::buildDirCountKey, QString::number(count) ); } void removeBuildDirConfig( KDevelop::IProject* project ) { int buildDirIndex = currentBuildDirIndex( project ); if ( !buildDirGroupExists( project, buildDirIndex ) ) { qCWarning(CMAKE) << "build directory config" << buildDirIndex << "to be removed but does not exist"; return; } int bdCount = buildDirCount(project); setBuildDirCount( project, bdCount - 1 ); removeOverrideBuildDirIndex( project ); setCurrentBuildDirIndex( project, -1 ); // move (rename) the upper config groups to keep the numbering // if there's nothing to move, just delete the group physically if (buildDirIndex + 1 == bdCount) buildDirGroup( project, buildDirIndex ).deleteGroup(); else for (int i = buildDirIndex + 1; i < bdCount; ++i) { KConfigGroup src = buildDirGroup( project, i ); KConfigGroup dest = buildDirGroup( project, i - 1 ); dest.deleteGroup(); src.copyTo(&dest); src.deleteGroup(); } } QHash readCacheValues(const KDevelop::Path& cmakeCachePath, QSet variables) { QHash ret; QFile file(cmakeCachePath.toLocalFile()); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { qCWarning(CMAKE) << "couldn't open CMakeCache.txt" << cmakeCachePath; return ret; } QTextStream in(&file); while (!in.atEnd() && !variables.isEmpty()) { QString line = in.readLine().trimmed(); if(!line.isEmpty() && line[0].isLetter()) { CacheLine c; c.readLine(line); if(!c.isCorrect()) continue; if (variables.remove(c.name())) { ret[c.name()] = c.value(); } } } return ret; } void updateConfig( KDevelop::IProject* project, int buildDirIndex) { if (buildDirIndex < 0) return; KConfigGroup buildDirGrp = buildDirGroup( project, buildDirIndex ); const KDevelop::Path builddir(buildDirGrp.readEntry( Config::Specific::buildDirPathKey, QString() )); const KDevelop::Path cacheFilePath( builddir, QStringLiteral("CMakeCache.txt")); const QMap keys = { { QStringLiteral("CMAKE_COMMAND"), Config::Specific::cmakeExecutableKey }, { QStringLiteral("CMAKE_INSTALL_PREFIX"), Config::Specific::cmakeInstallDirKey }, { QStringLiteral("CMAKE_BUILD_TYPE"), Config::Specific::cmakeBuildTypeKey } }; const QHash cacheValues = readCacheValues(cacheFilePath, keys.keys().toSet()); for(auto it = cacheValues.constBegin(), itEnd = cacheValues.constEnd(); it!=itEnd; ++it) { const QString key = keys.value(it.key()); Q_ASSERT(!key.isEmpty()); // Use cache only when the config value is not set. Without this check we will always // overwrite values provided by the user in config dialog. if (buildDirGrp.readEntry(key).isEmpty() && !it.value().isEmpty()) { buildDirGrp.writeEntry( key, it.value() ); } } } void attemptMigrate( KDevelop::IProject* project ) { if ( !baseGroup(project).hasKey( Config::Old::projectBuildDirs ) ) { qCDebug(CMAKE) << "CMake settings migration: already done, exiting"; return; } KConfigGroup baseGrp = baseGroup(project); KDevelop::Path buildDir( baseGrp.readEntry( Config::Old::currentBuildDirKey, QString() ) ); int buildDirIndex = -1; const QStringList existingBuildDirs = baseGrp.readEntry( Config::Old::projectBuildDirs, QStringList() ); { // also, find current build directory in this list (we need an index, not path) QString currentBuildDirCanonicalPath = QDir( buildDir.toLocalFile() ).canonicalPath(); for( int i = 0; i < existingBuildDirs.count(); ++i ) { const QString& nextBuildDir = existingBuildDirs.at(i); if( QDir(nextBuildDir).canonicalPath() == currentBuildDirCanonicalPath ) { buildDirIndex = i; } } } int buildDirsCount = existingBuildDirs.count(); qCDebug(CMAKE) << "CMake settings migration: existing build directories" << existingBuildDirs; qCDebug(CMAKE) << "CMake settings migration: build directory count" << buildDirsCount; qCDebug(CMAKE) << "CMake settings migration: current build directory" << buildDir << "(index" << buildDirIndex << ")"; baseGrp.writeEntry( Config::buildDirCountKey, buildDirsCount ); baseGrp.writeEntry( Config::buildDirIndexKey(), buildDirIndex ); for (int i = 0; i < buildDirsCount; ++i) { qCDebug(CMAKE) << "CMake settings migration: writing group" << i << ": path" << existingBuildDirs.at(i); KConfigGroup buildDirGrp = buildDirGroup( project, i ); buildDirGrp.writeEntry( Config::Specific::buildDirPathKey, existingBuildDirs.at(i) ); } baseGrp.deleteEntry( Config::Old::currentBuildDirKey ); baseGrp.deleteEntry( Config::Old::currentCMakeExecutableKey ); baseGrp.deleteEntry( Config::Old::currentBuildTypeKey ); baseGrp.deleteEntry( Config::Old::currentInstallDirKey ); baseGrp.deleteEntry( Config::Old::currentEnvironmentKey ); baseGrp.deleteEntry( Config::Old::currentExtraArgumentsKey ); baseGrp.deleteEntry( Config::Old::projectBuildDirs ); } void setOverrideBuildDirIndex( KDevelop::IProject* project, int overrideBuildDirIndex ) { writeProjectBaseParameter( project, Config::buildDirOverrideIndexKey, QString::number(overrideBuildDirIndex) ); } void removeOverrideBuildDirIndex( KDevelop::IProject* project, bool writeToMainIndex ) { KConfigGroup baseGrp = baseGroup(project); if( !baseGrp.hasKey(Config::buildDirOverrideIndexKey) ) return; if( writeToMainIndex ) baseGrp.writeEntry( Config::buildDirIndexKey(), baseGrp.readEntry(Config::buildDirOverrideIndexKey) ); baseGrp.deleteEntry(Config::buildDirOverrideIndexKey); } ICMakeDocumentation* cmakeDocumentation() { return KDevelop::ICore::self()->pluginController()->extensionForPlugin(QStringLiteral("org.kdevelop.ICMakeDocumentation")); } QStringList allBuildDirs(KDevelop::IProject* project) { QStringList result; int bdCount = buildDirCount(project); for (int i = 0; i < bdCount; ++i) result += buildDirGroup( project, i ).readEntry( Config::Specific::buildDirPathKey ); return result; } QString executeProcess(const QString& execName, const QStringList& args) { Q_ASSERT(!execName.isEmpty()); qCDebug(CMAKE) << "Executing:" << execName << "::" << args; QProcess p; QTemporaryDir tmp(QStringLiteral("kdevcmakemanager")); p.setWorkingDirectory( tmp.path() ); p.start(execName, args, QIODevice::ReadOnly); if(!p.waitForFinished()) { qCDebug(CMAKE) << "failed to execute:" << execName << args << p.exitStatus() << p.readAllStandardError(); } QByteArray b = p.readAllStandardOutput(); QString t; t.prepend(b.trimmed()); return t; } QStringList supportedGenerators() { QStringList generatorNames; bool hasNinja = ICore::self() && ICore::self()->pluginController()->pluginForExtension(QStringLiteral("org.kdevelop.IProjectBuilder"), QStringLiteral("KDevNinjaBuilder")); if (hasNinja) generatorNames << QStringLiteral("Ninja"); #ifdef Q_OS_WIN // Visual Studio solution is the standard generator under windows, but we don't want to use // the VS IDE, so we need nmake makefiles generatorNames << QStringLiteral("NMake Makefiles") << QStringLiteral("MinGW Makefiles"); #endif generatorNames << QStringLiteral("Unix Makefiles"); return generatorNames; } QString defaultGenerator() { const QStringList generatorNames = supportedGenerators(); QString defGen = generatorNames.value(CMakeBuilderSettings::self()->generator()); if (defGen.isEmpty()) { qCWarning(CMAKE) << "Couldn't find builder with index " << CMakeBuilderSettings::self()->generator() << ", defaulting to 0"; CMakeBuilderSettings::self()->setGenerator(0); defGen = generatorNames.at(0); } return defGen; } QVector importTestSuites(const Path &buildDir) { const auto contents = CMakeListsParser::readCMakeFile(buildDir.toLocalFile() + "/CTestTestfile.cmake"); QVector tests; for (const auto& entry: contents) { if (entry.name == QLatin1String("add_test")) { auto args = entry.arguments; Test test; test.name = args.takeFirst().value; test.executable = args.takeFirst().value; test.arguments = kTransform(args, [](const CMakeFunctionArgument& arg) { return arg.value; }); tests += test; } else if (entry.name == QLatin1String("subdirs")) { tests += importTestSuites(Path(buildDir, entry.arguments.constFirst().value)); } else if (entry.name == QLatin1String("set_tests_properties")) { if(entry.arguments.count() < 4 || entry.arguments.count() % 2) { qCWarning(CMAKE) << "found set_tests_properties() with unexpected number of arguments:" << entry.arguments.count(); continue; } if (tests.isEmpty() || entry.arguments.constFirst().value != tests.constLast().name) { qCWarning(CMAKE) << "found set_tests_properties(" << entry.arguments.constFirst().value << " ...), but expected test " << tests.constLast().name; continue; } if (entry.arguments[1].value != QLatin1String("PROPERTIES")) { qCWarning(CMAKE) << "found set_tests_properties(" << entry.arguments.constFirst().value << entry.arguments.at(1).value << "...), but expected PROPERTIES as second argument"; continue; } Test &test = tests.last(); - qWarning() << "ARGS"; - for (auto arg : entry.arguments) { - qWarning() << arg.value; - } for (int i = 2; i < entry.arguments.count(); i += 2) test.properties[entry.arguments[i].value] = entry.arguments[i + 1].value; } } return tests; } } diff --git a/plugins/cmake/kdevcmakedocumentation.json b/plugins/cmake/kdevcmakedocumentation.json index 76b55664fd..986471d15f 100644 --- a/plugins/cmake/kdevcmakedocumentation.json +++ b/plugins/cmake/kdevcmakedocumentation.json @@ -1,71 +1,72 @@ { "KPlugin": { "Authors": [ { "Email": "aleixpol@kde.org", "Name": "Aleix Pol", "Name[ru]": "Aleix Pol Gonzalez", "Name[x-test]": "xxAleix Polxx" } ], "Category": "Documentation", "Description": "Allows KDevelop to provide CMake documentation", "Description[bs]": "Dopišta KDevelopu da obezbjedi CMake dokumentaciju", "Description[ca@valencia]": "Permet al KDevelop proporcionar documentació de CMake", "Description[ca]": "Permet al KDevelop proporcionar documentació de CMake", "Description[cs]": "Umožňuje KDevelop poskytovat dokumentaci CMake", "Description[de]": "Ermöglicht das Bereitstellen von Dokumentation für CMake", "Description[es]": "Permite que KDevelop proporcione documentación de CMake", "Description[fi]": "Sallii KDevelop-ohjelman tarjota CMake-dokumentaation", "Description[fr]": "Permet à KDevelop de fournir la documentation CMake", "Description[gl]": "Permítelle a KDevelop fornecer documentación de CMake.", "Description[it]": "Permette a KDevelop di fornire la documentazione a CMake", "Description[nl]": "Staat KDevelop toe om CMake-documentatie te leveren", "Description[pl]": "Pozwala na dostęp z KDevelopa do dokumentacji CMake", "Description[pt]": "Permite ao KDevelop fornecer documentação sobre o CMake", "Description[pt_BR]": "Permite ao KDevelop fornecer documentação do CMake", "Description[sk]": "Povolí KDevelopu poskytnúť dokumentáciu CMake", "Description[sl]": "Omogoča, da KDevelop prikaže dokumentacijo za CMake", "Description[sv]": "Låter KDevelop tillhandahålla CMake-dokumentation", "Description[tr]": "KDevelop uygulamasına CMake belgelendirmesi sağlar", "Description[uk]": "Надає змогу KDevelop показувати документацію CMake", "Description[x-test]": "xxAllows KDevelop to provide CMake documentationxx", "Description[zh_CN]": "允许 KDevelop 提供 CMake 文档", "Icon": "cmake", "Id": "KDevCMakeDocumentation", "License": "GPL", "Name": "CMake Documentation", "Name[bs]": "CMake Dokumentacija", "Name[ca@valencia]": "Documentació del CMake", "Name[ca]": "Documentació del CMake", "Name[cs]": "Dokumentace CMake", "Name[de]": "CMake-Dokumentation", "Name[es]": "Documentación de CMake", "Name[fi]": "CMake-dokumentaatio", "Name[fr]": "Documentation CMake", "Name[gl]": "Documentación de CMake", "Name[it]": "Documentazione CMake", "Name[nl]": "CMake-documentatie", "Name[pl]": "Dokumentacja CMake", "Name[pt]": "Documentação do CMake", "Name[pt_BR]": "Documentação do CMake", "Name[sk]": "Dokumentácia CMake", "Name[sl]": "Dokumentacija za CMake", "Name[sv]": "CMake-dokumentation", "Name[tr]": "CMake Belgelendirmesi", "Name[uk]": "Документація з CMake", "Name[x-test]": "xxCMake Documentationxx", "Name[zh_CN]": "CMake 文档", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Interfaces": [ "org.kdevelop.IDocumentationProvider", "org.kdevelop.ICMakeDocumentation" ], + "X-KDevelop-Category": "Global", "X-KDevelop-Mode": "GUI", "X-KDevelop-SupportedMimeTypes": [ "text/x-cmake" ] } diff --git a/plugins/custom-definesandincludes/kdevdefinesandincludesmanager.json b/plugins/custom-definesandincludes/kdevdefinesandincludesmanager.json index ffd409a335..13ac3e37d8 100644 --- a/plugins/custom-definesandincludes/kdevdefinesandincludesmanager.json +++ b/plugins/custom-definesandincludes/kdevdefinesandincludesmanager.json @@ -1,56 +1,57 @@ { "KPlugin": { "Category": "Project Management", "Description": "Configure which macros and include directories/files will be added to the parser during project parsing.", "Description[ar]": "اضبط أيّ الماكروهات والملفّات/الأدلّة المضمّنة ستُضاف إلى المحلّل أثناء تحليل المشروع.", "Description[ca@valencia]": "Configura quines macros i directoris/fitxers s'afegiran a l'analitzador durant l'anàlisi del projecte.", "Description[ca]": "Configura quines macros i directoris/fitxers s'afegiran a l'analitzador durant l'anàlisi del projecte.", "Description[de]": "Einstellung der Makros und Include-Ordner/-Dateien, die für den Parser während des Einlesens des Projekts hinzugefügt werden.", "Description[es]": "Configurar las macros y los directorios/archivos a incluir que se añadirán al analizador sintáctico durante el análisis de los proyectos.", "Description[fi]": "Määritä, mitkä makrot ja include-hakemistot/tiedostot lisätään jäsentimelle projektin jäsentämisen aikana.", "Description[fr]": "Configurer quelles macros et dossiers / fichiers à inclure seront ajoutés pour l'analyseur pendant l'analyse du projet.", "Description[gl]": "Configure que macros e ficheiros ou directorios de inclusión se engaden ao analizador do proxecto.", "Description[it]": "Configura quali macro e cartelle/file di inclusione saranno aggiunti all'analizzatore durante l'analisi del progetto.", "Description[nl]": "Stel in welke macro's en ingevoegde mappen/bestanden toegevoegd zullen worden aan de parser bij het ontleden van het project.", "Description[pl]": "Ustawia jakie makra oraz katalogi/pliki dołączane zostaną dodane do programu przetwarzającego przy przetwarzaniu projektu.", "Description[pt]": "Configura as macros e ficheiros/pastas de inclusão a adicionar ao processador, durante o processamento do projecto.", "Description[pt_BR]": "Configura quais macros e arquivos/pastas incluídos serão adicionados ao processador durante o processamento do projeto.", "Description[sk]": "Nastaviť, ktoré makrá a zahrnuté adresáre/súbory sa pridajú do spracovača počas spracovania projektu.", "Description[sl]": "Nastavi, kateri makri in mape/datoteke z vključitvami bodo dodane razčlenjevalniku med razčlenjevanjem projekta.", "Description[sv]": "Anpassa vilka makron och filer eller kataloger att inkludera som läggs till i tolken under projekttolkning.", "Description[tr]": "Hangi makroların ve dahil etme dizinlerinin/dosyaların ayrıştırma sürecinde ayrıştırıcıya ekleneceğini yapılandırın.", "Description[uk]": "Налаштувати макроси і включені каталоги або файли, які буде додано під час обробки проекту.", "Description[x-test]": "xxConfigure which macros and include directories/files will be added to the parser during project parsing.xx", "Description[zh_CN]": "配置在解析项目时将添加哪些宏和包含目录/文件", "Icon": "kdevelop", "Id": "KDevDefinesAndIncludesManager", "Name": "Custom Defines And Includes Manager", "Name[ca@valencia]": "Gestor per personalitzar les definicions i inclusions", "Name[ca]": "Gestor per personalitzar les definicions i inclusions", "Name[de]": "Verwaltung benutzerdefinierter Definitionen und Includes", "Name[es]": "Gestor de definiciones e inclusiones personalizadas", "Name[fi]": "Mukautettujen definejen ja includejen hallinta", "Name[fr]": "Gestionnaire de définitions et inclusions personnalisées", "Name[gl]": "Xestor de definicións e inclusións personalizadas", "Name[it]": "Gestore delle definizioni e inclusioni personalizzate", "Name[nl]": "Beheerder van zelf gedefinieerd en invoegingen", "Name[pl]": "Zarządzanie własnymi definicjami i plikami dołączanymi", "Name[pt]": "Gestor de Definições e Inclusões Personalizadas", "Name[pt_BR]": "Gerenciador de definições e inclusões personalizadas", "Name[sk]": "Správca vlastných definícií a zahrnutí", "Name[sl]": "Upravljalnik določitev in vključitev po meri", "Name[sv]": "Hantering av egna definitioner och inkluderingar", "Name[tr]": "Özel Tanımlama ve İçerme Yöneticisi", "Name[uk]": "Керування нетиповими визначеннями і включеннями", "Name[x-test]": "xxCustom Defines And Includes Managerxx", "Name[zh_CN]": "自定义定义和包含管理器", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Interfaces": [ "org.kdevelop.IDefinesAndIncludesManager" ], "X-KDevelop-LoadMode": "AlwaysOn", + "X-KDevelop-Category": "Global", "X-KDevelop-Mode": "NoGUI" } diff --git a/plugins/custommake/kdevcustommakemanager.json b/plugins/custommake/kdevcustommakemanager.json index 91665dbf3c..351934926a 100644 --- a/plugins/custommake/kdevcustommakemanager.json +++ b/plugins/custommake/kdevcustommakemanager.json @@ -1,72 +1,73 @@ { "KPlugin": { "Category": "Project Management", "Description": "Imports and edits custom make projects", "Description[bs]": "Uvozi i uređuje korisnički prilagođene projekte", "Description[ca@valencia]": "Importa i edita projectes personalitzats de Make", "Description[ca]": "Importa i edita projectes personalitzats de Make", "Description[cs]": "Importuje a upravuje vlastní projekty make", "Description[de]": "Import und Bearbeitung benutzerdefinierten Make-Projekten", "Description[es]": "Importa y edita proyectos Make personalizados", "Description[fi]": "Tuo ja muokkaa räätälöityjä make-projekteja", "Description[fr]": "Importe et édite des projet make personnalisés", "Description[gl]": "Importa e edita proxectos que usen un sistema make personalizado.", "Description[it]": "Importa e modifica i progetti personalizzati di Make", "Description[nl]": "Importeert en bewerkt eigen make-projecten", "Description[pl]": "Import i modyfikacja projektów używających własnych skryptów wykorzystujących make", "Description[pt]": "Importa e edita projectos do 'make' personalizados", "Description[pt_BR]": "Importa e edita projetos make personalizados", "Description[sk]": "Importuje a upravuje vlastné projekty make", "Description[sl]": "Uvozi in ureja projekte po meri temelječe na Make", "Description[sv]": "Importerar och redigerar egna projekt som använder Make", "Description[tr]": "Özel make projelerini içeriye aktarır ve düzenler", "Description[uk]": "Імпортує і змінює проекти нетипових make", "Description[x-test]": "xxImports and edits custom make projectsxx", "Description[zh_CN]": "导入并编辑定制 make 工程", "Icon": "kdevelop", "Id": "KDevCustomMakeManager", "Name": "Custom Makefile Project Manager", "Name[bs]": "Korisnički Makefile menadžer projekta", "Name[ca@valencia]": "Gestor de projecte personalitzat Makefile", "Name[ca]": "Gestor de projecte personalitzat Makefile", "Name[de]": "Projektverwaltung mit eigenem Makefile", "Name[es]": "Gestor de proyectos Makefile personalizados", "Name[fi]": "Räätälöity Makefile-projektinhallinta", "Name[fr]": "Gestionnaire de projet à Makefile personnalisé", "Name[gl]": "Xestor de proxectos con Makefile personalizado", "Name[it]": "Gestore progetto Makefile personalizzato", "Name[nl]": "Aangepaste projectbeheerder voor makefile", "Name[pl]": "Zarządzanie własnym systemem budowania", "Name[pt]": "Gestor de Projectos de Makefiles Personalizadas", "Name[pt_BR]": "Gerenciador de Projeto personalizado do Makefile", "Name[sk]": "Správca projektu vlastného makefile", "Name[sl]": "Upravljalnik projektov z datoteko Makefile po meri", "Name[sv]": "Projekthantering av egna Make-filer", "Name[tr]": "Özel Makefile Proje Yöneticisi", "Name[uk]": "Керування проектами з власним Makefile", "Name[x-test]": "xxCustom Makefile Project Managerxx", "Name[zh_CN]": "自定义 Makefile 工程管理器", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-FileManager": "CustomMake", "X-KDevelop-IRequired": [ "org.kdevelop.IMakeBuilder", "org.kdevelop.IDefinesAndIncludesManager" ], "X-KDevelop-Interfaces": [ "org.kdevelop.IBuildSystemManager", "org.kdevelop.IProjectFileManager" ], + "X-KDevelop-Category": "Project", "X-KDevelop-Mode": "NoGUI", "X-KDevelop-ProjectFilesFilter": [ "GNUmakefile", "Makefile", "makefile", "GNUmakefile.*", "Makefile.*", "makefile.*" ], "X-KDevelop-ProjectFilesFilterDescription": "Makefiles" } diff --git a/plugins/customscript/kdevcustomscript.json b/plugins/customscript/kdevcustomscript.json index 8b1d21ce8c..88d24b4ec1 100644 --- a/plugins/customscript/kdevcustomscript.json +++ b/plugins/customscript/kdevcustomscript.json @@ -1,64 +1,65 @@ { "KPlugin": { "Authors": [ { "Name": "David Nolden", "Name[x-test]": "xxDavid Noldenxx" } ], "Category": "Utilities", "Description": "A plugin for formatting C files using custom scripts", "Description[bs]": "Dodatak za formatiranje C datoteka koristeći vlastite skripte", "Description[ca@valencia]": "Un connector per a formatar fitxers C emprant scripts personalitzats", "Description[ca]": "Un connector per a formatar fitxers C emprant scripts personalitzats", "Description[de]": "Ein Modul zum Formatieren von C-Dateien mit Hilfe von eigenen Skripten", "Description[es]": "Un complemento para formatear archivos de C usando scripts personalizados", "Description[fi]": "Liitännäinen C-tiedostojen muotoiluun käyttäen mukautettuja skriptejä", "Description[fr]": "Un module pour mettre en forme des fichiers C d'après des scripts personnalisés", "Description[gl]": "Complemento para formatar os ficheiros en C empregando scripts personalizados.", "Description[it]": "Un'estensione per la formattazione di file in C che usa script personalizzati", "Description[nl]": "Een plugin voor het formatteren van C-bestanden met eigen scripts", "Description[pl]": "Wtyczka do formatowania plików C wykorzystująca własne skrypty", "Description[pt]": "Um 'plugin' para formatar ficheiros em C com programas personalizados", "Description[pt_BR]": "Um plugin para formatar arquivos em C usando scripts personalizados", "Description[sk]": "Modul pre formátovanie súborov C pomocou vlastných skriptov", "Description[sl]": "Vstavek za oblikovanje izvorne kode C s pomočjo skript po meri", "Description[sv]": "Ett insticksprogram för att formatera C-filer med egna skript", "Description[tr]": "C dosyalarını özel betikler kullanarak biçimlendirmek için bir eklenti", "Description[uk]": "Додаток для форматування файлів мовою C за допомогою нетипових скриптів", "Description[x-test]": "xxA plugin for formatting C files using custom scriptsxx", "Description[zh_CN]": "使用自定义脚本格式化 C 文件的插件", "Icon": "kdevelop", "Id": "kdevcustomscript", "License": "LGPL", "Name": "Custom Script Formatter Backend", "Name[bs]": "Pozadinski program za formatiranje vlastitih skripti", "Name[ca@valencia]": "Dorsal de l'script formatador personalitzat", "Name[ca]": "Dorsal de l'script formatador personalitzat", "Name[de]": "Backend für einen Formatierer für eigene Skripte", "Name[es]": "Motor de formateo de scripts personalizados", "Name[fi]": "Mukautetun skriptin muotoilijataustaohjelma", "Name[fr]": "Moteur de mise en forme de script personnalisé", "Name[gl]": "Infraestrutura de formatación de scripts personalizados", "Name[it]": "Backend formattatore script personalizzato", "Name[nl]": "Backend voor formatteerprogramma met eigen scripts", "Name[pl]": "Silnik formatowania przy użyciu własnych skryptów", "Name[pt]": "Infra-Estrutura de Formatação com Programas Personalizados", "Name[pt_BR]": "Infraestrutura de formatação de scripts personalizados", "Name[sk]": "Backend formátovača vlastného skriptu", "Name[sl]": "Zaledje oblikovalnika skript po meri", "Name[sv]": "Formateringsgränssnitt för eget skript", "Name[tr]": "Özel Betik Biçimlendirici Arka Ucu", "Name[uk]": "Сервер скриптів нетипового форматування", "Name[x-test]": "xxCustom Script Formatter Backendxx", "Name[zh_CN]": "自定义脚本格式化器后端", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Interfaces": [ "org.kdevelop.ISourceFormatter" ], "X-KDevelop-LoadMode": "AlwaysOn", + "X-KDevelop-Category": "Global", "X-KDevelop-Mode": "NoGUI" } diff --git a/plugins/documentation/CMakeLists.txt b/plugins/documentation/CMakeLists.txt deleted file mode 100644 index 627a275164..0000000000 --- a/plugins/documentation/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -find_package(Qt5Help CONFIG) - -set_package_properties(Qt5Help PROPERTIES - PURPOSE "The Help module for the Qt toolkit, needed for the qthelp plugin" - URL "https://www.qt.io/" - TYPE OPTIONAL) -if(Qt5Help_FOUND) - ecm_optional_add_subdirectory(qthelp) -endif() - -ecm_optional_add_subdirectory(manpage) diff --git a/plugins/ghprovider/kdevghprovider.json b/plugins/ghprovider/kdevghprovider.json index 4f58bc5344..ad0509c1f0 100644 --- a/plugins/ghprovider/kdevghprovider.json +++ b/plugins/ghprovider/kdevghprovider.json @@ -1,56 +1,57 @@ { "KPlugin": { "Authors": [ { "Name": "Miquel Sabaté", "Name[x-test]": "xxMiquel Sabatéxx" } ], "Category": "Utilities", "Description": "This plugin helps to obtain projects from GitHub", "Description[ca@valencia]": "Este connector ajuda a obtindre els projectes des de GitHub", "Description[ca]": "Aquest connector ajuda a obtenir els projectes des de GitHub", "Description[de]": "Dieses Modul hilft dabei, GitHub-Projekte zu beziehen", "Description[es]": "Este complemento le ayuda a obtener proyectos de GitHub", "Description[fr]": "Ce module aide à obtenir des projets depuis GitHub", "Description[gl]": "Este complemento permite obter proxectos de GitHub.", "Description[it]": "Questa estensione consente di ottenere progetti da GitHub", "Description[nl]": "Deze plug-in helpt om projecten uit Github te verkrijgen", "Description[pl]": "Wtyczka ta pomaga przy pobieraniu projektów z GitHuba", "Description[pt]": "Este 'plugin' ajuda a obter projectos do GitHub", "Description[sk]": "Tento modul pomáha získavať projekty z GitHub", "Description[sl]": "Ta vstavek pomaga pri pridobivanju projektov iz GitHub-a", "Description[sv]": "Insticksprogrammet hjälper till att erhålla projekt från Github", "Description[tr]": "Bu eklenti Github'tan projeleri almanıza yardımcı olur", "Description[uk]": "За допомогою цього додатка можна отримувати проекти з GitHub", "Description[x-test]": "xxThis plugin helps to obtain projects from GitHubxx", "Icon": "kdevgh", "Id": "kdevghprovider", "License": "GPL", "Name": "GitHub Provider", "Name[ca@valencia]": "Proveïdor de GitHub", "Name[ca]": "Proveïdor de GitHub", "Name[cs]": "Poskytovatel GitHubu", "Name[de]": "GitHub-Provider", "Name[es]": "Proveedor de GitHub", "Name[fr]": "Fournisseur GitHub", "Name[gl]": "Fornecedor de GitHub", "Name[it]": "Fornitore GitHub", "Name[nl]": "Github-leverancier", "Name[pl]": "Dostawca GitHub", "Name[pt]": "Fornecedor do GitHub", "Name[sk]": "Poskytovateľ GitHub", "Name[sl]": "Ponudnik za GitHub", "Name[sv]": "Github-leverantör", "Name[tr]": "GitHub Sağlayıcı", "Name[uk]": "Надавач даних GitHub", "Name[x-test]": "xxGitHub Providerxx", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Interfaces": [ "org.kdevelop.IProjectProvider" ], + "X-KDevelop-Category": "Global", "X-KDevelop-Mode": "GUI" } diff --git a/plugins/git/kdevgit.json b/plugins/git/kdevgit.json index 5533f0ab9e..004f0d474f 100644 --- a/plugins/git/kdevgit.json +++ b/plugins/git/kdevgit.json @@ -1,69 +1,70 @@ { "KPlugin": { "Authors": [ { "Email": "powerfox@kde.ru", "Name": "Evgeniy Ivanov", "Name[x-test]": "xxEvgeniy Ivanovxx" } ], "Category": "Version Control", "Description": "This plugin integrates Git to KDevelop", "Description[ar]": "تكامل هذه الملحقة Git بِمطوّرك", "Description[ca@valencia]": "Este connector integra el Git al KDevelop", "Description[ca]": "Aquest connector integra el Git al KDevelop", "Description[cs]": "Tento modul integruje podporu pro Git v KDevelop", "Description[de]": "Dieses Modul integriert Git in KDevelop.", "Description[es]": "Este complemento integra Git en KDevelop", "Description[fr]": "Ce module externe intègre la gestion de Git dans KDevelop", "Description[it]": "Questa estensione integra Git in KDevelop", "Description[nl]": "Deze plug-in integreert Git in KDevelop", "Description[pl]": "Wtyczka ta integruje Git z KDevelop", "Description[pt]": "Este 'plugin' integra o Git no KDevelop", "Description[pt_BR]": "Este plugin integra o Git ao KDevelop", "Description[sk]": "Tento plugin integruje GIT do KDevelop.", "Description[sv]": "Insticksprogrammet integrerar Git i KDevelop", "Description[uk]": "Цей додаток інтегрує Git із KDevelop", "Description[x-test]": "xxThis plugin integrates Git to KDevelopxx", "Description[zh_CN]": "此插件对 KDevelop 整合 Git", "Icon": "git", "Id": "kdevgit", "License": "GPL", "Name": "Git Support", "Name[ar]": "دعم Git", "Name[bg]": "Поддръжка на Git", "Name[ca@valencia]": "Implementació de Git", "Name[ca]": "Implementació de Git", "Name[cs]": "Podpora Git", "Name[da]": "Git-understøttelse", "Name[de]": "Git-Unterstützung", "Name[el]": "Υποστήριξη Git", "Name[es]": "Implementación de Git", "Name[et]": "Giti toetus", "Name[fr]": "Prise en charge de Git", "Name[ga]": "Tacaíocht Git", "Name[it]": "Supporto per Git", "Name[kk]": "Git қолдауы", "Name[nds]": "Git-Ünnerstütten", "Name[nl]": "Git-ondersteuning", "Name[pa]": "Git ਸਹਿਯੋਗ", "Name[pl]": "Obsługa Git", "Name[pt]": "Suporte para o Git", "Name[sk]": "Podpora GIT", "Name[sv]": "Git-stöd", "Name[ug]": "Git قوللىشى", "Name[uk]": "Підтримка Git", "Name[x-test]": "xxGit Supportxx", "Name[zh_CN]": "Git 支持", "Name[zh_TW]": "Git 支援", "ServiceTypes": [ "KDevelop/Plugin" ], "Version": "0.9" }, "X-KDevelop-Interfaces": [ "org.kdevelop.IBasicVersionControl", "org.kdevelop.IDistributedVersionControl" ], + "X-KDevelop-Category": "Global", "X-KDevelop-Mode": "GUI" } diff --git a/plugins/kdeprovider/kdevkdeprovider.json b/plugins/kdeprovider/kdevkdeprovider.json index 70ca80c9a2..dea316212c 100644 --- a/plugins/kdeprovider/kdevkdeprovider.json +++ b/plugins/kdeprovider/kdevkdeprovider.json @@ -1,68 +1,69 @@ { "KPlugin": { "Authors": [ { "Name": "Aleix Pol", "Name[ru]": "Aleix Pol Gonzalez", "Name[x-test]": "xxAleix Polxx" } ], "Category": "Utilities", "Description": "This plugin helps to obtain KDE projects", "Description[bs]": "Ovaj dodatak omogućava dobijanje KDE projekada", "Description[ca@valencia]": "Este connector ajuda a obtindre els projectes KDE", "Description[ca]": "Aquest connector ajuda a obtenir els projectes KDE", "Description[de]": "Dieses Modul hilft dabei KDE-Projekte zu beziehen", "Description[es]": "Este complemento le ayuda a obtener proyectos de KDE", "Description[fi]": "Tämä liitännäinen auttaa KDE-projektien hakemisessa", "Description[fr]": "Ce module aide à obtenir des projets KDE", "Description[gl]": "Este complemento permite obter proxectos de KDE.", "Description[it]": "Questa estensione consente di ottenere progetti di KDE", "Description[nl]": "Deze plugin helpt om KDE-projects te verkrijgen", "Description[pl]": "Wtyczka ta pozwala na pobieranie projektów KDE", "Description[pt]": "Este 'plugin' ajuda a obter projectos do KDE", "Description[pt_BR]": "Este plugin ajuda a obter projetos do KDE", "Description[sk]": "Tento modul pomáha získavať KDE projekty", "Description[sl]": "Ta vstavek pomaga pri pridobivanju KDE-jevih projektov", "Description[sv]": "Insticksprogrammet hjälper till att erhålla KDE-projekt", "Description[tr]": "Bu eklenti KDE projelerini edinmeye yardım eder", "Description[uk]": "За допомогою цього додатка можна отримувати проекти KDE", "Description[x-test]": "xxThis plugin helps to obtain KDE projectsxx", "Description[zh_CN]": "此插件帮助您获取 KDE 项目", "Icon": "kde", "Id": "kdevkdeprovider", "License": "GPL", "Name": "KDE Provider", "Name[bs]": "KDE pružalac", "Name[ca@valencia]": "Proveïdor del KDE", "Name[ca]": "Proveïdor del KDE", "Name[cs]": "Poskytovatel KDE", "Name[de]": "KDE-Provider", "Name[es]": "Proveedor de KDE", "Name[fi]": "KDE-tarjoaja", "Name[fr]": "Fournisseur KDE", "Name[gl]": "Fornecedor de KDE", "Name[it]": "Fornitore di KDE", "Name[nl]": "KDE-leverancier", "Name[pl]": "Dostawca KDE", "Name[pt]": "Fornecedor do KDE", "Name[pt_BR]": "Fornecedor do KDE", "Name[sk]": "Poskytovateľ KDE", "Name[sl]": "KDE-jev ponudnik", "Name[sv]": "KDE-leverantör", "Name[tr]": "KDE Sağlayıcı", "Name[uk]": "Надавач даних KDE", "Name[x-test]": "xxKDE Providerxx", "Name[zh_CN]": "KDE 提供器", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-IRequired": [ "org.kdevelop.IBasicVersionControl@kdevgit" ], "X-KDevelop-Interfaces": [ "org.kdevelop.IProjectProvider" ], + "X-KDevelop-Category": "Global", "X-KDevelop-Mode": "GUI" } diff --git a/plugins/manpage/kdevmanpage.json b/plugins/manpage/kdevmanpage.json index 960f117cc4..ceeaafd9e7 100644 --- a/plugins/manpage/kdevmanpage.json +++ b/plugins/manpage/kdevmanpage.json @@ -1,59 +1,60 @@ { "KPlugin": { "Category": "Documentation", "Description": "This plugin provides Manual Pages integration", "Description[bs]": "Ovaj dodatak omogućava integraciju sa stranicama uputstava", "Description[ca@valencia]": "Este connector proporciona la integració de les pàgines del manual", "Description[ca]": "Aquest connector proporciona la integració de les pàgines del manual", "Description[de]": "Dieses Modul integriert Handbuch-Seiten", "Description[es]": "Este complemento proporciona la integración de páginas de manual", "Description[fi]": "Tämä liitännäinen tarjoaa integraation manuaalisivuille", "Description[fr]": "Ce module fournit une intégration des pages de manuel", "Description[gl]": "Este complemento fornece integración coas páxinas de manual.", "Description[it]": "Questa estensione fornisce l'integrazione delle pagine del manuale", "Description[nl]": "Deze plugin biedt integratie met man-pagina's", "Description[pl]": "Wtyczka ta zapewnia integrację stron instrukcji", "Description[pt]": "Este 'plugin' oferece a integração com as páginas de manual do Man", "Description[pt_BR]": "Este plugin fornece integração com as páginas de manuais", "Description[sk]": "Tento modul poskytuje integráciu manuálových stránok", "Description[sl]": "Ta vstavek omogoči podporo za strani man", "Description[sv]": "Insticksprogrammet tillhandahåller integrering av manualsidor", "Description[tr]": "Bu eklenti Kılavuz Sayfaları bütünleşmesini sağlar", "Description[uk]": "За допомогою цього додатка здійснюється інтеграція з системою сторінок підручника (man)", "Description[x-test]": "xxThis plugin provides Manual Pages integrationxx", "Description[zh_CN]": "此插件提供了帮助手册整合", "EnabledByDefault": false, "Icon": "x-office-address-book", "Id": "KDevManPage", "License": "GPL", "Name": "Man Pages", "Name[bs]": "Man stgranice", "Name[ca@valencia]": "Pàgines man", "Name[ca]": "Pàgines man", "Name[cs]": "Manuálové stránky", "Name[de]": "Handbuch-Seiten", "Name[es]": "Páginas de manual", "Name[fi]": "Mansivut", "Name[fr]": "Pages manuel", "Name[gl]": "Páxinas de manuais", "Name[it]": "Pagine man", "Name[nl]": "Man-pagina's", "Name[pl]": "Strony instrukcji", "Name[pt]": "Páginas do Man", "Name[pt_BR]": "Páginas de manuais", "Name[sk]": "Manuálové stránky", "Name[sl]": "Strani man", "Name[sv]": "Manualsidor", "Name[tr]": "Kılavuz Sayfaları", "Name[uk]": "Сторінки man", "Name[x-test]": "xxMan Pagesxx", "Name[zh_CN]": "Man 手册", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Interfaces": [ "org.kdevelop.IDocumentationProvider" ], + "X-KDevelop-Category": "Global", "X-KDevelop-Mode": "GUI" } diff --git a/plugins/patchreview/kdevpatchreview.json b/plugins/patchreview/kdevpatchreview.json index 025616678f..109038579b 100644 --- a/plugins/patchreview/kdevpatchreview.json +++ b/plugins/patchreview/kdevpatchreview.json @@ -1,81 +1,82 @@ { "KPlugin": { "Authors": [ { "Name": "David Nolden", "Name[x-test]": "xxDavid Noldenxx" } ], "Category": "Utilities", "Description": "This plugin allows reviewing patches directly in the editor.", "Description[ar]": "تسمح هذه الملحقة بمراجعة الرّقعة مباشرةً في المحرّر.", "Description[ca@valencia]": "Este connector permet revisar pedaços directament en l'editor.", "Description[ca]": "Aquest connector permet revisar pedaços directament en l'editor.", "Description[de]": "Dieses Modul ermöglicht es, Patches direkt im Editor durchzusehen.", "Description[es]": "Este complemento permite la revisión de parches directamente en el editor.", "Description[fr]": "Ce module permet de passer en revue des correctifs directement dans l'éditeur.", "Description[gl]": "Este complemento permite revisar parches directamente no editor.", "Description[it]": "Questa estensione permette di rivedere le patch direttamente nell'editor.", "Description[nl]": "Deze plugin laat patches herzien direct in de editor.", "Description[pl]": "Ta wtyczka pozwala na przeglądanie poprawek bezpośrednio w edytorze.", "Description[pt]": "Este 'plugin' permite a revisão das modificações directamente no editor.", "Description[pt_BR]": "Este plugin permite obter as modificações diretamente no editor.", "Description[sk]": "Tento plugin umožňuje zhodnotenie záplat priamo v editore.", "Description[sl]": "Vstavek omogoča pregled popravkov neposredno v urejevalniku.", "Description[sv]": "Insticksprogrammet gör det möjligt att direkt granska programfixar i editorn.", "Description[tr]": "Bu eklenti yamaların doğrudan düzenleyici içerisinde gözden geçirilmesini sağlar.", "Description[uk]": "За допомогою цього додатка ви зможете рецензувати латки безпосередньо у редакторі.", "Description[x-test]": "xxThis plugin allows reviewing patches directly in the editor.xx", "Description[zh_CN]": "此插件允许在编辑器中直接审阅补丁。", "Icon": "applications-engineering", "Id": "kdevpatchreview", "License": "GPL", "Name": "Patch Review", "Name[ar]": "مراجعة الرّقع", "Name[bg]": "Преглед на кръпки", "Name[bs]": "Pregled zakrpa", "Name[ca@valencia]": "Revisió del pedaç", "Name[ca]": "Revisió del pedaç", "Name[da]": "Gennemgang af rettelser", "Name[de]": "Patch-Durchsicht", "Name[el]": "Επιθεώρηση διορθώσεων", "Name[es]": "Revisión de parches", "Name[et]": "Paikade ülevaatamine", "Name[fr]": "Révision de correctifs", "Name[gl]": "Revisor de parches", "Name[hu]": "Javítócsomag átnézés", "Name[it]": "Revisione patch", "Name[kk]": "Жамау шолуы", "Name[nb]": "Lappegjennomgang", "Name[nds]": "Plasternakiek", "Name[nl]": "Patchoverzicht", "Name[pl]": "Przeglądanie poprawek", "Name[pt]": "Revisão da Modificação", "Name[pt_BR]": "Revisão da Modificação", "Name[ru]": "Рецензирование патчей", "Name[sk]": "Zhodnotenie záplaty", "Name[sl]": "Pregled popravkov", "Name[sv]": "Granska programfixar", "Name[tr]": "Yama Gözden Geçirmesi", "Name[ug]": "ياماق باھالاش", "Name[uk]": "Рецензування латки", "Name[x-test]": "xxPatch Reviewxx", "Name[zh_CN]": "补丁审阅", "Name[zh_TW]": "修補檢視", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Interfaces": [ "org.kdevelop.IPatchReview", "ILanguageSupport" ], "X-KDevelop-Languages": [ "Diff" ], + "X-KDevelop-Category": "Global", "X-KDevelop-Mode": "GUI", "X-KDevelop-SupportedMimeTypes": [ "text/x-diff", "text/x-patch" ] } diff --git a/plugins/qmakemanager/kdevqmakemanager.json b/plugins/qmakemanager/kdevqmakemanager.json index 0b39a9fcd1..86a93f8103 100644 --- a/plugins/qmakemanager/kdevqmakemanager.json +++ b/plugins/qmakemanager/kdevqmakemanager.json @@ -1,65 +1,66 @@ { "KPlugin": { "Category": "Project Management", "Description": "Imports and edits QMake projects", "Description[ca@valencia]": "Importa i edita projectes del QMake", "Description[ca]": "Importa i edita projectes del QMake", "Description[cs]": "Importuje a upravuje vlastní projekty QMake", "Description[de]": "Import und Bearbeitung von benutzerdefinierten QMake-Projekten", "Description[es]": "Importa y edita proyectos QMake", "Description[fi]": "Tuo ja muokkaa QMake-projekteja", "Description[fr]": "Importe et édite des projet QMake", "Description[gl]": "Importa e edita proxectos QMake.", "Description[it]": "Importa e modifica i progetti QMake", "Description[nl]": "Importeert en bewerkt QMake-projecten", "Description[pl]": "Importuje i edytuje projekty QMake", "Description[pt]": "Importa e edita os projectos do QMake", "Description[pt_BR]": "Importa e edita os projetos do QMake", "Description[sk]": "Importuje a upravuje projekty QMake", "Description[sl]": "Uvozi in ureja projekte po meri temelječe na QMake", "Description[sv]": "Importerar och redigerar QMake-projekt", "Description[tr]": "QMake projelerini içeriye aktarır ve düzenler", "Description[uk]": "Імпортує і дає змогу редагувати проекти QMake", "Description[x-test]": "xxImports and edits QMake projectsxx", "Description[zh_CN]": "导入并编辑 QMake 工程", "Icon": "qtlogo", "Id": "KDevQMakeManager", "Name": "QMake Project Manager", "Name[ca@valencia]": "Gestor de projectes QMake", "Name[ca]": "Gestor de projectes QMake", "Name[cs]": "Správce projektů QMake", "Name[de]": "QMake-Projektverwaltung", "Name[es]": "Gestor de proyectos QMake", "Name[fi]": "QMake-projektinhallinta", "Name[fr]": "Gestionnaire de projet QMake", "Name[gl]": "Xestor de proxectos QMake", "Name[it]": "Gestore progetto QMake", "Name[nl]": "QMake-projectbeheerder", "Name[pl]": "Zarządzanie projektem QMake", "Name[pt]": "Gestor de Projectos do QMake", "Name[pt_BR]": "Gerenciador de projetos do QMake", "Name[sk]": "Správca projektov QMake", "Name[sl]": "Upravljalnik projektov QMake", "Name[sv]": "QMake-projekthantering", "Name[tr]": "QMake Proje Yöneticisi", "Name[uk]": "Керування проектами QMake", "Name[x-test]": "xxQMake Project Managerxx", "Name[zh_CN]": "QMake 工程管理器", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-FileManager": "QMake", "X-KDevelop-IRequired": [ "org.kdevelop.IQMakeBuilder" ], "X-KDevelop-Interfaces": [ "org.kdevelop.IBuildSystemManager", "org.kdevelop.IProjectFileManager" ], + "X-KDevelop-Category": "Project", "X-KDevelop-Mode": "NoGUI", "X-KDevelop-ProjectFilesFilter": [ "*.pro" ], "X-KDevelop-ProjectFilesFilterDescription": "QMake Project Files" } diff --git a/plugins/qmljs/kdevqmljs.json b/plugins/qmljs/kdevqmljs.json index f19cebff28..132ed38a26 100644 --- a/plugins/qmljs/kdevqmljs.json +++ b/plugins/qmljs/kdevqmljs.json @@ -1,46 +1,47 @@ { "KPlugin": { "Category": "Language Support", "Icon": "text-x-qml", "Id": "kdevqmljs", "License": "GPL", "Name": "QML Support", "Name[ca@valencia]": "Implementació del QML", "Name[ca]": "Implementació del QML", "Name[cs]": "Podpora QML", "Name[de]": "Unterstützung für QML", "Name[es]": "Implementación de QML", "Name[fi]": "QML-tuki", "Name[fr]": "Prise en charge de QML", "Name[gl]": "Compatibilidade con QML", "Name[hu]": "QML támogatás", "Name[it]": "Supporto per QML", "Name[nl]": "Ondersteuning voor QML", "Name[pl]": "Obsługa QML", "Name[pt]": "Suporte para o QML", "Name[pt_BR]": "Suporte à QML", "Name[ru]": "Поддержка QML", "Name[sk]": "Podpora QML", "Name[sl]": "Podpora za QML", "Name[sv]": "QML-stöd", "Name[tr]": "QML Desteği", "Name[uk]": "Підтримка QML", "Name[x-test]": "xxQML Supportxx", "Name[zh_CN]": "QML 支持", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Interfaces": [ "ILanguageSupport" ], "X-KDevelop-Languages": [ "QML/JS", "JavaScript" ], + "X-KDevelop-Category": "Global", "X-KDevelop-Mode": "NoGUI", "X-KDevelop-SupportedMimeTypes": [ "text/x-qml", "application/javascript" ] } diff --git a/plugins/qthelp/kdevqthelp.json b/plugins/qthelp/kdevqthelp.json index 0665b902cb..9172312510 100644 --- a/plugins/qthelp/kdevqthelp.json +++ b/plugins/qthelp/kdevqthelp.json @@ -1,63 +1,64 @@ { "KPlugin": { "Authors": [ { "Name": "Aleix Pol", "Name[ru]": "Aleix Pol Gonzalez", "Name[x-test]": "xxAleix Polxx" } ], "Category": "Documentation", "Description": "This plugin provides QtHelp integration", "Description[ca@valencia]": "Este connector proveeix la integració amb QtHelp", "Description[ca]": "Aquest connector proveeix la integració amb QtHelp", "Description[de]": "Dieses Modul integriert die Qt-Hilfe", "Description[es]": "Este complemento proporciona la integración de QtHelp", "Description[fi]": "Tämä liitännäinen tarjoaa QtHelp-integraation", "Description[fr]": "Ce module fournit une intégration de l'aide Qt", "Description[gl]": "Este complemento fornece integración con QtHelp", "Description[it]": "Questa estensione fornisce l'integrazione di QtHelp", "Description[nl]": "Deze plug-in biedt integratie met QtHelp", "Description[pl]": "Wtyczka ta zapewnia integrację QtHelp", "Description[pt]": "Este 'plugin' oferece a integração com o QtHelp", "Description[pt_BR]": "Este plugin fornece integração com o QtHelp", "Description[sk]": "Tento modul poskytuje integráciu QtHelp", "Description[sl]": "Ta vstavek omogoča podporo za pomoč za Qt", "Description[sv]": "Insticksprogrammet tillhandahåller integrering av QtHelp", "Description[tr]": "Bu ekleneti QtHelp tümleştirmesi sağlar", "Description[uk]": "За допомогою цього додатка здійснюється інтеграція з QtHelp", "Description[x-test]": "xxThis plugin provides QtHelp integrationxx", "Description[zh_CN]": "此插件提供了 QtHelp 整合", "Icon": "qtlogo", "Id": "kdevqthelp", "License": "GPL", "Name": "Qt Documentation", "Name[ca@valencia]": "Documentació de les Qt", "Name[ca]": "Documentació de les Qt", "Name[cs]": "Dokumentace Qt", "Name[de]": "Qt-Dokumentation", "Name[es]": "Documentación de Qt", "Name[fi]": "Qt-dokumentaatio", "Name[fr]": "Documentation Qt", "Name[gl]": "Documentación de Qt", "Name[it]": "Documentazione Qt", "Name[nl]": "Qt-documentatie", "Name[pl]": "Dokumentacja Qt", "Name[pt]": "Documentação do Qt", "Name[pt_BR]": "Documentação do Qt", "Name[sk]": "Dokumentácia Qt", "Name[sl]": "Dokumentacija za Qt", "Name[sv]": "Qt-dokumentation", "Name[tr]": "Qt Belgelendirmesi", "Name[uk]": "Документація з Qt", "Name[x-test]": "xxQt Documentationxx", "Name[zh_CN]": "Qt 文档", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Interfaces": [ "org.kdevelop.IDocumentationProviderProvider" ], + "X-KDevelop-Category": "Global", "X-KDevelop-Mode": "GUI" } diff --git a/plugins/standardoutputview/kdevstandardoutputview.json b/plugins/standardoutputview/kdevstandardoutputview.json index 795c82f12d..05e75a50f9 100644 --- a/plugins/standardoutputview/kdevstandardoutputview.json +++ b/plugins/standardoutputview/kdevstandardoutputview.json @@ -1,57 +1,58 @@ { "KPlugin": { "Category": "Core", "Description": "Provides a text output tool view for other plugins to use, to show things like compiler messages.", "Description[ca@valencia]": "Proporciona una vista d'eina d'eixida de text per a utilitzar en altres connectors, per a visualitzar missatges del compilador, per exemple.", "Description[ca]": "Proporciona una vista d'eina de sortida de text per a utilitzar en altres connectors, per a visualitzar missatges del compilador, per exemple.", "Description[es]": "Proporciona un visor de salida de texto para que otros complementos muestren cosas como mensajes del compilador, por ejemplo.", "Description[nl]": "Levert tekstuitvoer van hulpmiddelen voor andere te gebruiken plugins, om zaken te tonen zoals berichten van compilers.", "Description[pt]": "Oferece uma área de texto para os outros 'plugins' usarem, para apresentar algumas coisas, como as mensagens do compilador.", "Description[sv]": "Tillhandahåller en verktygsvy för textutmatning som andra insticksprogram kan använda för att visa saker som kompilatormeddelanden.", "Description[uk]": "Забезпечує роботу панелі показу текстових даних інших додатків, зокрема попереджень компілятора.", "Description[x-test]": "xxProvides a text output tool view for other plugins to use, to show things like compiler messages.xx", "Icon": "kdevelop", "Id": "KDevStandardOutputView", "Name": "Output View", "Name[ar]": "عرض الخَرْج", "Name[bg]": "Преглед на резултата", "Name[bs]": "Pregled izlaza", "Name[ca@valencia]": "Vista de l'eixida", "Name[ca]": "Vista de la sortida", "Name[cs]": "Pohled na výstup", "Name[da]": "Visning af output", "Name[de]": "Ansicht für Ausgaben", "Name[el]": "Προβολή αποτελεσμάτων", "Name[es]": "Vista de la salida", "Name[et]": "Väljundivaade", "Name[fr]": "Vue de la sortie", "Name[ga]": "Amharc Aschurtha", "Name[gl]": "Vista da saída", "Name[hu]": "Kimenet nézet", "Name[it]": "Vista output", "Name[kk]": "Шығыс көрінісі", "Name[nb]": "Utdata-visning", "Name[nds]": "Utgaavansicht", "Name[nl]": "Uitvoerweergave", "Name[pl]": "Widok wyjścia", "Name[pt]": "Área de Resultados", "Name[pt_BR]": "Área de resultados", "Name[ru]": "Панель вывода", "Name[sk]": "Pohľad na výstup", "Name[sl]": "Prikaz izhoda", "Name[sv]": "Utmatningsvisning", "Name[tr]": "Çıktı Görünümü", "Name[ug]": "چىقىرىش كۆرۈنۈشى", "Name[uk]": "Перегляд виводу", "Name[x-test]": "xxOutput Viewxx", "Name[zh_CN]": "输出视图", "Name[zh_TW]": "輸出檢視", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-Interfaces": [ "org.kdevelop.IOutputView" ], + "X-KDevelop-Category": "Global", "X-KDevelop-Mode": "GUI" } diff --git a/plugins/subversion/kdevsubversion.json b/plugins/subversion/kdevsubversion.json index b71e10f19a..c1c433ee31 100644 --- a/plugins/subversion/kdevsubversion.json +++ b/plugins/subversion/kdevsubversion.json @@ -1,65 +1,66 @@ { "KPlugin": { "Authors": [ { "Name": "Dukju Ahn", "Name[x-test]": "xxDukju Ahnxx" } ], "Category": "Version Control", "Description": "This plugin integrates Subversion to KDevelop.", "Description[ca@valencia]": "Este connector integra el Subversion en el KDevelop.", "Description[ca]": "Aquest connector integra el Subversion en el KDevelop.", "Description[cs]": "Tento modul integruje podporu pro subversion v KDevelop", "Description[de]": "Dieses Modul integriert Subversion in KDevelop.", "Description[es]": "Este complemento integra Subversion en KDevelop.", "Description[fr]": "Ce module intègre Subversion dans KDevelop.", "Description[it]": "Questa estensione integra Subversion in KDevelop.", "Description[nl]": "Deze plugin integreert Subversion in KDevelop.", "Description[pl]": "Ta wtyczka integruje Subversion z KDevelop.", "Description[pt]": "Este 'plugin' integra o Subversion no KDevelop.", "Description[sk]": "Tento plugin integruje subversion do KDevelop.", "Description[sv]": "Insticksprogrammet integrerar Subversion i KDevelop.", "Description[uk]": "Цей додаток інтегрує Subversion із KDevelop.", "Description[x-test]": "xxThis plugin integrates Subversion to KDevelop.xx", "Icon": "subversion", "Id": "kdevsubversion", "License": "GPL", "Name": "Subversion Support", "Name[bg]": "Поддръжка на Subversion", "Name[ca@valencia]": "Implementació del Subversion", "Name[ca]": "Implementació del Subversion", "Name[cs]": "Podpora subversion", "Name[da]": "Subversion-understøttelse", "Name[de]": "Unterstützung für Subversion", "Name[el]": "Υποστήριξη Subversion", "Name[es]": "Implementación de Subversion", "Name[et]": "Subversioni toetus", "Name[fr]": "Prise en charge de Subversion", "Name[it]": "Supporto per Subversion", "Name[kk]": "Subversion қолдауы", "Name[nb]": "Støtte for subversion", "Name[nds]": "Subversion-Ünnerstütten", "Name[nl]": "Ondersteuning van subversion", "Name[pa]": "ਸਬਵਰਜਨ ਸਹਿਯੋਗ", "Name[pl]": "Obsługa Subversion", "Name[pt]": "Suporte para o Subversion", "Name[sk]": "Podpora subversion", "Name[sv]": "Subversion-stöd", "Name[ug]": "Subversion قوللىشى", "Name[uk]": "Підтримка Subversion", "Name[x-test]": "xxSubversion Supportxx", "Name[zh_CN]": "Subversion 支持", "Name[zh_TW]": "Subversion 支援", "ServiceTypes": [ "KDevelop/Plugin" ] }, "X-KDevelop-IRequired": [ "org.kdevelop.IOutputView" ], "X-KDevelop-Interfaces": [ "org.kdevelop.IBasicVersionControl" ], + "X-KDevelop-Category": "Global", "X-KDevelop-Mode": "GUI" } diff --git a/plugins/welcomepage/kdevwelcomepage.json b/plugins/welcomepage/kdevwelcomepage.json index cea77213b7..b214425919 100644 --- a/plugins/welcomepage/kdevwelcomepage.json +++ b/plugins/welcomepage/kdevwelcomepage.json @@ -1,47 +1,48 @@ { "KPlugin": { "Description": "Provides the welcome page visible in an empty session", "Description[ca@valencia]": "Proporciona la visibilitat de la pàgina de benvinguda en una sessió buida", "Description[ca]": "Proporciona la visibilitat de la pàgina de benvinguda en una sessió buida", "Description[es]": "Proporciona la página de bienvenida visible en una sesión vacía", "Description[gl]": "Fornece a páxina de benvida visíbel nunha sesión baleira.", "Description[it]": "Fornisce la pagina di benvenuto visibile in una sessione vuota", "Description[nl]": "Biedt de welkomst pagina zichtbaar in een lege sessie", "Description[pl]": "Dostarcza stronę powitania widoczną przy pustych sesjach", "Description[pt]": "Oferece a página de boas-vindas visível numa sessão vazia", "Description[sk]": "Poskytuje uvítaciu stránku viditeľnú v prázdnom sedení", "Description[sl]": "Ponuja pozdravni zaslon, ki je viden v prazni seji", "Description[sv]": "Tillhandahåller välkomstsidan synlig i en tom session", "Description[tr]": "Boş oturumda görünen hoşgeldin sayfasını sağlar", "Description[uk]": "Забезпечує роботу сторінки вітання, яку програма показує, якщо сеанс порожній", "Description[x-test]": "xxProvides the welcome page visible in an empty sessionxx", "Icon": "kdevelop", "Id": "KDevWelcomePage", "Name": "KDevelop Welcome Page", "Name[ar]": "صفحة ترحيب مطوّرك", "Name[ca@valencia]": "Pàgina de benvinguda del KDevelop", "Name[ca]": "Pàgina de benvinguda del KDevelop", "Name[cs]": "Uvítací stránka KDevelop", "Name[de]": "KDevelop-Startseite", "Name[es]": "Página de bienvenida de KDevelop", "Name[fr]": "Page d'accueil de KDevelop", "Name[gl]": "Páxina de benvida de KDevelop", "Name[hu]": "KDevelop üdvözlőképernyő", "Name[it]": "Pagina di benvenuto di KDevelop", "Name[nl]": "Welkomstpagina van KDevelop", "Name[pl]": "Strona powitalna KDevelop", "Name[pt]": "Página de Boas-Vindas do KDevelop", "Name[pt_BR]": "Página de boas-vindas do KDevelop", "Name[sk]": "Uvítacia stránka KDevelop", "Name[sl]": "Pozdravna stran za KDevelop", "Name[sv]": "KDevelop välkomstsida", "Name[tr]": "KDevelop Karşılama Sayfası", "Name[uk]": "Сторінка вітання KDevelop", "Name[x-test]": "xxKDevelop Welcome Pagexx", "Name[zh_CN]": "KDevelop 欢迎页面", "ServiceTypes": [ "KDevelop/Plugin" ] }, + "X-KDevelop-Category": "Global", "X-KDevelop-Mode": "GUI" }