diff --git a/RedPandaIDE/RedPandaIDE.pro b/RedPandaIDE/RedPandaIDE.pro index fd0c0b96..69518282 100644 --- a/RedPandaIDE/RedPandaIDE.pro +++ b/RedPandaIDE/RedPandaIDE.pro @@ -23,7 +23,8 @@ SOURCES += \ settingsdialog/settingsdialog.cpp \ settingsdialog/settingswidget.cpp \ systemconsts.cpp \ - utils.cpp + utils.cpp \ + widgets/issuestable.cpp HEADERS += \ compiler/compiler.h \ @@ -39,7 +40,9 @@ HEADERS += \ settingsdialog/settingsdialog.h \ settingsdialog/settingswidget.h \ systemconsts.h \ - utils.h + utils.h \ + common.h \ + widgets/issuestable.h FORMS += \ mainwindow.ui \ diff --git a/RedPandaIDE/common.h b/RedPandaIDE/common.h new file mode 100644 index 00000000..88859b40 --- /dev/null +++ b/RedPandaIDE/common.h @@ -0,0 +1,26 @@ +#ifndef COMMON_H +#define COMMON_H +#include +#include +#include +enum class CompileIssueType { + Other, + Warning, + Info, + Note, + Error, +}; + +struct CompileIssue { + QString filename; + int line; + int column; + QString description; + CompileIssueType type; +}; + +typedef std::shared_ptr PCompileIssue; + +Q_DECLARE_METATYPE(PCompileIssue); + +#endif // COMMON_H diff --git a/RedPandaIDE/compiler/compiler.cpp b/RedPandaIDE/compiler/compiler.cpp index 312bd6bf..85406de8 100644 --- a/RedPandaIDE/compiler/compiler.cpp +++ b/RedPandaIDE/compiler/compiler.cpp @@ -20,6 +20,8 @@ void Compiler::run() { emit compileStarted(); if (prepareForCompile()){ + mErrorCount = 0; + mWarningCount = 0; QElapsedTimer timer; timer.start(); runCommand(mCompiler, mArguments, QFileInfo(mCompiler).absolutePath()); @@ -27,8 +29,8 @@ void Compiler::run() log(""); log(tr("Compile Result:")); log("------------------"); - log(tr("- Errors: %1").arg(0)); - log(tr("- Warnings: %1").arg(0)); + log(tr("- Errors: %1").arg(mErrorCount)); + log(tr("- Warnings: %1").arg(mWarningCount)); if (!mOutputFile.isEmpty()) { log(tr("- Output Filename: %1").arg(mOutputFile)); QLocale locale = QLocale::system(); @@ -36,10 +38,136 @@ void Compiler::run() } log(tr("- Compilation Time: %1 secs").arg(timer.elapsed() / 1000.0)); } - this->deleteLater(); emit compileFinished(); } +QString Compiler::getFileNameFromOutputLine(QString &line) { + QString temp; + line = line.trimmed(); + while (true) { + int pos; + if (line.length() > 2 && line[1]==':') { // full file path at start, ignore this ':' + pos = line.indexOf(':',2); + } else { + pos = line.indexOf(':'); + } + if ( pos < 0) { + break; + } + temp = line.mid(0,pos); + line.remove(0,pos+1); + if (temp.compare("", Qt::CaseInsensitive)!=0 && !QFile(temp).exists()) { + continue; + } + + if (QFile(temp).fileName() == "ld.exe") { // skip ld.exe + continue; + } else { + break; + } + } + return temp; +} + +int Compiler::getLineNumberFromOutputLine(QString &line) +{ + line = line.trimmed(); + int pos = line.indexOf(':'); + int result=0; + if (pos < 0) { + pos = line.indexOf(','); + } + if (pos>=0) { + result = line.mid(0,pos).toInt(); + line.remove(0,pos+1); + } + return result; +} + +int Compiler::getColunmnFromOutputLine(QString &line) +{ + line = line.trimmed(); + int pos = line.indexOf(':'); + int result=0; + if (pos < 0) { + pos = line.indexOf(','); + } + if (pos>=0) { + result = line.mid(0,pos).toInt(); + line.remove(0,pos+1); + } + return result; +} + +CompileIssueType Compiler::getIssueTypeFromOutputLine(QString &line) +{ + CompileIssueType result = CompileIssueType::Other; + line = line.trimmed(); + int pos = line.indexOf(':'); + if (pos>=0) { + QString s=line.mid(0,pos); + if (s == "error" || s == "fatal error") { + mErrorCount += 1; + line = tr("[Error] ")+line.mid(pos+1); + result = CompileIssueType::Error; + } else if (s == "warning") { + mWarningCount += 1; + line = tr("[Warning] ")+line.mid(pos+1); + result = CompileIssueType::Warning; + } else if (s == "info") { + mWarningCount += 1; + line = tr("[Info] ")+line.mid(pos+1); + result = CompileIssueType::Info; + } else if (s == "note") { + mWarningCount += 1; + line = tr("[Note] ")+line.mid(pos+1); + result = CompileIssueType::Note; + } + } + return result; +} + +void Compiler::processOutput(QString &line) +{ + QString inFilePrefix = QString("In file included from "); + QString fromPrefix = QString("from "); + PCompileIssue issue = std::make_shared(); + QString description; + issue->type = CompileIssueType::Other; + if (line.startsWith(inFilePrefix)) { + line.remove(0,inFilePrefix.length()); + issue->filename = getFileNameFromOutputLine(line); + issue->line = getLineNumberFromOutputLine(line); + issue->column = getColunmnFromOutputLine(line); + issue->type = getIssueTypeFromOutputLine(line); + issue->description = inFilePrefix + issue->filename; + emit compileIssue(issue); + return; + } else if(line.startsWith(fromPrefix)) { + line.remove(0,fromPrefix.length()); + issue->filename = getFileNameFromOutputLine(line); + issue->line = getLineNumberFromOutputLine(line); + issue->column = getColunmnFromOutputLine(line); + issue->type = getIssueTypeFromOutputLine(line); + issue->description = " from " + issue->filename; + emit compileIssue(issue); + return; + } + + // Ignore code snippets that GCC produces + // they always start with a space + if (line.length()>0 && line[0] == ' ') { + return; + } + // assume regular main.cpp:line:col: message + issue->filename = getFileNameFromOutputLine(line); + issue->line = getLineNumberFromOutputLine(line); + issue->column = getColunmnFromOutputLine(line); + issue->type = getIssueTypeFromOutputLine(line); + issue->description = line.trimmed(); + emit compileIssue(issue); +} + void Compiler::stopCompile() { mStop = true; @@ -174,7 +302,7 @@ void Compiler::runCommand(const QString &cmd, const QString &arguments, const Q process.setWorkingDirectory(workingDir); process.connect(&process, &QProcess::readyReadStandardError,[&process,this](){ - this->log(process.readAllStandardError()); + this->error(process.readAllStandardError()); }); process.connect(&process, &QProcess::readyReadStandardOutput,[&process,this](){ this->log(process.readAllStandardOutput()); @@ -203,6 +331,10 @@ void Compiler::log(const QString &msg) void Compiler::error(const QString &msg) { - emit compileError(msg); + emit compileOutput(msg); + for (QString& s:msg.split("\n")) { + if (!s.isEmpty()) + processOutput(s); + } } diff --git a/RedPandaIDE/compiler/compiler.h b/RedPandaIDE/compiler/compiler.h index 81a42e58..7dde16d6 100644 --- a/RedPandaIDE/compiler/compiler.h +++ b/RedPandaIDE/compiler/compiler.h @@ -3,6 +3,7 @@ #include #include "settings.h" +#include "../common.h" class Compiler : public QThread { @@ -21,12 +22,17 @@ signals: void compileStarted(); void compileFinished(); void compileOutput(const QString& msg); - void compileError(const QString& errorMsg); + void compileIssue(PCompileIssue issue); public slots: void stopCompile(); protected: void run() override; + void processOutput(QString& line); + virtual QString getFileNameFromOutputLine(QString &line); + virtual int getLineNumberFromOutputLine(QString &line); + virtual int getColunmnFromOutputLine(QString &line); + virtual CompileIssueType getIssueTypeFromOutputLine(QString &line); protected: virtual Settings::PCompilerSet compilerSet() = 0; @@ -47,6 +53,8 @@ protected: QString mCompiler; QString mArguments; QString mOutputFile; + int mErrorCount; + int mWarningCount; private: bool mStop; diff --git a/RedPandaIDE/compiler/compilermanager.cpp b/RedPandaIDE/compiler/compilermanager.cpp index 40ba000f..943d4274 100644 --- a/RedPandaIDE/compiler/compilermanager.cpp +++ b/RedPandaIDE/compiler/compilermanager.cpp @@ -31,7 +31,7 @@ void CompilerManager::compile(const QString& filename, const QByteArray& encodin mCompiler = new FileCompiler(filename,encoding,silent,onlyCheckSyntax); connect(mCompiler, &Compiler::compileFinished, this ,&CompilerManager::onCompileFinished); connect(mCompiler, &Compiler::compileOutput, pMainWindow, &MainWindow::onCompileLog); - connect(mCompiler, &Compiler::compileError, pMainWindow, &MainWindow::onCompileError); + connect(mCompiler, &Compiler::compileIssue, pMainWindow, &MainWindow::onCompileIssue); mCompiler->start(); } @@ -41,7 +41,6 @@ void CompilerManager::run(const QString &filename, const QString &arguments, con if (mRunner!=nullptr) { return; } - qDebug()<<"lala1"; if (programHasConsole(filename)) { QString newArguments = QString(" 0 \"%1\" %2").arg(toLocalPath(filename)).arg(arguments); mRunner = new ExecutableRunner(includeTrailingPathDelimiter(pSettings->dirs().app())+"ConsolePauser.exe",newArguments,workDir); @@ -62,6 +61,7 @@ void CompilerManager::onCompileFinished() QMutexLocker locker(&compileMutex); qDebug() << "Compile finished"; mCompiler=nullptr; + delete mCompiler; } void CompilerManager::onRunnerTerminated() diff --git a/RedPandaIDE/compiler/filecompiler.h b/RedPandaIDE/compiler/filecompiler.h index 30c47d77..dfb3bf36 100644 --- a/RedPandaIDE/compiler/filecompiler.h +++ b/RedPandaIDE/compiler/filecompiler.h @@ -17,6 +17,7 @@ protected: private: QString mFileName; QByteArray mEncoding; + }; #endif // FILECOMPILER_H diff --git a/RedPandaIDE/editor.cpp b/RedPandaIDE/editor.cpp index 39fc53fd..1bfd3990 100644 --- a/RedPandaIDE/editor.cpp +++ b/RedPandaIDE/editor.cpp @@ -68,11 +68,17 @@ Editor::Editor(QWidget *parent, const QString& filename, this->setFolding(FoldStyle::BoxedTreeFoldStyle,3); this->setTabWidth(4); + this->setCaretLineVisible(true); + this->setCaretLineBackgroundColor(QColor(0xCCFFFF)); + this->setMatchedBraceForegroundColor(QColor("Red")); + + + this->setBraceMatching(BraceMatch::SloppyBraceMatch); //行号显示区域 setMarginType(0, QsciScintilla::NumberMargin); setMarginLineNumbers(0, true); setMarginWidth(0,"10"); - this->onLinesChanged(); + this->onLinesChanged(0,0); //断点设置区域 setMarginType(1, QsciScintilla::SymbolMargin); setMarginLineNumbers(1, false); @@ -107,8 +113,8 @@ Editor::Editor(QWidget *parent, const QString& filename, this,SLOT(onModificationChanged(bool))); connect(this , SIGNAL(cursorPositionChanged(int,int)), this, SLOT(onCursorPositionChanged(int,int))); - connect(this, SIGNAL(linesChanged()), - this,SLOT(onLinesChanged())); + connect(this, SIGNAL(linesChanged(int,int)), + this,SLOT(onLinesChanged(int, int))); } @@ -280,7 +286,7 @@ void Editor::wheelEvent(QWheelEvent *event) { } else { this->zoomOut(); } - onLinesChanged(); + onLinesChanged(0,0); } } @@ -292,10 +298,10 @@ void Editor::onCursorPositionChanged(int line, int index) { pMainWindow->updateStatusBarForEditingInfo(line,index+1,lines(),text().length()); } -void Editor::onLinesChanged() { +void Editor::onLinesChanged(int startLine, int count) { this->setMarginWidth(0,QString("0%1").arg(lines())); - qDebug()< #include #include +#include "common.h" Settings* createAppSettings(const QString& filepath = QString()) { QString filename; @@ -47,6 +48,9 @@ int main(int argc, char *argv[]) { QApplication app(argc, argv); + qRegisterMetaType("PCompileIssue"); + qRegisterMetaType("PCompileIssue&"); + //load translations QTranslator trans; trans.load(("RedPandaIDE_zh_CN")); diff --git a/RedPandaIDE/mainwindow.cpp b/RedPandaIDE/mainwindow.cpp index 810c722f..c57cd071 100644 --- a/RedPandaIDE/mainwindow.cpp +++ b/RedPandaIDE/mainwindow.cpp @@ -42,6 +42,12 @@ MainWindow::MainWindow(QWidget *parent) updateCompilerSet(); mCompilerManager = new CompilerManager(this); + + ui->actionIndent->setShortcut(Qt::Key_Tab); + ui->actionUnIndent->setShortcut(Qt::Key_Tab | Qt::ShiftModifier); + + ui->tableIssues->setErrorColor(QColor("Red")); + ui->tableIssues->setWarningColor(QColor("Orange")); } MainWindow::~MainWindow() @@ -177,15 +183,16 @@ void MainWindow::onCompileLog(const QString &msg) ui->txtCompilerOutput->appendPlainText(msg); } -void MainWindow::onCompileError(const QString &msg) +void MainWindow::onCompileIssue(PCompileIssue issue) { - qDebug()<tableIssues->addIssue(issue); } void MainWindow::on_actionCompile_triggered() { Editor * editor = mEditorList->getEditor(); if (editor != NULL ) { + ui->tableIssues->clearIssues(); mCompilerManager->compile(editor->filename(),editor->fileEncoding()); } } @@ -262,3 +269,28 @@ void MainWindow::on_actionUnIndent_triggered() editor->unindent(); } } + +void MainWindow::on_actionToggleComment_triggered() +{ + Editor * editor = mEditorList->getEditor(); + if (editor != NULL ) { + editor->toggleComment(); + } +} + +void MainWindow::on_actionUnfoldAll_triggered() +{ + Editor * editor = mEditorList->getEditor(); + if (editor != NULL ) { + editor->clearFolds(); + } +} + +void MainWindow::on_actionFoldAll_triggered() +{ + Editor * editor = mEditorList->getEditor(); + if (editor != NULL ) { + editor->clearFolds(); + editor->foldAll(); + } +} diff --git a/RedPandaIDE/mainwindow.h b/RedPandaIDE/mainwindow.h index ead51c93..77ab851c 100644 --- a/RedPandaIDE/mainwindow.h +++ b/RedPandaIDE/mainwindow.h @@ -2,6 +2,7 @@ #define MAINWINDOW_H #include +#include "common.h" QT_BEGIN_NAMESPACE @@ -63,10 +64,15 @@ private slots: void on_actionUnIndent_triggered(); + void on_actionToggleComment_triggered(); + + void on_actionUnfoldAll_triggered(); + + void on_actionFoldAll_triggered(); + public slots: void onCompileLog(const QString& msg); - - void onCompileError(const QString& msg); + void onCompileIssue(PCompileIssue issue); private: void setupActions(); @@ -81,7 +87,6 @@ private: QComboBox* mCompilerSet; CompilerManager* mCompilerManager; - // QWidget interface protected: void closeEvent(QCloseEvent *event) override; diff --git a/RedPandaIDE/mainwindow.ui b/RedPandaIDE/mainwindow.ui index 765bd9fd..b2f31b7c 100644 --- a/RedPandaIDE/mainwindow.ui +++ b/RedPandaIDE/mainwindow.ui @@ -160,9 +160,44 @@ 0 + + + Issues + + + + 5 + + + 5 + + + 5 + + + 5 + + + + + QAbstractItemView::NoEditTriggers + + + false + + + false + + + QAbstractItemView::SingleSelection + + + + + - Compiler Output + Compile Log @@ -247,6 +282,11 @@ + + + + + @@ -488,7 +528,32 @@ UnIndent + + + Toggle Comment + + + Ctrl+/ + + + + + Collapse All + + + + + Uncollapse All + + + + + IssuesTable + QTableView +
widgets/issuestable.h
+
+
diff --git a/RedPandaIDE/systemconsts.h b/RedPandaIDE/systemconsts.h index 0c807fa7..e1f1e7c0 100644 --- a/RedPandaIDE/systemconsts.h +++ b/RedPandaIDE/systemconsts.h @@ -3,6 +3,7 @@ #include +#ifdef Q_OS_WIN #define APP_SETTSINGS_FILENAME "redpandacpp.ini" #define GCC_PROGRAM "gcc.exe" #define GPP_PROGRAM "g++.exe" @@ -13,6 +14,9 @@ #define GPROF_PROGRAM "gprof.exe" #define CLEAN_PROGRAM "rm.exe" #define CPP_PROGRAM "cpp.exe" +#else +#error "Only support windows now!" +#endif #ifdef Q_OS_WIN # define PATH_SENSITIVITY Qt::CaseInsensitive diff --git a/RedPandaIDE/widgets/issuestable.cpp b/RedPandaIDE/widgets/issuestable.cpp new file mode 100644 index 00000000..1914acf8 --- /dev/null +++ b/RedPandaIDE/widgets/issuestable.cpp @@ -0,0 +1,143 @@ +#include "issuestable.h" +#include + + +IssuesTable::IssuesTable(QWidget *parent): + QTableView(parent) +{ + mModel = new IssuesModel(this); + this->setModel(mModel); + this->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed); + this->horizontalHeader()->setSectionResizeMode(3,QHeaderView::Stretch); + this->setColumnWidth(0,200); + this->setColumnWidth(1,60); + this->setColumnWidth(2,60); + +} + +IssuesModel *IssuesTable::issuesModel() +{ + return mModel; +} + +void IssuesTable::setErrorColor(QColor color) +{ + mModel->setErrorColor(color); +} + +void IssuesTable::setWarningColor(QColor color) +{ + mModel->setWarningColor(color); +} + +IssuesModel::IssuesModel(QObject *parent): + QAbstractTableModel(parent) +{ + +} + +void IssuesModel::addIssue(PCompileIssue issue) +{ + beginInsertColumns(QModelIndex(),mIssues.size(),mIssues.size()); + mIssues.push_back(issue); + endInsertRows(); +} + +void IssuesModel::clearIssues() +{ + if (mIssues.size()>0) { + beginRemoveRows(QModelIndex(),0,mIssues.size()-1); + mIssues.clear(); + endRemoveRows(); + } +} + +void IssuesModel::setErrorColor(QColor color) +{ + mErrorColor = color; +} + +void IssuesModel::setWarningColor(QColor color) +{ + mWarningColor = color; +} + +void IssuesTable::addIssue(PCompileIssue issue) +{ + mModel->addIssue(issue); +} + +void IssuesTable::clearIssues() +{ + mModel->clearIssues(); +} + +int IssuesModel::rowCount(const QModelIndex &) const +{ + return mIssues.size(); +} + +int IssuesModel::columnCount(const QModelIndex &) const +{ + return 4; +} + +QVariant IssuesModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + if (index.row()<0 || index.row() >= static_cast(mIssues.size())) + return QVariant(); + PCompileIssue issue = mIssues[index.row()]; + if (!issue) + return QVariant(); + switch (role) { + case Qt::DisplayRole: + switch (index.column()) { + case 0: + return issue->filename; + case 1: + if (issue->line>0) + return issue->line; + else + return ""; + case 2: + if (issue->column>0) + return issue->column; + else + return ""; + case 3: + return issue->description; + default: + return QVariant(); + } + case Qt::ForegroundRole: + switch(issue->type) { + case CompileIssueType::Error: + return mErrorColor; + case CompileIssueType::Warning: + return mWarningColor; + default: + return QVariant(); + } + default: + return QVariant(); + } +} + +QVariant IssuesModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { + switch(section) { + case 0: + return tr("Filename"); + case 1: + return tr("Line"); + case 2: + return tr("Col"); + case 3: + return tr("Description"); + } + } + return QVariant(); +} diff --git a/RedPandaIDE/widgets/issuestable.h b/RedPandaIDE/widgets/issuestable.h new file mode 100644 index 00000000..b2e40b43 --- /dev/null +++ b/RedPandaIDE/widgets/issuestable.h @@ -0,0 +1,57 @@ +#ifndef ISSUESTABLE_H +#define ISSUESTABLE_H + +#include +#include +#include "../common.h" +#include + +class IssuesModel : public QAbstractTableModel { + + Q_OBJECT +public: + explicit IssuesModel(QObject *parent = nullptr); + +public slots: + void addIssue(PCompileIssue issue); + void clearIssues(); + + void setErrorColor(QColor color); + void setWarningColor(QColor color); +private: + std::vector mIssues; + QColor mErrorColor; + QColor mWarningColor; + + // QAbstractItemModel interface +public: + int rowCount(const QModelIndex &parent) const override; + int columnCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; +}; + +class IssuesTable : public QTableView +{ + Q_OBJECT +public: + + explicit IssuesTable(QWidget* parent = nullptr); + + const std::vector & issues() const; + + IssuesModel* issuesModel(); + + void setErrorColor(QColor color); + void setWarningColor(QColor color); + +public slots: + void addIssue(PCompileIssue issue); + + void clearIssues(); + +private: + IssuesModel * mModel; +}; + +#endif // ISSUESTABLE_H