import "ecere"
+#if !defined(EAR_TO_ECON_ECDOC)
import "ec"
import "HTMLView"
import "IDESettings"
enum DocumentationType
{
+ unset,
nameSpaceDoc,
classDoc,
functionDoc,
enum DocumentationItem
{
+ unset,
description,
usage,
remarks,
static void FigureFileName(char * fileName, Module module, DocumentationType type, void * object, DocumentationItem item, void * data)
{
+ char hex[20];
+ fileName[0] = 0;
+ sprintf(hex, "%p", module);
+ strcat(fileName, hex);
+ strcat(fileName, "/");
+ sprintf(hex, "%p", object);
+ strcat(fileName, hex);
+ strcat(fileName, "/");
+ sprintf(hex, "%p", data);
+ strcat(fileName, hex);
+ strcat(fileName, "/");
+ if(type == nameSpaceDoc)
+ strcat(fileName, "namespace");
+ else if(type == functionDoc)
+ strcat(fileName, "function");
+ else if(type == classDoc)
+ strcat(fileName, "class");
+ else if(type == methodDoc)
+ strcat(fileName, "method");
+ strcat(fileName, "/");
+ if(item == description)
+ strcat(fileName, "description");
+ else if(item == usage)
+ strcat(fileName, "usage");
+ else if(item == remarks)
+ strcat(fileName, "remarks");
+ else if(item == example)
+ strcat(fileName, "example");
+ else if(item == seeAlso)
+ strcat(fileName, "seeAlso");
+ else if(item == enumerationValue)
+ strcat(fileName, "enumerationValue");
+ else if(item == definition)
+ strcat(fileName, "definition");
+ else if(item == conversion)
+ strcat(fileName, "conversion");
+ else if(item == memberDescription)
+ strcat(fileName, "memberDescription");
+ else if(item == propertyDescription)
+ strcat(fileName, "propertyDescription");
+ else if(item == parameter)
+ strcat(fileName, "parameter");
+ else if(item == returnValue)
+ strcat(fileName, "returnValue");
+}
+
+static void FigureFilePath(char * path, Module module, DocumentationType type, void * object, DocumentationItem item, void * data)
+{
+ char docPath[MAX_LOCATION];
NameSpace * nameSpace, * ns;
Class cl = null;
Method method = null;
GlobalFunction function = null;
char nsName[1024], temp[1024];
- char docFile[1024];
-
-
switch(type)
{
case nameSpaceDoc: nameSpace = object; break;
}
nsName[0] = 0;
+ temp[0] = 0;
ns = nameSpace;
while(ns && ns->name)
{
- strcpy(temp, "namespaces/");
- strcat(temp, ns->name);
+ strcpy(temp, ns->name);
strcat(temp, "/");
strcat(temp, nsName);
strcpy(nsName, temp);
ns = ns->parent;
}
- sprintf(docFile, "%s.eCdoc", (!module || !module.name || !strcmp(nsName, "namespaces/ecere/namespaces/com")) ? "ecereCOM" : module.name);
- if(strchr(docFile, DIR_SEP))
- {
- GetLastDirectory(docFile, temp);
- strcpy(docFile, temp);
- }
-
- sprintf(fileName, "<%s/%s>", settings.docDir, docFile); // Note that in the ecereIDE.ini file, there can be no quotes around the path, and there needs to be the final backslash. Otherwise this does not work.
- strcat(fileName, nsName);
+ docPath[0] = 0;
+ PathCatSlash(docPath, (!module || !module.name || !strcmp(nsName, "namespaces/ecere/namespaces/com")) ? "ecereCOM" : module.name);
+ //ChangeExtension(docPath, "eCdoc", docPath);
+ PathCatSlash(docPath, nsName);
if(cl)
{
- strcat(fileName, "classes/");
- strcat(fileName, cl.name);
- strcat(fileName, "/");
- }
-
- if(method)
- {
- strcat(fileName, "methods/");
- strcat(fileName, method.name);
- strcat(fileName, "/");
- }
- else if(function)
- {
- const char * name = RSearchString(function.name, "::", strlen(function.name), true, false);
- if(name) name += 2; else name = function.name;
- strcat(fileName, "functions/");
- strcat(fileName, name);
- strcat(fileName, "/");
- }
-
- switch(item)
- {
- case description: strcat(fileName, "description"); break;
- case usage: strcat(fileName, "usage"); break;
- case remarks: strcat(fileName, "remarks"); break;
- case example: strcat(fileName, "example"); break;
- case seeAlso: strcat(fileName, "seeAlso"); break;
- case returnValue: strcat(fileName, "returnValue"); break;
- case enumerationValue:
- strcat(fileName, "enumeration values/");
- strcat(fileName, ((NamedLink)data).name);
- break;
- case definition:
- strcat(fileName, "definitions/");
- strcat(fileName, ((Definition)data).name);
- break;
- case conversion:
- {
- const char * name = RSearchString(((Property)data).name, "::", strlen(((Property)data).name), true, false);
- if(name) name += 2; else name = ((Property)data).name;
- strcat(fileName, "conversions/");
- strcat(fileName, name);
- break;
- }
- case memberDescription:
- strcat(fileName, "data members/");
- strcat(fileName, ((DataMember)data).name);
- break;
- case propertyDescription:
- strcat(fileName, "properties/");
- strcat(fileName, ((Property)data).name);
- break;
- case parameter:
- {
- int count;
- char name[1024];
- Type prev;
- strcat(fileName, "parameters/");
- for(prev = data, count = 0; prev; prev = prev.prev, count++);
- sprintf(name, "%s.%d", ((Type)data).name, count);
- strcat(fileName, name);
- break;
- }
+ char * name = getDocFileNameFromTypeName(cl.name);
+ PathCatSlash(docPath, name);
+ delete name;
}
+ else
+ PathCatSlash(docPath, "_global-defs");
+ ChangeExtension(docPath, "econ", docPath);
+
+ path[0] = 0;
+ strcpy(path, settings.docDir);
+ PathCatSlash(path, docPath);
}
static char * ReadDoc(Module module, DocumentationType type, void * object, DocumentationItem item, void * data)
{
- char fileName[MAX_LOCATION];
String contents = null;
- File file;
+ bool docRetrieved = false;
+ NamespaceDoc nsDoc = null;
+ ClassDoc clDoc = null;
+ FunctionDoc fnDoc = null;
+ MethodDoc mdDoc = null;
+ String s = null;
+ char filePath[MAX_LOCATION];
+ Method method = null;
+ GlobalFunction function = null;
- FigureFileName(fileName, module, type, object, item, data);
- file = FileOpen(fileName, read);
- if(file)
+ ItemDoc doc = getDoc(filePath, module, type, object, item, data, false);
+
+ switch(type)
{
- uint len;
- if((len = file.GetSize()))
+ case functionDoc: function = object; break;
+ case methodDoc: method = object; break;
+ }
+
+ if(doc)
+ {
+ if(eClass_IsDerived(doc._class, class(ClassDoc)))
{
- contents = new char[len+1];
- file.Read(contents, 1, len);
- contents[len] = '\0';
+ clDoc = (ClassDoc)doc;
+ docRetrieved = true;
+ }
+ else if(eClass_IsDerived(doc._class, class(NamespaceDoc)))
+ {
+ nsDoc = (NamespaceDoc)doc;
+ docRetrieved = true;
}
- delete file;
}
- if(contents)
+
+ if(docRetrieved)
{
- int c;
- for(c = 0; contents[c]; c++)
- if(!isspace(contents[c])) break;
- if(!contents[c])
- delete contents;
+ ItemDoc itDoc = null;
+ if(type == functionDoc)
+ {
+ MapIterator<String, FunctionDoc> it { map = nsDoc.functions };
+ const char * name = RSearchString(function.name, "::", strlen(function.name), true, false);
+ if(name) name += 2; else name = function.name;
+ if(it.Index(name, false))
+ fnDoc = it.data;
+ }
+ else if(type == methodDoc)
+ {
+ MapIterator<String, MethodDoc> it { map = clDoc.methods };
+ if(it.Index(method.name, false))
+ mdDoc = it.data;
+ }
+
+ switch(item)
+ {
+ case description: s = type == methodDoc ? mdDoc.description : type == functionDoc ? fnDoc.description : type == classDoc ? clDoc.description : nsDoc.description; break;
+ case usage: s = type == methodDoc ? mdDoc.usage : type == functionDoc ? fnDoc.usage : type == classDoc ? clDoc.usage : null; break;
+ case remarks: s = type == methodDoc ? mdDoc.remarks : type == functionDoc ? fnDoc.remarks : type == classDoc ? clDoc.remarks : null; break;
+ case example: s = type == methodDoc ? mdDoc.example : type == functionDoc ? fnDoc.example : type == classDoc ? clDoc.example : null; break;
+ case seeAlso: s = type == methodDoc ? mdDoc.also : type == functionDoc ? fnDoc.also : type == classDoc ? clDoc.also : null; break;
+ case returnValue: s = type == methodDoc ? mdDoc.returnValue : type == functionDoc ? fnDoc.returnValue : null; break;
+ case enumerationValue:
+ if(clDoc && clDoc.values)
+ {
+ itDoc = clDoc.values[((NamedLink)data).name];
+ if(itDoc) s = itDoc.description;
+ }
+ break;
+ case definition:
+ if(nsDoc && nsDoc.defines)
+ {
+ itDoc = nsDoc.defines[((Definition)data).name];
+ if(itDoc) s = itDoc.description;
+ }
+ break;
+ case conversion:
+ if(clDoc && clDoc.conversions)
+ {
+ const char * name = RSearchString(((Property)data).name, "::", strlen(((Property)data).name), true, false);
+ if(name) name += 2; else name = ((Property)data).name;
+ itDoc = clDoc.conversions[name];
+ if(itDoc) s = itDoc.description;
+ }
+ break;
+ case memberDescription:
+ if(clDoc && clDoc.fields)
+ {
+ itDoc = clDoc.fields[((DataMember)data).name];
+ if(itDoc) s = itDoc.description;
+ }
+ break;
+ case propertyDescription:
+ if(clDoc && clDoc.properties)
+ {
+ itDoc = clDoc.properties[((Property)data).name];
+ if(itDoc) s = itDoc.description;
+ }
+ break;
+ case parameter:
+ if((type == functionDoc && fnDoc && fnDoc.parameters) || (type == methodDoc && mdDoc && mdDoc.parameters))
+ {
+ Type prev;
+ char * name;
+ for(prev = data; prev; prev = prev.prev);
+ name = ((Type)data).name;
+ if(type == functionDoc)
+ {
+ itDoc = fnDoc.parameters[name];
+ if(itDoc) s = itDoc.description;
+ }
+ else if(type == methodDoc)
+ {
+ itDoc = mdDoc.parameters[name];
+ if(itDoc) s = itDoc.description;
+ }
+ }
+ break;
+ }
+ if(s)
+ contents = CopyString(s);
}
if(editing && !contents && !readOnly)
contents = CopyString($"[Add Text]");
return contents;
}
+ItemDoc getDoc(char * filePath, Module module, DocumentationType type, void * object, DocumentationItem item, void * data, bool create)
+{
+ ItemDoc doc = null;
+ bool docRetrieved = false;
+ File f;
+ NamespaceDoc nsDoc = null;
+ ClassDoc clDoc = null;
+
+ char docPath[MAX_LOCATION];
+ Class cl = null;
+ Method method = null;
+
+ switch(type)
+ {
+ case classDoc: cl = (Class)object; break;
+ case methodDoc: method = object; cl = method._class; break;
+ }
+
+ FigureFilePath(filePath, module, type, object, item, data);
+
+ if(docCache[filePath])
+ {
+ DocCacheEntry entry = docCache[docPath];
+ if(cl && eClass_IsDerived(entry.doc._class, class(ClassDoc)))
+ {
+ clDoc = (ClassDoc)entry.doc;
+ docRetrieved = true;
+ }
+ else if(!cl && eClass_IsDerived(entry.doc._class, class(NamespaceDoc)))
+ {
+ nsDoc = (NamespaceDoc)entry.doc;
+ docRetrieved = true;
+ }
+ }
+
+ if(!docRetrieved)
+ {
+ f = FileOpen(filePath, read);
+ if(f)
+ {
+ JSONParser parser { f = f, eCON = true };
+ JSONResult jsonResult;
+ jsonResult = parser.GetObject(cl ? class(ClassDoc) : class(NamespaceDoc), cl ? &clDoc : &nsDoc);
+ delete parser;
+ delete f;
+
+ if(jsonResult == success)
+ {
+ docRetrieved = true;
+ docCache[docPath] = { added = GetTime(), doc = cl ? clDoc : nsDoc };
+ }
+ else
+ {
+ PrintLn("error: problem parsing file: ", filePath);
+ delete clDoc;
+ delete nsDoc;
+ }
+ }
+ }
+
+ if(!docRetrieved)
+ {
+ if(cl)
+ clDoc = { };
+ else
+ nsDoc = { };
+ doc = cl ? clDoc : nsDoc;
+ docCache[docPath] = { added = GetTime(), doc = cl ? clDoc : nsDoc };
+ docRetrieved = true;
+ }
+ else
+ {
+ doc = cl ? clDoc : nsDoc;
+ }
+
+ //void pruneDocCache()
+ {
+ MapIterator<const String, DocCacheEntry> it { map = docCache };
+ Array<const String> toRemove { };
+ Time now = GetTime();
+ for(entry : docCache)
+ {
+ Time diff = now - entry.added;
+ if(diff > 60*0.5)
+ toRemove.Add(&entry);
+ }
+ while(toRemove.count)
+ {
+ if(it.Index(toRemove.lastIterator.data, false))
+ {
+ delete it.data;
+ it.Remove();
+ }
+ toRemove.Remove(toRemove.lastIterator.pointer);
+ }
+ delete toRemove;
+ }
+ return doc;
+}
+
class APIPageNameSpace : APIPage
{
NameSpace * nameSpace;
{
// Writability test
{
- char docFile[MAX_LOCATION];
- Archive archive;
- Module module = page ? page.GetModule() : null;
- NameSpace * ns = page ? page.GetNameSpace() : null;
-
- sprintf(docFile, "%s/%s.eCdoc", settings.docDir, (!module || !module.name || (ns && ns->name && !strcmp(ns->name, "namespaces/ecere/namespaces/com"))) ? "ecereCOM" : module.name);
- if(FileExists(docFile))
- {
- archive = ArchiveOpen(docFile, { true } );
- readOnly = archive == null;
- delete archive;
- }
- else
+ char docDir[MAX_LOCATION];
+ readOnly = true;
+ strcpy(docDir, settings.docDir);
+ if(FileExists(docDir).isDirectory)
{
- readOnly = true;
- archive = ArchiveOpen(docFile, { true } );
- if(archive)
+ PathCatSlash(docDir, "___docWriteTest");
+ if(FileExists(docDir).isDirectory)
+ {
+ RemoveDir(docDir);
+ if(!FileExists(docDir))
+ readOnly = false;
+ }
+ else
{
- // Must create root directory on archive creation
- ArchiveDir dir = archive.OpenDirectory("", null, replace);
- if(dir)
+ MakeDir(docDir);
+ if(FileExists(docDir).isDirectory)
+ {
readOnly = false;
- delete dir;
+ RemoveDir(docDir);
+ }
}
- delete archive;
}
}
void SaveEdit()
{
- char archiveFile[MAX_LOCATION];
- char fileName[MAX_FILENAME];
- char directory[MAX_LOCATION];
- const char * location;
- Archive archive = null;
- if(SplitArchivePath(editString, archiveFile, &location))
+ Block block;
+ bool empty = true;
+ String contents = null;
+ uint len;
+ TempFile f { };
+ for(block = textBlock.parent.subBlocks.first; block; block = block.next)
{
- GetLastDirectory(location, fileName);
- StripLastDirectory(location, directory);
- archive = ArchiveOpen(archiveFile, { true } );
+ if(block.type == TEXT && block.textLen)
+ {
+ empty = false;
+ break;
+ }
}
+ if(!empty)
{
- TempFile f { };
- Block block;
- bool empty = true;
for(block = textBlock.parent.subBlocks.first; block; block = block.next)
{
- if(block.type == TEXT && block.textLen)
- {
- empty = false;
- break;
- }
+ if(block.type == BR)
+ f.Puts("<br>");
+ else if(block.type == TEXT)
+ f.Write(block.text, 1, block.textLen);
}
- if(!empty)
+ }
+ f.Seek(0, start);
+ if((len = f.GetSize()))
+ {
+ contents = new char[len+1];
+ f.Read(contents, 1, len);
+ contents[len] = '\0';
+ }
+ if(!empty && contents)
+ {
+ char temp[MAX_LOCATION];
+ char part[MAX_FILENAME];
+ Module module;
+ void * object;
+ void * data;
+ DocumentationType type;
+ DocumentationItem item;
+ ItemDoc doc;
+ bool docRetrieved = false;
+ NamespaceDoc nsDoc = null;
+ ClassDoc clDoc = null;
+ FunctionDoc fnDoc = null;
+ MethodDoc mdDoc = null;
+ //char filePath[MAX_LOCATION];
+ Class cl = null;
+ Method method = null;
+ GlobalFunction function = null;
+
+ strcpy(temp, editString);
+ SplitDirectory(temp, part, temp);
+ module = (Module)strtoull(part, null, 16);
+ SplitDirectory(temp, part, temp);
+ object = (void *)strtoull(part, null, 16);
+ SplitDirectory(temp, part, temp);
+ data = (void *)strtoull(part, null, 16);
+ SplitDirectory(temp, part, temp);
+ if(!strcmp(part, "namespace"))
+ type = nameSpaceDoc;
+ else if(!strcmp(part, "function"))
+ type = functionDoc;
+ else if(!strcmp(part, "class"))
+ type = classDoc;
+ else if(!strcmp(part, "method"))
+ type = methodDoc;
+ SplitDirectory(temp, part, temp);
+ if(!strcmp(part, "description"))
+ item = description;
+ else if(!strcmp(part, "usage"))
+ item = usage;
+ else if(!strcmp(part, "remarks"))
+ item = remarks;
+ else if(!strcmp(part, "example"))
+ item = example;
+ else if(!strcmp(part, "seeAlso"))
+ item = seeAlso;
+ else if(!strcmp(part, "enumerationValue"))
+ item = enumerationValue;
+ else if(!strcmp(part, "definition"))
+ item = definition;
+ else if(!strcmp(part, "conversion"))
+ item = conversion;
+ else if(!strcmp(part, "memberDescription"))
+ item = memberDescription;
+ else if(!strcmp(part, "propertyDescription"))
+ item = propertyDescription;
+ else if(!strcmp(part, "parameter"))
+ item = parameter;
+ else if(!strcmp(part, "returnValue"))
+ item = returnValue;
+
+ doc = getDoc(temp, module, type, object, item, data, true);
+
{
- for(block = textBlock.parent.subBlocks.first; block; block = block.next)
+ MapIterator<const String, DocCacheEntry> it { map = docCache };
+ if(it.Index(temp, false))
{
- if(block.type == BR)
- f.Puts("<br>");
- else if(block.type == TEXT)
- f.Write(block.text, 1, block.textLen);
+ it.data.doc = null;
+ delete it.data;
+ it.Remove();
}
}
- f.Seek(0, start);
- if(!empty || archive.FileExists(location))
+ switch(type)
{
- ArchiveDir dir = archive ? archive.OpenDirectory(directory, null, replace) : null;
- if(dir)
- dir.AddFromFile(fileName, f, null, replace, 0, null, null);
- delete dir;
+ case classDoc: cl = (Class)object; break;
+ case functionDoc: function = object; break;
+ case methodDoc: method = object; cl = method._class; break;
}
- delete archive;
- delete f;
- if(empty)
+
+ if(eClass_IsDerived(doc._class, class(ClassDoc)))
+ {
+ clDoc = (ClassDoc)doc;
+ docRetrieved = true;
+ }
+ else if(eClass_IsDerived(doc._class, class(NamespaceDoc)))
{
- Block parent = textBlock.parent;
- while((block = parent.subBlocks.first))
+ nsDoc = (NamespaceDoc)doc;
+ docRetrieved = true;
+ }
+
+ if(docRetrieved)
+ {
+ if(type == functionDoc)
+ {
+ const char * name = RSearchString(function.name, "::", strlen(function.name), true, false);
+ if(name) name += 2; else name = function.name;
+ if(!nsDoc.functions) nsDoc.functions = { };
+ fnDoc = nsDoc.functions[name];
+ if(!fnDoc)
+ {
+ fnDoc = { };
+ nsDoc.functions[name] = fnDoc;
+ }
+ }
+ else if(type == methodDoc)
{
- parent.subBlocks.Remove(block);
- delete block;
+ if(!clDoc.methods) clDoc.methods = { };
+ mdDoc = clDoc.methods[method.name];
+ if(!mdDoc)
+ {
+ mdDoc = { };
+ clDoc.methods[method.name] = mdDoc;
+ }
+ }
+
+ switch(item)
+ {
+ case description:
+ if(type == methodDoc) { mdDoc.description = contents; contents = null; }
+ else if(type == functionDoc) { fnDoc.description = contents; contents = null; }
+ else if(type == classDoc) { clDoc.description = contents; contents = null; }
+ else { nsDoc.description = contents; contents = null; }
+ break;
+ case usage:
+ if(type == methodDoc) { mdDoc.usage = contents; contents = null; }
+ else if(type == functionDoc) { fnDoc.usage = contents; contents = null; }
+ else if(type == classDoc) { clDoc.usage = contents; contents = null; }
+ break;
+ case remarks:
+ if(type == methodDoc) { mdDoc.remarks = contents; contents = null; }
+ else if(type == functionDoc) { fnDoc.remarks = contents; contents = null; }
+ else if(type == classDoc) { clDoc.remarks = contents; contents = null; }
+ break;
+ case example:
+ if(type == methodDoc) { mdDoc.example = contents; contents = null; }
+ else if(type == functionDoc) { fnDoc.example = contents; contents = null; }
+ else if(type == classDoc) { clDoc.example = contents; contents = null; }
+ break;
+ case seeAlso:
+ if(type == methodDoc) { mdDoc.also = contents; contents = null; }
+ else if(type == functionDoc) { fnDoc.also = contents; contents = null; }
+ else if(type == classDoc) { clDoc.also = contents; contents = null; }
+ break;
+ case returnValue:
+ if(type == methodDoc) { mdDoc.returnValue = contents; contents = null; }
+ else if(type == functionDoc) { fnDoc.returnValue = contents; contents = null; }
+ break;
+ case enumerationValue:
+ {
+ ValueDoc itDoc;
+ if(!clDoc.values) clDoc.values = { };
+ itDoc = clDoc.values[((NamedLink)data).name];
+ if(!itDoc)
+ {
+ itDoc = { };
+ clDoc.values[((NamedLink)data).name] = itDoc;
+ }
+ itDoc.description = contents; contents = null;
+ break;
+ }
+ case definition:
+ {
+ DefineDoc itDoc;
+ if(!nsDoc.defines) nsDoc.defines = { };
+ itDoc = nsDoc.defines[((Definition)data).name];
+ if(!itDoc)
+ {
+ itDoc = { };
+ nsDoc.defines[((Definition)data).name] = itDoc;
+ }
+ itDoc.description = contents; contents = null;
+ break;
+ }
+ case conversion:
+ {
+ ConversionDoc itDoc;
+ const char * name = RSearchString(((Property)data).name, "::", strlen(((Property)data).name), true, false);
+ if(name) name += 2; else name = ((Property)data).name;
+ if(!clDoc.conversions) clDoc.conversions = { };
+ itDoc = clDoc.conversions[name];
+ if(!itDoc)
+ {
+ itDoc = { };
+ clDoc.conversions[name] = itDoc;
+ }
+ itDoc.description = contents; contents = null;
+ break;
+ }
+ case memberDescription:
+ {
+ FieldDoc itDoc;
+ if(!clDoc.fields) clDoc.fields = { };
+ itDoc = clDoc.fields[((DataMember)data).name];
+ if(!itDoc)
+ {
+ itDoc = { };
+ clDoc.fields[((DataMember)data).name] = itDoc;
+ }
+ itDoc.description = contents; contents = null;
+ break;
+ }
+ case propertyDescription:
+ {
+ PropertyDoc itDoc;
+ if(!clDoc.properties) clDoc.properties = { };
+ itDoc = clDoc.properties[((Property)data).name];
+ if(!itDoc)
+ {
+ itDoc = { };
+ clDoc.properties[((Property)data).name] = itDoc;
+ }
+ itDoc.description = contents; contents = null;
+ break;
+ }
+ case parameter:
+ {
+ ParameterDoc itDoc;
+ char * name;
+ Type prev;
+ for(prev = data; prev; prev = prev.prev);
+ name = ((Type)data).name;
+ if(type == functionDoc)
+ {
+ if(!fnDoc.parameters) fnDoc.parameters = { };
+ itDoc = fnDoc.parameters[name];
+ if(!itDoc)
+ {
+ itDoc = { };
+ fnDoc.parameters[name] = itDoc;
+ }
+ itDoc.description = contents; contents = null;
+ }
+ else if(type == methodDoc)
+ {
+ if(!mdDoc.parameters) mdDoc.parameters = { };
+ itDoc = mdDoc.parameters[name];
+ if(!itDoc)
+ {
+ itDoc = { };
+ mdDoc.parameters[name] = itDoc;
+ }
+ itDoc.description = contents; contents = null;
+ }
+ break;
+ }
}
- textBlock = Block { type = TEXT, parent = parent, font = parent.font };
- textBlock.text = CopyString($"[Add Text]");
- textBlock.textLen = strlen(textBlock.text);
- parent.subBlocks.Add(textBlock);
}
- edit = false;
- if(created)
{
- ComputeMinSizes();
- ComputeSizes();
- PositionCaret(true);
- Update(null);
+ File f;
+ char filePath[MAX_LOCATION];
+ char dirPath[MAX_LOCATION];
+ strcpy(filePath, temp);
+ StripLastDirectory(filePath, dirPath);
+ if(FileExists(filePath))
+ DeleteFile(filePath);
+ if(cl ? !clDoc.isEmpty : !nsDoc.isEmpty)
+ {
+ if(!FileExists(dirPath))
+ MakeDir(dirPath);
+ f = FileOpen(filePath, write);
+ if(f)
+ {
+ if(cl)
+ WriteJSONObject(f, class(ClassDoc), clDoc, 0, true);
+ else
+ WriteJSONObject(f, class(NamespaceDoc), nsDoc, 0, true);
+ delete f;
+ }
+ else
+ {
+ PrintLn("error: writeClassDocFile -- problem opening file: ", filePath);
+ }
+ }
+ }
+ delete doc;
+ delete contents;
+ }
+
+ if(empty)
+ {
+ Block parent = textBlock.parent;
+ while((block = parent.subBlocks.first))
+ {
+ parent.subBlocks.Remove(block);
+ delete block;
}
+ textBlock = Block { type = TEXT, parent = parent, font = parent.font };
+ textBlock.text = CopyString($"[Add Text]");
+ textBlock.textLen = strlen(textBlock.text);
+ parent.subBlocks.Add(textBlock);
+ }
+
+ edit = false;
+ if(created)
+ {
+ ComputeMinSizes();
+ ComputeSizes();
+ PositionCaret(true);
+ Update(null);
}
}
return 0;
}
};
+#endif // !defined(EAR_TO_ECON_ECDOC)
+
+class ItemDoc
+{
+public:
+ property String name { get { return this ? name : null; } set { delete name; name = CopyString(value); } isset { return name && *name; } }
+ property String description { get { return this ? description : null; } set { delete description; description = CopyString(value); } isset { return description && *description; } }
+private:
+ char * name;
+ char * description;
+ property bool isEmpty
+ {
+ get
+ {
+ return !(
+ (name && *name) ||
+ (description && *description));
+ }
+ }
+ ~ItemDoc()
+ {
+ delete name;
+ delete description;
+ }
+}
+
+class MoreDoc : ItemDoc
+{
+public:
+ property String usage { get { return this ? usage : null; } set { delete usage; usage = CopyString(value); } isset { return usage && *usage; } }
+ property String example { get { return this ? example : null; } set { delete example; example = CopyString(value); } isset { return example && *example; } }
+ property String remarks { get { return this ? remarks : null; } set { delete remarks; remarks = CopyString(value); } isset { return remarks && *remarks; } }
+ property String also { get { return this ? also : null; } set { delete also; also = CopyString(value); } isset { return also && *also; } }
+private:
+ char * usage;
+ char * example;
+ char * remarks;
+ char * also;
+ property bool isEmpty
+ {
+ get
+ {
+ return !(
+ (usage && *usage) ||
+ (example && *example) ||
+ (remarks && *remarks) ||
+ (also && *also) ||
+ !ItemDoc::isEmpty);
+ }
+ }
+ ~MoreDoc()
+ {
+ delete usage;
+ delete example;
+ delete remarks;
+ delete also;
+ }
+}
+
+class NamespaceDoc : ItemDoc
+{
+public:
+ Map<String, DefineDoc> defines;
+ Map<String, FunctionDoc> functions;
+private:
+ property bool isEmpty
+ {
+ get
+ {
+ return !(
+ (defines && defines.count) ||
+ (functions && functions.count) ||
+ !ItemDoc::isEmpty);
+ }
+ }
+ ~NamespaceDoc()
+ {
+ delete defines;
+ delete functions;
+ }
+}
+
+class DefineDoc : ItemDoc { }
+
+class FunctionDoc : MoreDoc
+{
+public:
+ Map<String, ParameterDoc> parameters;
+ property String returnValue { get { return this ? returnValue : null; } set { delete returnValue; returnValue = CopyString(value); } isset { return returnValue && *returnValue; } }
+private:
+ char * returnValue;
+ property bool isEmpty
+ {
+ get
+ {
+ return !(
+ (parameters && parameters.count) ||
+ (returnValue && *returnValue) ||
+ !MoreDoc::isEmpty);
+ }
+ }
+ ~FunctionDoc()
+ {
+ delete parameters;
+ delete returnValue;
+ }
+}
+
+class ParameterDoc : ItemDoc
+{
+public:
+ uint position;
+}
+
+class ClassDoc : MoreDoc
+{
+public:
+ Map<String, ValueDoc> values;
+ Map<String, FieldDoc> fields;
+ Map<String, PropertyDoc> properties;
+ Map<String, ConversionDoc> conversions;
+ Map<String, MethodDoc> methods;
+private:
+ property bool isEmpty
+ {
+ get
+ {
+ return !(
+ (values && values.count) ||
+ (fields && fields.count) ||
+ (properties && properties.count) ||
+ (conversions && conversions.count) ||
+ (methods && methods.count) ||
+ !MoreDoc::isEmpty);
+ }
+ }
+ ~ClassDoc()
+ {
+ delete values;
+ delete fields;
+ delete properties;
+ delete conversions;
+ delete methods;
+ }
+}
+
+class ValueDoc : ItemDoc { }
+
+class FieldDoc : ItemDoc { }
+
+class PropertyDoc : ItemDoc { }
+
+class ConversionDoc : ItemDoc { }
+
+class MethodDoc : FunctionDoc { }
+
+char * getDocFileNameFromTypeName(const char * typeName)
+{
+ char * docFileName = new char[MAX_FILENAME];
+ const char * swap = "pointer";
+ const char * s = typeName;
+ char * d = docFileName;
+ const char * end = s + strlen(typeName);
+ int swapLen = strlen(swap);
+ for(; s < end; s++)
+ {
+ if(*s == ' ')
+ *d = '-';
+ else if(*s == '*')
+ {
+ strcpy(d, swap);
+ d += swapLen;
+ }
+ else
+ *d = *s;
+ d++;
+ }
+ *d = '\0';
+ return docFileName;
+}
+
+class DocCacheEntry
+{
+ Time added;
+ ItemDoc doc;
+
+ ~DocCacheEntry()
+ {
+ delete doc;
+ }
+}
+
+Map<const String, DocCacheEntry> docCache { };
--- /dev/null
+import "ecere"
+import "ec"
+import "Documentor.ec"
+
+#define sflnprintln(...) PrintLn(__FILE__, ":", __LINE__, ": ", ##__VA_ARGS__)
+
+static Context globalContext { };
+static OldList defines { };
+static OldList imports { };
+static NameSpace globalData;
+static OldList excludedSymbols { offset = (uint)(uintptr)&((Symbol)0).left };
+
+define app = (Convertor)__thisModule.application;
+
+#define UTF8_NUM_BYTES(x) (__extension__({ byte b = x; (b & 0x80 && b & 0x40) ? ((b & 0x20) ? ((b & 0x10) ? 4 : 3) : 2) : 1; }))
+
+default:
+/*extern */int __ecereVMethodID_class_OnGetString;
+private:
+
+
+static __attribute__((unused)) void Dummy()
+{
+ int a;
+ a.OnGetString(null, null, null);
+}
+
+static String ConvertReadDoc(const char * filePath)
+{
+ uint len;
+ String contents = null;
+ File f;
+ f = FileOpen(filePath, read);
+ if(f)
+ {
+ if((len = f.GetSize()))
+ {
+ contents = new char[len+1];
+ f.Read(contents, 1, len);
+ contents[len] = '\0';
+ }
+ delete f;
+ }
+ if(contents)
+ {
+ char * s;
+ for(s = contents; *s; s++)
+ if(!isspace(*s)) break;
+ if(!*s)
+ delete contents;
+ }
+ if(contents)
+ {
+ String buffer = new char[len+1];
+ char * i, * o = buffer;
+ for(i = contents; *i; i++)
+ {
+ if(i[0] == '<' && (i[1] == 'b' || i[1] == 'B') && (i[2] == 'r' || i[2] == 'R') && i[3] == '>' && i[4] == '\n')
+ i += 3;
+ else
+ {
+ *o = *i;
+ o++;
+ }
+ }
+ *o = 0;
+ delete contents;
+ contents = buffer;
+ }
+ return contents;
+}
+
+void ConvertModuleDoc(Module module, bool isDll)
+{
+ SubModule m;
+ bool readOnly = true;
+ char oldDocFilePath[MAX_LOCATION];
+ char docFilePath[MAX_LOCATION];
+ NameSpace * nameSpace = null;
+ if(module.name && (!strcmp(module.name, "ecere") || !strcmp(module.name, "ecereCOM")))
+ nameSpace = &module.application.systemNameSpace;
+ if(module.name && strcmp(module.name, "ecereCOM"))
+ nameSpace = &module.publicNameSpace;
+#ifdef _DEBUG
+ getDocFilePath(oldDocFilePath, null, module, nameSpace, false, true);
+ getDocFilePath(docFilePath, null, module, nameSpace, false, false);
+ PrintLn("ConvertModuleDoc:",
+ " convertDocDir(", app.convertDocDir, ")",
+ " outputDocDir(", app.outputDocDir, ")",
+ " oldDocFile(", oldDocFilePath, ")",
+ " docFile(", docFilePath, ")");
+#endif // def _DEBUG
+ getDocFilePath(oldDocFilePath, app.convertDocDir, module, nameSpace, true, true);
+ getDocFilePath(docFilePath, app.outputDocDir, module, nameSpace, true, false);
+
+ if(FileExists(oldDocFilePath))
+ {
+ if(FileExists(docFilePath).isDirectory)
+ {
+ char writeTestFilePath[MAX_LOCATION];
+ File f;
+ sprintf(writeTestFilePath, "%s/_", docFilePath);
+ f = FileOpen(writeTestFilePath, write);
+ PrintLn("Info: Directory exists for eCdoc in eCon format. Conversion is not required. Proceeding anyway.");
+ if(f)
+ {
+ delete f;
+ DeleteFile(writeTestFilePath);
+ readOnly = false;
+ }
+ else
+ {
+ readOnly = true;
+ PrintLn("Error: Directory for eCdoc in eCon format is not writable.");
+ }
+ }
+ else
+ {
+ MakeDir(docFilePath);
+ if(FileExists(docFilePath).isDirectory)
+ {
+ readOnly = false;
+ }
+ else
+ {
+ readOnly = true;
+ PrintLn("Error: Unable to create directory for eCdoc conversion to eCon.");
+ }
+ }
+
+ if(!readOnly)
+ {
+ char fileName[MAX_LOCATION];
+ DocConvertIterator fsi
+ {
+ bool onFolder(const char * folderPath, const char * folderName)
+ {
+ DocConvertLocType type = typeStack.lastIterator.data;
+ if(getNext)
+ {
+ if(type == namespaces)
+ {
+ PathCatSlash(nsDir, folderName);
+ namespaces.Add((ns = { namespaceDoc = { name = CopyString(folderName) }, nsPath = CopyString(nsDir) }));
+ nsStack.Add(ns);
+ }
+ else if(type == functions)
+ {
+ functionDoc = FunctionDoc { };
+ if(!ns.namespaceDoc.functions)
+ ns.namespaceDoc.functions = { };
+ ns.namespaceDoc.functions[folderName] = functionDoc;
+ }
+ else if(type == classes)
+ {
+ classDoc = ClassDoc { name = CopyString(folderName) };
+ ns.classes.Add(classDoc);
+ }
+ else if(type == methods)
+ {
+ methodDoc = MethodDoc { };
+ if(!classDoc.methods)
+ classDoc.methods = { };
+ classDoc.methods[folderName] = methodDoc;
+ }
+ else
+ sflnprintln("what?");
+ nameStack.Add(CopyString(folderName));
+ getNext = false;
+ if(!strcmp(folderName, "namespaces"))
+ sflnprintln("what?");
+ else if(!strcmp(folderName, "functions"))
+ sflnprintln("what?");
+ else if(!strcmp(folderName, "parameters"))
+ sflnprintln("what?");
+ else if(!strcmp(folderName, "classes"))
+ sflnprintln("what?");
+ else if(!strcmp(folderName, "enumeration values"))
+ sflnprintln("what?");
+ else if(!strcmp(folderName, "data members"))
+ sflnprintln("what?");
+ else if(!strcmp(folderName, "properties"))
+ sflnprintln("what?");
+ else if(!strcmp(folderName, "conversions"))
+ sflnprintln("what?");
+ else if(!strcmp(folderName, "methods"))
+ sflnprintln("what?");
+ }
+ else if((type == root || type == namespaces) && !strcmp(folderName, "namespaces"))
+ addType(namespaces);
+ else if((type == root || type == namespaces) && !strcmp(folderName, "functions"))
+ addType(functions);
+ else if((type == functions || type == methods) && !strcmp(folderName, "parameters"))
+ addType(parameters);
+ else if((type == root || type == namespaces) && !strcmp(folderName, "classes"))
+ addType(classes);
+ else if(type == classes && !strcmp(folderName, "enumeration values"))
+ addType(values);
+ else if(type == classes && !strcmp(folderName, "data members"))
+ addType(fields);
+ else if(type == classes && !strcmp(folderName, "properties"))
+ addType(properties);
+ else if(type == classes && !strcmp(folderName, "conversions"))
+ addType(conversions);
+ else if(type == classes && !strcmp(folderName, "methods"))
+ addType(methods);
+ return true;
+ }
+
+ void outFolder(const char * folderPath, const char * folderName, bool isRoot)
+ {
+ DocConvertLocType type = typeStack.lastIterator.data;
+ if(!isRoot)
+ {
+ if(!strcmp(folderName, "namespaces"))
+ exitType();
+ else if(!strcmp(folderName, "functions"))
+ exitType();
+ else if(!strcmp(folderName, "parameters"))
+ exitType();
+ else if(!strcmp(folderName, "classes"))
+ exitType();
+ else if(!strcmp(folderName, "enumeration values"))
+ exitType();
+ else if(!strcmp(folderName, "data members"))
+ exitType();
+ else if(!strcmp(folderName, "properties"))
+ exitType();
+ else if(!strcmp(folderName, "conversions"))
+ exitType();
+ else if(!strcmp(folderName, "methods"))
+ exitType();
+ else if(!strcmp(folderName, nameStack.lastIterator.data))
+ {
+ if(type == namespaces)
+ {
+ StripLastDirectory(nsDir, nsDir);
+ nsStack.Remove(nsStack.lastIterator.pointer);
+ ns = nsStack.lastIterator.data;
+ }
+ delete nameStack.lastIterator.data;
+ nameStack.Remove(nameStack.lastIterator.pointer);
+ getNext = true;
+ }
+ }
+ }
+
+ bool onFile(const char * filePath, const char * fileName)
+ {
+ DocConvertLocType type = typeStack.lastIterator.data;
+ if(!strstr(fileName, ".eCdoc"))
+ {
+ String doc = ConvertReadDoc(filePath);
+ if((type == root || type == namespaces || type == classes || type == methods || type == functions) && !strcmp(fileName, "description"))
+ {
+ if(type == root || type == namespaces) { ns.namespaceDoc.description = doc; doc = null; }
+ else if(type == classes) { classDoc.description = doc; doc = null; }
+ else if(type == methods) { methodDoc.description = doc; doc = null; }
+ else if(type == functions) { functionDoc.description = doc; doc = null; }
+ }
+ else if((type == classes || type == methods || type == functions) && !strcmp(fileName, "usage"))
+ {
+ if(type == classes) { classDoc.usage = doc; doc = null; }
+ else if(type == methods) { methodDoc.usage = doc; doc = null; }
+ else if(type == functions) { functionDoc.usage = doc; doc = null; }
+ }
+ else if((type == classes || type == methods || type == functions) && !strcmp(fileName, "example"))
+ {
+ if(type == classes) { classDoc.example = doc; doc = null; }
+ else if(type == methods) { methodDoc.example = doc; doc = null; }
+ else if(type == functions) { functionDoc.example = doc; doc = null; }
+ }
+ else if((type == classes || type == methods || type == functions) && !strcmp(fileName, "remarks"))
+ {
+ if(type == classes) { classDoc.remarks = doc; doc = null; }
+ else if(type == methods) { methodDoc.remarks = doc; doc = null; }
+ else if(type == functions) { functionDoc.remarks = doc; doc = null; }
+ }
+ else if((type == classes || type == methods || type == functions) && !strcmp(fileName, "seeAlso"))
+ {
+ if(type == classes) { classDoc.also = doc; doc = null; }
+ else if(type == methods) { methodDoc.also = doc; doc = null; }
+ else if(type == functions) { functionDoc.also = doc; doc = null; }
+ }
+ else if((type == methods || type == functions) && !strcmp(fileName, "returnValue"))
+ {
+ if(type == methods) { methodDoc.returnValue = doc; doc = null; }
+ else if(type == functions) { functionDoc.returnValue = doc; doc = null; }
+ }
+ else
+ {
+ if(type == parameters)
+ {
+ uint pos;
+ char * s;
+ char name[MAX_FILENAME];
+ ParameterDoc parameterDoc;
+ DocConvertLocType parentType = typeStack[typeStack.count-2];
+ strcpy(name, fileName);
+ s = strstr(name, ".");
+ if(s)
+ {
+ *s = 0;
+ s++;
+ pos = atoi(s);
+ }
+ else
+ sflnprintln("what?");
+ if(parentType == functions)
+ {
+ parameterDoc = ParameterDoc { description = doc, position = pos };
+ doc = null;
+ if(!functionDoc.parameters)
+ functionDoc.parameters = { };
+ functionDoc.parameters[name] = parameterDoc;
+ }
+ else if(parentType == methods)
+ {
+ parameterDoc = ParameterDoc { description = doc, position = pos };
+ doc = null;
+ if(!methodDoc.parameters)
+ methodDoc.parameters = { };
+ methodDoc.parameters[name] = parameterDoc;
+ }
+ else
+ sflnprintln("what?");
+ }
+ else if(type == values)
+ {
+ ValueDoc valueDoc { description = doc };
+ doc = null;
+ if(!classDoc.values)
+ classDoc.values = { };
+ classDoc.values[fileName] = valueDoc;
+ }
+ else if(type == fields)
+ {
+ FieldDoc fieldDoc { description = doc };
+ doc = null;
+ if(!classDoc.fields)
+ classDoc.fields = { };
+ classDoc.fields[fileName] = fieldDoc;
+ }
+ else if(type == properties)
+ {
+ PropertyDoc propertyDoc { description = doc };
+ doc = null;
+ if(!classDoc.properties)
+ classDoc.properties = { };
+ classDoc.properties[fileName] = propertyDoc;
+ }
+ else if(type == conversions)
+ {
+ ConversionDoc conversionDoc { description = doc };
+ doc = null;
+ if(!classDoc.conversions)
+ classDoc.conversions = { };
+ classDoc.conversions[fileName] = conversionDoc;
+ }
+ else
+ sflnprintln("what?");
+ }
+ delete doc;
+ }
+ return true;
+ }
+ };
+ fsi.nsDir[0] = '\0';
+ PathCatSlash(fsi.nsDir, docFilePath);
+ fsi.typeStack.Add(root);
+ fsi.namespaces.Add((fsi.ns = { namespaceDoc = { }, nsPath = CopyString(fsi.nsDir) }));
+ fsi.nsStack.Add(fsi.ns);
+ sprintf(fileName, "<%s>", oldDocFilePath);
+ fsi.iterate(fileName);
+ for(ns : fsi.namespaces)
+ {
+ writeNamespaceDocFile(ns.namespaceDoc, ns.nsPath);
+ for(c : ns.classes)
+ {
+ writeClassDocFile(c, ns.nsPath);
+ }
+ }
+ }
+ }
+ else
+ {
+ PrintLn("Info: old eCdoc format file (", oldDocFilePath, ") does not exist.");
+ }
+
+ for(m = module.modules.first; m; m = m.next)
+ {
+ if(m.importMode == publicAccess || !isDll)
+ ConvertModuleDoc(m.module, true);
+ }
+}
+
+void getDocFilePath(char * docFilePath, char * docDir, Module module, NameSpace * ns, bool includeDir, bool old)
+{
+ sprintf(docFilePath, old ? "%s%s%s.eCdoc" : "%s%s%s", includeDir ? docDir : "", includeDir ? "/" : "",
+ (!module || !module.name ||
+ (ns && ns->name && !strcmp(ns->name, "namespaces/ecere/namespaces/com"))) ? "ecereCOM" : module.name);
+}
+
+static void writeNamespaceDocFile(NamespaceDoc namespaceDoc, const char * path)
+{
+ if(!namespaceDoc.isEmpty)
+ {
+ char * filePath = new char[MAX_LOCATION];
+ File f;
+ strcpy(filePath, path);
+ PathCatSlash(filePath, "_global-defs");
+ ChangeExtension(filePath, "econ", filePath);
+ MakeDir(path);
+ DeleteFile(filePath);
+ f = FileOpen(filePath, write);
+ if(f)
+ {
+ WriteJSONObject(f, class(NamespaceDoc), namespaceDoc, 0, true);
+ delete f;
+ }
+ else
+ {
+ PrintLn("error: writeNamespaceDocFile -- problem opening file: ", filePath);
+ }
+ delete filePath;
+ }
+}
+
+static void writeClassDocFile(ClassDoc classDoc, const char * path)
+{
+ if(!classDoc.isEmpty)
+ {
+ char * name = getDocFileNameFromTypeName(classDoc.name);
+ char * filePath = new char[MAX_LOCATION];
+ File f;
+ strcpy(filePath, path);
+ PathCatSlash(filePath, name);
+ ChangeExtension(filePath, "econ", filePath);
+ DeleteFile(filePath);
+ f = FileOpen(filePath, write);
+ if(f)
+ {
+ WriteJSONObject(f, class(ClassDoc), classDoc, 0, true);
+ delete f;
+ }
+ else
+ {
+ PrintLn("error: writeClassDocFile -- problem opening file: ", filePath);
+ }
+ delete name;
+ delete filePath;
+ }
+}
+
+Application componentsApp;
+
+class Convertor : Application
+{
+ char * moduleName;;
+ char * convertDocDir;
+ char * outputDocDir;
+
+ bool Init()
+ {
+ SetGlobalContext(globalContext);
+ SetExcludedSymbols(&excludedSymbols);
+ SetDefines(&::defines);
+ SetImports(&imports);
+ SetInDocumentor(true);
+
+ SetGlobalData(globalData);
+
+ if(argc == 3 || argc == 4)
+ {
+ convertDocDir = CopyString(argv[1]);
+ outputDocDir = CopyString(argv[2]);
+ if(argc == 4)
+ moduleName = CopyString(argv[3]);
+ else
+ moduleName = CopyString("ecere");
+ return true;
+ }
+ return false;
+ }
+
+ void Main()
+ {
+ if(Init())
+ {
+ OpenModule(moduleName);
+ }
+ else
+ {
+ PrintLn($"Syntax: exename <old doc dir> <new doc dir> [<module name>]");
+ }
+ system("pause");
+ }
+
+ void Terminate()
+ {
+ delete moduleName;
+ delete convertDocDir;
+ delete outputDocDir;
+
+ FreeContext(globalContext);
+ FreeExcludedSymbols(excludedSymbols);
+ ::defines.Free(FreeModuleDefine);
+ imports.Free(FreeModuleImport);
+
+ FreeGlobalData(globalData);
+ FreeTypeData(componentsApp);
+ FreeIncludeFiles();
+ delete componentsApp;
+ }
+
+ void OpenModule(const char * filePath)
+ {
+ char moduleName[MAX_LOCATION];
+ char extension[MAX_EXTENSION];
+ Module module = null;
+ static char symbolsDir[MAX_LOCATION];
+
+ FreeContext(globalContext);
+ FreeExcludedSymbols(excludedSymbols);
+ ::defines.Free(FreeModuleDefine);
+ imports.Free(FreeModuleImport);
+
+ FreeGlobalData(globalData);
+ FreeIncludeFiles();
+ if(componentsApp)
+ {
+ FreeTypeData(componentsApp);
+ delete componentsApp;
+ }
+
+ componentsApp = __ecere_COM_Initialize(false, 1, null);
+ SetPrivateModule(componentsApp);
+
+ StripLastDirectory(filePath, symbolsDir);
+ SetSymbolsDir(symbolsDir);
+
+ GetExtension(filePath, extension);
+
+ ImportModule(filePath, normalImport, publicAccess, false);
+
+ if(extension[0] && strcmpi(extension, "so") && strcmpi(extension, "dll") && strcmpi(extension, "dylib"))
+ componentsApp.name = CopyString(filePath);
+
+ for(module = componentsApp.allModules.first; module; module = module.next)
+ {
+ if(module.name && (!strcmp(module.name, "ecere") || !strcmp(module.name, "ecereCOM")))
+ break;
+ }
+ if(!module)
+ eModule_LoadStrict(componentsApp, "ecereCOM", publicAccess /*privateAccess*/);
+
+ GetLastDirectory(filePath, moduleName);
+ // Extension, path and lib prefix get removed in Module::name
+ if(extension[0])
+ {
+ StripExtension(moduleName);
+ if((!strcmpi(extension, "so") || !strcmpi(extension, "dylib")) && strstr(moduleName, "lib") == moduleName)
+ {
+ int len = strlen(moduleName) - 3;
+ memmove(moduleName, moduleName + 3, len);
+ moduleName[len] = 0;
+ }
+ }
+
+ ConvertModuleDoc(componentsApp, false);
+ }
+}
+
+class DocConvertNamespaceStackFrame
+{
+ NamespaceDoc namespaceDoc { };
+ Array<ClassDoc> classes { };
+ char * nsPath;
+}
+
+enum DocConvertLocType { root, namespaces, functions, parameters, classes, values, fields, properties, conversions, methods };
+
+class DocConvertIterator : NormalFileSystemIterator
+{
+ char * nsDir;
+
+ bool getNext;
+ Array<String> nameStack { };
+ Array<DocConvertLocType> typeStack { };
+ Array<DocConvertNamespaceStackFrame> nsStack { };
+ Array<DocConvertNamespaceStackFrame> namespaces { };
+ DocConvertNamespaceStackFrame ns;
+ DefineDoc defineDoc;
+ FunctionDoc functionDoc;
+ ClassDoc classDoc;
+ MethodDoc methodDoc;
+
+ void addType(DocConvertLocType type)
+ {
+ typeStack.Add(type);
+ getNext = true;
+ }
+ void exitType()
+ {
+ typeStack.Remove(typeStack.lastIterator.pointer);
+ getNext = false;
+ }
+
+ DocConvertIterator()
+ {
+ nsDir = new char[MAX_LOCATION];
+ }
+ ~DocConvertIterator()
+ {
+ delete nsDir;
+ }
+}
+
+public class NormalFileSystemIterator : FileSystemIterator
+{
+public:
+ Array<StackFrame> stack { };
+
+ char * extensions;
+ property char * extensions { set { delete extensions; if(value) extensions = CopyString(value); } }
+
+ ~NormalFileSystemIterator()
+ {
+ delete extensions;
+ }
+
+ void iterate(const char * startPath)
+ {
+ StackFrame frame;
+ char startName[MAX_FILENAME];
+ startName[0] = '\0';
+ GetLastDirectory(startPath, startName);
+
+ onInit(startPath, startName);
+ {
+ frame = StackFrame { };
+ stack.Add(frame);
+ frame.path = CopyString(startPath);
+ frame.listing = FileListing { startPath, extensions = extensions };
+ }
+
+ if(iterateStartPath)
+ {
+ FileAttribs attribs = FileExists(startPath);
+ if(attribs.isDrive)
+ onVolume(startPath);
+ else
+ {
+ if(attribs.isDirectory)
+ onFolder(startPath, startName);
+ else if(attribs.isFile)
+ onFile(startPath, startName);
+ }
+ }
+
+ while(stack.count)
+ {
+ if(frame.listing.Find())
+ {
+ bool peek = frame.listing.stats.attribs.isDirectory && onFolder(frame.listing.path, frame.listing.name);
+ if(!frame.listing.stats.attribs.isDirectory)
+ {
+ onFile(frame.listing.path, frame.listing.name);
+ }
+ else if(peek)
+ {
+ StackFrame newFrame { };
+ stack.Add(newFrame);
+ newFrame.path = CopyString(frame.listing.path);
+ newFrame.listing = FileListing { newFrame.path, extensions = frame.listing.extensions };
+ frame = newFrame;
+ }
+ }
+ else
+ {
+ StackFrame parentFrame = stack.count > 1 ? stack[stack.count - 2] : null;
+ outFolder(parentFrame ? parentFrame.listing.path : startPath, parentFrame ? parentFrame.listing.name : startName, !parentFrame);
+ stack.lastIterator.Remove();
+ if(stack.count)
+ frame = stack.lastIterator.data;
+ else
+ frame = null;
+ }
+ }
+ }
+}
+
+public class FileSystemIterator
+{
+public:
+ bool iterateStartPath;
+
+ virtual bool onInit(const char * startPath, const char * startName)
+ {
+ return false;
+ }
+
+ virtual bool onFile(const char * filePath, const char * fileName)
+ {
+ return true;
+ }
+
+ virtual bool onFolder(const char * folderPath, const char * folderName)
+ {
+ return true;
+ }
+
+ virtual bool onVolume(const char * volumePath)
+ {
+ return true;
+ }
+
+ virtual void outFolder(const char * folderPath, const char * folderName, bool isRoot)
+ {
+ }
+}
+
+public class StackFrame
+{
+ int tag;
+ char * path;
+ FileListing listing;
+
+ ~StackFrame()
+ {
+ delete path;
+ }
+};