diff --git a/README.md b/README.md index ca21e40..aab7610 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,14 @@ The D Completion Daemon is an auto-complete program for the D programming langua ![Teaser](teaser.png "This is what the future looks like - Jayce, League of Legends") +(The above is a screenshot of [Textadept](http://foicica.com/textadept/)) + DCD consists of a client and a server. The client (dcd-client) is used by a text editor script or from the command line. The server (dcd-server) is responsible for caching imported files, calculating autocomplete information, and sending it back to the client. #Status -*This program is still in an alpha state.* +This program is reasonably stable. * Working: * Autocompletion of properties of built-in types such as int, float, double, etc. @@ -21,17 +23,17 @@ back to the client. * Finding the declaration location of a symbol at the cursor * *import* statement completions * Display of documentation comments in function call tips + * *alias this* * Not working: * Automatic starting of the server by the client * UFCS * Autocompletion of declarations with template arguments * *auto* declarations - * *alias this* * Determining the type of an enum member when no base type is specified, but the first member has an initialaizer * That one feature that you *REALLY* needed #Setup -1. Install a recent D compiler. DCD is only tested with DMD 2.064.2 +1. Install a recent D compiler. DCD is tested with 2.065 and the 2.066 betas. 1. Run ```git submodule update --init``` after cloning this repository to grab the MessagePack and Datapacked libraries and the parser from DScanner. 1. run the ```build.sh``` script to build the client and server. (Or build.bat on Windows) 1. Configure your text editor to call the dcd-client program. See the *editors* folder for directions on configuring your specific editor. diff --git a/actypes.d b/actypes.d index 541a2bc..29ab41f 100644 --- a/actypes.d +++ b/actypes.d @@ -107,7 +107,11 @@ public: ACSymbol*[] getPartsByName(string name) { ACSymbol s = ACSymbol(name); - return array(parts.equalRange(&s)); + auto er = parts.equalRange(&s); + if (er.empty) + return array(aliasThisParts.equalRange(&s)); + else + return array(er); } /** @@ -121,6 +125,11 @@ public: */ TTree!(ACSymbol*, true, "a < b", false) parts; + /** + * Symbols included due to an alias this. + */ + TTree!(ACSymbol*, true, "a < b", false) aliasThisParts; + /** * Calltip to display if this is a function */ @@ -233,14 +242,9 @@ struct Scope { import std.range; ACSymbol s = ACSymbol(name); - auto r = array(symbols.equalRange(&s)); - foreach (i; r) - { - import std.string; - assert (i.name == name, format("%s %s %d", i.name, name, r.length)); - } - if (r.length > 0) - return cast(typeof(return)) r; + auto er = symbols.equalRange(&s); + if (!er.empty) + return cast(typeof(return)) array(er); if (parent is null) return []; return parent.getSymbolsByName(name); @@ -262,6 +266,13 @@ struct Scope return s.getSymbolsByName(name); } + ACSymbol*[] getSymbolsAtGlobalScope(string name) + { + if (parent !is null) + return parent.getSymbolsAtGlobalScope(name); + return getSymbolsByName(name); + } + /// Imports contained in this scope UnrolledList!(ImportInformation*) importInformation; @@ -313,7 +324,7 @@ TTree!(ACSymbol*, true, "a < b", false) arraySymbols; TTree!(ACSymbol*, true, "a < b", false) assocArraySymbols; /** - * Enum, union, class, and interface properties + * Struct, enum, union, class, and interface properties */ TTree!(ACSymbol*, true, "a < b", false) aggregateSymbols; @@ -506,7 +517,7 @@ static this() s.parts.insert(stringof_); } - aggregateSymbols.insert(allocate!ACSymbol(Mallocator.it, "tupleof", CompletionKind.variableName)); + aggregateSymbols.insert(allocate!ACSymbol(Mallocator.it, "tupleof", CompletionKind.keyword)); aggregateSymbols.insert(mangleof_); aggregateSymbols.insert(alignof_); aggregateSymbols.insert(sizeof_); diff --git a/autocomplete.d b/autocomplete.d index c91968f..a34a920 100644 --- a/autocomplete.d +++ b/autocomplete.d @@ -185,7 +185,6 @@ AutocompleteResponse complete(const AutocompleteRequest request) case tok!"[": case tok!";": case tok!":": - // TODO: global scope break; default: break; @@ -299,8 +298,16 @@ ACSymbol*[] getSymbolsByTokenChain(T)(Scope* completionScope, Log.trace("Getting symbols from token chain", tokens.map!stringToken); // Find the symbol corresponding to the beginning of the chain - ACSymbol*[] symbols = completionScope.getSymbolsByNameAndCursor( - stringToken(tokens[0]), cursorPosition); + ACSymbol*[] symbols; + if (tokens[0] == tok!"." && tokens.length > 1) + { + tokens = tokens[1 .. $]; + Log.info("Looking for ", stringToken(tokens[0]), " at global scope"); + symbols = completionScope.getSymbolsAtGlobalScope(stringToken(tokens[0])); + } + else + symbols = completionScope.getSymbolsByNameAndCursor(stringToken(tokens[0]), cursorPosition); + if (symbols.length == 0) { Log.error("Could not find declaration of ", stringToken(tokens[0]), @@ -493,6 +500,7 @@ void setCompletions(T)(ref AutocompleteResponse response, if (completionType == CompletionType.identifiers) { + import containers.ttree; if (symbols[0].qualifier == SymbolQualifier.func || symbols[0].kind == CompletionKind.functionName) { @@ -501,7 +509,10 @@ void setCompletions(T)(ref AutocompleteResponse response, if (symbols.length == 0) return; } - foreach (s; symbols[0].parts[].filter!(a => a.name !is null + TTree!(ACSymbol*, true, "a < b", false) parts; + parts.insert(symbols[0].parts[]); + parts.insert(symbols[0].aliasThisParts[]); + foreach (s; parts[].filter!(a => a.name !is null && a.name.length > 0 && a.name[0] != '*' && (partial is null ? true : a.name.toUpper().startsWith(partial.toUpper())) && !response.completions.canFind(a.name))) @@ -542,7 +553,15 @@ void setCompletions(T)(ref AutocompleteResponse response, { auto constructor = symbols[0].getPartsByName("*constructor*"); if (constructor.length == 0) - return; + { + // Build a call tip out of the struct fields + if (symbols[0].kind == CompletionKind.structName) + { + response.completionType = CompletionType.calltips; + response.completions = [generateStructConstructorCalltip(symbols[0])]; + return; + } + } else { symbols = constructor; @@ -560,6 +579,34 @@ void setCompletions(T)(ref AutocompleteResponse response, } } +string generateStructConstructorCalltip(const ACSymbol* symbol) +in +{ + assert (symbol.kind == CompletionKind.structName); +} +body +{ + string generatedStructConstructorCalltip = "this("; + size_t i = 0; + immutable c = count(symbol.parts[].filter!(a => a.kind == CompletionKind.variableName)); + foreach (part; array(symbol.parts[]).sort!((a, b) => a.location < b.location)) + { + if (part.kind != CompletionKind.variableName) + continue; + i++; + if (part.type !is null) + { + generatedStructConstructorCalltip ~= part.type.name; + generatedStructConstructorCalltip ~= " "; + } + generatedStructConstructorCalltip ~= part.name; + if (i < c) + generatedStructConstructorCalltip ~= ", "; + } + generatedStructConstructorCalltip ~= ")"; + return generatedStructConstructorCalltip; +} + /** * */ diff --git a/build.sh b/build.sh index d80d54d..294cdd4 100755 --- a/build.sh +++ b/build.sh @@ -37,6 +37,7 @@ dmd\ containers/src/containers/unrolledlist.d\ containers/src/containers/hashset.d\ containers/src/containers/internal/hash.d\ + containers/src/containers/internal/node.d\ containers/src/containers/slist.d\ msgpack-d/src/msgpack.d\ -Icontainers/src\ diff --git a/containers b/containers index 2a72525..74b3944 160000 --- a/containers +++ b/containers @@ -1 +1 @@ -Subproject commit 2a72525f649850f452f21e7f362f70440f487a61 +Subproject commit 74b394404947f8c1ea9c6e769fdb413db049d27d diff --git a/conversion/third.d b/conversion/third.d index 5a832e2..705cf94 100644 --- a/conversion/third.d +++ b/conversion/third.d @@ -69,11 +69,6 @@ private: case className: case interfaceName: resolveInheritance(currentSymbol); - goto case structName; - case structName: - case unionName: - resolveAliasThis(currentSymbol); - resolveMixinTemplates(currentSymbol); break; case variableName: case memberVariableName: @@ -85,6 +80,8 @@ private: t = t.type; currentSymbol.acSymbol.type = t; break; + case structName: + case unionName: case enumName: case keyword: case enumMember: @@ -97,10 +94,24 @@ private: case mixinTemplateName: break; } + foreach (child; currentSymbol.children) { thirdPass(child); } + + with (CompletionKind) switch (currentSymbol.acSymbol.kind) + { + case className: + case interfaceName: + case structName: + case unionName: + resolveAliasThis(currentSymbol); + resolveMixinTemplates(currentSymbol); + break; + default: + break; + } } void resolveInheritance(SemanticSymbol* currentSymbol) @@ -129,10 +140,16 @@ private: void resolveAliasThis(SemanticSymbol* currentSymbol) { - // TODO: + foreach (aliasThis; currentSymbol.aliasThis) + { + auto parts = currentSymbol.acSymbol.getPartsByName(aliasThis); + if (parts.length == 0 || parts[0].type is null) + continue; + currentSymbol.acSymbol.aliasThisParts.insert(parts[0].type.parts[]); + } } - void resolveMixinTemplates(SemanticSymbol* currentSymbol) + void resolveMixinTemplates(SemanticSymbol*) { // TODO: }