Drastic performance increase. Helps address #108

This commit is contained in:
Hackerpilot 2014-02-07 01:15:49 -08:00
parent 2a812d372b
commit e6c03b6610
6 changed files with 272 additions and 245 deletions

262
actypes.d
View File

@ -26,6 +26,12 @@ import std.array;
import messages; import messages;
import std.array; import std.array;
import std.typecons; import std.typecons;
import std.container;
bool comparitor(const(ACSymbol)* a, const(ACSymbol)* b) pure nothrow
{
return a.name < b.name;
}
/** /**
* Any special information about a variable declaration symbol. * Any special information about a variable declaration symbol.
@ -49,6 +55,8 @@ struct ACSymbol
{ {
public: public:
@disable this();
/** /**
* Params: * Params:
* name = the symbol's name * name = the symbol's name
@ -56,6 +64,7 @@ public:
this(string name) this(string name)
{ {
this.name = name; this.name = name;
this.parts = new RedBlackTree!(ACSymbol*, comparitor, true);
} }
/** /**
@ -67,6 +76,7 @@ public:
{ {
this.name = name; this.name = name;
this.kind = kind; this.kind = kind;
this.parts = new RedBlackTree!(ACSymbol*, comparitor, true);
} }
/** /**
@ -75,51 +85,29 @@ public:
* kind = the symbol's completion kind * kind = the symbol's completion kind
* resolvedType = the resolved type of the symbol * resolvedType = the resolved type of the symbol
*/ */
this(string name, CompletionKind kind, const(ACSymbol)* type) this(string name, CompletionKind kind, ACSymbol* type)
{ {
this.name = name; this.name = name;
this.kind = kind; this.kind = kind;
this.type = type; this.type = type;
this.parts = new RedBlackTree!(ACSymbol*, comparitor, true);
} }
/**
* Comparison operator sorts based on the name field
*/
int opCmp(string str) const
{
if (str < this.name) return -1;
if (str > this.name) return 1;
return 0;
}
/// ditto
int opCmp(const(ACSymbol)* other) const
{
return this.opCmp(other.name);
}
/** /**
* Gets all parts whose name matches the given string. * Gets all parts whose name matches the given string.
*/ */
const(ACSymbol)*[] getPartsByName(string name) const ACSymbol*[] getPartsByName(string name)
{ {
return cast(typeof(return)) parts.filter!(a => a.name == name).array; import std.range;
} ACSymbol s = ACSymbol(name);
return parts.equalRange(&s).array();
size_t estimateMemory(size_t runningTotal) const
{
runningTotal = runningTotal + name.length + callTip.length
+ ACSymbol.sizeof;
foreach (part; parts)
runningTotal = part.estimateMemory(runningTotal);
return runningTotal;
} }
/** /**
* Symbols that compose this symbol, such as enum members, class variables, * Symbols that compose this symbol, such as enum members, class variables,
* methods, etc. * methods, etc.
*/ */
const(ACSymbol)*[] parts; RedBlackTree!(ACSymbol*, comparitor, true) parts;
/** /**
* Symbol's name * Symbol's name
@ -144,7 +132,7 @@ public:
/** /**
* The symbol that represents the type. * The symbol that represents the type.
*/ */
const(ACSymbol)* type; ACSymbol* type;
/** /**
* Symbol location * Symbol location
@ -164,6 +152,13 @@ public:
struct Scope struct Scope
{ {
this (size_t begin, size_t end)
{
this.startLocation = begin;
this.endLocation = end;
this.symbols = new RedBlackTree!(ACSymbol*, comparitor, true);
}
Scope* getScopeByCursor(size_t cursorPosition) const Scope* getScopeByCursor(size_t cursorPosition) const
{ {
if (cursorPosition < startLocation) return null; if (cursorPosition < startLocation) return null;
@ -177,32 +172,38 @@ struct Scope
return cast(typeof(return)) &this; return cast(typeof(return)) &this;
} }
const(ACSymbol)*[] getSymbolsInCursorScope(size_t cursorPosition) const ACSymbol*[] getSymbolsInCursorScope(size_t cursorPosition) const
{ {
auto s = getScopeByCursor(cursorPosition); auto s = getScopeByCursor(cursorPosition);
if (s is null) if (s is null)
return []; return [];
const(ACSymbol)*[] symbols = cast(typeof(return)) s.symbols; auto symbols = s.symbols;
Scope* sc = s.parent; Scope* sc = s.parent;
while (sc !is null) while (sc !is null)
{ {
symbols ~= sc.symbols; foreach (sym; sc.symbols)
symbols.insert(sym);
sc = sc.parent; sc = sc.parent;
} }
return symbols; return symbols.array();
} }
const(ACSymbol)*[] getSymbolsByName(string name) const ACSymbol*[] getSymbolsByName(string name) const
{ {
const(ACSymbol)*[] retVal = cast(typeof(return)) symbols.filter!(a => a.name == name).array(); import std.range;
if (retVal.length > 0) ACSymbol s = ACSymbol(name);
return retVal; RedBlackTree!(ACSymbol*, comparitor, true) t = cast() symbols;
auto r = t.equalRange(&s).array();
version(assert) foreach (n; r)
assert (n.name == name, name);
if (r.length > 0)
return cast(typeof(return)) r;
if (parent is null) if (parent is null)
return []; return [];
return parent.getSymbolsByName(name); return parent.getSymbolsByName(name);
} }
const(ACSymbol)*[] getSymbolsByNameAndCursor(string name, size_t cursorPosition) const ACSymbol*[] getSymbolsByNameAndCursor(string name, size_t cursorPosition) const
{ {
auto s = getScopeByCursor(cursorPosition); auto s = getScopeByCursor(cursorPosition);
if (s is null) if (s is null)
@ -210,12 +211,23 @@ struct Scope
return s.getSymbolsByName(name); return s.getSymbolsByName(name);
} }
const(ACSymbol)*[] symbols; /// Imports contained in this scope
ImportInformation[] importInformation; ImportInformation[] importInformation;
/// The scope that contains this one
Scope* parent; Scope* parent;
/// Child scopes
Scope*[] children; Scope*[] children;
/// Start location of this scope in bytes
size_t startLocation; size_t startLocation;
/// End location of this scope in bytes
size_t endLocation; size_t endLocation;
/// Symbols contained in this scope
RedBlackTree!(ACSymbol*, comparitor, true) symbols;
} }
struct ImportInformation struct ImportInformation
@ -236,6 +248,12 @@ struct ImportInformation
*/ */
static this() static this()
{ {
auto bSym = new RedBlackTree!(ACSymbol*, comparitor, true);
auto arrSym = new RedBlackTree!(ACSymbol*, comparitor, true);
auto asarrSym = new RedBlackTree!(ACSymbol*, comparitor, true);
auto aggSym = new RedBlackTree!(ACSymbol*, comparitor, true);
auto clSym = new RedBlackTree!(ACSymbol*, comparitor, true);
auto bool_ = new ACSymbol("bool", CompletionKind.keyword); auto bool_ = new ACSymbol("bool", CompletionKind.keyword);
auto int_ = new ACSymbol("int", CompletionKind.keyword); auto int_ = new ACSymbol("int", CompletionKind.keyword);
auto long_ = new ACSymbol("long", CompletionKind.keyword); auto long_ = new ACSymbol("long", CompletionKind.keyword);
@ -255,46 +273,44 @@ static this()
auto stringof_ = new ACSymbol("init", CompletionKind.keyword); auto stringof_ = new ACSymbol("init", CompletionKind.keyword);
auto init = new ACSymbol("stringof", CompletionKind.keyword); auto init = new ACSymbol("stringof", CompletionKind.keyword);
arraySymbols ~= alignof_; arrSym.insert(alignof_);
arraySymbols ~= new ACSymbol("dup", CompletionKind.keyword); arrSym.insert(new ACSymbol("dup", CompletionKind.keyword));
arraySymbols ~= new ACSymbol("idup", CompletionKind.keyword); arrSym.insert(new ACSymbol("idup", CompletionKind.keyword));
arraySymbols ~= init; arrSym.insert(init);
arraySymbols ~= new ACSymbol("length", CompletionKind.keyword, ulong_); arrSym.insert(new ACSymbol("length", CompletionKind.keyword, ulong_));
arraySymbols ~= mangleof_; arrSym.insert(mangleof_);
arraySymbols ~= new ACSymbol("ptr", CompletionKind.keyword); arrSym.insert(new ACSymbol("ptr", CompletionKind.keyword));
arraySymbols ~= new ACSymbol("reverse", CompletionKind.keyword); arrSym.insert(new ACSymbol("reverse", CompletionKind.keyword));
arraySymbols ~= sizeof_; arrSym.insert(sizeof_);
arraySymbols ~= new ACSymbol("sort", CompletionKind.keyword); arrSym.insert(new ACSymbol("sort", CompletionKind.keyword));
arraySymbols ~= stringof_; arrSym.insert(stringof_);
arraySymbols.sort();
assocArraySymbols ~= alignof_; asarrSym.insert(alignof_);
assocArraySymbols ~= new ACSymbol("byKey", CompletionKind.keyword); asarrSym.insert(new ACSymbol("byKey", CompletionKind.keyword));
assocArraySymbols ~= new ACSymbol("byValue", CompletionKind.keyword); asarrSym.insert(new ACSymbol("byValue", CompletionKind.keyword));
assocArraySymbols ~= new ACSymbol("dup", CompletionKind.keyword); asarrSym.insert(new ACSymbol("dup", CompletionKind.keyword));
assocArraySymbols ~= new ACSymbol("get", CompletionKind.keyword); asarrSym.insert(new ACSymbol("get", CompletionKind.keyword));
assocArraySymbols ~= new ACSymbol("init", CompletionKind.keyword); asarrSym.insert(new ACSymbol("init", CompletionKind.keyword));
assocArraySymbols ~= new ACSymbol("keys", CompletionKind.keyword); asarrSym.insert(new ACSymbol("keys", CompletionKind.keyword));
assocArraySymbols ~= new ACSymbol("length", CompletionKind.keyword, ulong_); asarrSym.insert(new ACSymbol("length", CompletionKind.keyword, ulong_));
assocArraySymbols ~= mangleof_; asarrSym.insert(mangleof_);
assocArraySymbols ~= new ACSymbol("rehash", CompletionKind.keyword); asarrSym.insert(new ACSymbol("rehash", CompletionKind.keyword));
assocArraySymbols ~= sizeof_; asarrSym.insert(sizeof_);
assocArraySymbols ~= stringof_; asarrSym.insert(stringof_);
assocArraySymbols ~= init; asarrSym.insert(init);
assocArraySymbols ~= new ACSymbol("values", CompletionKind.keyword); asarrSym.insert(new ACSymbol("values", CompletionKind.keyword));
assocArraySymbols.sort();
foreach (s; [bool_, int_, long_, byte_, char_, dchar_, short_, ubyte_, uint_, foreach (s; [bool_, int_, long_, byte_, char_, dchar_, short_, ubyte_, uint_,
ulong_, ushort_, wchar_]) ulong_, ushort_, wchar_])
{ {
s.parts ~= new ACSymbol("init", CompletionKind.keyword, s); s.parts.insert(new ACSymbol("init", CompletionKind.keyword, s));
s.parts ~= new ACSymbol("min", CompletionKind.keyword, s); s.parts.insert(new ACSymbol("min", CompletionKind.keyword, s));
s.parts ~= new ACSymbol("max", CompletionKind.keyword, s); s.parts.insert(new ACSymbol("max", CompletionKind.keyword, s));
s.parts ~= alignof_; s.parts.insert(alignof_);
s.parts ~= sizeof_; s.parts.insert(sizeof_);
s.parts ~= stringof_; s.parts.insert(stringof_);
s.parts ~= mangleof_; s.parts.insert(mangleof_);
s.parts ~= init; s.parts.insert(init);
} }
auto cdouble_ = new ACSymbol("cdouble", CompletionKind.keyword); auto cdouble_ = new ACSymbol("cdouble", CompletionKind.keyword);
@ -312,54 +328,54 @@ static this()
foreach (s; [cdouble_, cent_, cfloat_, creal_, double_, float_, foreach (s; [cdouble_, cent_, cfloat_, creal_, double_, float_,
idouble_, ifloat_, ireal_, real_, ucent_]) idouble_, ifloat_, ireal_, real_, ucent_])
{ {
s.parts ~= alignof_; s.parts.insert(alignof_);
s.parts ~= new ACSymbol("dig", CompletionKind.keyword, s); s.parts.insert(new ACSymbol("dig", CompletionKind.keyword, s));
s.parts ~= new ACSymbol("epsilon", CompletionKind.keyword, s); s.parts.insert(new ACSymbol("epsilon", CompletionKind.keyword, s));
s.parts ~= new ACSymbol("infinity", CompletionKind.keyword, s); s.parts.insert(new ACSymbol("infinity", CompletionKind.keyword, s));
s.parts ~= new ACSymbol("init", CompletionKind.keyword, s); s.parts.insert(new ACSymbol("init", CompletionKind.keyword, s));
s.parts ~= mangleof_; s.parts.insert(mangleof_);
s.parts ~= new ACSymbol("mant_dig", CompletionKind.keyword, int_); s.parts.insert(new ACSymbol("mant_dig", CompletionKind.keyword, int_));
s.parts ~= new ACSymbol("max", CompletionKind.keyword, s); s.parts.insert(new ACSymbol("max", CompletionKind.keyword, s));
s.parts ~= new ACSymbol("max_10_exp", CompletionKind.keyword, int_); s.parts.insert(new ACSymbol("max_10_exp", CompletionKind.keyword, int_));
s.parts ~= new ACSymbol("max_exp", CompletionKind.keyword, int_); s.parts.insert(new ACSymbol("max_exp", CompletionKind.keyword, int_));
s.parts ~= new ACSymbol("min", CompletionKind.keyword, s); s.parts.insert(new ACSymbol("min", CompletionKind.keyword, s));
s.parts ~= new ACSymbol("min_exp", CompletionKind.keyword, int_); s.parts.insert(new ACSymbol("min_exp", CompletionKind.keyword, int_));
s.parts ~= new ACSymbol("min_10_exp", CompletionKind.keyword, int_); s.parts.insert(new ACSymbol("min_10_exp", CompletionKind.keyword, int_));
s.parts ~= new ACSymbol("min_normal", CompletionKind.keyword, s); s.parts.insert(new ACSymbol("min_normal", CompletionKind.keyword, s));
s.parts ~= new ACSymbol("nan", CompletionKind.keyword, s); s.parts.insert(new ACSymbol("nan", CompletionKind.keyword, s));
s.parts ~= sizeof_; s.parts.insert(sizeof_);
s.parts ~= stringof_; s.parts.insert(stringof_);
} }
aggregateSymbols ~= new ACSymbol("tupleof", CompletionKind.variableName); aggSym.insert(new ACSymbol("tupleof", CompletionKind.variableName));
aggregateSymbols ~= mangleof_; aggSym.insert(mangleof_);
aggregateSymbols ~= alignof_; aggSym.insert(alignof_);
aggregateSymbols ~= sizeof_; aggSym.insert(sizeof_);
aggregateSymbols ~= stringof_; aggSym.insert(stringof_);
aggregateSymbols ~= init; aggSym.insert(init);
classSymbols ~= new ACSymbol("classInfo", CompletionKind.variableName); clSym.insert(new ACSymbol("classInfo", CompletionKind.variableName));
classSymbols ~= new ACSymbol("tupleof", CompletionKind.variableName); clSym.insert(new ACSymbol("tupleof", CompletionKind.variableName));
classSymbols ~= new ACSymbol("__vptr", CompletionKind.variableName); clSym.insert(new ACSymbol("__vptr", CompletionKind.variableName));
classSymbols ~= new ACSymbol("__monitor", CompletionKind.variableName); clSym.insert(new ACSymbol("__monitor", CompletionKind.variableName));
classSymbols ~= mangleof_; clSym.insert(mangleof_);
classSymbols ~= alignof_; clSym.insert(alignof_);
classSymbols ~= sizeof_; clSym.insert(sizeof_);
classSymbols ~= stringof_; clSym.insert(stringof_);
classSymbols ~= init; clSym.insert(init);
ireal_.parts ~= new ACSymbol("im", CompletionKind.keyword, real_); ireal_.parts.insert(new ACSymbol("im", CompletionKind.keyword, real_));
ifloat_.parts ~= new ACSymbol("im", CompletionKind.keyword, float_); ifloat_.parts.insert(new ACSymbol("im", CompletionKind.keyword, float_));
idouble_.parts ~= new ACSymbol("im", CompletionKind.keyword, double_); idouble_.parts.insert(new ACSymbol("im", CompletionKind.keyword, double_));
ireal_.parts ~= new ACSymbol("re", CompletionKind.keyword, real_); ireal_.parts.insert(new ACSymbol("re", CompletionKind.keyword, real_));
ifloat_.parts ~= new ACSymbol("re", CompletionKind.keyword, float_); ifloat_.parts.insert(new ACSymbol("re", CompletionKind.keyword, float_));
idouble_.parts ~= new ACSymbol("re", CompletionKind.keyword, double_); idouble_.parts.insert(new ACSymbol("re", CompletionKind.keyword, double_));
auto void_ = new ACSymbol("void", CompletionKind.keyword); auto void_ = new ACSymbol("void", CompletionKind.keyword);
builtinSymbols = [bool_, int_, long_, byte_, char_, dchar_, short_, ubyte_, uint_, bSym.insert([bool_, int_, long_, byte_, char_, dchar_, short_, ubyte_, uint_,
ulong_, ushort_, wchar_, cdouble_, cent_, cfloat_, creal_, double_, ulong_, ushort_, wchar_, cdouble_, cent_, cfloat_, creal_, double_,
float_, idouble_, ifloat_, ireal_, real_, ucent_, void_]; float_, idouble_, ifloat_, ireal_, real_, ucent_, void_]);
// _argptr has type void* // _argptr has type void*
argptrType = new Type; argptrType = new Type;
@ -382,12 +398,18 @@ static this()
TypeSuffix argumentsTypeSuffix = new TypeSuffix; TypeSuffix argumentsTypeSuffix = new TypeSuffix;
argumentsTypeSuffix.array = true; argumentsTypeSuffix.array = true;
argumentsType.typeSuffixes ~= argptrTypeSuffix; argumentsType.typeSuffixes ~= argptrTypeSuffix;
builtinSymbols = bSym;
arraySymbols = arrSym;
assocArraySymbols = asarrSym;
aggregateSymbols = aggSym;
classSymbols = clSym;
} }
const(ACSymbol)*[] builtinSymbols; RedBlackTree!(ACSymbol*, comparitor, true) builtinSymbols;
const(ACSymbol)*[] arraySymbols; RedBlackTree!(ACSymbol*, comparitor, true) arraySymbols;
const(ACSymbol)*[] assocArraySymbols; RedBlackTree!(ACSymbol*, comparitor, true) assocArraySymbols;
const(ACSymbol)*[] aggregateSymbols; RedBlackTree!(ACSymbol*, comparitor, true) aggregateSymbols;
const(ACSymbol)*[] classSymbols; RedBlackTree!(ACSymbol*, comparitor, true) classSymbols;
Type argptrType; Type argptrType;
Type argumentsType; Type argumentsType;

View File

@ -122,7 +122,7 @@ final class FirstPass : ASTVisitor
dec.parameters, dec.comment); dec.parameters, dec.comment);
symbol.protection = protection; symbol.protection = protection;
symbol.parent = currentSymbol; symbol.parent = currentSymbol;
symbol.acSymbol.doc = formatComment(dec.comment); symbol.acSymbol.doc = dec.comment;
currentSymbol.addChild(symbol); currentSymbol.addChild(symbol);
if (dec.functionBody !is null) if (dec.functionBody !is null)
{ {
@ -183,7 +183,7 @@ final class FirstPass : ASTVisitor
symbol.type = t; symbol.type = t;
symbol.protection = protection; symbol.protection = protection;
symbol.parent = currentSymbol; symbol.parent = currentSymbol;
symbol.acSymbol.doc = formatComment(dec.comment); symbol.acSymbol.doc = dec.comment;
currentSymbol.addChild(symbol); currentSymbol.addChild(symbol);
} }
} }
@ -251,12 +251,8 @@ final class FirstPass : ASTVisitor
currentSymbol = new SemanticSymbol(null, CompletionKind.moduleName, currentSymbol = new SemanticSymbol(null, CompletionKind.moduleName,
symbolFile); symbolFile);
rootSymbol = currentSymbol; rootSymbol = currentSymbol;
currentScope = new Scope(0, size_t.max);
currentScope = new Scope();
currentScope.startLocation = 0;
currentScope.endLocation = size_t.max;
moduleScope = currentScope; moduleScope = currentScope;
mod.accept(this); mod.accept(this);
} }
@ -268,7 +264,7 @@ final class FirstPass : ASTVisitor
CompletionKind.enumName, symbolFile, dec.name.index); CompletionKind.enumName, symbolFile, dec.name.index);
symbol.type = dec.type; symbol.type = dec.type;
symbol.parent = currentSymbol; symbol.parent = currentSymbol;
symbol.acSymbol.doc = formatComment(dec.comment); symbol.acSymbol.doc = dec.comment;
currentSymbol = symbol; currentSymbol = symbol;
if (dec.enumBody !is null) if (dec.enumBody !is null)
dec.enumBody.accept(this); dec.enumBody.accept(this);
@ -283,7 +279,7 @@ final class FirstPass : ASTVisitor
CompletionKind.enumMember, symbolFile, member.name.index); CompletionKind.enumMember, symbolFile, member.name.index);
symbol.type = member.type; symbol.type = member.type;
symbol.parent = currentSymbol; symbol.parent = currentSymbol;
symbol.acSymbol.doc = formatComment(member.comment); symbol.acSymbol.doc = member.comment;
currentSymbol.addChild(symbol); currentSymbol.addChild(symbol);
} }
@ -300,16 +296,14 @@ final class FirstPass : ASTVisitor
override void visit(StructBody structBody) override void visit(StructBody structBody)
{ {
// Log.trace(__FUNCTION__, " ", typeof(structBody).stringof); // Log.trace(__FUNCTION__, " ", typeof(structBody).stringof);
Scope* s = new Scope; Scope* s = new Scope(structBody.startLocation, structBody.endLocation);
s.startLocation = structBody.startLocation;
s.endLocation = structBody.endLocation;
// Log.trace("Added scope ", s.startLocation, " ", s.endLocation); // Log.trace("Added scope ", s.startLocation, " ", s.endLocation);
ACSymbol* thisSymbol = new ACSymbol("this", CompletionKind.variableName, ACSymbol* thisSymbol = new ACSymbol("this", CompletionKind.variableName,
currentSymbol.acSymbol); currentSymbol.acSymbol);
thisSymbol.location = s.startLocation; thisSymbol.location = s.startLocation;
thisSymbol.symbolFile = symbolFile; thisSymbol.symbolFile = symbolFile;
currentSymbol.acSymbol.parts ~= thisSymbol; currentSymbol.acSymbol.parts.insert(thisSymbol);
s.parent = currentScope; s.parent = currentScope;
currentScope = s; currentScope = s;
@ -353,11 +347,10 @@ final class FirstPass : ASTVisitor
override void visit(BlockStatement blockStatement) override void visit(BlockStatement blockStatement)
{ {
// Log.trace(__FUNCTION__, " ", typeof(blockStatement).stringof); // Log.trace(__FUNCTION__, " ", typeof(blockStatement).stringof);
Scope* s = new Scope; Scope* s = new Scope(blockStatement.startLocation,
blockStatement.endLocation);
s.parent = currentScope; s.parent = currentScope;
currentScope.children ~= s; currentScope.children ~= s;
s.startLocation = blockStatement.startLocation;
s.endLocation = blockStatement.endLocation;
if (currentSymbol.acSymbol.kind == CompletionKind.functionName) if (currentSymbol.acSymbol.kind == CompletionKind.functionName)
{ {
@ -395,12 +388,12 @@ private:
SemanticSymbol* symbol = new SemanticSymbol(getCached(dec.name.text), SemanticSymbol* symbol = new SemanticSymbol(getCached(dec.name.text),
kind, symbolFile, dec.name.index); kind, symbolFile, dec.name.index);
if (kind == CompletionKind.className) if (kind == CompletionKind.className)
symbol.acSymbol.parts ~= classSymbols; symbol.acSymbol.parts.insert(classSymbols[]);
else else
symbol.acSymbol.parts ~= aggregateSymbols; symbol.acSymbol.parts.insert(aggregateSymbols[]);
symbol.parent = currentSymbol; symbol.parent = currentSymbol;
symbol.protection = protection; symbol.protection = protection;
symbol.acSymbol.doc = formatComment(dec.comment); symbol.acSymbol.doc = dec.comment;
currentSymbol = symbol; currentSymbol = symbol;
dec.accept(this); dec.accept(this);
currentSymbol = symbol.parent; currentSymbol = symbol.parent;
@ -415,7 +408,7 @@ private:
processParameters(symbol, null, "this", parameters, doc); processParameters(symbol, null, "this", parameters, doc);
symbol.protection = protection; symbol.protection = protection;
symbol.parent = currentSymbol; symbol.parent = currentSymbol;
symbol.acSymbol.doc = formatComment(doc); symbol.acSymbol.doc = doc;
currentSymbol.addChild(symbol); currentSymbol.addChild(symbol);
if (functionBody !is null) if (functionBody !is null)
{ {
@ -429,10 +422,10 @@ private:
{ {
SemanticSymbol* symbol = new SemanticSymbol("~this", SemanticSymbol* symbol = new SemanticSymbol("~this",
CompletionKind.functionName, symbolFile, location); CompletionKind.functionName, symbolFile, location);
symbol.acSymbol.callTip = /*formatComment(doc) ~*/ "~this()"; symbol.acSymbol.callTip = "~this()";
symbol.protection = protection; symbol.protection = protection;
symbol.parent = currentSymbol; symbol.parent = currentSymbol;
symbol.acSymbol.doc = formatComment(doc); symbol.acSymbol.doc = doc;
currentSymbol.addChild(symbol); currentSymbol.addChild(symbol);
if (functionBody !is null) if (functionBody !is null)
{ {
@ -483,9 +476,6 @@ private:
if (returnType is null) if (returnType is null)
return "%s%s".format(name, parameterString); return "%s%s".format(name, parameterString);
return "%s %s%s".format(formatNode(returnType), name, parameterString); return "%s %s%s".format(formatNode(returnType), name, parameterString);
// if (returnType is null)
// return "%s%s%s".format(formatComment(doc), name, parameterString);
// return "%s%s %s%s".format(formatComment(doc), formatNode(returnType), name, parameterString);
} }
/// Current protection type /// Current protection type
@ -537,11 +527,11 @@ public:
private: private:
void assignToScopes(const(ACSymbol)* currentSymbol) void assignToScopes(ACSymbol* currentSymbol)
{ {
Scope* s = moduleScope.getScopeByCursor(currentSymbol.location); Scope* s = moduleScope.getScopeByCursor(currentSymbol.location);
s.symbols ~= currentSymbol; s.symbols.insert(currentSymbol);
foreach (part; currentSymbol.parts) foreach (part; currentSymbol.parts[])
assignToScopes(part); assignToScopes(part);
} }
@ -550,19 +540,21 @@ private:
Scope* currentScope) Scope* currentScope)
{ {
immutable string firstPart = info.importParts[0]; immutable string firstPart = info.importParts[0];
const(ACSymbol)*[] symbols = currentScope.getSymbolsByName(firstPart); ACSymbol*[] symbols = currentScope.getSymbolsByName(firstPart);
immutable bool found = symbols.length > 0; immutable bool found = symbols.length > 0;
const(ACSymbol)* firstSymbol = found ACSymbol* firstSymbol = found
? symbols[0] : new ACSymbol(firstPart, CompletionKind.packageName); ? symbols[0] : new ACSymbol(firstPart, CompletionKind.packageName);
if (!found) if (!found)
currentScope.symbols ~= firstSymbol; {
currentScope.symbols.insert(firstSymbol);
}
ACSymbol* currentSymbol = cast(ACSymbol*) firstSymbol; ACSymbol* currentSymbol = cast(ACSymbol*) firstSymbol;
foreach (size_t i, string importPart; info.importParts[1 .. $]) foreach (size_t i, string importPart; info.importParts[1 .. $])
{ {
symbols = currentSymbol.getPartsByName(importPart); symbols = currentSymbol.getPartsByName(importPart);
ACSymbol* s = symbols.length > 0 ACSymbol* s = symbols.length > 0
? cast(ACSymbol*) symbols[0] : new ACSymbol(importPart, CompletionKind.packageName); ? cast(ACSymbol*) symbols[0] : new ACSymbol(importPart, CompletionKind.packageName);
currentSymbol.parts ~= s; currentSymbol.parts.insert(s);
currentSymbol = s; currentSymbol = s;
} }
currentSymbol.kind = CompletionKind.moduleName; currentSymbol.kind = CompletionKind.moduleName;
@ -573,18 +565,16 @@ private:
{ {
foreach (importInfo; currentScope.importInformation) foreach (importInfo; currentScope.importInformation)
{ {
auto symbols = ModuleCache.getSymbolsInModule( ACSymbol*[] symbols = ModuleCache.getSymbolsInModule(
ModuleCache.resolveImportLoctation(importInfo.modulePath)); ModuleCache.resolveImportLoctation(importInfo.modulePath));
ACSymbol* moduleSymbol = createImportSymbols(importInfo, currentScope); ACSymbol* moduleSymbol = createImportSymbols(importInfo, currentScope);
currentScope.symbols ~= moduleSymbol; currentScope.symbols.insert(moduleSymbol);
currentScope.symbols ~= symbols; currentScope.symbols.insert(symbols);
if (importInfo.importedSymbols.length == 0) if (importInfo.importedSymbols.length == 0)
{ {
currentScope.symbols ~= symbols;
moduleSymbol.parts ~= symbols;
if (importInfo.isPublic && currentScope.parent is null) if (importInfo.isPublic && currentScope.parent is null)
{ {
rootSymbol.acSymbol.parts ~= symbols; rootSymbol.acSymbol.parts.insert(symbols);
} }
continue; continue;
} }
@ -606,17 +596,17 @@ private:
s.qualifier = symbol.qualifier; s.qualifier = symbol.qualifier;
s.location = symbol.location; s.location = symbol.location;
s.symbolFile = symbol.symbolFile; s.symbolFile = symbol.symbolFile;
currentScope.symbols ~= s; currentScope.symbols.insert(s);
moduleSymbol.parts ~= s; moduleSymbol.parts.insert(s);
if (importInfo.isPublic && currentScope.parent is null) if (importInfo.isPublic && currentScope.parent is null)
rootSymbol.acSymbol.parts ~= s; rootSymbol.acSymbol.parts.insert(s);
} }
else else
{ {
moduleSymbol.parts ~= symbol; moduleSymbol.parts.insert(symbol);
currentScope.symbols ~= symbol; currentScope.symbols.insert(symbol);
if (importInfo.isPublic && currentScope.parent is null) if (importInfo.isPublic && currentScope.parent is null)
rootSymbol.acSymbol.parts ~= symbol; rootSymbol.acSymbol.parts.insert(symbol);
} }
} }
} }
@ -673,7 +663,7 @@ private:
case memberVariableName: case memberVariableName:
case functionName: case functionName:
case aliasName: case aliasName:
const(ACSymbol)* t = resolveType(currentSymbol.type, ACSymbol* t = resolveType(currentSymbol.type,
currentSymbol.acSymbol.location); currentSymbol.acSymbol.location);
while (t !is null && t.kind == CompletionKind.aliasName) while (t !is null && t.kind == CompletionKind.aliasName)
t = t.type; t = t.type;
@ -691,7 +681,6 @@ private:
case mixinTemplateName: case mixinTemplateName:
break; break;
} }
foreach (child; currentSymbol.children) foreach (child; currentSymbol.children)
{ {
thirdPass(child); thirdPass(child);
@ -703,7 +692,7 @@ private:
// Log.trace("Resolving inheritance for ", currentSymbol.acSymbol.name); // Log.trace("Resolving inheritance for ", currentSymbol.acSymbol.name);
outer: foreach (string[] base; currentSymbol.baseClasses) outer: foreach (string[] base; currentSymbol.baseClasses)
{ {
const(ACSymbol)* baseClass; ACSymbol* baseClass;
if (base.length == 0) if (base.length == 0)
continue; continue;
auto symbols = moduleScope.getSymbolsByNameAndCursor( auto symbols = moduleScope.getSymbolsByNameAndCursor(
@ -718,7 +707,7 @@ private:
continue outer; continue outer;
baseClass = symbols[0]; baseClass = symbols[0];
} }
currentSymbol.acSymbol.parts ~= baseClass.parts; currentSymbol.acSymbol.parts.insert(baseClass.parts[]);
} }
} }
@ -732,11 +721,11 @@ private:
// TODO: // TODO:
} }
const(ACSymbol)* resolveType(Type t, size_t location) ACSymbol* resolveType(Type t, size_t location)
{ {
if (t is null) return null; if (t is null) return null;
if (t.type2 is null) return null; if (t.type2 is null) return null;
const(ACSymbol)* s; ACSymbol* s;
if (t.type2.builtinType != tok!"") if (t.type2.builtinType != tok!"")
s = convertBuiltinType(t.type2); s = convertBuiltinType(t.type2);
else if (t.type2.typeConstructor != tok!"") else if (t.type2.typeConstructor != tok!"")
@ -780,21 +769,24 @@ private:
return strings; return strings;
} }
static const(ACSymbol)* processSuffix(const(ACSymbol)* symbol, const TypeSuffix suffix) static ACSymbol* processSuffix(ACSymbol* symbol, const TypeSuffix suffix)
{ {
import std.container;
if (suffix.star) if (suffix.star)
return symbol; return symbol;
if (suffix.array || suffix.type) if (suffix.array || suffix.type)
{ {
ACSymbol* s = new ACSymbol; ACSymbol* s = new ACSymbol(null);
s.parts = suffix.array ? arraySymbols : assocArraySymbols; s.parts = new RedBlackTree!(ACSymbol*, comparitor, true);
s.parts.insert(suffix.array ? (cast() arraySymbols)[]
: (cast() assocArraySymbols)[]);
s.type = symbol; s.type = symbol;
s.qualifier = suffix.array ? SymbolQualifier.array : SymbolQualifier.assocArray; s.qualifier = suffix.array ? SymbolQualifier.array : SymbolQualifier.assocArray;
return s; return s;
} }
if (suffix.parameters) if (suffix.parameters)
{ {
ACSymbol* s = new ACSymbol; ACSymbol* s = new ACSymbol(null);
s.type = symbol; s.type = symbol;
s.qualifier = SymbolQualifier.func; s.qualifier = SymbolQualifier.func;
s.callTip = suffix.delegateOrFunction.text ~ formatNode(suffix.parameters); s.callTip = suffix.delegateOrFunction.text ~ formatNode(suffix.parameters);
@ -803,22 +795,21 @@ private:
return null; return null;
} }
static const(ACSymbol)* convertBuiltinType(const Type2 type2) static ACSymbol* convertBuiltinType(const Type2 type2)
{ {
string stringRepresentation = str(type2.builtinType); string stringRepresentation = str(type2.builtinType);
if (stringRepresentation is null) return null; if (stringRepresentation is null) return null;
// TODO: Make this use binary search instead // TODO: Make this use binary search instead
foreach (s; builtinSymbols) auto t = cast() builtinSymbols;
if (s.name == stringRepresentation) ACSymbol s = ACSymbol(stringRepresentation);
return s; return t.equalRange(&s).front();
return null;
} }
SemanticSymbol* rootSymbol; SemanticSymbol* rootSymbol;
Scope* moduleScope; Scope* moduleScope;
} }
const(ACSymbol)*[] convertAstToSymbols(const(Token)[] tokens, string symbolFile) ACSymbol*[] convertAstToSymbols(const(Token)[] tokens, string symbolFile)
{ {
Module m = parseModuleSimple(tokens, symbolFile); Module m = parseModuleSimple(tokens, symbolFile);
@ -831,7 +822,7 @@ const(ACSymbol)*[] convertAstToSymbols(const(Token)[] tokens, string symbolFile)
ThirdPass third = ThirdPass(second.rootSymbol, second.moduleScope); ThirdPass third = ThirdPass(second.rootSymbol, second.moduleScope);
third.run(); third.run();
return cast(typeof(return)) third.rootSymbol.acSymbol.parts; return third.rootSymbol.acSymbol.parts.array();
} }
const(Scope)* generateAutocompleteTrees(const(Token)[] tokens, string symbolFile) const(Scope)* generateAutocompleteTrees(const(Token)[] tokens, string symbolFile)
@ -953,28 +944,6 @@ string formatNode(T)(T node)
private void doesNothing(string a, size_t b, size_t c, string d, bool e) {} private void doesNothing(string a, size_t b, size_t c, string d, bool e) {}
string formatComment(string comment)
{
import std.string;
import std.regex;
enum tripleSlashRegex = `(?:\t )*///`;
enum slashStarRegex = `(?:^/\*\*+)|(?:\n?\s*\*+/$)|(?:(?<=\n)\s*\* ?)`;
enum slashPlusRegex = `(?:^/\+\++)|(?:\n?\s*\++/$)|(?:(?<=\n)\s*\+ ?)`;
if (comment is null)
return null;
string re;
if (comment[0 .. 3] == "///")
re = tripleSlashRegex;
else if (comment[1] == '+')
re = slashPlusRegex;
else
re = slashStarRegex;
return (comment.replaceAll(regex(re), ""))
.replaceFirst(regex("^\n"), "")
.replaceAll(regex(`\\`), `\\`)
.replaceAll(regex("\n"), `\n`).outdent();
}
/** /**
* Dummy doc comment for getCached * Dummy doc comment for getCached
*/ */
@ -983,14 +952,3 @@ string getCached(string s)
return s.length == 0 ? "" return s.length == 0 ? ""
: ModuleCache.stringCache.cacheGet(cast(const(ubyte)[]) s); : ModuleCache.stringCache.cacheGet(cast(const(ubyte)[]) s);
} }
//unittest
//{
// auto comment1 = "/**\n * This is some text\n */";
// auto result1 = formatComment(comment1);
// assert (result1 == `This is some text\n\n`, result1);
//
// auto comment2 = "///some\n///text";
// auto result2 = formatComment(comment2);
// assert (result2 == `some\ntext\n\n`, result2);
//}

View File

@ -66,7 +66,7 @@ AutocompleteResponse getDoc(const AutocompleteRequest request)
const(Scope)* completionScope = generateAutocompleteTrees(tokenArray, "stdin"); const(Scope)* completionScope = generateAutocompleteTrees(tokenArray, "stdin");
auto expression = getExpression(beforeTokens); auto expression = getExpression(beforeTokens);
const(ACSymbol)*[] symbols = getSymbolsByTokenChain(completionScope, expression, ACSymbol*[] symbols = getSymbolsByTokenChain(completionScope, expression,
request.cursorPosition, CompletionType.ddoc); request.cursorPosition, CompletionType.ddoc);
if (symbols.length == 0) if (symbols.length == 0)
@ -79,7 +79,7 @@ AutocompleteResponse getDoc(const AutocompleteRequest request)
continue; continue;
} }
Log.trace("Adding doc comment for ", symbol.name, ": ", symbol.doc); Log.trace("Adding doc comment for ", symbol.name, ": ", symbol.doc);
response.docComments ~= symbol.doc; response.docComments ~= formatComment(symbol.doc);
} }
return response; return response;
} }
@ -112,7 +112,7 @@ AutocompleteResponse findDeclaration(const AutocompleteRequest request)
const(Scope)* completionScope = generateAutocompleteTrees(tokenArray, "stdin"); const(Scope)* completionScope = generateAutocompleteTrees(tokenArray, "stdin");
auto expression = getExpression(beforeTokens); auto expression = getExpression(beforeTokens);
const(ACSymbol)*[] symbols = getSymbolsByTokenChain(completionScope, expression, ACSymbol*[] symbols = getSymbolsByTokenChain(completionScope, expression,
request.cursorPosition, CompletionType.location); request.cursorPosition, CompletionType.location);
if (symbols.length > 0) if (symbols.length > 0)
@ -156,12 +156,12 @@ bool shouldSwapWithType(CompletionType completionType, CompletionKind kind,
return completionType == CompletionType.identifiers && isInteresting; return completionType == CompletionType.identifiers && isInteresting;
} }
const(ACSymbol)*[] getSymbolsByTokenChain(T)(const(Scope)* completionScope, ACSymbol*[] getSymbolsByTokenChain(T)(const(Scope)* completionScope,
T tokens, size_t cursorPosition, CompletionType completionType) T tokens, size_t cursorPosition, CompletionType completionType)
{ {
Log.trace("Getting symbols from token chain", tokens.map!"a.text"); Log.trace("Getting symbols from token chain", tokens.map!"a.text");
// Find the symbol corresponding to the beginning of the chain // Find the symbol corresponding to the beginning of the chain
const(ACSymbol)*[] symbols = completionScope.getSymbolsByNameAndCursor( ACSymbol*[] symbols = completionScope.getSymbolsByNameAndCursor(
tokens[0].text, cursorPosition); tokens[0].text, cursorPosition);
if (symbols.length == 0) if (symbols.length == 0)
{ {
@ -293,7 +293,7 @@ const(ACSymbol)*[] getSymbolsByTokenChain(T)(const(Scope)* completionScope,
skip(); skip();
Parser p = new Parser(); Parser p = new Parser();
p.setTokens(tokens[h .. i].array()); p.setTokens(tokens[h .. i].array());
const(ACSymbol)*[] overloads; ACSymbol*[] overloads;
if (p.isSliceExpression()) if (p.isSliceExpression())
overloads = symbols[0].getPartsByName("opSlice"); overloads = symbols[0].getPartsByName("opSlice");
else else
@ -395,7 +395,7 @@ dotCompletion:
case tok!"stringLiteral": case tok!"stringLiteral":
case tok!"wstringLiteral": case tok!"wstringLiteral":
case tok!"dstringLiteral": case tok!"dstringLiteral":
foreach (symbol; arraySymbols) foreach (symbol; (cast() arraySymbols)[])
{ {
response.completionKinds ~= symbol.kind; response.completionKinds ~= symbol.kind;
response.completions ~= symbol.name; response.completions ~= symbol.name;
@ -478,7 +478,7 @@ void setCompletions(T)(ref AutocompleteResponse response,
if (tokens.length == 0) if (tokens.length == 0)
return; return;
const(ACSymbol)*[] symbols = getSymbolsByTokenChain(completionScope, tokens, ACSymbol*[] symbols = getSymbolsByTokenChain(completionScope, tokens,
cursorPosition, completionType); cursorPosition, completionType);
if (symbols.length == 0) if (symbols.length == 0)
@ -494,7 +494,7 @@ void setCompletions(T)(ref AutocompleteResponse response,
if (symbols.length == 0) if (symbols.length == 0)
return; return;
} }
foreach (s; symbols[0].parts.filter!(a => a.name !is null foreach (s; symbols[0].parts[].filter!(a => a.name !is null
&& a.name.length > 0 && a.name[0] != '*' && a.name.length > 0 && a.name[0] != '*'
&& (partial is null ? true : a.name.toUpper().startsWith(partial.toUpper())) && (partial is null ? true : a.name.toUpper().startsWith(partial.toUpper()))
&& !response.completions.canFind(a.name))) && !response.completions.canFind(a.name)))
@ -671,3 +671,36 @@ void setImportCompletions(T)(T tokens, ref AutocompleteResponse response)
} }
} }
} }
string formatComment(string comment)
{
import std.string;
import std.regex;
enum tripleSlashRegex = `(?:\t )*///`;
enum slashStarRegex = `(?:^/\*\*+)|(?:\n?\s*\*+/$)|(?:(?<=\n)\s*\* ?)`;
enum slashPlusRegex = `(?:^/\+\++)|(?:\n?\s*\++/$)|(?:(?<=\n)\s*\+ ?)`;
if (comment is null)
return null;
string re;
if (comment[0 .. 3] == "///")
re = tripleSlashRegex;
else if (comment[1] == '+')
re = slashPlusRegex;
else
re = slashStarRegex;
return (comment.replaceAll(regex(re), ""))
.replaceFirst(regex("^\n"), "")
.replaceAll(regex(`\\`), `\\`)
.replaceAll(regex("\n"), `\n`).outdent();
}
//unittest
//{
// auto comment1 = "/**\n * This is some text\n */";
// auto result1 = formatComment(comment1);
// assert (result1 == `This is some text\n\n`, result1);
//
// auto comment2 = "///some\n///text";
// auto result2 = formatComment(comment2);
// assert (result2 == `some\ntext\n\n`, result2);
//}

View File

@ -29,16 +29,23 @@ import std.array;
import std.path; import std.path;
import std.algorithm; import std.algorithm;
import std.conv; import std.conv;
import std.container;
import actypes; import actypes;
import semantic; import semantic;
import astconverter; import astconverter;
import stupidlog; import stupidlog;
struct CacheEntry bool cacheComparitor(CacheEntry* a, CacheEntry* b) pure nothrow
{ {
const(ACSymbol)*[] symbols; return cast(ubyte[]) a.path < cast(ubyte[]) b.path;
}
private struct CacheEntry
{
ACSymbol*[] symbols;
SysTime modificationTime; SysTime modificationTime;
string path;
void opAssign(ref const CacheEntry other) void opAssign(ref const CacheEntry other)
{ {
this.symbols = cast(typeof(symbols)) other.symbols; this.symbols = cast(typeof(symbols)) other.symbols;
@ -54,6 +61,11 @@ bool existanceCheck(A)(A path)
return false; return false;
} }
static this()
{
ModuleCache.cache = new RedBlackTree!(CacheEntry*, cacheComparitor);
}
/** /**
* Caches pre-parsed module information. * Caches pre-parsed module information.
*/ */
@ -66,7 +78,7 @@ struct ModuleCache
*/ */
static void clear() static void clear()
{ {
cache = cache.init; cache.clear();
} }
/** /**
@ -91,15 +103,18 @@ struct ModuleCache
* Returns: * Returns:
* The symbols defined in the given module * The symbols defined in the given module
*/ */
static const(ACSymbol)*[] getSymbolsInModule(string location) static ACSymbol*[] getSymbolsInModule(string location)
{ {
if (location is null) if (location is null)
return []; return [];
if (!needsReparsing(location)) if (!needsReparsing(location))
{ {
if (location in cache) CacheEntry e;
return cache[location].symbols; e.path = location;
auto r = cache.equalRange(&e);
if (!r.empty)
return r.front.symbols;
return []; return [];
} }
@ -107,25 +122,21 @@ struct ModuleCache
recursionGuard[location] = true; recursionGuard[location] = true;
const(ACSymbol)*[] symbols; ACSymbol*[] symbols;
try try
{ {
import core.memory;
File f = File(location); File f = File(location);
ubyte[] source = uninitializedArray!(ubyte[])(cast(size_t)f.size); ubyte[] source = uninitializedArray!(ubyte[])(cast(size_t)f.size);
f.rawRead(source); f.rawRead(source);
GC.disable();
LexerConfig config; LexerConfig config;
config.fileName = location; config.fileName = location;
StringCache* cache = new StringCache(StringCache.defaultBucketCount); StringCache* cache = new StringCache(StringCache.defaultBucketCount);
auto tokens = source.byToken(config, cache).array(); auto tokens = source.byToken(config, cache).array();
symbols = convertAstToSymbols(tokens, location); symbols = convertAstToSymbols(tokens, location);
GC.enable();
// Parsing allocates a lot of AST nodes. We can greatly reduce the
// program's idle memory use by running the GC here.
// TODO: Re-visit this when D gets a precise GC.
import core.memory;
GC.collect();
GC.minimize();
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -135,8 +146,8 @@ struct ModuleCache
SysTime access; SysTime access;
SysTime modification; SysTime modification;
getTimes(location, access, modification); getTimes(location, access, modification);
CacheEntry c = CacheEntry(symbols, modification); CacheEntry* c = new CacheEntry(symbols, modification, location);
cache[location] = c; cache.insert(c);
recursionGuard[location] = false; recursionGuard[location] = false;
return symbols; return symbols;
} }
@ -202,16 +213,21 @@ private:
return true; return true;
if (recursionGuard[mod]) if (recursionGuard[mod])
return false; return false;
if (!exists(mod) || mod !in cache) if (!exists(mod))
return true;
CacheEntry e;
e.path = mod;
auto r = cache.equalRange(&e);
if (r.empty)
return true; return true;
SysTime access; SysTime access;
SysTime modification; SysTime modification;
getTimes(mod, access, modification); getTimes(mod, access, modification);
return cache[mod].modificationTime != modification; return r.front.modificationTime != modification;
} }
// Mapping of file paths to their cached symbols. // Mapping of file paths to their cached symbols.
static CacheEntry[string] cache; static RedBlackTree!(CacheEntry*, cacheComparitor) cache;
static bool[string] recursionGuard; static bool[string] recursionGuard;

View File

@ -45,7 +45,7 @@ public:
void addChild(SemanticSymbol* child) void addChild(SemanticSymbol* child)
{ {
children ~= child; children ~= child;
acSymbol.parts ~= child.acSymbol; acSymbol.parts.insert(child.acSymbol);
} }
/// Autocompletion symbol /// Autocompletion symbol

View File

@ -96,8 +96,6 @@ int main(string[] args)
sw.stop(); sw.stop();
Log.info("Startup completed in ", sw.peek().to!("msecs", float), " milliseconds"); Log.info("Startup completed in ", sw.peek().to!("msecs", float), " milliseconds");
// ModuleCache.estimateMemory();
// No relative paths // No relative paths
version (Posix) chdir("/"); version (Posix) chdir("/");