diff --git a/addons/katebuild-plugin/plugin_katebuild.cpp b/addons/katebuild-plugin/plugin_katebuild.cpp index feac5bd83..cc8952c77 100644 --- a/addons/katebuild-plugin/plugin_katebuild.cpp +++ b/addons/katebuild-plugin/plugin_katebuild.cpp @@ -1,1055 +1,1049 @@ /* plugin_katebuild.c Kate Plugin ** ** Copyright (C) 2013 by Alexander Neundorf ** Copyright (C) 2006-2015 by Kåre Särs ** Copyright (C) 2011 by Ian Wakeling ** ** This code is mostly a modification of the GPL'ed Make plugin ** by Adriaan de Groot. */ /* ** 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 in a file called COPYING; if not, write to ** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, ** MA 02110-1301, USA. */ #include "plugin_katebuild.h" #include -#include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "SelectTargetView.h" K_PLUGIN_FACTORY_WITH_JSON (KateBuildPluginFactory, "katebuildplugin.json", registerPlugin();) static const QString DefConfigCmd = QStringLiteral("cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=/usr/local ../"); static const QString DefConfClean; static const QString DefTargetName = QStringLiteral("all"); static const QString DefBuildCmd = QStringLiteral("make"); static const QString DefCleanCmd = QStringLiteral("make clean"); /******************************************************************/ KateBuildPlugin::KateBuildPlugin(QObject *parent, const VariantList&): KTextEditor::Plugin(parent) { // KF5 FIXME KGlobal::locale()->insertCatalog("katebuild-plugin"); } /******************************************************************/ QObject *KateBuildPlugin::createView (KTextEditor::MainWindow *mainWindow) { return new KateBuildView(this, mainWindow); } /******************************************************************/ KateBuildView::KateBuildView(KTextEditor::Plugin *plugin, KTextEditor::MainWindow *mw) : QObject (mw) , m_win(mw) , m_buildWidget(0) , m_outputWidgetWidth(0) , m_proc(this) , m_stdOut() , m_stdErr() , m_buildCancelled(false) , m_displayModeBeforeBuild(1) // NOTE this will not allow spaces in file names. // e.g. from gcc: "main.cpp:14: error: cannot convert ‘std::string’ to ‘int’ in return" , m_filenameDetector(QStringLiteral("(([a-np-zA-Z]:[\\\\/])?[a-zA-Z0-9_\\.\\-/\\\\]+\\.[a-zA-Z0-9]+):([0-9]+)(.*)")) // e.g. from icpc: "main.cpp(14): error: no suitable conversion function from "std::string" to "int" exists" , m_filenameDetectorIcpc(QStringLiteral("(([a-np-zA-Z]:[\\\\/])?[a-zA-Z0-9_\\.\\-/\\\\]+\\.[a-zA-Z0-9]+)\\(([0-9]+)\\)(:.*)")) , m_filenameDetectorGccWorked(false) , m_newDirDetector(QStringLiteral("make\\[.+\\]: .+ `.*'")) { KXMLGUIClient::setComponentName (QLatin1String("katebuild"), i18n ("Kate Build Plugin")); setXMLFile(QLatin1String("ui.rc")); m_toolView = mw->createToolView(plugin, QStringLiteral("kate_plugin_katebuildplugin"), KTextEditor::MainWindow::Bottom, QIcon::fromTheme(QStringLiteral("application-x-ms-dos-executable")), i18n("Build Output")); QAction *a = actionCollection()->addAction(QStringLiteral("select_target")); a->setText(i18n("Select Target...")); a->setIcon(QIcon::fromTheme(QStringLiteral("select"))); connect(a, SIGNAL(triggered(bool)), this, SLOT(slotSelectTarget())); a = actionCollection()->addAction(QStringLiteral("build_default_target")); a->setText(i18n("Build Default Target")); connect(a, SIGNAL(triggered(bool)), this, SLOT(slotBuildDefaultTarget())); a = actionCollection()->addAction(QStringLiteral("build_previous_target")); a->setText(i18n("Build Previous Target")); connect(a, SIGNAL(triggered(bool)), this, SLOT(slotBuildPreviousTarget())); a = actionCollection()->addAction(QStringLiteral("stop")); a->setText(i18n("Stop")); a->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete"))); connect(a, SIGNAL(triggered(bool)), this, SLOT(slotStop())); a = actionCollection()->addAction(QStringLiteral("goto_next")); a->setText(i18n("Next Error")); a->setIcon(QIcon::fromTheme(QStringLiteral("go-next"))); actionCollection()->setDefaultShortcut(a, Qt::SHIFT+Qt::ALT+Qt::Key_Right); connect(a, SIGNAL(triggered(bool)), this, SLOT(slotNext())); a = actionCollection()->addAction(QStringLiteral("goto_prev")); a->setText(i18n("Previous Error")); a->setIcon(QIcon::fromTheme(QStringLiteral("go-previous"))); actionCollection()->setDefaultShortcut(a, Qt::SHIFT+Qt::ALT+Qt::Key_Left); connect(a, SIGNAL(triggered(bool)), this, SLOT(slotPrev())); m_buildWidget = new QWidget(m_toolView); m_buildUi.setupUi(m_buildWidget); m_targetsUi = new TargetsUi(this, m_buildUi.u_tabWidget); m_buildUi.u_tabWidget->insertTab(0, m_targetsUi, i18nc("Tab label", "Target Settings")); m_buildUi.u_tabWidget->setCurrentWidget(m_targetsUi); m_buildWidget->installEventFilter(this); m_buildUi.buildAgainButton->setVisible(true); m_buildUi.cancelBuildButton->setVisible(true); m_buildUi.buildStatusLabel->setVisible(true); m_buildUi.buildAgainButton2->setVisible(false); m_buildUi.cancelBuildButton2->setVisible(false); m_buildUi.buildStatusLabel2->setVisible(false); m_buildUi.extraLineLayout->setAlignment(Qt::AlignRight); m_buildUi.cancelBuildButton->setEnabled(false); m_buildUi.cancelBuildButton2->setEnabled(false); connect(m_buildUi.errTreeWidget, SIGNAL(itemClicked(QTreeWidgetItem*,int)), SLOT(slotErrorSelected(QTreeWidgetItem*))); m_buildUi.plainTextEdit->setReadOnly(true); slotDisplayMode(FullOutput); connect(m_buildUi.displayModeSlider, SIGNAL(valueChanged(int)), this, SLOT(slotDisplayMode(int))); connect(m_buildUi.buildAgainButton, SIGNAL(clicked()), this, SLOT(slotBuildPreviousTarget())); connect(m_buildUi.cancelBuildButton, SIGNAL(clicked()), this, SLOT(slotStop())); connect(m_buildUi.buildAgainButton2, SIGNAL(clicked()), this, SLOT(slotBuildPreviousTarget())); connect(m_buildUi.cancelBuildButton2, SIGNAL(clicked()), this, SLOT(slotStop())); connect(m_targetsUi->newTarget, SIGNAL(clicked()), this, SLOT(targetSetNew())); connect(m_targetsUi->copyTarget, SIGNAL(clicked()), this, SLOT(targetOrSetCopy())); connect(m_targetsUi->deleteTarget, SIGNAL(clicked()), this, SLOT(targetDelete())); connect(m_targetsUi->addButton, SIGNAL(clicked()), this, SLOT(slotAddTargetClicked())); connect(m_targetsUi->buildButton, SIGNAL(clicked()), this, SLOT(slotBuildActiveTarget())); connect(m_targetsUi, SIGNAL(enterPressed()), this, SLOT(slotBuildActiveTarget())); m_proc.setOutputChannelMode(KProcess::SeparateChannels); connect(&m_proc, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(slotProcExited(int,QProcess::ExitStatus))); connect(&m_proc, SIGNAL(readyReadStandardError()),this, SLOT(slotReadReadyStdErr())); connect(&m_proc, SIGNAL(readyReadStandardOutput()),this, SLOT(slotReadReadyStdOut())); connect(m_win, SIGNAL(unhandledShortcutOverride(QEvent*)), this, SLOT(handleEsc(QEvent*))); m_toolView->installEventFilter(this); m_win->guiFactory()->addClient(this); // watch for project plugin view creation/deletion connect(m_win, SIGNAL(pluginViewCreated (const QString &, QObject *)) , this, SLOT(slotPluginViewCreated (const QString &, QObject *))); connect(m_win, SIGNAL(pluginViewDeleted (const QString &, QObject *)) , this, SLOT(slotPluginViewDeleted (const QString &, QObject *))); // update once project plugin state manually m_projectPluginView = m_win->pluginView (QStringLiteral("kateprojectplugin")); slotProjectMapChanged (); } /******************************************************************/ KateBuildView::~KateBuildView() { m_win->guiFactory()->removeClient( this ); delete m_toolView; } /******************************************************************/ QWidget *KateBuildView::toolView() const { return m_toolView; } /******************************************************************/ void KateBuildView::readSessionConfig(const KConfigGroup& cg) { int numTargets = cg.readEntry(QStringLiteral("NumTargets"), 0); m_targetsUi->targetsModel.clear(); int tmpIndex; int tmpCmd; if (numTargets == 0 ) { // either the config is empty or uses the older format m_targetsUi->targetsModel.addTargetSet(i18n("Target Set"), QString()); m_targetsUi->targetsModel.addCommand(0, i18n("build"), cg.readEntry(QStringLiteral("Make Command"), DefBuildCmd)); m_targetsUi->targetsModel.addCommand(0, i18n("clean"), cg.readEntry(QStringLiteral("Clean Command"), DefCleanCmd)); m_targetsUi->targetsModel.addCommand(0, i18n("config"), DefConfigCmd); QString quickCmd = cg.readEntry(QStringLiteral("Quick Compile Command")); if (!quickCmd.isEmpty()) { m_targetsUi->targetsModel.addCommand(0, i18n("quick"), quickCmd); } tmpIndex = 0; tmpCmd = 0; } else { for (int i=0; itargetsModel.addTargetSet(targetSetName, buildDir); if (targetNames.isEmpty()) { QString quickCmd = cg.readEntry(QStringLiteral("%1 QuickCmd").arg(i)); m_targetsUi->targetsModel.addCommand(i, i18n("build"), cg.readEntry(QStringLiteral("%1 BuildCmd"), DefBuildCmd)); m_targetsUi->targetsModel.addCommand(i, i18n("clean"), cg.readEntry(QStringLiteral("%1 CleanCmd"), DefCleanCmd)); if (!quickCmd.isEmpty()) { m_targetsUi->targetsModel.addCommand(i, i18n("quick"), quickCmd); } m_targetsUi->targetsModel.setDefaultCmd(i, i18n("build")); } else { for (int tn=0; tntargetsModel.addCommand(i, targetName, cg.readEntry(QStringLiteral("%1 BuildCmd %2").arg(i).arg(targetName), DefBuildCmd)); } QString defCmd = cg.readEntry(QStringLiteral("%1 Target Default").arg(i), QString()); m_targetsUi->targetsModel.setDefaultCmd(i, defCmd); } } tmpIndex = cg.readEntry(QStringLiteral("Active Target Index"), 0); tmpCmd = cg.readEntry(QStringLiteral("Active Target Command"), 0); } m_targetsUi->targetsView->expandAll(); m_targetsUi->targetsView->resizeColumnToContents(0); m_targetsUi->targetsView->collapseAll(); QModelIndex root = m_targetsUi->targetsModel.index(tmpIndex); QModelIndex cmdIndex = m_targetsUi->targetsModel.index(tmpCmd, 0, root); m_targetsUi->targetsView->setCurrentIndex(cmdIndex); } /******************************************************************/ void KateBuildView::writeSessionConfig(KConfigGroup& cg) { m_targetsUi->targetsModel.deleteTargetSet(i18n("Project Plugin Targets")); QList targets = m_targetsUi->targetsModel.targetSets(); cg.writeEntry("NumTargets", targets.size()); for (int i=0; itargetsView->currentIndex(); if (ind.internalId() == TargetModel::InvalidIndex) { set = ind.row(); } else { set = ind.internalId(); setRow = ind.row(); } if (setRow < 0) setRow = 0; cg.writeEntry(QStringLiteral("Active Target Index"), set); cg.writeEntry(QStringLiteral("Active Target Command"), setRow); slotAddProjectTarget(); } /******************************************************************/ void KateBuildView::slotNext() { const int itemCount = m_buildUi.errTreeWidget->topLevelItemCount(); if (itemCount == 0) { return; } QTreeWidgetItem *item = m_buildUi.errTreeWidget->currentItem(); if (item && item->isHidden()) item = 0; int i = (item == 0) ? -1 : m_buildUi.errTreeWidget->indexOfTopLevelItem(item); while (++i < itemCount) { item = m_buildUi.errTreeWidget->topLevelItem(i); if (!item->text(1).isEmpty() && !item->isHidden()) { m_buildUi.errTreeWidget->setCurrentItem(item); m_buildUi.errTreeWidget->scrollToItem(item); slotErrorSelected(item); return; } } } /******************************************************************/ void KateBuildView::slotPrev() { const int itemCount = m_buildUi.errTreeWidget->topLevelItemCount(); if (itemCount == 0) { return; } QTreeWidgetItem *item = m_buildUi.errTreeWidget->currentItem(); if (item && item->isHidden()) item = 0; int i = (item == 0) ? itemCount : m_buildUi.errTreeWidget->indexOfTopLevelItem(item); while (--i >= 0) { item = m_buildUi.errTreeWidget->topLevelItem(i); if (!item->text(1).isEmpty() && !item->isHidden()) { m_buildUi.errTreeWidget->setCurrentItem(item); m_buildUi.errTreeWidget->scrollToItem(item); slotErrorSelected(item); return; } } } /******************************************************************/ void KateBuildView::slotErrorSelected(QTreeWidgetItem *item) { // get stuff const QString filename = item->data(0, Qt::UserRole).toString(); if (filename.isEmpty()) return; const int line = item->data(1, Qt::UserRole).toInt(); const int column = item->data(2, Qt::UserRole).toInt(); // open file (if needed, otherwise, this will activate only the right view...) m_win->openUrl(QUrl::fromLocalFile(filename)); // any view active? if (!m_win->activeView()) { return; } // do it ;) m_win->activeView()->setCursorPosition(KTextEditor::Cursor(line-1, column)); m_win->activeView()->setFocus(); } /******************************************************************/ void KateBuildView::addError(const QString &filename, const QString &line, const QString &column, const QString &message) { bool isError=false; bool isWarning=false; QTreeWidgetItem* item = new QTreeWidgetItem(m_buildUi.errTreeWidget); item->setBackground(1, Qt::gray); // The strings are twice in case kate is translated but not make. if (message.contains(QStringLiteral("error")) || message.contains(i18nc("The same word as 'make' uses to mark an error.","error")) || message.contains(QStringLiteral("undefined reference")) || message.contains(i18nc("The same word as 'ld' uses to mark an ...","undefined reference")) ) { isError=true; item->setForeground(1, Qt::red); m_numErrors++; item->setHidden(false); } if (message.contains(QStringLiteral("warning")) || message.contains(i18nc("The same word as 'make' uses to mark a warning.","warning")) ) { isWarning=true; item->setForeground(1, Qt::yellow); m_numWarnings++; item->setHidden(m_buildUi.displayModeSlider->value() > 2); } item->setTextAlignment(1, Qt::AlignRight); // visible text //remove path from visible file name QFileInfo file(filename); item->setText(0, file.fileName()); item->setText(1, line); item->setText(2, message.trimmed()); // used to read from when activating an item item->setData(0, Qt::UserRole, filename); item->setData(1, Qt::UserRole, line); item->setData(2, Qt::UserRole, column); if ((isError==false) && (isWarning==false)) { item->setHidden(m_buildUi.displayModeSlider->value() > 1); } item->setData(0, IsErrorRole, isError); item->setData(0, IsWarningRole, isWarning); // add tooltips in all columns // The enclosing ... enables word-wrap for long error messages item->setData(0, Qt::ToolTipRole, filename); item->setData(1, Qt::ToolTipRole, QStringLiteral("%1").arg(message)); item->setData(2, Qt::ToolTipRole, QStringLiteral("%1").arg(message)); } /******************************************************************/ QUrl KateBuildView::docUrl() { KTextEditor::View *kv = m_win->activeView(); if (!kv) { qDebug() << "no KTextEditor::View" << endl; return QUrl(); } if (kv->document()->isModified()) kv->document()->save(); return kv->document()->url(); } /******************************************************************/ bool KateBuildView::checkLocal(const QUrl &dir) { if (dir.path().isEmpty()) { KMessageBox::sorry(0, i18n("There is no file or directory specified for building.")); return false; } else if (!dir.isLocalFile()) { KMessageBox::sorry(0, i18n("The file \"%1\" is not a local file. " "Non-local files cannot be compiled.", dir.path())); return false; } return true; } /******************************************************************/ void KateBuildView::clearBuildResults() { m_buildUi.plainTextEdit->clear(); m_buildUi.errTreeWidget->clear(); m_stdOut.clear(); m_stdErr.clear(); m_numErrors = 0; m_numWarnings = 0; m_make_dir_stack.clear(); } /******************************************************************/ bool KateBuildView::startProcess(const QString &dir, const QString &command) { if (m_proc.state() != QProcess::NotRunning) { return false; } // clear previous runs clearBuildResults(); // activate the output tab m_buildUi.u_tabWidget->setCurrentIndex(1); m_displayModeBeforeBuild = m_buildUi.displayModeSlider->value(); m_buildUi.displayModeSlider->setValue(0); m_win->showToolView(m_toolView); // set working directory m_make_dir = dir; m_make_dir_stack.push(m_make_dir); // FIXME check m_proc.setWorkingDirectory(m_make_dir); m_proc.setShellCommand(command); m_proc.start(); if(!m_proc.waitForStarted(500)) { KMessageBox::error(0, i18n("Failed to run \"%1\". exitStatus = %2", command, m_proc.exitStatus())); return false; } m_buildUi.cancelBuildButton->setEnabled(true); m_buildUi.cancelBuildButton2->setEnabled(true); m_buildUi.buildAgainButton->setEnabled(false); m_buildUi.buildAgainButton2->setEnabled(false); QApplication::setOverrideCursor(QCursor(Qt::BusyCursor)); return true; } /******************************************************************/ bool KateBuildView::slotStop() { if (m_proc.state() != QProcess::NotRunning) { m_buildCancelled = true; QString msg = i18n("Building %1 cancelled", m_currentlyBuildingTarget); m_buildUi.buildStatusLabel->setText(msg); m_buildUi.buildStatusLabel2->setText(msg); m_proc.terminate(); return true; } return false; } /******************************************************************/ void KateBuildView::slotBuildActiveTarget() { if (!m_targetsUi->targetsView->currentIndex().isValid()) { slotSelectTarget(); } else { buildCurrentTarget(); } } /******************************************************************/ void KateBuildView::slotBuildPreviousTarget() { if (!m_previousIndex.isValid()) { slotSelectTarget(); } else { m_targetsUi->targetsView->setCurrentIndex(m_previousIndex); buildCurrentTarget(); } } /******************************************************************/ void KateBuildView::slotBuildDefaultTarget() { QModelIndex defaultTarget = m_targetsUi->targetsModel.defaultTarget(m_targetsUi->targetsView->currentIndex()); m_targetsUi->targetsView->setCurrentIndex(defaultTarget); buildCurrentTarget(); } /******************************************************************/ void KateBuildView::slotSelectTarget() { SelectTargetView *dialog = new SelectTargetView(&(m_targetsUi->targetsModel)); dialog->setCurrentIndex(m_targetsUi->targetsView->currentIndex()); int result = dialog->exec(); if (result == QDialog::Accepted) { m_targetsUi->targetsView->setCurrentIndex(dialog->currentIndex()); buildCurrentTarget(); } delete dialog; dialog = 0; } /******************************************************************/ bool KateBuildView::buildCurrentTarget() { if (m_proc.state() != QProcess::NotRunning) { displayBuildResult(i18n("Already building..."), KTextEditor::Message::Warning); return false; } QFileInfo docFInfo = docUrl().toLocalFile(); // docUrl() saves the current document QModelIndex ind = m_targetsUi->targetsView->currentIndex(); m_previousIndex = ind; if (!ind.isValid()) { KMessageBox::sorry(0, i18n("No target available for building.")); return false; } QString buildCmd = m_targetsUi->targetsModel.command(ind); QString cmdName = m_targetsUi->targetsModel.cmdName(ind); QString workDir = m_targetsUi->targetsModel.workDir(ind); QString targetSet = m_targetsUi->targetsModel.targetName(ind); QString dir = workDir; if (workDir.isEmpty()) { dir = docFInfo.absolutePath(); if (dir.isEmpty()) { KMessageBox::sorry(0, i18n("There is no local file or directory specified for building.")); return false; } } // Check if the command contains the file name or directory if (buildCmd.contains(QStringLiteral("%f")) || buildCmd.contains(QStringLiteral("%d")) || buildCmd.contains(QStringLiteral("%n"))) { if (docFInfo.absoluteFilePath().isEmpty()) { return false; } buildCmd.replace(QStringLiteral("%n"), docFInfo.baseName()); buildCmd.replace(QStringLiteral("%f"), docFInfo.absoluteFilePath()); buildCmd.replace(QStringLiteral("%d"), docFInfo.absolutePath()); } m_filenameDetectorGccWorked = false; m_currentlyBuildingTarget = QStringLiteral("%1: %2").arg(targetSet).arg(cmdName); m_buildCancelled = false; QString msg = i18n("Building target %1 ...", m_currentlyBuildingTarget); m_buildUi.buildStatusLabel->setText(msg); m_buildUi.buildStatusLabel2->setText(msg); return startProcess(dir, buildCmd); } /******************************************************************/ void KateBuildView::displayBuildResult(const QString &msg, KTextEditor::Message::MessageType level) { KTextEditor::View *kv = m_win->activeView(); if (!kv) return; delete m_infoMessage; m_infoMessage = new KTextEditor::Message(xi18nc("@info", "Make Results:%1", msg), level); m_infoMessage->setWordWrap(true); m_infoMessage->setPosition(KTextEditor::Message::BottomInView); m_infoMessage->setAutoHide(5000); m_infoMessage->setAutoHideMode(KTextEditor::Message::Immediate); m_infoMessage->setView(kv); kv->document()->postMessage(m_infoMessage); } /******************************************************************/ void KateBuildView::slotProcExited(int exitCode, QProcess::ExitStatus) { QApplication::restoreOverrideCursor(); m_buildUi.cancelBuildButton->setEnabled(false); m_buildUi.cancelBuildButton2->setEnabled(false); m_buildUi.buildAgainButton->setEnabled(true); m_buildUi.buildAgainButton2->setEnabled(true); QString buildStatus = i18n("Building %1 completed.", m_currentlyBuildingTarget); // did we get any errors? if (m_numErrors || m_numWarnings || (exitCode != 0)) { m_buildUi.u_tabWidget->setCurrentIndex(1); if (m_buildUi.displayModeSlider->value() == 0) { m_buildUi.displayModeSlider->setValue(m_displayModeBeforeBuild > 0 ? m_displayModeBeforeBuild: 1); } m_buildUi.errTreeWidget->resizeColumnToContents(0); m_buildUi.errTreeWidget->resizeColumnToContents(1); m_buildUi.errTreeWidget->resizeColumnToContents(2); m_buildUi.errTreeWidget->horizontalScrollBar()->setValue(0); //m_buildUi.errTreeWidget->setSortingEnabled(true); m_win->showToolView(m_toolView); } if (m_numErrors || m_numWarnings) { QStringList msgs; if (m_numErrors) { msgs << i18np("Found one error.", "Found %1 errors.", m_numErrors); buildStatus = i18n("Building %1 had errors.", m_currentlyBuildingTarget); } else if (m_numWarnings) { msgs << i18np("Found one warning.", "Found %1 warnings.", m_numWarnings); buildStatus = i18n("Building %1 had warnings.", m_currentlyBuildingTarget); } displayBuildResult(msgs.join(QLatin1Char('\n')), m_numErrors ? KTextEditor::Message::Error : KTextEditor::Message::Warning); } else if (exitCode != 0) { displayBuildResult(i18n("Build failed."), KTextEditor::Message::Warning); } else { displayBuildResult(i18n("Build completed without problems."), KTextEditor::Message::Positive); } if (!m_buildCancelled) { m_buildUi.buildStatusLabel->setText(buildStatus); m_buildUi.buildStatusLabel2->setText(buildStatus); m_buildCancelled = false; } } /******************************************************************/ void KateBuildView::slotReadReadyStdOut() { // read data from procs stdout and add // the text to the end of the output // FIXME This works for utf8 but not for all charsets QString l = QString::fromUtf8(m_proc.readAllStandardOutput()); l.remove(QLatin1Char('\r')); m_stdOut += l; // handle one line at a time do { const int end = m_stdOut.indexOf(QLatin1Char('\n')); if (end < 0) break; const QString line = m_stdOut.mid(0, end); m_buildUi.plainTextEdit->appendPlainText(line); //qDebug() << line; if (line.indexOf(m_newDirDetector) >=0) { //qDebug() << "Enter/Exit dir found"; int open = line.indexOf(QLatin1Char('`')); int close = line.indexOf(QLatin1Char('\'')); QString newDir = line.mid(open+1, close-open-1); //qDebug () << "New dir = " << newDir; if ((m_make_dir_stack.size() > 1) && (m_make_dir_stack.top() == newDir)) { m_make_dir_stack.pop(); newDir = m_make_dir_stack.top(); } else { m_make_dir_stack.push(newDir); } m_make_dir = newDir; } m_stdOut.remove(0, end + 1); } while (1); } /******************************************************************/ void KateBuildView::slotReadReadyStdErr() { // FIXME This works for utf8 but not for all charsets QString l = QString::fromUtf8(m_proc.readAllStandardError()); l.remove(QLatin1Char('\r')); m_stdErr += l; do { const int end = m_stdErr.indexOf(QLatin1Char('\n')); if (end < 0) break; const QString line = m_stdErr.mid(0, end); m_buildUi.plainTextEdit->appendPlainText(line); processLine(line); m_stdErr.remove(0, end + 1); } while (1); } /******************************************************************/ void KateBuildView::processLine(const QString &line) { //qDebug() << line ; //look for a filename - int index = m_filenameDetector.indexIn(line); + QRegularExpressionMatch match = m_filenameDetector.match(line); - QRegExp* rx = 0; - if (index >= 0) + if (match.hasMatch()) { m_filenameDetectorGccWorked = true; - rx = &m_filenameDetector; } else { if (!m_filenameDetectorGccWorked) { // let's see whether the icpc regexp works: // so for icpc users error detection will be a bit slower, // since always both regexps are checked. // But this should be the minority, for gcc and clang users // both regexes will only be checked until the first regex // matched the first time. - index = m_filenameDetectorIcpc.indexIn(line); - if (index >= 0) - { - rx = &m_filenameDetectorIcpc; - } + match = m_filenameDetectorIcpc.match(line); } } - if (!rx) + if (!match.hasMatch()) { addError(QString(), QStringLiteral("0"), QString(), line); //kDebug() << "A filename was not found in the line "; return; } - QString filename = rx->cap(1); - QString line_n = rx->cap(3); - QString msg = rx->cap(4); + QString filename = match.captured(1); + const QString line_n = match.captured(3); + const QString msg = match.captured(4); //qDebug() << "File Name:"<targetsView->currentIndex(); if (current.parent().isValid()) { current = current.parent(); } QModelIndex index = m_targetsUi->targetsModel.addCommand(current.row(), DefTargetName, DefBuildCmd); m_targetsUi->targetsView->setCurrentIndex(index); } /******************************************************************/ void KateBuildView::targetSetNew() { int row = m_targetsUi->targetsModel.addTargetSet(i18n("Target Set"), QString()); QModelIndex buildIndex = m_targetsUi->targetsModel.addCommand(row, i18n("Build"), DefBuildCmd); m_targetsUi->targetsModel.addCommand(row, i18n("Clean"), DefCleanCmd); m_targetsUi->targetsModel.addCommand(row, i18n("Config"), DefConfigCmd); m_targetsUi->targetsModel.addCommand(row, i18n("ConfigClean"), DefConfClean); m_targetsUi->targetsView->setCurrentIndex(buildIndex); } /******************************************************************/ void KateBuildView::targetOrSetCopy() { QModelIndex newIndex = m_targetsUi->targetsModel.copyTargetOrSet(m_targetsUi->targetsView->currentIndex()); if (m_targetsUi->targetsModel.hasChildren(newIndex)) { m_targetsUi->targetsView->setCurrentIndex(newIndex.child(0,0)); return; } m_targetsUi->targetsView->setCurrentIndex(newIndex); } /******************************************************************/ void KateBuildView::targetDelete() { QModelIndex current = m_targetsUi->targetsView->currentIndex(); m_targetsUi->targetsModel.deleteItem(current); if (m_targetsUi->targetsModel.rowCount() == 0) { targetSetNew(); } } /******************************************************************/ void KateBuildView::slotDisplayMode(int mode) { QTreeWidget *tree=m_buildUi.errTreeWidget; tree->setVisible(mode != 0); m_buildUi.plainTextEdit->setVisible(mode == 0); QString modeText; switch(mode) { case OnlyErrors: modeText = i18n("Only Errors"); break; case ErrorsAndWarnings: modeText = i18n("Errors and Warnings"); break; case ParsedOutput: modeText = i18n("Parsed Output"); break; case FullOutput: modeText = i18n("Full Output"); break; } m_buildUi.displayModeLabel->setText(modeText); if (mode < 1) { return; } const int itemCount = tree->topLevelItemCount(); for (int i=0;itopLevelItem(i); if ( (item->data(0, IsWarningRole).toBool()==false) && (item->data(0, IsErrorRole).toBool()==false) ) { item->setHidden(mode > 1); } if (item->data(0, IsWarningRole).toBool()==true) { item->setHidden(mode > 2); } if (item->data(0, IsErrorRole).toBool()==true) { item->setHidden(false); } } } /******************************************************************/ void KateBuildView::slotPluginViewCreated (const QString &name, QObject *pluginView) { // add view if (name == QLatin1String("kateprojectplugin")) { m_projectPluginView = pluginView; slotProjectMapChanged (); connect (pluginView, SIGNAL(projectMapChanged()), this, SLOT(slotProjectMapChanged())); } } /******************************************************************/ void KateBuildView::slotPluginViewDeleted (const QString &name, QObject *) { // remove view if (name == QLatin1String("kateprojectplugin")) { m_projectPluginView = 0; m_targetsUi->targetsModel.deleteTargetSet(i18n("Project Plugin Targets")); } } /******************************************************************/ void KateBuildView::slotProjectMapChanged () { // only do stuff with valid project if (!m_projectPluginView) { return; } m_targetsUi->targetsModel.deleteTargetSet(i18n("Project Plugin Targets")); slotAddProjectTarget(); } /******************************************************************/ void KateBuildView::slotAddProjectTarget() { // only do stuff with valid project if (!m_projectPluginView) { return; } // query new project map QVariantMap projectMap = m_projectPluginView->property("projectMap").toMap(); // do we have a valid map for build settings? QVariantMap buildMap = projectMap.value(QStringLiteral("build")).toMap(); if (buildMap.isEmpty()) { return; } // Delete any old project plugin targets m_targetsUi->targetsModel.deleteTargetSet(i18n("Project Plugin Targets")); int set = m_targetsUi->targetsModel.addTargetSet(i18n("Project Plugin Targets"), buildMap.value(QStringLiteral("directory")).toString()); QVariantList targetsets = buildMap.value(QStringLiteral("targets")).toList(); foreach (const QVariant &targetVariant, targetsets) { QVariantMap targetMap = targetVariant.toMap(); QString tgtName = targetMap[QStringLiteral("name")].toString(); QString buildCmd = targetMap[QStringLiteral("build_cmd")].toString(); if (tgtName.isEmpty() || buildCmd.isEmpty()) { continue; } m_targetsUi->targetsModel.addCommand(set, tgtName, buildCmd); } QModelIndex ind = m_targetsUi->targetsModel.index(set); if (!ind.child(0,0).data().isValid()) { QString buildCmd = buildMap.value(QStringLiteral("build")).toString(); QString cleanCmd = buildMap.value(QStringLiteral("clean")).toString(); QString quickCmd = buildMap.value(QStringLiteral("quick")).toString(); if (!buildCmd.isEmpty()) { // we have loaded an "old" project file (<= 4.12) m_targetsUi->targetsModel.addCommand(set, i18n("build"), buildCmd); } if (!cleanCmd.isEmpty()) { m_targetsUi->targetsModel.addCommand(set, i18n("clean"), cleanCmd); } if (!quickCmd.isEmpty()) { m_targetsUi->targetsModel.addCommand(set, i18n("quick"), quickCmd); } } } /******************************************************************/ bool KateBuildView::eventFilter(QObject *obj, QEvent *event) { if (event->type() == QEvent::KeyPress) { QKeyEvent *ke = static_cast(event); if ((obj == m_toolView) && (ke->key() == Qt::Key_Escape)) { m_win->hideToolView(m_toolView); event->accept(); return true; } } if ((event->type() == QEvent::Resize) && (obj == m_buildWidget)) { if (m_buildUi.u_tabWidget->currentIndex() == 1) { if ((m_outputWidgetWidth == 0) && m_buildUi.buildAgainButton->isVisible()) { QSize msh = m_buildWidget->minimumSizeHint(); m_outputWidgetWidth = msh.width(); } } bool useVertLayout = (m_buildWidget->width() < m_outputWidgetWidth); m_buildUi.buildAgainButton->setVisible(!useVertLayout); m_buildUi.cancelBuildButton->setVisible(!useVertLayout); m_buildUi.buildStatusLabel->setVisible(!useVertLayout); m_buildUi.buildAgainButton2->setVisible(useVertLayout); m_buildUi.cancelBuildButton2->setVisible(useVertLayout); m_buildUi.buildStatusLabel2->setVisible(useVertLayout); } return QObject::eventFilter(obj, event); } /******************************************************************/ void KateBuildView::handleEsc(QEvent *e) { if (!m_win) return; QKeyEvent *k = static_cast(e); if (k->key() == Qt::Key_Escape && k->modifiers() == Qt::NoModifier) { if (m_toolView->isVisible()) { m_win->hideToolView(m_toolView); } } } #include "plugin_katebuild.moc" // kate: space-indent on; indent-width 4; replace-tabs on; diff --git a/addons/katebuild-plugin/plugin_katebuild.h b/addons/katebuild-plugin/plugin_katebuild.h index 3d815289c..674df719b 100644 --- a/addons/katebuild-plugin/plugin_katebuild.h +++ b/addons/katebuild-plugin/plugin_katebuild.h @@ -1,179 +1,179 @@ #ifndef PLUGIN_KATEBUILD_H #define PLUGIN_KATEBUILD_H /* plugin_katebuild.h Kate Plugin ** ** Copyright (C) 2008-2015 by Kåre Särs ** ** This code is almost a total rewrite of the GPL'ed Make plugin ** by Adriaan de Groot. */ /* ** 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 in a file called COPYING; 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 #include #include #include #include #include "ui_build.h" #include "targets.h" /******************************************************************/ class KateBuildView : public QObject, public KXMLGUIClient, public KTextEditor::SessionConfigInterface { Q_OBJECT Q_INTERFACES(KTextEditor::SessionConfigInterface) Q_PROPERTY(QUrl docUrl READ docUrl) public: enum ResultDetails { FullOutput, ParsedOutput, ErrorsAndWarnings, OnlyErrors }; enum TreeWidgetRoles { IsErrorRole = Qt::UserRole+1, IsWarningRole }; KateBuildView(KTextEditor::Plugin *plugin, KTextEditor::MainWindow *mw); ~KateBuildView(); // reimplemented: read and write session config void readSessionConfig(const KConfigGroup& config) Q_DECL_OVERRIDE; void writeSessionConfig(KConfigGroup& config) Q_DECL_OVERRIDE; QWidget *toolView() const; bool buildCurrentTarget(); QUrl docUrl(); private Q_SLOTS: // Building void slotSelectTarget(); void slotBuildActiveTarget(); void slotBuildPreviousTarget(); void slotBuildDefaultTarget(); bool slotStop(); // Parse output void slotProcExited(int exitCode, QProcess::ExitStatus exitStatus); void slotReadReadyStdErr(); void slotReadReadyStdOut(); // Selecting warnings/errors void slotNext(); void slotPrev(); void slotErrorSelected(QTreeWidgetItem *item); // Settings void targetSetNew(); void targetOrSetCopy(); void targetDelete(); void slotAddTargetClicked(); void slotDisplayMode(int mode); void handleEsc(QEvent *e); /** * keep track if the project plugin is alive and if the project map did change */ void slotPluginViewCreated(const QString &name, QObject *pluginView); void slotPluginViewDeleted(const QString &name, QObject *pluginView); void slotProjectMapChanged(); void slotAddProjectTarget(); protected: bool eventFilter(QObject *obj, QEvent *ev) Q_DECL_OVERRIDE; private: void processLine(const QString &); void addError(const QString &filename, const QString &line, const QString &column, const QString &message); bool startProcess(const QString &dir, const QString &command); bool checkLocal(const QUrl &dir); void clearBuildResults(); void displayBuildResult(const QString &message, KTextEditor::Message::MessageType level); KTextEditor::MainWindow *m_win; QWidget *m_toolView; Ui::build m_buildUi; QWidget *m_buildWidget; int m_outputWidgetWidth; TargetsUi *m_targetsUi; KProcess m_proc; QString m_stdOut; QString m_stdErr; QString m_currentlyBuildingTarget; bool m_buildCancelled; int m_displayModeBeforeBuild; QString m_make_dir; QStack m_make_dir_stack; - QRegExp m_filenameDetector; - QRegExp m_filenameDetectorIcpc; + QRegularExpression m_filenameDetector; + QRegularExpression m_filenameDetectorIcpc; bool m_filenameDetectorGccWorked; - QRegExp m_newDirDetector; + QRegularExpression m_newDirDetector; unsigned int m_numErrors; unsigned int m_numWarnings; QString m_prevItemContent; QModelIndex m_previousIndex; QPointer m_infoMessage; /** * current project plugin view, if any */ QObject *m_projectPluginView; }; typedef QList VariantList; /******************************************************************/ class KateBuildPlugin : public KTextEditor::Plugin { Q_OBJECT public: explicit KateBuildPlugin(QObject* parent = 0, const VariantList& = VariantList()); virtual ~KateBuildPlugin() {} QObject *createView(KTextEditor::MainWindow *mainWindow) Q_DECL_OVERRIDE; }; #endif