Move syntax checking capability to QgsCodeEditorPython

This commit is contained in:
Nyall Dawson 2023-03-17 12:34:09 +10:00
parent ac5f8da74e
commit 3312bf1ad2
9 changed files with 112 additions and 8 deletions

View File

@ -2590,7 +2590,8 @@ Qgis.ScriptLanguage.__doc__ = 'Scripting languages.\n\n.. versionadded:: 3.30\n\
Qgis.ScriptLanguage.baseClass = Qgis
# monkey patching scoped based enum
Qgis.ScriptLanguageCapability.Reformat.__doc__ = "Language supports automatic code reformatting"
Qgis.ScriptLanguageCapability.__doc__ = 'Script language capabilities.\n\nThe flags reflect the support capabilities of a scripting language.\n\n.. versionadded:: 3.32\n\n' + '* ``Reformat``: ' + Qgis.ScriptLanguageCapability.Reformat.__doc__
Qgis.ScriptLanguageCapability.CheckSyntax.__doc__ = "Language supports syntax checking"
Qgis.ScriptLanguageCapability.__doc__ = 'Script language capabilities.\n\nThe flags reflect the support capabilities of a scripting language.\n\n.. versionadded:: 3.32\n\n' + '* ``Reformat``: ' + Qgis.ScriptLanguageCapability.Reformat.__doc__ + '\n' + '* ``CheckSyntax``: ' + Qgis.ScriptLanguageCapability.CheckSyntax.__doc__
# --
Qgis.ScriptLanguageCapability.baseClass = Qgis
Qgis.ScriptLanguageCapabilities.baseClass = Qgis

View File

@ -1592,6 +1592,7 @@ The development version
enum class ScriptLanguageCapability
{
Reformat,
CheckSyntax,
};
typedef QFlags<Qgis::ScriptLanguageCapability> ScriptLanguageCapabilities;

View File

@ -427,6 +427,15 @@ Applies code reformatting to the editor.
This is only supported for editors which return the Qgis.ScriptLanguageCapability.Reformat capability from :py:func:`~QgsCodeEditor.languageCapabilities`.
.. versionadded:: 3.32
%End
virtual bool checkSyntax();
%Docstring
Applies syntax checking to the editor.
This is only supported for editors which return the Qgis.ScriptLanguageCapability.CheckSyntax capability from :py:func:`~QgsCodeEditor.languageCapabilities`.
.. versionadded:: 3.32
%End

View File

@ -85,6 +85,9 @@ Updates the editor capabilities.
.. versionadded:: 3.32
%End
virtual bool checkSyntax();
public slots:
void searchSelectedTextInPyQGISDocs();

View File

@ -2735,6 +2735,7 @@ class CORE_EXPORT Qgis
enum class ScriptLanguageCapability : int
{
Reformat = 1 << 0, //!< Language supports automatic code reformatting
CheckSyntax = 1 << 1, //!< Language supports syntax checking
};
Q_ENUM( ScriptLanguageCapability )

View File

@ -224,16 +224,29 @@ void QgsCodeEditor::contextMenuEvent( QContextMenuEvent *event )
QMenu *menu = createStandardContextMenu();
menu->setAttribute( Qt::WA_DeleteOnClose );
if ( languageCapabilities() & Qgis::ScriptLanguageCapability::Reformat )
if ( ( languageCapabilities() & Qgis::ScriptLanguageCapability::Reformat ) ||
( languageCapabilities() & Qgis::ScriptLanguageCapability::CheckSyntax ) )
{
menu->addSeparator();
}
if ( languageCapabilities() & Qgis::ScriptLanguageCapability::Reformat )
{
QAction *reformatAction = new QAction( tr( "Reformat Code" ), menu );
reformatAction->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "console/iconFormatCode.svg" ) ) );
reformatAction->setEnabled( !isReadOnly() );
connect( reformatAction, &QAction::triggered, this, &QgsCodeEditor::reformatCode );
menu->addAction( reformatAction );
}
if ( languageCapabilities() & Qgis::ScriptLanguageCapability::Reformat )
{
QAction *syntaxCheckAction = new QAction( tr( "Check Syntax" ), menu );
syntaxCheckAction->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "console/iconSyntaxErrorConsole.svg" ) ) );
connect( syntaxCheckAction, &QAction::triggered, this, &QgsCodeEditor::checkSyntax );
menu->addAction( syntaxCheckAction );
}
populateContextMenu( menu );
menu->exec( mapToGlobal( event->pos() ) );
@ -709,6 +722,11 @@ void QgsCodeEditor::reformatCode()
endUndoAction();
}
bool QgsCodeEditor::checkSyntax()
{
return true;
}
QStringList QgsCodeEditor::history() const
{
return mHistory;

View File

@ -446,6 +446,15 @@ class GUI_EXPORT QgsCodeEditor : public QsciScintilla
*/
void reformatCode();
/**
* Applies syntax checking to the editor.
*
* This is only supported for editors which return the Qgis::ScriptLanguageCapability::CheckSyntax capability from languageCapabilities().
*
* \since QGIS 3.32
*/
virtual bool checkSyntax();
signals:
/**

View File

@ -374,13 +374,13 @@ QString QgsCodeEditorPython::reformatCodeString( const QString &string )
if ( settings.value( "pythonConsole/sortImports", true ).toBool() )
{
const QString defineSortImports = QStringLiteral(
"def __qgis_sort_imports(str):\n"
"def __qgis_sort_imports(script):\n"
" try:\n"
" import isort\n"
" except ImportError:\n"
" return '_ImportError'\n"
" options={'line_length': %1, 'profile': '%2', 'known_first_party': ['qgis', 'console', 'processing', 'plugins']}\n"
" return isort.code(str, **options)\n" )
" return isort.code(script, **options)\n" )
.arg( maxLineLength )
.arg( formatter == QLatin1String( "black" ) ? QStringLiteral( "black" ) : QString() );
@ -415,13 +415,13 @@ QString QgsCodeEditorPython::reformatCodeString( const QString &string )
const int level = settings.value( QStringLiteral( "pythonConsole/autopep8Level" ), 1 ).toInt();
const QString defineReformat = QStringLiteral(
"def __qgis_reformat(str):\n"
"def __qgis_reformat(script):\n"
" try:\n"
" import autopep8\n"
" except ImportError:\n"
" return '_ImportError'\n"
" options={'aggressive': %1, 'max_line_length': %2}\n"
" return autopep8.fix_code(str, options=options)\n" )
" return autopep8.fix_code(script, options=options)\n" )
.arg( level )
.arg( maxLineLength );
@ -455,13 +455,13 @@ QString QgsCodeEditorPython::reformatCodeString( const QString &string )
const bool normalize = settings.value( QStringLiteral( "pythonConsole/blackNormalizeQuotes" ), true ).toBool();
const QString defineReformat = QStringLiteral(
"def __qgis_reformat(str):\n"
"def __qgis_reformat(script):\n"
" try:\n"
" import black\n"
" except ImportError:\n"
" return '_ImportError'\n"
" options={'string_normalization': %1, 'line_length': %2}\n"
" return black.format_str(str, mode=black.Mode(**options))\n" )
" return black.format_str(script, mode=black.Mode(**options))\n" )
.arg( QgsProcessingUtils::variantToPythonLiteral( normalize ) )
.arg( maxLineLength );
@ -621,12 +621,72 @@ void QgsCodeEditorPython::updateCapabilities()
if ( !QgsPythonRunner::isValid() )
return;
mCapabilities |= Qgis::ScriptLanguageCapability::CheckSyntax;
// we could potentially check for autopep8/black import here and reflect the capabilty accordingly.
// (current approach is to to always indicate this capability and raise a user-friendly warning
// when attempting to reformat if the libraries can't be imported)
mCapabilities |= Qgis::ScriptLanguageCapability::Reformat;
}
bool QgsCodeEditorPython::checkSyntax()
{
clearWarnings();
if ( !QgsPythonRunner::isValid() )
{
return true;
}
const QString originalText = text();
const QString defineCheckSyntax = QStringLiteral(
"def __check_syntax(script):\n"
" try:\n"
" compile(script.encode('utf-8'), '', 'exec')\n"
" except SyntaxError as detail:\n"
" eline = detail.lineno or 1\n"
" eline -= 1\n"
" ecolumn = detail.offset or 1\n"
" edescr = detail.msg\n"
" return '!!!!'.join([str(eline), str(ecolumn), edescr])\n"
" return ''" );
if ( !QgsPythonRunner::run( defineCheckSyntax ) )
{
QgsDebugMsg( QStringLiteral( "Error running script: %1" ).arg( defineCheckSyntax ) );
return true;
}
const QString script = QStringLiteral( "__check_syntax(%1)" ).arg( QgsProcessingUtils::stringToPythonLiteral( originalText ) );
QString result;
if ( QgsPythonRunner::eval( script, result ) )
{
if ( result.size() == 0 )
{
return true;
}
else
{
const QStringList parts = result.split( QStringLiteral( "!!!!" ) );
if ( parts.size() == 3 )
{
const int line = parts.at( 0 ).toInt();
const int column = parts.at( 1 ).toInt();
addWarning( line, parts.at( 2 ) );
setCursorPosition( line, column - 1 );
ensureLineVisible( line );
}
return false;
}
}
else
{
QgsDebugMsg( QStringLiteral( "Error running script: %1" ).arg( script ) );
return true;
}
}
void QgsCodeEditorPython::searchSelectedTextInPyQGISDocs()
{
if ( !hasSelectedText() )

View File

@ -104,6 +104,8 @@ class GUI_EXPORT QgsCodeEditorPython : public QgsCodeEditor
*/
void updateCapabilities();
bool checkSyntax() override;
public slots:
/**