1 #include "function.h"
2 #include <QMetaMethod>
3 #include <QStringList>
4 
5 namespace NeovimQt {
6 
7 typedef QPair<QString,QString> StringPair;
8 
9 /**
10  * \class NeovimQt::Function
11  *
12  * \brief Representation of a Neovim API function signature
13  */
14 
15 /**
16  * Construct invalid function
17  */
Function()18 Function::Function()
19 :can_fail(false), m_valid(false)
20 {
21 }
22 
23 /**
24  * Construct new function with the given return type, name, parameters and error
25  */
Function(const QString & ret,const QString & name,QList<QPair<QString,QString>> params,bool can_fail)26 Function::Function(const QString& ret, const QString& name, QList<QPair<QString,QString> > params, bool can_fail)
27 :m_valid(true)
28 {
29 	this->return_type = ret;
30 	this->name = name;
31 	this->parameters = params;
32 	this->can_fail = can_fail;
33 }
34 
35 /**
36  * Construct new function with the given return type, name, parameters and error
37  */
Function(const QString & ret,const QString & name,QList<QString> paramTypes,bool can_fail)38 Function::Function(const QString& ret, const QString& name, QList<QString> paramTypes, bool can_fail)
39 :m_valid(true)
40 {
41 	this->return_type = ret;
42 	this->name = name;
43 	foreach (QString type, paramTypes) {
44 		this->parameters .append(QPair<QString,QString>(type, ""));
45 	}
46 	this->can_fail = can_fail;
47 }
48 
49 /**
50  * Returns true if this function has all the necessary attributes
51  */
isValid() const52 bool Function::isValid() const
53 {
54 	return m_valid;
55 }
56 
57 /**
58  * Two functions are considered identical if their names
59  * argument and return types, and error status are identical
60  */
operator ==(const Function & other)61 bool Function::operator==(const Function& other)
62 {
63 	if ( this->name != other.name ) {
64 		return false;
65 	}
66 
67 	if ( this->return_type != other.return_type ) {
68 		return false;
69 	}
70 	if (this->parameters.size() != other.parameters.size()) {
71 		return false;
72 	}
73 	for (int i=0; i<this->parameters.size(); i++) {
74 		if ( this->parameters.at(i).first != other.parameters.at(i).first ) {
75 			return false;
76 		}
77 	}
78 	return true;
79 }
80 
fromVariant(const QVariant & fun)81 Function Function::fromVariant(const QVariant& fun)
82 {
83 	Function f;
84 	if (!fun.canConvert<QVariantMap>()) {
85 		qDebug() << "Found unexpected data type when unpacking function" << fun;
86 		return f;
87 	}
88 
89 	const QVariantMap& m = fun.toMap();
90 	QMapIterator<QString,QVariant> it(m);
91 	while(it.hasNext()) {
92 		it.next();
93 
94 		if ( it.key() == "return_type" ) {
95 			if (!it.value().canConvert<QByteArray>()) {
96 				qDebug() << "Found unexpected data type when unpacking function" << fun;
97 				return f;
98 			}
99 			f.return_type = QString::fromUtf8(it.value().toByteArray());
100 		} else if ( it.key() == "name" ) {
101 			if (!it.value().canConvert<QByteArray>()) {
102 				qDebug() << "Found unexpected data type when unpacking function" << fun;
103 				return f;
104 			}
105 			f.name = QString::fromUtf8(it.value().toByteArray());
106 		} else if ( it.key() == "can_fail" ) {
107 			if (!it.value().canConvert<bool>()) {
108 				qDebug() << "Found unexpected data type when unpacking function" << fun;
109 				return f;
110 			}
111 			f.can_fail = it.value().toBool();
112 		} else if ( it.key() == "parameters" ) {
113 			if (!it.value().canConvert<QVariantList>()) {
114 				qDebug() << "Found unexpected data type when unpacking function" << fun;
115 				return f;
116 			}
117 			f.parameters = parseParameters(it.value().toList());
118 		} else if ( it.key() == "id" ) {
119 			// Deprecated
120 		} else if ( it.key() == "receives_channel_id" ) {
121 			// Internal
122 		} else if ( it.key() == "impl_name" ) {
123 			// Internal
124 		} else if ( it.key() == "method" ) {
125 			// Internal
126 		} else if ( it.key() == "noeval" ) {
127 			// API only function
128 		} else if ( it.key() == "deferred" || it.key() == "async" ) {
129 			// Internal, "deferred" renamed "async" in neovim/ccdeb91
130 		} else if ( it.key() == "deprecated_since" || it.key() == "since" ) {
131 			// Creation/Deprecation
132 		} else {
133 			qDebug() << "Unsupported function attribute"<< it.key() << it.value();
134 		}
135 	}
136 
137 	f.m_valid = true;
138 	return f;
139 }
140 
141 /**
142  * Retrieve parameter types from a list of function parameters in the metadata
143  * object. Basically retrieves the even numbered elements of the array (types)
144  * i.e. [Type0 name0 Type1 name1 ... ] -> [Type0 Type1 ...]
145  *
146  */
parseParameters(const QVariantList & obj)147 QList<QPair<QString,QString> > Function::parseParameters(const QVariantList& obj)
148 {
149 	QList<QPair<QString,QString> > fail;
150 	QList<QPair<QString,QString> > res;
151 	foreach(const QVariant& val, obj) {
152 
153 		const QVariantList& params = val.toList();
154 		if ( params.size() % 2 != 0 ) {
155 			return fail;
156 		}
157 
158 		for (int j=0; j<params.size(); j+=2) {
159 			QByteArray type, name;
160 			if (!params.at(j).canConvert<QByteArray>() ||
161 					!params.at(j+1).canConvert<QByteArray>()) {
162 				return fail;
163 			}
164 			QPair<QString,QString> arg(
165 					QString::fromUtf8(params.at(j).toByteArray()),
166 					QString::fromUtf8(params.at(j+1).toByteArray()));
167 			res.append(arg);
168 		}
169 	}
170 	return res;
171 }
172 
signature() const173 QString Function::signature() const
174 {
175 	QStringList sigparams;
176 	foreach(const StringPair p, parameters) {
177 		sigparams.append(QString("%1 %2").arg(p.first).arg(p.second));
178 	}
179 
180 	QString notes;
181 	if (can_fail) {
182 		notes += " !fail";
183 	}
184 	return  QString("%1 %2(%3)%4").arg(return_type).arg(name).arg(sigparams.join(", ")).arg(notes);
185 }
186 
187 } // Namespace
188 
189