aboutsummaryrefslogtreecommitdiffstats
path: root/SQLiteStudio3/guiSQLiteStudio/sqleditor.cpp
diff options
context:
space:
mode:
authorLibravatarUnit 193 <unit193@unit193.net>2023-04-30 18:30:36 -0400
committerLibravatarUnit 193 <unit193@unit193.net>2023-04-30 18:30:36 -0400
commit3565aad630864ecdbe53fdaa501ea708555b3c7c (patch)
treec743e4ad0bad39ebdb2f514c7cc52d34a257ebbe /SQLiteStudio3/guiSQLiteStudio/sqleditor.cpp
parent1fdc150116cad39aae5c5da407c3312b47a59e3a (diff)
New upstream version 3.4.4+dfsg.upstream/3.4.4+dfsg
Diffstat (limited to 'SQLiteStudio3/guiSQLiteStudio/sqleditor.cpp')
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/sqleditor.cpp280
1 files changed, 236 insertions, 44 deletions
diff --git a/SQLiteStudio3/guiSQLiteStudio/sqleditor.cpp b/SQLiteStudio3/guiSQLiteStudio/sqleditor.cpp
index 50189d4..d1f1a47 100644
--- a/SQLiteStudio3/guiSQLiteStudio/sqleditor.cpp
+++ b/SQLiteStudio3/guiSQLiteStudio/sqleditor.cpp
@@ -1,8 +1,10 @@
#include "sqleditor.h"
-#include "log.h"
+#include "common/mouseshortcut.h"
+#include "sqlitesyntaxhighlighter.h"
+#include "db/db.h"
#include "uiconfig.h"
#include "uiutils.h"
-#include "services/config.h"
+#include "services/codesnippetmanager.h"
#include "iconmanager.h"
#include "completer/completerwindow.h"
#include "completionhelper.h"
@@ -16,12 +18,13 @@
#include "dbobjectdialogs.h"
#include "searchtextlocator.h"
#include "services/codeformatter.h"
-#include "sqlitestudio.h"
#include "style.h"
#include "dbtree/dbtreeitem.h"
#include "dbtree/dbtree.h"
#include "dbtree/dbtreemodel.h"
+#include "dbtree/dbtreeview.h"
#include "common/lazytrigger.h"
+#include "common/extaction.h"
#include <QAction>
#include <QMenu>
#include <QTimer>
@@ -36,6 +39,28 @@
CFG_KEYS_DEFINE(SqlEditor)
+QHash<SqlEditor::Action, QAction*> SqlEditor::staticActions;
+bool SqlEditor::wrapWords = false;
+
+void SqlEditor::createStaticActions()
+{
+ staticActions[WORD_WRAP] = new ExtAction(tr("Wrap words", "sql editor"), MainWindow::getInstance());
+
+ staticActions[WORD_WRAP]->setCheckable(true);
+ staticActions[WORD_WRAP]->setChecked(wrapWords);
+ connect(staticActions[WORD_WRAP], &QAction::toggled, [=](bool value)
+ {
+ wrapWords = value;
+ CFG_UI.General.SqlEditorWrapWords.set(value);
+ });
+}
+
+void SqlEditor::staticInit()
+{
+ wrapWords = CFG_UI.General.SqlEditorWrapWords.get();
+ createStaticActions();
+}
+
SqlEditor::SqlEditor(QWidget *parent) :
QPlainTextEdit(parent)
{
@@ -44,8 +69,8 @@ SqlEditor::SqlEditor(QWidget *parent) :
SqlEditor::~SqlEditor()
{
- if (objectsInNamedDbFuture.isRunning())
- objectsInNamedDbFuture.waitForFinished();
+ if (objectsInNamedDbWatcher->isRunning())
+ objectsInNamedDbWatcher->waitForFinished();
if (queryParser)
{
@@ -57,20 +82,25 @@ SqlEditor::~SqlEditor()
void SqlEditor::init()
{
highlighter = new SqliteSyntaxHighlighter(document());
- setFont(CFG_UI.Fonts.SqlEditor.get());
initActions();
setupMenu();
+ objectsInNamedDbWatcher = new QFutureWatcher<QHash<QString,QStringList>>(this);
+ connect(objectsInNamedDbWatcher, SIGNAL(finished()), this, SLOT(scheduleQueryParserForSchemaRefresh()));
+
textLocator = new SearchTextLocator(document(), this);
connect(textLocator, SIGNAL(found(int,int)), this, SLOT(found(int,int)));
connect(textLocator, SIGNAL(reachedEnd()), this, SLOT(reachedEnd()));
+ connect(textLocator, SIGNAL(newCursorPositionAfterAllReplaced(int)), this, SLOT(moveCursorTo(int)));
lineNumberArea = new LineNumberArea(this);
+ changeFont(CFG_UI.Fonts.SqlEditor.get());
connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(updateLineNumberAreaWidth()));
connect(this, SIGNAL(updateRequest(QRect,int)), this, SLOT(updateLineNumberArea(QRect,int)));
connect(this, SIGNAL(textChanged()), this, SLOT(checkContentSize()));
connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(cursorMoved()));
+ MouseShortcut::forWheel(Qt::ControlModifier, this, SLOT(fontSizeChangeRequested(int)), viewport());
updateLineNumberAreaWidth();
highlightCurrentCursorContext();
@@ -97,6 +127,7 @@ void SqlEditor::init()
connect(this, &QWidget::customContextMenuRequested, this, &SqlEditor::customContextMenuRequested);
connect(CFG_UI.Fonts.SqlEditor, SIGNAL(changed(QVariant)), this, SLOT(changeFont(QVariant)));
connect(CFG, SIGNAL(massSaveCommitted()), this, SLOT(configModified()));
+ connect(STYLE, SIGNAL(paletteChanged()), this, SLOT(colorsConfigChanged()));
}
void SqlEditor::removeErrorMarkers()
@@ -143,6 +174,8 @@ void SqlEditor::createActions()
createAction(FIND_PREV, tr("Find previous", "sql editor"), this, SLOT(findPrevious()), this);
createAction(REPLACE, ICONS.SEARCH_AND_REPLACE, tr("Replace", "sql editor"), this, SLOT(replace()), this);
createAction(TOGGLE_COMMENT, tr("Toggle comment", "sql editor"), this, SLOT(toggleComment()), this);
+ createAction(INCR_FONT_SIZE, tr("Increase font size", "sql editor"), this, SLOT(incrFontSize()), this);
+ createAction(DECR_FONT_SIZE, tr("Decrease font size", "sql editor"), this, SLOT(decrFontSize()), this);
actionMap[CUT]->setEnabled(false);
actionMap[COPY]->setEnabled(false);
@@ -153,12 +186,14 @@ void SqlEditor::createActions()
connect(this, &QPlainTextEdit::undoAvailable, this, &SqlEditor::updateUndoAction);
connect(this, &QPlainTextEdit::redoAvailable, this, &SqlEditor::updateRedoAction);
connect(this, &QPlainTextEdit::copyAvailable, this, &SqlEditor::updateCopyAction);
+
+ connect(CFG_UI.General.SqlEditorWrapWords, SIGNAL(changed(QVariant)), this, SLOT(wordWrappingChanged(QVariant)));
}
void SqlEditor::setupDefShortcuts()
{
setShortcutContext({CUT, COPY, PASTE, DELETE, SELECT_ALL, UNDO, REDO, COMPLETE, FORMAT_SQL, SAVE_SQL_FILE, OPEN_SQL_FILE,
- DELETE_LINE}, Qt::WidgetWithChildrenShortcut);
+ DELETE_LINE, INCR_FONT_SIZE, DECR_FONT_SIZE}, Qt::WidgetWithChildrenShortcut);
BIND_SHORTCUTS(SqlEditor, Action);
}
@@ -167,6 +202,7 @@ void SqlEditor::setupMenu()
{
contextMenu = new QMenu(this);
contextMenu->addAction(actionMap[FORMAT_SQL]);
+ contextMenu->addAction(staticActions[WORD_WRAP]);
contextMenu->addSeparator();
contextMenu->addAction(actionMap[SAVE_SQL_FILE]);
contextMenu->addAction(actionMap[OPEN_SQL_FILE]);
@@ -194,7 +230,7 @@ void SqlEditor::setDb(Db* value)
{
db = value;
refreshValidObjects();
- scheduleQueryParser(true);
+ scheduleQueryParser(true, true);
}
void SqlEditor::setAutoCompletion(bool enabled)
@@ -243,10 +279,13 @@ bool SqlEditor::handleValidObjectContextMenu(const QPoint& pos)
void SqlEditor::saveToFile(const QString &fileName)
{
+ if (!openSaveActionsEnabled)
+ return;
+
QFile file(fileName);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
{
- notifyError(tr("Could not open file '%1' for writing: %2").arg(fileName).arg(file.errorString()));
+ notifyError(tr("Could not open file '%1' for writing: %2").arg(fileName, file.errorString()));
return;
}
@@ -271,7 +310,34 @@ void SqlEditor::toggleLineCommentForLine(const QTextBlock& block)
}
else
cur.insertText("--");
+}
+bool SqlEditor::getAlwaysEnforceErrorsChecking() const
+{
+ return alwaysEnforceErrorsChecking;
+}
+
+void SqlEditor::setAlwaysEnforceErrorsChecking(bool newAlwaysEnforceErrorsChecking)
+{
+ alwaysEnforceErrorsChecking = newAlwaysEnforceErrorsChecking;
+}
+
+bool SqlEditor::getHighlightingSyntax() const
+{
+ return highlightingSyntax;
+}
+
+void SqlEditor::setOpenSaveActionsEnabled(bool value)
+{
+ openSaveActionsEnabled = value;
+ if (value)
+ {
+ noConfigShortcutActions.remove(SAVE_SQL_FILE);
+ noConfigShortcutActions.remove(SAVE_AS_SQL_FILE);
+ noConfigShortcutActions.remove(OPEN_SQL_FILE);
+ }
+ else
+ noConfigShortcutActions << SAVE_SQL_FILE << SAVE_AS_SQL_FILE << OPEN_SQL_FILE;
}
void SqlEditor::updateUndoAction(bool enabled)
@@ -487,6 +553,12 @@ void SqlEditor::updateCompleterPosition()
void SqlEditor::completeSelected()
{
+ if (completer->getMode() == CompleterWindow::SNIPPETS)
+ {
+ insertPlainText(CODESNIPPETS->getCodeByName(completer->getSnippetName()));
+ return;
+ }
+
deletePreviousChars(completer->getNumberOfCharsToRemove());
ExpectedTokenPtr token = completer->getSelected();
@@ -505,7 +577,7 @@ void SqlEditor::completeSelected()
void SqlEditor::checkForAutoCompletion()
{
- if (!db || !autoCompletion || deletionKeyPressed || !richFeaturesEnabled)
+ if (!db || !autoCompletion || deletionKeyPressed || !richFeaturesEnabled || !CFG_CORE.CodeAssistant.AutoTrigger.get())
return;
Lexer lexer;
@@ -529,21 +601,25 @@ void SqlEditor::refreshValidObjects()
if (!db || !db->isValid())
return;
- objectsInNamedDbFuture = QtConcurrent::run([this]()
+ Db* dbClone = db->clone();
+ QFuture<QHash<QString,QStringList>> objectsInNamedDbFuture = QtConcurrent::run([dbClone]()
{
- QMutexLocker lock(&objectsInNamedDbMutex);
- objectsInNamedDb.clear();
-
- SchemaResolver resolver(db);
+ dbClone->openQuiet();
+ QHash<QString,QStringList> objectsByDbName;
+ SchemaResolver resolver(dbClone);
QSet<QString> databases = resolver.getDatabases();
databases << "main";
QStringList objects;
- for (const QString& dbName : databases)
+ for (const QString& dbName : qAsConst(databases))
{
objects = resolver.getAllObjects(dbName);
- objectsInNamedDb[dbName] << objects;
+ objectsByDbName[dbName] << objects;
}
+ dbClone->closeQuiet();
+ delete dbClone;
+ return objectsByDbName;
});
+ objectsInNamedDbWatcher->setFuture(objectsInNamedDbFuture);
}
void SqlEditor::setObjectLinks(bool enabled)
@@ -551,7 +627,7 @@ void SqlEditor::setObjectLinks(bool enabled)
objectLinksEnabled = enabled;
setMouseTracking(enabled);
highlighter->setObjectLinksEnabled(enabled);
- highlighter->rehighlight();
+ highlightSyntax();
if (enabled)
handleValidObjectCursor(mapFromGlobal(QCursor::pos()));
@@ -574,7 +650,7 @@ void SqlEditor::clearDbObjects()
void SqlEditor::lineNumberAreaPaintEvent(QPaintEvent* event)
{
QPainter painter(lineNumberArea);
- painter.fillRect(event->rect(), STYLE->extendedPalette().editorLineBase());
+ painter.fillRect(event->rect(), STYLE->extendedPalette().editorLineNumberBase());
QTextBlock block = firstVisibleBlock();
int blockNumber = block.blockNumber();
int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top();
@@ -649,9 +725,32 @@ void SqlEditor::highlightParenthesis(QList<QTextEdit::ExtraSelection>& selection
markMatchedParenthesis(thePar->position, matchedPar->position, selections);
}
-void SqlEditor::highlightCurrentCursorContext()
+void SqlEditor::highlightCurrentQuery(QList<QTextEdit::ExtraSelection>& selections)
+{
+ QTextCursor cursor = textCursor();
+ int curPos = cursor.position();
+ QString contents = cursor.document()->toPlainText();
+ QPair<int,int> boundries = getQueryBoundriesForPosition(contents, curPos, true);
+ if (boundries.second < 0)
+ return;
+
+ QTextEdit::ExtraSelection selection;
+ selection.format.setBackground(STYLE->extendedPalette().editorCurrentQueryBase());
+
+ cursor.setPosition(boundries.first);
+ cursor.setPosition(boundries.second, QTextCursor::KeepAnchor);
+ selection.cursor = cursor;
+ selections.append(selection);
+}
+
+void SqlEditor::highlightCurrentCursorContext(bool delayedCall)
{
QList<QTextEdit::ExtraSelection> selections;
+ if (delayedCall)
+ highlightCurrentQuery(selections);
+ else if (currentQueryTimer)
+ currentQueryTimer->start();
+
highlightCurrentLine(selections);
highlightParenthesis(selections);
setExtraSelections(selections);
@@ -661,8 +760,8 @@ void SqlEditor::markMatchedParenthesis(int pos1, int pos2, QList<QTextEdit::Extr
{
QTextEdit::ExtraSelection selection;
- selection.format.setBackground(style()->standardPalette().windowText());
- selection.format.setForeground(style()->standardPalette().window());
+ selection.format.setBackground(Cfg::getSyntaxParenthesisBg());
+ selection.format.setForeground(Cfg::getSyntaxParenthesisFg());
QTextCursor cursor = textCursor();
@@ -829,7 +928,7 @@ void SqlEditor::completerRightPressed()
void SqlEditor::parseContents()
{
- if (!richFeaturesEnabled)
+ if (!richFeaturesEnabled && !alwaysEnforceErrorsChecking)
return;
QString sql = toPlainText();
@@ -841,13 +940,20 @@ void SqlEditor::parseContents()
sql = virtualSqlExpression.arg(sql);
}
+ queryParser->parse(sql);
if (richFeaturesEnabled)
- {
- queryParser->parse(sql);
checkForValidObjects();
- checkForSyntaxErrors();
- highlighter->rehighlight();
- }
+
+ checkForSyntaxErrors();
+
+ if (richFeaturesEnabled)
+ highlightSyntax();
+}
+
+void SqlEditor::scheduleQueryParserForSchemaRefresh()
+{
+ objectsInNamedDb = objectsInNamedDbWatcher->future().result();
+ scheduleQueryParser(true, true);
}
void SqlEditor::checkForSyntaxErrors()
@@ -858,9 +964,9 @@ void SqlEditor::checkForSyntaxErrors()
// Marking invalid tokens, like in "SELECT * from test] t" - the "]" token is invalid.
// Such tokens don't cause parser to fail.
- for (SqliteQueryPtr query : queryParser->getQueries())
+ for (const SqliteQueryPtr& query : queryParser->getQueries())
{
- for (TokenPtr token : query->tokens)
+ for (TokenPtr& token : query->tokens)
{
if (token->type == Token::INVALID)
markErrorAt(token->start, token->end, true);
@@ -886,16 +992,15 @@ void SqlEditor::checkForValidObjects()
if (!db || !db->isValid())
return;
- QMutexLocker lock(&objectsInNamedDbMutex);
QList<SqliteStatement::FullObject> fullObjects;
QString dbName;
- for (SqliteQueryPtr query : queryParser->getQueries())
+ for (const SqliteQueryPtr& query : queryParser->getQueries())
{
fullObjects = query->getContextFullObjects();
- for (const SqliteStatement::FullObject& fullObj : fullObjects)
+ for (SqliteStatement::FullObject& fullObj : fullObjects)
{
dbName = fullObj.database ? stripObjName(fullObj.database->value) : "main";
- if (!objectsInNamedDb.contains(dbName))
+ if (!objectsInNamedDb.contains(dbName, Qt::CaseInsensitive))
continue;
if (fullObj.type == SqliteStatement::FullObject::DATABASE)
@@ -905,7 +1010,7 @@ void SqlEditor::checkForValidObjects()
continue;
}
- if (!objectsInNamedDb[dbName].contains(stripObjName(fullObj.object->value)))
+ if (!objectsInNamedDb[dbName].contains(stripObjName(fullObj.object->value), Qt::CaseInsensitive))
continue;
// Valid object name
@@ -914,7 +1019,7 @@ void SqlEditor::checkForValidObjects()
}
}
-void SqlEditor::scheduleQueryParser(bool force)
+void SqlEditor::scheduleQueryParser(bool force, bool skipCompleter)
{
if (!document()->isModified() && !force)
return;
@@ -923,7 +1028,8 @@ void SqlEditor::scheduleQueryParser(bool force)
document()->setModified(false);
queryParserTrigger->schedule();
- autoCompleteTrigger->schedule();
+ if (!skipCompleter)
+ autoCompleteTrigger->schedule();
}
int SqlEditor::sqlIndex(int idx)
@@ -968,7 +1074,14 @@ QString SqlEditor::getSelectedText() const
void SqlEditor::openObject(const QString& database, const QString& name)
{
DbObjectDialogs dialogs(db);
- dialogs.editObject(database, name);
+ dialogs.editObject(DbObjectDialogs::Type::UNKNOWN, database, name);
+}
+
+void SqlEditor::highlightSyntax()
+{
+ highlightingSyntax = true;
+ highlighter->rehighlight();
+ highlightingSyntax = false;
}
void SqlEditor::updateLineNumberAreaWidth()
@@ -987,7 +1100,6 @@ void SqlEditor::highlightCurrentLine(QList<QTextEdit::ExtraSelection>& selection
if (!isReadOnly() && isEnabled())
{
QTextEdit::ExtraSelection selection;
-
selection.format.setBackground(STYLE->extendedPalette().editorLineBase());
selection.format.setProperty(QTextFormat::FullWidthSelection, true);
selection.cursor = textCursor();
@@ -1059,6 +1171,9 @@ void SqlEditor::saveToFile()
void SqlEditor::saveAsToFile()
{
+ if (!openSaveActionsEnabled)
+ return;
+
QString dir = getFileDialogInitPath();
QString fName = QFileDialog::getSaveFileName(this, tr("Save to file"), dir);
if (fName.isNull())
@@ -1071,6 +1186,9 @@ void SqlEditor::saveAsToFile()
void SqlEditor::loadFromFile()
{
+ if (!openSaveActionsEnabled)
+ return;
+
QString dir = getFileDialogInitPath();
QString filters = tr("SQL scripts (*.sql);;All files (*)");
QString fName = QFileDialog::getOpenFileName(this, tr("Open file"), dir, filters);
@@ -1083,7 +1201,7 @@ void SqlEditor::loadFromFile()
QString sql = readFileContents(fName, &err);
if (sql.isNull() && !err.isNull())
{
- notifyError(tr("Could not open file '%1' for reading: %2").arg(fName).arg(err));
+ notifyError(tr("Could not open file '%1' for reading: %2").arg(fName, err));
return;
}
@@ -1293,13 +1411,14 @@ void SqlEditor::reachedEnd()
void SqlEditor::changeFont(const QVariant& font)
{
- setFont(font.value<QFont>());
+ auto f = font.value<QFont>();
+ setFont(f);
+ lineNumberArea->setFont(f);
}
void SqlEditor::configModified()
{
- highlighter->rehighlight();
- highlightCurrentCursorContext();
+ colorsConfigChanged();
}
void SqlEditor::toggleComment()
@@ -1387,6 +1506,51 @@ void SqlEditor::toggleComment()
setTextCursor(cur);
}
+void SqlEditor::wordWrappingChanged(const QVariant& value)
+{
+ setLineWrapMode(value.toBool() ? QPlainTextEdit::WidgetWidth : QPlainTextEdit::NoWrap);
+}
+
+void SqlEditor::currentCursorContextDelayedHighlight()
+{
+ highlightCurrentCursorContext(true);
+}
+
+void SqlEditor::fontSizeChangeRequested(int delta)
+{
+ changeFontSize(delta >= 0 ? 1 : -1);
+}
+
+void SqlEditor::incrFontSize()
+{
+ changeFontSize(1);
+}
+
+void SqlEditor::decrFontSize()
+{
+ changeFontSize(-1);
+}
+
+void SqlEditor::moveCursorTo(int pos)
+{
+ QTextCursor cur = textCursor();
+ cur.setPosition(pos);
+ setTextCursor(cur);
+}
+
+void SqlEditor::changeFontSize(int factor)
+{
+ auto f = font();
+ f.setPointSize(f.pointSize() + factor);
+ CFG_UI.Fonts.SqlEditor.set(f);
+}
+
+void SqlEditor::colorsConfigChanged()
+{
+ highlightSyntax();
+ highlightCurrentCursorContext();
+}
+
void SqlEditor::keyPressEvent(QKeyEvent* e)
{
switch (e->key())
@@ -1563,6 +1727,21 @@ QToolBar* SqlEditor::getToolBar(int toolbar) const
return nullptr;
}
+void SqlEditor::setCurrentQueryHighlighting(bool enabled)
+{
+ if (enabled && !currentQueryTimer)
+ {
+ currentQueryTimer = new QTimer(this);
+ currentQueryTimer->setInterval(300);
+ currentQueryTimer->setSingleShot(true);
+ connect(currentQueryTimer, SIGNAL(timeout()), this, SLOT(currentCursorContextDelayedHighlight()));
+ }
+ else if (!enabled && currentQueryTimer)
+ {
+ safe_delete(currentQueryTimer);
+ }
+}
+
QString SqlEditor::getVirtualSqlExpression() const
{
return virtualSqlExpression;
@@ -1600,7 +1779,7 @@ const SqlEditor::DbObject* SqlEditor::getValidObjectForPosition(const QPoint& po
const SqlEditor::DbObject* SqlEditor::getValidObjectForPosition(int position, bool movedLeft)
{
- for (const DbObject& obj : validDbObjects)
+ for (DbObject& obj : validDbObjects)
{
if ((!movedLeft && position > obj.from && position-1 <= obj.to) ||
(movedLeft && position >= obj.from && position <= obj.to))
@@ -1641,3 +1820,16 @@ void SqlEditor::changeEvent(QEvent* e)
QPlainTextEdit::changeEvent(e);
}
+
+void SqlEditor::showEvent(QShowEvent* event)
+{
+ UNUSED(event);
+ setLineWrapMode(wrapWords ? QPlainTextEdit::WidgetWidth : QPlainTextEdit::NoWrap);
+}
+
+void SqlEditor::dropEvent(QDropEvent* e)
+{
+ QPlainTextEdit::dropEvent(e);
+ if (MAINWINDOW->getDbTree()->getModel()->hasDbTreeItem(e->mimeData()))
+ e->ignore();
+}