diff --git a/RedPandaIDE/compiler/compilermanager.cpp b/RedPandaIDE/compiler/compilermanager.cpp index 37f01217..4a5443bb 100644 --- a/RedPandaIDE/compiler/compilermanager.cpp +++ b/RedPandaIDE/compiler/compilermanager.cpp @@ -10,6 +10,8 @@ CompilerManager::CompilerManager(QObject *parent) : QObject(parent) mCompiler = nullptr; mBackgroundSyntaxChecker = nullptr; mRunner = nullptr; + mSyntaxCheckErrorCount = 0; + mCompileErrorCount = 0; } bool CompilerManager::compiling() @@ -28,8 +30,10 @@ void CompilerManager::compile(const QString& filename, const QByteArray& encodin if (mCompiler!=nullptr) { return; } + mCompileErrorCount = 0; mCompiler = new FileCompiler(filename,encoding,silent,onlyCheckSyntax); connect(mCompiler, &Compiler::compileFinished, this ,&CompilerManager::onCompileFinished); + connect(mCompiler, &Compiler::compileFinished, pMainWindow, &MainWindow::onCompileFinished); connect(mCompiler, &Compiler::compileOutput, pMainWindow, &MainWindow::onCompileLog); connect(mCompiler, &Compiler::compileIssue, pMainWindow, &MainWindow::onCompileIssue); connect(mCompiler, &Compiler::compileErrorOccured, pMainWindow, &MainWindow::onCompileErrorOccured); @@ -60,7 +64,6 @@ bool CompilerManager::canCompile(const QString &filename) void CompilerManager::onCompileFinished() { QMutexLocker locker(&compileMutex); - qDebug() << "Compile finished"; mCompiler=nullptr; delete mCompiler; } @@ -72,6 +75,16 @@ void CompilerManager::onRunnerTerminated() mRunner=nullptr; } +int CompilerManager::syntaxCheckErrorCount() const +{ + return mSyntaxCheckErrorCount; +} + +int CompilerManager::compileErrorCount() const +{ + return mCompileErrorCount; +} + CompileError::CompileError(const QString &reason):BaseError(reason) { diff --git a/RedPandaIDE/compiler/compilermanager.h b/RedPandaIDE/compiler/compilermanager.h index 4c0a554b..b06b6144 100644 --- a/RedPandaIDE/compiler/compilermanager.h +++ b/RedPandaIDE/compiler/compilermanager.h @@ -19,11 +19,17 @@ public: void compile(const QString& filename, const QByteArray& encoding, bool silent=false,bool onlyCheckSyntax=false); void run(const QString& filename, const QString& arguments, const QString& workDir); bool canCompile(const QString& filename); + int compileErrorCount() const; + + int syntaxCheckErrorCount() const; + private slots: void onCompileFinished(); void onRunnerTerminated(); private: Compiler* mCompiler; + int mCompileErrorCount; + int mSyntaxCheckErrorCount; Compiler* mBackgroundSyntaxChecker; ExecutableRunner* mRunner; QMutex compileMutex; diff --git a/RedPandaIDE/editor.cpp b/RedPandaIDE/editor.cpp index dfb4e707..9fb28700 100644 --- a/RedPandaIDE/editor.cpp +++ b/RedPandaIDE/editor.cpp @@ -409,6 +409,115 @@ void Editor::copyAsHTML() QGuiApplication::clipboard()->setMimeData(mimeData); } +void Editor::addSyntaxIssues(int line, int startChar, CompileIssueType errorType, const QString &hint) +{ + PSyntaxIssue pError; + BufferCoord p; + QString token; + SynHighlighterTokenType tokenType; + int tokenKind,start; + PSynHighlighterAttribute attr; + PSyntaxIssueList lst; + if ((line<1) || (line>lines()->count())) + return; + pError = std::make_shared(); + p.Char = startChar; + p.Line = line; + if (startChar >= lines()->getString(line-1).length()) { + start = 1; + token = lines()->getString(line-1); + } else { + if (!GetHighlighterAttriAtRowColEx(p,token,tokenType,tokenKind,start,attr)) + return; + } + pError->startChar = start; + pError->endChar = start + token.length(); + pError->col = charToColumn(line,pError->startChar); + pError->endCol = charToColumn(line,pError->endChar); + pError->hint = hint; + pError->token = token; + pError->issueType = errorType; + if (mSyntaxIssues.contains(line)) { + lst = mSyntaxIssues[line]; + } else { + lst = std::make_shared(); + mSyntaxIssues[line] = lst; + } + lst->append(pError); +} + +void Editor::clearSyntaxIssues() +{ + mSyntaxIssues.clear(); +} + +void Editor::gotoNextSyntaxIssue() +{ + auto iter = mSyntaxIssues.find(caretY()); + if (iter==mSyntaxIssues.end()) + return; + iter++; + if (iter==mSyntaxIssues.end()) + return; + BufferCoord p; + p.Char = (*iter)->at(0)->startChar; + p.Line = iter.key(); + setCaretXY(p); +} + +void Editor::gotoPrevSyntaxIssue() +{ + auto iter = mSyntaxIssues.find(caretY()); + if (iter==mSyntaxIssues.end()) + return; + if (iter==mSyntaxIssues.begin()) + return; + iter--; + BufferCoord p; + p.Char = (*iter)->at(0)->startChar; + p.Line = iter.key(); + setCaretXY(p); + +} + +bool Editor::hasNextSyntaxIssue() const +{ + auto iter = mSyntaxIssues.find(caretY()); + if (iter==mSyntaxIssues.end()) + return false; + iter++; + if (iter==mSyntaxIssues.end()) + return false; + return true; +} + +bool Editor::hasPrevSyntaxIssue() const +{ + auto iter = mSyntaxIssues.find(caretY()); + if (iter==mSyntaxIssues.end()) + return true; + if (iter==mSyntaxIssues.begin()) + return true; + return false; +} + +Editor::PSyntaxIssueList Editor::getErrorsAtLine(int line) +{ + if (mSyntaxIssues.contains(line)) + return mSyntaxIssues[line]; + return PSyntaxIssueList(); +} + +Editor::PSyntaxIssue Editor::getErrorAtPosition(const BufferCoord &pos) +{ + PSyntaxIssueList lst = getErrorsAtLine(pos.Line); + for (PSyntaxIssue issue: *lst) { + if (issue->startChar<=pos.Char && pos.Char<=issue->endChar) + return issue; + } + return PSyntaxIssue(); +} + void Editor::onModificationChanged(bool) { updateCaption(); } diff --git a/RedPandaIDE/editor.h b/RedPandaIDE/editor.h index 568e9864..32a1adf1 100644 --- a/RedPandaIDE/editor.h +++ b/RedPandaIDE/editor.h @@ -6,6 +6,7 @@ #include #include "qsynedit/SynEdit.h" #include "colorscheme.h" +#include "common.h" class SaveException: public std::exception { @@ -46,6 +47,20 @@ public: RawStringNoEscape }; + struct SyntaxIssue { + int col; + int endCol; + int startChar; + int endChar; + CompileIssueType issueType; + QString token; + QString hint; + }; + + using PSyntaxIssue = std::shared_ptr; + using SyntaxIssueList = QVector; + using PSyntaxIssueList = std::shared_ptr; + explicit Editor(QWidget *parent); explicit Editor(QWidget *parent, const QString& filename, @@ -82,6 +97,15 @@ public: void copyToClipboard() override; void cutToClipboard() override; void copyAsHTML(); + + void addSyntaxIssues(int line, int startChar, CompileIssueType errorType, const QString& hint); + void clearSyntaxIssues(); + void gotoNextSyntaxIssue(); + void gotoPrevSyntaxIssue(); + bool hasPrevSyntaxIssue() const; + bool hasNextSyntaxIssue() const; + PSyntaxIssueList getErrorsAtLine(int line); + PSyntaxIssue getErrorAtPosition(const BufferCoord& pos); signals: @@ -114,6 +138,7 @@ private: QTabWidget* mParentPageControl; bool mInProject; bool mIsNew; + QMap mSyntaxIssues; // QWidget interface protected: diff --git a/RedPandaIDE/editorlist.cpp b/RedPandaIDE/editorlist.cpp index 98a1ff14..80815970 100644 --- a/RedPandaIDE/editorlist.cpp +++ b/RedPandaIDE/editorlist.cpp @@ -5,6 +5,7 @@ #include #include #include +#include EditorList::EditorList(QTabWidget* leftPageWidget, QTabWidget* rightPageWidget, @@ -128,6 +129,23 @@ void EditorList::applyColorSchemes(const QString& name) } } +bool EditorList::isFileOpened(const QString &name) +{ + QFileInfo fileInfo(name); + QString filename = fileInfo.absoluteFilePath(); + for (int i=0;icount();i++) { + Editor* e = static_cast(mLeftPageWidget->widget(i)); + if (e->filename().compare(filename)==0) + return true; + } + for (int i=0;icount();i++) { + Editor* e = static_cast(mRightPageWidget->widget(i)); + if (e->filename().compare(filename)==0) + return true; + } + return false; +} + bool EditorList::closeAll(bool force) { beginUpdate(); auto end = finally([this] { diff --git a/RedPandaIDE/editorlist.h b/RedPandaIDE/editorlist.h index a7fc0102..75ff2708 100644 --- a/RedPandaIDE/editorlist.h +++ b/RedPandaIDE/editorlist.h @@ -40,6 +40,7 @@ public: void endUpdate(); void applySettings(); void applyColorSchemes(const QString& name); + bool isFileOpened(const QString& name); private: QTabWidget* getNewEditorPageControl() const; diff --git a/RedPandaIDE/mainwindow.cpp b/RedPandaIDE/mainwindow.cpp index b305042a..1020b668 100644 --- a/RedPandaIDE/mainwindow.cpp +++ b/RedPandaIDE/mainwindow.cpp @@ -22,8 +22,10 @@ MainWindow* pMainWindow; MainWindow::MainWindow(QWidget *parent) - : QMainWindow(parent) - , ui(new Ui::MainWindow) + : QMainWindow(parent), + ui(new Ui::MainWindow), + mMessageControlChanged(false), + mCheckSyntaxInBack(false) { ui->setupUi(this); // status bar @@ -71,8 +73,9 @@ MainWindow::MainWindow(QWidget *parent) ui->actionEncode_in_ANSI->setCheckable(true); ui->actionEncode_in_UTF_8->setCheckable(true); + openCloseMessageSheet(false); + mPreviousHeight = 250; updateEditorActions(); - applySettings(); } @@ -249,6 +252,36 @@ void MainWindow::updateCompilerSet() mCompilerSet->setCurrentIndex(index); } +void MainWindow::openCloseMessageSheet(bool open) +{ +// if Assigned(fReportToolWindow) then +// Exit; + + // Switch between open and close + if (open) { + QList sizes = ui->splitterMessages->sizes(); + int tabHeight = ui->tabMessages->tabBar()->height(); + ui->tabMessages->setMinimumHeight(tabHeight+5); + int totalSize = sizes[0] + sizes[1]; + sizes[1] = mPreviousHeight; + sizes[0] = std::max(1,totalSize - sizes[1]); + ui->splitterMessages->setSizes(sizes); + } else { + QList sizes = ui->splitterMessages->sizes(); + mPreviousHeight = sizes[1]; + int totalSize = sizes[0] + sizes[1]; + int tabHeight = ui->tabMessages->tabBar()->height(); + ui->tabMessages->setMinimumHeight(tabHeight); + sizes[1] = tabHeight; + sizes[0] = std::max(1,totalSize - sizes[1]); + ui->splitterMessages->setSizes(sizes); + } + QSplitterHandle* handle = ui->splitterMessages->handle(1); + handle->setEnabled(open); + int idxClose = ui->tabMessages->indexOf(ui->tabClose); + ui->tabMessages->setTabVisible(idxClose,open); +} + void MainWindow::on_actionNew_triggered() { @@ -337,6 +370,104 @@ void MainWindow::onCompileLog(const QString &msg) void MainWindow::onCompileIssue(PCompileIssue issue) { ui->tableIssues->addIssue(issue); + + // Update tab caption +// if CompilerOutput.Items.Count = 1 then +// CompSheet.Caption := Lang[ID_SHEET_COMP] + ' (' + IntToStr(CompilerOutput.Items.Count) + ')'; + + if (issue->type == CompileIssueType::Error || issue->type == + CompileIssueType::Warning) { + Editor* e = mEditorList->getOpenedEditorByFilename(issue->filename); + if (e!=nullptr && (issue->line>0)) { + int line = issue->line; + if (line > e->lines()->count()) + return; + int col = std::min(issue->column,e->lines()->getString(line-1).length()+1); + e->addSyntaxIssues(line,col,issue->type,issue->description); + } + } +} + +void MainWindow::onCompileFinished() +{ + // Update tab caption + int i = ui->tabMessages->indexOf(ui->tabIssues); + if (i==-1) + return; + ui->tabMessages->setTabText(i, tr("Issues") + + QString(" (%1)").arg(ui->tableIssues->model()->rowCount())); + + // Close it if there's nothing to show + if (mCheckSyntaxInBack) { + // check syntax in back, don't change message panel + } else if ( + (ui->tableIssues->count() == 0) +// and (ResourceOutput.Items.Count = 0) +// and devData.AutoCloseProgress + ) { + openCloseMessageSheet(false); + // Or open it if there is anything to show + } else { + if (ui->tableIssues->count() > 0) { + if (ui->tabMessages->currentIndex() != i) { + ui->tabMessages->setCurrentIndex(i); + mMessageControlChanged = false; + } +// end else if (ResourceOutput.Items.Count > 0) then begin +// if MessageControl.ActivePage <> ResSheet then begin +// MessageControl.ActivePage := ResSheet; +// fMessageControlChanged := False; +// end; +// end; + openCloseMessageSheet(true); + } + } + + Editor * e = mEditorList->getEditor(); + if (e!=nullptr) { + e->beginUpdate(); + e->endUpdate(); + } + + // Jump to problem location, sorted by significance + if ((mCompilerManager->compileErrorCount() > 0) && (!mCheckSyntaxInBack)) { + // First try to find errors + for (int i=0;itableIssues->count();i++) { + PCompileIssue issue = ui->tableIssues->issue(i); + if (issue->type == CompileIssueType::Error) { + ui->tableIssues->selectRow(i); + QModelIndex index =ui->tableIssues->model()->index(i,0); + ui->tableIssues->doubleClicked(index); + } + } + + // Then try to find warnings + for (int i=0;itableIssues->count();i++) { + PCompileIssue issue = ui->tableIssues->issue(i); + if (issue->type == CompileIssueType::Warning) { + ui->tableIssues->selectRow(i); + QModelIndex index =ui->tableIssues->model()->index(i,0); + ui->tableIssues->doubleClicked(index); + } + } + // Then try to find anything with a line number... +// for I := 0 to CompilerOutput.Items.Count - 1 do begin +// if not SameStr(CompilerOutput.Items[I].Caption, '') then begin +// CompilerOutput.Selected := CompilerOutput.Items[I]; +// CompilerOutput.Selected.MakeVisible(False); +// CompilerOutputDblClick(CompilerOutput); +// Exit; +// end; +// end; + + // Then try to find a resource error +// if ResourceOutput.Items.Count > 0 then begin +// ResourceOutput.Selected := ResourceOutput.Items[0]; +// ResourceOutput.Selected.MakeVisible(False); +// CompilerOutputDblClick(ResourceOutput); +// end; + } + mCheckSyntaxInBack=false; } void MainWindow::onCompileErrorOccured(const QString &reason) @@ -523,3 +654,14 @@ void MainWindow::on_actionConvert_to_UTF_8_triggered() return; editor->convertToEncoding(ENCODING_UTF8); } + +void MainWindow::on_tabMessages_tabBarClicked(int index) +{ + mMessageControlChanged = false; + int idxClose = ui->tabMessages->indexOf(ui->tabClose); + if (index == idxClose) { + openCloseMessageSheet(false); + } else { + openCloseMessageSheet(true); + } +} diff --git a/RedPandaIDE/mainwindow.h b/RedPandaIDE/mainwindow.h index c5602abc..5c9fe428 100644 --- a/RedPandaIDE/mainwindow.h +++ b/RedPandaIDE/mainwindow.h @@ -88,15 +88,19 @@ private slots: void on_actionConvert_to_UTF_8_triggered(); + void on_tabMessages_tabBarClicked(int index); + public slots: void onCompileLog(const QString& msg); void onCompileIssue(PCompileIssue issue); + void onCompileFinished(); void onCompileErrorOccured(const QString& reason); private: void setupActions(); void updateCompilerSet(); + void openCloseMessageSheet(bool open); private: Ui::MainWindow *ui; @@ -108,6 +112,9 @@ private: QMenu * mMenuEncodingList; QComboBox* mCompilerSet; CompilerManager* mCompilerManager; + bool mMessageControlChanged; + bool mCheckSyntaxInBack; + int mPreviousHeight; // QWidget interface protected: diff --git a/RedPandaIDE/mainwindow.ui b/RedPandaIDE/mainwindow.ui index 36f67684..7ded64d3 100644 --- a/RedPandaIDE/mainwindow.ui +++ b/RedPandaIDE/mainwindow.ui @@ -33,10 +33,13 @@ 0 - + Qt::Vertical + + false + @@ -58,11 +61,14 @@ 0 - + Qt::Horizontal - + + false + + 1 @@ -147,7 +153,7 @@ - + 0 @@ -158,9 +164,14 @@ QTabWidget::South - 0 + 5 + + + :/icons/images/newlook24/013-compile.png + + Issues @@ -204,7 +215,22 @@ + + + + :/icons/images/newlook24/068-resrc.png + + + + Resource + + + + + :/icons/images/newlook24/015-compres.png + + Compile Log @@ -239,6 +265,36 @@ + + + + :/icons/images/newlook24/020-debug.png + + + + Debug + + + + + + :/icons/images/newlook24/074-search.png + + + + Search + + + + + + :/icons/images/newlook24/008-close.png + + + + Close + + diff --git a/RedPandaIDE/widgets/issuestable.cpp b/RedPandaIDE/widgets/issuestable.cpp index 75044d2d..d0a9f14b 100644 --- a/RedPandaIDE/widgets/issuestable.cpp +++ b/RedPandaIDE/widgets/issuestable.cpp @@ -69,6 +69,11 @@ PCompileIssue IssuesModel::issue(int row) return mIssues[row]; } +int IssuesModel::count() +{ + return mIssues.size(); +} + void IssuesTable::addIssue(PCompileIssue issue) { mModel->addIssue(issue); @@ -84,6 +89,11 @@ PCompileIssue IssuesTable::issue(const int row) return mModel->issue(row); } +int IssuesTable::count() +{ + return mModel->count(); +} + void IssuesTable::clearIssues() { mModel->clearIssues(); diff --git a/RedPandaIDE/widgets/issuestable.h b/RedPandaIDE/widgets/issuestable.h index f8f1c8a8..87261a6f 100644 --- a/RedPandaIDE/widgets/issuestable.h +++ b/RedPandaIDE/widgets/issuestable.h @@ -26,6 +26,7 @@ private: // QAbstractItemModel interface public: + int count(); int rowCount(const QModelIndex &parent) const override; int columnCount(const QModelIndex &parent) const override; QVariant data(const QModelIndex &index, int role) const override; @@ -51,6 +52,7 @@ public slots: PCompileIssue issue(const QModelIndex& index); PCompileIssue issue(const int row); + int count(); void clearIssues();