diff --git a/QCodeEditor/src/internal/QCodeEditor.cpp b/QCodeEditor/src/internal/QCodeEditor.cpp index 332e32bfd..f529938cf 100644 --- a/QCodeEditor/src/internal/QCodeEditor.cpp +++ b/QCodeEditor/src/internal/QCodeEditor.cpp @@ -616,6 +616,23 @@ void QCodeEditor::keyPressEvent(QKeyEvent* e) { return; } + // Duplicate line + if (e->key() == Qt::Key_D && + (e->modifiers() & Qt::ControlModifier) && + (e->modifiers() & Qt::ShiftModifier)) { + auto tc = textCursor(); + auto linepos = tc.positionInBlock(); + tc.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor); + tc.movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor); + auto line = tc.selectedText(); + tc.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor); + tc.insertText(line); + tc.movePosition(QTextCursor::PreviousBlock, QTextCursor::MoveAnchor); + tc.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, linepos); + setTextCursor(tc); + return; + } + bool doSave = false; if (e->modifiers() == Qt::ControlModifier) { diff --git a/pages/pagelisp.cpp b/pages/pagelisp.cpp index b6781b7f9..de1e93802 100644 --- a/pages/pagelisp.cpp +++ b/pages/pagelisp.cpp @@ -340,15 +340,16 @@ void PageLisp::updateRecentList() void PageLisp::makeEditorConnections(ScriptEditor *editor) { connect(editor->codeEditor(), &QCodeEditor::textChanged, [editor, this]() { - setEditorDirty(editor); + setEditorDirty(editor); }); connect(editor->codeEditor(), &QCodeEditor::clearConsoleTriggered, [this]() { ui->debugEdit->clear(); }); - connect(editor, &ScriptEditor::fileOpened, [this](QString fileName) { + connect(editor, &ScriptEditor::fileOpened, [editor, this](QString fileName) { mRecentFiles.removeAll(fileName); mRecentFiles.prepend(fileName); updateRecentList(); + setEditorClean(editor); }); connect(editor, &ScriptEditor::fileSaved, [editor, this](QString fileName) { if (mVesc) { @@ -702,19 +703,20 @@ void PageLisp::on_helpButton_clicked() "in the lisp-scripting chat at
" "https://discord.gg/JgvV5NwYts

" "Keyboard Commands
" - "Ctrl + '+' : Increase font size
" - "Ctrl + '-' : Decrease font size
" - "Ctrl + space : Show auto-complete suggestions
" - "Ctrl + '/' : Toggle auto-comment on line or block
" - "Ctrl + '#' : Toggle auto-comment on line or block
" - "Ctrl + 'i' : Auto-indent selected lines
" - "Ctrl + 'f' : Open search (and replace) bar
" - "Ctrl + 'e' : Upload (and run if set) application
" - "Ctrl + 'w' : Stream application
" - "Ctrl + 'q' : Stop application
" - "Ctrl + 'd' : Clear console
" - "Ctrl + 's' : Save file
" - "Ctrl + 'r' : Run selected block in REPL
"; + "Ctrl + '+' : Increase font size
" + "Ctrl + '-' : Decrease font size
" + "Ctrl + space : Show auto-complete suggestions
" + "Ctrl + '/' : Toggle auto-comment on line or block
" + "Ctrl + '#' : Toggle auto-comment on line or block
" + "Ctrl + 'i' : Auto-indent selected lines
" + "Ctrl + 'f' : Open search (and replace) bar
" + "Ctrl + 'e' : Upload (and run if set) application
" + "Ctrl + 'w' : Stream application
" + "Ctrl + 'q' : Stop application
" + "Ctrl + 'd' : Clear console
" + "Ctrl + 's' : Save file
" + "Ctrl + 'r' : Run selected block in REPL
" + "Ctrl + Shift + 'd' : Duplicate current line
"; HelpDialog::showHelpMonospace(this, "VESC Tool Script Editor", html); } diff --git a/pages/pagescripting.cpp b/pages/pagescripting.cpp index 2b811587e..3f60ad253 100755 --- a/pages/pagescripting.cpp +++ b/pages/pagescripting.cpp @@ -423,10 +423,11 @@ void PageScripting::makeEditorConnections(ScriptEditor *editor) connect(editor->codeEditor(), &QCodeEditor::clearConsoleTriggered, [this]() { ui->debugEdit->clear(); }); - connect(editor, &ScriptEditor::fileOpened, [this](QString fileName) { + connect(editor, &ScriptEditor::fileOpened, [editor, this](QString fileName) { mRecentFiles.removeAll(fileName); mRecentFiles.prepend(fileName); updateRecentList(); + setEditorClean(editor); }); connect(editor, &ScriptEditor::fileSaved, [editor, this](QString fileName) { if (mVesc) { @@ -711,20 +712,21 @@ bool PageScripting::eraseQml(int size, bool reload) void PageScripting::on_helpButton_clicked() { QString html = "Keyboard Commands
" - "Ctrl + '+' : Increase font size
" - "Ctrl + '-' : Decrease font size
" - "Ctrl + space : Show auto-complete suggestions
" - "Ctrl + '/' : Toggle auto-comment on line or block
" - "Ctrl + '#' : Toggle auto-comment on line or block
" - "Ctrl + 'i' : Auto-indent selected line or block
" - "Ctrl + 'f' : Open search (and replace) bar
" - "Ctrl + 'e' : Run or restart embedded
" - "Ctrl + 'w' : Run or restart window
" - "Ctrl + 'q' : Stop code
" - "Ctrl + 'd' : Clear console
" - "Ctrl + 's' : Save file
"; - - HelpDialog::showHelpMonospace(this, "VESC Tool Script Editor", html.replace(" "," ")); + "Ctrl + '+' : Increase font size
" + "Ctrl + '-' : Decrease font size
" + "Ctrl + space : Show auto-complete suggestions
" + "Ctrl + '/' : Toggle auto-comment on line or block
" + "Ctrl + '#' : Toggle auto-comment on line or block
" + "Ctrl + 'i' : Auto-indent selected line or block
" + "Ctrl + 'f' : Open search (and replace) bar
" + "Ctrl + 'e' : Run or restart embedded
" + "Ctrl + 'w' : Run or restart window
" + "Ctrl + 'q' : Stop code
" + "Ctrl + 'd' : Clear console
" + "Ctrl + 's' : Save file
" + "Ctrl + Shift + 'd' : Duplicate current line
"; + + HelpDialog::showHelpMonospace(this, "VESC Tool Script Editor", html); } void PageScripting::on_exportCArrayHwButton_clicked() diff --git a/res/CHANGELOG.md b/res/CHANGELOG.md index 540dc71ae..991dfac26 100644 --- a/res/CHANGELOG.md +++ b/res/CHANGELOG.md @@ -2,6 +2,8 @@ #### Released TBD * Auto-save UI-state on regular intervals. * Movable lisp-tabs and better tab behavior in general. +* Detect file system changes in script editor. +* Duplicate line shortcut in script editor. ### 6.05 #### Released 2024-08-19 diff --git a/widgets/scripteditor.cpp b/widgets/scripteditor.cpp index 16c3534f4..44bada88f 100644 --- a/widgets/scripteditor.cpp +++ b/widgets/scripteditor.cpp @@ -41,12 +41,17 @@ ScriptEditor::ScriptEditor(QWidget *parent) : ui->saveButton->setIcon(Utility::getIcon("icons/Save-96.png")); ui->saveAsButton->setIcon(Utility::getIcon("icons/Save as-96.png")); ui->refreshButton->setIcon(Utility::getIcon("icons/Refresh-96.png")); + ui->fileChangedReloadButton->setIcon(Utility::getIcon("icons/Refresh-96.png")); + ui->fileChangeIgnoreButton->setIcon(Utility::getIcon("icons/Cancel-96.png")); + ui->fileChangedWidget->setStyleSheet("background-color:rgba(200, 52, 52, 100);"); ui->searchWidget->setVisible(false); + ui->fileChangedWidget->setVisible(false); ui->codeEdit->setTabReplaceSize(4); connect(ui->codeEdit, &QCodeEditor::saveTriggered, [this]() { on_saveButton_clicked(); }); + connect(ui->codeEdit, &QCodeEditor::searchTriggered, [this]() { ui->searchWidget->setVisible(true); auto selected = ui->codeEdit->textCursor().selectedText(); @@ -57,6 +62,19 @@ ScriptEditor::ScriptEditor(QWidget *parent) : ui->searchEdit->setFocus(); ui->searchEdit->selectAll(); }); + + connect(&mFsWatcher, &QFileSystemWatcher::fileChanged, [this](const QString &path) { + (void)path; + + if (hasUnsavedContent()) { + ui->fileChangedWidget->setVisible(true); + ui->codeEdit->setEnabled(false); + } + + if (!mFsWatcher.files().contains(path)) { + mFsWatcher.addPath(path); + } + }); } ScriptEditor::~ScriptEditor() @@ -77,6 +95,13 @@ QString ScriptEditor::fileNow() void ScriptEditor::setFileNow(QString fileName) { ui->fileNowLabel->setText(fileName); + + mFsWatcher.removePaths(mFsWatcher.files()); + QFileInfo f(fileName); + if (f.exists()) { + mFsWatcher.addPath(fileName); + } + emit fileNameChanged(fileName); } @@ -105,7 +130,7 @@ QString ScriptEditor::contentAsText() QString res = ui->codeEdit->toPlainText(); if (!QSettings().value("scripting/uploadContentEditor", true).toBool()) { - QString fileName = ui->fileNowLabel->text(); + QString fileName = fileNow(); QFileInfo fi(fileName); if (!fi.exists()) { @@ -129,7 +154,7 @@ bool ScriptEditor::hasUnsavedContent() { bool res = false; - QString fileName = ui->fileNowLabel->text(); + QString fileName = fileNow(); QFileInfo fi(fileName); if (!fi.exists()) { // Use a threshold of 5 characters @@ -160,7 +185,7 @@ void ScriptEditor::keyPressEvent(QKeyEvent *event) void ScriptEditor::on_openFileButton_clicked() { - QString path = ui->fileNowLabel->text(); + QString path = fileNow(); if (path.isEmpty()) { path = QSettings().value("scripting/lastPath", "").toString(); } @@ -180,8 +205,7 @@ void ScriptEditor::on_openFileButton_clicked() QSettings().setValue("scripting/lastPath", QFileInfo(file).canonicalPath()); ui->codeEdit->setPlainText(file.readAll()); - ui->fileNowLabel->setText(fileName); - emit fileNameChanged(fileName); + setFileNow(fileName); emit fileOpened(fileName); @@ -191,7 +215,7 @@ void ScriptEditor::on_openFileButton_clicked() void ScriptEditor::on_saveButton_clicked() { - QString fileName = ui->fileNowLabel->text(); + QString fileName = fileNow(); QFileInfo fi(fileName); if (!fi.exists()) { @@ -206,8 +230,10 @@ void ScriptEditor::on_saveButton_clicked() return; } + mFsWatcher.removePaths(mFsWatcher.files()); file.write(ui->codeEdit->toPlainText().toUtf8()); file.close(); + mFsWatcher.addPath(fileName); emit fileSaved(fileName); } @@ -215,13 +241,13 @@ void ScriptEditor::on_saveButton_clicked() void ScriptEditor::on_saveAsButton_clicked() { QString fileName = QFileDialog::getSaveFileName(this, - tr("Save %1").arg(mIsModeLisp ? "Lisp" : "Qml"), ui->fileNowLabel->text(), + tr("Save %1").arg(mIsModeLisp ? "Lisp" : "Qml"), fileNow(), mIsModeLisp ? tr("Lisp files (*.lisp)") : tr("QML files (*.qml)")); QString ending = mIsModeLisp ? ".lisp" : ".qml"; if (!fileName.isEmpty()) { - if (!fileName.toLower().endsWith(ending)) { + if (!fileName.endsWith(ending, Qt::CaseInsensitive)) { fileName.append(ending); } @@ -232,12 +258,11 @@ void ScriptEditor::on_saveAsButton_clicked() return; } + mFsWatcher.removePaths(mFsWatcher.files()); file.write(ui->codeEdit->toPlainText().toUtf8()); file.close(); - ui->fileNowLabel->setText(fileName); - emit fileNameChanged(fileName); - + setFileNow(fileName); emit fileSaved(fileName); } } @@ -290,7 +315,7 @@ void ScriptEditor::on_searchCaseSensitiveBox_toggled(bool checked) void ScriptEditor::on_refreshButton_clicked() { - QString fileName = ui->fileNowLabel->text(); + QString fileName = fileNow(); QFileInfo fi(fileName); if (!fi.exists()) { @@ -306,10 +331,8 @@ void ScriptEditor::on_refreshButton_clicked() } ui->codeEdit->setPlainText(QString::fromUtf8(file.readAll())); - ui->fileNowLabel->setText(fileName); - emit fileOpened(fileName); - file.close(); + emit fileOpened(fileName); } void ScriptEditor::on_searchEdit_returnPressed() @@ -320,3 +343,16 @@ void ScriptEditor::on_searchEdit_returnPressed() ui->codeEdit->searchNextResult(); } } + +void ScriptEditor::on_fileChangeIgnoreButton_clicked() +{ + ui->fileChangedWidget->setVisible(false); + ui->codeEdit->setEnabled(true); + ui->codeEdit->setFocus(); +} + +void ScriptEditor::on_fileChangedReloadButton_clicked() +{ + on_refreshButton_clicked(); + on_fileChangeIgnoreButton_clicked(); +} diff --git a/widgets/scripteditor.h b/widgets/scripteditor.h index bf825945e..da1b0c980 100644 --- a/widgets/scripteditor.h +++ b/widgets/scripteditor.h @@ -22,6 +22,7 @@ #include #include +#include namespace Ui { class ScriptEditor; @@ -66,10 +67,13 @@ private slots: void on_searchCaseSensitiveBox_toggled(bool checked); void on_refreshButton_clicked(); void on_searchEdit_returnPressed(); + void on_fileChangeIgnoreButton_clicked(); + void on_fileChangedReloadButton_clicked(); private: Ui::ScriptEditor *ui; bool mIsModeLisp; + QFileSystemWatcher mFsWatcher; }; diff --git a/widgets/scripteditor.ui b/widgets/scripteditor.ui index f840d6da3..6098893f9 100644 --- a/widgets/scripteditor.ui +++ b/widgets/scripteditor.ui @@ -29,13 +29,74 @@ 6 + + + + + 4 + + + 0 + + + 10 + + + 0 + + + 10 + + + + + This file has been changed on the filesystem. + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + Reload + + + true + + + + + + + Ignore + + + true + + + + + + false - QTextEdit::NoWrap + QTextEdit::LineWrapMode::NoWrap