This commit is contained in:
Hackerpilot 2014-07-11 18:51:48 +00:00
commit fb46f25560
6 changed files with 105 additions and 27 deletions

View File

@ -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") ![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. 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 The server (dcd-server) is responsible for caching imported files, calculating autocomplete information, and sending it
back to the client. back to the client.
#Status #Status
*This program is still in an alpha state.* This program is reasonably stable.
* Working: * Working:
* Autocompletion of properties of built-in types such as int, float, double, etc. * 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 * Finding the declaration location of a symbol at the cursor
* *import* statement completions * *import* statement completions
* Display of documentation comments in function call tips * Display of documentation comments in function call tips
* *alias this*
* Not working: * Not working:
* Automatic starting of the server by the client * Automatic starting of the server by the client
* UFCS * UFCS
* Autocompletion of declarations with template arguments * Autocompletion of declarations with template arguments
* *auto* declarations * *auto* declarations
* *alias this*
* Determining the type of an enum member when no base type is specified, but the first member has an initialaizer * 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 * That one feature that you *REALLY* needed
#Setup #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 ```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. 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. 1. Configure your text editor to call the dcd-client program. See the *editors* folder for directions on configuring your specific editor.

View File

@ -107,7 +107,11 @@ public:
ACSymbol*[] getPartsByName(string name) ACSymbol*[] getPartsByName(string name)
{ {
ACSymbol s = ACSymbol(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; 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 * Calltip to display if this is a function
*/ */
@ -233,14 +242,9 @@ struct Scope
{ {
import std.range; import std.range;
ACSymbol s = ACSymbol(name); ACSymbol s = ACSymbol(name);
auto r = array(symbols.equalRange(&s)); auto er = symbols.equalRange(&s);
foreach (i; r) if (!er.empty)
{ return cast(typeof(return)) array(er);
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;
if (parent is null) if (parent is null)
return []; return [];
return parent.getSymbolsByName(name); return parent.getSymbolsByName(name);
@ -262,6 +266,13 @@ struct Scope
return s.getSymbolsByName(name); return s.getSymbolsByName(name);
} }
ACSymbol*[] getSymbolsAtGlobalScope(string name)
{
if (parent !is null)
return parent.getSymbolsAtGlobalScope(name);
return getSymbolsByName(name);
}
/// Imports contained in this scope /// Imports contained in this scope
UnrolledList!(ImportInformation*) importInformation; UnrolledList!(ImportInformation*) importInformation;
@ -313,7 +324,7 @@ TTree!(ACSymbol*, true, "a < b", false) arraySymbols;
TTree!(ACSymbol*, true, "a < b", false) assocArraySymbols; 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; TTree!(ACSymbol*, true, "a < b", false) aggregateSymbols;
@ -506,7 +517,7 @@ static this()
s.parts.insert(stringof_); 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(mangleof_);
aggregateSymbols.insert(alignof_); aggregateSymbols.insert(alignof_);
aggregateSymbols.insert(sizeof_); aggregateSymbols.insert(sizeof_);

View File

@ -185,7 +185,6 @@ AutocompleteResponse complete(const AutocompleteRequest request)
case tok!"[": case tok!"[":
case tok!";": case tok!";":
case tok!":": case tok!":":
// TODO: global scope
break; break;
default: default:
break; break;
@ -299,8 +298,16 @@ ACSymbol*[] getSymbolsByTokenChain(T)(Scope* completionScope,
Log.trace("Getting symbols from token chain", Log.trace("Getting symbols from token chain",
tokens.map!stringToken); tokens.map!stringToken);
// Find the symbol corresponding to the beginning of the chain // Find the symbol corresponding to the beginning of the chain
ACSymbol*[] symbols = completionScope.getSymbolsByNameAndCursor( ACSymbol*[] symbols;
stringToken(tokens[0]), cursorPosition); 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) if (symbols.length == 0)
{ {
Log.error("Could not find declaration of ", stringToken(tokens[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) if (completionType == CompletionType.identifiers)
{ {
import containers.ttree;
if (symbols[0].qualifier == SymbolQualifier.func if (symbols[0].qualifier == SymbolQualifier.func
|| symbols[0].kind == CompletionKind.functionName) || symbols[0].kind == CompletionKind.functionName)
{ {
@ -501,7 +509,10 @@ 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 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] != '*' && 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)))
@ -542,7 +553,15 @@ void setCompletions(T)(ref AutocompleteResponse response,
{ {
auto constructor = symbols[0].getPartsByName("*constructor*"); auto constructor = symbols[0].getPartsByName("*constructor*");
if (constructor.length == 0) 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 else
{ {
symbols = constructor; 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;
}
/** /**
* *
*/ */

View File

@ -37,6 +37,7 @@ dmd\
containers/src/containers/unrolledlist.d\ containers/src/containers/unrolledlist.d\
containers/src/containers/hashset.d\ containers/src/containers/hashset.d\
containers/src/containers/internal/hash.d\ containers/src/containers/internal/hash.d\
containers/src/containers/internal/node.d\
containers/src/containers/slist.d\ containers/src/containers/slist.d\
msgpack-d/src/msgpack.d\ msgpack-d/src/msgpack.d\
-Icontainers/src\ -Icontainers/src\

@ -1 +1 @@
Subproject commit 2a72525f649850f452f21e7f362f70440f487a61 Subproject commit 74b394404947f8c1ea9c6e769fdb413db049d27d

View File

@ -69,11 +69,6 @@ private:
case className: case className:
case interfaceName: case interfaceName:
resolveInheritance(currentSymbol); resolveInheritance(currentSymbol);
goto case structName;
case structName:
case unionName:
resolveAliasThis(currentSymbol);
resolveMixinTemplates(currentSymbol);
break; break;
case variableName: case variableName:
case memberVariableName: case memberVariableName:
@ -85,6 +80,8 @@ private:
t = t.type; t = t.type;
currentSymbol.acSymbol.type = t; currentSymbol.acSymbol.type = t;
break; break;
case structName:
case unionName:
case enumName: case enumName:
case keyword: case keyword:
case enumMember: case enumMember:
@ -97,10 +94,24 @@ private:
case mixinTemplateName: case mixinTemplateName:
break; break;
} }
foreach (child; currentSymbol.children) foreach (child; currentSymbol.children)
{ {
thirdPass(child); 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) void resolveInheritance(SemanticSymbol* currentSymbol)
@ -129,10 +140,16 @@ private:
void resolveAliasThis(SemanticSymbol* currentSymbol) 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: // TODO:
} }