[Gui] ExpressionCompleter: fix some GitHub Action reports (#8264)

* [Gui] ExpressionCompleter: fix some GitHub Action reports

- too long lines
- too short variable names
- use .empty()

- also some reformatting according to our current clang file
This commit is contained in:
Uwe
2023-01-27 00:37:54 +01:00
committed by GitHub
parent db6d44b1da
commit e07af014e6

View File

@@ -49,9 +49,10 @@ Q_DECLARE_METATYPE(App::ObjectIdentifier)
using namespace App;
using namespace Gui;
class ExpressionCompleterModel: public QAbstractItemModel {
class ExpressionCompleterModel: public QAbstractItemModel
{
public:
ExpressionCompleterModel(QObject *parent, bool noProperty)
ExpressionCompleterModel(QObject* parent, bool noProperty)
:QAbstractItemModel(parent), noProperty(noProperty)
{
}
@@ -60,21 +61,22 @@ public:
noProperty = enabled;
}
void setDocumentObject(const App::DocumentObject *obj, bool checkInList) {
void setDocumentObject(const App::DocumentObject* obj, bool checkInList)
{
beginResetModel();
if(obj) {
if (obj) {
currentDoc = obj->getDocument()->getName();
currentObj = obj->getNameInDocument();
if (!noProperty && checkInList) {
inList = obj->getInListEx(true);
}
} else {
}
else {
currentDoc.clear();
currentObj.clear();
inList.clear();
}
endResetModel();
}
// This ExpressionCompleter model works without any physical items.
@@ -153,7 +155,8 @@ public:
// |-- prop2.path2 (contextual) - (row 1, [ 6,-1,-1,0]) = encode as parent => INVALID, LEAF ITEM
//
struct Info {
struct Info
{
qint32 doc;
qint32 obj;
qint32 prop;
@@ -177,8 +180,6 @@ public:
static const quint64 k_maskContextualHierarchy = ((1ULL << k_numBitsContextualHierarchy) - 1);
static const quint64 k_maskDocuments = ((1ULL << k_numBitsDocuments) - 1);
union InfoPtrEncoding
{
quint64 d_enc;
@@ -186,26 +187,30 @@ public:
{
quint8 doc;
quint8 prop;
quint16 obj:15;
quint16 contextualHierarchy:1;
quint16 obj : 15;
quint16 contextualHierarchy : 1;
} d32;
void* ptr;
InfoPtrEncoding(const Info& info) : d_enc(0)
InfoPtrEncoding(const Info& info)
: d_enc(0)
{
if (sizeof(void*) < sizeof(InfoPtrEncoding)) {
d32.doc = (quint8)(info.doc+1);
d32.obj = (quint16)(info.obj+1);
d32.prop = (quint8)(info.prop+1);
if (sizeof(void*) < sizeof(InfoPtrEncoding)) {
d32.doc = (quint8)(info.doc + 1);
d32.obj = (quint16)(info.obj + 1);
d32.prop = (quint8)(info.prop + 1);
d32.contextualHierarchy = info.contextualHierarchy;
} else {
d_enc = ((quint64(info.doc+1) & k_maskDocuments ) << k_offsetDocuments)
| ((quint64(info.contextualHierarchy) & k_maskContextualHierarchy) << k_offsetContextualHierarchy)
| ((quint64(info.obj+1) & k_maskObj) << k_offsetObj)
| ((quint64(info.prop+1) & k_maskProp) << k_offsetProp);
}
else {
d_enc = ((quint64(info.doc + 1) & k_maskDocuments) << k_offsetDocuments)
| ((quint64(info.contextualHierarchy) & k_maskContextualHierarchy)
<< k_offsetContextualHierarchy)
| ((quint64(info.obj + 1) & k_maskObj) << k_offsetObj)
| ((quint64(info.prop + 1) & k_maskProp) << k_offsetProp);
}
}
InfoPtrEncoding(void* pointer) : d_enc(0)
InfoPtrEncoding(void* pointer)
: d_enc(0)
{
this->ptr = pointer;
}
@@ -213,17 +218,18 @@ public:
Info DecodeInfo()
{
Info info;
if (sizeof(void*) < sizeof(InfoPtrEncoding)) {
info.doc = qint32(d32.doc) - 1;
info.obj = qint32(d32.obj) -1;
info.prop = qint32(d32.prop) -1;
info.obj = qint32(d32.obj) - 1;
info.prop = qint32(d32.prop) - 1;
info.contextualHierarchy = d32.contextualHierarchy;
} else {
info.doc = ((d_enc >> k_offsetDocuments) & k_maskDocuments) - 1;
info.contextualHierarchy = ((d_enc >> k_offsetContextualHierarchy) & k_maskContextualHierarchy);
info.obj = ((d_enc >> k_offsetObj) & k_maskObj) - 1;
info.prop = ((d_enc >> k_offsetProp) & k_maskProp) - 1;
}
else {
info.doc = ((d_enc >> k_offsetDocuments) & k_maskDocuments) - 1;
info.contextualHierarchy =
((d_enc >> k_offsetContextualHierarchy) & k_maskContextualHierarchy);
info.obj = ((d_enc >> k_offsetObj) & k_maskObj) - 1;
info.prop = ((d_enc >> k_offsetProp) & k_maskProp) - 1;
}
return info;
}
@@ -235,34 +241,33 @@ public:
return ptrEnc.ptr;
}
static Info getInfo(const QModelIndex &index) {
static Info getInfo(const QModelIndex& index)
{
InfoPtrEncoding enc(index.internalPointer());
return enc.DecodeInfo();
}
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override {
if(role!=Qt::EditRole && role!=Qt::DisplayRole && role!=Qt::UserRole)
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override
{
if (role != Qt::EditRole && role != Qt::DisplayRole && role != Qt::UserRole)
return QVariant();
QVariant v;
QVariant variant;
Info info = getInfo(index);
_data(info,index.row(),&v,nullptr,role==Qt::UserRole);
FC_TRACE( info.doc << "," << info.obj << "," << info.prop << ","
<< info.contextualHierarchy << "," << index.row()
<< ": " << v.toString().toUtf8().constData());
return v;
_data(info, index.row(), &variant, nullptr, role == Qt::UserRole);
FC_TRACE(info.doc << "," << info.obj << "," << info.prop << "," << info.contextualHierarchy
<< "," << index.row() << ": " << variant.toString().toUtf8().constData());
return variant;
}
static std::vector<App::ObjectIdentifier> retrieveSubPaths(const App::Property* prop)
{
std::vector<App::ObjectIdentifier> result;
if (prop)
{
if (prop) {
prop->getPaths(result);
// need to filter out irrelevant paths (len 1, aka just this object identifier)
auto res = std::remove_if(
result.begin(), result.end(), [](const App::ObjectIdentifier& path) -> bool {
return path.getComponents().size() == 0;
return path.getComponents().empty();
});
result.erase(res, result.end());
}
@@ -284,24 +289,25 @@ public:
// this function is called in two modes:
// - obtain the count of a node identified by Info,row => count != nullptr, v==nullptr
// - get the text of an item. This text will contain separators but NO full path
void _data(const Info &info, int row, QVariant *v, int *count, bool sep=false) const {
void _data(const Info& info, int row, QVariant* v, int* count, bool sep = false) const
{
int idx;
// identify the document index. For any children of the root, it is given by traversing
// the flat list and identified by [row]
idx = info.doc<0?row:info.doc;
idx = info.doc < 0 ? row : info.doc;
const auto &docs = App::GetApplication().getDocuments();
int docSize = (int)docs.size()*2;
int docSize = (int)docs.size() * 2;
int objSize = 0;
int propSize = 0;
std::vector<std::pair<const char*, App::Property*> > props;
App::Document *doc = nullptr;
App::DocumentObject *obj = nullptr;
const char *propName = nullptr;
App::Document* doc = nullptr;
App::DocumentObject* obj = nullptr;
const char* propName = nullptr;
App::Property* prop = nullptr;
// check if the document is uniquely identified: either the correct index in info.doc
// OR if, the node is a descendant of the root, its row lands within 0...docsize
if(idx>=0 && idx<docSize) {
doc = docs[idx/2];
if (idx >= 0 && idx < docSize) {
doc = docs[idx / 2];
}
else {
// if we're looking at the ROOT, or the row identifies one of the other ROOT elements
@@ -310,30 +316,31 @@ public:
//
// We need to process the ROOT so we get the correct count for its children
doc = App::GetApplication().getDocument(currentDoc.c_str());
if(!doc) // no current, there are no additional objects
if (!doc)// no current, there are no additional objects
return;
// move to the current documents' objects' range
idx -= docSize;
if(info.doc<0)
if (info.doc < 0)
row = idx;
const auto &objs = doc->getObjects();
objSize = (int)objs.size()*2;
objSize = (int)objs.size() * 2;
// if this is a valid object, we found our object and break.
// if not, this may be the root or one of current object's properties
if(idx>=0 && idx<objSize) {
obj = objs[idx/2];
if (idx >= 0 && idx < objSize) {
obj = objs[idx / 2];
// if they are in the ignore list skip
if(inList.count(obj))
if (inList.count(obj))
return;
} else if (!noProperty) {
}
else if (!noProperty) {
// need to check the current object's props range, or we're parsing the ROOT
auto cobj = doc->getObject(currentObj.c_str());
if(cobj) {
if (cobj) {
// move to the props range of the current object
idx -= objSize;
if(info.doc<0)
if (info.doc < 0)
row = idx;
// get the properties
cobj->getPropertyNamedList(props);
@@ -341,9 +348,9 @@ public:
// if this is an invalid index, bail out
// if it's the ROOT break!
if(idx >= propSize)
if (idx >= propSize)
return;
if(idx>=0) {
if (idx >= 0) {
obj = cobj; // we only set the active object if we're not processing the root.
propName = props[idx].first;
prop = props[idx].second;
@@ -352,39 +359,39 @@ public:
}
}
// the item is the ROOT or a CHILD of the root
if(info.doc<0) {
if (info.doc < 0) {
// and we're asking for a count, compute it
if(count) {
if (count) {
// note that if we're dealing with a valid DOC node (row>0, ROOT_info)
// objSize and propSize will be zero because of the early exit above
*count = docSize + objSize + propSize;
}
if(idx>=0 && v) {
if (idx >= 0 && v) {
// we're asking for this child's data, and IT's NOT THE ROOT
QString res;
// we resolved the property
if (propName) {
res = QString::fromLatin1(propName);
// resolve the property
if (sep && !noProperty && retrieveSubPaths(prop).size() != 0)
if (sep && !noProperty && !retrieveSubPaths(prop).empty())
res += QLatin1Char('.');
}
else if(obj) {
else if (obj) {
// the object has been resolved, use the saved idx to figure out quotation or not.
if(idx & 1)
if (idx & 1)
res = QString::fromUtf8(quote(obj->Label.getStrValue()).c_str());
else
else
res = QString::fromLatin1(obj->getNameInDocument());
if(sep && !noProperty)
if (sep && !noProperty)
res += QLatin1Char('.');
}
else {
// the document has been resolved, use the saved idx to figure out quotation or not.
if(idx & 1)
if (idx & 1)
res = QString::fromUtf8(quote(doc->Label.getStrValue()).c_str());
else
res = QString::fromLatin1(doc->getName());
if(sep)
if (sep)
res += QLatin1Char('#');
}
v->setValue(res);
@@ -394,28 +401,28 @@ public:
}
// object not resolved
if(!obj) {
if (!obj) {
// are we pointing to an object item, or our father (info) is an object
idx = info.obj<0?row:info.obj;
idx = info.obj < 0 ? row : info.obj;
const auto &objs = doc->getObjects();
objSize = (int)objs.size()*2;
objSize = (int)objs.size() * 2;
// if invalid index, or in the ignore list bail out
if(idx<0 || idx>=objSize || inList.count(obj))
if (idx < 0 || idx >= objSize || inList.count(obj))
return;
obj = objs[idx/2];
obj = objs[idx / 2];
if(info.obj<0) {
if (info.obj < 0) {
// if this is AN actual Object item and not a root
if(count)
if (count)
*count = objSize; // set the correct count if requested
if(v) {
if (v) {
// resolve the name
QString res;
if(idx&1)
if (idx & 1)
res = QString::fromUtf8(quote(obj->Label.getStrValue()).c_str());
else
res = QString::fromLatin1(obj->getNameInDocument());
if(sep && !noProperty)
if (sep && !noProperty)
res += QLatin1Char('.');
v->setValue(res);
}
@@ -423,9 +430,9 @@ public:
}
}
if(noProperty)
if (noProperty)
return;
if(!propName) {
if (!propName) {
idx = info.prop < 0 ? row : info.prop;
obj->getPropertyNamedList(props);
propSize = (int)props.size();
@@ -445,9 +452,8 @@ public:
QString res = QString::fromLatin1(propName);
// check to see if we have accessible paths from this prop name?
if (sep && retrieveSubPaths(prop).size() != 0)
if (sep && !retrieveSubPaths(prop).empty())
res += QLatin1Char('.');
*v = res;
}
return;
@@ -460,8 +466,7 @@ public:
idx = row;
std::vector<App::ObjectIdentifier> paths = retrieveSubPaths(prop);
if (count)
{
if (count) {
*count = paths.size();
}
@@ -470,30 +475,27 @@ public:
return;
}
if (v)
{
if (v) {
auto str = paths[idx].getSubPathStr();
if (str.size() && (str[0] == '.' || str[0] == '#'))
{
if (str.size() && (str[0] == '.' || str[0] == '#')) {
// skip the "."
*v = QString::fromLatin1(str.c_str() + 1);
} else {
}
else {
*v = QString::fromLatin1(str.c_str());
}
}
}
return;
}
QModelIndex parent(const QModelIndex & index) const override {
if(!index.isValid())
if (!index.isValid())
return QModelIndex();
Info parentInfo = getInfo(index);
Info grandParentInfo = parentInfo;
if (parentInfo.contextualHierarchy) {
// for contextual hierarchy we have this:
// ROOT -> Object in Current Doc -> Prop In Object -> PropPath
@@ -505,7 +507,8 @@ public:
}
// if the parent is the object or a prop attached to the root, we just need the below line
return createIndex(parentInfo.doc, 0, infoId(Info::root));
} else {
}
else {
if (parentInfo.prop >= 0) {
grandParentInfo.prop = -1;
return createIndex(parentInfo.prop, 0, infoId(grandParentInfo));
@@ -531,7 +534,6 @@ public:
info = Info::root;
if (element.isValid()) {
parentInfo = getInfo(element);
info = parentInfo;
@@ -539,7 +541,7 @@ public:
if (parentInfo.doc < 0) {
// need special casing to properly identify this model's object
const auto& docs = App::GetApplication().getDocuments();
auto docsSize = static_cast<int>(docs.size()*2);
auto docsSize = static_cast<int>(docs.size() * 2);
info.doc = element.row();
@@ -548,30 +550,31 @@ public:
if (element.row() >= docsSize) {
info.contextualHierarchy = 1;
}
} else if (parentInfo.contextualHierarchy) {
}
else if (parentInfo.contextualHierarchy) {
const auto& docs = App::GetApplication().getDocuments();
auto cdoc = App::GetApplication().getDocument(currentDoc.c_str());
if (cdoc) {
int objsSize = static_cast<int>(cdoc->getObjects().size()*2);
int objsSize = static_cast<int>(cdoc->getObjects().size() * 2);
int idx = parentInfo.doc - static_cast<int>(docs.size());
if (idx < objsSize) {
// |-- Parent (OBJECT) - (row 4, [-1,-1,-1,0]) = encode as element => [parent.row,-1,-1,1]
// |- element (PROP) - (row 0, [parent.row,-1,-1,1]) = encode as element => [parent.row,-1,parent.row,1]
// |-- Parent (OBJECT) - (row 4, [-1,-1,-1,0]) = encode as element => [parent.row,-1,-1,1]
// |- element (PROP) - (row 0, [parent.row,-1,-1,1]) = encode as element => [parent.row,-1,parent.row,1]
info.doc = parentInfo.doc;
info.obj =
-1;// object information is determined by the DOC index actually
info.obj = -1;// object information is determined by the DOC index actually
info.prop = element.row();
info.contextualHierarchy = 1;
} else {
}
else {
// if my parent (parentInfo) is a prop, it means that our element is a prop path
// and that is a leaf item (we don't split prop paths further)
// we can't encode leaf items into an "Info"
return false;
}
} else {
}
else {
// no contextual document
return false;
}
@@ -580,17 +583,20 @@ public:
// regular hierarchy
else if (parentInfo.obj <= 0) {
info.obj = element.row();
} else if (parentInfo.prop <= 0) {
}
else if (parentInfo.prop <= 0) {
info.prop = element.row();
} else {
}
else {
return false;
}
}
return true;
}
QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const override {
if(row<0)
QModelIndex index(int row, int column,
const QModelIndex & parent = QModelIndex()) const override {
if (row < 0)
return QModelIndex();
Info myParentInfoEncoded = Info::root;
@@ -599,20 +605,21 @@ public:
if (!parentCanHaveChildren) {
return QModelIndex();
}
return createIndex(row, column, infoId(myParentInfoEncoded));
}
// function returns how many children the QModelIndex parent has
int rowCount(const QModelIndex & parent = QModelIndex()) const override {
int rowCount(const QModelIndex& parent = QModelIndex()) const override
{
Info info;
int row = 0;
if(!parent.isValid()) {
if (!parent.isValid()) {
// we're getting the row count for the root
// that is: document hierarchy _and_ contextual completion
info = Info::root;
row = -1;
}else{
}
else {
// try to encode the parent's QModelIndex into an info structure
// if the paren't can't have any children, return 0
if (!modelIndexToParentInfo(parent, info)) {
@@ -620,13 +627,14 @@ public:
}
}
int count = 0;
_data(info,row,nullptr,&count);
FC_TRACE( info.doc << "," << info.obj << "," << info.prop << ","
<< info.contextualHierarchy << "," << row << " row count " << count);
_data(info, row, nullptr, &count);
FC_TRACE(info.doc << "," << info.obj << "," << info.prop << "," << info.contextualHierarchy
<< "," << row << " row count " << count);
return count;
}
int columnCount(const QModelIndex &) const override {
int columnCount(const QModelIndex&) const override
{
return 1;
}
@@ -646,8 +654,8 @@ const ExpressionCompleterModel::Info ExpressionCompleterModel::Info::root = {-1,
* @param parent Parent object owning the completer.
*/
ExpressionCompleter::ExpressionCompleter(const App::DocumentObject * currentDocObj,
QObject *parent, bool noProperty, bool checkInList)
ExpressionCompleter::ExpressionCompleter(const App::DocumentObject* currentDocObj,
QObject* parent, bool noProperty, bool checkInList)
: QCompleter(parent), currentObj(currentDocObj)
, noProperty(noProperty), checkInList(checkInList)
{
@@ -655,7 +663,7 @@ ExpressionCompleter::ExpressionCompleter(const App::DocumentObject * currentDocO
}
void ExpressionCompleter::init() {
if(model())
if (model())
return;
auto m = new ExpressionCompleterModel(this,noProperty);
@@ -663,29 +671,30 @@ void ExpressionCompleter::init() {
setModel(m);
}
void ExpressionCompleter::setDocumentObject(const App::DocumentObject *obj, bool _checkInList) {
if(!obj || !obj->getNameInDocument())
void ExpressionCompleter::setDocumentObject(const App::DocumentObject* obj, bool _checkInList)
{
if (!obj || !obj->getNameInDocument())
currentObj = App::DocumentObjectT();
else
currentObj = obj;
setCompletionPrefix(QString());
checkInList = _checkInList;
auto m = model();
if(m)
if (m)
static_cast<ExpressionCompleterModel*>(m)->setDocumentObject(obj, checkInList);
}
void ExpressionCompleter::setNoProperty(bool enabled) {
noProperty = enabled;
auto m = model();
if(m)
if (m)
static_cast<ExpressionCompleterModel*>(m)->setNoProperty(enabled);
}
QString ExpressionCompleter::pathFromIndex ( const QModelIndex & index ) const
QString ExpressionCompleter::pathFromIndex(const QModelIndex& index) const
{
auto m = model();
if(!m || !index.isValid())
if (!m || !index.isValid())
return QString();
QString res;
@@ -693,7 +702,7 @@ QString ExpressionCompleter::pathFromIndex ( const QModelIndex & index ) const
do {
res = m->data(parent, Qt::UserRole).toString() + res;
parent = parent.parent();
}while(parent.isValid());
} while (parent.isValid());
auto info = ExpressionCompleterModel::getInfo(index);
FC_TRACE("join path " << info.doc << "," << info.obj << "," << info.prop << ","
@@ -702,35 +711,35 @@ QString ExpressionCompleter::pathFromIndex ( const QModelIndex & index ) const
return res;
}
QStringList ExpressionCompleter::splitPath ( const QString & input ) const
QStringList ExpressionCompleter::splitPath(const QString& input) const
{
QStringList l;
QStringList resultList;
std::string path = input.toUtf8().constData();
if(path.empty())
return l;
if (path.empty())
return resultList;
int retry = 0;
std::string lastElem; // used to recover in case of parse failure after ".".
std::string trim; // used to delete ._self added for another recovery path
while(1) {
while (1) {
try {
// this will not work for incomplete Tokens at the end
// "Sketch." will fail to parse and complete.
App::ObjectIdentifier p = ObjectIdentifier::parse(
App::ObjectIdentifier ident = ObjectIdentifier::parse(
currentObj.getObject(), path);
std::vector<std::string> sl = p.getStringList();
auto sli = sl.begin();
if(retry>1 && !sl.empty())
sl.pop_back();
std::vector<std::string> stringList = ident.getStringList();
auto stringListIter = stringList.begin();
if (retry > 1 && !stringList.empty())
stringList.pop_back();
if (!sl.empty()) {
if (!trim.empty() && boost::ends_with(sl.back(), trim))
sl.back().resize(sl.back().size() - trim.size());
while (sli != sl.end()) {
l << Base::Tools::fromStdString(*sli);
++sli;
if (!stringList.empty()) {
if (!trim.empty() && boost::ends_with(stringList.back(), trim))
stringList.back().resize(stringList.back().size() - trim.size());
while (stringListIter != stringList.end()) {
resultList << Base::Tools::fromStdString(*stringListIter);
++stringListIter;
}
}
if (lastElem.size()) {
@@ -738,50 +747,49 @@ QStringList ExpressionCompleter::splitPath ( const QString & input ) const
if (!lastElem.empty()) {
// erase the separator
lastElem.erase(lastElem.begin());
l << Base::Tools::fromStdString(lastElem);
resultList << Base::Tools::fromStdString(lastElem);
} else {
// add empty string to allow completion after "." or "#"
l << QString();
resultList << QString();
}
}
FC_TRACE("split path " << path
<< " -> " << l.join(QLatin1String("/")).toUtf8().constData());
return l;
FC_TRACE("split path " << path << " -> "
<< resultList.join(QLatin1String("/")).toUtf8().constData());
return resultList;
}
catch (const Base::Exception &e) {
FC_TRACE("split path " << path << " error: " << e.what());
catch (const Base::Exception& except) {
FC_TRACE("split path " << path << " error: " << except.what());
if (!retry) {
size_t lastElemStart = path.rfind('.');
if (lastElemStart == std::string::npos)
{
if (lastElemStart == std::string::npos) {
lastElemStart = path.rfind('#');
}
if (lastElemStart != std::string::npos ) {
if (lastElemStart != std::string::npos) {
lastElem = path.substr(lastElemStart);
path = path.substr(0, lastElemStart);
}
retry++;
continue;
}else if(retry==1) {
}
else if (retry == 1) {
// restore path from retry 0
if (lastElem.size() > 1)
{
if (lastElem.size() > 1) {
path = path + lastElem;
lastElem = "";
}
// else... we don't reset lastElem if it's a '.' or '#' to allow chaining completions
char last = path[path.size()-1];
if(last!='#' && last!='.' && path.find('#')!=std::string::npos) {
char last = path[path.size() - 1];
if (last != '#' && last != '.' && path.find('#') != std::string::npos) {
path += "._self";
++retry;
continue;
}
}else if(retry==2) {
path.resize(path.size()-6);
char last = path[path.size()-1];
if(last!='.' && last!='<' && path.find("#<<")!=std::string::npos) {
}
else if (retry == 2) {
path.resize(path.size() - 6);
char last = path[path.size() - 1];
if (last != '.' && last != '<' && path.find("#<<") != std::string::npos) {
path += ">>._self";
++retry;
trim = ">>";
@@ -805,13 +813,17 @@ void ExpressionCompleter::slotUpdate(const QString & prefix, int pos)
// ExpressionParser::tokenize() only supports std::string but we need a tuple QString
// because due to UTF-8 encoding a std::string may be longer than a QString
// See https://forum.freecadweb.org/viewtopic.php?f=3&t=69931
auto tokenizeExpression = [](const QString & expr) {
std::vector<std::tuple<int, int, std::string> > result = ExpressionParser::tokenize(expr.toStdString());
std::vector<std::tuple<int, int, QString> > tokens;
std::transform(result.cbegin(), result.cend(), std::back_inserter(tokens), [](const std::tuple<int, int, std::string>& item) {
return std::make_tuple(get<0>(item), get<1>(item), QString::fromStdString(get<2>(item)));
});
auto tokenizeExpression = [](const QString& expr) {
std::vector<std::tuple<int, int, std::string>> result =
ExpressionParser::tokenize(expr.toStdString());
std::vector<std::tuple<int, int, QString>> tokens;
std::transform(result.cbegin(),
result.cend(),
std::back_inserter(tokens),
[](const std::tuple<int, int, std::string>& item) {
return std::make_tuple(
get<0>(item), get<1>(item), QString::fromStdString(get<2>(item)));
});
return tokens;
};
@@ -825,8 +837,8 @@ void ExpressionCompleter::slotUpdate(const QString & prefix, int pos)
// No tokens
if (tokens.empty()) {
if (auto p = popup())
p->setVisible(false);
if (auto itemView = popup())
itemView->setVisible(false);
return;
}
@@ -834,7 +846,7 @@ void ExpressionCompleter::slotUpdate(const QString & prefix, int pos)
// Pop those trailing tokens depending on the given position, which may be
// in the middle of a token, and we shall include that token.
for(auto it = tokens.begin(); it != tokens.end(); ++it) {
for (auto it = tokens.begin(); it != tokens.end(); ++it) {
if (get<1>(*it) >= pos) {
// Include the immediately followed '.' or '#', because we'll be
// inserting these separators too, in ExpressionCompleteModel::pathFromIndex()
@@ -855,14 +867,15 @@ void ExpressionCompleter::slotUpdate(const QString & prefix, int pos)
// First, check if we have unclosing string starting from the end
bool stringing = false;
for(; i >= 0; --i) {
for (; i >= 0; --i) {
int token = get<0>(tokens[i]);
if (token == ExpressionParser::STRING) {
stringing = false;
break;
}
if (token == ExpressionParser::LT && i > 0 && get<0>(tokens[i-1])==ExpressionParser::LT) {
if (token == ExpressionParser::LT && i > 0
&& get<0>(tokens[i - 1]) == ExpressionParser::LT) {
--i;
stringing = true;
break;
@@ -871,14 +884,14 @@ void ExpressionCompleter::slotUpdate(const QString & prefix, int pos)
// Not an unclosed string and the last character is a space
if (!stringing && !prefix.isEmpty() && prefix[prefixEnd-1] == QChar(32)) {
if (auto p = popup())
p->setVisible(false);
if (auto itemView = popup())
itemView->setVisible(false);
return;
}
if (!stringing) {
i = static_cast<long>(tokens.size()) - 1;
for (;i>=0;--i) {
for (; i >= 0; --i) {
int token = get<0>(tokens[i]);
if (token != '.' &&
token != '#' &&
@@ -887,7 +900,6 @@ void ExpressionCompleter::slotUpdate(const QString & prefix, int pos)
token != ExpressionParser::UNIT)
break;
}
++i;
}
@@ -917,12 +929,13 @@ void ExpressionCompleter::slotUpdate(const QString & prefix, int pos)
}
else {
if (auto p = popup())
p->setVisible(false);
if (auto itemView = popup())
itemView->setVisible(false);
}
}
ExpressionLineEdit::ExpressionLineEdit(QWidget *parent, bool noProperty, char checkPrefix, bool checkInList)
ExpressionLineEdit::ExpressionLineEdit(QWidget* parent, bool noProperty,
char checkPrefix, bool checkInList)
: QLineEdit(parent)
, completer(nullptr)
, block(true)
@@ -938,7 +951,8 @@ void ExpressionLineEdit::setPrefix(char prefix) {
checkPrefix = prefix;
}
void ExpressionLineEdit::setDocumentObject(const App::DocumentObject * currentDocObj, bool _checkInList)
void ExpressionLineEdit::setDocumentObject(const App::DocumentObject* currentDocObj,
bool _checkInList)
{
checkInList = _checkInList;
if (completer) {
@@ -951,15 +965,19 @@ void ExpressionLineEdit::setDocumentObject(const App::DocumentObject * currentDo
completer->setCaseSensitivity(Qt::CaseInsensitive);
if (!exactMatch)
completer->setFilterMode(Qt::MatchContains);
connect(completer, qOverload<const QString&>(&QCompleter::activated), this, &ExpressionLineEdit::slotCompleteTextSelected);
connect(completer, qOverload<const QString&>(&QCompleter::highlighted), this, &ExpressionLineEdit::slotCompleteTextHighlighted);
connect(this, &ExpressionLineEdit::textChanged2, completer, &ExpressionCompleter::slotUpdate);
connect(completer, qOverload<const QString&>(&QCompleter::activated),
this, &ExpressionLineEdit::slotCompleteTextSelected);
connect(completer,
qOverload<const QString&>(&QCompleter::highlighted),
this, &ExpressionLineEdit::slotCompleteTextHighlighted);
connect(this, &ExpressionLineEdit::textChanged2,
completer, &ExpressionCompleter::slotUpdate);
}
}
void ExpressionLineEdit::setNoProperty(bool enabled) {
noProperty = enabled;
if(completer)
if (completer)
completer->setNoProperty(enabled);
}
@@ -984,7 +1002,7 @@ void ExpressionLineEdit::hideCompleter()
void ExpressionLineEdit::slotTextChanged(const QString & text)
{
if (!block) {
if(!text.size() || (checkPrefix && text[0]!=QLatin1Char(checkPrefix)))
if (!text.size() || (checkPrefix && text[0] != QLatin1Char(checkPrefix)))
return;
Q_EMIT textChanged2(text,cursorPosition());
}
@@ -1007,12 +1025,10 @@ void ExpressionLineEdit::slotCompleteText(const QString & completionPrefix, bool
// chain completions if we select an entry from the completer drop down
// and that entry ends with '.' or '#'
if(isActivated)
{
if (isActivated) {
std::string textToComplete = completionPrefix.toUtf8().constData();
if (textToComplete.size() &&
(*textToComplete.crbegin() == '.' || *textToComplete.crbegin() == '#'))
{
if (textToComplete.size()
&& (*textToComplete.crbegin() == '.' || *textToComplete.crbegin() == '#')) {
Base::FlagToggler<bool> flag(block, true);
slotTextChanged(before + after);
}
@@ -1030,14 +1046,15 @@ void ExpressionLineEdit::slotCompleteTextSelected(const QString& completionPrefi
}
void ExpressionLineEdit::keyPressEvent(QKeyEvent *e) {
void ExpressionLineEdit::keyPressEvent(QKeyEvent* e)
{
Base::FlagToggler<bool> flag(block,true);
QLineEdit::keyPressEvent(e);
}
void ExpressionLineEdit::contextMenuEvent(QContextMenuEvent *event)
void ExpressionLineEdit::contextMenuEvent(QContextMenuEvent* event)
{
QMenu *menu = createStandardContextMenu();
QMenu* menu = createStandardContextMenu();
menu->addSeparator();
QAction* match = menu->addAction(tr("Exact match"));
@@ -1062,7 +1079,7 @@ void ExpressionLineEdit::contextMenuEvent(QContextMenuEvent *event)
///////////////////////////////////////////////////////////////////////
ExpressionTextEdit::ExpressionTextEdit(QWidget *parent)
ExpressionTextEdit::ExpressionTextEdit(QWidget* parent)
: QPlainTextEdit(parent)
, completer(nullptr)
, block(true)
@@ -1071,13 +1088,14 @@ ExpressionTextEdit::ExpressionTextEdit(QWidget *parent)
connect(this, &QPlainTextEdit::textChanged, this, &ExpressionTextEdit::slotTextChanged);
}
void ExpressionTextEdit::setExactMatch(bool enabled) {
void ExpressionTextEdit::setExactMatch(bool enabled)
{
exactMatch = enabled;
if (completer)
completer->setFilterMode(exactMatch ? Qt::MatchStartsWith : Qt::MatchContains);
}
void ExpressionTextEdit::setDocumentObject(const App::DocumentObject * currentDocObj)
void ExpressionTextEdit::setDocumentObject(const App::DocumentObject* currentDocObj)
{
if (completer) {
completer->setDocumentObject(currentDocObj);
@@ -1090,9 +1108,12 @@ void ExpressionTextEdit::setDocumentObject(const App::DocumentObject * currentDo
completer->setFilterMode(Qt::MatchContains);
completer->setWidget(this);
completer->setCaseSensitivity(Qt::CaseInsensitive);
connect(completer, qOverload<const QString&>(&QCompleter::activated), this, &ExpressionTextEdit::slotCompleteText);
connect(completer, qOverload<const QString&>(&QCompleter::highlighted), this, &ExpressionTextEdit::slotCompleteText);
connect(this, &ExpressionTextEdit::textChanged2, completer, &ExpressionCompleter::slotUpdate);
connect(completer, qOverload<const QString&>(&QCompleter::activated), this,
&ExpressionTextEdit::slotCompleteText);
connect(completer, qOverload<const QString&>(&QCompleter::highlighted), this,
&ExpressionTextEdit::slotCompleteText);
connect(this, &ExpressionTextEdit::textChanged2, completer,
&ExpressionCompleter::slotUpdate);
}
}
@@ -1115,28 +1136,30 @@ void ExpressionTextEdit::slotTextChanged()
}
}
void ExpressionTextEdit::slotCompleteText(const QString & completionPrefix)
void ExpressionTextEdit::slotCompleteText(const QString& completionPrefix)
{
QTextCursor cursor = textCursor();
int start,end;
completer->getPrefixRange(start,end);
int pos = cursor.positionInBlock();
if(pos<end)
cursor.movePosition(QTextCursor::NextCharacter,QTextCursor::MoveAnchor,end-pos);
if (pos < end) {
cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, end - pos);
}
cursor.movePosition(QTextCursor::PreviousCharacter,QTextCursor::KeepAnchor,end-start);
Base::FlagToggler<bool> flag(block,false);
cursor.insertText(completionPrefix);
completer->updatePrefixEnd(cursor.positionInBlock());
}
void ExpressionTextEdit::keyPressEvent(QKeyEvent *e) {
void ExpressionTextEdit::keyPressEvent(QKeyEvent* e)
{
Base::FlagToggler<bool> flag(block,true);
QPlainTextEdit::keyPressEvent(e);
}
void ExpressionTextEdit::contextMenuEvent(QContextMenuEvent *event)
void ExpressionTextEdit::contextMenuEvent(QContextMenuEvent* event)
{
QMenu *menu = createStandardContextMenu();
QMenu* menu = createStandardContextMenu();
menu->addSeparator();
QAction* match = menu->addAction(tr("Exact match"));