Skip to content

Commit 41184bf

Browse files
authoredSep 10, 2018
Merge pull request #7835 from m-kuhn/expressionCodeCompletion
Code completion for expression builder [FEATURE]
2 parents 7ed8460 + da7cc96 commit 41184bf

9 files changed

+360
-8
lines changed
 
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/************************************************************************
2+
* This file has been generated automatically from *
3+
* *
4+
* src/gui/qgscodeeditorexpression.h *
5+
* *
6+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
7+
************************************************************************/
8+
9+
10+
11+
12+
13+
class QgsCodeEditorExpression : QgsCodeEditor
14+
{
15+
%Docstring
16+
17+
A QGIS expression editor based on QScintilla2. Adds syntax highlighting and
18+
code autocompletion.
19+
20+
.. versionadded:: 3.4
21+
%End
22+
23+
%TypeHeaderCode
24+
#include "qgscodeeditorexpression.h"
25+
%End
26+
public:
27+
QgsCodeEditorExpression( QWidget *parent /TransferThis/ = 0 );
28+
%Docstring
29+
Constructor for QgsCodeEditorExpression
30+
%End
31+
32+
void setExpressionContext( const QgsExpressionContext &context );
33+
%Docstring
34+
Variables and functions from this expression context will be added to
35+
the API.
36+
Will also reload all globally registered functions.
37+
%End
38+
39+
void setFields( const QgsFields &fields );
40+
%Docstring
41+
Field names will be added to the API.
42+
%End
43+
44+
};
45+
46+
47+
/************************************************************************
48+
* This file has been generated automatically from *
49+
* *
50+
* src/gui/qgscodeeditorexpression.h *
51+
* *
52+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
53+
************************************************************************/

‎python/gui/gui_auto.sip

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@
7979
%If ( HAVE_QSCI_SIP )
8080
%Include auto_generated/qgscodeeditorsql.sip
8181
%End
82+
%If ( HAVE_QSCI_SIP )
83+
%Include auto_generated/qgscodeeditorexpression.sip
84+
%End
8285
%Include auto_generated/qgscollapsiblegroupbox.sip
8386
%Include auto_generated/qgscolorbrewercolorrampdialog.sip
8487
%Include auto_generated/qgscolorbutton.sip

‎src/gui/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ SET(QGIS_GUI_SRCS
229229
qgscodeeditorhtml.cpp
230230
qgscodeeditorpython.cpp
231231
qgscodeeditorsql.cpp
232+
qgscodeeditorexpression.cpp
232233
qgscollapsiblegroupbox.cpp
233234
qgscolorbrewercolorrampdialog.cpp
234235
qgscolorbutton.cpp
@@ -406,6 +407,7 @@ SET(QGIS_GUI_MOC_HDRS
406407
qgscodeeditorhtml.h
407408
qgscodeeditorpython.h
408409
qgscodeeditorsql.h
410+
qgscodeeditorexpression.h
409411
qgscollapsiblegroupbox.h
410412
qgscolorbrewercolorrampdialog.h
411413
qgscolorbutton.h

‎src/gui/qgscodeeditorexpression.cpp

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
/***************************************************************************
2+
qgscodeeditorexpressoin.cpp - An expression editor based on QScintilla
3+
--------------------------------------
4+
Date : 8.9.2018
5+
Copyright : (C) 2018 by Matthias Kuhn
6+
Email : matthias@opengis.ch
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#include "qgsapplication.h"
17+
#include "qgscodeeditorexpression.h"
18+
19+
#include <QString>
20+
#include <QFont>
21+
#include <QLabel>
22+
23+
QgsCodeEditorExpression::QgsCodeEditorExpression( QWidget *parent )
24+
: QgsCodeEditor( parent )
25+
{
26+
if ( !parent )
27+
{
28+
setTitle( tr( "Expression Editor" ) );
29+
}
30+
setMarginVisible( false );
31+
setFoldingVisible( true );
32+
setAutoCompletionCaseSensitivity( false );
33+
initializeLexer();
34+
}
35+
36+
void QgsCodeEditorExpression::setExpressionContext( const QgsExpressionContext &context )
37+
{
38+
mVariables.clear();
39+
40+
const QStringList variableNames = context.filteredVariableNames();
41+
for ( const QString &var : variableNames )
42+
{
43+
mVariables << '@' + var;
44+
}
45+
46+
mContextFunctions = context.functionNames();
47+
48+
mFunctions.clear();
49+
50+
const int count = QgsExpression::functionCount();
51+
for ( int i = 0; i < count; i++ )
52+
{
53+
QgsExpressionFunction *func = QgsExpression::Functions()[i];
54+
if ( func->isDeprecated() ) // don't show deprecated functions
55+
continue;
56+
if ( func->isContextual() )
57+
{
58+
//don't show contextual functions by default - it's up the the QgsExpressionContext
59+
//object to provide them if supported
60+
continue;
61+
}
62+
63+
QString signature = func->name();
64+
if ( !signature.startsWith( '$' ) )
65+
{
66+
signature += '(';
67+
68+
QStringList paramNames;
69+
const auto &parameters = func->parameters();
70+
for ( const auto &param : parameters )
71+
{
72+
paramNames << param.name();
73+
}
74+
75+
// No named parameters but there should be parameteres? Show an ellipsis at least
76+
if ( parameters.isEmpty() && func->params() )
77+
signature += QChar( 0x2026 );
78+
79+
signature += paramNames.join( ", " );
80+
81+
signature += ')';
82+
}
83+
mFunctions << signature;
84+
}
85+
86+
updateApis();
87+
}
88+
89+
void QgsCodeEditorExpression::setFields( const QgsFields &fields )
90+
{
91+
mFieldNames.clear();
92+
93+
for ( const QgsField &field : fields )
94+
{
95+
mFieldNames << field.name();
96+
}
97+
98+
updateApis();
99+
}
100+
101+
102+
void QgsCodeEditorExpression::initializeLexer()
103+
{
104+
QFont font = getMonospaceFont();
105+
#ifdef Q_OS_MAC
106+
// The font size gotten from getMonospaceFont() is too small on Mac
107+
font.setPointSize( QLabel().font().pointSize() );
108+
#endif
109+
mSqlLexer = new QgsLexerExpression( this );
110+
mSqlLexer->setDefaultFont( font );
111+
mSqlLexer->setFont( font, -1 );
112+
font.setBold( true );
113+
mSqlLexer->setFont( font, QsciLexerSQL::Keyword );
114+
mSqlLexer->setColor( Qt::darkYellow, QsciLexerSQL::DoubleQuotedString ); // fields
115+
116+
setLexer( mSqlLexer );
117+
}
118+
119+
void QgsCodeEditorExpression::updateApis()
120+
{
121+
mApis = new QgsSciApisExpression( mSqlLexer );
122+
123+
for ( const QString &var : qgis::as_const( mVariables ) )
124+
{
125+
mApis->add( var );
126+
}
127+
128+
for ( const QString &function : qgis::as_const( mContextFunctions ) )
129+
{
130+
mApis->add( function );
131+
}
132+
133+
for ( const QString &function : qgis::as_const( mFunctions ) )
134+
{
135+
mApis->add( function );
136+
}
137+
138+
for ( const QString &fieldName : qgis::as_const( mFieldNames ) )
139+
{
140+
mApis->add( fieldName );
141+
}
142+
143+
mApis->prepare();
144+
mSqlLexer->setAPIs( mApis );
145+
}
146+
147+
///@cond PRIVATE
148+
QgsLexerExpression::QgsLexerExpression( QObject *parent )
149+
: QsciLexerSQL( parent )
150+
{
151+
}
152+
153+
const char *QgsLexerExpression::language() const
154+
{
155+
return "QGIS Expression";
156+
}
157+
158+
bool QgsLexerExpression::caseSensitive() const
159+
{
160+
return false;
161+
}
162+
163+
const char *QgsLexerExpression::wordCharacters() const
164+
{
165+
return "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_@";
166+
}
167+
168+
QgsSciApisExpression::QgsSciApisExpression( QsciLexer *lexer )
169+
: QsciAPIs( lexer )
170+
{
171+
172+
}
173+
174+
QStringList QgsSciApisExpression::callTips( const QStringList &context, int commas, QsciScintilla::CallTipsStyle style, QList<int> &shifts )
175+
{
176+
const QStringList originalTips = QsciAPIs::callTips( context, commas, style, shifts );
177+
QStringList lowercaseTips;
178+
for ( const QString &tip : originalTips )
179+
lowercaseTips << tip.toLower();
180+
181+
return lowercaseTips;
182+
}
183+
///@endcond

‎src/gui/qgscodeeditorexpression.h

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/***************************************************************************
2+
qgscodeeditorsql.h - A SQL editor based on QScintilla
3+
--------------------------------------
4+
Date : 06-Oct-2013
5+
Copyright : (C) 2013 by Salvatore Larosa
6+
Email : lrssvtml (at) gmail (dot) com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#ifndef QGSCODEEDITOREXPRESSION_H
17+
#define QGSCODEEDITOREXPRESSION_H
18+
19+
#include "qgis_sip.h"
20+
#include "qgis_gui.h"
21+
#include "qgscodeeditor.h"
22+
#include "qgsexpressioncontext.h"
23+
24+
#include <Qsci/qscilexersql.h>
25+
26+
SIP_IF_MODULE( HAVE_QSCI_SIP )
27+
28+
/**
29+
* \ingroup gui
30+
*
31+
* A QGIS expression editor based on QScintilla2. Adds syntax highlighting and
32+
* code autocompletion.
33+
*
34+
* \since QGIS 3.4
35+
*/
36+
class GUI_EXPORT QgsCodeEditorExpression : public QgsCodeEditor
37+
{
38+
Q_OBJECT
39+
40+
public:
41+
//! Constructor for QgsCodeEditorExpression
42+
QgsCodeEditorExpression( QWidget *parent SIP_TRANSFERTHIS = nullptr );
43+
44+
/**
45+
* Variables and functions from this expression context will be added to
46+
* the API.
47+
* Will also reload all globally registered functions.
48+
*/
49+
void setExpressionContext( const QgsExpressionContext &context );
50+
51+
/**
52+
* Field names will be added to the API.
53+
*/
54+
void setFields( const QgsFields &fields );
55+
56+
private:
57+
void initializeLexer();
58+
void updateApis();
59+
QsciAPIs *mApis = nullptr;
60+
QsciLexerSQL *mSqlLexer;
61+
62+
QStringList mVariables;
63+
QStringList mContextFunctions;
64+
QStringList mFunctions;
65+
QStringList mFieldNames;
66+
};
67+
68+
#ifndef SIP_RUN
69+
///@cond PRIVATE
70+
71+
/**
72+
* Internal use.
73+
74+
setAutoCompletionCaseSensitivity( false ) is not sufficient when installing
75+
a lexer, since its caseSensitive() method is actually used, and defaults
76+
to true.
77+
\note not available in Python bindings
78+
\ingroup gui
79+
*/
80+
class QgsLexerExpression : public QsciLexerSQL
81+
{
82+
Q_OBJECT
83+
84+
public:
85+
//! Constructor
86+
explicit QgsLexerExpression( QObject *parent = nullptr );
87+
88+
const char *language() const override;
89+
90+
bool caseSensitive() const override;
91+
92+
const char *wordCharacters() const override;
93+
};
94+
95+
class QgsSciApisExpression : public QsciAPIs
96+
{
97+
public:
98+
QgsSciApisExpression( QsciLexer *lexer );
99+
100+
QStringList callTips( const QStringList &context, int commas, QsciScintilla::CallTipsStyle style, QList<int> &shifts ) override;
101+
};
102+
///@endcond
103+
#endif
104+
105+
#endif

0 commit comments

Comments
 (0)
Please sign in to comment.