Skip to content

Commit

Permalink
Merge pull request #7835 from m-kuhn/expressionCodeCompletion
Browse files Browse the repository at this point in the history
Code completion for expression builder [FEATURE]
  • Loading branch information
m-kuhn committed Sep 10, 2018
2 parents 7ed8460 + da7cc96 commit 41184bf
Show file tree
Hide file tree
Showing 9 changed files with 360 additions and 8 deletions.
53 changes: 53 additions & 0 deletions python/gui/auto_generated/qgscodeeditorexpression.sip.in
@@ -0,0 +1,53 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/qgscodeeditorexpression.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/





class QgsCodeEditorExpression : QgsCodeEditor
{
%Docstring

A QGIS expression editor based on QScintilla2. Adds syntax highlighting and
code autocompletion.

.. versionadded:: 3.4
%End

%TypeHeaderCode
#include "qgscodeeditorexpression.h"
%End
public:
QgsCodeEditorExpression( QWidget *parent /TransferThis/ = 0 );
%Docstring
Constructor for QgsCodeEditorExpression
%End

void setExpressionContext( const QgsExpressionContext &context );
%Docstring
Variables and functions from this expression context will be added to
the API.
Will also reload all globally registered functions.
%End

void setFields( const QgsFields &fields );
%Docstring
Field names will be added to the API.
%End

};


/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/qgscodeeditorexpression.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
3 changes: 3 additions & 0 deletions python/gui/gui_auto.sip
Expand Up @@ -79,6 +79,9 @@
%If ( HAVE_QSCI_SIP )
%Include auto_generated/qgscodeeditorsql.sip
%End
%If ( HAVE_QSCI_SIP )
%Include auto_generated/qgscodeeditorexpression.sip
%End
%Include auto_generated/qgscollapsiblegroupbox.sip
%Include auto_generated/qgscolorbrewercolorrampdialog.sip
%Include auto_generated/qgscolorbutton.sip
Expand Down
2 changes: 2 additions & 0 deletions src/gui/CMakeLists.txt
Expand Up @@ -229,6 +229,7 @@ SET(QGIS_GUI_SRCS
qgscodeeditorhtml.cpp
qgscodeeditorpython.cpp
qgscodeeditorsql.cpp
qgscodeeditorexpression.cpp
qgscollapsiblegroupbox.cpp
qgscolorbrewercolorrampdialog.cpp
qgscolorbutton.cpp
Expand Down Expand Up @@ -406,6 +407,7 @@ SET(QGIS_GUI_MOC_HDRS
qgscodeeditorhtml.h
qgscodeeditorpython.h
qgscodeeditorsql.h
qgscodeeditorexpression.h
qgscollapsiblegroupbox.h
qgscolorbrewercolorrampdialog.h
qgscolorbutton.h
Expand Down
183 changes: 183 additions & 0 deletions src/gui/qgscodeeditorexpression.cpp
@@ -0,0 +1,183 @@
/***************************************************************************
qgscodeeditorexpressoin.cpp - An expression editor based on QScintilla
--------------------------------------
Date : 8.9.2018
Copyright : (C) 2018 by Matthias Kuhn
Email : matthias@opengis.ch
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgsapplication.h"
#include "qgscodeeditorexpression.h"

#include <QString>
#include <QFont>
#include <QLabel>

QgsCodeEditorExpression::QgsCodeEditorExpression( QWidget *parent )
: QgsCodeEditor( parent )
{
if ( !parent )
{
setTitle( tr( "Expression Editor" ) );
}
setMarginVisible( false );
setFoldingVisible( true );
setAutoCompletionCaseSensitivity( false );
initializeLexer();
}

void QgsCodeEditorExpression::setExpressionContext( const QgsExpressionContext &context )
{
mVariables.clear();

const QStringList variableNames = context.filteredVariableNames();
for ( const QString &var : variableNames )
{
mVariables << '@' + var;
}

mContextFunctions = context.functionNames();

mFunctions.clear();

const int count = QgsExpression::functionCount();
for ( int i = 0; i < count; i++ )
{
QgsExpressionFunction *func = QgsExpression::Functions()[i];
if ( func->isDeprecated() ) // don't show deprecated functions
continue;
if ( func->isContextual() )
{
//don't show contextual functions by default - it's up the the QgsExpressionContext
//object to provide them if supported
continue;
}

QString signature = func->name();
if ( !signature.startsWith( '$' ) )
{
signature += '(';

QStringList paramNames;
const auto &parameters = func->parameters();
for ( const auto &param : parameters )
{
paramNames << param.name();
}

// No named parameters but there should be parameteres? Show an ellipsis at least
if ( parameters.isEmpty() && func->params() )
signature += QChar( 0x2026 );

signature += paramNames.join( ", " );

signature += ')';
}
mFunctions << signature;
}

updateApis();
}

void QgsCodeEditorExpression::setFields( const QgsFields &fields )
{
mFieldNames.clear();

for ( const QgsField &field : fields )
{
mFieldNames << field.name();
}

updateApis();
}


void QgsCodeEditorExpression::initializeLexer()
{
QFont font = getMonospaceFont();
#ifdef Q_OS_MAC
// The font size gotten from getMonospaceFont() is too small on Mac
font.setPointSize( QLabel().font().pointSize() );
#endif
mSqlLexer = new QgsLexerExpression( this );
mSqlLexer->setDefaultFont( font );
mSqlLexer->setFont( font, -1 );
font.setBold( true );
mSqlLexer->setFont( font, QsciLexerSQL::Keyword );
mSqlLexer->setColor( Qt::darkYellow, QsciLexerSQL::DoubleQuotedString ); // fields

setLexer( mSqlLexer );
}

void QgsCodeEditorExpression::updateApis()
{
mApis = new QgsSciApisExpression( mSqlLexer );

for ( const QString &var : qgis::as_const( mVariables ) )
{
mApis->add( var );
}

for ( const QString &function : qgis::as_const( mContextFunctions ) )
{
mApis->add( function );
}

for ( const QString &function : qgis::as_const( mFunctions ) )
{
mApis->add( function );
}

for ( const QString &fieldName : qgis::as_const( mFieldNames ) )
{
mApis->add( fieldName );
}

mApis->prepare();
mSqlLexer->setAPIs( mApis );
}

///@cond PRIVATE
QgsLexerExpression::QgsLexerExpression( QObject *parent )
: QsciLexerSQL( parent )
{
}

const char *QgsLexerExpression::language() const
{
return "QGIS Expression";
}

bool QgsLexerExpression::caseSensitive() const
{
return false;
}

const char *QgsLexerExpression::wordCharacters() const
{
return "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_@";
}

QgsSciApisExpression::QgsSciApisExpression( QsciLexer *lexer )
: QsciAPIs( lexer )
{

}

QStringList QgsSciApisExpression::callTips( const QStringList &context, int commas, QsciScintilla::CallTipsStyle style, QList<int> &shifts )
{
const QStringList originalTips = QsciAPIs::callTips( context, commas, style, shifts );
QStringList lowercaseTips;
for ( const QString &tip : originalTips )
lowercaseTips << tip.toLower();

return lowercaseTips;
}
///@endcond
105 changes: 105 additions & 0 deletions src/gui/qgscodeeditorexpression.h
@@ -0,0 +1,105 @@
/***************************************************************************
qgscodeeditorsql.h - A SQL editor based on QScintilla
--------------------------------------
Date : 06-Oct-2013
Copyright : (C) 2013 by Salvatore Larosa
Email : lrssvtml (at) gmail (dot) com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#ifndef QGSCODEEDITOREXPRESSION_H
#define QGSCODEEDITOREXPRESSION_H

#include "qgis_sip.h"
#include "qgis_gui.h"
#include "qgscodeeditor.h"
#include "qgsexpressioncontext.h"

#include <Qsci/qscilexersql.h>

SIP_IF_MODULE( HAVE_QSCI_SIP )

/**
* \ingroup gui
*
* A QGIS expression editor based on QScintilla2. Adds syntax highlighting and
* code autocompletion.
*
* \since QGIS 3.4
*/
class GUI_EXPORT QgsCodeEditorExpression : public QgsCodeEditor
{
Q_OBJECT

public:
//! Constructor for QgsCodeEditorExpression
QgsCodeEditorExpression( QWidget *parent SIP_TRANSFERTHIS = nullptr );

/**
* Variables and functions from this expression context will be added to
* the API.
* Will also reload all globally registered functions.
*/
void setExpressionContext( const QgsExpressionContext &context );

/**
* Field names will be added to the API.
*/
void setFields( const QgsFields &fields );

private:
void initializeLexer();
void updateApis();
QsciAPIs *mApis = nullptr;
QsciLexerSQL *mSqlLexer;

QStringList mVariables;
QStringList mContextFunctions;
QStringList mFunctions;
QStringList mFieldNames;
};

#ifndef SIP_RUN
///@cond PRIVATE

/**
* Internal use.
setAutoCompletionCaseSensitivity( false ) is not sufficient when installing
a lexer, since its caseSensitive() method is actually used, and defaults
to true.
\note not available in Python bindings
\ingroup gui
*/
class QgsLexerExpression : public QsciLexerSQL
{
Q_OBJECT

public:
//! Constructor
explicit QgsLexerExpression( QObject *parent = nullptr );

const char *language() const override;

bool caseSensitive() const override;

const char *wordCharacters() const override;
};

class QgsSciApisExpression : public QsciAPIs
{
public:
QgsSciApisExpression( QsciLexer *lexer );

QStringList callTips( const QStringList &context, int commas, QsciScintilla::CallTipsStyle style, QList<int> &shifts ) override;
};
///@endcond
#endif

#endif

0 comments on commit 41184bf

Please sign in to comment.