From 41dbdaed4a204e973b34877e36659e40b3407477 Mon Sep 17 00:00:00 2001 From: Hackerpilot Date: Sun, 13 Oct 2013 17:03:44 +0000 Subject: [PATCH] More work on the new completion process. Fixed #55 --- actypes.d | 33 ++++------ astconverter.d | 160 ++++++++++++++++++++++++++++++++++++------------- modulecache.d | 47 +++++++++++---- semantic.d | 6 ++ server.d | 9 +-- 5 files changed, 174 insertions(+), 81 deletions(-) diff --git a/actypes.d b/actypes.d index fd92767..f788250 100644 --- a/actypes.d +++ b/actypes.d @@ -25,6 +25,7 @@ import std.stdio; import std.array; import messages; import std.array; +import std.typecons; /** * Any special information about a variable declaration symbol. @@ -74,7 +75,7 @@ public: * kind = the symbol's completion kind * 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.kind = kind; @@ -101,7 +102,7 @@ public: * Symbols that compose this symbol, such as enum members, class variables, * methods, etc. */ - ACSymbol*[] parts; + const(ACSymbol)*[] parts; /** * Symbol's name @@ -111,7 +112,7 @@ public: /** * The symbol that represents the type. */ - ACSymbol* type; + const(ACSymbol)* type; /** * Calltip to display if this is a function @@ -145,16 +146,6 @@ public: { 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 @@ -198,7 +189,7 @@ struct Scope return s.getSymbolsByName(name); } - ACSymbol*[] symbols; + const(ACSymbol)*[] symbols; ImportInformation[] importInformation; Scope* parent; Scope*[] children; @@ -211,7 +202,7 @@ struct ImportInformation /// module relative path string modulePath; /// symbols to import from this module - string[string] importedSymbols; + Tuple!(string, string)[] importedSymbols; /// true if the import is public bool isPublic; } @@ -279,7 +270,6 @@ static this() s.parts ~= sizeof_; s.parts ~= stringof_; s.parts ~= mangleof_; - s.sortParts(); } auto cdouble_ = new ACSymbol("cdouble", CompletionKind.keyword); @@ -314,7 +304,6 @@ static this() s.parts ~= new ACSymbol("nan", CompletionKind.keyword, s); s.parts ~= sizeof_; s.parts ~= stringof_; - s.sortParts(); } ireal_.parts ~= new ACSymbol("im", CompletionKind.keyword, real_); @@ -331,8 +320,8 @@ static this() float_, idouble_, ifloat_, ireal_, real_, ucent_, void_]; } -ACSymbol*[] builtinSymbols; -ACSymbol*[] arraySymbols; -ACSymbol*[] assocArraySymbols; -ACSymbol*[] classSymbols; -ACSymbol*[] structSymbols; +const(ACSymbol)*[] builtinSymbols; +const(ACSymbol)*[] arraySymbols; +const(ACSymbol)*[] assocArraySymbols; +const(ACSymbol)*[] classSymbols; +const(ACSymbol)*[] structSymbols; diff --git a/astconverter.d b/astconverter.d index 1a833d9..feff926 100644 --- a/astconverter.d +++ b/astconverter.d @@ -25,6 +25,7 @@ import std.array; import std.conv; import std.path; import std.range; +import std.typecons; import stdx.d.ast; import stdx.d.lexer; @@ -35,6 +36,7 @@ import constants; import messages; import semantic; import stupidlog; +import modulecache; // circular import /** * First Pass handles the following: @@ -256,9 +258,7 @@ final class FirstPass : ASTVisitor override void visit(ImportDeclaration importDeclaration) { - Log.trace(__FUNCTION__, " ImportDeclaration"); - - +// Log.trace(__FUNCTION__, " ImportDeclaration"); foreach (single; importDeclaration.singleImports.filter!( a => a !is null && a.identifierChain !is null)) { @@ -273,10 +273,10 @@ final class FirstPass : ASTVisitor importDeclaration.importBindings.singleImport.identifierChain); foreach (bind; importDeclaration.importBindings.importBinds) { - if (bind.right == TokenType.invalid) - info.importedSymbols[bind.left.value] = null; - else - info.importedSymbols[bind.left.value] = bind.right.value; + Tuple!(string, string) bindTuple; + bindTuple[0] = bind.left.value.dup; + bindTuple[1] = bind.right == TokenType.invalid ? null : bind.right.value.dup; + info.importedSymbols ~= bindTuple; } currentScope.importInformation ~= info; } @@ -462,7 +462,7 @@ public: private: - void assignToScopes(ACSymbol* currentSymbol) + void assignToScopes(const(ACSymbol)* currentSymbol) { moduleScope.getScopeByCursor(currentSymbol.location).symbols ~= currentSymbol; @@ -474,7 +474,35 @@ private: { 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) { +// Log.trace("third pass on ", currentSymbol.acSymbol.name); with (CompletionKind) final switch (currentSymbol.acSymbol.kind) { case className: case interfaceName: - // resolve inheritance + resolveInheritance(currentSymbol); goto case structName; case structName: case unionName: + resolveAliasThis(currentSymbol); + resolveMixinTemplates(currentSymbol); break; case variableName: case memberVariableName: - break; case functionName: +// Log.trace("Resolving type of ", currentSymbol.acSymbol.name); + currentSymbol.acSymbol.type = resolveType(currentSymbol.type, + currentSymbol.acSymbol.location); break; case enumName: - break; case keyword: case enumMember: case packageName: @@ -535,33 +567,78 @@ private: case aliasName: case templateName: 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) - in + void resolveAliasThis(SemanticSymbol* currentSymbol) { - assert (t !is null); - assert (t.type2 !is null); + // TODO: } - 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) s = convertBuiltinType(t.type2); 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) { - if (t.type2.symbol.dot) - Log.error("TODO: global scoped symbol handling"); + // TODO: global scoped symbol handling string[] symbolParts = expandSymbol( 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) s = processSuffix(s, suffix); - return null; + return s; } static string[] expandSymbol(const IdentifierOrTemplateChain chain) @@ -570,6 +647,8 @@ private: for (size_t i = 0; i != chain.identifiersOrTemplateInstances.length; ++i) { auto identOrTemplate = chain.identifiersOrTemplateInstances[i]; + if (identOrTemplate is null) + continue; strings[i] = identOrTemplate.templateInstance is null ? identOrTemplate.identifier.value.dup : identOrTemplate.identifier.value.dup; @@ -577,14 +656,14 @@ private: return strings; } - static ACSymbol* processSuffix(ACSymbol* symbol, const TypeSuffix suffix) + static const(ACSymbol)* processSuffix(const(ACSymbol)* symbol, const TypeSuffix suffix) { if (suffix.star) return symbol; if (suffix.array || suffix.type) { ACSymbol* s = new ACSymbol; - s.parts = arraySymbols; + s.parts = suffix.array ? arraySymbols : assocArraySymbols; s.type = symbol; s.qualifier = suffix.array ? SymbolQualifier.array : SymbolQualifier.assocArray; return s; @@ -597,7 +676,7 @@ private: return null; } - static ACSymbol* convertBuiltinType(const Type2 type2) + static const(ACSymbol)* convertBuiltinType(const Type2 type2) { string stringRepresentation = getTokenValue(type2.builtinType); if (stringRepresentation is null) return null; @@ -619,6 +698,7 @@ const(ACSymbol)*[] convertAstToSymbols(Module m, string symbolFile) SecondPass second = SecondPass(first.rootSymbol, first.moduleScope); second.run(); ThirdPass third = ThirdPass(second.rootSymbol, second.moduleScope); + third.run(); 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; } -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: string[] iotcToStringArray(const IdentifierOrTemplateChain iotc) @@ -664,5 +731,18 @@ string[] iotcToStringArray(const IdentifierOrTemplateChain iotc) 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; } diff --git a/modulecache.d b/modulecache.d index 31587e4..aa4ca88 100644 --- a/modulecache.d +++ b/modulecache.d @@ -63,18 +63,24 @@ struct ModuleCache /** * 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)) - return; - importPaths ~= path; - foreach (fileName; dirEntries(path, "*.{d,di}", SpanMode.depth)) + foreach (path; paths) { - Log.info("Loading and caching completions for ", fileName); - getSymbolsInModule(fileName); - import core.memory; - GC.collect(); - GC.minimize(); + if (!exists(path)) + { + Log.error("Cannot cache modules in ", path, " because it does not exist"); + continue; + } + 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); if (location is null) return []; + if (!needsReparsing(location)) - return cache[location].symbols; + { + if (location in cache) + return cache[location].symbols; + return []; + } + + recursionGuard[location] = true; const(ACSymbol)*[] symbols; try @@ -117,6 +130,10 @@ struct ModuleCache getTimes(location, access, modification); CacheEntry c = CacheEntry(symbols, modification); cache[location] = c; + recursionGuard[location] = false; + import core.memory; + GC.collect(); + GC.minimize(); return symbols; } @@ -142,7 +159,7 @@ struct ModuleCache if (filePath.exists()) return filePath; } - writeln("Could not find ", moduleName); + Log.error("Could not find ", moduleName); return null; } @@ -161,6 +178,10 @@ private: */ static bool needsReparsing(string mod) { + if (mod !in recursionGuard) + return true; + if (recursionGuard[mod]) + return false; if (!exists(mod) || mod !in cache) return true; SysTime access; @@ -172,6 +193,8 @@ private: // Mapping of file paths to their cached symbols. static CacheEntry[string] cache; + static bool[string] recursionGuard; + // Listing of paths to check for imports static string[] importPaths; } diff --git a/semantic.d b/semantic.d index 36596e5..85c1d2d 100644 --- a/semantic.d +++ b/semantic.d @@ -22,6 +22,7 @@ import messages; import actypes; import stdx.d.ast; import stdx.d.lexer; +import stupidlog; /** * Intermediate form between ACSymbol and the AST classes. Stores enough @@ -41,6 +42,11 @@ public: acSymbol.symbolFile = symbolFile; } + ~this() + { + Log.trace(acSymbol.name, " destructor"); + } + void addChild(SemanticSymbol* child) { children ~= child; diff --git a/server.d b/server.d index 4bf0ca1..0e9853f 100644 --- a/server.d +++ b/server.d @@ -91,8 +91,7 @@ int main(string[] args) Log.info("Sockets shut down."); } - foreach (path; importPaths) - ModuleCache.addImportPath(path); + ModuleCache.addImportPaths(importPaths); Log.info("Import directories: ", ModuleCache.getImportPaths()); 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); if (request.kind == RequestKind.addImport) { - foreach (path; request.importPaths) - { - ModuleCache.addImportPath(path); - } - + ModuleCache.addImportPaths(request.importPaths); } else if (request.kind == RequestKind.clearCache) {