89 #define COMMAND_QMLCLASS Doc::alias("qmlclass") |
89 #define COMMAND_QMLCLASS Doc::alias("qmlclass") |
90 #define COMMAND_QMLPROPERTY Doc::alias("qmlproperty") |
90 #define COMMAND_QMLPROPERTY Doc::alias("qmlproperty") |
91 #define COMMAND_QMLATTACHEDPROPERTY Doc::alias("qmlattachedproperty") |
91 #define COMMAND_QMLATTACHEDPROPERTY Doc::alias("qmlattachedproperty") |
92 #define COMMAND_QMLINHERITS Doc::alias("inherits") |
92 #define COMMAND_QMLINHERITS Doc::alias("inherits") |
93 #define COMMAND_QMLSIGNAL Doc::alias("qmlsignal") |
93 #define COMMAND_QMLSIGNAL Doc::alias("qmlsignal") |
|
94 #define COMMAND_QMLATTACHEDSIGNAL Doc::alias("qmlattachedsignal") |
94 #define COMMAND_QMLMETHOD Doc::alias("qmlmethod") |
95 #define COMMAND_QMLMETHOD Doc::alias("qmlmethod") |
|
96 #define COMMAND_QMLATTACHEDMETHOD Doc::alias("qmlattachedmethod") |
95 #define COMMAND_QMLDEFAULT Doc::alias("default") |
97 #define COMMAND_QMLDEFAULT Doc::alias("default") |
96 #endif |
98 #endif |
97 |
99 |
98 QStringList CppCodeParser::exampleFiles; |
100 QStringList CppCodeParser::exampleFiles; |
99 QStringList CppCodeParser::exampleDirs; |
101 QStringList CppCodeParser::exampleDirs; |
216 |
224 |
217 if (!exampleFilePatterns.isEmpty()) |
225 if (!exampleFilePatterns.isEmpty()) |
218 exampleNameFilter = exampleFilePatterns.join(" "); |
226 exampleNameFilter = exampleFilePatterns.join(" "); |
219 else |
227 else |
220 exampleNameFilter = "*.cpp *.h *.js *.xq *.svg *.xml *.ui"; |
228 exampleNameFilter = "*.cpp *.h *.js *.xq *.svg *.xml *.ui"; |
221 } |
229 |
222 |
230 QStringList exampleImagePatterns = config.getStringList( |
|
231 CONFIG_EXAMPLES + Config::dot + CONFIG_IMAGEEXTENSIONS); |
|
232 |
|
233 if (!exampleImagePatterns.isEmpty()) |
|
234 exampleImageFilter = exampleImagePatterns.join(" "); |
|
235 else |
|
236 exampleImageFilter = "*.png"; |
|
237 } |
|
238 |
|
239 /*! |
|
240 Clear the map of common node types and call |
|
241 the same function in the base class. |
|
242 */ |
223 void CppCodeParser::terminateParser() |
243 void CppCodeParser::terminateParser() |
224 { |
244 { |
225 nodeTypeMap.clear(); |
245 nodeTypeMap.clear(); |
226 CodeParser::terminateParser(); |
246 CodeParser::terminateParser(); |
227 } |
247 } |
228 |
248 |
|
249 /*! |
|
250 Returns "Cpp". |
|
251 */ |
229 QString CppCodeParser::language() |
252 QString CppCodeParser::language() |
230 { |
253 { |
231 return "Cpp"; |
254 return "Cpp"; |
232 } |
255 } |
233 |
256 |
|
257 /*! |
|
258 Returns a list of extensions for header files. |
|
259 */ |
234 QString CppCodeParser::headerFileNameFilter() |
260 QString CppCodeParser::headerFileNameFilter() |
235 { |
261 { |
236 return "*.ch *.h *.h++ *.hh *.hpp *.hxx"; |
262 return "*.ch *.h *.h++ *.hh *.hpp *.hxx"; |
237 } |
263 } |
238 |
264 |
|
265 /*! |
|
266 Returns a list of extensions for source files, i.e. not |
|
267 header files. |
|
268 */ |
239 QString CppCodeParser::sourceFileNameFilter() |
269 QString CppCodeParser::sourceFileNameFilter() |
240 { |
270 { |
241 return "*.c++ *.cc *.cpp *.cxx"; |
271 return "*.c++ *.cc *.cpp *.cxx"; |
242 } |
272 } |
243 |
273 |
341 mutableSequentialIteratorClasses.clear(); |
377 mutableSequentialIteratorClasses.clear(); |
342 associativeIteratorClasses.clear(); |
378 associativeIteratorClasses.clear(); |
343 mutableAssociativeIteratorClasses.clear(); |
379 mutableAssociativeIteratorClasses.clear(); |
344 } |
380 } |
345 |
381 |
|
382 /*! |
|
383 This is called after all the source files (i.e., not the |
|
384 header files) have been parsed. It traverses the tree to |
|
385 resolve property links, normalize overload signatures, and |
|
386 do other housekeeping of the tree. |
|
387 */ |
346 void CppCodeParser::doneParsingSourceFiles(Tree *tree) |
388 void CppCodeParser::doneParsingSourceFiles(Tree *tree) |
347 { |
389 { |
348 tree->root()->makeUndocumentedChildrenInternal(); |
390 tree->root()->makeUndocumentedChildrenInternal(); |
349 tree->root()->normalizeOverloads(); |
391 tree->root()->normalizeOverloads(); |
350 tree->fixInheritance(); |
392 tree->fixInheritance(); |
351 tree->resolveProperties(); |
393 tree->resolveProperties(); |
352 } |
394 } |
353 |
395 |
|
396 /*! |
|
397 This function searches the \a tree to find a FunctionNode |
|
398 for a function with the signature \a synopsis. If the |
|
399 \a relative node is provided, the search begins there. If |
|
400 \a fuzzy is true, base classes are searched. The function |
|
401 node is returned, if found. |
|
402 */ |
354 const FunctionNode *CppCodeParser::findFunctionNode(const QString& synopsis, |
403 const FunctionNode *CppCodeParser::findFunctionNode(const QString& synopsis, |
355 Tree *tree, |
404 Tree *tree, |
356 Node *relative, |
405 Node *relative, |
357 bool fuzzy) |
406 bool fuzzy) |
358 { |
407 { |
676 classNode = static_cast<const ClassNode*>(n); |
727 classNode = static_cast<const ClassNode*>(n); |
677 } |
728 } |
678 return new QmlClassNode(tre->root(), names[0], classNode); |
729 return new QmlClassNode(tre->root(), names[0], classNode); |
679 } |
730 } |
680 else if ((command == COMMAND_QMLSIGNAL) || |
731 else if ((command == COMMAND_QMLSIGNAL) || |
681 (command == COMMAND_QMLMETHOD)) { |
732 (command == COMMAND_QMLMETHOD) || |
|
733 (command == COMMAND_QMLATTACHEDSIGNAL) || |
|
734 (command == COMMAND_QMLATTACHEDMETHOD)) { |
682 QString element; |
735 QString element; |
683 QString name; |
736 QString type; |
684 QmlClassNode* qmlClass = 0; |
737 QmlClassNode* qmlClass = 0; |
685 if (splitQmlArg(doc,arg,element,name)) { |
738 if (splitQmlMethodArg(doc,arg,type,element)) { |
686 Node* n = tre->findNode(QStringList(element),Node::Fake); |
739 Node* n = tre->findNode(QStringList(element),Node::Fake); |
687 if (n && n->subType() == Node::QmlClass) { |
740 if (n && n->subType() == Node::QmlClass) { |
688 qmlClass = static_cast<QmlClassNode*>(n); |
741 qmlClass = static_cast<QmlClassNode*>(n); |
689 if (command == COMMAND_QMLSIGNAL) |
742 if (command == COMMAND_QMLSIGNAL) |
690 return new QmlSignalNode(qmlClass,name); |
743 return makeFunctionNode(doc,arg,qmlClass,Node::QmlSignal,false,COMMAND_QMLSIGNAL); |
|
744 else if (command == COMMAND_QMLATTACHEDSIGNAL) |
|
745 return makeFunctionNode(doc,arg,qmlClass,Node::QmlSignal,true,COMMAND_QMLATTACHEDSIGNAL); |
|
746 else if (command == COMMAND_QMLMETHOD) |
|
747 return makeFunctionNode(doc,arg,qmlClass,Node::QmlMethod,false,COMMAND_QMLMETHOD); |
|
748 else if (command == COMMAND_QMLATTACHEDMETHOD) |
|
749 return makeFunctionNode(doc,arg,qmlClass,Node::QmlMethod,true,COMMAND_QMLATTACHEDMETHOD); |
691 else |
750 else |
692 return new QmlMethodNode(qmlClass,name); |
751 return 0; // never get here. |
693 } |
752 } |
694 } |
753 } |
695 } |
754 } |
696 #endif |
755 #endif |
697 return 0; |
756 return 0; |
703 A QML property argument has the form... |
762 A QML property argument has the form... |
704 |
763 |
705 <type> <element>::<name> |
764 <type> <element>::<name> |
706 |
765 |
707 This function splits the argument into those three |
766 This function splits the argument into those three |
708 parts, sets \a type, \a element, and \a property, |
767 parts, sets \a type, \a element, and \a name, |
709 and returns true. If any of the parts isn't found, |
768 and returns true. If any of the parts isn't found, |
710 a debug message is output and false is returned. |
769 a qdoc warning is output and false is returned. |
711 */ |
770 */ |
712 bool CppCodeParser::splitQmlPropertyArg(const Doc& doc, |
771 bool CppCodeParser::splitQmlPropertyArg(const Doc& doc, |
713 const QString& arg, |
772 const QString& arg, |
714 QString& type, |
773 QString& type, |
715 QString& element, |
774 QString& element, |
716 QString& property) |
775 QString& name) |
717 { |
776 { |
718 QStringList blankSplit = arg.split(" "); |
777 QStringList blankSplit = arg.split(" "); |
719 if (blankSplit.size() > 1) { |
778 if (blankSplit.size() > 1) { |
720 type = blankSplit[0]; |
779 type = blankSplit[0]; |
721 QStringList colonSplit(blankSplit[1].split("::")); |
780 QStringList colonSplit(blankSplit[1].split("::")); |
722 if (colonSplit.size() > 1) { |
781 if (colonSplit.size() > 1) { |
723 element = colonSplit[0]; |
782 element = colonSplit[0]; |
724 property = colonSplit[1]; |
783 name = colonSplit[1]; |
725 return true; |
784 return true; |
726 } |
785 } |
727 else |
786 else |
728 doc.location().warning(tr("Missing QML element name or property name")); |
787 doc.location().warning(tr("Missing parent QML element name")); |
729 } |
788 } |
730 else |
789 else |
731 doc.location().warning(tr("Missing QML property type or property path")); |
790 doc.location().warning(tr("Missing property type")); |
732 return false; |
791 return false; |
733 } |
792 } |
734 |
793 |
735 /*! |
794 /*! |
736 A QML signal or method argument has the form... |
795 A QML signal or method argument has the form... |
737 |
796 |
738 <element>::<name> |
797 <type> <element>::<name>(<param>, <param>, ...) |
739 |
798 |
740 This function splits the argument into those two |
799 This function splits the argument into those two |
741 parts, sets \a element, and \a name, and returns |
800 parts, sets \a element, and \a name, and returns |
742 true. If either of the parts isn't found, a debug |
801 true. If either of the parts isn't found, a debug |
743 message is output and false is returned. |
802 message is output and false is returned. |
744 */ |
803 */ |
745 bool CppCodeParser::splitQmlArg(const Doc& doc, |
804 bool CppCodeParser::splitQmlMethodArg(const Doc& doc, |
746 const QString& arg, |
805 const QString& arg, |
747 QString& element, |
806 QString& type, |
748 QString& name) |
807 QString& element) |
749 { |
808 { |
750 QStringList colonSplit(arg.split("::")); |
809 QStringList colonSplit(arg.split("::")); |
751 if (colonSplit.size() > 1) { |
810 if (colonSplit.size() > 1) { |
752 element = colonSplit[0]; |
811 QStringList blankSplit = colonSplit[0].split(" "); |
753 name = colonSplit[1]; |
812 if (blankSplit.size() > 1) { |
|
813 type = blankSplit[0]; |
|
814 element = blankSplit[1]; |
|
815 } |
|
816 else { |
|
817 type = QString(""); |
|
818 element = colonSplit[0]; |
|
819 } |
754 return true; |
820 return true; |
755 } |
821 } |
756 else |
822 else |
757 doc.location().warning(tr("Missing QML element name or signal/method name")); |
823 doc.location().warning(tr("Missing parent QML element or method signature")); |
758 return false; |
824 return false; |
759 } |
825 } |
760 |
826 |
761 /*! |
827 /*! |
762 Process the topic \a command group with arguments \a args. |
828 Process the topic \a command group with arguments \a args. |
785 property, |
851 property, |
786 attached); |
852 attached); |
787 } |
853 } |
788 } |
854 } |
789 if (qmlPropGroup) { |
855 if (qmlPropGroup) { |
790 new QmlPropertyNode(qmlPropGroup,property,type,attached); |
856 const ClassNode *correspondingClass = static_cast<const QmlClassNode*>(qmlPropGroup->parent())->classNode(); |
|
857 PropertyNode *correspondingProperty = 0; |
|
858 if (correspondingClass) |
|
859 correspondingProperty = static_cast<PropertyNode*>((Node*)correspondingClass->findNode(property, Node::Property)); |
|
860 QmlPropertyNode *qmlPropNode = new QmlPropertyNode(qmlPropGroup,property,type,attached); |
|
861 if (correspondingProperty) { |
|
862 bool writableList = type.startsWith("list") && correspondingProperty->dataType().endsWith('*'); |
|
863 qmlPropNode->setWritable(writableList || correspondingProperty->isWritable()); |
|
864 } |
791 ++arg; |
865 ++arg; |
792 while (arg != args.end()) { |
866 while (arg != args.end()) { |
793 if (splitQmlPropertyArg(doc,(*arg),type,element,property)) { |
867 if (splitQmlPropertyArg(doc,(*arg),type,element,property)) { |
794 new QmlPropertyNode(qmlPropGroup, |
868 QmlPropertyNode* qmlPropNode = new QmlPropertyNode(qmlPropGroup, |
795 property, |
869 property, |
796 type, |
870 type, |
797 attached); |
871 attached); |
|
872 if (correspondingProperty) { |
|
873 bool writableList = type.startsWith("list") && correspondingProperty->dataType().endsWith('*'); |
|
874 qmlPropNode->setWritable(writableList || correspondingProperty->isWritable()); |
|
875 } |
798 } |
876 } |
799 ++arg; |
877 ++arg; |
800 } |
878 } |
801 } |
879 } |
802 } |
880 } |
1347 tok != Tok_Eoi) || |
1429 tok != Tok_Eoi) || |
1348 tok == Tok_RightBracket) { |
1430 tok == Tok_RightBracket) { |
1349 returnType.append(lexeme()); |
1431 returnType.append(lexeme()); |
1350 readToken(); |
1432 readToken(); |
1351 } |
1433 } |
1352 if (tok != Tok_Semicolon) |
1434 if (tok != Tok_Semicolon) { |
1353 return false; |
1435 return false; |
|
1436 } |
1354 } |
1437 } |
1355 else if (tok == Tok_Colon) { |
1438 else if (tok == Tok_Colon) { |
1356 returnType.appendHotspot(); |
1439 returnType.appendHotspot(); |
1357 |
1440 |
1358 while (tok != Tok_Semicolon && tok != Tok_Eoi) { |
1441 while (tok != Tok_Semicolon && tok != Tok_Eoi) { |
1359 returnType.append(lexeme()); |
1442 returnType.append(lexeme()); |
1360 readToken(); |
1443 readToken(); |
1361 } |
1444 } |
1362 if (tok != Tok_Semicolon) |
1445 if (tok != Tok_Semicolon) { |
1363 return false; |
1446 return false; |
|
1447 } |
1364 } |
1448 } |
1365 |
1449 |
1366 VariableNode *var = new VariableNode(parent, name); |
1450 VariableNode *var = new VariableNode(parent, name); |
1367 var->setAccess(access); |
1451 var->setAccess(access); |
1368 var->setLocation(location()); |
1452 var->setLocation(location()); |
1371 if (compat) |
1455 if (compat) |
1372 var->setStatus(Node::Compat); |
1456 var->setStatus(Node::Compat); |
1373 var->setStatic(sta); |
1457 var->setStatic(sta); |
1374 return false; |
1458 return false; |
1375 } |
1459 } |
1376 if (tok != Tok_LeftParen) |
1460 if (tok != Tok_LeftParen) { |
1377 return false; |
1461 return false; |
|
1462 } |
1378 } |
1463 } |
1379 readToken(); |
1464 readToken(); |
1380 |
1465 |
1381 FunctionNode *func = new FunctionNode(parent, name); |
1466 FunctionNode *func = new FunctionNode(type, parent, name, attached); |
1382 func->setAccess(access); |
1467 func->setAccess(access); |
1383 func->setLocation(location()); |
1468 func->setLocation(location()); |
1384 func->setReturnType(returnType.toString()); |
1469 func->setReturnType(returnType.toString()); |
1385 func->setParentPath(parentPath); |
1470 func->setParentPath(parentPath); |
1386 func->setTemplateStuff(templateStuff); |
1471 func->setTemplateStuff(templateStuff); |
1737 value = "?"; |
1825 value = "?"; |
1738 } |
1826 } |
1739 |
1827 |
1740 if (key == "READ") |
1828 if (key == "READ") |
1741 tre->addPropertyFunction(property, value, PropertyNode::Getter); |
1829 tre->addPropertyFunction(property, value, PropertyNode::Getter); |
1742 else if (key == "WRITE") |
1830 else if (key == "WRITE") { |
1743 tre->addPropertyFunction(property, value, PropertyNode::Setter); |
1831 tre->addPropertyFunction(property, value, PropertyNode::Setter); |
1744 else if (key == "STORED") |
1832 property->setWritable(true); |
|
1833 } else if (key == "STORED") |
1745 property->setStored(value.toLower() == "true"); |
1834 property->setStored(value.toLower() == "true"); |
1746 else if (key == "DESIGNABLE") |
1835 else if (key == "DESIGNABLE") |
1747 property->setDesignable(value.toLower() == "true"); |
1836 property->setDesignable(value.toLower() == "true"); |
1748 else if (key == "RESET") |
1837 else if (key == "RESET") |
1749 tre->addPropertyFunction(property, value, PropertyNode::Resetter); |
1838 tre->addPropertyFunction(property, value, PropertyNode::Resetter); |
1750 |
|
1751 else if (key == "NOTIFY") { |
1839 else if (key == "NOTIFY") { |
1752 tre->addPropertyFunction(property, value, PropertyNode::Notifier); |
1840 tre->addPropertyFunction(property, value, PropertyNode::Notifier); |
1753 } |
1841 } |
1754 |
1842 |
1755 } |
1843 } |
2078 Tokenizer stringTokenizer(loc, latin1); |
2168 Tokenizer stringTokenizer(loc, latin1); |
2079 stringTokenizer.setParsingFnOrMacro(true); |
2169 stringTokenizer.setParsingFnOrMacro(true); |
2080 tokenizer = &stringTokenizer; |
2170 tokenizer = &stringTokenizer; |
2081 readToken(); |
2171 readToken(); |
2082 |
2172 |
2083 bool ok = matchFunctionDecl(root, parentPathPtr, funcPtr); |
2173 bool ok = matchFunctionDecl(root, parentPathPtr, funcPtr, QString(), type, attached); |
2084 // potential memory leak with funcPtr |
2174 // potential memory leak with funcPtr |
2085 |
2175 |
2086 tokenizer = outerTokenizer; |
2176 tokenizer = outerTokenizer; |
2087 tok = outerTok; |
2177 tok = outerTok; |
2088 |
|
2089 return ok; |
2178 return ok; |
|
2179 } |
|
2180 |
|
2181 /*! |
|
2182 Create a new FunctionNode for a QML method or signal, as |
|
2183 specified by \a type, as a child of \a parent. \a sig is |
|
2184 the complete signature, and if \a attached is true, the |
|
2185 method or signal is "attached". \a qdoctag is the text of |
|
2186 the \a type. |
|
2187 */ |
|
2188 FunctionNode* CppCodeParser::makeFunctionNode(const Doc& doc, |
|
2189 const QString& sig, |
|
2190 InnerNode* parent, |
|
2191 Node::Type type, |
|
2192 bool attached, |
|
2193 QString qdoctag) |
|
2194 { |
|
2195 QStringList pp; |
|
2196 FunctionNode* fn = 0; |
|
2197 if (!makeFunctionNode(sig,&pp,&fn,parent,type,attached) && |
|
2198 !makeFunctionNode("void "+sig,&pp,&fn,parent,type,attached)) { |
|
2199 doc.location().warning(tr("Invalid syntax in '\\%1'").arg(qdoctag)); |
|
2200 } |
|
2201 if (fn) |
|
2202 return fn; |
|
2203 return 0; |
2090 } |
2204 } |
2091 |
2205 |
2092 void CppCodeParser::parseQiteratorDotH(const Location &location, |
2206 void CppCodeParser::parseQiteratorDotH(const Location &location, |
2093 const QString &filePath) |
2207 const QString &filePath) |
2094 { |
2208 { |
2143 QString fullPath = Config::findFile(fake->doc().location(), |
2257 QString fullPath = Config::findFile(fake->doc().location(), |
2144 exampleFiles, |
2258 exampleFiles, |
2145 exampleDirs, |
2259 exampleDirs, |
2146 proFileName, |
2260 proFileName, |
2147 userFriendlyFilePath); |
2261 userFriendlyFilePath); |
|
2262 |
2148 if (fullPath.isEmpty()) { |
2263 if (fullPath.isEmpty()) { |
2149 QString tmp = proFileName; |
2264 QString tmp = proFileName; |
2150 proFileName = examplePath + "/" + "qbuild.pro"; |
2265 proFileName = examplePath + "/" + "qbuild.pro"; |
2151 userFriendlyFilePath.clear(); |
2266 userFriendlyFilePath.clear(); |
2152 fullPath = Config::findFile(fake->doc().location(), |
2267 fullPath = Config::findFile(fake->doc().location(), |
2162 } |
2277 } |
2163 |
2278 |
2164 int sizeOfBoringPartOfName = fullPath.size() - proFileName.size(); |
2279 int sizeOfBoringPartOfName = fullPath.size() - proFileName.size(); |
2165 fullPath.truncate(fullPath.lastIndexOf('/')); |
2280 fullPath.truncate(fullPath.lastIndexOf('/')); |
2166 |
2281 |
2167 QStringList exampleFiles = Config::getFilesHere(fullPath, |
2282 QStringList exampleFiles = Config::getFilesHere(fullPath,exampleNameFilter); |
2168 exampleNameFilter); |
2283 QString imagesPath = fullPath + "/images"; |
|
2284 QStringList imageFiles = Config::getFilesHere(imagesPath,exampleImageFilter); |
|
2285 |
|
2286 #if 0 |
|
2287 qDebug() << "examplePath:" << examplePath; |
|
2288 qDebug() << " exampleFiles" << exampleFiles; |
|
2289 qDebug() << "imagesPath:" << imagesPath; |
|
2290 qDebug() << "fullPath:" << fullPath; |
|
2291 qDebug() << " imageFiles" << imageFiles; |
|
2292 #endif |
|
2293 |
2169 if (!exampleFiles.isEmpty()) { |
2294 if (!exampleFiles.isEmpty()) { |
2170 // move main.cpp and to the end, if it exists |
2295 // move main.cpp and to the end, if it exists |
2171 QString mainCpp; |
2296 QString mainCpp; |
2172 QMutableStringListIterator i(exampleFiles); |
2297 QMutableStringListIterator i(exampleFiles); |
2173 i.toBack(); |
2298 i.toBack(); |