diff --git a/app/main.cpp b/app/main.cpp index 4aa437799f..9255a300e6 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -1,826 +1,826 @@ /*************************************************************************** * 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 "config-kdevelop.h" #include "kdevelop_version.h" #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) { for (const UrlInfo& info : infos) { if (!ICore::self()->documentController()->openDocument(info.url, info.cursor)) { qWarning(APP) << 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()) { const auto activeSession = KDevelop::Core::self()->sessionController()->activeSession(); if (!activeSession) { qWarning(APP) << "No active session, can't save state"; return; } const QString x11SessionId = sm.sessionId() + QLatin1Char('_') + sm.sessionKey(); 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; } /// Tries to find sessions containing project @p projectUrl in @p sessions. static const KDevelop::SessionInfos findSessionsWithProject(const SessionInfos& sessions, const QUrl& projectUrl) { if (!projectUrl.isValid()) return {}; KDevelop::SessionInfos infos; for (auto it = sessions.constBegin(); it != sessions.constEnd(); ++it) { if (it->projects.contains(projectUrl)) { infos << *it; } } return infos; } /// 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; for (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; for (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; for (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(); #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) // If possible, use the Software backend for QQuickWidget (currently used in the // welcome page plugin). This means we don't need OpenGL at all, avoiding issues // like https://bugs.kde.org/show_bug.cgi?id=386527. QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Software); #endif // 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"); } 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"); KAboutData aboutData( QStringLiteral("kdevelop"), i18n( "KDevelop" ), QByteArray(KDEVELOP_VERSION_STRING), i18n("The KDevelop Integrated Development Environment"), KAboutLicense::GPL, i18n("Copyright 1999-2018, 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.addAuthor( i18n("Friedrich W. H. Kossebau"), QString(), QStringLiteral("kossebau@kde.org") ); 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(), QString() ); 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; 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{"f", "fetch"}, i18n("Open KDevelop and fetch the project from the given ."), QStringLiteral("repo url")}); parser.addOption(QCommandLineOption{QStringList{"p", "project"}, i18n("Open KDevelop and load the given project. can be either a .kdev4 file or a directory path."), 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, or directories to load as projects" ), QStringLiteral("[FILE[:line[:column]] | DIRECTORY]...")); // 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; for (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; for (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")); for (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 for (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(APP) << "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(APP) << sessionData->uuid.toString() << sessionData->name << "is not running"; return 5; } } - if (parser.isSet("project")) { + if (parser.isSet(QStringLiteral("project"))) { const auto project = parser.value(QStringLiteral("project")); QFileInfo info(project); QUrl projectUrl; if (info.suffix() == QLatin1String("kdev4")) { projectUrl = QUrl::fromLocalFile(info.absoluteFilePath()); } else if (info.isDir()) { QDir dir(info.absoluteFilePath()); const auto potentialProjectFiles = dir.entryList({QStringLiteral("*.kdev4")}, QDir::Files, QDir::Name); qDebug(APP) << "Found these potential project files:" << potentialProjectFiles; if (!potentialProjectFiles.isEmpty()) { projectUrl = QUrl::fromLocalFile(dir.absoluteFilePath(potentialProjectFiles.value(0))); } } else { QTextStream qerr(stderr); qerr << "Invalid project: " << project << " - should be either a path to a .kdev4 file or a directory containing a .kdev4 file"; return 1; } qDebug(APP) << "Attempting to find a suitable session for project" << projectUrl; const auto sessionInfos = findSessionsWithProject(availableSessionInfos, projectUrl); qDebug(APP) << "Found matching sessions:" << sessionInfos.size(); if (!sessionInfos.isEmpty()) { // TODO: If there's more than one match: Allow the user to select which session to open? qDebug(APP) << "Attempting to open session:" << sessionInfos.at(0).name; session = sessionInfos.at(0).uuid.toString(); } } KDevIDEExtension::init(); qDebug(APP) << "Attempting to initialize session:" << session; 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")); } const auto fetchUrlStrings = parser.values(QStringLiteral("fetch")); for (const auto& fetchUrlString : fetchUrlStrings) { core->projectControllerInternal()->fetchProjectFromUrl(QUrl::fromUserInput(fetchUrlString)); } 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/language/duchain/navigation/abstractdeclarationnavigationcontext.cpp b/kdevplatform/language/duchain/navigation/abstractdeclarationnavigationcontext.cpp index 0070fe10cc..b76b616a9e 100644 --- a/kdevplatform/language/duchain/navigation/abstractdeclarationnavigationcontext.cpp +++ b/kdevplatform/language/duchain/navigation/abstractdeclarationnavigationcontext.cpp @@ -1,822 +1,822 @@ /* Copyright 2007 David Nolden 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 "abstractdeclarationnavigationcontext.h" #include #include #include "../functiondeclaration.h" #include "../functiondefinition.h" #include "../classfunctiondeclaration.h" #include "../namespacealiasdeclaration.h" #include "../forwarddeclaration.h" #include "../types/enumeratortype.h" #include "../types/enumerationtype.h" #include "../types/functiontype.h" #include "../duchainutils.h" #include "../types/pointertype.h" #include "../types/referencetype.h" #include "../types/typeutils.h" #include "../types/typesystem.h" #include "../persistentsymboltable.h" #include #include #include #include #include #include namespace KDevelop { class AbstractDeclarationNavigationContextPrivate { public: DeclarationPointer m_declaration; bool m_fullBackwardSearch = false; }; AbstractDeclarationNavigationContext::AbstractDeclarationNavigationContext(const DeclarationPointer& decl, const TopDUContextPointer& topContext, AbstractNavigationContext* previousContext) : AbstractNavigationContext((topContext ? topContext : TopDUContextPointer(decl ? decl->topContext() : nullptr)), previousContext) , d(new AbstractDeclarationNavigationContextPrivate) { d->m_declaration = decl; //Jump from definition to declaration if possible FunctionDefinition* definition = dynamic_cast(d->m_declaration.data()); if(definition && definition->declaration()) d->m_declaration = DeclarationPointer(definition->declaration()); } AbstractDeclarationNavigationContext::~AbstractDeclarationNavigationContext() { } QString AbstractDeclarationNavigationContext::name() const { if(d->m_declaration.data()) return prettyQualifiedIdentifier(d->m_declaration).toString(); else return declarationName(d->m_declaration); } QString AbstractDeclarationNavigationContext::html(bool shorten) { DUChainReadLocker lock(DUChain::lock(), 300); if ( !lock.locked() ) { return {}; } clear(); AbstractNavigationContext::html(shorten); modifyHtml() += QLatin1String("

") + fontSizePrefix(shorten); addExternalHtml(prefix()); if(!d->m_declaration.data()) { modifyHtml() += i18n("
lost declaration
"); return currentHtml(); } if(auto context = previousContext()) { const QString link = createLink(context->name(), context->name(), NavigationAction(context)); modifyHtml() += navigationHighlight(i18n("Back to %1
", link)); } QExplicitlySharedDataPointer doc; if( !shorten ) { doc = ICore::self()->documentationController()->documentationForDeclaration(d->m_declaration.data()); const AbstractFunctionDeclaration* function = dynamic_cast(d->m_declaration.data()); if( function ) { htmlFunction(); } else if( d->m_declaration->isTypeAlias() || d->m_declaration->type() || d->m_declaration->kind() == Declaration::Instance ) { if( d->m_declaration->isTypeAlias() ) modifyHtml() += importantHighlight(QStringLiteral("typedef ")); if(d->m_declaration->type()) modifyHtml() += i18n("enumerator "); AbstractType::Ptr useType = d->m_declaration->abstractType(); if(d->m_declaration->isTypeAlias()) { //Do not show the own name as type of typedefs if(useType.cast()) useType = useType.cast()->type(); } eventuallyMakeTypeLinks( useType ); modifyHtml() += QLatin1Char(' ') + identifierHighlight(declarationName(d->m_declaration).toHtmlEscaped(), d->m_declaration); if(auto integralType = d->m_declaration->type()) { const QString plainValue = integralType->valueAsString(); if (!plainValue.isEmpty()) { modifyHtml() += QStringLiteral(" = ") + plainValue; } } modifyHtml() += QStringLiteral("
"); }else{ if( d->m_declaration->kind() == Declaration::Type && d->m_declaration->abstractType().cast() ) { htmlClass(); } if ( d->m_declaration->kind() == Declaration::Namespace ) { modifyHtml() += i18n("namespace %1 ", identifierHighlight(d->m_declaration->qualifiedIdentifier().toString().toHtmlEscaped(), d->m_declaration)); } else if ( d->m_declaration->kind() == Declaration::NamespaceAlias ) { modifyHtml() += identifierHighlight(declarationName(d->m_declaration).toHtmlEscaped(), d->m_declaration); } if(d->m_declaration->type()) { EnumerationType::Ptr enumeration = d->m_declaration->type(); modifyHtml() += i18n("enumeration %1 ", identifierHighlight(d->m_declaration->identifier().toString().toHtmlEscaped(), d->m_declaration)); } if(d->m_declaration->isForwardDeclaration()) { ForwardDeclaration* forwardDec = static_cast(d->m_declaration.data()); Declaration* resolved = forwardDec->resolve(topContext().data()); if(resolved) { modifyHtml() += i18n("(resolved forward-declaration: "); makeLink(resolved->identifier().toString(), DeclarationPointer(resolved), NavigationAction::NavigateDeclaration ); modifyHtml() += i18n(") "); }else{ modifyHtml() += i18n("(unresolved forward-declaration) "); QualifiedIdentifier id = forwardDec->qualifiedIdentifier(); const auto& forwardDecFile = forwardDec->topContext()->parsingEnvironmentFile(); uint count; const IndexedDeclaration* decls; PersistentSymbolTable::self().declarations(id, count, decls); for(uint a = 0; a < count; ++a) { auto dec = decls[a].data(); if (!dec || dec->isForwardDeclaration()) { continue; } const auto& decFile = forwardDec->topContext()->parsingEnvironmentFile(); if ((static_cast(decFile) != static_cast(forwardDecFile)) || (decFile && forwardDecFile && decFile->language() != forwardDecFile->language())) { // the language of the declarations must match continue; } modifyHtml() += QStringLiteral("
"); makeLink(i18n("possible resolution from"), DeclarationPointer(dec), NavigationAction::NavigateDeclaration); modifyHtml() += QLatin1Char(' ') + dec->url().str(); } } } modifyHtml() += QStringLiteral("
"); } }else{ AbstractType::Ptr showType = d->m_declaration->abstractType(); if(showType && showType.cast()) { showType = showType.cast()->returnType(); if(showType) modifyHtml() += labelHighlight(i18n("Returns: ")); }else if(showType) { modifyHtml() += labelHighlight(i18n("Type: ")); } if(showType) { eventuallyMakeTypeLinks(showType); modifyHtml() += QStringLiteral(" "); } } QualifiedIdentifier identifier = d->m_declaration->qualifiedIdentifier(); if( identifier.count() > 1 ) { if( d->m_declaration->context() && d->m_declaration->context()->owner() ) { Declaration* decl = d->m_declaration->context()->owner(); FunctionDefinition* definition = dynamic_cast(decl); if(definition && definition->declaration()) decl = definition->declaration(); if(decl->abstractType().cast()) modifyHtml() += labelHighlight(i18n("Enum: ")); else modifyHtml() += labelHighlight(i18n("Container: ")); makeLink( declarationName(DeclarationPointer(decl)), DeclarationPointer(decl), NavigationAction::NavigateDeclaration ); modifyHtml() += QStringLiteral(" "); } else { QualifiedIdentifier parent = identifier; parent.pop(); modifyHtml() += labelHighlight(i18n("Scope: %1 ", typeHighlight(parent.toString().toHtmlEscaped()))); } } if( shorten && !d->m_declaration->comment().isEmpty() ) { QString comment = QString::fromUtf8(d->m_declaration->comment()); if( comment.length() > 60 ) { comment.truncate(60); comment += QLatin1String("..."); } comment.replace(QLatin1Char('\n'), QLatin1Char(' ')); comment.replace(QLatin1String("
"), QLatin1String(" ")); comment.replace(QLatin1String("
"), QLatin1String(" ")); modifyHtml() += commentHighlight(comment.toHtmlEscaped()) + QLatin1String(" "); } QString access = stringFromAccess(d->m_declaration); if( !access.isEmpty() ) modifyHtml() += labelHighlight(i18n("Access: %1 ", propertyHighlight(access.toHtmlEscaped()))); ///@todo Enumerations QString detailsHtml; const QStringList details = declarationDetails(d->m_declaration); if( !details.isEmpty() ) { bool first = true; for (const QString& str : details) { if( !first ) detailsHtml += QLatin1String(", "); first = false; detailsHtml += propertyHighlight(str); } } QString kind = declarationKind(d->m_declaration); if( !kind.isEmpty() ) { if( !detailsHtml.isEmpty() ) modifyHtml() += labelHighlight(i18n("Kind: %1 %2 ", importantHighlight(kind.toHtmlEscaped()), detailsHtml)); else modifyHtml() += labelHighlight(i18n("Kind: %1 ", importantHighlight(kind.toHtmlEscaped()))); } if (d->m_declaration->isDeprecated()) { modifyHtml() += labelHighlight(i18n("Status: %1 ", propertyHighlight(i18n("Deprecated")))); } modifyHtml() += QStringLiteral("
"); if(!shorten) htmlAdditionalNavigation(); if( !shorten ) { if(dynamic_cast(d->m_declaration.data())) modifyHtml() += labelHighlight(i18n( "Def.: " )); else modifyHtml() += labelHighlight(i18n( "Decl.: " )); makeLink( QStringLiteral("%1 :%2").arg( d->m_declaration->url().toUrl().fileName() ).arg( d->m_declaration->rangeInCurrentRevision().start().line()+1 ), d->m_declaration, NavigationAction::JumpToSource ); modifyHtml() += QStringLiteral(" "); //modifyHtml() += "
"; if(!dynamic_cast(d->m_declaration.data())) { if( FunctionDefinition* definition = FunctionDefinition::definition(d->m_declaration.data()) ) { modifyHtml() += labelHighlight(i18n( " Def.: " )); makeLink( QStringLiteral("%1 :%2").arg( definition->url().toUrl().fileName() ).arg( definition->rangeInCurrentRevision().start().line()+1 ), DeclarationPointer(definition), NavigationAction::JumpToSource ); } } if( FunctionDefinition* definition = dynamic_cast(d->m_declaration.data()) ) { if(definition->declaration()) { modifyHtml() += labelHighlight(i18n( " Decl.: " )); makeLink( QStringLiteral("%1 :%2").arg( definition->declaration()->url().toUrl().fileName() ).arg( definition->declaration()->rangeInCurrentRevision().start().line()+1 ), DeclarationPointer(definition->declaration()), NavigationAction::JumpToSource ); } } modifyHtml() += QStringLiteral(" "); //The action name _must_ stay "show_uses", since that is also used from outside makeLink(i18n("Show uses"), QStringLiteral("show_uses"), NavigationAction(d->m_declaration, NavigationAction::NavigateUses)); } QByteArray declarationComment = d->m_declaration->comment(); if( !shorten && (!declarationComment.isEmpty() || doc) ) { modifyHtml() += QStringLiteral("

"); if(doc) { QString comment = doc->description(); connect(doc.data(), &IDocumentation::descriptionChanged, this, &AbstractDeclarationNavigationContext::contentsChanged); if(!comment.isEmpty()) { modifyHtml() += QLatin1String("

") + commentHighlight(comment) + QLatin1String("

"); } } QString comment = QString::fromUtf8(declarationComment); if(!comment.isEmpty()) { // if the first paragraph does not contain a tag, we assume that this is a plain-text comment if (!Qt::mightBeRichText(comment)) { // still might contain extra html tags for line breaks (this is the case for doxygen-style comments sometimes) // let's protect them from being removed completely comment.replace(QRegExp(QStringLiteral("
")), QStringLiteral("\n")); comment = comment.toHtmlEscaped(); comment.replace(QLatin1Char('\n'), QLatin1String("
")); //Replicate newlines in html } modifyHtml() += commentHighlight(comment); modifyHtml() += QStringLiteral("

"); } } if(!shorten) { modifyHtml() += declarationSizeInformation(d->m_declaration); } if(!shorten && doc) { modifyHtml() += QLatin1String("

") + i18n("Show documentation for "); makeLink(prettyQualifiedName(d->m_declaration), d->m_declaration, NavigationAction::ShowDocumentation); modifyHtml() += QStringLiteral("

"); } //modifyHtml() += "
"; addExternalHtml(suffix()); modifyHtml() += fontSizeSuffix(shorten) + QLatin1String("

"); return currentHtml(); } AbstractType::Ptr AbstractDeclarationNavigationContext::typeToShow(AbstractType::Ptr type) { return type; } void AbstractDeclarationNavigationContext::htmlFunction() { const AbstractFunctionDeclaration* function = dynamic_cast(d->m_declaration.data()); Q_ASSERT(function); const ClassFunctionDeclaration* classFunDecl = dynamic_cast(d->m_declaration.data()); const FunctionType::Ptr type = d->m_declaration->abstractType().cast(); if( !type ) { modifyHtml() += errorHighlight(QStringLiteral("Invalid type
")); return; } if( !classFunDecl || (!classFunDecl->isConstructor() && !classFunDecl->isDestructor()) ) { // only print return type for global functions and non-ctor/dtor methods eventuallyMakeTypeLinks( type->returnType() ); } modifyHtml() += QLatin1Char(' ') + identifierHighlight(prettyIdentifier(d->m_declaration).toString().toHtmlEscaped(), d->m_declaration); if( type->indexedArgumentsSize() == 0 ) { modifyHtml() += QStringLiteral("()"); } else { modifyHtml() += QStringLiteral("( "); bool first = true; int firstDefaultParam = type->indexedArgumentsSize() - function->defaultParametersSize(); int currentArgNum = 0; QVector decls; if (DUContext* argumentContext = DUChainUtils::getArgumentContext(d->m_declaration.data())) { decls = argumentContext->localDeclarations(topContext().data()); } foreach(const AbstractType::Ptr& argType, type->arguments()) { if( !first ) modifyHtml() += QStringLiteral(", "); first = false; eventuallyMakeTypeLinks( argType ); if (currentArgNum < decls.size()) { modifyHtml() += QLatin1Char(' ') + identifierHighlight(decls[currentArgNum]->identifier().toString().toHtmlEscaped(), d->m_declaration); } if (currentArgNum >= firstDefaultParam) { IndexedString defaultStr = function->defaultParameters()[currentArgNum - firstDefaultParam]; if (!defaultStr.isEmpty()) { modifyHtml() += QLatin1String(" = ") + defaultStr.str().toHtmlEscaped(); } } ++currentArgNum; } modifyHtml() += QStringLiteral(" )"); } modifyHtml() += QStringLiteral("
"); } Identifier AbstractDeclarationNavigationContext::prettyIdentifier(const DeclarationPointer& decl) const { Identifier ret; QualifiedIdentifier q = prettyQualifiedIdentifier(decl); if(!q.isEmpty()) ret = q.last(); return ret; } QualifiedIdentifier AbstractDeclarationNavigationContext::prettyQualifiedIdentifier(const DeclarationPointer& decl) const { if(decl) return decl->qualifiedIdentifier(); else return QualifiedIdentifier(); } QString AbstractDeclarationNavigationContext::prettyQualifiedName(const DeclarationPointer& decl) const { const auto qid = prettyQualifiedIdentifier(decl); if (qid.isEmpty()) { return i18nc("An anonymous declaration (class, function, etc.)", ""); } return qid.toString(); } void AbstractDeclarationNavigationContext::htmlAdditionalNavigation() { ///Check if the function overrides or hides another one const ClassFunctionDeclaration* classFunDecl = dynamic_cast(d->m_declaration.data()); if(classFunDecl) { Declaration* overridden = DUChainUtils::getOverridden(d->m_declaration.data()); if(overridden) { modifyHtml() += i18n("Overrides a "); makeLink(i18n("function"), QStringLiteral("jump_to_overridden"), NavigationAction(DeclarationPointer(overridden), NavigationAction::NavigateDeclaration)); modifyHtml() += i18n(" from "); makeLink(prettyQualifiedName(DeclarationPointer(overridden->context()->owner())), QStringLiteral("jump_to_overridden_container"), NavigationAction(DeclarationPointer(overridden->context()->owner()), NavigationAction::NavigateDeclaration)); modifyHtml() += QStringLiteral("
"); }else{ //Check if this declarations hides other declarations QList decls; foreach(const DUContext::Import &import, d->m_declaration->context()->importedParentContexts()) if(import.context(topContext().data())) decls += import.context(topContext().data())->findDeclarations(QualifiedIdentifier(d->m_declaration->identifier()), CursorInRevision::invalid(), AbstractType::Ptr(), topContext().data(), DUContext::DontSearchInParent); uint num = 0; foreach(Declaration* decl, decls) { modifyHtml() += i18n("Hides a "); makeLink(i18n("function"), QStringLiteral("jump_to_hide_%1").arg(num), NavigationAction(DeclarationPointer(decl), NavigationAction::NavigateDeclaration)); modifyHtml() += i18n(" from "); makeLink(prettyQualifiedName(DeclarationPointer(decl->context()->owner())), QStringLiteral("jump_to_hide_container_%1").arg(num), NavigationAction(DeclarationPointer(decl->context()->owner()), NavigationAction::NavigateDeclaration)); modifyHtml() += QStringLiteral("
"); ++num; } } ///Show all places where this function is overridden if(classFunDecl->isVirtual()) { Declaration* classDecl = d->m_declaration->context()->owner(); if(classDecl) { uint maxAllowedSteps = d->m_fullBackwardSearch ? (uint)-1 : 10; const QList overriders = DUChainUtils::getOverriders(classDecl, classFunDecl, maxAllowedSteps); if(!overriders.isEmpty()) { modifyHtml() += i18n("Overridden in "); bool first = true; for (Declaration* overrider : overriders) { if(!first) modifyHtml() += QStringLiteral(", "); first = false; const auto owner = DeclarationPointer(overrider->context()->owner()); const QString name = prettyQualifiedName(owner); makeLink(name, name, NavigationAction(DeclarationPointer(overrider), NavigationAction::NavigateDeclaration)); } modifyHtml() += QStringLiteral("
"); } if(maxAllowedSteps == 0) createFullBackwardSearchLink(overriders.isEmpty() ? i18n("Overriders possible, show all") : i18n("More overriders possible, show all")); } } } ///Show all classes that inherit this one uint maxAllowedSteps = d->m_fullBackwardSearch ? (uint)-1 : 10; const QList inheriters = DUChainUtils::getInheriters(d->m_declaration.data(), maxAllowedSteps); if(!inheriters.isEmpty()) { modifyHtml() += i18n("Inherited by "); bool first = true; for (Declaration* importer : inheriters) { if(!first) modifyHtml() += QStringLiteral(", "); first = false; const QString importerName = prettyQualifiedName(DeclarationPointer(importer)); makeLink(importerName, importerName, NavigationAction(DeclarationPointer(importer), NavigationAction::NavigateDeclaration)); } modifyHtml() += QStringLiteral("
"); } if(maxAllowedSteps == 0) createFullBackwardSearchLink(inheriters.isEmpty() ? i18n("Inheriters possible, show all") : i18n("More inheriters possible, show all")); } void AbstractDeclarationNavigationContext::createFullBackwardSearchLink(const QString& string) { makeLink(string, QStringLiteral("m_fullBackwardSearch=true"), NavigationAction(QStringLiteral("m_fullBackwardSearch=true"))); modifyHtml() += QStringLiteral("
"); } NavigationContextPointer AbstractDeclarationNavigationContext::executeKeyAction(const QString& key) { if(key == QLatin1String("m_fullBackwardSearch=true")) { d->m_fullBackwardSearch = true; clear(); } return NavigationContextPointer(this); } void AbstractDeclarationNavigationContext::htmlClass() { StructureType::Ptr klass = d->m_declaration->abstractType().cast(); Q_ASSERT(klass); ClassDeclaration* classDecl = dynamic_cast(klass->declaration(topContext().data())); if(classDecl) { switch ( classDecl->classType() ) { case ClassDeclarationData::Class: modifyHtml() += QStringLiteral("class "); break; case ClassDeclarationData::Struct: modifyHtml() += QStringLiteral("struct "); break; case ClassDeclarationData::Union: modifyHtml() += QStringLiteral("union "); break; case ClassDeclarationData::Interface: modifyHtml() += QStringLiteral("interface "); break; case ClassDeclarationData::Trait: modifyHtml() += QStringLiteral("trait "); break; } eventuallyMakeTypeLinks( klass.cast() ); FOREACH_FUNCTION( const BaseClassInstance& base, classDecl->baseClasses ) { modifyHtml() += QLatin1String(", ") + stringFromAccess(base.access) + QLatin1Char(' ') + (base.virtualInheritance ? QStringLiteral("virtual") : QString()) + QLatin1Char(' '); eventuallyMakeTypeLinks(base.baseClass.abstractType()); } } else { /// @todo How can we get here? and should this really be a class? modifyHtml() += QStringLiteral("class "); eventuallyMakeTypeLinks( klass.cast() ); } modifyHtml() += QStringLiteral(" "); } void AbstractDeclarationNavigationContext::htmlIdentifiedType(AbstractType::Ptr type, const IdentifiedType* idType) { Q_ASSERT(type); Q_ASSERT(idType); if( Declaration* decl = idType->declaration(topContext().data()) ) { //Remove the last template-identifiers, because we create those directly QualifiedIdentifier id = prettyQualifiedIdentifier(DeclarationPointer(decl)); Identifier lastId = id.last(); id.pop(); lastId.clearTemplateIdentifiers(); id.push(lastId); if(decl->context() && decl->context()->owner()) { //Also create full type-links for the context around AbstractType::Ptr contextType = decl->context()->owner()->abstractType(); IdentifiedType* contextIdType = dynamic_cast(contextType.data()); if(contextIdType && !contextIdType->equals(idType)) { //Create full type information for the context if(!id.isEmpty()) id = id.mid(id.count()-1); htmlIdentifiedType(contextType, contextIdType); modifyHtml() += QStringLiteral("::").toHtmlEscaped(); } } //We leave out the * and & reference and pointer signs, those are added to the end makeLink(id.toString() , DeclarationPointer(idType->declaration(topContext().data())), NavigationAction::NavigateDeclaration ); } else { qCDebug(LANGUAGE) << "could not resolve declaration:" << idType->declarationId().isDirect() << idType->qualifiedIdentifier().toString() << "in top-context" << topContext()->url().str(); modifyHtml() += typeHighlight(type->toString().toHtmlEscaped()); } } void AbstractDeclarationNavigationContext::eventuallyMakeTypeLinks( AbstractType::Ptr type ) { type = typeToShow(type); if( !type ) { modifyHtml() += typeHighlight(QStringLiteral("").toHtmlEscaped()); return; } AbstractType::Ptr target = TypeUtils::targetTypeKeepAliases( type, topContext().data() ); const IdentifiedType* idType = dynamic_cast( target.data() ); qCDebug(LANGUAGE) << "making type-links for" << type->toString(); if( idType && idType->declaration(topContext().data()) ) { ///@todo This is C++ specific, move into subclass if(target->modifiers() & AbstractType::ConstModifier) modifyHtml() += typeHighlight(QStringLiteral("const ")); htmlIdentifiedType(target, idType); //We need to exchange the target type, else template-parameters may confuse this SimpleTypeExchanger exchangeTarget(target, AbstractType::Ptr()); AbstractType::Ptr exchanged = exchangeTarget.exchange(type); if(exchanged) { QString typeSuffixString = exchanged->toString(); QRegExp suffixExp(QStringLiteral("\\&|\\*")); int suffixPos = typeSuffixString.indexOf(suffixExp); if(suffixPos != -1) modifyHtml() += typeHighlight(typeSuffixString.mid(suffixPos)); } } else { if(idType) { qCDebug(LANGUAGE) << "identified type could not be resolved:" << idType->qualifiedIdentifier() << idType->declarationId().isValid() << idType->declarationId().isDirect(); } modifyHtml() += typeHighlight(type->toString().toHtmlEscaped()); } } DeclarationPointer AbstractDeclarationNavigationContext::declaration() const { return d->m_declaration; } QString AbstractDeclarationNavigationContext::identifierHighlight(const QString& identifier, const DeclarationPointer& decl) const { QString ret = nameHighlight(identifier); if (!decl) { return ret; } if (decl->isDeprecated()) { ret = QStringLiteral("") + ret + QStringLiteral(""); } return ret; } QString AbstractDeclarationNavigationContext::stringFromAccess(Declaration::AccessPolicy access) { switch(access) { case Declaration::Private: return QStringLiteral("private"); case Declaration::Protected: return QStringLiteral("protected"); case Declaration::Public: return QStringLiteral("public"); default: break; } return QString(); } QString AbstractDeclarationNavigationContext::stringFromAccess(const DeclarationPointer& decl) { const ClassMemberDeclaration* memberDecl = dynamic_cast(decl.data()); if( memberDecl ) { return stringFromAccess(memberDecl->accessPolicy()); } return QString(); } QString AbstractDeclarationNavigationContext::declarationName( const DeclarationPointer& decl ) const { if( NamespaceAliasDeclaration* alias = dynamic_cast(decl.data()) ) { if( alias->identifier().isEmpty() ) return QLatin1String("using namespace ") + alias->importIdentifier().toString(); else return QLatin1String("namespace ") + alias->identifier().toString() + QLatin1String(" = ") + alias->importIdentifier().toString(); } if( !decl ) return i18nc("A declaration that is unknown", "Unknown"); else return prettyIdentifier(decl).toString(); } QStringList AbstractDeclarationNavigationContext::declarationDetails(const DeclarationPointer& decl) { QStringList details; const AbstractFunctionDeclaration* function = dynamic_cast(decl.data()); const ClassMemberDeclaration* memberDecl = dynamic_cast(decl.data()); if( memberDecl ) { if( memberDecl->isMutable() ) details << QStringLiteral("mutable"); if( memberDecl->isRegister() ) details << QStringLiteral("register"); if( memberDecl->isStatic() ) details << QStringLiteral("static"); if( memberDecl->isAuto() ) details << QStringLiteral("auto"); if( memberDecl->isExtern() ) details << QStringLiteral("extern"); if( memberDecl->isFriend() ) details << QStringLiteral("friend"); } if( decl->isDefinition() ) details << i18nc("tells if a declaration is defining the variable's value", "definition"); if( decl->isExplicitlyDeleted() ) details << QStringLiteral("deleted"); if( memberDecl && memberDecl->isForwardDeclaration() ) details << i18nc("as in c++ forward declaration", "forward"); AbstractType::Ptr t(decl->abstractType()); if( t ) { if( t->modifiers() & AbstractType::ConstModifier ) details << i18nc("a variable that won't change, const", "constant"); if( t->modifiers() & AbstractType::VolatileModifier ) details << QStringLiteral("volatile"); } if( function ) { if( function->isInline() ) details << QStringLiteral("inline"); if( function->isExplicit() ) details << QStringLiteral("explicit"); if( function->isVirtual() ) details << QStringLiteral("virtual"); const ClassFunctionDeclaration* classFunDecl = dynamic_cast(decl.data()); if( classFunDecl ) { if( classFunDecl->isSignal() ) details << QStringLiteral("signal"); if( classFunDecl->isSlot() ) details << QStringLiteral("slot"); if( classFunDecl->isFinal() ) details << QStringLiteral("final"); if( classFunDecl->isConstructor() ) details << QStringLiteral("constructor"); if( classFunDecl->isDestructor() ) details << QStringLiteral("destructor"); if( classFunDecl->isConversionFunction() ) details << QStringLiteral("conversion-function"); if( classFunDecl->isAbstract() ) details << QStringLiteral("abstract"); } } return details; } QString AbstractDeclarationNavigationContext::declarationSizeInformation(const DeclarationPointer& decl) { // Note that ClassMemberDeclaration also includes ClassDeclaration, which uses the sizeOf and alignOf fields, // but normally leaves the bitOffsetOf unset (-1). const ClassMemberDeclaration* memberDecl = dynamic_cast(decl.data()); if (memberDecl && (memberDecl->bitOffsetOf() > 0 || memberDecl->sizeOf() > 0 || memberDecl->alignOf() > 0)) { - QString sizeInfo = "

"; + QString sizeInfo = QStringLiteral("

"); if (memberDecl->bitOffsetOf() >= 0) { const auto byteOffset = memberDecl->bitOffsetOf() / 8; const auto bitOffset = memberDecl->bitOffsetOf() % 8; const QString byteOffsetStr = i18np("1 Byte", "%1 Bytes", byteOffset); const QString bitOffsetStr = bitOffset ? i18np("1 Bit", "%1 Bits", bitOffset) : QString(); sizeInfo += i18n("offset in parent: %1", bitOffset ? i18nc("%1: bytes, %2: bits", "%1, %2", byteOffsetStr, bitOffsetStr) : byteOffsetStr) + QLatin1String("; "); } if (memberDecl->sizeOf() >= 0) { sizeInfo += i18n("size: %1 Bytes", memberDecl->sizeOf()) + QLatin1String("; "); } if (memberDecl->alignOf() >= 0) { sizeInfo += i18n("aligned to: %1 Bytes", memberDecl->alignOf()); } - sizeInfo += "

"; + sizeInfo += QStringLiteral("

"); return sizeInfo; } return QString(); } } diff --git a/kdevplatform/language/duchain/types/unsuretype.cpp b/kdevplatform/language/duchain/types/unsuretype.cpp index d286f5a066..4c4f87890d 100644 --- a/kdevplatform/language/duchain/types/unsuretype.cpp +++ b/kdevplatform/language/duchain/types/unsuretype.cpp @@ -1,121 +1,121 @@ /* Copyright 2009 David Nolden 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 "unsuretype.h" #include "typeregister.h" #include "typesystem.h" namespace KDevelop { REGISTER_TYPE(UnsureType); DEFINE_LIST_MEMBER_HASH(UnsureTypeData, m_types, IndexedType) UnsureType::UnsureType(const KDevelop::UnsureType& rhs): AbstractType(copyData(*rhs.d_func())) { } UnsureType::UnsureType() : AbstractType(createData()) { } void UnsureType::accept0(KDevelop::TypeVisitor* v) const { FOREACH_FUNCTION(const IndexedType& type, d_func()->m_types) { AbstractType::Ptr t = type.abstractType(); v->visit(t.data()); } } KDevelop::AbstractType* UnsureType::clone() const { return new UnsureType(*this); } QString UnsureType::toString() const { QStringList typeNames; typeNames.reserve(d_func()->m_typesSize()); FOREACH_FUNCTION(const IndexedType& type, d_func()->m_types) { AbstractType::Ptr t = type.abstractType(); if(t) typeNames.append(t->toString()); else - typeNames.append(QLatin1String("none")); + typeNames.append(QStringLiteral("none")); } QString ret = QLatin1String("unsure (") + typeNames.join(QLatin1String(", ")) + QLatin1Char(')'); return ret; } bool UnsureType::equals(const KDevelop::AbstractType* rhs) const { const UnsureType* rhsU = dynamic_cast(rhs); if(!rhsU) return false; if(d_func()->typeClassId != rhsU->d_func()->typeClassId) return false; if(d_func()->m_typesSize() != rhsU->d_func()->m_typesSize()) return false; for(uint a = 0; a < d_func()->m_typesSize(); ++a) if(d_func()->m_types()[a] != rhsU->d_func()->m_types()[a]) return false; return KDevelop::AbstractType::equals(rhs); } uint UnsureType::hash() const { KDevHash kdevhash(AbstractType::hash()); FOREACH_FUNCTION(const IndexedType& type, d_func()->m_types) kdevhash << type.hash(); return kdevhash << d_func()->m_typesSize(); } KDevelop::AbstractType::WhichType UnsureType::whichType() const { return TypeUnsure; } void UnsureType::exchangeTypes(KDevelop::TypeExchanger* exchanger) { for(uint a = 0; a < d_func()->m_typesSize(); ++a) { AbstractType::Ptr from = d_func()->m_types()[a].abstractType(); AbstractType::Ptr exchanged = exchanger->exchange(from); if(exchanged != from) d_func_dynamic()->m_typesList()[a] = exchanged->indexed(); } KDevelop::AbstractType::exchangeTypes(exchanger); } void UnsureType::addType(const KDevelop::IndexedType& type) { if ( !d_func_dynamic()->m_typesList().contains(type) ) { d_func_dynamic()->m_typesList().append(type); } } void UnsureType::removeType(const KDevelop::IndexedType& type) { d_func_dynamic()->m_typesList().removeOne(type); } const KDevelop::IndexedType* UnsureType::types() const { return d_func()->m_types(); } uint UnsureType::typesSize() const { return d_func()->m_typesSize(); } UnsureType::UnsureType(KDevelop::UnsureTypeData& data): AbstractType(data) { } } diff --git a/plugins/cmake/cmakebuilddirchooser.cpp b/plugins/cmake/cmakebuilddirchooser.cpp index 5ad586c463..a81561077a 100644 --- a/plugins/cmake/cmakebuilddirchooser.cpp +++ b/plugins/cmake/cmakebuilddirchooser.cpp @@ -1,345 +1,345 @@ /* KDevelop CMake Support * * Copyright 2007 Aleix Pol * * 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 "cmakebuilddirchooser.h" #include "ui_cmakebuilddirchooser.h" #include "cmakeutils.h" #include "debug.h" #include #include #include #include #include #include #include #include #include #include using namespace KDevelop; CMakeBuildDirChooser::CMakeBuildDirChooser(QWidget* parent) : QDialog(parent) { setWindowTitle(i18n("Configure a build directory - %1", ICore::self()->runtimeController()->currentRuntime()->name())); m_buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); m_buttonBox->button(QDialogButtonBox::Ok)->setDefault(true); connect(m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); auto mainWidget = new QWidget(this); auto mainLayout = new QVBoxLayout; setLayout(mainLayout); mainLayout->addWidget(mainWidget); m_chooserUi = new Ui::CMakeBuildDirChooser; m_chooserUi->setupUi(mainWidget); setShowAvailableBuildDirs(false); mainLayout->addWidget(m_buttonBox); m_chooserUi->buildFolder->setMode(KFile::Directory|KFile::ExistingOnly); m_chooserUi->installPrefix->setMode(KFile::Directory|KFile::ExistingOnly); m_extraArgumentsHistory = new CMakeExtraArgumentsHistory(m_chooserUi->extraArguments); connect(m_chooserUi->buildFolder, &KUrlRequester::textChanged, this, &CMakeBuildDirChooser::updated); connect(m_chooserUi->buildType, static_cast(&QComboBox::currentIndexChanged), this, &CMakeBuildDirChooser::updated); connect(m_chooserUi->extraArguments, &KComboBox::editTextChanged, this, &CMakeBuildDirChooser::updated); connect(m_chooserUi->availableBuildDirs, static_cast(&QComboBox::currentIndexChanged), this, &CMakeBuildDirChooser::adoptPreviousBuildDirectory); updated(); } CMakeBuildDirChooser::~CMakeBuildDirChooser() { delete m_extraArgumentsHistory; delete m_chooserUi; } void CMakeBuildDirChooser::setProject( IProject* project ) { m_project = project; KDevelop::Path folder = m_project->path(); QString relative=CMake::projectRootRelative(m_project); folder.cd(relative); m_srcFolder = folder; m_chooserUi->buildFolder->setUrl(KDevelop::proposedBuildFolder(m_srcFolder).toUrl()); setWindowTitle(i18n("Configure a build directory for %1", project->name())); update(); } void CMakeBuildDirChooser::buildDirSettings( const KDevelop::Path& buildDir, QString& srcDir, QString& installDir, QString& buildType) { static const QString srcLine = QStringLiteral("CMAKE_HOME_DIRECTORY:INTERNAL="); static const QString installLine = QStringLiteral("CMAKE_INSTALL_PREFIX:PATH="); static const QString buildLine = QStringLiteral("CMAKE_BUILD_TYPE:STRING="); const Path cachePath(buildDir, QStringLiteral("CMakeCache.txt")); QFile file(cachePath.toLocalFile()); srcDir.clear(); installDir.clear(); buildType.clear(); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { qCWarning(CMAKE) << "Something really strange happened reading" << cachePath; return; } int cnt = 0; while (cnt != 3 && !file.atEnd()) { // note: CMakeCache.txt is UTF8-encoded, also see bug 329305 QString line = QString::fromUtf8(file.readLine().trimmed()); if (line.startsWith(srcLine)) { srcDir = line.mid(srcLine.count()); ++cnt; } if (line.startsWith(installLine)) { installDir = line.mid(installLine.count()); ++cnt; } if (line.startsWith(buildLine)) { buildType = line.mid(buildLine.count()); ++cnt; } } qCDebug(CMAKE) << "The source directory for " << file.fileName() << "is" << srcDir; qCDebug(CMAKE) << "The install directory for " << file.fileName() << "is" << installDir; qCDebug(CMAKE) << "The build type for " << file.fileName() << "is" << buildType; } void CMakeBuildDirChooser::updated() { StatusTypes st; Path chosenBuildFolder(m_chooserUi->buildFolder->url()); bool emptyUrl = chosenBuildFolder.isEmpty(); if( emptyUrl ) st |= BuildFolderEmpty; bool dirEmpty = false, dirExists= false, dirRelative = false; QString srcDir; QString installDir; QString buildType; if(!emptyUrl) { QDir d(chosenBuildFolder.toLocalFile()); dirExists = d.exists(); dirEmpty = dirExists && d.count()<=2; dirRelative = d.isRelative(); if(!dirEmpty && dirExists && !dirRelative) { bool hasCache=QFile::exists(Path(chosenBuildFolder, QStringLiteral("CMakeCache.txt")).toLocalFile()); if(hasCache) { QString proposed=m_srcFolder.toLocalFile(); buildDirSettings(chosenBuildFolder, srcDir, installDir, buildType); if(!srcDir.isEmpty()) { auto rt = ICore::self()->runtimeController()->currentRuntime(); if(QDir(rt->pathInHost(Path(srcDir)).toLocalFile()).canonicalPath() == QDir(proposed).canonicalPath()) { st |= CorrectBuildDir | BuildDirCreated; } } else { qCWarning(CMAKE) << "maybe you are trying a damaged CMakeCache.txt file. Proper: "; } if(!installDir.isEmpty() && QDir(installDir).exists()) { m_chooserUi->installPrefix->setUrl(QUrl::fromLocalFile(installDir)); } m_chooserUi->buildType->setCurrentText(buildType); } } if(m_alreadyUsed.contains(chosenBuildFolder.toLocalFile()) && !m_chooserUi->availableBuildDirs->isEnabled()) { st=DirAlreadyCreated; } } else { setStatus(i18n("You need to specify a build directory."), false); return; } if(st & (BuildDirCreated | CorrectBuildDir)) { setStatus(i18n("Using an already created build directory."), true); m_chooserUi->installPrefix->setEnabled(false); m_chooserUi->buildType->setEnabled(false); } else { bool correct = (dirEmpty || !dirExists) && !(st & DirAlreadyCreated) && !dirRelative; if(correct) { st |= CorrectBuildDir; setStatus(i18n("Creating a new build directory."), true); } else { //Useful to explain what's going wrong if(st & DirAlreadyCreated) setStatus(i18n("Build directory already configured."), false); else if (!srcDir.isEmpty()) setStatus(i18n("This build directory is for %1, " "but the project directory is %2.", srcDir, m_srcFolder.toLocalFile()), false); else if(dirRelative) setStatus(i18n("You may not select a relative build directory."), false); else if(!dirEmpty) setStatus(i18n("The selected build directory is not empty."), false); } m_chooserUi->installPrefix->setEnabled(correct); m_chooserUi->buildType->setEnabled(correct); } } void CMakeBuildDirChooser::setCMakeExecutable(const Path& path) { m_chooserUi->cmakeExecutable->setUrl(path.toUrl()); updated(); } void CMakeBuildDirChooser::setInstallPrefix(const Path& path) { m_chooserUi->installPrefix->setUrl(path.toUrl()); updated(); } void CMakeBuildDirChooser::setBuildFolder(const Path& path) { m_chooserUi->buildFolder->setUrl(path.toUrl()); updated(); } void CMakeBuildDirChooser::setBuildType(const QString& s) { m_chooserUi->buildType->addItem(s); m_chooserUi->buildType->setCurrentIndex(m_chooserUi->buildType->findText(s)); updated(); } void CMakeBuildDirChooser::setAlreadyUsed (const QStringList & used) { m_chooserUi->availableBuildDirs->addItems(used); m_alreadyUsed = used; updated(); } void CMakeBuildDirChooser::setExtraArguments(const QString& args) { m_chooserUi->extraArguments->setEditText(args); updated(); } void CMakeBuildDirChooser::setStatus(const QString& message, bool canApply) { KColorScheme scheme(QPalette::Normal); KColorScheme::ForegroundRole role; if (canApply) { role = KColorScheme::PositiveText; } else { role = KColorScheme::NegativeText; } - m_chooserUi->status->setText(QStringLiteral("%2").arg(scheme.foreground(role).color().name()).arg(message)); + m_chooserUi->status->setText(QStringLiteral("%2").arg(scheme.foreground(role).color().name(), message)); auto okButton = m_buttonBox->button(QDialogButtonBox::Ok); okButton->setEnabled(canApply); if (canApply) { auto cancelButton = m_buttonBox->button(QDialogButtonBox::Cancel); cancelButton->clearFocus(); } } void CMakeBuildDirChooser::adoptPreviousBuildDirectory(int index) { if (index > 0) { Q_ASSERT(m_project); m_chooserUi->cmakeExecutable->setUrl(CMake::currentCMakeExecutable(m_project, index -1).toUrl()); m_chooserUi->buildFolder->setUrl(CMake::currentBuildDir(m_project, index -1).toUrl()); m_chooserUi->installPrefix->setUrl(CMake::currentInstallDir(m_project, index -1).toUrl()); m_chooserUi->buildType->setCurrentText(CMake::currentBuildType(m_project, index -1)); m_chooserUi->extraArguments->setCurrentText(CMake::currentExtraArguments(m_project, index -1)); } m_chooserUi->label_5->setEnabled(index == 0); m_chooserUi->cmakeExecutable->setEnabled(index == 0); m_chooserUi->label_3->setEnabled(index == 0); m_chooserUi->buildFolder->setEnabled(index == 0); m_chooserUi->label->setEnabled(index == 0); m_chooserUi->installPrefix->setEnabled(index == 0); m_chooserUi->label_2->setEnabled(index == 0); m_chooserUi->buildType->setEnabled(index == 0); m_chooserUi->status->setEnabled(index == 0); m_chooserUi->extraArguments->setEnabled(index == 0); m_chooserUi->label_4->setEnabled(index == 0); } bool CMakeBuildDirChooser::reuseBuilddir() { return m_chooserUi->availableBuildDirs->currentIndex() > 0; } int CMakeBuildDirChooser::alreadyUsedIndex() const { return m_chooserUi->availableBuildDirs->currentIndex() - 1; } void CMakeBuildDirChooser::setShowAvailableBuildDirs(bool show) { m_chooserUi->availableLabel->setVisible(show); m_chooserUi->availableBuildDirs->setVisible(show); } Path CMakeBuildDirChooser::cmakeExecutable() const { return Path(m_chooserUi->cmakeExecutable->url()); } Path CMakeBuildDirChooser::installPrefix() const { return Path(m_chooserUi->installPrefix->url()); } Path CMakeBuildDirChooser::buildFolder() const { return Path(m_chooserUi->buildFolder->url()); } QString CMakeBuildDirChooser::buildType() const { return m_chooserUi->buildType->currentText(); } QString CMakeBuildDirChooser::extraArguments() const { return m_chooserUi->extraArguments->currentText(); } diff --git a/plugins/cmake/settings/cmakecachedelegate.cpp b/plugins/cmake/settings/cmakecachedelegate.cpp index 21258803c6..4c2e109f24 100644 --- a/plugins/cmake/settings/cmakecachedelegate.cpp +++ b/plugins/cmake/settings/cmakecachedelegate.cpp @@ -1,204 +1,204 @@ /* KDevelop CMake Support * * Copyright 2008 Aleix Pol * * 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 "cmakecachedelegate.h" #include #include #include #include #include #include CMakeCacheDelegate::CMakeCacheDelegate(QObject * parent) : QItemDelegate(parent) { m_sample=new KUrlRequester(); } CMakeCacheDelegate::~CMakeCacheDelegate() { delete m_sample; } QWidget * CMakeCacheDelegate::createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const { QWidget *ret=nullptr; if(index.column()==2) { QModelIndex typeIdx=index.sibling(index.row(), 1); QString type=typeIdx.model()->data(typeIdx, Qt::DisplayRole).toString(); if(type==QLatin1String("BOOL")) { QCheckBox* box=new QCheckBox(parent); connect(box, &QCheckBox::toggled, this, &CMakeCacheDelegate::checkboxToggled); ret = box; } else if(type==QLatin1String("STRING")) { QModelIndex stringsIdx=index.sibling(index.row(), 5); QString strings=typeIdx.model()->data(stringsIdx, Qt::DisplayRole).toString(); if (!strings.isEmpty()) { QComboBox* comboBox = new QComboBox(parent); comboBox->setEditable(true); comboBox->addItems(strings.split(';')); ret = comboBox; } else { ret=QItemDelegate::createEditor(parent, option, index); } } else if(type==QLatin1String("PATH") || type==QLatin1String("FILEPATH")) { KUrlRequester *r=new KUrlRequester(parent); if(type==QLatin1String("FILEPATH")) r->setMode(KFile::File); else r->setMode(KFile::Directory | KFile::ExistingOnly); emit const_cast(this)->sizeHintChanged ( index ); qCDebug(CMAKE) << "EMITINT!" << index; ret=r; } else { ret=QItemDelegate::createEditor(parent, option, index); } if(!ret) qCDebug(CMAKE) << "Did not recognize type " << type; } return ret; } void CMakeCacheDelegate::setEditorData(QWidget * editor, const QModelIndex & index) const { if(index.column()==2) { QModelIndex typeIdx=index.sibling(index.row(), 1); QString type=index.model()->data(typeIdx, Qt::DisplayRole).toString(); QString value=index.model()->data(index, Qt::DisplayRole).toString(); if(type==QLatin1String("BOOL")) { QCheckBox *boolean=qobject_cast(editor); boolean->setCheckState(value==QLatin1String("ON") ? Qt::Checked : Qt::Unchecked); } else if(type==QLatin1String("PATH") || type==QLatin1String("FILEPATH")) { KUrlRequester *url=qobject_cast(editor); url->setUrl(QUrl(value)); } else { QItemDelegate::setEditorData(editor, index); } } else qCDebug(CMAKE) << "Error. trying to edit a read-only field"; } void CMakeCacheDelegate::setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const { if(index.column()==2) { QModelIndex typeIdx=index.sibling(index.row(), 1); QString type=model->data(typeIdx, Qt::DisplayRole).toString(); QString value; if(type==QLatin1String("BOOL")) { QCheckBox *boolean=qobject_cast(editor); - value = boolean->isChecked() ? "ON" : "OFF"; + value = boolean->isChecked() ? QStringLiteral("ON") : QStringLiteral("OFF"); } else if(type==QLatin1String("PATH") || type==QLatin1String("FILEPATH")) { KUrlRequester *urlreq=qobject_cast(editor); value = urlreq->url().toDisplayString(QUrl::StripTrailingSlash | QUrl::PreferLocalFile); //CMake usually don't put it } else { QItemDelegate::setModelData(editor, model, index); return; } model->setData(index, value, Qt::DisplayRole); } else qCDebug(CMAKE) << "Error. trying to edit a read-only field"; } void CMakeCacheDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const { if(index.column()==2) { QModelIndex typeIdx=index.sibling(index.row(), 1); QString type=index.model()->data(typeIdx, Qt::DisplayRole).toString(); if(type==QLatin1String("BOOL")) return; } QItemDelegate::paint(painter, option, index); } QSize CMakeCacheDelegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index ) const { // qCDebug(CMAKE) << "calculant" << index << bool(option.state & QStyle::State_Editing); QSize ret=QItemDelegate::sizeHint(option, index); if(index.column()==2 && option.state & QStyle::State_Editing) { QModelIndex typeIdx=index.sibling(index.row(), 1); QString type=index.model()->data(typeIdx, Qt::DisplayRole).toString(); if(type==QLatin1String("PATH")) { ret.setHeight(m_sample->sizeHint().height()); } } return ret; } void CMakeCacheDelegate::checkboxToggled() { // whenever the check box gets toggled, we directly want to set the // model data which is done by closing the editor which in turn // calls setModelData. otherwise, the behavior is quite confusing, see e.g. // https://bugs.kde.org/show_bug.cgi?id=304352 QCheckBox* editor = qobject_cast(sender()); Q_ASSERT(editor); - closeEditor(editor); + emit closeEditor(editor); } void CMakeCacheDelegate::closingEditor(QWidget * editor, QAbstractItemDelegate::EndEditHint hint) { Q_UNUSED(editor); Q_UNUSED(hint); qCDebug(CMAKE) << "closing..."; } // void CMakeCacheDelegate::updateEditorGeometry ( QWidget * editor, const QStyleOptionViewItem & option, // const QModelIndex & index ) const // { // if(index.column()==2) // { // QModelIndex typeIdx=index.sibling(index.row(), 1); // QString type=index.model()->data(typeIdx, Qt::DisplayRole).toString(); // if(type=="PATH") // { // KUrlRequester* urlreq=qobject_cast(editor); // urlreq->setGeometry(QRect(option.rect.topLeft(), urlreq->sizeHint())); // return; // } // } // QItemDelegate::updateEditorGeometry( editor, option, index ); // } diff --git a/plugins/cmake/settings/cmakecachemodel.cpp b/plugins/cmake/settings/cmakecachemodel.cpp index f7fac31c0b..dc39899852 100644 --- a/plugins/cmake/settings/cmakecachemodel.cpp +++ b/plugins/cmake/settings/cmakecachemodel.cpp @@ -1,226 +1,226 @@ /* KDevelop CMake Support * * Copyright 2007 Aleix Pol * * 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 "cmakecachemodel.h" #include #include #include "cmakecachereader.h" #include //4 columns: name, type, value, comment //name:type=value - comment CMakeCacheModel::CMakeCacheModel(QObject *parent, const KDevelop::Path &path) : QStandardItemModel(parent), m_filePath(path) { read(); } void CMakeCacheModel::reset() { - emit beginResetModel(); + beginResetModel(); clear(); m_internal.clear(); m_modifiedRows.clear(); read(); - emit endResetModel(); + endResetModel(); } void CMakeCacheModel::read() { // Set headers const QStringList labels{ i18n("Name"), i18n("Type"), i18n("Value"), i18n("Comment"), i18n("Advanced"), i18n("Strings"), }; setHorizontalHeaderLabels(labels); QFile file(m_filePath.toLocalFile()); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { qCDebug(CMAKE) << "error. Could not find the file"; return; } int currentIdx=0; QStringList currentComment; QTextStream in(&file); QHash variablePos; while (!in.atEnd()) { QString line = in.readLine().trimmed(); if(line.startsWith(QLatin1String("//"))) currentComment += line.mid(2); else if(!line.isEmpty() && !line.startsWith('#')) //it is a variable { CacheLine c; c.readLine(line); if(c.isCorrect()) { QString name=c.name(), flag=c.flag(); QString type=c.type(); QString value=c.value(); const QList lineItems{ new QStandardItem(name), new QStandardItem(type), new QStandardItem(value), new QStandardItem(currentComment.join(QLatin1Char('\n'))), }; if(flag==QLatin1String("INTERNAL")) { m_internal.insert(name); } else if(flag==QLatin1String("ADVANCED") || flag==QLatin1String("STRINGS")) { if(variablePos.contains(name)) { int pos=variablePos[name]; // if the flag is not ADVANCED, it's STRINGS. // The latter is stored in column 5 int column = flag==QLatin1String("ADVANCED") ? 4 : 5; QStandardItem *p = item(pos, column); if(!p) { p=new QStandardItem(value); setItem(pos, column, p); } else { p->setText(value); } } else { qCDebug(CMAKE) << "Flag for an unknown variable"; } } if(!flag.isEmpty()) { lineItems[0]->setText(lineItems[0]->text()+'-'+flag); } insertRow(currentIdx, lineItems); if (!variablePos.contains(name)) { variablePos[name]=currentIdx; } currentIdx++; currentComment.clear(); } } else if(line.startsWith('#') && line.contains(QLatin1String("INTERNAL"))) { m_internalBegin=currentIdx; // qCDebug(CMAKE) << "Comment: " << line << " -.- " << currentIdx; } else if(!line.startsWith('#') && !line.isEmpty()) { qCDebug(CMAKE) << "unrecognized cache line: " << line; } } } bool CMakeCacheModel::setData(const QModelIndex& index, const QVariant& value, int role) { bool ret = QStandardItemModel::setData(index, value, role); if (ret) { m_modifiedRows.insert(index.row()); } return ret; } QVariantMap CMakeCacheModel::changedValues() const { QVariantMap ret; for(int i=0; itext()+':'+type->text(), valu->text()); } return ret; } QString CMakeCacheModel::value(const QString & varName) const { for(int i=0; itext()==varName) { QStandardItem* valu = item(i, 2); return valu->text(); } } return QString(); } bool CMakeCacheModel::isAdvanced(int i) const { QStandardItem *p=item(i, 4); bool isAdv= (p!=nullptr) || i>m_internalBegin; if(!isAdv) { p=item(i, 1); isAdv = p->text()==QLatin1String("INTERNAL") || p->text()==QLatin1String("STATIC"); } return isAdv || m_internal.contains(item(i,0)->text()); } bool CMakeCacheModel::isInternal(int i) const { bool isInt= i>m_internalBegin; return isInt; } QList< QModelIndex > CMakeCacheModel::persistentIndices() const { QList< QModelIndex > ret; for(int i=0; itext()==QLatin1String("BOOL")) { QStandardItem* valu = item(i, 2); ret.append(valu->index()); } } return ret; } KDevelop::Path CMakeCacheModel::filePath() const { return m_filePath; } diff --git a/plugins/cmake/settings/cmakepreferences.cpp b/plugins/cmake/settings/cmakepreferences.cpp index f848e874a7..dd170ec34c 100644 --- a/plugins/cmake/settings/cmakepreferences.cpp +++ b/plugins/cmake/settings/cmakepreferences.cpp @@ -1,405 +1,405 @@ /* KDevelop CMake Support * * Copyright 2006 Matt Rogers * Copyright 2007-2008 Aleix Pol * * 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 "cmakepreferences.h" #include #include #include #include #include #include #include #include #include #include "ui_cmakebuildsettings.h" #include "cmakecachedelegate.h" #include "cmakebuilddirchooser.h" #include "cmakebuilderconfig.h" #include #include #include #include #include #include using namespace KDevelop; CMakePreferences::CMakePreferences(IPlugin* plugin, const ProjectConfigOptions& options, QWidget* parent) : ConfigPage(plugin, nullptr, parent), m_project(options.project), m_currentModel(nullptr) { m_prefsUi = new Ui::CMakeBuildSettings; m_prefsUi->setupUi(this); m_prefsUi->cacheList->setItemDelegate(new CMakeCacheDelegate(m_prefsUi->cacheList)); m_prefsUi->cacheList->setSelectionMode(QAbstractItemView::SingleSelection); m_prefsUi->cacheList->horizontalHeader()->setStretchLastSection(true); m_prefsUi->cacheList->verticalHeader()->hide(); // configure the extraArguments widget to span the advanced box width but not // expand the dialog to the width of the longest element in the argument history. // static_cast needed because KComboBox::minimumSizeHint() override mistakingly made it protected m_prefsUi->extraArguments->setMinimumWidth(static_cast(m_prefsUi->extraArguments)->minimumSizeHint().width()); m_extraArgumentsHistory = new CMakeExtraArgumentsHistory(m_prefsUi->extraArguments); connect(m_prefsUi->buildDirs, static_cast(&KComboBox::currentIndexChanged), this, &CMakePreferences::buildDirChanged); connect(m_prefsUi->showInternal, &QCheckBox::stateChanged, this, &CMakePreferences::showInternal); connect(m_prefsUi->addBuildDir, &QPushButton::pressed, this, &CMakePreferences::createBuildDir); connect(m_prefsUi->removeBuildDir, &QPushButton::pressed, this, &CMakePreferences::removeBuildDir); connect(m_prefsUi->showAdvanced, &QPushButton::toggled, this, &CMakePreferences::showAdvanced); connect(m_prefsUi->environment, &EnvironmentSelectionWidget::currentProfileChanged, this, &CMakePreferences::changed); connect(m_prefsUi->configureEnvironment, &EnvironmentConfigureButton::environmentConfigured, this, &CMakePreferences::changed); connect(m_prefsUi->installationPrefix, &KUrlRequester::textChanged, this, &CMakePreferences::changed); connect(m_prefsUi->buildType, static_cast(&QComboBox::currentIndexChanged), this, &CMakePreferences::changed); connect(m_prefsUi->buildType, &QComboBox::currentTextChanged, this, &CMakePreferences::changed); connect(m_prefsUi->extraArguments, &KComboBox::currentTextChanged, this, &CMakePreferences::changed); connect(m_prefsUi->extraArguments, &KComboBox::editTextChanged, this, &CMakePreferences::changed); connect(m_prefsUi->cMakeExecutable, &KUrlRequester::textChanged, this, &CMakePreferences::changed); showInternal(m_prefsUi->showInternal->checkState()); m_subprojFolder = Path(options.projectTempFile).parent(); qCDebug(CMAKE) << "Source folder: " << m_srcFolder << options.projectTempFile; // foreach(const QVariant &v, args) // { // qCDebug(CMAKE) << "arg: " << v.toString(); // } m_prefsUi->configureEnvironment->setSelectionWidget(m_prefsUi->environment); m_prefsUi->showAdvanced->setChecked(false); showAdvanced(false); reset(); // load the initial values } CMakePreferences::~CMakePreferences() { CMake::removeOverrideBuildDirIndex(m_project); delete m_extraArgumentsHistory; delete m_prefsUi; } void CMakePreferences::initAdvanced() { m_prefsUi->environment->setCurrentProfile( CMake::currentEnvironment(m_project) ); m_prefsUi->installationPrefix->setText(CMake::currentInstallDir(m_project).toLocalFile()); m_prefsUi->installationPrefix->setMode(KFile::Directory); const QString buildType = CMake::currentBuildType(m_project); if (m_prefsUi->buildType->findText(buildType) == -1) { m_prefsUi->buildType->addItem(buildType); } m_prefsUi->buildType->setCurrentIndex(m_prefsUi->buildType->findText(buildType)); m_prefsUi->extraArguments->setEditText(CMake::currentExtraArguments(m_project)); m_prefsUi->cMakeExecutable->setText(CMake::currentCMakeExecutable(m_project).toLocalFile()); } void CMakePreferences::reset() { qCDebug(CMAKE) << "********loading"; m_prefsUi->buildDirs->clear(); m_prefsUi->buildDirs->addItems( CMake::allBuildDirs(m_project) ); CMake::removeOverrideBuildDirIndex(m_project); // addItems() triggers buildDirChanged(), compensate for it m_prefsUi->buildDirs->setCurrentIndex( CMake::currentBuildDirIndex(m_project) ); initAdvanced(); m_srcFolder = m_project->path(); m_prefsUi->removeBuildDir->setEnabled(m_prefsUi->buildDirs->count()!=0); // QString cmDir=group.readEntry("CMakeDirectory"); // m_prefsUi->kcfg_cmakeDir->setUrl(QUrl(cmDir)); // qCDebug(CMAKE) << "cmakedir" << cmDir; } void CMakePreferences::apply() { qCDebug(CMAKE) << "*******saving"; // the build directory list is incrementally maintained through createBuildDir() and removeBuildDir(). // We won't rewrite it here based on the data from m_prefsUi->buildDirs. CMake::removeOverrideBuildDirIndex( m_project, true ); // save current selection int savedBuildDir = CMake::currentBuildDirIndex(m_project); if (savedBuildDir < 0) { // no build directory exists: skip any writing to config file as well as configuring return; } CMake::setCurrentEnvironment( m_project, m_prefsUi->environment->currentProfile() ); CMake::setCurrentInstallDir( m_project, Path(m_prefsUi->installationPrefix->text()) ); const QString buildType = m_prefsUi->buildType->currentText(); if (m_prefsUi->buildType->findText(buildType) == -1) { m_prefsUi->buildType->addItem(buildType); } CMake::setCurrentBuildType( m_project, buildType ); CMake::setCurrentExtraArguments( m_project, m_prefsUi->extraArguments->currentText() ); CMake::setCurrentCMakeExecutable( m_project, Path(m_prefsUi->cMakeExecutable->text()) ); qCDebug(CMAKE) << "writing to cmake config: using builddir " << CMake::currentBuildDirIndex(m_project); qCDebug(CMAKE) << "writing to cmake config: builddir path " << CMake::currentBuildDir(m_project); qCDebug(CMAKE) << "writing to cmake config: installdir " << CMake::currentInstallDir(m_project); qCDebug(CMAKE) << "writing to cmake config: build type " << CMake::currentBuildType(m_project); qCDebug(CMAKE) << "writing to cmake config: cmake executable " << CMake::currentCMakeExecutable(m_project); qCDebug(CMAKE) << "writing to cmake config: environment " << CMake::currentEnvironment(m_project); //We run cmake on the builddir to generate it configure(); } void CMakePreferences::defaults() { // do nothing } void CMakePreferences::configureCacheView() { // Sets up the cache view after model re-creation/reset. // Emits changed(false) because model re-creation probably means // mass programmatical invocation of itemChanged(), which invokes changed(true) - which is not what we want. m_prefsUi->cacheList->setModel(m_currentModel); m_prefsUi->cacheList->hideColumn(1); m_prefsUi->cacheList->hideColumn(3); m_prefsUi->cacheList->hideColumn(4); m_prefsUi->cacheList->hideColumn(5); m_prefsUi->cacheList->horizontalHeader()->resizeSection(0, 200); if( m_currentModel ) { m_prefsUi->cacheList->setEnabled( true ); foreach(const QModelIndex & idx, m_currentModel->persistentIndices()) { m_prefsUi->cacheList->openPersistentEditor(idx); } } else { m_prefsUi->cacheList->setEnabled( false ); } showInternal(m_prefsUi->showInternal->checkState()); } void CMakePreferences::updateCache(const Path &newBuildDir) { const Path file = newBuildDir.isValid() ? Path(newBuildDir, QStringLiteral("CMakeCache.txt")) : Path(); if(QFile::exists(file.toLocalFile())) { if (m_currentModel) { m_currentModel->deleteLater(); } m_currentModel = new CMakeCacheModel(this, file); configureCacheView(); connect(m_currentModel, &CMakeCacheModel::itemChanged, this, &CMakePreferences::cacheEdited); connect(m_currentModel, &CMakeCacheModel::modelReset, this, &CMakePreferences::configureCacheView); connect(m_prefsUi->cacheList->selectionModel(), &QItemSelectionModel::currentChanged, this, &CMakePreferences::listSelectionChanged); } else { disconnect(m_prefsUi->cacheList->selectionModel(), &QItemSelectionModel::currentChanged, this, nullptr); if (m_currentModel) { m_currentModel->deleteLater(); m_currentModel = nullptr; } configureCacheView(); } if( !m_currentModel ) emit changed(); } void CMakePreferences::listSelectionChanged(const QModelIndex & index, const QModelIndex& ) { qCDebug(CMAKE) << "item " << index << " selected"; QModelIndex idx = index.sibling(index.row(), 3); QModelIndex idxType = index.sibling(index.row(), 1); QString comment=QStringLiteral("%1. %2") - .arg(m_currentModel->itemFromIndex(idxType)->text()) - .arg(m_currentModel->itemFromIndex(idx)->text()); + .arg(m_currentModel->itemFromIndex(idxType)->text(), + m_currentModel->itemFromIndex(idx)->text()); m_prefsUi->commentText->setText(comment); } void CMakePreferences::showInternal(int state) { if(!m_currentModel) return; bool showAdv=(state == Qt::Checked); for(int i=0; irowCount(); i++) { bool hidden=m_currentModel->isInternal(i) || (!showAdv && m_currentModel->isAdvanced(i)); m_prefsUi->cacheList->setRowHidden(i, hidden); } } void CMakePreferences::buildDirChanged(int index) { CMake::setOverrideBuildDirIndex( m_project, index ); const Path buildDir = CMake::currentBuildDir(m_project); initAdvanced(); updateCache(buildDir); qCDebug(CMAKE) << "builddir Changed" << buildDir; emit changed(); } void CMakePreferences::cacheUpdated() { const Path buildDir = CMake::currentBuildDir(m_project); updateCache(buildDir); qCDebug(CMAKE) << "cache updated for" << buildDir; } void CMakePreferences::createBuildDir() { CMakeBuildDirChooser bdCreator; bdCreator.setProject( m_project ); // NOTE: (on removing the trailing slashes) // Generally, we have no clue about how shall a trailing slash look in the current system. // Moreover, the slash may be a part of the filename. // It may be '/' or '\', so maybe should we rely on CMake::allBuildDirs() for returning well-formed paths? QStringList used = CMake::allBuildDirs( m_project ); bdCreator.setAlreadyUsed(used); bdCreator.setCMakeExecutable(Path(CMakeBuilderSettings::self()->cmakeExecutable().toLocalFile())); if(bdCreator.exec()) { int addedBuildDirIndex = m_prefsUi->buildDirs->count(); // 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 " << bdCreator.buildFolder(); qCDebug(CMAKE) << "adding to cmake config: installdir " << bdCreator.installPrefix(); qCDebug(CMAKE) << "adding to cmake config: extra args" << bdCreator.extraArguments(); qCDebug(CMAKE) << "adding to cmake config: build type " << bdCreator.buildType(); qCDebug(CMAKE) << "adding to cmake config: cmake executable " << bdCreator.cmakeExecutable(); qCDebug(CMAKE) << "adding to cmake config: environment empty"; CMake::setOverrideBuildDirIndex( m_project, addedBuildDirIndex ); CMake::setBuildDirCount( m_project, addedBuildDirIndex + 1 ); CMake::setCurrentBuildDir( m_project, bdCreator.buildFolder() ); CMake::setCurrentInstallDir( m_project, bdCreator.installPrefix() ); CMake::setCurrentExtraArguments( m_project, bdCreator.extraArguments() ); CMake::setCurrentBuildType( m_project, bdCreator.buildType() ); CMake::setCurrentCMakeExecutable(m_project, bdCreator.cmakeExecutable()); CMake::setCurrentEnvironment( m_project, QString() ); QString newbuilddir = bdCreator.buildFolder().toLocalFile(); m_prefsUi->buildDirs->addItem( newbuilddir ); m_prefsUi->buildDirs->setCurrentIndex( addedBuildDirIndex ); m_prefsUi->removeBuildDir->setEnabled( true ); qCDebug(CMAKE) << "Emitting changed signal for cmake kcm"; emit changed(); } //TODO: Save it for next runs } void CMakePreferences::removeBuildDir() { int curr=m_prefsUi->buildDirs->currentIndex(); if(curr < 0) return; Path removedPath = CMake::currentBuildDir( m_project ); QString removed = removedPath.toLocalFile(); if(QDir(removed).exists()) { KMessageBox::ButtonCode ret = KMessageBox::warningYesNo(this, i18n("The %1 directory is about to be removed in KDevelop's list.\n" "Do you want KDevelop to remove it in the file system as well?", removed)); if(ret == KMessageBox::Yes) { auto deleteJob = KIO::del(removedPath.toUrl()); KJobWidgets::setWindow(deleteJob, this); if (!deleteJob->exec()) KMessageBox::error(this, i18n("Could not remove: %1", removed)); } } qCDebug(CMAKE) << "removing from cmake config: using builddir " << curr; qCDebug(CMAKE) << "removing from cmake config: builddir path " << removedPath; qCDebug(CMAKE) << "removing from cmake config: installdir " << CMake::currentInstallDir( m_project ); qCDebug(CMAKE) << "removing from cmake config: extra args" << CMake::currentExtraArguments( m_project ); qCDebug(CMAKE) << "removing from cmake config: buildtype " << CMake::currentBuildType( m_project ); qCDebug(CMAKE) << "removing from cmake config: cmake executable " << CMake::currentCMakeExecutable(m_project); qCDebug(CMAKE) << "removing from cmake config: environment " << CMake::currentEnvironment( m_project ); CMake::removeBuildDirConfig(m_project); m_prefsUi->buildDirs->removeItem( curr ); // this triggers buildDirChanged() if(m_prefsUi->buildDirs->count()==0) m_prefsUi->removeBuildDir->setEnabled(false); emit changed(); } void CMakePreferences::configure() { IProjectBuilder *b=m_project->buildSystemManager()->builder(); KJob* job=b->configure(m_project); if( m_currentModel ) { QVariantMap map = m_currentModel->changedValues(); job->setProperty("extraCMakeCacheValues", map); connect(job, &KJob::finished, m_currentModel, &CMakeCacheModel::reset); } else { connect(job, &KJob::finished, this, &CMakePreferences::cacheUpdated); } connect(job, &KJob::finished, m_project, &IProject::reloadModel); ICore::self()->runController()->registerJob(job); } void CMakePreferences::showAdvanced(bool v) { qCDebug(CMAKE) << "toggle pressed: " << v; m_prefsUi->advancedBox->setHidden(!v); } QString CMakePreferences::name() const { return i18n("CMake"); } QString CMakePreferences::fullName() const { return i18n("Configure CMake settings"); } QIcon CMakePreferences::icon() const { return QIcon::fromTheme("cmake"); } diff --git a/plugins/cmakebuilder/cmakejob.cpp b/plugins/cmakebuilder/cmakejob.cpp index f1c23e1697..5396ffce69 100644 --- a/plugins/cmakebuilder/cmakejob.cpp +++ b/plugins/cmakebuilder/cmakejob.cpp @@ -1,142 +1,142 @@ /* KDevelop CMake Support * * Copyright 2006-2007 Andreas Pakulat * Copyright 2008 Hamish Rodda * * 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 "cmakejob.h" #include #include #include #include #include #include #include #include #include #include #include "cmakeutils.h" #include "debug.h" using namespace KDevelop; CMakeJob::CMakeJob(QObject* parent) : OutputExecuteJob(parent) { setCapabilities( Killable ); setFilteringStrategy( OutputModel::CompilerFilter ); setProperties( NeedWorkingDirectory | PortableMessages | DisplayStderr | IsBuilderHint ); setToolTitle( i18n("CMake") ); setStandardToolView( KDevelop::IOutputView::BuildView ); setBehaviours(KDevelop::IOutputView::AllowUserClose | KDevelop::IOutputView::AutoScroll ); } void CMakeJob::start() { qCDebug(KDEV_CMAKEBUILDER) << "Configuring cmake" << workingDirectory(); if( !m_project ) { setError(NoProjectError); setErrorText(QStringLiteral("Internal error: no project specified to configure.")); emitResult(); return; } QDir::temp().mkpath(workingDirectory().toLocalFile()); CMake::updateConfig( m_project, CMake::currentBuildDirIndex(m_project) ); OutputExecuteJob::start(); } QUrl CMakeJob::workingDirectory() const { KDevelop::Path path = CMake::currentBuildDir( m_project ); qCDebug(KDEV_CMAKEBUILDER) << "builddir: " << path; Q_ASSERT(path.isValid()); //We cannot get the project folder as a build directory! return path.toUrl(); } QStringList CMakeJob::commandLine() const { QStringList args; args << CMake::currentCMakeExecutable( m_project ).toLocalFile(); args << QStringLiteral("-DCMAKE_EXPORT_COMPILE_COMMANDS=ON"); QString installDir = CMake::currentInstallDir( m_project ).toLocalFile(); if( !installDir.isEmpty() ) { args << QStringLiteral("-DCMAKE_INSTALL_PREFIX=%1").arg(installDir); } QString buildType = CMake::currentBuildType( m_project ); if( !buildType.isEmpty() ) { args << QStringLiteral("-DCMAKE_BUILD_TYPE=%1").arg(buildType); } QVariantMap cacheArgs = property("extraCMakeCacheValues").toMap(); for( auto it = cacheArgs.constBegin(), itEnd = cacheArgs.constEnd(); it!=itEnd; ++it) { - args << QStringLiteral("-D%1=%2").arg(it.key()).arg(it.value().toString()); + args << QStringLiteral("-D%1=%2").arg(it.key(), it.value().toString()); } auto rt = ICore::self()->runtimeController()->currentRuntime(); //if we are creating a new build directory, we'll want to specify the generator QDir builddir(rt->pathInRuntime(CMake::currentBuildDir( m_project )).toLocalFile()); if(!builddir.exists() || !builddir.exists(QStringLiteral("CMakeCache.txt"))) { CMakeBuilderSettings::self()->load(); args << QStringLiteral("-G") << CMake::defaultGenerator(); } QString cmakeargs = CMake::currentExtraArguments( m_project ); if( !cmakeargs.isEmpty() ) { KShell::Errors err; QStringList tmp = KShell::splitArgs( cmakeargs, KShell::TildeExpand | KShell::AbortOnMeta, &err ); if( err == KShell::NoError ) { args += tmp; } else { qCWarning(KDEV_CMAKEBUILDER) << "Ignoring cmake Extra arguments"; if( err == KShell::BadQuoting ) { qCWarning(KDEV_CMAKEBUILDER) << "CMake arguments badly quoted:" << cmakeargs; } else { qCWarning(KDEV_CMAKEBUILDER) << "CMake arguments had meta character:" << cmakeargs; } } } args << rt->pathInRuntime(CMake::projectRoot( m_project )).toLocalFile(); return args; } QString CMakeJob::environmentProfile() const { return CMake::currentEnvironment( m_project ); } void CMakeJob::setProject(KDevelop::IProject* project) { m_project = project; if (m_project) setJobName( i18n("CMake: %1", m_project->name()) ); } diff --git a/plugins/cppcheck/CMakeLists.txt b/plugins/cppcheck/CMakeLists.txt index 8e77f72b2d..632ab68f93 100644 --- a/plugins/cppcheck/CMakeLists.txt +++ b/plugins/cppcheck/CMakeLists.txt @@ -1,63 +1,69 @@ add_definitions(-DTRANSLATION_DOMAIN=\"kdevcppcheck\") +add_definitions( + -DQT_NO_CAST_FROM_ASCII + -DQT_NO_CAST_TO_ASCII + -DQT_NO_CAST_FROM_BYTEARRAY +) + find_package(Cppcheck QUIET) set_package_properties(Cppcheck PROPERTIES DESCRIPTION "A tool for static C/C++ code analysis" URL "http://cppcheck.sourceforge.net/" PURPOSE "Recommended: required by the non-essential Cppcheck plugin" TYPE RUNTIME ) set(kdevcppcheck_core_SRCS parser.cpp job.cpp parameters.cpp utils.cpp ) ecm_qt_declare_logging_category(kdevcppcheck_core_SRCS HEADER debug.h IDENTIFIER KDEV_CPPCHECK CATEGORY_NAME "kdevelop.plugins.cppcheck" ) kconfig_add_kcfg_files(kdevcppcheck_core_SRCS config/globalsettings.kcfgc config/projectsettings.kcfgc ) add_library(kdevcppcheck_core STATIC ${kdevcppcheck_core_SRCS} ) target_link_libraries(kdevcppcheck_core KDev::Language KDev::Project KDev::Shell ) set(kdevcppcheck_SRCS plugin.cpp problemmodel.cpp config/globalconfigpage.cpp config/projectconfigpage.cpp ) ki18n_wrap_ui(kdevcppcheck_SRCS config/globalconfigpage.ui config/projectconfigpage.ui ) qt5_add_resources(kdevcppcheck_SRCS kdevcppcheck.qrc ) kdevplatform_add_plugin(kdevcppcheck JSON kdevcppcheck.json SOURCES ${kdevcppcheck_SRCS} ) target_link_libraries(kdevcppcheck kdevcppcheck_core KF5::ItemViews ) ecm_install_icons(ICONS icons/128-apps-cppcheck.png DESTINATION ${KDE_INSTALL_ICONDIR} THEME hicolor) if(BUILD_TESTING) add_subdirectory(tests) endif() diff --git a/plugins/cppcheck/config/globalsettings.kcfg b/plugins/cppcheck/config/globalsettings.kcfg index f545eb97af..1960ebe86b 100644 --- a/plugins/cppcheck/config/globalsettings.kcfg +++ b/plugins/cppcheck/config/globalsettings.kcfg @@ -1,25 +1,25 @@ "parameters.h" - QStandardPaths::findExecutable("cppcheck") + QStandardPaths::findExecutable(QStringLiteral("cppcheck")) defaults::hideOutputView defaults::showXmlOutput diff --git a/plugins/cppcheck/config/projectconfigpage.cpp b/plugins/cppcheck/config/projectconfigpage.cpp index f7ec290b0f..6b76c40117 100644 --- a/plugins/cppcheck/config/projectconfigpage.cpp +++ b/plugins/cppcheck/config/projectconfigpage.cpp @@ -1,128 +1,128 @@ /* This file is part of KDevelop Copyright 2013 Christoph Thielecke Copyright 2016 Anton Anikin 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, o (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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "projectconfigpage.h" #include "ui_projectconfigpage.h" #include "projectsettings.h" #include #include namespace cppcheck { ProjectConfigPage::ProjectConfigPage(KDevelop::IPlugin* plugin, KDevelop::IProject* project, QWidget* parent) : ConfigPage(plugin, new ProjectSettings, parent) , ui(new Ui::ProjectConfigPage) , m_parameters(new Parameters(project)) { configSkeleton()->setSharedConfig(project->projectConfiguration()); configSkeleton()->load(); ui->setupUi(this); ui->commandLine->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); connect(this, &ProjectConfigPage::changed, this, &ProjectConfigPage::updateCommandLine); connect(ui->commandLineFilter->lineEdit(), &QLineEdit::textChanged, this, &ProjectConfigPage::updateCommandLine); connect(ui->commandLineBreaks, &QCheckBox::stateChanged, this, &ProjectConfigPage::updateCommandLine); } ProjectConfigPage::~ProjectConfigPage() { } QIcon ProjectConfigPage::icon() const { return QIcon::fromTheme(QStringLiteral("cppcheck")); } void ProjectConfigPage::defaults() { ConfigPage::defaults(); updateCommandLine(); } void ProjectConfigPage::reset() { ConfigPage::reset(); updateCommandLine(); } QString ProjectConfigPage::name() const { return i18n("Cppcheck"); } void ProjectConfigPage::updateCommandLine() { m_parameters->checkStyle = ui->kcfg_checkStyle->isChecked(); m_parameters->checkPerformance = ui->kcfg_checkPerformance->isChecked(); m_parameters->checkPortability = ui->kcfg_checkPortability->isChecked(); m_parameters->checkInformation = ui->kcfg_checkInformation->isChecked(); m_parameters->checkUnusedFunction = ui->kcfg_checkUnusedFunction->isChecked(); m_parameters->checkMissingInclude = ui->kcfg_checkMissingInclude->isChecked(); m_parameters->inconclusiveAnalysis = ui->kcfg_inconclusiveAnalysis->isChecked(); m_parameters->forceCheck = ui->kcfg_forceCheck->isChecked(); m_parameters->checkConfig = ui->kcfg_checkConfig->isChecked(); m_parameters->useProjectIncludes = ui->kcfg_useProjectIncludes->isChecked(); m_parameters->useSystemIncludes = ui->kcfg_useSystemIncludes->isChecked(); m_parameters->ignoredIncludes = ui->kcfg_ignoredIncludes->text(); m_parameters->extraParameters = ui->kcfg_extraParameters->text().trimmed(); QString message; - QString commandLine = m_parameters->commandLine(message).join(' '); + QString commandLine = m_parameters->commandLine(message).join(QLatin1Char(' ')); if (message.isEmpty()) { ui->messageWidget->hide(); } else { ui->messageWidget->setText(message); ui->messageWidget->show(); } if (!ui->commandLineBreaks->isChecked()) { ui->commandLine->setPlainText(commandLine); return; } commandLine.replace(QLatin1String(" -"), QLatin1String("\n-")); QString filterText = ui->commandLineFilter->lineEdit()->text(); if (filterText.isEmpty()) { ui->commandLine->setPlainText(commandLine); ui->commandLineBreaks->setEnabled(true); return; } - QStringList lines = commandLine.split('\n'); + QStringList lines = commandLine.split(QLatin1Char('\n')); QMutableStringListIterator i(lines); while (i.hasNext()) { if (!i.next().contains(filterText)) { i.remove(); } } - ui->commandLine->setPlainText(lines.join('\n')); + ui->commandLine->setPlainText(lines.join(QLatin1Char('\n'))); ui->commandLineBreaks->setEnabled(false); } } diff --git a/plugins/cppcheck/job.cpp b/plugins/cppcheck/job.cpp index e42d29cdf6..cdd0f27c04 100644 --- a/plugins/cppcheck/job.cpp +++ b/plugins/cppcheck/job.cpp @@ -1,217 +1,217 @@ /* This file is part of KDevelop Copyright 2011 Mathieu Lornac Copyright 2011 Damien Coppel Copyright 2011 Lionel Duc Copyright 2011 Sebastien Rannou Copyright 2011 Lucas Sarie Copyright 2006-2008 Hamish Rodda Copyright 2002 Harald Fernengel Copyright 2013 Christoph Thielecke Copyright 2016-2017 Anton Anikin 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "job.h" #include "debug.h" #include "parser.h" #include "utils.h" #include #include #include #include #include #include namespace cppcheck { Job::Job(const Parameters& params, QObject* parent) : KDevelop::OutputExecuteJob(parent) , m_timer(new QElapsedTimer) , m_parser(new CppcheckParser) , m_showXmlOutput(params.showXmlOutput) , m_projectRootPath(params.projectRootPath()) { setJobName(i18n("Cppcheck Analysis (%1)", prettyPathName(params.checkPath))); setCapabilities(KJob::Killable); setStandardToolView(KDevelop::IOutputView::TestView); setBehaviours(KDevelop::IOutputView::AutoScroll); setProperties(KDevelop::OutputExecuteJob::JobProperty::DisplayStdout); setProperties(KDevelop::OutputExecuteJob::JobProperty::DisplayStderr); setProperties(KDevelop::OutputExecuteJob::JobProperty::PostProcessOutput); *this << params.commandLine(); qCDebug(KDEV_CPPCHECK) << "checking path" << params.checkPath; } Job::~Job() { doKill(); } void Job::postProcessStdout(const QStringList& lines) { static const auto fileNameRegex = QRegularExpression(QStringLiteral("Checking ([^:]*)\\.{3}")); static const auto percentRegex = QRegularExpression(QStringLiteral("(\\d+)% done")); QRegularExpressionMatch match; for (const QString& line : lines) { match = fileNameRegex.match(line); if (match.hasMatch()) { emit infoMessage(this, match.captured(1)); continue; } match = percentRegex.match(line); if (match.hasMatch()) { setPercent(match.captured(1).toULong()); continue; } } m_standardOutput << lines; if (status() == KDevelop::OutputExecuteJob::JobStatus::JobRunning) { KDevelop::OutputExecuteJob::postProcessStdout(lines); } } void Job::postProcessStderr(const QStringList& lines) { static const auto xmlStartRegex = QRegularExpression(QStringLiteral("\\s*<")); for (const QString & line : lines) { // unfortunately sometime cppcheck send non-XML messages to stderr. // For example, if we pass '-I /missing_include_dir' to the argument list, // then stderr output will contains such line (tested on cppcheck 1.72): // // (information) Couldn't find path given by -I '/missing_include_dir' // // Therefore we must 'move' such messages to m_standardOutput. if (line.indexOf(xmlStartRegex) != -1) { // the line contains XML m_xmlOutput << line; m_parser->addData(line); m_problems = m_parser->parse(); emitProblems(); } else { KDevelop::IProblem::Ptr problem(new KDevelop::DetectedProblem(i18n("Cppcheck"))); problem->setSeverity(KDevelop::IProblem::Error); problem->setDescription(line); problem->setExplanation(QStringLiteral("Check your cppcheck settings")); m_problems = {problem}; emitProblems(); if (m_showXmlOutput) { m_standardOutput << line; } else { postProcessStdout({line}); } } } if (status() == KDevelop::OutputExecuteJob::JobStatus::JobRunning && m_showXmlOutput) { KDevelop::OutputExecuteJob::postProcessStderr(lines); } } void Job::start() { m_standardOutput.clear(); m_xmlOutput.clear(); - qCDebug(KDEV_CPPCHECK) << "executing:" << commandLine().join(' '); + qCDebug(KDEV_CPPCHECK) << "executing:" << commandLine().join(QLatin1Char(' ')); m_timer->restart(); KDevelop::OutputExecuteJob::start(); } void Job::childProcessError(QProcess::ProcessError e) { QString message; switch (e) { case QProcess::FailedToStart: message = i18n("Failed to start Cppcheck from \"%1\".", commandLine()[0]); break; case QProcess::Crashed: if (status() != KDevelop::OutputExecuteJob::JobStatus::JobCanceled) { message = i18n("Cppcheck crashed."); } break; case QProcess::Timedout: message = i18n("Cppcheck process timed out."); break; case QProcess::WriteError: message = i18n("Write to Cppcheck process failed."); break; case QProcess::ReadError: message = i18n("Read from Cppcheck process failed."); break; case QProcess::UnknownError: // current cppcheck errors will be displayed in the output view // don't notify the user break; } if (!message.isEmpty()) { KMessageBox::error(qApp->activeWindow(), message, i18n("Cppcheck Error")); } KDevelop::OutputExecuteJob::childProcessError(e); } void Job::childProcessExited(int exitCode, QProcess::ExitStatus exitStatus) { qCDebug(KDEV_CPPCHECK) << "Process Finished, exitCode" << exitCode << "process exit status" << exitStatus; - postProcessStdout({QString("Elapsed time: %1 s.").arg(m_timer->elapsed()/1000.0)}); + postProcessStdout({QStringLiteral("Elapsed time: %1 s.").arg(m_timer->elapsed()/1000.0)}); if (exitCode != 0) { qCDebug(KDEV_CPPCHECK) << "cppcheck failed, standard output: "; - qCDebug(KDEV_CPPCHECK) << m_standardOutput.join('\n'); + qCDebug(KDEV_CPPCHECK) << m_standardOutput.join(QLatin1Char('\n')); qCDebug(KDEV_CPPCHECK) << "cppcheck failed, XML output: "; - qCDebug(KDEV_CPPCHECK) << m_xmlOutput.join('\n'); + qCDebug(KDEV_CPPCHECK) << m_xmlOutput.join(QLatin1Char('\n')); } KDevelop::OutputExecuteJob::childProcessExited(exitCode, exitStatus); } void Job::emitProblems() { if (!m_problems.isEmpty()) { emit problemsDetected(m_problems); } } } diff --git a/plugins/cppcheck/parameters.cpp b/plugins/cppcheck/parameters.cpp index 988c28f8f2..04c3e6aee5 100644 --- a/plugins/cppcheck/parameters.cpp +++ b/plugins/cppcheck/parameters.cpp @@ -1,270 +1,271 @@ /* This file is part of KDevelop Copyright 2016 Anton Anikin 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "parameters.h" #include "globalsettings.h" #include "projectsettings.h" #include #include #include #include #include #include #include #include namespace cppcheck { void includesForItem(KDevelop::ProjectBaseItem* parent, QSet& includes) { foreach (auto child, parent->children()) { if (child->type() == KDevelop::ProjectBaseItem::ProjectItemType::File) { continue; } else if (child->type() == KDevelop::ProjectBaseItem::ProjectItemType::ExecutableTarget || child->type() == KDevelop::ProjectBaseItem::ProjectItemType::LibraryTarget || child->type() == KDevelop::ProjectBaseItem::ProjectItemType::Target) { if (auto buildSystemManager = child->project()->buildSystemManager()) { foreach (auto dir, buildSystemManager->includeDirectories(child)) { includes.insert(dir); } } } includesForItem(child, includes); } } QList includesForProject(KDevelop::IProject* project) { QSet includesSet; includesForItem(project->projectItem(), includesSet); return includesSet.toList(); } Parameters::Parameters(KDevelop::IProject* project) : m_project(project) { executablePath = KDevelop::Path(GlobalSettings::executablePath()).toLocalFile(); hideOutputView = GlobalSettings::hideOutputView(); showXmlOutput = GlobalSettings::showXmlOutput(); if (!project) { checkStyle = defaults::checkStyle; checkPerformance = defaults::checkPerformance; checkPortability = defaults::checkPortability; checkInformation = defaults::checkInformation; checkUnusedFunction = defaults::checkUnusedFunction; checkMissingInclude = defaults::checkMissingInclude; inconclusiveAnalysis = defaults::inconclusiveAnalysis; forceCheck = defaults::forceCheck; checkConfig = defaults::checkConfig; useProjectIncludes = defaults::useProjectIncludes; useSystemIncludes = defaults::useSystemIncludes; return; } ProjectSettings projectSettings; projectSettings.setSharedConfig(project->projectConfiguration()); projectSettings.load(); checkStyle = projectSettings.checkStyle(); checkPerformance = projectSettings.checkPerformance(); checkPortability = projectSettings.checkPortability(); checkInformation = projectSettings.checkInformation(); checkUnusedFunction = projectSettings.checkUnusedFunction(); checkMissingInclude = projectSettings.checkMissingInclude(); inconclusiveAnalysis = projectSettings.inconclusiveAnalysis(); forceCheck = projectSettings.forceCheck(); checkConfig = projectSettings.checkConfig(); useProjectIncludes = projectSettings.useProjectIncludes(); useSystemIncludes = projectSettings.useSystemIncludes(); ignoredIncludes = projectSettings.ignoredIncludes(); extraParameters = projectSettings.extraParameters(); m_projectRootPath = m_project->path(); if (auto buildSystemManager = m_project->buildSystemManager()) { m_projectBuildPath = buildSystemManager->buildDirectory(m_project->projectItem()); } m_includeDirectories = includesForProject(project); } QStringList Parameters::commandLine() const { QString temp; return commandLine(temp); } QStringList Parameters::commandLine(QString& infoMessage) const { static const auto mocHeaderRegex = QRegularExpression(QStringLiteral("#define\\s+Q_MOC_OUTPUT_REVISION\\s+(.+)")); static const auto mocParametersRegex = QRegularExpression(QStringLiteral("-DQ_MOC_OUTPUT_REVISION=\\d{2,}")); const QString mocMessage = i18n( "It seems that this project uses Qt library. For correctly work of cppcheck " "the value for define Q_MOC_OUTPUT_REVISION must be set. Unfortunately, the plugin is unable " "to find this value automatically - you should set it manually by adding " "'-DQ_MOC_OUTPUT_REVISION=XX' to extra parameters. The 'XX' value can be found in any project's " "moc-generated file or in the header file."); QStringList result; result << executablePath; result << QStringLiteral("--xml-version=2"); if (checkStyle) { result << QStringLiteral("--enable=style"); } if (checkPerformance) { result << QStringLiteral("--enable=performance"); } if (checkPortability) { result << QStringLiteral("--enable=portability"); } if (checkInformation) { result << QStringLiteral("--enable=information"); } if (checkUnusedFunction) { result << QStringLiteral("--enable=unusedFunction"); } if (checkMissingInclude) { result << QStringLiteral("--enable=missingInclude"); } if (inconclusiveAnalysis) { result << QStringLiteral("--inconclusive"); } if (forceCheck) { result << QStringLiteral("--force"); } if (checkConfig) { result << QStringLiteral("--check-config"); } // Try to automatically get value of Q_MOC_OUTPUT_REVISION for Qt-projects. // If such define is not correctly set, cppcheck 'fails' on files with moc-includes // and not return any errors, even if the file contains them. if (!mocParametersRegex.match(extraParameters).hasMatch()) { bool qtUsed = false; bool mocDefineFound = false; foreach (auto dir, m_includeDirectories) { if (dir.path().endsWith(QLatin1String("QtCore"))) { qtUsed = true; QFile qtHeader(dir.path() + QStringLiteral("/qobjectdefs.h")); if (!qtHeader.open(QIODevice::ReadOnly)) { break; } while(!qtHeader.atEnd()) { - auto match = mocHeaderRegex.match(qtHeader.readLine()); + auto match = mocHeaderRegex.match(QString::fromUtf8(qtHeader.readLine())); if (match.hasMatch()) { mocDefineFound = true; result << QStringLiteral("-DQ_MOC_OUTPUT_REVISION=") + match.captured(1); break; } } break; } } if (qtUsed && !mocDefineFound) { infoMessage = mocMessage; } } if (!extraParameters.isEmpty()) { result << KShell::splitArgs(applyPlaceholders(extraParameters)); } if (m_project && useProjectIncludes) { QList ignored; - foreach (const QString& element, applyPlaceholders(ignoredIncludes).split(';')) { + const auto elements = applyPlaceholders(ignoredIncludes).split(QLatin1Char(';')); + for (const QString& element : elements) { if (!element.trimmed().isEmpty()) { ignored.append(KDevelop::Path(element)); } } foreach (const auto& dir, m_includeDirectories) { if (ignored.contains(dir)) { continue; } else if (useSystemIncludes || dir == m_projectRootPath || m_projectRootPath.isParentOf(dir) || dir == m_projectBuildPath || m_projectBuildPath.isParentOf(dir)) { result << QStringLiteral("-I"); result << dir.toLocalFile(); } } } if (m_project && m_project->managerPlugin()) { if (m_project->managerPlugin()->componentName() == QStringLiteral("kdevcmakemanager")) { result << QStringLiteral("-i") << m_projectBuildPath.toLocalFile() + QStringLiteral("/CMakeFiles"); } } result << checkPath; return result; } KDevelop::Path Parameters::projectRootPath() const { return m_projectRootPath; } QString Parameters::applyPlaceholders(const QString& text) const { QString result(text); if (m_project) { result.replace(QLatin1String("%p"), m_projectRootPath.toLocalFile()); result.replace(QLatin1String("%b"), m_projectBuildPath.toLocalFile()); } return result; } } diff --git a/plugins/cppcheck/parser.cpp b/plugins/cppcheck/parser.cpp index edd66c4270..7d5ae95d3f 100644 --- a/plugins/cppcheck/parser.cpp +++ b/plugins/cppcheck/parser.cpp @@ -1,306 +1,306 @@ /* This file is part of KDevelop Copyright 2013 Christoph Thielecke Copyright 2016 Anton Anikin 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "parser.h" #include "debug.h" #include #include #include #include #include #include namespace cppcheck { /** * Convert the value of \ attribute of \ element from cppcheck's * XML-output to 'good-looking' HTML-version. This is necessary because the * displaying of the original message is performed without line breaks - such * tooltips are uncomfortable to read, and large messages will not fit into the * screen. * * This function put the original message into \ tag that automatically * provides line wrapping by builtin capabilities of Qt library. The source text * also can contain tokens '\012' (line break) - they are present in the case of * source code examples. In such cases, the entire text between the first and * last tokens (i.e. source code) is placed into \ tag. * * @param[in] input the original value of \ attribute * @return HTML version for displaying in problem's tooltip */ QString verboseMessageToHtml( const QString & input ) { QString output(QStringLiteral("%1").arg(input.toHtmlEscaped())); output.replace(QLatin1String("\\012"), QLatin1String("\n")); - if (output.count('\n') >= 2) { - output.replace(output.indexOf('\n'), 1, QStringLiteral("
") );
-        output.replace(output.lastIndexOf('\n'), 1, QStringLiteral("

") ); + if (output.count(QLatin1Char('\n')) >= 2) { + output.replace(output.indexOf(QLatin1Char('\n')), 1, QStringLiteral("
") );
+        output.replace(output.lastIndexOf(QLatin1Char('\n')), 1, QStringLiteral("

") ); } return output; } CppcheckParser::CppcheckParser() { } CppcheckParser::~CppcheckParser() { } void CppcheckParser::clear() { m_stateStack.clear(); } bool CppcheckParser::startElement() { State newState = Unknown; qCDebug(KDEV_CPPCHECK) << "CppcheckParser::startElement: elem: " << qPrintable(name().toString()); - if (name() == "results") { + if (name() == QLatin1String("results")) { newState = Results; } - else if (name() == "cppcheck") { + else if (name() == QLatin1String("cppcheck")) { newState = CppCheck; } - else if (name() == "errors") { + else if (name() == QLatin1String("errors")) { newState = Errors; } - else if (name() == "location") { + else if (name() == QLatin1String("location")) { newState = Location; if (attributes().hasAttribute(QStringLiteral("file")) && attributes().hasAttribute(QStringLiteral("line"))) { QString errorFile = attributes().value(QStringLiteral("file")).toString(); // Usually when "file0" attribute exists it associated with source and // attribute "file" associated with header). // But sometimes cppcheck produces errors with "file" and "file0" attributes // both associated with same *source* file. In such cases attribute "file" contains // only file name, without full path. Therefore we should use "file0" instead "file". if (!QFile::exists(errorFile) && attributes().hasAttribute(QStringLiteral("file0"))) { errorFile = attributes().value(QStringLiteral("file0")).toString(); } m_errorFiles += errorFile; m_errorLines += attributes().value(QStringLiteral("line")).toString().toInt(); } } - else if (name() == "error") { + else if (name() == QLatin1String("error")) { newState = Error; m_errorSeverity = QStringLiteral("unknown"); m_errorInconclusive = false; m_errorFiles.clear(); m_errorLines.clear(); m_errorMessage.clear(); m_errorVerboseMessage.clear(); if (attributes().hasAttribute(QStringLiteral("msg"))) { m_errorMessage = attributes().value(QStringLiteral("msg")).toString(); } if (attributes().hasAttribute(QStringLiteral("verbose"))) { m_errorVerboseMessage = verboseMessageToHtml(attributes().value(QStringLiteral("verbose")).toString()); } if (attributes().hasAttribute(QStringLiteral("severity"))) { m_errorSeverity = attributes().value(QStringLiteral("severity")).toString(); } if (attributes().hasAttribute(QStringLiteral("inconclusive"))) { m_errorInconclusive = true; } } else { m_stateStack.push(m_stateStack.top()); return true; } m_stateStack.push(newState); return true; } bool CppcheckParser::endElement(QVector& problems) { qCDebug(KDEV_CPPCHECK) << "CppcheckParser::endElement: elem: " << qPrintable(name().toString()); State state = m_stateStack.pop(); switch (state) { case CppCheck: if (attributes().hasAttribute(QStringLiteral("version"))) { qCDebug(KDEV_CPPCHECK) << "Cppcheck report version: " << attributes().value(QStringLiteral("version")); } break; case Errors: // errors finished break; case Error: qCDebug(KDEV_CPPCHECK) << "CppcheckParser::endElement: new error elem: line: " << (m_errorLines.isEmpty() ? QStringLiteral("?") : QString::number(m_errorLines.first())) << " at " << (m_errorFiles.isEmpty() ? QStringLiteral("?") : m_errorFiles.first()) << ", msg: " << m_errorMessage; storeError(problems); break; case Results: // results finished break; case Location: break; default: break; } return true; } QVector CppcheckParser::parse() { QVector problems; qCDebug(KDEV_CPPCHECK) << "CppcheckParser::parse!"; while (!atEnd()) { int readNextVal = readNext(); switch (readNextVal) { case StartDocument: clear(); break; case StartElement: startElement(); break; case EndElement: endElement(problems); break; case Characters: break; default: qCDebug(KDEV_CPPCHECK) << "CppcheckParser::startElement: case: " << readNextVal; break; } } qCDebug(KDEV_CPPCHECK) << "CppcheckParser::parse: end"; if (hasError()) { switch (error()) { case CustomError: case UnexpectedElementError: case NotWellFormedError: KMessageBox::error( qApp->activeWindow(), i18n("Cppcheck XML Parsing: error at line %1, column %2: %3", lineNumber(), columnNumber(), errorString()), i18n("Cppcheck Error")); break; case NoError: case PrematureEndOfDocumentError: break; } } return problems; } void CppcheckParser::storeError(QVector& problems) { // Construct problem with using first location element KDevelop::IProblem::Ptr problem = getProblem(); // Adds other elements as diagnostics. // This allows the user to track the problem. for (int locationIdx = 1; locationIdx < m_errorFiles.size(); ++locationIdx) { problem->addDiagnostic(getProblem(locationIdx)); } problems.push_back(problem); } KDevelop::IProblem::Ptr CppcheckParser::getProblem(int locationIdx) const { KDevelop::IProblem::Ptr problem(new KDevelop::DetectedProblem(i18n("Cppcheck"))); QStringList messagePrefix; QString errorMessage(m_errorMessage); if (m_errorSeverity == QLatin1String("error")) { problem->setSeverity(KDevelop::IProblem::Error); } else if (m_errorSeverity == QLatin1String("warning")) { problem->setSeverity(KDevelop::IProblem::Warning); } else { problem->setSeverity(KDevelop::IProblem::Hint); messagePrefix.push_back(m_errorSeverity); } if (m_errorInconclusive) { messagePrefix.push_back(QStringLiteral("inconclusive")); } if (!messagePrefix.isEmpty()) { errorMessage = QStringLiteral("(%1) %2").arg(messagePrefix.join(QStringLiteral(", ")), m_errorMessage); } problem->setDescription(errorMessage); problem->setExplanation(m_errorVerboseMessage); KDevelop::DocumentRange range; if (locationIdx < 0 || locationIdx >= m_errorFiles.size()) { range = KDevelop::DocumentRange::invalid(); } else { range.document = KDevelop::IndexedString(m_errorFiles.at(locationIdx)); range.setBothLines(m_errorLines.at(locationIdx) - 1); range.setBothColumns(0); } problem->setFinalLocation(range); problem->setFinalLocationMode(KDevelop::IProblem::TrimmedLine); return problem; } } diff --git a/plugins/cppcheck/problemmodel.cpp b/plugins/cppcheck/problemmodel.cpp index f45819a5f4..0551781942 100644 --- a/plugins/cppcheck/problemmodel.cpp +++ b/plugins/cppcheck/problemmodel.cpp @@ -1,160 +1,162 @@ /* This file is part of KDevelop Copyright 2017 Anton Anikin 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "problemmodel.h" #include "plugin.h" #include "utils.h" #include #include #include #include #include #include namespace cppcheck { inline KDevelop::ProblemModelSet* problemModelSet() { return KDevelop::ICore::self()->languageController()->problemModelSet(); } -static const QString problemModelId = QStringLiteral("Cppcheck"); +namespace Strings { +QString problemModelId() { return QStringLiteral("Cppcheck"); } +} ProblemModel::ProblemModel(Plugin* plugin) : KDevelop::ProblemModel(plugin) , m_plugin(plugin) , m_project(nullptr) { setFeatures(CanDoFullUpdate | ScopeFilter | SeverityFilter | Grouping | CanByPassScopeFilter); reset(); - problemModelSet()->addModel(problemModelId, i18n("Cppcheck"), this); + problemModelSet()->addModel(Strings::problemModelId(), i18n("Cppcheck"), this); } ProblemModel::~ProblemModel() { - problemModelSet()->removeModel(problemModelId); + problemModelSet()->removeModel(Strings::problemModelId()); } KDevelop::IProject* ProblemModel::project() const { return m_project; } void ProblemModel::fixProblemFinalLocation(KDevelop::IProblem::Ptr problem) { // Fix problems with incorrect range, which produced by cppcheck's errors // without element. In this case location automatically gets "/". // To avoid this we set project's root path as problem location. Q_ASSERT(m_project); auto range = problem->finalLocation(); if (range.document.isEmpty()) { range.document = KDevelop::IndexedString(m_project->path().toLocalFile()); problem->setFinalLocation(range); } for (auto diagnostic : problem->diagnostics()) { fixProblemFinalLocation(diagnostic); } } bool ProblemModel::problemExists(KDevelop::IProblem::Ptr newProblem) { for (auto problem : m_problems) { if (newProblem->source() == problem->source() && newProblem->severity() == problem->severity() && newProblem->finalLocation() == problem->finalLocation() && newProblem->description() == problem->description() && newProblem->explanation() == problem->explanation()) return true; } return false; } void ProblemModel::addProblems(const QVector& problems) { static int maxLength = 0; if (m_problems.isEmpty()) { maxLength = 0; } for (auto problem : problems) { fixProblemFinalLocation(problem); if (problemExists(problem)) { continue; } m_problems.append(problem); addProblem(problem); // This performs adjusting of columns width in the ProblemsView if (maxLength < problem->description().length()) { maxLength = problem->description().length(); setProblems(m_problems); } } } void ProblemModel::setProblems() { setProblems(m_problems); } void ProblemModel::reset() { reset(nullptr, QString()); } void ProblemModel::reset(KDevelop::IProject* project, const QString& path) { m_project = project; m_path = path; clearProblems(); m_problems.clear(); QString tooltip = i18nc("@info:tooltip", "Re-Run Last Cppcheck Analysis"); if (m_project) { tooltip += QStringLiteral(" (%1)").arg(prettyPathName(m_path)); } setFullUpdateTooltip(tooltip); } void ProblemModel::show() { - problemModelSet()->showModel(problemModelId); + problemModelSet()->showModel(Strings::problemModelId()); } void ProblemModel::forceFullUpdate() { if (m_project && !m_plugin->isRunning()) { m_plugin->runCppcheck(m_project, m_path); } } } diff --git a/plugins/cppcheck/tests/CMakeLists.txt b/plugins/cppcheck/tests/CMakeLists.txt index d3b5fe75ec..70667f12f7 100644 --- a/plugins/cppcheck/tests/CMakeLists.txt +++ b/plugins/cppcheck/tests/CMakeLists.txt @@ -1,13 +1,19 @@ +remove_definitions( + -DQT_NO_CAST_FROM_ASCII + -DQT_NO_CAST_TO_ASCII + -DQT_NO_CAST_FROM_BYTEARRAY +) + ecm_add_test( test_cppcheckparser.cpp TEST_NAME test_cppcheckparser LINK_LIBRARIES kdevcppcheck_core Qt5::Test KDev::Tests ) ecm_add_test( test_cppcheckjob.cpp TEST_NAME test_cppcheckjob LINK_LIBRARIES kdevcppcheck_core Qt5::Test KDev::Tests ) diff --git a/plugins/custom-definesandincludes/kcm_widget/parserwidget.cpp b/plugins/custom-definesandincludes/kcm_widget/parserwidget.cpp index 0992564a80..238e6bfdca 100644 --- a/plugins/custom-definesandincludes/kcm_widget/parserwidget.cpp +++ b/plugins/custom-definesandincludes/kcm_widget/parserwidget.cpp @@ -1,218 +1,218 @@ /* * This file is part of KDevelop * * Copyright 2015 Sergey Kalinichev * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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, see . * */ #include "parserwidget.h" #include "ui_parserwidget.h" #include "compilerprovider/settingsmanager.h" #include namespace { QString languageStandard(const QString& arguments) { int idx = arguments.indexOf(QLatin1String("-std=")); if(idx == -1){ return QStringLiteral("c++11"); } idx += 5; int end = arguments.indexOf(' ', idx) != -1 ? arguments.indexOf(' ', idx) : arguments.size(); return arguments.mid(idx, end - idx); } QString languageDefaultStandard(Utils::LanguageType languageType) { switch (languageType) { case Utils::C: - return QLatin1String("c99"); + return QStringLiteral("c99"); case Utils::Cpp: - return QLatin1String("c++11"); + return QStringLiteral("c++11"); case Utils::OpenCl: - return QLatin1String("CL1.1"); + return QStringLiteral("CL1.1"); case Utils::Cuda: - return QLatin1String("c++11"); + return QStringLiteral("c++11"); case Utils::ObjC: - return QLatin1String("c99"); + return QStringLiteral("c99"); case Utils::Other: break; } Q_UNREACHABLE(); } bool isCustomParserArguments(Utils::LanguageType languageType, const QString& arguments, const QStringList& standards) { const auto defaultArguments = SettingsManager::globalInstance()->defaultParserArguments(); auto standard = languageStandard(arguments); auto tmpArgs(arguments); tmpArgs.replace(standard, languageDefaultStandard(languageType)); if (tmpArgs == defaultArguments[languageType] && standards.contains(standard)) { return false; } return true; } const int customProfileIdx = 0; } ParserWidget::ParserWidget(QWidget* parent) : QWidget(parent) , m_ui(new Ui::ParserWidget()) { m_ui->setupUi(this); connect(m_ui->parserOptionsC, &QLineEdit::textEdited, this, &ParserWidget::textEdited); connect(m_ui->parserOptionsCpp, &QLineEdit::textEdited, this, &ParserWidget::textEdited); connect(m_ui->parserOptionsOpenCl, &QLineEdit::textEdited, this, &ParserWidget::textEdited); connect(m_ui->parserOptionsCuda, &QLineEdit::textEdited, this, &ParserWidget::textEdited); connect(m_ui->parseHeadersInPlainC, &QCheckBox::stateChanged, this, &ParserWidget::textEdited); connect(m_ui->languageStandardsC, static_cast(&QComboBox::activated), this, &ParserWidget::languageStandardChangedC); connect(m_ui->languageStandardsCpp, static_cast(&QComboBox::activated), this, &ParserWidget::languageStandardChangedCpp); connect(m_ui->languageStandardsOpenCl, static_cast(&QComboBox::activated), this, &ParserWidget::languageStandardChangedOpenCl); connect(m_ui->languageStandardsCuda, static_cast(&QComboBox::activated), this, &ParserWidget::languageStandardChangedCuda); updateEnablements(); } ParserWidget::~ParserWidget() = default; void ParserWidget::textEdited() { emit changed(); } void ParserWidget::languageStandardChangedC(const QString& standard) { if (m_ui->languageStandardsC->currentIndex() == customProfileIdx) { m_ui->parserOptionsC->setText(SettingsManager::globalInstance()->defaultParserArguments()[Utils::C]); } else { auto text = SettingsManager::globalInstance()->defaultParserArguments()[Utils::C]; auto currentStandard = languageStandard(text); m_ui->parserOptionsC->setText(text.replace(currentStandard, standard)); } textEdited(); updateEnablements(); } void ParserWidget::languageStandardChangedCpp(const QString& standard) { if (m_ui->languageStandardsCpp->currentIndex() == customProfileIdx) { m_ui->parserOptionsCpp->setText(SettingsManager::globalInstance()->defaultParserArguments()[Utils::Cpp]); } else { auto text = SettingsManager::globalInstance()->defaultParserArguments()[Utils::Cpp]; auto currentStandard = languageStandard(text); m_ui->parserOptionsCpp->setText(text.replace(currentStandard, standard)); } textEdited(); updateEnablements(); } void ParserWidget::languageStandardChangedOpenCl(const QString& standard) { if (m_ui->languageStandardsOpenCl->currentIndex() == customProfileIdx) { m_ui->parserOptionsOpenCl->setText(SettingsManager::globalInstance()->defaultParserArguments()[Utils::OpenCl]); } else { auto text = SettingsManager::globalInstance()->defaultParserArguments()[Utils::OpenCl]; auto currentStandard = languageStandard(text); m_ui->parserOptionsOpenCl->setText(text.replace(currentStandard, standard)); } textEdited(); updateEnablements(); } void ParserWidget::languageStandardChangedCuda(const QString& standard) { if (m_ui->languageStandardsCuda->currentIndex() == customProfileIdx) { m_ui->parserOptionsCuda->setText(SettingsManager::globalInstance()->defaultParserArguments()[Utils::Cuda]); } else { auto text = SettingsManager::globalInstance()->defaultParserArguments()[Utils::Cuda]; auto currentStandard = languageStandard(text); m_ui->parserOptionsCuda->setText(text.replace(currentStandard, standard)); } textEdited(); updateEnablements(); } void ParserWidget::setParserArguments(const ParserArguments& arguments) { auto setArguments = [arguments](QComboBox* languageStandards, QLineEdit* parserOptions, Utils::LanguageType languageType) { QStringList standards; const int languageStandardsCount = languageStandards->count(); standards.reserve(languageStandardsCount-1); for (int i = 1; i < languageStandardsCount; ++i) { standards << languageStandards->itemText(i); } const QString& arg = arguments[languageType]; if (isCustomParserArguments(languageType, arg, standards)) { languageStandards->setCurrentIndex(customProfileIdx); } else { languageStandards->setCurrentText(languageStandard(arg)); } parserOptions->setText(arg); }; setArguments(m_ui->languageStandardsCpp, m_ui->parserOptionsCpp, Utils::Cpp); setArguments(m_ui->languageStandardsC, m_ui->parserOptionsC, Utils::C); setArguments(m_ui->languageStandardsOpenCl, m_ui->parserOptionsOpenCl, Utils::OpenCl); setArguments(m_ui->languageStandardsCuda, m_ui->parserOptionsCuda, Utils::Cuda); m_ui->parseHeadersInPlainC->setChecked(!arguments.parseAmbiguousAsCPP); updateEnablements(); } ParserArguments ParserWidget::parserArguments() const { ParserArguments arguments; arguments[Utils::C] = m_ui->parserOptionsC->text(); arguments[Utils::Cpp] = m_ui->parserOptionsCpp->text(); arguments[Utils::OpenCl] = m_ui->parserOptionsOpenCl->text(); arguments[Utils::Cuda] = m_ui->parserOptionsCuda->text(); arguments.parseAmbiguousAsCPP = !m_ui->parseHeadersInPlainC->isChecked(); return arguments; } void ParserWidget::updateEnablements() { m_ui->parserOptionsCpp->setEnabled(m_ui->languageStandardsCpp->currentIndex() == customProfileIdx); m_ui->parserOptionsC->setEnabled(m_ui->languageStandardsC->currentIndex() == customProfileIdx); m_ui->parserOptionsOpenCl->setEnabled(m_ui->languageStandardsOpenCl->currentIndex() == customProfileIdx); m_ui->parserOptionsCuda->setEnabled(m_ui->languageStandardsCuda->currentIndex() == customProfileIdx); } diff --git a/plugins/debuggercommon/mi/micommandqueue.h b/plugins/debuggercommon/mi/micommandqueue.h index eea73d316f..ae96ec5e50 100644 --- a/plugins/debuggercommon/mi/micommandqueue.h +++ b/plugins/debuggercommon/mi/micommandqueue.h @@ -1,63 +1,66 @@ // ************************************************************************* // micommandqueue.cpp // ------------------- // begin : Wed Dec 5, 2007 // copyright : (C) 2007 by Hamish Rodda // email : rodda@kde.org // ************************************************************************** // // ************************************************************************** // * * // * 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. * // * * // ************************************************************************** #ifndef MICOMMANDQUEUE_H #define MICOMMANDQUEUE_H #include "dbgglobal.h" #include namespace KDevMI { namespace MI { class MICommand; class CommandQueue { public: CommandQueue(); ~CommandQueue(); + /// CommandQueue takes ownership of @p command. void enqueue(MICommand* command); bool isEmpty() const; int count() const; void clear(); /// Whether the queue contains a command with CmdImmediately or CmdInterrupt flags. bool haveImmediateCommand() const; /** * Retrieve and remove the next command from the list. + * Ownership of the command is transferred to the caller. + * Returns @c nullptr if the list is empty. */ MICommand* nextCommand(); private: void rationalizeQueue(MICommand* command); void removeVariableUpdates(); void removeStackListUpdates(); void dumpQueue(); QList m_commandList; int m_immediatelyCounter = 0; uint32_t m_tokenCounter = 0; }; } // end of namespace MI } // end of namespace KDevMI #endif // MICOMMANDQUEUE_H diff --git a/plugins/debuggercommon/widgets/selectaddressdialog.ui b/plugins/debuggercommon/widgets/selectaddressdialog.ui index aca17460f5..a9626862c9 100644 --- a/plugins/debuggercommon/widgets/selectaddressdialog.ui +++ b/plugins/debuggercommon/widgets/selectaddressdialog.ui @@ -1,93 +1,106 @@ SelectAddressDialog 0 0 291 94 - Address selector + Address Selector Select the address to disassemble around Select address to disassemble around QComboBox::InsertAtTop false + + + + Qt::Vertical + + + + 20 + 40 + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok KComboBox QComboBox
kcombobox.h
KHistoryComboBox KComboBox
khistorycombobox.h
buttonBox accepted() SelectAddressDialog accept() 140 72 146 88 buttonBox rejected() SelectAddressDialog reject() 245 71 241 88
diff --git a/plugins/ghprovider/ghresource.cpp b/plugins/ghprovider/ghresource.cpp index cb96aa899e..1fb6ecc551 100644 --- a/plugins/ghprovider/ghresource.cpp +++ b/plugins/ghprovider/ghresource.cpp @@ -1,231 +1,231 @@ /* This file is part of KDevelop * * Copyright (C) 2012-2013 Miquel Sabaté * * 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 #include #include #include #include #include #include #include #include #include namespace gh { /// Base url for the Github API v3. const static QUrl baseUrl(QStringLiteral("https://api.github.com")); KIO::StoredTransferJob* createHttpAuthJob(const QString &httpHeader) { QUrl url = baseUrl; url = url.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + QLatin1String("/authorizations")); // generate a unique token, see bug 372144 const QString tokenName = "KDevelop Github Provider : " + QHostInfo::localHostName() + " - " + QDateTime::currentDateTimeUtc().toString(); const QByteArray data = QByteArrayLiteral("{ \"scopes\": [\"repo\"], \"note\": \"") + tokenName.toUtf8() + QByteArrayLiteral("\" }"); KIO::StoredTransferJob *job = KIO::storedHttpPost(data, url, KIO::HideProgressInfo); job->setProperty("requestedTokenName", tokenName); job->addMetaData(QStringLiteral("customHTTPHeader"), httpHeader); return job; } Resource::Resource(QObject *parent, ProviderModel *model) : QObject(parent), m_model(model) { /* There's nothing to do here */ } void Resource::searchRepos(const QString &uri, const QString &token) { KIO::TransferJob *job = getTransferJob(uri, token); connect(job, &KIO::TransferJob::data, this, &Resource::slotRepos); } void Resource::getOrgs(const QString &token) { KIO::TransferJob *job = getTransferJob(QStringLiteral("/user/orgs"), token); connect(job, &KIO::TransferJob::data, this, &Resource::slotOrgs); } void Resource::authenticate(const QString &name, const QString &password) { auto job = createHttpAuthJob(QLatin1String("Authorization: Basic ") + QString::fromUtf8(QByteArray(name.toUtf8() + ':' + password.toUtf8()).toBase64())); - job->addMetaData("PropagateHttpHeader","true"); + job->addMetaData(QStringLiteral("PropagateHttpHeader"), QStringLiteral("true")); connect(job, &KIO::StoredTransferJob::result, this, &Resource::slotAuthenticate); job->start(); } void Resource::twoFactorAuthenticate(const QString &transferHeader, const QString &code) { auto job = createHttpAuthJob(transferHeader + QLatin1String("\nX-GitHub-OTP: ") + code); connect(job, &KIO::StoredTransferJob::result, this, &Resource::slotAuthenticate); job->start(); } void Resource::revokeAccess(const QString &id, const QString &name, const QString &password) { QUrl url = baseUrl; url.setPath(url.path() + "/authorizations/" + id); KIO::TransferJob *job = KIO::http_delete(url, KIO::HideProgressInfo); job->addMetaData(QStringLiteral("customHTTPHeader"), QLatin1String("Authorization: Basic ") + QString (name + ':' + password).toUtf8().toBase64()); /* And we don't care if it's successful ;) */ job->start(); } KIO::TransferJob * Resource::getTransferJob(const QString &path, const QString &token) const { QUrl url = baseUrl; url = url.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + path); KIO::TransferJob *job = KIO::get(url, KIO::Reload, KIO::HideProgressInfo); if (!token.isEmpty()) job->addMetaData(QStringLiteral("customHTTPHeader"), "Authorization: token " + token); return job; } void Resource::retrieveRepos(const QByteArray &data) { QJsonParseError error; QJsonDocument doc = QJsonDocument::fromJson(data, &error); if (error.error == 0) { const QVariantList list = doc.toVariant().toList(); m_model->clear(); for (const QVariant& it : list) { const QVariantMap &map = it.toMap(); Response res; res.name = map.value(QStringLiteral("name")).toString(); res.url = map.value(QStringLiteral("clone_url")).toUrl(); if (map.value(QStringLiteral("fork")).toBool()) res.kind = Fork; else if (map.value(QStringLiteral("private")).toBool()) res.kind = Private; else res.kind = Public; ProviderItem *item = new ProviderItem(res); m_model->appendRow(item); } } emit reposUpdated(); } void Resource::retrieveOrgs(const QByteArray &data) { QStringList res; QJsonParseError error; QJsonDocument doc = QJsonDocument::fromJson(data, &error); if (error.error == 0) { const QVariantList json = doc.toVariant().toList(); res.reserve(json.size()); for (const QVariant& it : json) { QVariantMap map = it.toMap(); res << map.value(QStringLiteral("login")).toString(); } } emit orgsUpdated(res); } void Resource::slotAuthenticate(KJob *job) { const QString tokenName = job->property("requestedTokenName").toString(); Q_ASSERT(!tokenName.isEmpty()); if (job->error()) { emit authenticated("", "", tokenName); return; } const auto metaData = qobject_cast(job)->metaData(); if (metaData[QStringLiteral("responsecode")] == QStringLiteral("401")) { const auto& header = metaData[QStringLiteral("HTTP-Headers")]; if (header.contains(QStringLiteral("X-GitHub-OTP: required;"), Qt::CaseInsensitive)) { emit twoFactorAuthRequested(qobject_cast(job)->outgoingMetaData()[QStringLiteral("customHTTPHeader")]); return; } } QJsonParseError error; QJsonDocument doc = QJsonDocument::fromJson(qobject_cast(job)->data(), &error); qCDebug(GHPROVIDER) << "Response:" << doc; if (error.error == 0) { QVariantMap map = doc.toVariant().toMap(); emit authenticated(map.value(QStringLiteral("id")).toByteArray(), map.value(QStringLiteral("token")).toByteArray(), tokenName); } else emit authenticated("", "", tokenName); } void Resource::slotRepos(KIO::Job *job, const QByteArray &data) { if (!job) { qCWarning(GHPROVIDER) << "NULL job returned!"; return; } if (job->error()) { qCWarning(GHPROVIDER) << "Job error: " << job->errorString(); return; } m_temp.append(data); if (data.isEmpty()) { retrieveRepos(m_temp); m_temp = ""; } } void Resource::slotOrgs(KIO::Job *job, const QByteArray &data) { QList res; if (!job) { qCWarning(GHPROVIDER) << "NULL job returned!"; emit orgsUpdated(res); return; } if (job->error()) { qCWarning(GHPROVIDER) << "Job error: " << job->errorString(); emit orgsUpdated(res); return; } m_orgTemp.append(data); if (data.isEmpty()) { retrieveOrgs(m_orgTemp); m_orgTemp = ""; } } } // End of namespace gh diff --git a/plugins/makebuilder/makejob.cpp b/plugins/makebuilder/makejob.cpp index 82d01a6fa8..228c3070d7 100644 --- a/plugins/makebuilder/makejob.cpp +++ b/plugins/makebuilder/makejob.cpp @@ -1,313 +1,313 @@ /* This file is part of KDevelop Copyright 2004 Roberto Raggi Copyright 2007 Andreas Pakulat Copyright 2007 Dukju Ahn Copyright 2008 Hamish Rodda Copyright 2012 Ivan Shapovalov This library 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 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 "makejob.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "makebuilder.h" #include "makebuilderpreferences.h" #include "debug.h" using namespace KDevelop; class MakeJobCompilerFilterStrategy : public CompilerFilterStrategy { public: using CompilerFilterStrategy::CompilerFilterStrategy; IFilterStrategy::Progress progressInLine(const QString& line) override; }; IFilterStrategy::Progress MakeJobCompilerFilterStrategy::progressInLine(const QString& line) { // example string: [ 97%] Built target clang-parser static const QRegularExpression re(QStringLiteral("^\\[([\\d ][\\d ]\\d)%\\] (.*)")); QRegularExpressionMatch match = re.match(line); if (match.hasMatch()) { bool ok; const int percent = match.capturedRef(1).toInt(&ok); if (ok) { // this is output from make, likely const QString action = match.captured(2); return {action, percent}; } } return {}; } MakeJob::MakeJob(QObject* parent, KDevelop::ProjectBaseItem* item, CommandType c, const QStringList& overrideTargets, const MakeVariables& variables ) : OutputExecuteJob(parent) , m_idx(item->index()) , m_command(c) , m_overrideTargets(overrideTargets) , m_variables(variables) { auto bsm = item->project()->buildSystemManager(); auto buildDir = bsm->buildDirectory(item); Q_ASSERT(item && item->model() && m_idx.isValid() && this->item() == item); setCapabilities( Killable ); setFilteringStrategy(new MakeJobCompilerFilterStrategy(buildDir.toUrl())); setProperties( NeedWorkingDirectory | PortableMessages | DisplayStderr | IsBuilderHint ); QString title; if( !m_overrideTargets.isEmpty() ) title = i18n("Make (%1): %2", item->text(), m_overrideTargets.join(QLatin1Char(' '))); else title = i18n("Make (%1)", item->text()); setJobName( title ); setToolTitle( i18n("Make") ); } MakeJob::~MakeJob() { } void MakeJob::start() { ProjectBaseItem* it = item(); qCDebug(KDEV_MAKEBUILDER) << "Building with make" << m_command << m_overrideTargets.join(QLatin1Char(' ')); if (!it) { setError(ItemNoLongerValidError); setErrorText(i18n("Build item no longer available")); return emitResult(); } if( it->type() == KDevelop::ProjectBaseItem::File ) { setError(IncorrectItemError); setErrorText(i18n("Internal error: cannot build a file item")); return emitResult(); } setStandardToolView(IOutputView::BuildView); setBehaviours(KDevelop::IOutputView::AllowUserClose | KDevelop::IOutputView::AutoScroll); OutputExecuteJob::start(); } KDevelop::ProjectBaseItem * MakeJob::item() const { return ICore::self()->projectController()->projectModel()->itemFromIndex(m_idx); } MakeJob::CommandType MakeJob::commandType() const { return m_command; } QStringList MakeJob::customTargets() const { return m_overrideTargets; } QUrl MakeJob::workingDirectory() const { ProjectBaseItem* it = item(); if(!it) return QUrl(); KDevelop::IBuildSystemManager *bldMan = it->project()->buildSystemManager(); if( bldMan ) return bldMan->buildDirectory( it ).toUrl(); // the correct build dir else { // Just build in-source, where the build directory equals the one with particular target/source. for( ProjectBaseItem* item = it; item; item = item->parent() ) { switch( item->type() ) { case KDevelop::ProjectBaseItem::Folder: case KDevelop::ProjectBaseItem::BuildFolder: return static_cast(item)->path().toUrl(); break; case KDevelop::ProjectBaseItem::Target: case KDevelop::ProjectBaseItem::File: break; } } return QUrl(); } } QStringList MakeJob::privilegedExecutionCommand() const { ProjectBaseItem* it = item(); if(!it) return QStringList(); KSharedConfigPtr configPtr = it->project()->projectConfiguration(); KConfigGroup builderGroup( configPtr, "MakeBuilder" ); bool runAsRoot = builderGroup.readEntry( "Install As Root", false ); if ( runAsRoot && m_command == InstallCommand ) { QString suCommand = builderGroup.readEntry( "Su Command", QString() ); bool suCommandIsDigit; QStringList suCommandWithArg; int suCommandNum = suCommand.toInt(&suCommandIsDigit); /* * "if(suCommandIsDigit)" block exists only because of backwards compatibility * reasons, In earlier versions of KDevelop, suCommand's type was * int, if a user upgrades to current version from an older version, * suCommandIsDigit will become "true" and we will set suCommand according * to the stored config entry. */ if(suCommandIsDigit) { switch(suCommandNum) { case 1: { suCommand = QStringLiteral("kdesudo"); break; } case 2: { suCommand = QStringLiteral("sudo"); break; } default: suCommand = QStringLiteral("kdesu"); } builderGroup.writeEntry("Su Command", suCommand); //so that suCommandIsDigit becomes false next time //project is opened. } suCommandWithArg = KShell::splitArgs(suCommand); if( suCommandWithArg.isEmpty() ) { suCommandWithArg = QStringList{QStringLiteral("kdesu"), QStringLiteral("-t")}; } return suCommandWithArg; } return QStringList(); } QStringList MakeJob::commandLine() const { ProjectBaseItem* it = item(); if(!it) return QStringList(); QStringList cmdline; KSharedConfigPtr configPtr = it->project()->projectConfiguration(); KConfigGroup builderGroup( configPtr, "MakeBuilder" ); // TODO: migrate to more generic key term "Make Executable" QString makeBin = builderGroup.readEntry("Make Binary", MakeBuilderPreferences::standardMakeExecutable()); cmdline << makeBin; if( ! builderGroup.readEntry("Abort on First Error", true)) { cmdline << (isNMake(makeBin) ? "/K" : "-k"); } // note: nmake does not support the -j flag if (!isNMake(makeBin)) { if (builderGroup.readEntry("Override Number Of Jobs", false)) { int jobCount = builderGroup.readEntry("Number Of Jobs", 1); if (jobCount > 0) { cmdline << QStringLiteral("-j%1").arg(jobCount); } } else { // use the ideal thread count by default cmdline << QStringLiteral("-j%1").arg(QThread::idealThreadCount()); } } if( builderGroup.readEntry("Display Only", false) ) { cmdline << (isNMake(makeBin) ? "/N" : "-n"); } QString extraOptions = builderGroup.readEntry("Additional Options", QString()); if( ! extraOptions.isEmpty() ) { foreach(const QString& option, KShell::splitArgs( extraOptions ) ) cmdline << option; } for (MakeVariables::const_iterator it = m_variables.constBegin(); it != m_variables.constEnd(); ++it) { - cmdline += QStringLiteral("%1=%2").arg(it->first).arg(it->second); + cmdline += QStringLiteral("%1=%2").arg(it->first, it->second); } if( m_overrideTargets.isEmpty() ) { QString target; switch (it->type()) { case KDevelop::ProjectBaseItem::Target: case KDevelop::ProjectBaseItem::ExecutableTarget: case KDevelop::ProjectBaseItem::LibraryTarget: Q_ASSERT(it->target()); cmdline << it->target()->text(); break; case KDevelop::ProjectBaseItem::BuildFolder: target = builderGroup.readEntry("Default Target", QString()); if( !target.isEmpty() ) cmdline << target; break; default: break; } }else { cmdline += m_overrideTargets; } return cmdline; } QString MakeJob::environmentProfile() const { ProjectBaseItem* it = item(); if(!it) return QString(); KSharedConfigPtr configPtr = it->project()->projectConfiguration(); KConfigGroup builderGroup( configPtr, "MakeBuilder" ); return builderGroup.readEntry( "Default Make Environment Profile", QString() ); } bool MakeJob::isNMake(const QString& makeBin) { return !QFileInfo(makeBin).baseName().compare(QStringLiteral("nmake"), Qt::CaseInsensitive); } diff --git a/plugins/outlineview/CMakeLists.txt b/plugins/outlineview/CMakeLists.txt index b1a31c7ab4..1149ebb735 100644 --- a/plugins/outlineview/CMakeLists.txt +++ b/plugins/outlineview/CMakeLists.txt @@ -1,22 +1,29 @@ add_definitions(-DTRANSLATION_DOMAIN=\"kdevoutlineview\") + +add_definitions( + -DQT_NO_CAST_FROM_ASCII + -DQT_NO_CAST_TO_ASCII + -DQT_NO_CAST_FROM_BYTEARRAY +) + set(kdevoutlineview_SRCS outlineviewplugin.cpp outlinenode.cpp outlinemodel.cpp outlinewidget.cpp ) ecm_qt_declare_logging_category(kdevoutlineview_SRCS HEADER debug.h IDENTIFIER PLUGIN_OUTLINE CATEGORY_NAME "kdevelop.plugins.outline" ) kdevplatform_add_plugin(kdevoutlineview JSON kdevoutlineview.json SOURCES ${kdevoutlineview_SRCS}) target_link_libraries(kdevoutlineview KDev::Interfaces KDev::Language KF5::CoreAddons KF5::I18n KF5::ItemModels KF5::TextEditor ) diff --git a/plugins/outlineview/outlinenode.cpp b/plugins/outlineview/outlinenode.cpp index c1f8339f6d..db6b285e70 100644 --- a/plugins/outlineview/outlinenode.cpp +++ b/plugins/outlineview/outlinenode.cpp @@ -1,299 +1,299 @@ /* * KDevelop outline view * Copyright 2010, 2015 Alex Richardson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "outlinenode.h" #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KDevelop; OutlineNode::OutlineNode(const QString& text, OutlineNode* parent) : m_cachedText(text) , m_parent(parent) { } OutlineNode::OutlineNode(DUContext* ctx, const QString& name, OutlineNode* parent) : m_cachedText(name) , m_declOrContext(ctx) , m_parent(parent) { KTextEditor::CodeCompletionModel::CompletionProperties prop; switch (ctx->type()) { case KDevelop::DUContext::Class: prop |= KTextEditor::CodeCompletionModel::Class; break; case KDevelop::DUContext::Enum: prop |= KTextEditor::CodeCompletionModel::Enum; break; case KDevelop::DUContext::Function: prop |= KTextEditor::CodeCompletionModel::Function; break; case KDevelop::DUContext::Namespace: prop |= KTextEditor::CodeCompletionModel::Namespace; break; case KDevelop::DUContext::Template: prop |= KTextEditor::CodeCompletionModel::Template; break; default: break; } m_cachedIcon = DUChainUtils::iconForProperties(prop); appendContext(ctx, ctx->topContext()); } OutlineNode::OutlineNode(Declaration* decl, OutlineNode* parent) : m_declOrContext(decl) , m_parent(parent) { // qCDebug(PLUGIN_OUTLINE) << "Adding:" << decl->qualifiedIdentifier().toString() << ": " <identifier().toString(); m_cachedIcon = DUChainUtils::iconForDeclaration(decl); if (NamespaceAliasDeclaration* alias = dynamic_cast(decl)) { //e.g. C++ using namespace statement m_cachedText = alias->importIdentifier().toString(); } else if (ClassMemberDeclaration* member = dynamic_cast(decl)) { if (member->isFriend()) { - m_cachedText = "friend " + m_cachedText; + m_cachedText = QLatin1String("friend ") + m_cachedText; } } if (AbstractType::Ptr type = decl->abstractType()) { //add the (function return) type at the end (after a colon - like UML) //so that the first thing seen is the name of the function/variable //and not the (function return) type AbstractType::WhichType typeEnum = type->whichType(); switch (typeEnum) { case AbstractType::TypeFunction: { FunctionType::Ptr func = type.cast(); // func->partToString() does not add the argument names -> do it manually if (DUContext* fCtx = DUChainUtils::getFunctionContext(decl)) { - m_cachedText += '('; + m_cachedText += QLatin1Char('('); bool first = true; foreach (Declaration* childDecl, fCtx->localDeclarations(decl->topContext())) { if (first) { first = false; } else { m_cachedText += QStringLiteral(", "); } if (childDecl->abstractType()) { m_cachedText += childDecl->abstractType()->toString(); } auto ident = childDecl->identifier(); if (!ident.isEmpty()) { - m_cachedText += ' ' + ident.toString(); + m_cachedText += QLatin1Char(' ') + ident.toString(); } } - m_cachedText += ')'; + m_cachedText += QLatin1Char(')'); } else { qCWarning(PLUGIN_OUTLINE) << "Missing function context:" << decl->qualifiedIdentifier().toString(); m_cachedText += func->partToString(FunctionType::SignatureArguments); } //constructors/destructors have no return type, a trailing semicolon would look stupid if (func->returnType()) { - m_cachedText += " : " + func->partToString(FunctionType::SignatureReturn); + m_cachedText += QLatin1String(" : ") + func->partToString(FunctionType::SignatureReturn); } return; // don't append any children here! } case AbstractType::TypeEnumeration: //no need to append the fully qualified type break; case AbstractType::TypeEnumerator: //no need to append the fully qualified type Q_ASSERT(decl->type()); - m_cachedText += " = " + decl->type()->valueAsString(); + m_cachedText += QLatin1String(" = ") + decl->type()->valueAsString(); break; case AbstractType::TypeStructure: { //this seems to be the way it has to be done (after grepping through source code) //TODO shouldn't there be some kind of isFriend() functionality? static IndexedIdentifier friendIdentifier(Identifier(QStringLiteral("friend"))); const bool isFriend = decl->indexedIdentifier() == friendIdentifier; if (isFriend) { //FIXME There seems to be no way of finding out whether the friend is class/struct/etc - m_cachedText += ' ' + type->toString(); + m_cachedText += QLatin1Char(' ') + type->toString(); } break; } case AbstractType::TypeAlias: { //append the type it aliases TypeAliasType::Ptr alias = type.cast(); if (AbstractType::Ptr targetType = alias->type()) { - m_cachedText += " : " + targetType->toString(); + m_cachedText += QLatin1String(" : ") + targetType->toString(); } } break; default: QString typeStr = type->toString(); if (!typeStr.isEmpty()) { - m_cachedText += " : " + typeStr; + m_cachedText += QLatin1String(" : ") + typeStr; } } } //these two don't seem to be hit if (decl->isAutoDeclaration()) { - m_cachedText = "Implicit: " + m_cachedText; + m_cachedText = QLatin1String("Implicit: ") + m_cachedText; } if (decl->isAnonymous()) { - m_cachedText = "" + m_cachedText; + m_cachedText = QLatin1String("") + m_cachedText; } if (DUContext* ctx = decl->internalContext()) { appendContext(ctx, decl->topContext()); } if (m_cachedText.isEmpty()) { m_cachedText = i18nc("An anonymous declaration (class, function, etc.)", ""); } } std::unique_ptr OutlineNode::dummyNode() { return std::unique_ptr(new OutlineNode(QStringLiteral(""), nullptr)); } std::unique_ptr OutlineNode::fromTopContext(TopDUContext* ctx) { auto result = dummyNode(); result->appendContext(ctx, ctx); return result; } void OutlineNode::appendContext(DUContext* ctx, TopDUContext* top) { // qDebug() << ctx->scopeIdentifier().toString() << "context type=" << ctx->type(); foreach (Declaration* childDecl, ctx->localDeclarations(top)) { if (childDecl) { m_children.emplace_back(childDecl, this); } } bool certainlyRequiresSorting = false; foreach (DUContext* childContext, ctx->childContexts()) { if (childContext->owner()) { // if there is a onwner, this will already have been handled by the loop above // TODO: is this always true? With my testing so far it seems to be // qDebug() << childContext->scopeIdentifier(true).toString() // << " has an owner declaration: " << childContext->owner()->toString() << "-> skip"; continue; } QVector decls = childContext->localDeclarations(top); if (decls.isEmpty()) { continue; } // we now know that we will have o sort since we appended a node in the wrong order certainlyRequiresSorting = true; QString ctxName = childContext->scopeIdentifier(true).toString(); // if child context is a template context or if name is empty append to current list, // otherwise create a new context node if (childContext->type() == DUContext::Template || ctxName.isEmpty()) { //append all subcontexts to this node appendContext(childContext, top); } else { // context without matching declaration, for example the definition of // "class Foo::Bar if it was forward declared in a namespace before: // namespace Foo { class Bar; } // class Foo::Bar { ... }; // TODO: icon and location for the namespace if (childContext->type() == DUContext::ContextType::Helper) { // This context could be for a definition of an existing class method. // If we don't merge all those context end up with a tree like this: // +-+- FooClass // | \-- method1() // +-+- FooClass // | \-- method2() // \ OtherStuff auto it = std::find_if(m_children.begin(), m_children.end(), [childContext](const OutlineNode& node) { if (DUContext* ctx = dynamic_cast(node.duChainObject())) { return ctx->equalScopeIdentifier(childContext); } return false; }); if (it != m_children.end()) { it->appendContext(childContext, top); } else { // TODO: get the correct icon for the context m_children.emplace_back(childContext, ctxName, this); } } else { // just add the context m_children.emplace_back(childContext, ctxName, this); } } } // we now need to sort since sometimes the elements from ctx->localDeclarations(top) // are not in the order they appear in the source. Additionally, if we had any child // contexts that were added, they will be at the end of the list // and need to be moved to the correct location. In that case certainlyRequiresSorting // will be true and we can pass it to sortByLocation() to skip the std::is_sorted() call sortByLocation(certainlyRequiresSorting); } void OutlineNode::sortByLocation(bool requiresSorting) { if (m_children.size() <= 1) { return; } // TODO: does it make sense to cache m_declOrContext->range().start? // adds 8 bytes to each node, but save a lot of pointer lookups when sorting // qDebug("sorting children of %s (%p) by location", qPrintable(m_cachedText), this); auto compare = [](const OutlineNode& n1, const OutlineNode& n2) -> bool { // nodes without decl always go at the end if (!n1.m_declOrContext) { return false; } else if (!n2.m_declOrContext) { return true; } return n1.m_declOrContext->range().start < n2.m_declOrContext->range().start; }; // since most nodes will be correctly sorted we check that before calling std::sort(). // This saves a lot of move ctor/assingnment calls in the common case. // If we appended a context without a Declaration* we know that it will be unsorted // so we can pass requiresSorting = true to skip the useless std::is_sorted() call. // uncomment the following qDebug() lines to see whether this optimization really makes sense if (requiresSorting || !std::is_sorted(m_children.begin(), m_children.end(), compare)) { // qDebug("Need to sort node %s(%p)", qPrintable(m_cachedText), this); std::sort(m_children.begin(), m_children.end(), compare); } else { // qDebug("Node %s(%p) was sorted!", qPrintable(m_cachedText), this); } } OutlineNode::~OutlineNode() { } diff --git a/plugins/qmakemanager/parser/qmake.g b/plugins/qmakemanager/parser/qmake.g index d50ab61907..b5c40959c9 100644 --- a/plugins/qmakemanager/parser/qmake.g +++ b/plugins/qmakemanager/parser/qmake.g @@ -1,246 +1,246 @@ ------------------------------------------------------------------------------- -- This file is part of the QMake parser in KDevelop4 -- Copyright (c) 2007 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. ------------------------------------------------------------------------------- ----------------------------------------------------------- -- Global declarations ----------------------------------------------------------- [: namespace QMake { class Lexer; } #include :] %parser_declaration_header "QtCore/QString" ------------------------------------------------------------ -- Parser class members ------------------------------------------------------------ %parserclass (public declaration) [: /** * Transform the raw input into tokens. * When this method returns, the parser's token stream has been filled * and any parse_*() method can be called. */ void tokenize( const QString& contents ); enum ProblemType { Error, Warning, Info }; void reportProblem( Parser::ProblemType type, const QString& message ); QString tokenText(qint64 begin, qint64 end) const; void setDebug( bool debug ); :] %parserclass (private declaration) [: QString m_contents; bool m_debug; :] ----------------------------------------------------------- -- List of defined tokens ----------------------------------------------------------- %token LBRACE("lbrace"), RBRACE("rbrace"), LPAREN("lparen"),RPAREN("rparen") ;; %token PLUSEQ("pluseq"),EQUAL("equal"),MINUSEQ("minuseq"),STAREQ("stareq"), TILDEEQ("tildeeq") ;; %token COLON("colon"), COMMA("comma"), CONT("cont"), EXCLAM("exclam"), NEWLINE("newline"), OR("or") ;; %token IDENTIFIER("identifier"), VALUE("value") ;; -- token that makes the parser fail in any case: %token INVALID ("invalid token") ;; -- The actual grammar starts here. ( #statements=statement )* -> project ;; ( id=IDENTIFIER ( var=variableAssignment | scope=scope ) [: (*yynode)->isNewline = false; (*yynode)->isExclam = false; :] ) | ( EXCLAM id=IDENTIFIER scope=scope [: (*yynode)->isNewline = false; (*yynode)->isExclam = true; :] ) | NEWLINE [: (*yynode)->isNewline = true; (*yynode)->isExclam = false; :] -> statement [ member variable isNewline: bool; member variable isExclam: bool; ] ;; functionArguments=functionArguments ( scopeBody=scopeBody | orOperator=orOperator scopeBody=scopeBody | 0 ) | ( orOperator=orOperator | 0 ) scopeBody=scopeBody -> scope ;; ( OR #item=item )+ -> orOperator ;; id=IDENTIFIER ( functionArguments=functionArguments | 0 ) -> item ;; op=op ( values=valueList ( NEWLINE | 0 ) | NEWLINE | 0 ) -> variableAssignment ;; optoken=PLUSEQ | optoken=MINUSEQ | optoken=STAREQ | optoken=EQUAL | optoken=TILDEEQ -> op ;; ( #list=value | CONT NEWLINE )+ -> valueList ;; value=VALUE -> value ;; LPAREN args=argumentList RPAREN -> functionArguments ;; ( ( #args=value | CONT NEWLINE ) ( ( COMMA | CONT NEWLINE ) #args=value )* | 0 ) -> argumentList ;; LBRACE ( NEWLINE | 0 ) ( #statements=statement )* RBRACE | COLON #statements=statement -> scopeBody ;; ----------------------------------------------------------------- -- Code segments copied to the implementation (.cpp) file. -- If existent, kdevelop-pg's current syntax requires this block -- to occur at the end of the file. ----------------------------------------------------------------- [: #include "qmakelexer.h" #include #include namespace QMake { void Parser::tokenize( const QString& contents ) { m_contents = contents; QMake::Lexer lexer( this, contents ); int kind = Parser::Token_EOF; do { kind = lexer.nextTokenKind(); if ( !kind ) // when the lexer returns 0, the end of file is reached kind = Parser::Token_EOF; Parser::Token &t = this->tokenStream->push(); t.kind = kind; if( t.kind == Parser::Token_EOF ) { t.begin = -1; t.end = -1; }else { t.begin = lexer.tokenBegin(); t.end = lexer.tokenEnd(); } if( m_debug ) { qCDebug(KDEV_QMAKE) << kind << "(" << t.begin << "," << t.end << ")::" << tokenText(t.begin, t.end); } } while ( kind != Parser::Token_EOF ); this->yylex(); // produce the look ahead token } QString Parser::tokenText( qint64 begin, qint64 end ) const { return m_contents.mid((int)begin, (int)end-begin+1); } void Parser::reportProblem( Parser::ProblemType type, const QString& message ) { if (type == Error) qCDebug(KDEV_QMAKE) << "** ERROR:" << message; else if (type == Warning) qCDebug(KDEV_QMAKE) << "** WARNING:" << message; else if (type == Info) qCDebug(KDEV_QMAKE) << "** Info:" << message; } // custom error recovery void Parser::expectedToken(int /*expected*/, qint64 /*where*/, const QString& name) { reportProblem( Parser::Error, - QString("Expected token \"%1\"").arg(name)); + QStringLiteral("Expected token \"%1\"").arg(name)); } void Parser::expectedSymbol(int /*expected_symbol*/, const QString& name) { qint64 line; qint64 col; size_t index = tokenStream->index()-1; Token &token = tokenStream->at(index); qCDebug(KDEV_QMAKE) << "token starts at:" << token.begin; qCDebug(KDEV_QMAKE) << "index is:" << index; tokenStream->startPosition(index, &line, &col); QString tokenValue = tokenText(token.begin, token.end); reportProblem( Parser::Error, - QString("Expected symbol \"%1\" (current token: \"%2\" [%3] at line: %4 col: %5)") + QStringLiteral("Expected symbol \"%1\" (current token: \"%2\" [%3] at line: %4 col: %5)") .arg(name) - .arg(token.kind != 0 ? tokenValue : "EOF") + .arg(token.kind != 0 ? tokenValue : QStringLiteral("EOF")) .arg(token.kind) .arg(line) .arg(col)); } void Parser::setDebug( bool debug ) { m_debug = debug; } } // end of namespace QMake :] -- kate: space-indent on; indent-width 4; tab-width 4; replace-tabs on; auto-insert-doxygen on diff --git a/plugins/qmakemanager/qmakeprojectfile.cpp b/plugins/qmakemanager/qmakeprojectfile.cpp index a6ca50eaf9..ec7a42e725 100644 --- a/plugins/qmakemanager/qmakeprojectfile.cpp +++ b/plugins/qmakemanager/qmakeprojectfile.cpp @@ -1,443 +1,443 @@ /* KDevelop QMake Support * * Copyright 2006 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 "qmakeprojectfile.h" #include #include #include #include "debug.h" #include "parser/ast.h" #include "qmakecache.h" #include "qmakemkspecs.h" #include "qmakeconfig.h" #include #include #define ifDebug(x) QHash> QMakeProjectFile::m_qmakeQueryCache; const QStringList QMakeProjectFile::FileVariables = QStringList{ QStringLiteral("IDLS"), QStringLiteral("RESOURCES"), QStringLiteral("IMAGES"), QStringLiteral("LEXSOURCES"), QStringLiteral("DISTFILES"), QStringLiteral("YACCSOURCES"), QStringLiteral("TRANSLATIONS"), QStringLiteral("HEADERS"), QStringLiteral("SOURCES"), QStringLiteral("INTERFACES"), QStringLiteral("FORMS"), }; QMakeProjectFile::QMakeProjectFile(const QString& projectfile) : QMakeFile(projectfile) , m_mkspecs(nullptr) , m_cache(nullptr) { } void QMakeProjectFile::setQMakeCache(QMakeCache* cache) { m_cache = cache; } void QMakeProjectFile::setMkSpecs(QMakeMkSpecs* mkspecs) { m_mkspecs = mkspecs; } bool QMakeProjectFile::read() { // default values // NOTE: if we already have such a var, e.g. in an include file, we must not overwrite it here! if (!m_variableValues.contains(QStringLiteral("QT"))) { m_variableValues[QStringLiteral("QT")] = QStringList{QStringLiteral("core"), QStringLiteral("gui")}; } if (!m_variableValues.contains(QStringLiteral("CONFIG"))) { m_variableValues[QStringLiteral("CONFIG")] = QStringList() << QStringLiteral("qt"); } Q_ASSERT(m_mkspecs); foreach (const QString& var, m_mkspecs->variables()) { if (!m_variableValues.contains(var)) { m_variableValues[var] = m_mkspecs->variableValues(var); } } if (m_cache) { foreach (const QString& var, m_cache->variables()) { if (!m_variableValues.contains(var)) { m_variableValues[var] = m_cache->variableValues(var); } } } /// TODO: more special variables m_variableValues[QStringLiteral("PWD")] = QStringList() << pwd(); m_variableValues[QStringLiteral("_PRO_FILE_")] = QStringList() << proFile(); m_variableValues[QStringLiteral("_PRO_FILE_PWD_")] = QStringList() << proFilePwd(); m_variableValues[QStringLiteral("OUT_PWD")] = QStringList() << outPwd(); const QString qtInstallHeaders = QStringLiteral("QT_INSTALL_HEADERS"); const QString qtVersion = QStringLiteral("QT_VERSION"); const QString qtInstallLibs = QStringLiteral("QT_INSTALL_LIBS"); const QString executable = QMakeConfig::qmakeExecutable(project()); if (!m_qmakeQueryCache.contains(executable)) { const auto queryResult = QMakeConfig::queryQMake(executable, {qtInstallHeaders, qtVersion, qtInstallLibs}); if (queryResult.isEmpty()) { qCWarning(KDEV_QMAKE) << "Failed to query qmake - bad qmake executable configured?" << executable; } m_qmakeQueryCache[executable] = queryResult; } const auto cachedQueryResult = m_qmakeQueryCache.value(executable); m_qtIncludeDir = cachedQueryResult.value(qtInstallHeaders); m_qtVersion = cachedQueryResult.value(qtVersion); m_qtLibDir = cachedQueryResult.value(qtInstallLibs); return QMakeFile::read(); } QStringList QMakeProjectFile::subProjects() const { ifDebug(qCDebug(KDEV_QMAKE) << "Fetching subprojects";) QStringList list; - foreach (QString subdir, variableValues("SUBDIRS")) { + foreach (QString subdir, variableValues(QStringLiteral("SUBDIRS"))) { QString fileOrPath; ifDebug(qCDebug(KDEV_QMAKE) << "Found value:" << subdir;) if (containsVariable(subdir + ".file") && !variableValues(subdir + ".file").isEmpty()) { subdir = variableValues(subdir + ".file").first(); } else if (containsVariable(subdir + ".subdir") && !variableValues(subdir + ".subdir").isEmpty()) { subdir = variableValues(subdir + ".subdir").first(); } if (subdir.endsWith(QLatin1String(".pro"))) { fileOrPath = resolveToSingleFileName(subdir.trimmed()); } else { fileOrPath = resolveToSingleFileName(subdir.trimmed()); } if (fileOrPath.isEmpty()) { qCWarning(KDEV_QMAKE) << "could not resolve subdir" << subdir << "to file or path, skipping"; continue; } list << fileOrPath; } ifDebug(qCDebug(KDEV_QMAKE) << "found" << list.size() << "subprojects";) return list; } bool QMakeProjectFile::hasSubProject(const QString& file) const { foreach (const QString& sub, subProjects()) { if (sub == file) { return true; } else if (QFileInfo(file).absoluteDir() == sub) { return true; } } return false; } void QMakeProjectFile::addPathsForVariable(const QString& variable, QStringList* list, const QString& base) const { const QStringList values = variableValues(variable); ifDebug(qCDebug(KDEV_QMAKE) << variable << values;) for (const QString& val : values) { QString path = resolveToSingleFileName(val, base); if (!path.isEmpty() && !list->contains(val)) { list->append(path); } } } QStringList QMakeProjectFile::includeDirectories() const { ifDebug(qCDebug(KDEV_QMAKE) << "Fetching include dirs" << m_qtIncludeDir;) ifDebug(qCDebug(KDEV_QMAKE) << "CONFIG" << variableValues("CONFIG");) QStringList list; addPathsForVariable(QStringLiteral("INCLUDEPATH"), &list); addPathsForVariable(QStringLiteral("QMAKE_INCDIR"), &list); if (variableValues(QStringLiteral("CONFIG")).contains(QStringLiteral("opengl"))) { addPathsForVariable(QStringLiteral("QMAKE_INCDIR_OPENGL"), &list); } if (variableValues(QStringLiteral("CONFIG")).contains(QStringLiteral("qt"))) { if (!list.contains(m_qtIncludeDir)) list << m_qtIncludeDir; QDir incDir(m_qtIncludeDir); auto modules = variableValues(QStringLiteral("QT")); if (!modules.isEmpty() && !modules.contains(QStringLiteral("core"))) { // TODO: proper dependency tracking of modules // for now, at least include core if we include any other module modules << QStringLiteral("core"); } // TODO: This is all very fragile, should rather read QMake module .pri files (e.g. qt_lib_core_private.pri) foreach (const QString& module, modules) { QString pattern = module; bool isPrivate = false; if (module.endsWith(QLatin1String("-private"))) { pattern.chop(qstrlen("-private")); isPrivate = true; } else if (module.endsWith(QLatin1String("_private"))) { // _private is less common, but still a valid suffix pattern.chop(qstrlen("_private")); isPrivate = true; } if (pattern == QLatin1String("qtestlib") || pattern == QLatin1String("testlib")) { pattern = QStringLiteral("QtTest"); } else if (pattern == QLatin1String("qaxcontainer")) { pattern = QStringLiteral("ActiveQt"); } else if (pattern == QLatin1String("qaxserver")) { pattern = QStringLiteral("ActiveQt"); } QFileInfoList match = incDir.entryInfoList({QString("Qt%1").arg(pattern)}, QDir::Dirs); if (match.isEmpty()) { // try non-prefixed pattern match = incDir.entryInfoList({pattern}, QDir::Dirs); if (match.isEmpty()) { qCWarning(KDEV_QMAKE) << "unhandled Qt module:" << module << pattern; continue; } } QString path = match.first().canonicalFilePath(); if (isPrivate) { path += '/' + m_qtVersion + '/' + match.first().fileName() + "/private/"; } if (!list.contains(path)) { list << path; } } } if (variableValues(QStringLiteral("CONFIG")).contains(QStringLiteral("thread"))) { addPathsForVariable(QStringLiteral("QMAKE_INCDIR_THREAD"), &list); } if (variableValues(QStringLiteral("CONFIG")).contains(QStringLiteral("x11"))) { addPathsForVariable(QStringLiteral("QMAKE_INCDIR_X11"), &list); } addPathsForVariable(QStringLiteral("MOC_DIR"), &list, outPwd()); addPathsForVariable(QStringLiteral("OBJECTS_DIR"), &list, outPwd()); addPathsForVariable(QStringLiteral("UI_DIR"), &list, outPwd()); ifDebug(qCDebug(KDEV_QMAKE) << "final list:" << list;) return list; } // Scan QMAKE_C*FLAGS for -F and -iframework and QMAKE_LFLAGS for good measure. Time will // tell if we need to scan the release/debug/... specific versions of QMAKE_C*FLAGS. // Also include QT_INSTALL_LIBS which corresponds to Qt's framework directory on OS X. QStringList QMakeProjectFile::frameworkDirectories() const { const auto variablesToCheck = {QStringLiteral("QMAKE_CFLAGS"), QStringLiteral("QMAKE_CXXFLAGS"), QStringLiteral("QMAKE_LFLAGS")}; const QLatin1String fOption("-F"); const QLatin1String iframeworkOption("-iframework"); QStringList fwDirs; for (const auto& var : variablesToCheck) { bool storeArg = false; foreach (const auto& arg, variableValues(var)) { if (arg == fOption || arg == iframeworkOption) { // detached -F/-iframework arg; set a warrant to store the next argument storeArg = true; } else { if (arg.startsWith(fOption)) { fwDirs << arg.mid(fOption.size()); } else if (arg.startsWith(iframeworkOption)) { fwDirs << arg.mid(iframeworkOption.size()); } else if (storeArg) { fwDirs << arg; } // cancel any outstanding warrants to store the next argument storeArg = false; } } } #ifdef Q_OS_OSX fwDirs << m_qtLibDir; #endif return fwDirs; } QStringList QMakeProjectFile::extraArguments() const { const auto variablesToCheck = {QStringLiteral("QMAKE_CXXFLAGS")}; const auto prefixes = { "-F", "-iframework", "-I", "-D" }; QStringList args; for (const auto& var : variablesToCheck) { foreach (const auto& arg, variableValues(var)) { auto argHasPrefix = [arg](const char* prefix) { return arg.startsWith(prefix); }; if ( !std::any_of(prefixes.begin(), prefixes.end(), argHasPrefix)) { args << arg; } } } return args; } QStringList QMakeProjectFile::files() const { ifDebug(qCDebug(KDEV_QMAKE) << "Fetching files";) QStringList list; foreach (const QString& variable, QMakeProjectFile::FileVariables) { foreach (const QString& value, variableValues(variable)) { list += resolveFileName(value); } } ifDebug(qCDebug(KDEV_QMAKE) << "found" << list.size() << "files";) return list; } QStringList QMakeProjectFile::filesForTarget(const QString& s) const { ifDebug(qCDebug(KDEV_QMAKE) << "Fetching files";) QStringList list; if (variableValues(QStringLiteral("INSTALLS")).contains(s)) { const QStringList files = variableValues(s + ".files"); for (const QString& val : files) { list += QStringList(resolveFileName(val)); } } if (!variableValues(QStringLiteral("INSTALLS")).contains(s) || s == QLatin1String("target")) { foreach (const QString& variable, QMakeProjectFile::FileVariables) { foreach (const QString& value, variableValues(variable)) { list += QStringList(resolveFileName(value)); } } } ifDebug(qCDebug(KDEV_QMAKE) << "found" << list.size() << "files";) return list; } QString QMakeProjectFile::getTemplate() const { QString templ = QStringLiteral("app"); if (!variableValues(QStringLiteral("TEMPLATE")).isEmpty()) { templ = variableValues(QStringLiteral("TEMPLATE")).first(); } return templ; } QStringList QMakeProjectFile::targets() const { ifDebug(qCDebug(KDEV_QMAKE) << "Fetching targets";) QStringList list; list += variableValues(QStringLiteral("TARGET")); if (list.isEmpty() && getTemplate() != QLatin1String("subdirs")) { list += QFileInfo(absoluteFile()).baseName(); } - foreach (const QString& target, variableValues("INSTALLS")) { + foreach (const QString& target, variableValues(QStringLiteral("INSTALLS"))) { if (!target.isEmpty() && target != QLatin1String("target")) list << target; } if (list.removeAll(QString())) { // remove empty targets - which is probably a bug... qCWarning(KDEV_QMAKE) << "got empty entry in TARGET of file" << absoluteFile(); } ifDebug(qCDebug(KDEV_QMAKE) << "found" << list.size() << "targets";) return list; } QMakeProjectFile::~QMakeProjectFile() { // TODO: delete cache, specs, ...? } QStringList QMakeProjectFile::resolveVariable(const QString& variable, VariableInfo::VariableType type) const { if (type == VariableInfo::QtConfigVariable) { if (m_mkspecs->isQMakeInternalVariable(variable)) { return QStringList() << m_mkspecs->qmakeInternalVariable(variable); } else { qCWarning(KDEV_QMAKE) << "unknown QtConfig Variable:" << variable; return QStringList(); } } return QMakeFile::resolveVariable(variable, type); } QMakeMkSpecs* QMakeProjectFile::mkSpecs() const { return m_mkspecs; } QMakeCache* QMakeProjectFile::qmakeCache() const { return m_cache; } QList QMakeProjectFile::defines() const { QList d; - foreach (QString def, variableMap()["DEFINES"]) { + foreach (QString def, variableMap()[QStringLiteral("DEFINES")]) { int pos = def.indexOf('='); if (pos >= 0) { // a value is attached to define d.append(DefinePair(def.left(pos), def.mid(pos + 1))); } else { // a value-less define d.append(DefinePair(def, QString())); } } return d; } QString QMakeProjectFile::pwd() const { return absoluteDir(); } QString QMakeProjectFile::outPwd() const { if (!project()) { return absoluteDir(); } else { return QMakeConfig::buildDirFromSrc(project(), KDevelop::Path(absoluteDir())).toLocalFile(); } } QString QMakeProjectFile::proFile() const { return absoluteFile(); } QString QMakeProjectFile::proFilePwd() const { return absoluteDir(); } diff --git a/plugins/qmljs/duchain/cache.cpp b/plugins/qmljs/duchain/cache.cpp index 227905f2e2..d30ce7cc72 100644 --- a/plugins/qmljs/duchain/cache.cpp +++ b/plugins/qmljs/duchain/cache.cpp @@ -1,262 +1,262 @@ /* * This file is part of qmljs, the QML/JS language support plugin for KDevelop * Copyright (c) 2013 Sven Brauch * Copyright (c) 2014 Denis Steckelmacher * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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, see . * */ #include "cache.h" #include "debug.h" #include #include #include #include #include #include QmlJS::Cache::Cache() { // qmlplugindump from Qt4 and Qt5. They will be tried in order when dumping // a binary QML file. m_pluginDumpExecutables << PluginDumpExecutable(QStringLiteral("qmlplugindump"), QStringLiteral("1.0")) << PluginDumpExecutable(QStringLiteral("qmlplugindump-qt4"), QStringLiteral("1.0")) << PluginDumpExecutable(QStringLiteral("qmlplugindump-qt5"), QStringLiteral("2.0")) << PluginDumpExecutable(QStringLiteral("qml1plugindump-qt5"), QStringLiteral("1.0")); } QmlJS::Cache& QmlJS::Cache::instance() { static Cache *c = nullptr; if (!c) { c = new Cache(); } return *c; } QString QmlJS::Cache::modulePath(const KDevelop::IndexedString& baseFile, const QString& uri, const QString& version) { QMutexLocker lock(&m_mutex); QString cacheKey = uri + version; QString path = m_modulePaths.value(cacheKey, QString()); if (!path.isEmpty()) { return path; } // List of the paths in which the modules will be looked for KDevelop::Path::List paths; for (auto path : QCoreApplication::instance()->libraryPaths()) { KDevelop::Path p(path); // Change /path/to/qt5/plugins to /path/to/qt5/{qml,imports} paths << p.cd(QStringLiteral("../qml")); paths << p.cd(QStringLiteral("../imports")); } paths << m_includeDirs[baseFile]; // Find the path for which /u/r/i exists QString fragment = QString(uri).replace(QLatin1Char('.'), QDir::separator()); bool isVersion1 = version.startsWith(QLatin1String("1.")); bool isQtQuick = (uri == QLatin1String("QtQuick")); const QStringList modulesWithoutVersionSuffix{"QtQml", "QtMultimedia", "QtQuick.LocalStorage", "QtQuick.XmlListModel"}; if (!version.isEmpty() && !isVersion1 && !modulesWithoutVersionSuffix.contains(uri)) { // Modules having a version greater or equal to 2 are stored in a directory // name like QtQuick.2 fragment += QLatin1Char('.') + version.section(QLatin1Char('.'), 0, 0); } for (auto p : paths) { QString pathString = p.cd(fragment).path(); // HACK: QtQuick 1.0 is put in $LIB/qt5/imports/builtins.qmltypes. The "QtQuick" // identifier appears nowhere. if (isQtQuick && isVersion1) { if (QFile::exists(p.cd(QStringLiteral("builtins.qmltypes")).path())) { path = p.path(); break; } } else if (QFile::exists(pathString + QLatin1String("/plugins.qmltypes"))) { path = pathString; break; } } m_modulePaths.insert(cacheKey, path); return path; } QStringList QmlJS::Cache::getFileNames(const QFileInfoList& fileInfos) { QStringList result; for (const QFileInfo& fileInfo : fileInfos) { QString filePath = fileInfo.canonicalFilePath(); // If the module directory contains a plugins.qmltypes files, use it // and skip everything else if (filePath.endsWith(QLatin1String("plugins.qmltypes"))) { return QStringList() << filePath; - } else if (fileInfo.dir().exists(QLatin1String("plugins.qmltypes"))) { - return {fileInfo.dir().filePath(QLatin1String("plugins.qmltypes"))}; + } else if (fileInfo.dir().exists(QStringLiteral("plugins.qmltypes"))) { + return {fileInfo.dir().filePath(QStringLiteral("plugins.qmltypes"))}; } // Non-so files don't need any treatment if (!filePath.endsWith(QLatin1String(".so"))) { result.append(filePath); continue; } // Use the cache to speed-up reparses { QMutexLocker lock(&m_mutex); if (m_modulePaths.contains(filePath)) { QString cachedFilePath = m_modulePaths.value(filePath); if (!cachedFilePath.isEmpty()) { result.append(cachedFilePath); } continue; } } // Locate an existing dump of the file QString dumpFile = QStringLiteral("kdevqmljssupport/%1.qml").arg( QString::fromLatin1(QCryptographicHash::hash(filePath.toUtf8(), QCryptographicHash::Md5).toHex()) ); QString dumpPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, dumpFile ); if (!dumpPath.isEmpty()) { QMutexLocker lock(&m_mutex); result.append(dumpPath); m_modulePaths.insert(filePath, dumpPath); continue; } // Create a dump of the file const QStringList args = {QStringLiteral("-noinstantiate"), QStringLiteral("-path"), filePath}; for (const PluginDumpExecutable& executable : m_pluginDumpExecutables) { QProcess qmlplugindump; qmlplugindump.setProcessChannelMode(QProcess::SeparateChannels); qmlplugindump.start(executable.executable, args, QIODevice::ReadOnly); qCDebug(KDEV_QMLJS_DUCHAIN) << "starting qmlplugindump with args:" << executable.executable << args << qmlplugindump.state() << fileInfo.absolutePath(); if (!qmlplugindump.waitForFinished(3000)) { if (qmlplugindump.state() == QProcess::Running) { qCWarning(KDEV_QMLJS_DUCHAIN) << "qmlplugindump didn't finish in time -- killing"; qmlplugindump.kill(); qmlplugindump.waitForFinished(100); } else { qCDebug(KDEV_QMLJS_DUCHAIN) << "qmlplugindump attempt failed" << qmlplugindump.program() << qmlplugindump.arguments() << qmlplugindump.readAllStandardError(); } continue; } if (qmlplugindump.exitCode() != 0) { qCWarning(KDEV_QMLJS_DUCHAIN) << "qmlplugindump finished with exit code:" << qmlplugindump.exitCode(); continue; } // Open a file in which the dump can be written QFile dumpFile( QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + dumpPath ); if (dumpFile.open(QIODevice::WriteOnly)) { qmlplugindump.readLine(); // Skip "import QtQuick.tooling 1.1" dumpFile.write("// " + filePath.toUtf8() + '\n'); dumpFile.write("import QtQuick " + executable.quickVersion.toUtf8() + '\n'); dumpFile.write(qmlplugindump.readAllStandardOutput()); dumpFile.close(); result.append(dumpFile.fileName()); QMutexLocker lock(&m_mutex); m_modulePaths.insert(filePath, dumpFile.fileName()); break; } } } return result; } void QmlJS::Cache::setFileCustomIncludes(const KDevelop::IndexedString& file, const KDevelop::Path::List& dirs) { QMutexLocker lock(&m_mutex); m_includeDirs[file] = dirs; } void QmlJS::Cache::addDependency(const KDevelop::IndexedString& file, const KDevelop::IndexedString& dependency) { QMutexLocker lock(&m_mutex); m_dependees[dependency].insert(file); m_dependencies[file].insert(dependency); } QList QmlJS::Cache::filesThatDependOn(const KDevelop::IndexedString& file) { QMutexLocker lock(&m_mutex); return m_dependees[file].toList(); } QList QmlJS::Cache::dependencies(const KDevelop::IndexedString& file) { QMutexLocker lock(&m_mutex); return m_dependencies[file].toList(); } bool QmlJS::Cache::isUpToDate(const KDevelop::IndexedString& file) { QMutexLocker lock(&m_mutex); return m_isUpToDate.value(file, false); } void QmlJS::Cache::setUpToDate(const KDevelop::IndexedString& file, bool upToDate) { QMutexLocker lock(&m_mutex); m_isUpToDate[file] = upToDate; }