mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-15 22:02:49 -06:00
511 lines
12 KiB
C++
511 lines
12 KiB
C++
#include "Docedit.h"
|
|
|
|
bool DocKey::operator==(const DocKey& b) const
|
|
{
|
|
return nameing == b.nameing && nesting == b.nesting && item == b.item && lang == b.lang;
|
|
}
|
|
|
|
dword GetHashValue(const DocKey& k)
|
|
{
|
|
return CombineHash(GetHashValue(k.nameing), GetHashValue(k.nesting),
|
|
GetHashValue(k.item)) << k.lang;
|
|
}
|
|
|
|
int CharFilterNameing(int c)
|
|
{
|
|
return c == ':' ? '_' : c;
|
|
}
|
|
|
|
String DocFile(const String& package, const String& name)
|
|
{
|
|
return AppendFileName(SourcePath(package, "doc.dpp"), name);
|
|
}
|
|
|
|
Time GetFileTime(const char *path)
|
|
{
|
|
FindFile ff(path);
|
|
return ff ? Time(ff.GetLastWriteTime()) : Null;
|
|
}
|
|
|
|
void CreateDocDir(const String& package)
|
|
{
|
|
::CreateDirectory(SourcePath(package, "doc.dpp"), 0);
|
|
}
|
|
|
|
String AsCode(const DocKey& k)
|
|
{
|
|
return AsCString(k.nameing) + ", " + AsCString(k.nesting) + ", " +
|
|
AsCString(k.item) + ", " + AsCString(LNGAsText(k.lang));
|
|
}
|
|
|
|
bool ReadCode(CParser& p, DocKey& key)
|
|
{
|
|
if(!p.IsString()) return false;
|
|
key.nameing = p.ReadString();
|
|
if(!p.Char(',') || !p.IsString()) return false;
|
|
key.nesting = p.ReadString();
|
|
if(!p.Char(',') || !p.IsString()) return false;
|
|
key.item = p.ReadString();
|
|
if(!p.Char(',') || !p.IsString()) return false;
|
|
key.lang = LNGFromText(p.ReadString());
|
|
return true;
|
|
}
|
|
|
|
void DocDir::SaveLinks(const String& package) const
|
|
{
|
|
CreateDocDir(package);
|
|
FileOut out(DocFile(package, "links"));
|
|
int q = dir.Find(package);
|
|
if(q >= 0) {
|
|
const ArrayMap<DocKey, Entry>& p = dir[q];
|
|
for(int i = 0; i < p.GetCount(); i++) {
|
|
const Entry& w = p[i];
|
|
const DocKey& k = p.GetKey(i);
|
|
if(w.type == LINK)
|
|
out << "LINK(" << AsCode(k) << ", " << AsCString(w.text) << ")\r\n";
|
|
if(w.type == IGNORED)
|
|
out << "IGNORED(" << AsCode(k) << ")\r\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
void DocDir::SaveDir(const String& package) const
|
|
{
|
|
CreateDocDir(package);
|
|
FileOut out(DocFile(package, "dir.h"));
|
|
int q = dir.Find(package);
|
|
if(q >= 0) {
|
|
const ArrayMap<DocKey, Entry>& p = dir[q];
|
|
for(int i = 0; i < p.GetCount(); i++) {
|
|
const Entry& w = p[i];
|
|
if(w.type == NORMAL || w.type == EXTERNAL) {
|
|
const DocKey& k = p.GetKey(i);
|
|
out << "#ifdef INCLUDE_NAMESPACE_" <<
|
|
Filter(k.nameing, CharFilterNameing) << "\r\n";
|
|
out << "//" << k.item << "\r\n";
|
|
out << "//" << k.nesting << "\r\n";
|
|
out << "//" << k.nameing << "\r\n";
|
|
out << "#include \"doc.dpp/";
|
|
out << w.text;
|
|
out << "\" //";
|
|
if(w.type == EXTERNAL)
|
|
out << "*";
|
|
out << LNGAsText(k.lang) << "\r\n";
|
|
out << "#endif\r\n\r\n";
|
|
}
|
|
}
|
|
}
|
|
SaveLinks(package);
|
|
}
|
|
|
|
bool DocDir::LoadLinks(const String& package)
|
|
{
|
|
String f = LoadFile(DocFile(package, "links"));
|
|
CParser p(f);
|
|
ArrayMap<DocKey, Entry>& pk = dir.GetAdd(package);
|
|
try {
|
|
while(!p.IsEof()) {
|
|
if(p.Id("LINK")) {
|
|
DocKey key;
|
|
p.PassChar('(');
|
|
if(!ReadCode(p, key)) return false;
|
|
p.PassChar(',');
|
|
String link = p.ReadString();
|
|
p.PassChar(')');
|
|
Entry& e = pk.GetAdd(key);
|
|
e.text = link;
|
|
e.type = LINK;
|
|
}
|
|
if(p.Id("IGNORED")) {
|
|
DocKey key;
|
|
p.PassChar('(');
|
|
if(!ReadCode(p, key)) return false;
|
|
p.PassChar(')');
|
|
Entry& e = pk.GetAdd(key);
|
|
e.text.Clear();
|
|
e.type = IGNORED;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
catch(CParser::Error) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool DocDir::LoadDir(const String& package)
|
|
{
|
|
ArrayMap<DocKey, Entry>& p = dir.GetAdd(package);
|
|
p.Clear();
|
|
LoadLinks(package);
|
|
FileIn in(DocFile(package, "dir.h"));
|
|
int q = 0;
|
|
String d[3];
|
|
while(!in.IsEof()) {
|
|
String l = in.GetLine();
|
|
if(memcmp(l, "#include ", 9) == 0) {
|
|
const char *b = strchr(l, '/');
|
|
if(!b) return false;
|
|
const char *e = strchr(b + 1, '\"');
|
|
if(!e) return false;
|
|
String fn = String(b + 1, e);
|
|
e = strchr(e, '/');
|
|
if(!e || e[1] != '/') return false;
|
|
e += 2;
|
|
int type = NORMAL;
|
|
if(*e == '*') {
|
|
type = EXTERNAL;
|
|
e++;
|
|
}
|
|
Entry& w = p.GetAdd(DocKey(d[2], d[1], d[0], LNGFromText(e)));
|
|
w.text = fn;
|
|
w.type = type;
|
|
q = 0;
|
|
}
|
|
if(l[0] == '/' && l[1] == '/') {
|
|
if(q >= 3) return false;
|
|
d[q] = l.Mid(2);
|
|
q++;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int DocDir::ReadDocHeader(const char *filename, DocKey& key)
|
|
{
|
|
FileIn in(filename);
|
|
if(!in) return -1;
|
|
String l = in.GetLine();
|
|
CParser p(l);
|
|
if(!p.Id("ITEM") || !p.Char('(') || !ReadCode(p, key)) return -1;
|
|
p.Char(')');
|
|
if(p.Id("EXTERNAL"))
|
|
return EXTERNAL;
|
|
return NORMAL;
|
|
}
|
|
|
|
void DocDir::RebuildDir(const String& package)
|
|
{
|
|
FindFile ff(DocFile(package, "*.dpp"));
|
|
Progress pi("Rebuilding " + package + " doc directory %d");
|
|
ArrayMap<DocKey, Entry>& p = dir.GetAdd(package);
|
|
p.Clear();
|
|
LoadLinks(package);
|
|
while(ff) {
|
|
DocKey key;
|
|
pi.Step();
|
|
int q = ReadDocHeader(DocFile(package, ff.GetName()), key);
|
|
if(q >= 0) {
|
|
Entry& w = p.GetAdd(key);
|
|
w.text = ff.GetName();
|
|
w.type = q;
|
|
}
|
|
ff.Next();
|
|
}
|
|
SaveDir(package);
|
|
}
|
|
|
|
void DocDir::Refresh(const String& package)
|
|
{
|
|
FindFile ff(DocFile(package, "dir.h"));
|
|
if(!ff) {
|
|
RebuildDir(package);
|
|
return;
|
|
}
|
|
FileTime dirtime = ff.GetLastWriteTime();
|
|
ff.Search(DocFile(package, "links"));
|
|
if(ff && ff.GetLastWriteTime() > dirtime) {
|
|
RebuildDir(package);
|
|
return;
|
|
}
|
|
const ArrayMap<DocKey, Entry>& p = dir.GetAdd(package);
|
|
Index<String> dfn;
|
|
for(int i = 0; i < p.GetCount(); i++)
|
|
if(p[i].type == NORMAL || p[i].type == EXTERNAL)
|
|
dfn.Add(p[i].text);
|
|
ff.Search(DocFile(package, "*.dpp"));
|
|
int count = 0;
|
|
while(ff) {
|
|
DocKey key;
|
|
if(dfn.Find(ff.GetName()) >= 0)
|
|
if(ff.GetLastWriteTime() > dirtime) {
|
|
RebuildDir(package);
|
|
return;
|
|
}
|
|
else
|
|
count++;
|
|
else
|
|
if(ReadDocHeader(DocFile(package, ff.GetName()), key) >= 0) {
|
|
RebuildDir(package);
|
|
return;
|
|
}
|
|
ff.Next();
|
|
}
|
|
if(count != dfn.GetCount())
|
|
RebuildDir(package);
|
|
}
|
|
|
|
int CharFilterLNG_(int c)
|
|
{
|
|
return c == '-' ? '_' : c;
|
|
}
|
|
|
|
String DocDir::GetAddFileName(const String& package, const DocKey& key, int type)
|
|
{
|
|
Entry& w = dir.GetAdd(package).GetAdd(key);
|
|
String& fn = w.text;
|
|
w.type = type;
|
|
if(!IsEmpty(fn)) return fn;
|
|
String nm = key.nameing + '_' + key.nesting + '_' + key.item;
|
|
String n;
|
|
const char *s = nm;
|
|
while(*s && n.GetLength() < 30)
|
|
if(iscid(*s))
|
|
n.Cat(*s++);
|
|
else {
|
|
n.Cat('_');
|
|
while(*s && !iscid(*s))
|
|
s++;
|
|
}
|
|
n << '_' << LNGAsText(key.lang);
|
|
int i = 0;
|
|
for(;;) {
|
|
fn = n + FormatIntAlpha(i) + ".dpp";
|
|
if(!FindFile(DocFile(package, fn)))
|
|
return fn;
|
|
i++;
|
|
}
|
|
}
|
|
|
|
bool DocDir::GetFileName(const DocKey& key, String& fn, String& package)
|
|
{
|
|
dword hv = GetHashValue(key);
|
|
for(int i = 0; i < dir.GetCount(); i++) {
|
|
const ArrayMap<DocKey, Entry>& p = dir[i];
|
|
int q = p.Find(key, hv);
|
|
if(q >= 0) {
|
|
fn = p[q].text;
|
|
package = dir.GetKey(i);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void DocDir::RemoveOtherKey(const DocKey& key, int t1, int t2)
|
|
{
|
|
dword hv = GetHashValue(key);
|
|
for(int i = 0; i < dir.GetCount(); i++) {
|
|
int q = dir[i].Find(key, hv);
|
|
if(q >= 0) {
|
|
Entry& e = dir[i][q];
|
|
if(e.type != t1 && e.type != t2) {
|
|
if(e.type == NORMAL || e.type == EXTERNAL)
|
|
DeleteFile(DocFile(dir.GetKey(i), dir[i][q].text));
|
|
dir[i].Remove(q);
|
|
}
|
|
SaveDir(dir.GetKey(i));
|
|
SaveLinks(dir.GetKey(i));
|
|
}
|
|
}
|
|
}
|
|
|
|
void DocDir::Remove(const DocKey& k)
|
|
{
|
|
RemoveOtherKey(k, UNDOCUMENTED, UNDOCUMENTED);
|
|
}
|
|
|
|
void DocDir::SaveText(const String& package, const DocKey& k, const String& text, bool external)
|
|
{
|
|
RemoveOtherKey(k, NORMAL, EXTERNAL);
|
|
CreateDocDir(package);
|
|
String fn = DocFile(package, GetAddFileName(package, k, external ? EXTERNAL : NORMAL));
|
|
FileOut out(fn);
|
|
out << "ITEM(" << AsCode(k) << ")";
|
|
if(external)
|
|
out << " EXTERNAL";
|
|
out << "\r\n";
|
|
int n = 0;
|
|
for(;;) {
|
|
int m = min(text.GetLength() - n, 2048);
|
|
if(m == 0) break;
|
|
out << "TEXT(\r\n\t";
|
|
out << AsCString(~text + n, ~text + n + m, 64, "\t") << "\r\n";
|
|
out << ")\r\n";
|
|
n += m;
|
|
}
|
|
out << "END_ITEM\r\n";
|
|
SaveDir(package);
|
|
SaveLinks(package);
|
|
}
|
|
|
|
void DocDir::SaveLink(const String& package, const DocKey& key, const String& text)
|
|
{
|
|
RemoveOtherKey(key, LINK);
|
|
Entry& w = dir.GetAdd(package).GetAdd(key);
|
|
w.text = text;
|
|
w.type = LINK;
|
|
SaveLinks(package);
|
|
}
|
|
|
|
void DocDir::Ignore(const String& package, const DocKey& key)
|
|
{
|
|
RemoveOtherKey(key, IGNORED);
|
|
Entry& w = dir.GetAdd(package).GetAdd(key);
|
|
w.type = IGNORED;
|
|
SaveLinks(package);
|
|
}
|
|
|
|
DocDir::Entry *DocDir::Find(const DocKey& key, String& package) const
|
|
{
|
|
dword hv = GetHashValue(key);
|
|
for(int i = 0; i < dir.GetCount(); i++) {
|
|
int q = dir[i].Find(key, hv);
|
|
if(q >= 0) {
|
|
package = dir.GetKey(i);
|
|
return const_cast<Entry *>(&dir[i][q]);
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
DocDir::Entry *DocDir::Find(const DocKey& key) const
|
|
{
|
|
String dm;
|
|
return Find(key, dm);
|
|
}
|
|
|
|
int DocDir::GetStatus(const DocKey& key)
|
|
{
|
|
DocDir::Entry *e = Find(key);
|
|
return e ? e->type : UNDOCUMENTED;
|
|
}
|
|
|
|
int DocDir::GetStatus(const String& nameing, const String& nesting, const String& item,
|
|
int lang)
|
|
{
|
|
return GetStatus(DocKey(nameing, nesting, item, lang));
|
|
}
|
|
|
|
String DocDir::GetFilePath(const DocKey& key) const
|
|
{
|
|
String package;
|
|
Entry *e = Find(key, package);
|
|
if(!e || e->type != NORMAL && e->type != EXTERNAL) return Null;
|
|
return DocFile(package, e->text);
|
|
}
|
|
|
|
String DocDir::GetPackage(const DocKey& key) const
|
|
{
|
|
String package;
|
|
Entry *e = Find(key, package);
|
|
return e ? package : Null;
|
|
}
|
|
|
|
String DocDir::GetText(const DocKey& key) const
|
|
{
|
|
String s = LoadFile(GetFilePath(key));
|
|
if(s.IsEmpty()) return Null;
|
|
CParser p(s);
|
|
DocKey dummy;
|
|
if(!p.Id("ITEM") || !p.Char('(') || !ReadCode(p, dummy)) return "Invalid file";
|
|
p.Char(')');
|
|
p.Id("EXTERNAL");
|
|
String text;
|
|
while(p.Id("TEXT")) {
|
|
p.Char('(');
|
|
if(p.IsString())
|
|
text.Cat(p.ReadString());
|
|
else
|
|
return "Invalid file";
|
|
p.Char(')');
|
|
}
|
|
return p.Id("END_ITEM") ? text : "Invalid file";
|
|
}
|
|
|
|
String DocDir::GetLink(const DocKey& key) const
|
|
{
|
|
DocDir::Entry *e = Find(key);
|
|
if(!e) return Null;
|
|
return e->type == LINK ? e->text : Null;
|
|
}
|
|
|
|
bool Contains(const DocSet& r, const DocKey& key)
|
|
{
|
|
int q = r.Find(key.nameing);
|
|
if(q < 0) return false;
|
|
int w = r[q].Find(key.nesting);
|
|
if(w < 0) return false;
|
|
return r[q][w].Find(key.item) >= 0;
|
|
}
|
|
|
|
bool Contains(const String& big, const String& part)
|
|
{
|
|
if(part.GetLength() > big.GetLength()) return false;
|
|
const char *s = big;
|
|
const char *e = s + big.GetLength() - part.GetLength();
|
|
int l = part.GetLength();
|
|
while(s <= e) {
|
|
if(memcmp(s, part, l) == 0)
|
|
return true;
|
|
s++;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
DocSet DocDir::Select(const DocQuery& query)
|
|
{
|
|
DocSet r;
|
|
CppBase base;
|
|
int i;
|
|
for(i = 0; i < doc_base.GetCount(); i++) {
|
|
String nameing = doc_base.GetKey(i);
|
|
CppNamespace& mm = doc_base[i];
|
|
for(int i = 0; i < mm.GetCount(); i++) {
|
|
String nesting = mm.GetKey(i);
|
|
CppNest& nn = mm[i];
|
|
for(int i = 0; i < nn.GetCount(); i++) {
|
|
CppItem& q = nn[i];
|
|
String item = nn.GetKey(i);
|
|
if((query.name.IsEmpty() || query.name == q.name) &&
|
|
(query.text.IsEmpty() || Contains(item, query.text)) &&
|
|
(query.package.IsEmpty() || q.package.CompareNoCase(query.package) == 0) &&
|
|
(query.header.IsEmpty() || q.file.CompareNoCase(query.header) == 0)) {
|
|
String pk;
|
|
const Entry *e = Find(DocKey(nameing, nesting, item, query.lang), pk);
|
|
int st = e ? e->type : UNDOCUMENTED;
|
|
if((query.undocumented || e) && (st != IGNORED || query.ignored)) {
|
|
DocItem& a = r.GetAdd(nameing).GetAdd(nesting).GetAdd(item);
|
|
a.cppitem = &q;
|
|
a.item = item;
|
|
a.status = st;
|
|
a.package = e ? pk : q.package;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for(i = 0; i < dir.GetCount(); i++) {
|
|
ArrayMap<DocKey, Entry>& pk = dir[i];
|
|
String package = dir.GetKey(i);
|
|
for(int j = 0; j < pk.GetCount(); j++) {
|
|
const DocKey& k = pk.GetKey(j);
|
|
if(k.lang == query.lang &&
|
|
query.name.IsEmpty() &&
|
|
(query.text.IsEmpty() || Contains(k.item, query.text)) &&
|
|
(query.package.IsEmpty() || package.CompareNoCase(query.package) == 0) &&
|
|
query.header.IsEmpty() &&
|
|
!Contains(r, k)) {
|
|
DocItem& a = r.GetAdd(k.nameing).GetAdd(k.nesting).GetAdd(k.item);
|
|
a.cppitem = NULL;
|
|
a.item = k.item;
|
|
int st = pk[j].type;
|
|
a.status = st == NORMAL ? OBSOLETE : st == LINK ? OBSOLETELINK : st;
|
|
a.package = package;
|
|
}
|
|
}
|
|
}
|
|
return r;
|
|
}
|
|
|
|
DocDir doc_dir;
|