Browse Source

Added generated attribute documentation. Move AngelScript API dumping to a separate .cpp file. Hack fix for Doxygen parsing bug which would cause the Events page not to be recognized. Closes #303.

Lasse Öörni 11 years ago
parent
commit
9a7a9110a2

File diff suppressed because it is too large
+ 1836 - 1128
Docs/ScriptAPI.dox


+ 1 - 0
Docs/Urho3D.dox

@@ -46,6 +46,7 @@ For further reference, see:
 \ref ScriptAPI "Scripting API (generated)" <br>
 \ref ScriptAPI "Scripting API (generated)" <br>
 \ref LuaScriptAPI "Lua Scripting API (generated)" <br>
 \ref LuaScriptAPI "Lua Scripting API (generated)" <br>
 \ref EventList "Event list (generated)" <br>
 \ref EventList "Event list (generated)" <br>
+\ref AttributeList "Attribute list (generated)" <br>
 
 
 For Urho3D related links and projects, see \ref ExternalLinks "External Links".
 For Urho3D related links and projects, see \ref ExternalLinks "External Links".
 
 

+ 3 - 0
Source/Engine/Core/Context.h

@@ -111,6 +111,9 @@ public:
         return i != networkAttributes_.End() ? &i->second_ : 0;
         return i != networkAttributes_.End() ? &i->second_ : 0;
     }
     }
 
 
+    /// Return all registered attributes.
+    const HashMap<ShortStringHash, Vector<AttributeInfo> >& GetAllAttributes() const { return attributes_; }
+
     /// Return event receivers for a sender and event type, or null if they do not exist.
     /// Return event receivers for a sender and event type, or null if they do not exist.
     HashSet<Object*>* GetEventReceivers(Object* sender, StringHash eventType)
     HashSet<Object*>* GetEventReceivers(Object* sender, StringHash eventType)
     {
     {

+ 4 - 4
Source/Engine/LuaScript/LuaScriptInstance.cpp

@@ -85,7 +85,7 @@ void LuaScriptInstance::RegisterObject(Context* context)
     context->RegisterFactory<LuaScriptInstance>(LOGIC_CATEGORY);
     context->RegisterFactory<LuaScriptInstance>(LOGIC_CATEGORY);
 
 
     ACCESSOR_ATTRIBUTE(LuaScriptInstance, VAR_BOOL, "Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(LuaScriptInstance, VAR_BOOL, "Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
-	ACCESSOR_ATTRIBUTE(LuaScriptInstance, VAR_RESOURCEREF, "Script File", GetScriptFileAttr, SetScriptFileAttr, ResourceRef, ResourceRef(LuaFile::GetTypeStatic()), AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(LuaScriptInstance, VAR_RESOURCEREF, "Script File", GetScriptFileAttr, SetScriptFileAttr, ResourceRef, ResourceRef(LuaFile::GetTypeStatic()), AM_DEFAULT);
     REF_ACCESSOR_ATTRIBUTE(LuaScriptInstance, VAR_STRING, "Script Object Type", GetScriptObjectType, SetScriptObjectType, String, String::EMPTY, AM_DEFAULT);
     REF_ACCESSOR_ATTRIBUTE(LuaScriptInstance, VAR_STRING, "Script Object Type", GetScriptObjectType, SetScriptObjectType, String, String::EMPTY, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(LuaScriptInstance, VAR_BUFFER, "Script Data", GetScriptDataAttr, SetScriptDataAttr, PODVector<unsigned char>, Variant::emptyBuffer, AM_FILE | AM_NOEDIT);
     ACCESSOR_ATTRIBUTE(LuaScriptInstance, VAR_BUFFER, "Script Data", GetScriptDataAttr, SetScriptDataAttr, PODVector<unsigned char>, Variant::emptyBuffer, AM_FILE | AM_NOEDIT);
     ACCESSOR_ATTRIBUTE(LuaScriptInstance, VAR_BUFFER, "Script Network Data", GetScriptNetworkDataAttr, SetScriptNetworkDataAttr, PODVector<unsigned char>, Variant::emptyBuffer, AM_NET | AM_NOEDIT);
     ACCESSOR_ATTRIBUTE(LuaScriptInstance, VAR_BUFFER, "Script Network Data", GetScriptNetworkDataAttr, SetScriptNetworkDataAttr, PODVector<unsigned char>, Variant::emptyBuffer, AM_NET | AM_NOEDIT);
@@ -707,13 +707,13 @@ WeakPtr<LuaFunction> LuaScriptInstance::GetScriptObjectFunction(const String& fu
 
 
 void LuaScriptInstance::SetScriptFileAttr(ResourceRef value)
 void LuaScriptInstance::SetScriptFileAttr(ResourceRef value)
 {
 {
-	ResourceCache* cache = GetSubsystem<ResourceCache>();
-	SetScriptFile(cache->GetResource<LuaFile>(value.name_));
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    SetScriptFile(cache->GetResource<LuaFile>(value.name_));
 }
 }
 
 
 ResourceRef LuaScriptInstance::GetScriptFileAttr() const
 ResourceRef LuaScriptInstance::GetScriptFileAttr() const
 {
 {
-	return GetResourceRef(scriptFile_, LuaFile::GetTypeStatic());
+    return GetResourceRef(scriptFile_, LuaFile::GetTypeStatic());
 }
 }
 
 
 }
 }

+ 0 - 462
Source/Engine/Script/Script.cpp

@@ -22,10 +22,7 @@
 
 
 #include "Precompiled.h"
 #include "Precompiled.h"
 #include "Addons.h"
 #include "Addons.h"
-#include "Context.h"
 #include "EngineEvents.h"
 #include "EngineEvents.h"
-#include "File.h"
-#include "FileSystem.h"
 #include "Log.h"
 #include "Log.h"
 #include "Profiler.h"
 #include "Profiler.h"
 #include "Scene.h"
 #include "Scene.h"
@@ -41,113 +38,6 @@
 namespace Urho3D
 namespace Urho3D
 {
 {
 
 
-/// %Object property info for scripting API dump.
-struct PropertyInfo
-{
-    /// Construct.
-    PropertyInfo() :
-        read_(false),
-        write_(false),
-        indexed_(false)
-    {
-    }
-
-    /// Property name.
-    String name_;
-    /// Property data type.
-    String type_;
-    /// Reading supported flag.
-    bool read_;
-    /// Writing supported flag.
-    bool write_;
-    /// Indexed flag.
-    bool indexed_;
-};
-
-void ExtractPropertyInfo(const String& functionName, const String& declaration, Vector<PropertyInfo>& propertyInfos)
-{
-    String propertyName = functionName.Substring(4);
-    PropertyInfo* info = 0;
-    for (unsigned k = 0; k < propertyInfos.Size(); ++k)
-    {
-        if (propertyInfos[k].name_ == propertyName)
-        {
-            info = &propertyInfos[k];
-            break;
-        }
-    }
-    if (!info)
-    {
-        propertyInfos.Resize(propertyInfos.Size() + 1);
-        info = &propertyInfos.Back();
-        info->name_ = propertyName;
-    }
-    if (functionName.Contains("get_"))
-    {
-        info->read_ = true;
-        // Extract type from the return value
-        Vector<String> parts = declaration.Split(' ');
-        if (parts.Size())
-        {
-            if (parts[0] != "const")
-                info->type_ = parts[0];
-            else if (parts.Size() > 1)
-                info->type_ = parts[1];
-        }
-        // If get method has parameters, it is indexed
-        if (!declaration.Contains("()"))
-        {
-            info->indexed_ = true;
-            info->type_ += "[]";
-        }
-
-        // Sanitate the reference operator away
-        info->type_.Replace("&", "");
-    }
-    if (functionName.Contains("set_"))
-    {
-        info->write_ = true;
-        if (info->type_.Empty())
-        {
-            // Extract type from parameters
-            unsigned begin = declaration.Find(',');
-            if (begin == String::NPOS)
-                begin = declaration.Find('(');
-            else
-                info->indexed_ = true;
-
-            if (begin != String::NPOS)
-            {
-                ++begin;
-                unsigned end = declaration.Find(')');
-                if (end != String::NPOS)
-                {
-                    info->type_ = declaration.Substring(begin, end - begin);
-                    // Sanitate const & reference operator away
-                    info->type_.Replace("const ", "");
-                    info->type_.Replace("&in", "");
-                    info->type_.Replace("&", "");
-                }
-            }
-        }
-    }
-}
-
-bool ComparePropertyStrings(const String& lhs, const String& rhs)
-{
-    int spaceLhs = lhs.Find(' ');
-    int spaceRhs = rhs.Find(' ');
-    if (spaceLhs != String::NPOS && spaceRhs != String::NPOS)
-        return String::Compare(lhs.CString() + spaceLhs, rhs.CString() + spaceRhs, true) < 0;
-    else
-        return String::Compare(lhs.CString(), rhs.CString(), true) < 0;
-}
-
-bool ComparePropertyInfos(const PropertyInfo& lhs, const PropertyInfo& rhs)
-{
-    return String::Compare(lhs.name_.CString(), rhs.name_.CString(), true) < 0;
-}
-
 Script::Script(Context* context) :
 Script::Script(Context* context) :
     Object(context),
     Object(context),
     scriptEngine_(0),
     scriptEngine_(0),
@@ -277,320 +167,6 @@ void Script::SetExecuteConsoleCommands(bool enable)
         UnsubscribeFromEvent(E_CONSOLECOMMAND);
         UnsubscribeFromEvent(E_CONSOLECOMMAND);
 }
 }
 
 
-void Script::DumpAPI(DumpMode mode)
-{
-    // Does not use LOGRAW macro here to ensure the messages are always dumped regardless of URHO3D_LOGGING compiler directive and of Log subsystem availability
-    if (mode == DOXYGEN)
-        Log::WriteRaw("namespace Urho3D\n{\n\n/**\n\\page ScriptAPI Scripting API\n\n");
-    else if (mode == C_HEADER)
-        Log::WriteRaw("// Script API header intended to be 'force included' in IDE for AngelScript content assist / code completion\n\n"
-            "#define int8 signed char\n"
-            "#define int16 signed short\n"
-            "#define int64 long\n"
-            "#define uint8 unsigned char\n"
-            "#define uint16 unsigned short\n"
-            "#define uint64 unsigned long\n"
-            "#define null 0\n");
-
-    if (mode == DOXYGEN)
-        Log::WriteRaw("\\section ScriptAPI_Classes Classes\n");
-    else if (mode == C_HEADER)
-        Log::WriteRaw("\n// Classes\n");
-
-    unsigned types = scriptEngine_->GetObjectTypeCount();
-    Vector<Pair<String, unsigned> > sortedTypes;
-    for (unsigned i = 0; i < types; ++i)
-    {
-        asIObjectType* type = scriptEngine_->GetObjectTypeByIndex(i);
-        if (type)
-        {
-            String typeName(type->GetName());
-            sortedTypes.Push(MakePair(typeName, i));
-        }
-    }
-    Sort(sortedTypes.Begin(), sortedTypes.End());
-    
-    for (unsigned i = 0; i < sortedTypes.Size(); ++i)
-    {
-        asIObjectType* type = scriptEngine_->GetObjectTypeByIndex(sortedTypes[i].second_);
-        if (type)
-        {
-            String typeName(type->GetName());
-            Vector<String> methodDeclarations;
-            Vector<PropertyInfo> propertyInfos;
-
-            if (mode == DOXYGEN)
-                Log::WriteRaw("\n### " + typeName + "\n");
-            else if (mode == C_HEADER)
-            {
-                ///\todo Find a cleaner way to do this instead of hardcoding
-                if (typeName == "Array")
-                    Log::WriteRaw("\ntemplate <class T> class " + typeName + "\n{\n");
-                else
-                    Log::WriteRaw("\nclass " + typeName + "\n{\n");
-            }
-
-            unsigned methods = type->GetMethodCount();
-            for (unsigned j = 0; j < methods; ++j)
-            {
-                asIScriptFunction* method = type->GetMethodByIndex(j);
-                String methodName(method->GetName());
-                String declaration(method->GetDeclaration());
-                if (methodName.Contains("get_") || methodName.Contains("set_"))
-                    ExtractPropertyInfo(methodName, declaration, propertyInfos);
-                else
-                {
-                    // Sanitate the method name. \todo For now, skip the operators
-                    if (!declaration.Contains("::op"))
-                    {
-                        String prefix(typeName + "::");
-                        declaration.Replace(prefix, "");
-                        ///\todo Is there a better way to mark deprecated API bindings for AngelScript?
-                        unsigned posBegin = declaration.FindLast("const String&in = \"deprecated:");
-                        if (posBegin != String::NPOS)
-                        {
-                            // Assume this 'mark' is added as the last parameter
-                            unsigned posEnd = declaration.Find(')', posBegin);
-                            if (posBegin != String::NPOS)
-                            {
-                                declaration.Replace(posBegin, posEnd - posBegin, "");
-                                posBegin = declaration.Find(", ", posBegin - 2);
-                                if (posBegin != String::NPOS)
-                                    declaration.Replace(posBegin, 2, "");
-                                if (mode == DOXYGEN)
-                                    declaration += " // deprecated";
-                                else if (mode == C_HEADER)
-                                    declaration = "/* deprecated */\n" + declaration;
-                            }
-                        }
-                        methodDeclarations.Push(declaration);
-                    }
-                }
-            }
-
-            // Assume that the same property is never both an accessor property, and a direct one
-            unsigned properties = type->GetPropertyCount();
-            for (unsigned j = 0; j < properties; ++j)
-            {
-                const char* propertyName;
-                const char* propertyDeclaration;
-                int typeId;
-
-                type->GetProperty(j, &propertyName, &typeId);
-                propertyDeclaration = scriptEngine_->GetTypeDeclaration(typeId);
-
-                PropertyInfo newInfo;
-                newInfo.name_ = String(propertyName);
-                newInfo.type_ = String(propertyDeclaration);
-                newInfo.read_ = newInfo.write_ = true;
-                propertyInfos.Push(newInfo);
-            }
-            
-            Sort(methodDeclarations.Begin(), methodDeclarations.End(), ComparePropertyStrings);
-            Sort(propertyInfos.Begin(), propertyInfos.End(), ComparePropertyInfos);
-            
-            if (!methodDeclarations.Empty())
-            {
-                if (mode == DOXYGEN)
-                    Log::WriteRaw("\nMethods:\n\n");
-                else if (mode == C_HEADER)
-                    Log::WriteRaw("// Methods:\n");
-                for (unsigned j = 0; j < methodDeclarations.Size(); ++j)
-                    OutputAPIRow(mode, methodDeclarations[j]);
-            }
-
-            if (!propertyInfos.Empty())
-            {
-                if (mode == DOXYGEN)
-                    Log::WriteRaw("\nProperties:\n\n");
-                else if (mode == C_HEADER)
-                    Log::WriteRaw("\n// Properties:\n");
-                for (unsigned j = 0; j < propertyInfos.Size(); ++j)
-                {
-                    String remark;
-                    String cppdoc;
-                    if (!propertyInfos[j].write_)
-                        remark = "readonly";
-                    else if (!propertyInfos[j].read_)
-                        remark = "writeonly";
-                    if (!remark.Empty())
-                    {
-                        if (mode == DOXYGEN)
-                        {
-                            remark = " // " + remark;
-                        }
-                        else if (mode == C_HEADER)
-                        {
-                            cppdoc = "/* " + remark + " */\n";
-                            remark.Clear();
-                        }
-                    }
-
-                    OutputAPIRow(mode, cppdoc + propertyInfos[j].type_ + " " + propertyInfos[j].name_ + remark);
-                }
-            }
-
-            if (mode == DOXYGEN)
-                Log::WriteRaw("\n");
-            else if (mode == C_HEADER)
-                Log::WriteRaw("};\n");
-        }
-    }
-
-    Vector<PropertyInfo> globalPropertyInfos;
-    Vector<String> globalFunctions;
-
-    unsigned functions = scriptEngine_->GetGlobalFunctionCount();
-    for (unsigned i = 0; i < functions; ++i)
-    {
-        asIScriptFunction* function = scriptEngine_->GetGlobalFunctionByIndex(i);
-        String functionName(function->GetName());
-        String declaration(function->GetDeclaration());
-
-        if (functionName.Contains("set_") || functionName.Contains("get_"))
-            ExtractPropertyInfo(functionName, declaration, globalPropertyInfos);
-        else
-            globalFunctions.Push(declaration);
-    }
-
-    Sort(globalFunctions.Begin(), globalFunctions.End(), ComparePropertyStrings);
-    Sort(globalPropertyInfos.Begin(), globalPropertyInfos.End(), ComparePropertyInfos);
-
-    if (mode == DOXYGEN)
-        Log::WriteRaw("\\section ScriptAPI_Enums Enumerations\n");
-    else if (mode == C_HEADER)
-        Log::WriteRaw("\n// Enumerations\n");
-
-    unsigned enums = scriptEngine_->GetEnumCount();
-    Vector<Pair<String, unsigned> > sortedEnums;
-    for (unsigned i = 0; i < enums; ++i)
-    {
-        int typeId;
-        sortedEnums.Push(MakePair(String(scriptEngine_->GetEnumByIndex(i, &typeId)), i));
-    }
-    Sort(sortedEnums.Begin(), sortedEnums.End());
-    
-    for (unsigned i = 0; i < sortedEnums.Size(); ++i)
-    {
-        int typeId;
-        if (mode == DOXYGEN)
-            Log::WriteRaw("\n### " + String(scriptEngine_->GetEnumByIndex(sortedEnums[i].second_, &typeId)) + "\n\n");
-        else if (mode == C_HEADER)
-            Log::WriteRaw("\nenum " + String(scriptEngine_->GetEnumByIndex(sortedEnums[i].second_, &typeId)) + "\n{\n");
-
-        for (unsigned j = 0; j < (unsigned)scriptEngine_->GetEnumValueCount(typeId); ++j)
-        {
-            int value = 0;
-            const char* name = scriptEngine_->GetEnumValueByIndex(typeId, j, &value);
-            OutputAPIRow(mode, String(name), false, ",");
-        }
-
-        if (mode == DOXYGEN)
-            Log::WriteRaw("\n");
-        else if (mode == C_HEADER)
-            Log::WriteRaw("};\n");
-    }
-    
-    if (mode == DOXYGEN)
-        Log::WriteRaw("\\section ScriptAPI_GlobalFunctions Global functions\n");
-    else if (mode == C_HEADER)
-        Log::WriteRaw("\n// Global functions\n");
-
-    for (unsigned i = 0; i < globalFunctions.Size(); ++i)
-        OutputAPIRow(mode, globalFunctions[i]);
-
-    if (mode == DOXYGEN)
-        Log::WriteRaw("\\section ScriptAPI_GlobalProperties Global properties\n");
-    else if (mode == C_HEADER)
-        Log::WriteRaw("\n// Global properties\n");
-
-    for (unsigned i = 0; i < globalPropertyInfos.Size(); ++i)
-        OutputAPIRow(mode, globalPropertyInfos[i].type_ + " " + globalPropertyInfos[i].name_, true);
-
-    if (mode == DOXYGEN)
-        Log::WriteRaw("\\section ScriptAPI_GlobalConstants Global constants\n");
-    else if (mode == C_HEADER)
-        Log::WriteRaw("\n// Global constants\n");
-
-    Vector<String> globalConstants;
-    unsigned properties = scriptEngine_->GetGlobalPropertyCount();
-    for (unsigned i = 0; i < properties; ++i)
-    {
-        const char* propertyName;
-        const char* propertyDeclaration;
-        int typeId;
-        scriptEngine_->GetGlobalPropertyByIndex(i, &propertyName, 0, &typeId);
-        propertyDeclaration = scriptEngine_->GetTypeDeclaration(typeId);
-
-        String type(propertyDeclaration);
-        globalConstants.Push(type + " " + String(propertyName));
-    }
-    
-    Sort(globalConstants.Begin(), globalConstants.End(), ComparePropertyStrings);
-    
-    for (unsigned i = 0; i < globalConstants.Size(); ++i)
-        OutputAPIRow(mode, globalConstants[i], true);
-
-    // Dump event descriptions in Doxygen mode. This means going through the header files, as the information is not
-    // available otherwise
-    if (mode == DOXYGEN)
-    {
-        FileSystem* fileSystem = GetSubsystem<FileSystem>();
-        Vector<String> headerFiles;
-        String path = fileSystem->GetProgramDir();
-        path.Replace("/Bin", "/Source/Engine");
-        
-        fileSystem->ScanDir(headerFiles, path, "*.h", SCAN_FILES, true);
-        if (!headerFiles.Empty())
-        {
-            Log::WriteRaw("\n\\page EventList Event list\n");
-            Sort(headerFiles.Begin(), headerFiles.End());
-        }
-        
-        for (unsigned i = 0; i < headerFiles.Size(); ++i)
-        {
-            if (headerFiles[i].EndsWith("Events.h"))
-            {
-                SharedPtr<File> file(new File(context_, path + headerFiles[i], FILE_READ));
-                if (!file->IsOpen())
-                    continue;
-                
-                unsigned start = headerFiles[i].Find('/') + 1;
-                unsigned end = headerFiles[i].Find("Events.h");
-                Log::WriteRaw("\n## %" + headerFiles[i].Substring(start, end - start) + " events\n");
-                
-                while (!file->IsEof())
-                {
-                    String line = file->ReadLine();
-                    if (line.StartsWith("EVENT"))
-                    {
-                        Vector<String> parts = line.Split(',');
-                        if (parts.Size() == 2)
-                            Log::WriteRaw("\n### " + parts[1].Substring(0, parts[1].Length() - 1).Trimmed() + "\n");
-                    }
-                    if (line.Contains("PARAM"))
-                    {
-                        Vector<String> parts = line.Split(',');
-                        if (parts.Size() == 2)
-                        {
-                            String paramName = parts[1].Substring(0, parts[1].Find(')')).Trimmed();
-                            String paramType = parts[1].Substring(parts[1].Find("// ") + 3);
-                            if (!paramName.Empty() && !paramType.Empty())
-                                Log::WriteRaw("- %" + paramName + " : " + paramType + "\n");
-                        }
-                    }
-                }
-            }
-        }
-        
-        if (!headerFiles.Empty())
-            Log::WriteRaw("\n");
-    }
-
-    if (mode == DOXYGEN)
-        Log::WriteRaw("*/\n\n}\n");
-}
-
 void Script::MessageCallback(const asSMessageInfo* msg)
 void Script::MessageCallback(const asSMessageInfo* msg)
 {
 {
     String message;
     String message;
@@ -681,44 +257,6 @@ asIScriptContext* Script::GetScriptFileContext()
     return scriptFileContexts_[scriptNestingLevel_];
     return scriptFileContexts_[scriptNestingLevel_];
 }
 }
 
 
-void Script::OutputAPIRow(DumpMode mode, const String& row, bool removeReference, String separator)
-{
-    String out(row);
-    ///\todo We need C++11 <regex> in String class to handle REGEX whole-word replacement correctly. Can't do that since we still support VS2008.
-    // Commenting out to temporary fix property name like 'doubleClickInterval' from being wrongly replaced.
-    // Fortunately, there is no occurence of type 'double' in the API at the moment.
-    //out.Replace("double", "float");   // s/\bdouble\b/float/g
-    out.Replace("&in", "&");
-    out.Replace("&out", "&");
-    if (removeReference)
-        out.Replace("&", "");
-
-    if (mode == DOXYGEN)
-        Log::WriteRaw("- " + out + "\n");
-    else if (mode == C_HEADER)
-    {
-        out.Replace("@", "");
-        out.Replace("?&", "void*");
-
-        // s/(\w+)\[\]/Array<\1>/g
-        unsigned posBegin = String::NPOS;
-        while (1)   // Loop to cater for array of array of T
-        {
-            unsigned posEnd = out.Find("[]");
-            if (posEnd == String::NPOS)
-                break;
-            if (posBegin > posEnd)
-                posBegin = posEnd - 1;
-            while (posBegin < posEnd && isalnum(out[posBegin]))
-                --posBegin;
-            ++posBegin;
-            out.Replace(posBegin, posEnd - posBegin + 2, "Array<" + out.Substring(posBegin, posEnd - posBegin) + ">");
-        }
-
-        Log::WriteRaw(out + separator + "\n");
-    }
-}
-
 void Script::HandleConsoleCommand(StringHash eventType, VariantMap& eventData)
 void Script::HandleConsoleCommand(StringHash eventType, VariantMap& eventData)
 {
 {
     using namespace ConsoleCommand;
     using namespace ConsoleCommand;

+ 554 - 0
Source/Engine/Script/ScriptAPIDump.cpp

@@ -0,0 +1,554 @@
+//
+// Copyright (c) 2008-2014 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "Precompiled.h"
+#include "Context.h"
+#include "File.h"
+#include "FileSystem.h"
+#include "Log.h"
+#include "Script.h"
+
+#include <angelscript.h>
+
+#include "DebugNew.h"
+
+namespace Urho3D
+{
+
+/// %Object property info for scripting API dump.
+struct PropertyInfo
+{
+    /// Construct.
+    PropertyInfo() :
+        read_(false),
+        write_(false),
+        indexed_(false)
+    {
+    }
+
+    /// Property name.
+    String name_;
+    /// Property data type.
+    String type_;
+    /// Reading supported flag.
+    bool read_;
+    /// Writing supported flag.
+    bool write_;
+    /// Indexed flag.
+    bool indexed_;
+};
+
+void ExtractPropertyInfo(const String& functionName, const String& declaration, Vector<PropertyInfo>& propertyInfos)
+{
+    String propertyName = functionName.Substring(4);
+    PropertyInfo* info = 0;
+    for (unsigned k = 0; k < propertyInfos.Size(); ++k)
+    {
+        if (propertyInfos[k].name_ == propertyName)
+        {
+            info = &propertyInfos[k];
+            break;
+        }
+    }
+    if (!info)
+    {
+        propertyInfos.Resize(propertyInfos.Size() + 1);
+        info = &propertyInfos.Back();
+        info->name_ = propertyName;
+    }
+    if (functionName.Contains("get_"))
+    {
+        info->read_ = true;
+        // Extract type from the return value
+        Vector<String> parts = declaration.Split(' ');
+        if (parts.Size())
+        {
+            if (parts[0] != "const")
+                info->type_ = parts[0];
+            else if (parts.Size() > 1)
+                info->type_ = parts[1];
+        }
+        // If get method has parameters, it is indexed
+        if (!declaration.Contains("()"))
+        {
+            info->indexed_ = true;
+            info->type_ += "[]";
+        }
+
+        // Sanitate the reference operator away
+        info->type_.Replace("&", "");
+    }
+    if (functionName.Contains("set_"))
+    {
+        info->write_ = true;
+        if (info->type_.Empty())
+        {
+            // Extract type from parameters
+            unsigned begin = declaration.Find(',');
+            if (begin == String::NPOS)
+                begin = declaration.Find('(');
+            else
+                info->indexed_ = true;
+
+            if (begin != String::NPOS)
+            {
+                ++begin;
+                unsigned end = declaration.Find(')');
+                if (end != String::NPOS)
+                {
+                    info->type_ = declaration.Substring(begin, end - begin);
+                    // Sanitate const & reference operator away
+                    info->type_.Replace("const ", "");
+                    info->type_.Replace("&in", "");
+                    info->type_.Replace("&", "");
+                }
+            }
+        }
+    }
+}
+
+bool ComparePropertyStrings(const String& lhs, const String& rhs)
+{
+    int spaceLhs = lhs.Find(' ');
+    int spaceRhs = rhs.Find(' ');
+    if (spaceLhs != String::NPOS && spaceRhs != String::NPOS)
+        return String::Compare(lhs.CString() + spaceLhs, rhs.CString() + spaceRhs, true) < 0;
+    else
+        return String::Compare(lhs.CString(), rhs.CString(), true) < 0;
+}
+
+bool ComparePropertyInfos(const PropertyInfo& lhs, const PropertyInfo& rhs)
+{
+    return String::Compare(lhs.name_.CString(), rhs.name_.CString(), true) < 0;
+}
+
+void Script::OutputAPIRow(DumpMode mode, const String& row, bool removeReference, String separator)
+{
+    String out(row);
+    ///\todo We need C++11 <regex> in String class to handle REGEX whole-word replacement correctly. Can't do that since we still support VS2008.
+    // Commenting out to temporary fix property name like 'doubleClickInterval' from being wrongly replaced.
+    // Fortunately, there is no occurence of type 'double' in the API at the moment.
+    //out.Replace("double", "float");   // s/\bdouble\b/float/g
+    out.Replace("&in", "&");
+    out.Replace("&out", "&");
+    if (removeReference)
+        out.Replace("&", "");
+
+    if (mode == DOXYGEN)
+        Log::WriteRaw("- " + out + "\n");
+    else if (mode == C_HEADER)
+    {
+        out.Replace("@", "");
+        out.Replace("?&", "void*");
+
+        // s/(\w+)\[\]/Array<\1>/g
+        unsigned posBegin = String::NPOS;
+        while (1)   // Loop to cater for array of array of T
+        {
+            unsigned posEnd = out.Find("[]");
+            if (posEnd == String::NPOS)
+                break;
+            if (posBegin > posEnd)
+                posBegin = posEnd - 1;
+            while (posBegin < posEnd && isalnum(out[posBegin]))
+                --posBegin;
+            ++posBegin;
+            out.Replace(posBegin, posEnd - posBegin + 2, "Array<" + out.Substring(posBegin, posEnd - posBegin) + ">");
+        }
+
+        Log::WriteRaw(out + separator + "\n");
+    }
+}
+
+void Script::DumpAPI(DumpMode mode)
+{
+    // Does not use LOGRAW macro here to ensure the messages are always dumped regardless of URHO3D_LOGGING compiler directive
+    // and of Log subsystem availability
+
+    // Dump event descriptions and attribute definitions in Doxygen mode. For events, this means going through the header files,
+    // as the information is not available otherwise. 
+    /// \todo Dump events + attributes before the actual script API because the remarks (readonly / writeonly) seem to throw off
+    // Doxygen parsing and the following page definition(s) may not be properly recognized
+    if (mode == DOXYGEN)
+    {
+        Log::WriteRaw("namespace Urho3D\n{\n\n/**\n");
+
+        FileSystem* fileSystem = GetSubsystem<FileSystem>();
+        Vector<String> headerFiles;
+        String path = fileSystem->GetProgramDir();
+        path.Replace("/Bin", "/Source/Engine");
+        
+        fileSystem->ScanDir(headerFiles, path, "*.h", SCAN_FILES, true);
+        if (!headerFiles.Empty())
+        {
+            Log::WriteRaw("\n\\page EventList Event list\n");
+            Sort(headerFiles.Begin(), headerFiles.End());
+
+            for (unsigned i = 0; i < headerFiles.Size(); ++i)
+            {
+                if (headerFiles[i].EndsWith("Events.h"))
+                {
+                    SharedPtr<File> file(new File(context_, path + headerFiles[i], FILE_READ));
+                    if (!file->IsOpen())
+                        continue;
+                    
+                    unsigned start = headerFiles[i].Find('/') + 1;
+                    unsigned end = headerFiles[i].Find("Events.h");
+                    Log::WriteRaw("\n## %" + headerFiles[i].Substring(start, end - start) + " events\n");
+                    
+                    while (!file->IsEof())
+                    {
+                        String line = file->ReadLine();
+                        if (line.StartsWith("EVENT"))
+                        {
+                            Vector<String> parts = line.Split(',');
+                            if (parts.Size() == 2)
+                                Log::WriteRaw("\n### " + parts[1].Substring(0, parts[1].Length() - 1).Trimmed() + "\n");
+                        }
+                        if (line.Contains("PARAM"))
+                        {
+                            Vector<String> parts = line.Split(',');
+                            if (parts.Size() == 2)
+                            {
+                                String paramName = parts[1].Substring(0, parts[1].Find(')')).Trimmed();
+                                String paramType = parts[1].Substring(parts[1].Find("// ") + 3);
+                                if (!paramName.Empty() && !paramType.Empty())
+                                    Log::WriteRaw("- %" + paramName + " : " + paramType + "\n");
+                            }
+                        }
+                    }
+                }
+            }
+            
+            Log::WriteRaw("\n");
+        }
+        
+        Log::WriteRaw("\n\\page AttributeList Attribute list\n");
+        
+        const HashMap<ShortStringHash, Vector<AttributeInfo> >& attributes = context_->GetAllAttributes();
+
+        Vector<String> objectTypes;
+        for (HashMap<ShortStringHash, Vector<AttributeInfo> >::ConstIterator i = attributes.Begin(); i != attributes.End();
+            ++i)
+            objectTypes.Push(context_->GetTypeName(i->first_));
+        
+        Sort(objectTypes.Begin(), objectTypes.End());
+        
+        for (unsigned i = 0; i < objectTypes.Size(); ++i)
+        {
+            const Vector<AttributeInfo>& attrs = attributes.Find(objectTypes[i])->second_;
+            unsigned usableAttrs = 0;
+            for (unsigned j = 0; j < attrs.Size(); ++j)
+            {
+                // Attributes that are not shown in the editor are typically internal and not usable for eg. attribute
+                // animation
+                if (attrs[j].mode_ & AM_NOEDIT)
+                    continue;
+                ++usableAttrs;
+            }
+            
+            if (!usableAttrs)
+                continue;
+            
+            Log::WriteRaw("\n### %" + objectTypes[i] + "\n");
+            
+            for (unsigned j = 0; j < attrs.Size(); ++j)
+            {
+                if (attrs[j].mode_ & AM_NOEDIT)
+                    continue;
+                // Prepend each word in the attribute name with % to prevent unintended links
+                Vector<String> nameParts = attrs[j].name_.Split(' ');
+                for (unsigned k = 0; k < nameParts.Size(); ++k)
+                {
+                    if (nameParts[k].Length() > 1 && IsAlpha(nameParts[k][0]))
+                        nameParts[k] = "%" + nameParts[k];
+                }
+                String name;
+                name.Join(nameParts, " ");
+                String type = Variant::GetTypeName(attrs[j].type_);
+                // Variant typenames are all uppercase. Convert primitive types to the proper lowercase form for the documentation
+                if (type == "Int" || type == "Bool" || type == "Float")
+                    type[0] = ToLower(type[0]);
+                
+                Log::WriteRaw("- " + name + " : " + type + "\n");
+            }
+        }
+        
+        Log::WriteRaw("\n");
+    }
+    
+    if (mode == DOXYGEN)
+        Log::WriteRaw("\n\\page ScriptAPI Scripting API\n\n");
+    else if (mode == C_HEADER)
+        Log::WriteRaw("// Script API header intended to be 'force included' in IDE for AngelScript content assist / code completion\n\n"
+            "#define int8 signed char\n"
+            "#define int16 signed short\n"
+            "#define int64 long\n"
+            "#define uint8 unsigned char\n"
+            "#define uint16 unsigned short\n"
+            "#define uint64 unsigned long\n"
+            "#define null 0\n");
+
+    if (mode == DOXYGEN)
+        Log::WriteRaw("\\section ScriptAPI_Classes Classes\n");
+    else if (mode == C_HEADER)
+        Log::WriteRaw("\n// Classes\n");
+
+    unsigned types = scriptEngine_->GetObjectTypeCount();
+    Vector<Pair<String, unsigned> > sortedTypes;
+    for (unsigned i = 0; i < types; ++i)
+    {
+        asIObjectType* type = scriptEngine_->GetObjectTypeByIndex(i);
+        if (type)
+        {
+            String typeName(type->GetName());
+            sortedTypes.Push(MakePair(typeName, i));
+        }
+    }
+    Sort(sortedTypes.Begin(), sortedTypes.End());
+    
+    for (unsigned i = 0; i < sortedTypes.Size(); ++i)
+    {
+        asIObjectType* type = scriptEngine_->GetObjectTypeByIndex(sortedTypes[i].second_);
+        if (type)
+        {
+            String typeName(type->GetName());
+            Vector<String> methodDeclarations;
+            Vector<PropertyInfo> propertyInfos;
+
+            if (mode == DOXYGEN)
+                Log::WriteRaw("\n### " + typeName + "\n");
+            else if (mode == C_HEADER)
+            {
+                ///\todo Find a cleaner way to do this instead of hardcoding
+                if (typeName == "Array")
+                    Log::WriteRaw("\ntemplate <class T> class " + typeName + "\n{\n");
+                else
+                    Log::WriteRaw("\nclass " + typeName + "\n{\n");
+            }
+
+            unsigned methods = type->GetMethodCount();
+            for (unsigned j = 0; j < methods; ++j)
+            {
+                asIScriptFunction* method = type->GetMethodByIndex(j);
+                String methodName(method->GetName());
+                String declaration(method->GetDeclaration());
+                if (methodName.Contains("get_") || methodName.Contains("set_"))
+                    ExtractPropertyInfo(methodName, declaration, propertyInfos);
+                else
+                {
+                    // Sanitate the method name. \todo For now, skip the operators
+                    if (!declaration.Contains("::op"))
+                    {
+                        String prefix(typeName + "::");
+                        declaration.Replace(prefix, "");
+                        ///\todo Is there a better way to mark deprecated API bindings for AngelScript?
+                        unsigned posBegin = declaration.FindLast("const String&in = \"deprecated:");
+                        if (posBegin != String::NPOS)
+                        {
+                            // Assume this 'mark' is added as the last parameter
+                            unsigned posEnd = declaration.Find(')', posBegin);
+                            if (posBegin != String::NPOS)
+                            {
+                                declaration.Replace(posBegin, posEnd - posBegin, "");
+                                posBegin = declaration.Find(", ", posBegin - 2);
+                                if (posBegin != String::NPOS)
+                                    declaration.Replace(posBegin, 2, "");
+                                if (mode == DOXYGEN)
+                                    declaration += " // deprecated";
+                                else if (mode == C_HEADER)
+                                    declaration = "/* deprecated */\n" + declaration;
+                            }
+                        }
+                        methodDeclarations.Push(declaration);
+                    }
+                }
+            }
+
+            // Assume that the same property is never both an accessor property, and a direct one
+            unsigned properties = type->GetPropertyCount();
+            for (unsigned j = 0; j < properties; ++j)
+            {
+                const char* propertyName;
+                const char* propertyDeclaration;
+                int typeId;
+
+                type->GetProperty(j, &propertyName, &typeId);
+                propertyDeclaration = scriptEngine_->GetTypeDeclaration(typeId);
+
+                PropertyInfo newInfo;
+                newInfo.name_ = String(propertyName);
+                newInfo.type_ = String(propertyDeclaration);
+                newInfo.read_ = newInfo.write_ = true;
+                propertyInfos.Push(newInfo);
+            }
+            
+            Sort(methodDeclarations.Begin(), methodDeclarations.End(), ComparePropertyStrings);
+            Sort(propertyInfos.Begin(), propertyInfos.End(), ComparePropertyInfos);
+            
+            if (!methodDeclarations.Empty())
+            {
+                if (mode == DOXYGEN)
+                    Log::WriteRaw("\nMethods:\n\n");
+                else if (mode == C_HEADER)
+                    Log::WriteRaw("// Methods:\n");
+                for (unsigned j = 0; j < methodDeclarations.Size(); ++j)
+                    OutputAPIRow(mode, methodDeclarations[j]);
+            }
+
+            if (!propertyInfos.Empty())
+            {
+                if (mode == DOXYGEN)
+                    Log::WriteRaw("\nProperties:\n\n");
+                else if (mode == C_HEADER)
+                    Log::WriteRaw("\n// Properties:\n");
+                for (unsigned j = 0; j < propertyInfos.Size(); ++j)
+                {
+                    String remark;
+                    String cppdoc;
+                    if (!propertyInfos[j].write_)
+                        remark = "readonly";
+                    else if (!propertyInfos[j].read_)
+                        remark = "writeonly";
+                    if (!remark.Empty())
+                    {
+                        if (mode == DOXYGEN)
+                        {
+                            remark = " // " + remark;
+                        }
+                        else if (mode == C_HEADER)
+                        {
+                            cppdoc = "/* " + remark + " */\n";
+                            remark.Clear();
+                        }
+                    }
+
+                    OutputAPIRow(mode, cppdoc + propertyInfos[j].type_ + " " + propertyInfos[j].name_ + remark);
+                }
+            }
+
+            if (mode == DOXYGEN)
+                Log::WriteRaw("\n");
+            else if (mode == C_HEADER)
+                Log::WriteRaw("};\n");
+        }
+    }
+
+    Vector<PropertyInfo> globalPropertyInfos;
+    Vector<String> globalFunctions;
+
+    unsigned functions = scriptEngine_->GetGlobalFunctionCount();
+    for (unsigned i = 0; i < functions; ++i)
+    {
+        asIScriptFunction* function = scriptEngine_->GetGlobalFunctionByIndex(i);
+        String functionName(function->GetName());
+        String declaration(function->GetDeclaration());
+
+        if (functionName.Contains("set_") || functionName.Contains("get_"))
+            ExtractPropertyInfo(functionName, declaration, globalPropertyInfos);
+        else
+            globalFunctions.Push(declaration);
+    }
+
+    Sort(globalFunctions.Begin(), globalFunctions.End(), ComparePropertyStrings);
+    Sort(globalPropertyInfos.Begin(), globalPropertyInfos.End(), ComparePropertyInfos);
+
+    if (mode == DOXYGEN)
+        Log::WriteRaw("\\section ScriptAPI_Enums Enumerations\n");
+    else if (mode == C_HEADER)
+        Log::WriteRaw("\n// Enumerations\n");
+
+    unsigned enums = scriptEngine_->GetEnumCount();
+    Vector<Pair<String, unsigned> > sortedEnums;
+    for (unsigned i = 0; i < enums; ++i)
+    {
+        int typeId;
+        sortedEnums.Push(MakePair(String(scriptEngine_->GetEnumByIndex(i, &typeId)), i));
+    }
+    Sort(sortedEnums.Begin(), sortedEnums.End());
+    
+    for (unsigned i = 0; i < sortedEnums.Size(); ++i)
+    {
+        int typeId;
+        if (mode == DOXYGEN)
+            Log::WriteRaw("\n### " + String(scriptEngine_->GetEnumByIndex(sortedEnums[i].second_, &typeId)) + "\n\n");
+        else if (mode == C_HEADER)
+            Log::WriteRaw("\nenum " + String(scriptEngine_->GetEnumByIndex(sortedEnums[i].second_, &typeId)) + "\n{\n");
+
+        for (unsigned j = 0; j < (unsigned)scriptEngine_->GetEnumValueCount(typeId); ++j)
+        {
+            int value = 0;
+            const char* name = scriptEngine_->GetEnumValueByIndex(typeId, j, &value);
+            OutputAPIRow(mode, String(name), false, ",");
+        }
+
+        if (mode == DOXYGEN)
+            Log::WriteRaw("\n");
+        else if (mode == C_HEADER)
+            Log::WriteRaw("};\n");
+    }
+    
+    if (mode == DOXYGEN)
+        Log::WriteRaw("\\section ScriptAPI_GlobalFunctions Global functions\n");
+    else if (mode == C_HEADER)
+        Log::WriteRaw("\n// Global functions\n");
+
+    for (unsigned i = 0; i < globalFunctions.Size(); ++i)
+        OutputAPIRow(mode, globalFunctions[i]);
+
+    if (mode == DOXYGEN)
+        Log::WriteRaw("\\section ScriptAPI_GlobalProperties Global properties\n");
+    else if (mode == C_HEADER)
+        Log::WriteRaw("\n// Global properties\n");
+
+    for (unsigned i = 0; i < globalPropertyInfos.Size(); ++i)
+        OutputAPIRow(mode, globalPropertyInfos[i].type_ + " " + globalPropertyInfos[i].name_, true);
+
+    if (mode == DOXYGEN)
+        Log::WriteRaw("\\section ScriptAPI_GlobalConstants Global constants\n");
+    else if (mode == C_HEADER)
+        Log::WriteRaw("\n// Global constants\n");
+
+    Vector<String> globalConstants;
+    unsigned properties = scriptEngine_->GetGlobalPropertyCount();
+    for (unsigned i = 0; i < properties; ++i)
+    {
+        const char* propertyName;
+        const char* propertyDeclaration;
+        int typeId;
+        scriptEngine_->GetGlobalPropertyByIndex(i, &propertyName, 0, &typeId);
+        propertyDeclaration = scriptEngine_->GetTypeDeclaration(typeId);
+
+        String type(propertyDeclaration);
+        globalConstants.Push(type + " " + String(propertyName));
+    }
+    
+    Sort(globalConstants.Begin(), globalConstants.End(), ComparePropertyStrings);
+    
+    for (unsigned i = 0; i < globalConstants.Size(); ++i)
+        OutputAPIRow(mode, globalConstants[i], true);
+        
+    if (mode == DOXYGEN)
+        Log::WriteRaw("*/\n\n}\n");
+}
+
+}

+ 19 - 3
Source/Tools/ScriptCompiler/ScriptCompiler.cpp

@@ -30,6 +30,10 @@
 #include "Script.h"
 #include "Script.h"
 #include "ScriptFile.h"
 #include "ScriptFile.h"
 
 
+#ifdef URHO3D_LUA
+#include "LuaScript.h"
+#endif
+
 #ifdef WIN32
 #ifdef WIN32
 #include <windows.h>
 #include <windows.h>
 #endif
 #endif
@@ -67,11 +71,23 @@ int main(int argc, char** argv)
     }
     }
 
 
     SharedPtr<Context> context(new Context());
     SharedPtr<Context> context(new Context());
-
-    // Note: creating the Engine registers most subsystems which don't require engine initialization
     SharedPtr<Engine> engine(new Engine(context));
     SharedPtr<Engine> engine(new Engine(context));
     context->RegisterSubsystem(new Script(context));
     context->RegisterSubsystem(new Script(context));
-
+    
+    // In API dumping mode initialize the engine and instantiate LuaScript system if available so that we
+    // can dump attributes from as many classes as possible
+    if (dumpApiMode)
+    {
+        VariantMap engineParameters;
+        engineParameters["Headless"] = true;
+        engineParameters["WorkerThreads"] = false;
+        engineParameters["LogName"] = String::EMPTY;
+        engine->Initialize(engineParameters);
+    #ifdef URHO3D_LUA
+        context->RegisterSubsystem(new LuaScript(context));
+    #endif
+    }
+    
     Log* log = context->GetSubsystem<Log>();
     Log* log = context->GetSubsystem<Log>();
     // Register Log subsystem manually if compiled without logging support
     // Register Log subsystem manually if compiled without logging support
     if (!log)
     if (!log)

Some files were not shown because too many files changed in this diff