diff --git a/python/console/console_editor.py b/python/console/console_editor.py index 4f5be86d66b9..8d395cd123d0 100644 --- a/python/console/console_editor.py +++ b/python/console/console_editor.py @@ -80,8 +80,6 @@ def eventFilter(self, obj, event): class Editor(QgsCodeEditorPython): - MARKER_NUM = 6 - def __init__(self, parent=None): super().__init__(parent) self.parent = parent @@ -89,22 +87,11 @@ def __init__(self, parent=None): self.lastModified = 0 self.opening = ['(', '{', '[', "'", '"'] self.closing = [')', '}', ']', "'", '"'] - - # List of marker line to be deleted from check syntax - self.bufferMarkerLine = [] - self.settings = QgsSettings() - self.markerDefine(QgsApplication.getThemePixmap("console/iconSyntaxErrorConsole.svg"), - self.MARKER_NUM) - self.setMinimumHeight(120) - self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) - # Annotations - self.setAnnotationDisplay(QsciScintilla.ANNOTATION_BOXED) - # Disable command key ctrl, shift = self.SCMOD_CTRL << 16, self.SCMOD_SHIFT << 16 self.SendScintilla(QsciScintilla.SCI_CLEARCMDKEY, ord('L') + ctrl) @@ -160,9 +147,9 @@ def contextMenuEvent(self, e): QCoreApplication.translate("PythonConsole", "Hide Editor"), self.hideEditor) menu.addSeparator() # ------------------------------ - syntaxCheck = menu.addAction(QgsApplication.getThemeIcon("console/iconSyntaxErrorConsole.svg"), - QCoreApplication.translate("PythonConsole", "Check Syntax"), - self.syntaxCheck, 'Ctrl+4') + syntaxCheckAction = menu.addAction(QgsApplication.getThemeIcon("console/iconSyntaxErrorConsole.svg"), + QCoreApplication.translate("PythonConsole", "Check Syntax"), + self.syntaxCheck, 'Ctrl+4') runSelected = menu.addAction(QgsApplication.getThemeIcon("console/mIconRunConsole.svg"), # spellok QCoreApplication.translate("PythonConsole", "Run Selected"), self.runSelectedCode, 'Ctrl+E') # spellok @@ -218,7 +205,7 @@ def contextMenuEvent(self, e): menu.addAction(QgsApplication.getThemeIcon("console/iconSettingsConsole.svg"), QCoreApplication.translate("PythonConsole", "Options…"), self.parent.pc.openSettings) - syntaxCheck.setEnabled(False) + syntaxCheckAction.setEnabled(False) pasteAction.setEnabled(False) pyQGISHelpAction.setEnabled(False) gist_menu.setEnabled(False) @@ -237,7 +224,7 @@ def contextMenuEvent(self, e): pyQGISHelpAction.setEnabled(True) if not self.text() == '': selectAllAction.setEnabled(True) - syntaxCheck.setEnabled(True) + syntaxCheckAction.setEnabled(True) if self.isUndoAvailable(): undoAction.setEnabled(True) if self.isRedoAvailable(): @@ -461,7 +448,7 @@ def runScriptCode(self): self.parent.pc.callWidgetMessageBarEditor(msgEditorBlank, 0, True) return - if self.syntaxCheck(fromContextMenu=False): + if self.syntaxCheck(): if filename and self.isModified() and autoSave: self.parent.save(filename) elif not filename or self.isModified(): @@ -496,16 +483,13 @@ def goToLine(self, objName, linenr): self.setFocus() def syntaxCheck(self): - eline = None - ecolumn = 0 - edescr = '' source = self.text() + self.clearWarnings() try: filename = self.parent.tw.currentWidget().path if not filename: tmpFile = self.createTempFile() filename = tmpFile - # source = open(filename, 'r').read() + '\n' if isinstance(source, type("")): source = source.encode('utf-8') if isinstance(filename, type("")): @@ -514,37 +498,16 @@ def syntaxCheck(self): compile(source, filename, 'exec') except SyntaxError as detail: eline = detail.lineno and detail.lineno or 1 + eline -= 1 ecolumn = detail.offset and detail.offset or 1 edescr = detail.msg - if eline is not None: - eline -= 1 - for markerLine in self.bufferMarkerLine: - self.markerDelete(markerLine) - self.clearAnnotations(markerLine) - self.bufferMarkerLine.remove(markerLine) - if (eline) not in self.bufferMarkerLine: - self.bufferMarkerLine.append(eline) - - self.setMarginWidth(1, "0000") - self.markerAdd(eline, self.MARKER_NUM) - font = self.lexerFont() - font.setItalic(True) - styleAnn = QsciStyle(-1, "Annotation", - QColor(255, 0, 0), - QColor(255, 200, 0), - font, - True) - self.annotate(eline, edescr, styleAnn) + + self.addWarning(eline, edescr) self.setCursorPosition(eline, ecolumn - 1) - # self.setSelection(eline, ecolumn, eline, self.lineLength(eline)-1) self.ensureLineVisible(eline) - # self.ensureCursorVisible() return False - else: - self.setMarginWidth(1, 0) - self.markerDeleteAll() - self.clearAnnotations() - return True + + return True def keyPressEvent(self, e): t = e.text() diff --git a/python/gui/auto_generated/codeeditors/qgscodeeditor.sip.in b/python/gui/auto_generated/codeeditors/qgscodeeditor.sip.in index 008ef925707a..05afe50601f0 100644 --- a/python/gui/auto_generated/codeeditors/qgscodeeditor.sip.in +++ b/python/gui/auto_generated/codeeditors/qgscodeeditor.sip.in @@ -120,6 +120,24 @@ Returns the monospaced font to use for code editors. %End + void addWarning( int lineNumber, const QString &warning ); +%Docstring +Adds a ``warning`` message and indicator to the specified a ``lineNumber``. + +.. seealso:: :py:func:`clearWarnings` + +.. versionadded:: 3.16 +%End + + void clearWarnings(); +%Docstring +Clears all warning messages from the editor. + +.. seealso:: :py:func:`addWarning` + +.. versionadded:: 3.16 +%End + protected: bool isFixedPitch( const QFont &font ); diff --git a/src/gui/codeeditors/qgscodeeditor.cpp b/src/gui/codeeditors/qgscodeeditor.cpp index 6027b4129c29..242846545abf 100644 --- a/src/gui/codeeditors/qgscodeeditor.cpp +++ b/src/gui/codeeditors/qgscodeeditor.cpp @@ -27,6 +27,7 @@ #include #include #include +#include QMap< QgsCodeEditorColorScheme::ColorRole, QString > QgsCodeEditor::sColorRoleToSettingsKey { @@ -88,6 +89,13 @@ QgsCodeEditor::QgsCodeEditor( QWidget *parent, const QString &title, bool foldin SendScintilla( SCI_SETMULTIPASTE, 1 ); SendScintilla( SCI_SETVIRTUALSPACEOPTIONS, SCVS_RECTANGULARSELECTION ); + markerDefine( QgsApplication::getThemePixmap( "console/iconSyntaxErrorConsole.svg" ), + MARKER_NUMBER ); + SendScintilla( SCI_SETMARGINTYPEN, 3, SC_MARGIN_SYMBOL ); + SendScintilla( SCI_SETMARGINMASKN, 3, 1 << MARKER_NUMBER ); + setMarginWidth( 3, 0 ); + setAnnotationDisplay( QsciScintilla::AnnotationBoxed ); + connect( QgsGui::instance(), &QgsGui::optionsChanged, this, [ = ] { setSciWidget(); @@ -443,3 +451,29 @@ void QgsCodeEditor::setCustomAppearance( const QString &scheme, const QMap &customColors = QMap< QgsCodeEditorColorScheme::ColorRole, QColor >(), const QString &fontFamily = QString(), int fontSize = 0 ) SIP_SKIP; + /** + * Adds a \a warning message and indicator to the specified a \a lineNumber. + * + * \see clearWarnings() + * \since QGIS 3.16 + */ + void addWarning( int lineNumber, const QString &warning ); + + /** + * Clears all warning messages from the editor. + * + * \see addWarning() + * \since QGIS 3.16 + */ + void clearWarnings(); + protected: bool isFixedPitch( const QFont &font ); @@ -188,7 +204,11 @@ class GUI_EXPORT QgsCodeEditor : public QsciScintilla QString mFontFamily; int mFontSize = 0; + QVector< int > mWarningLines; + static QMap< QgsCodeEditorColorScheme::ColorRole, QString > sColorRoleToSettingsKey; + + static constexpr int MARKER_NUMBER = 6; }; // clazy:excludeall=qstring-allocations