Merge pull request #506 from andrewcsmith/patch-2
[supercollider.git] / editors / sc-ide / core / sc_introspection.cpp
blob4f03ec82f2cb847dc195b62539b70f64c4adf82d
1 /*
2 SuperCollider Qt IDE
3 Copyright (c) 2012 Jakob Leben & Tim Blechmann
4 http://www.audiosynth.com
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 #include "sc_introspection.hpp"
23 #include "../common/SC_DirUtils.h"
24 #include "../widgets/main_window.hpp"
26 #include "yaml-cpp/node.h"
27 #include "yaml-cpp/parser.h"
29 #include <cassert>
31 #include <QDebug>
32 #include <QHash>
34 namespace ScIDE {
35 namespace ScLanguage {
37 Introspection::Introspection()
39 initPaths();
42 Introspection::Introspection( QString const & yamlString )
44 initPaths();
45 bool parsingSuccessful = parse(yamlString);
46 if (!parsingSuccessful)
47 throw std::runtime_error("Introspection parse error");
50 void Introspection::initPaths()
52 char userExtensionDir[PATH_MAX];
53 sc_GetUserExtensionDirectory(userExtensionDir, PATH_MAX);
54 mUserExtensionDir = QString(userExtensionDir) + QString("/");
56 char systemExtensionDir[PATH_MAX];
57 sc_GetSystemExtensionDirectory(systemExtensionDir, PATH_MAX);
58 mSystemExtensionDir = QString(systemExtensionDir) + QString("/");
61 Introspection::~Introspection()
63 clear();
66 bool Introspection::parse(const QString & yamlString )
68 using std::make_pair;
70 clear();
72 //qDebug("parsing introspection...");
74 std::stringstream stream;
75 stream << yamlString.toStdString();
76 YAML::Parser parser(stream);
78 YAML::Node doc;
79 if(!parser.GetNextDocument(doc)) {
80 MainWindow::instance()->showStatusMessage("no YAML document");
81 return false;
84 assert (doc.Type() == YAML::NodeType::Sequence);
85 for (YAML::Iterator it = doc.begin(); it != doc.end(); ++it)
87 assert(it->Type() == YAML::NodeType::Sequence);
88 QString name = (*it)[0].to<std::string>().c_str();
89 Class *klass = new Class;
90 klass->name = name;
91 mClassMap.insert(make_pair(klass->name, QSharedPointer<Class>(klass)));
94 for (YAML::Iterator docIterator = doc.begin(); docIterator != doc.end(); ++docIterator)
96 const YAML::Node & node = *docIterator;
97 QString name = node[0].to<std::string>().c_str();
98 ClassMap::iterator it = mClassMap.find(name);
99 assert(it != mClassMap.end());
100 Class *klass = it->second.data();
102 //qDebug() << klass->name;
104 ClassMap::iterator class_it;
106 QString metaClassName = node[1].to<std::string>().c_str();
107 class_it = mClassMap.find(metaClassName);
108 assert(class_it != mClassMap.end());
109 klass->metaClass = class_it->second.data();
111 if (node[2].Read(YAML::Null))
112 klass->superClass = 0;
113 else {
114 QString superClassName = node[2].to<std::string>().c_str();
115 class_it = mClassMap.find(superClassName);
116 assert(class_it != mClassMap.end());
117 klass->superClass = class_it->second.data();
120 klass->definition.path = node[3].to<std::string>().c_str();
121 klass->definition.position = node[4].to<int>();
123 const YAML::Node &methodSeq = node[5];
124 if (methodSeq.Type() != YAML::NodeType::Sequence)
125 continue;
127 //assert(methodSeq.Type() == YAML::NodeType::Sequence);
128 for (YAML::Iterator mit = methodSeq.begin(); mit != methodSeq.end(); ++mit)
130 const YAML::Node &methodNode = *mit;
131 assert(methodNode.Type() == YAML::NodeType::Sequence);
132 assert(methodNode.size() >= 2);
134 assert(methodNode[0].Type() == YAML::NodeType::Scalar);
135 assert(methodNode[1].Type() == YAML::NodeType::Scalar);
137 Method *method = new Method;
138 method->ownerClass = klass;
139 method->name = methodNode[1].to<std::string>().c_str();
140 method->definition.path = methodNode[2].to<std::string>().c_str();
141 method->definition.position = methodNode[3].to<int>();
143 //qDebug() << "--" << method->name;
145 const YAML::Node &argNode = methodNode[4];
146 assert(argNode.Type() == YAML::NodeType::Sequence);
147 YAML::Iterator arg = argNode.begin();
148 while (arg != argNode.end())
150 Argument argument;
152 // get arg name
153 assert(arg->Type() == YAML::NodeType::Scalar);
154 argument.name = arg->to<std::string>().c_str();
156 //qDebug() << "---# " << argument.name;
158 // get arg default value
159 ++arg;
160 if (arg == argNode.end())
161 break;
162 if(!arg->Read(YAML::Null)) {
163 assert(arg->Type() == YAML::NodeType::Scalar);
164 argument.defaultValue = arg->to<std::string>().c_str();
167 method->arguments.append(argument);
169 // next arg
170 ++arg;
173 klass->methods.append(method);
174 mMethodMap.insert(make_pair(method->name, QSharedPointer<Method>(method)));
178 inferClassLibraryPath();
180 //qDebug("done parsing introspection.");
181 return true;
184 QString Introspection::compactLibraryPath(QString const & path) const
186 if (path.startsWith(mClassLibraryPath))
187 return path.mid(mClassLibraryPath.length());
189 if (path.startsWith(mUserExtensionDir))
190 return QString("Extensions/") + path.mid(mUserExtensionDir.length());
192 if (path.startsWith(mSystemExtensionDir))
193 return QString("Extensions/") + path.mid(mSystemExtensionDir.length());
195 return path;
198 void Introspection::inferClassLibraryPath()
200 ClassMap::const_iterator object_class_it = mClassMap.find("Object");
201 assert(object_class_it != mClassMap.end());
202 Class *objectClass = object_class_it->second.data();
204 QString classLibPath = objectClass->definition.path;
205 int len = classLibPath.lastIndexOf("Common");
206 if (len != -1)
207 classLibPath.truncate(len);
208 else
209 classLibPath.clear();
211 mClassLibraryPath = classLibPath;
214 bool Introspection::ensureIntrospectionData() const
216 if (!introspectionAvailable()) {
217 MainWindow::instance()->showStatusMessage("Sclang Introspection not available, yet!");
218 return false;
219 } else
220 return true;
223 const Class * Introspection::findClass(const QString &className) const
225 if (!ensureIntrospectionData())
226 return NULL;
228 ClassMap::const_iterator klass_it = mClassMap.find(className);
229 if (klass_it == mClassMap.end()) {
230 MainWindow::instance()->showStatusMessage("Class not defined!");
231 return NULL;
233 return klass_it->second.data();
236 std::vector<const Class*> Introspection::findClassPartial(const QString & partialClassName) const
238 std::vector<const Class*> matchingClasses;
239 if (!ensureIntrospectionData())
240 return matchingClasses;
242 typedef ClassMap::const_iterator class_iterator;
244 for (class_iterator it = mClassMap.begin(); it != mClassMap.end(); ++it) {
245 QString const & key = it->first;
246 if (key.contains(partialClassName, Qt::CaseInsensitive)) {
247 if (!key.startsWith("Meta_"))
248 matchingClasses.push_back(it->second.data());
252 return matchingClasses;
256 std::vector<const Method*> Introspection::findMethodPartial(const QString & partialMethodName) const
258 std::vector<const Method*> matchingMethods;
259 if (!ensureIntrospectionData())
260 return matchingMethods;
262 typedef MethodMap::const_iterator class_iterator;
264 for (class_iterator it = mMethodMap.begin(); it != mMethodMap.end(); ++it) {
265 QString const & key = it->first;
266 if (key.contains(partialMethodName, Qt::CaseInsensitive))
267 matchingMethods.push_back(it->second.data());
270 return matchingMethods;
273 Introspection::ClassMethodMap Introspection::constructMethodMap(const Class * klass) const
275 ClassMethodMap methodMap;
276 if (!klass)
277 return methodMap;
279 foreach (Method *method, klass->metaClass->methods) {
280 QList<Method*> & list = methodMap[method->definition.path];
281 list.append(method);
284 foreach (Method *method, klass->methods) {
285 QList<Method*> & list = methodMap[method->definition.path];
286 list.append(method);
288 return methodMap;
291 QString Method::signature( SignatureStyle style ) const
293 QString sig = ownerClass->name.get();
294 if (sig.startsWith("Meta_")) {
295 sig.remove(0, 5);
296 sig.append(": *");
298 else
299 sig.append(": ");
301 sig.append(name.get());
303 if (style == SignatureWithoutArguments)
304 return sig;
306 int argc = arguments.count();
307 if (argc) {
308 sig.append(" (");
309 for( int i = 0; i < argc; ++i )
311 const Argument & arg = arguments[i];
312 if (i > 0)
313 sig.append(", ");
314 sig.append(arg.name);
315 if (style == SignatureWithArgumentsAndDefaultValues && !arg.defaultValue.get().isEmpty()) {
316 sig.append(" = ");
317 sig.append(arg.defaultValue);
320 sig.append(")");
322 else if (name.get().endsWith('_'))
323 sig.append(" (value)");
325 return sig;
328 } // namespace ScLanguage
329 } // namespace ScIDE