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