More work on the new completion process. Fixed #55

This commit is contained in:
Hackerpilot 2013-10-13 17:03:44 +00:00
parent 6b432053f0
commit 41dbdaed4a
5 changed files with 174 additions and 81 deletions

View File

@ -25,6 +25,7 @@ import std.stdio;
import std.array; import std.array;
import messages; import messages;
import std.array; import std.array;
import std.typecons;
/** /**
* Any special information about a variable declaration symbol. * Any special information about a variable declaration symbol.
@ -74,7 +75,7 @@ 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, ACSymbol* type) this(string name, CompletionKind kind, const(ACSymbol)* type)
{ {
this.name = name; this.name = name;
this.kind = kind; this.kind = kind;
@ -101,7 +102,7 @@ public:
* 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.
*/ */
ACSymbol*[] parts; const(ACSymbol)*[] parts;
/** /**
* Symbol's name * Symbol's name
@ -111,7 +112,7 @@ public:
/** /**
* The symbol that represents the type. * The symbol that represents the type.
*/ */
ACSymbol* type; const(ACSymbol)* type;
/** /**
* Calltip to display if this is a function * Calltip to display if this is a function
@ -145,16 +146,6 @@ public:
{ {
return cast(typeof(return)) parts.filter!(a => a.name == name).array; return cast(typeof(return)) parts.filter!(a => a.name == name).array;
} }
/**
* Sorts the parts array, and the parts array of each part, and so on.
*/
void sortParts()
{
parts.sort();
foreach (p; parts)
p.sortParts();
}
} }
struct Scope struct Scope
@ -198,7 +189,7 @@ struct Scope
return s.getSymbolsByName(name); return s.getSymbolsByName(name);
} }
ACSymbol*[] symbols; const(ACSymbol)*[] symbols;
ImportInformation[] importInformation; ImportInformation[] importInformation;
Scope* parent; Scope* parent;
Scope*[] children; Scope*[] children;
@ -211,7 +202,7 @@ struct ImportInformation
/// module relative path /// module relative path
string modulePath; string modulePath;
/// symbols to import from this module /// symbols to import from this module
string[string] importedSymbols; Tuple!(string, string)[] importedSymbols;
/// true if the import is public /// true if the import is public
bool isPublic; bool isPublic;
} }
@ -279,7 +270,6 @@ static this()
s.parts ~= sizeof_; s.parts ~= sizeof_;
s.parts ~= stringof_; s.parts ~= stringof_;
s.parts ~= mangleof_; s.parts ~= mangleof_;
s.sortParts();
} }
auto cdouble_ = new ACSymbol("cdouble", CompletionKind.keyword); auto cdouble_ = new ACSymbol("cdouble", CompletionKind.keyword);
@ -314,7 +304,6 @@ static this()
s.parts ~= new ACSymbol("nan", CompletionKind.keyword, s); s.parts ~= new ACSymbol("nan", CompletionKind.keyword, s);
s.parts ~= sizeof_; s.parts ~= sizeof_;
s.parts ~= stringof_; s.parts ~= stringof_;
s.sortParts();
} }
ireal_.parts ~= new ACSymbol("im", CompletionKind.keyword, real_); ireal_.parts ~= new ACSymbol("im", CompletionKind.keyword, real_);
@ -331,8 +320,8 @@ static this()
float_, idouble_, ifloat_, ireal_, real_, ucent_, void_]; float_, idouble_, ifloat_, ireal_, real_, ucent_, void_];
} }
ACSymbol*[] builtinSymbols; const(ACSymbol)*[] builtinSymbols;
ACSymbol*[] arraySymbols; const(ACSymbol)*[] arraySymbols;
ACSymbol*[] assocArraySymbols; const(ACSymbol)*[] assocArraySymbols;
ACSymbol*[] classSymbols; const(ACSymbol)*[] classSymbols;
ACSymbol*[] structSymbols; const(ACSymbol)*[] structSymbols;

View File

@ -25,6 +25,7 @@ import std.array;
import std.conv; import std.conv;
import std.path; import std.path;
import std.range; import std.range;
import std.typecons;
import stdx.d.ast; import stdx.d.ast;
import stdx.d.lexer; import stdx.d.lexer;
@ -35,6 +36,7 @@ import constants;
import messages; import messages;
import semantic; import semantic;
import stupidlog; import stupidlog;
import modulecache; // circular import
/** /**
* First Pass handles the following: * First Pass handles the following:
@ -256,9 +258,7 @@ final class FirstPass : ASTVisitor
override void visit(ImportDeclaration importDeclaration) override void visit(ImportDeclaration importDeclaration)
{ {
Log.trace(__FUNCTION__, " ImportDeclaration"); // Log.trace(__FUNCTION__, " ImportDeclaration");
foreach (single; importDeclaration.singleImports.filter!( foreach (single; importDeclaration.singleImports.filter!(
a => a !is null && a.identifierChain !is null)) a => a !is null && a.identifierChain !is null))
{ {
@ -273,10 +273,10 @@ final class FirstPass : ASTVisitor
importDeclaration.importBindings.singleImport.identifierChain); importDeclaration.importBindings.singleImport.identifierChain);
foreach (bind; importDeclaration.importBindings.importBinds) foreach (bind; importDeclaration.importBindings.importBinds)
{ {
if (bind.right == TokenType.invalid) Tuple!(string, string) bindTuple;
info.importedSymbols[bind.left.value] = null; bindTuple[0] = bind.left.value.dup;
else bindTuple[1] = bind.right == TokenType.invalid ? null : bind.right.value.dup;
info.importedSymbols[bind.left.value] = bind.right.value; info.importedSymbols ~= bindTuple;
} }
currentScope.importInformation ~= info; currentScope.importInformation ~= info;
} }
@ -462,7 +462,7 @@ public:
private: private:
void assignToScopes(ACSymbol* currentSymbol) void assignToScopes(const(ACSymbol)* currentSymbol)
{ {
moduleScope.getScopeByCursor(currentSymbol.location).symbols moduleScope.getScopeByCursor(currentSymbol.location).symbols
~= currentSymbol; ~= currentSymbol;
@ -474,7 +474,35 @@ private:
{ {
foreach (importInfo; currentScope.importInformation) foreach (importInfo; currentScope.importInformation)
{ {
// TODO: actually process imports here auto symbols = ModuleCache.getSymbolsInModule(importInfo.modulePath);
if (importInfo.importedSymbols.length == 0)
{
currentScope.symbols ~= symbols;
continue;
}
symbolLoop: foreach (symbol; symbols)
{
foreach (tup; importInfo.importedSymbols)
{
if (tup[0] != symbol.name)
continue symbolLoop;
if (tup[1] !is null)
{
ACSymbol* s = new ACSymbol(tup[1],
symbol.kind, symbol.type);
// TODO: Compiler gets confused here, so cast the types.
s.parts = cast(typeof(s.parts)) symbol.parts;
// TODO: Re-format callTip with new name?
s.callTip = symbol.callTip;
s.qualifier = symbol.qualifier;
s.location = symbol.location;
s.symbolFile = symbol.symbolFile;
currentScope.symbols ~= s;
}
else
currentScope.symbols ~= symbol;
}
}
} }
} }
@ -509,22 +537,26 @@ private:
void thirdPass(SemanticSymbol* currentSymbol) void thirdPass(SemanticSymbol* currentSymbol)
{ {
// Log.trace("third pass on ", currentSymbol.acSymbol.name);
with (CompletionKind) final switch (currentSymbol.acSymbol.kind) with (CompletionKind) final switch (currentSymbol.acSymbol.kind)
{ {
case className: case className:
case interfaceName: case interfaceName:
// resolve inheritance resolveInheritance(currentSymbol);
goto case structName; goto case structName;
case structName: case structName:
case unionName: case unionName:
resolveAliasThis(currentSymbol);
resolveMixinTemplates(currentSymbol);
break; break;
case variableName: case variableName:
case memberVariableName: case memberVariableName:
break;
case functionName: case functionName:
// Log.trace("Resolving type of ", currentSymbol.acSymbol.name);
currentSymbol.acSymbol.type = resolveType(currentSymbol.type,
currentSymbol.acSymbol.location);
break; break;
case enumName: case enumName:
break;
case keyword: case keyword:
case enumMember: case enumMember:
case packageName: case packageName:
@ -535,33 +567,78 @@ private:
case aliasName: case aliasName:
case templateName: case templateName:
case mixinTemplateName: case mixinTemplateName:
break;
}
foreach (child; currentSymbol.children)
thirdPass(child);
}
void resolveInheritance(SemanticSymbol* currentSymbol)
{
// Log.trace("Resolving inheritance for ", currentSymbol.acSymbol.name);
outer: foreach (string[] base; currentSymbol.baseClasses)
{
const(ACSymbol)* baseClass;
if (base.length == 0)
continue;
auto symbols = moduleScope.getSymbolsByNameAndCursor(
base[0], currentSymbol.acSymbol.location);
if (symbols.length == 0)
continue;
baseClass = symbols[0];
foreach (part; base[1..$])
{
symbols = baseClass.getPartsByName(part);
if (symbols.length == 0)
continue outer;
baseClass = symbols[0];
}
currentSymbol.acSymbol.parts ~= baseClass.parts;
} }
} }
ACSymbol* resolveType(const Type t) void resolveAliasThis(SemanticSymbol* currentSymbol)
in
{ {
assert (t !is null); // TODO:
assert (t.type2 !is null);
} }
body
void resolveMixinTemplates(SemanticSymbol* currentSymbol)
{ {
ACSymbol* s; // TODO:
}
const(ACSymbol)* resolveType(Type t, size_t location)
{
if (t is null) return null;
if (t.type2 is null) return null;
const(ACSymbol)* s;
if (t.type2.builtinType != TokenType.invalid) if (t.type2.builtinType != TokenType.invalid)
s = convertBuiltinType(t.type2); s = convertBuiltinType(t.type2);
else if (t.type2.typeConstructor != TokenType.invalid) else if (t.type2.typeConstructor != TokenType.invalid)
s = resolveType(t.type2.type); s = resolveType(t.type2.type, location);
else if (t.type2.symbol !is null) else if (t.type2.symbol !is null)
{ {
if (t.type2.symbol.dot) // TODO: global scoped symbol handling
Log.error("TODO: global scoped symbol handling");
string[] symbolParts = expandSymbol( string[] symbolParts = expandSymbol(
t.type2.symbol.identifierOrTemplateChain); t.type2.symbol.identifierOrTemplateChain);
auto symbols = moduleScope.getSymbolsByNameAndCursor(
symbolParts[0], location);
if (symbols.length == 0)
goto resolveSuffixes;
s = symbols[0];
foreach (symbolPart; symbolParts[1..$])
{
auto parts = s.getPartsByName(symbolPart);
if (parts.length == 0)
goto resolveSuffixes;
s = parts[0];
}
} }
resolveSuffixes:
foreach (suffix; t.typeSuffixes) foreach (suffix; t.typeSuffixes)
s = processSuffix(s, suffix); s = processSuffix(s, suffix);
return null; return s;
} }
static string[] expandSymbol(const IdentifierOrTemplateChain chain) static string[] expandSymbol(const IdentifierOrTemplateChain chain)
@ -570,6 +647,8 @@ private:
for (size_t i = 0; i != chain.identifiersOrTemplateInstances.length; ++i) for (size_t i = 0; i != chain.identifiersOrTemplateInstances.length; ++i)
{ {
auto identOrTemplate = chain.identifiersOrTemplateInstances[i]; auto identOrTemplate = chain.identifiersOrTemplateInstances[i];
if (identOrTemplate is null)
continue;
strings[i] = identOrTemplate.templateInstance is null ? strings[i] = identOrTemplate.templateInstance is null ?
identOrTemplate.identifier.value.dup identOrTemplate.identifier.value.dup
: identOrTemplate.identifier.value.dup; : identOrTemplate.identifier.value.dup;
@ -577,14 +656,14 @@ private:
return strings; return strings;
} }
static ACSymbol* processSuffix(ACSymbol* symbol, const TypeSuffix suffix) static const(ACSymbol)* processSuffix(const(ACSymbol)* symbol, const TypeSuffix suffix)
{ {
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;
s.parts = arraySymbols; s.parts = suffix.array ? arraySymbols : 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;
@ -597,7 +676,7 @@ private:
return null; return null;
} }
static ACSymbol* convertBuiltinType(const Type2 type2) static const(ACSymbol)* convertBuiltinType(const Type2 type2)
{ {
string stringRepresentation = getTokenValue(type2.builtinType); string stringRepresentation = getTokenValue(type2.builtinType);
if (stringRepresentation is null) return null; if (stringRepresentation is null) return null;
@ -619,6 +698,7 @@ const(ACSymbol)*[] convertAstToSymbols(Module m, string symbolFile)
SecondPass second = SecondPass(first.rootSymbol, first.moduleScope); SecondPass second = SecondPass(first.rootSymbol, first.moduleScope);
second.run(); second.run();
ThirdPass third = ThirdPass(second.rootSymbol, second.moduleScope); ThirdPass third = ThirdPass(second.rootSymbol, second.moduleScope);
third.run();
return cast(typeof(return)) third.rootSymbol.acSymbol.parts; return cast(typeof(return)) third.rootSymbol.acSymbol.parts;
} }
@ -634,19 +714,6 @@ const(Scope)* generateAutocompleteTrees(const(Token)[] tokens, string symbolFile
return cast(typeof(return)) third.moduleScope; return cast(typeof(return)) third.moduleScope;
} }
version(unittest) Module parseTestCode(string code)
{
LexerConfig config;
const(Token)[] tokens = byToken(cast(ubyte[]) code, config);
Parser p = new Parser;
p.fileName = "unittest";
p.tokens = tokens;
Module m = p.parseModule();
assert (p.errorCount == 0);
assert (p.warningCount == 0);
return m;
}
private: private:
string[] iotcToStringArray(const IdentifierOrTemplateChain iotc) string[] iotcToStringArray(const IdentifierOrTemplateChain iotc)
@ -664,5 +731,18 @@ string[] iotcToStringArray(const IdentifierOrTemplateChain iotc)
private static string convertChainToImportPath(IdentifierChain chain) private static string convertChainToImportPath(IdentifierChain chain)
{ {
return to!string(chain.identifiers.map!(a => a.value).join(dirSeparator).array) ~ ".d"; return to!string(chain.identifiers.map!(a => a.value.dup).join(dirSeparator).array) ~ ".d";
}
version(unittest) Module parseTestCode(string code)
{
LexerConfig config;
const(Token)[] tokens = byToken(cast(ubyte[]) code, config);
Parser p = new Parser;
p.fileName = "unittest";
p.tokens = tokens;
Module m = p.parseModule();
assert (p.errorCount == 0);
assert (p.warningCount == 0);
return m;
} }

View File

@ -63,18 +63,24 @@ struct ModuleCache
/** /**
* Adds the given path to the list of directories checked for imports * Adds the given path to the list of directories checked for imports
*/ */
static void addImportPath(string path) static void addImportPaths(string[] paths)
{ {
if (!exists(path)) foreach (path; paths)
return;
importPaths ~= path;
foreach (fileName; dirEntries(path, "*.{d,di}", SpanMode.depth))
{ {
Log.info("Loading and caching completions for ", fileName); if (!exists(path))
getSymbolsInModule(fileName); {
import core.memory; Log.error("Cannot cache modules in ", path, " because it does not exist");
GC.collect(); continue;
GC.minimize(); }
importPaths ~= path;
}
foreach (path; paths)
{
foreach (fileName; dirEntries(path, "*.{d,di}", SpanMode.depth))
{
Log.info("Loading and caching completions for ", fileName);
getSymbolsInModule(fileName);
}
} }
} }
@ -90,8 +96,15 @@ struct ModuleCache
string location = resolveImportLoctation(moduleName); string location = resolveImportLoctation(moduleName);
if (location is null) if (location is null)
return []; return [];
if (!needsReparsing(location)) if (!needsReparsing(location))
return cache[location].symbols; {
if (location in cache)
return cache[location].symbols;
return [];
}
recursionGuard[location] = true;
const(ACSymbol)*[] symbols; const(ACSymbol)*[] symbols;
try try
@ -117,6 +130,10 @@ struct ModuleCache
getTimes(location, access, modification); getTimes(location, access, modification);
CacheEntry c = CacheEntry(symbols, modification); CacheEntry c = CacheEntry(symbols, modification);
cache[location] = c; cache[location] = c;
recursionGuard[location] = false;
import core.memory;
GC.collect();
GC.minimize();
return symbols; return symbols;
} }
@ -142,7 +159,7 @@ struct ModuleCache
if (filePath.exists()) if (filePath.exists())
return filePath; return filePath;
} }
writeln("Could not find ", moduleName); Log.error("Could not find ", moduleName);
return null; return null;
} }
@ -161,6 +178,10 @@ private:
*/ */
static bool needsReparsing(string mod) static bool needsReparsing(string mod)
{ {
if (mod !in recursionGuard)
return true;
if (recursionGuard[mod])
return false;
if (!exists(mod) || mod !in cache) if (!exists(mod) || mod !in cache)
return true; return true;
SysTime access; SysTime access;
@ -172,6 +193,8 @@ private:
// Mapping of file paths to their cached symbols. // Mapping of file paths to their cached symbols.
static CacheEntry[string] cache; static CacheEntry[string] cache;
static bool[string] recursionGuard;
// Listing of paths to check for imports // Listing of paths to check for imports
static string[] importPaths; static string[] importPaths;
} }

View File

@ -22,6 +22,7 @@ import messages;
import actypes; import actypes;
import stdx.d.ast; import stdx.d.ast;
import stdx.d.lexer; import stdx.d.lexer;
import stupidlog;
/** /**
* Intermediate form between ACSymbol and the AST classes. Stores enough * Intermediate form between ACSymbol and the AST classes. Stores enough
@ -41,6 +42,11 @@ public:
acSymbol.symbolFile = symbolFile; acSymbol.symbolFile = symbolFile;
} }
~this()
{
Log.trace(acSymbol.name, " destructor");
}
void addChild(SemanticSymbol* child) void addChild(SemanticSymbol* child)
{ {
children ~= child; children ~= child;

View File

@ -91,8 +91,7 @@ int main(string[] args)
Log.info("Sockets shut down."); Log.info("Sockets shut down.");
} }
foreach (path; importPaths) ModuleCache.addImportPaths(importPaths);
ModuleCache.addImportPath(path);
Log.info("Import directories: ", ModuleCache.getImportPaths()); Log.info("Import directories: ", ModuleCache.getImportPaths());
ubyte[] buffer = new ubyte[1024 * 1024 * 4]; // 4 megabytes should be enough for anybody... ubyte[] buffer = new ubyte[1024 * 1024 * 4]; // 4 megabytes should be enough for anybody...
@ -150,11 +149,7 @@ int main(string[] args)
msgpack.unpack(buffer[size_t.sizeof .. bytesReceived], request); msgpack.unpack(buffer[size_t.sizeof .. bytesReceived], request);
if (request.kind == RequestKind.addImport) if (request.kind == RequestKind.addImport)
{ {
foreach (path; request.importPaths) ModuleCache.addImportPaths(request.importPaths);
{
ModuleCache.addImportPath(path);
}
} }
else if (request.kind == RequestKind.clearCache) else if (request.kind == RequestKind.clearCache)
{ {