Autocomplete of enum members and handling of opSlice and opIndex
This commit is contained in:
parent
fe11e9d359
commit
bf3c7ba500
|
|
@ -17,14 +17,13 @@ back to the client.
|
||||||
* Autocompletion of class, struct, and interface instances.
|
* Autocompletion of class, struct, and interface instances.
|
||||||
* Display of call tips (but only for the first overload)
|
* Display of call tips (but only for the first overload)
|
||||||
* Not working:
|
* Not working:
|
||||||
|
* Automatic starting of the server by the client
|
||||||
* Windows support (I don't know that it won't work, but this program is not tested on Windows yet)
|
* Windows support (I don't know that it won't work, but this program is not tested on Windows yet)
|
||||||
* UFCS
|
* UFCS
|
||||||
* Templates
|
* Templated declarations
|
||||||
* *auto* declarations
|
* *auto* declarations
|
||||||
* Operator overloading (opIndex, opSlice, etc) when autocompleting
|
* Determining the type of an enum member when no base type is specified, but the first member has an initialaizer
|
||||||
* Instances of enum types resolve to the enum itself instead of the enum base type
|
|
||||||
* Public imports
|
* Public imports
|
||||||
* Array and associative array indexing in autocompletion
|
|
||||||
* That one feature that you *REALLY* needed
|
* That one feature that you *REALLY* needed
|
||||||
|
|
||||||
#Setup
|
#Setup
|
||||||
|
|
|
||||||
46
actypes.d
46
actypes.d
|
|
@ -216,35 +216,37 @@ public:
|
||||||
*/
|
*/
|
||||||
void resolveSymbolTypes()
|
void resolveSymbolTypes()
|
||||||
{
|
{
|
||||||
|
// TODO: auto declarations.
|
||||||
|
|
||||||
// We only care about resolving types of variables, all other symbols
|
// We only care about resolving types of variables, all other symbols
|
||||||
// don't have any indirection
|
// don't have any indirection
|
||||||
// TODO: Resolve types of enum members
|
|
||||||
foreach (ref s; symbols.filter!(a => (a.kind == CompletionKind.variableName
|
foreach (ref s; symbols.filter!(a => (a.kind == CompletionKind.variableName
|
||||||
|| a.kind == CompletionKind.functionName
|
|| a.kind == CompletionKind.functionName || a.kind == CompletionKind.memberVariableName
|
||||||
|| a.kind == CompletionKind.memberVariableName) && a.resolvedType is null)())
|
|| a.kind == CompletionKind.enumMember) && a.resolvedType is null)())
|
||||||
{
|
{
|
||||||
//writeln("Resolving type of symbol ", s.name);
|
//writeln("Resolving type of symbol ", s.name);
|
||||||
Type type = s.type;
|
Type type = s.type;
|
||||||
if (type is null)
|
if (type is null)
|
||||||
continue;
|
continue;
|
||||||
if (type.type2.builtinType != TokenType.invalid)
|
|
||||||
{
|
|
||||||
// This part is easy. Autocomplete properties of built-in types
|
|
||||||
s.resolvedType = findSymbolInCurrentScope(s.location,
|
|
||||||
getTokenValue(type.type2.builtinType));
|
|
||||||
}
|
|
||||||
else if (type.type2.symbol !is null)
|
|
||||||
{
|
|
||||||
// Look up a type by its name for cases like class, enum,
|
|
||||||
// interface, struct, or union members.
|
|
||||||
|
|
||||||
// TODO: Does not work with qualified names or template instances
|
if (type.type2.builtinType != TokenType.invalid)
|
||||||
Symbol sym = type.type2.symbol;
|
{
|
||||||
if (sym.identifierOrTemplateChain.identifiersOrTemplateInstances.length != 1)
|
// This part is easy. Autocomplete properties of built-in types
|
||||||
return;
|
s.resolvedType = findSymbolInCurrentScope(s.location,
|
||||||
s.resolvedType = findSymbolInCurrentScope(s.location,
|
getTokenValue(type.type2.builtinType));
|
||||||
sym.identifierOrTemplateChain.identifiersOrTemplateInstances[0].identifier.value);
|
}
|
||||||
}
|
else if (type.type2.symbol !is null)
|
||||||
|
{
|
||||||
|
// Look up a type by its name for cases like class, enum,
|
||||||
|
// interface, struct, or union members.
|
||||||
|
|
||||||
|
// TODO: Does not work with qualified names or template instances
|
||||||
|
Symbol sym = type.type2.symbol;
|
||||||
|
if (sym.identifierOrTemplateChain.identifiersOrTemplateInstances.length != 1)
|
||||||
|
return;
|
||||||
|
s.resolvedType = findSymbolInCurrentScope(s.location,
|
||||||
|
sym.identifierOrTemplateChain.identifiersOrTemplateInstances[0].identifier.value);
|
||||||
|
}
|
||||||
foreach (suffix; type.typeSuffixes)
|
foreach (suffix; type.typeSuffixes)
|
||||||
{
|
{
|
||||||
// Handle type suffixes for declarations, e.g.:
|
// Handle type suffixes for declarations, e.g.:
|
||||||
|
|
@ -264,21 +266,19 @@ public:
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// ormal array
|
// normal array
|
||||||
s.resolvedType.qualifier = SymbolQualifier.array;
|
s.resolvedType.qualifier = SymbolQualifier.array;
|
||||||
s.resolvedType.parts ~= arraySymbols;
|
s.resolvedType.parts ~= arraySymbols;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (suffix.delegateOrFunction.type != TokenType.invalid)
|
else if (suffix.delegateOrFunction.type != TokenType.invalid)
|
||||||
{
|
{
|
||||||
// TODO: figure out how to handle this, if necessary
|
|
||||||
s.resolvedType.qualifier = SymbolQualifier.func;
|
s.resolvedType.qualifier = SymbolQualifier.func;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
foreach (c; children)
|
foreach (c; children)
|
||||||
{
|
{
|
||||||
assert (c !is null);
|
|
||||||
c.resolveSymbolTypes();
|
c.resolveSymbolTypes();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,7 @@ class AutocompleteVisitor : ASTVisitor
|
||||||
|
|
||||||
override void visit(EnumDeclaration dec)
|
override void visit(EnumDeclaration dec)
|
||||||
{
|
{
|
||||||
|
// TODO: Set enum type based on initializer of first member
|
||||||
// writeln("EnumDeclaration visit");
|
// writeln("EnumDeclaration visit");
|
||||||
auto symbol = new ACSymbol;
|
auto symbol = new ACSymbol;
|
||||||
symbol.name = dec.name.value;
|
symbol.name = dec.name.value;
|
||||||
|
|
@ -102,6 +103,8 @@ class AutocompleteVisitor : ASTVisitor
|
||||||
|
|
||||||
if (dec.enumBody !is null)
|
if (dec.enumBody !is null)
|
||||||
{
|
{
|
||||||
|
Scope enumBodyScope = new Scope(dec.enumBody.startLocation,
|
||||||
|
dec.enumBody.endLocation);
|
||||||
foreach (member; dec.enumBody.enumMembers)
|
foreach (member; dec.enumBody.enumMembers)
|
||||||
{
|
{
|
||||||
auto s = new ACSymbol;
|
auto s = new ACSymbol;
|
||||||
|
|
@ -114,7 +117,10 @@ class AutocompleteVisitor : ASTVisitor
|
||||||
s.type = type;
|
s.type = type;
|
||||||
if (parentSymbol !is null)
|
if (parentSymbol !is null)
|
||||||
parentSymbol.parts ~= s;
|
parentSymbol.parts ~= s;
|
||||||
|
enumBodyScope.symbols ~= s;
|
||||||
}
|
}
|
||||||
|
enumBodyScope.parent = scope_;
|
||||||
|
scope_.children ~= enumBodyScope;
|
||||||
}
|
}
|
||||||
|
|
||||||
parentSymbol = p;
|
parentSymbol = p;
|
||||||
|
|
|
||||||
|
|
@ -154,8 +154,6 @@ void setCompletions(T)(ref AutocompleteResponse response,
|
||||||
CompletionType completionType)
|
CompletionType completionType)
|
||||||
{
|
{
|
||||||
|
|
||||||
writeln("Getting completions for ", map!"a.value"(tokens));
|
|
||||||
writeln("Resolving symbols for editor buffer");
|
|
||||||
visitor.scope_.resolveSymbolTypes();
|
visitor.scope_.resolveSymbolTypes();
|
||||||
ACSymbol symbol = visitor.scope_.findSymbolInCurrentScope(cursorPosition, tokens[0].value);
|
ACSymbol symbol = visitor.scope_.findSymbolInCurrentScope(cursorPosition, tokens[0].value);
|
||||||
if (symbol is null)
|
if (symbol is null)
|
||||||
|
|
@ -166,7 +164,8 @@ void setCompletions(T)(ref AutocompleteResponse response,
|
||||||
|
|
||||||
if (completionType == CompletionType.identifiers
|
if (completionType == CompletionType.identifiers
|
||||||
&& symbol.kind == CompletionKind.memberVariableName
|
&& symbol.kind == CompletionKind.memberVariableName
|
||||||
|| symbol.kind == CompletionKind.variableName)
|
|| symbol.kind == CompletionKind.variableName
|
||||||
|
|| symbol.kind == CompletionKind.enumMember)
|
||||||
{
|
{
|
||||||
symbol = symbol.resolvedType;
|
symbol = symbol.resolvedType;
|
||||||
if (symbol is null)
|
if (symbol is null)
|
||||||
|
|
@ -233,6 +232,7 @@ void setCompletions(T)(ref AutocompleteResponse response,
|
||||||
}
|
}
|
||||||
if (symbol.kind == CompletionKind.variableName
|
if (symbol.kind == CompletionKind.variableName
|
||||||
|| symbol.kind == CompletionKind.memberVariableName
|
|| symbol.kind == CompletionKind.memberVariableName
|
||||||
|
|| symbol.kind == CompletionKind.enumMember
|
||||||
|| (symbol.kind == CompletionKind.functionName
|
|| (symbol.kind == CompletionKind.functionName
|
||||||
&& (completionType == CompletionType.identifiers
|
&& (completionType == CompletionType.identifiers
|
||||||
|| i + 1 < tokens.length)))
|
|| i + 1 < tokens.length)))
|
||||||
|
|
@ -248,8 +248,6 @@ void setCompletions(T)(ref AutocompleteResponse response,
|
||||||
case lBracket:
|
case lBracket:
|
||||||
open = TokenType.lBracket;
|
open = TokenType.lBracket;
|
||||||
close = TokenType.rBracket;
|
close = TokenType.rBracket;
|
||||||
// TODO: handle opIndex
|
|
||||||
// TODO: handle opSlice
|
|
||||||
if (symbol.qualifier == SymbolQualifier.array)
|
if (symbol.qualifier == SymbolQualifier.array)
|
||||||
{
|
{
|
||||||
auto h = i;
|
auto h = i;
|
||||||
|
|
@ -267,7 +265,24 @@ void setCompletions(T)(ref AutocompleteResponse response,
|
||||||
skip();
|
skip();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return;
|
{
|
||||||
|
auto h = i;
|
||||||
|
skip();
|
||||||
|
Parser p;
|
||||||
|
p.setTokens(tokens[h .. i].array());
|
||||||
|
ACSymbol overload;
|
||||||
|
if (p.isSliceExpression())
|
||||||
|
overload = symbol.getPartByName("opSlice");
|
||||||
|
else
|
||||||
|
overload = symbol.getPartByName("opIndex");
|
||||||
|
if (overload !is null)
|
||||||
|
{
|
||||||
|
writeln("opIndex or opSlice used, ", overload.name);
|
||||||
|
symbol = overload.resolvedType;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case dot:
|
case dot:
|
||||||
break;
|
break;
|
||||||
|
|
@ -282,10 +297,10 @@ void setCompletions(T)(ref AutocompleteResponse response,
|
||||||
}
|
}
|
||||||
if (completionType == CompletionType.identifiers)
|
if (completionType == CompletionType.identifiers)
|
||||||
{
|
{
|
||||||
writeln("Writing out the parts of ", symbol.name);
|
writeln("Writing completions for ", symbol.name);
|
||||||
foreach (s; symbol.parts)
|
foreach (s; symbol.parts)
|
||||||
{
|
{
|
||||||
//writeln("Adding ", s.name, " to the completion list");
|
writeln("Adding ", s.name, " to the completion list");
|
||||||
response.completionKinds ~= s.kind;
|
response.completionKinds ~= s.kind;
|
||||||
response.completions ~= s.name;
|
response.completions ~= s.name;
|
||||||
}
|
}
|
||||||
|
|
@ -293,7 +308,6 @@ void setCompletions(T)(ref AutocompleteResponse response,
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
writeln("Adding calltip for ", symbol.name, ": ", symbol.calltip);
|
|
||||||
response.completions ~= symbol.calltip;
|
response.completions ~= symbol.calltip;
|
||||||
response.completionType = CompletionType.calltips;
|
response.completionType = CompletionType.calltips;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
12
server.d
12
server.d
|
|
@ -54,10 +54,6 @@ int main(string[] args)
|
||||||
|
|
||||||
importPaths ~= loadConfiguredImportDirs();
|
importPaths ~= loadConfiguredImportDirs();
|
||||||
|
|
||||||
foreach (path; importPaths)
|
|
||||||
ModuleCache.addImportPath(path);
|
|
||||||
writeln("Import directories: ", ModuleCache.getImportPaths());
|
|
||||||
|
|
||||||
auto socket = new TcpSocket(AddressFamily.INET);
|
auto socket = new TcpSocket(AddressFamily.INET);
|
||||||
socket.blocking = true;
|
socket.blocking = true;
|
||||||
socket.bind(new InternetAddress("127.0.0.1", port));
|
socket.bind(new InternetAddress("127.0.0.1", port));
|
||||||
|
|
@ -69,7 +65,15 @@ int main(string[] args)
|
||||||
socket.close();
|
socket.close();
|
||||||
writeln("Sockets shut down.");
|
writeln("Sockets shut down.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (path; importPaths)
|
||||||
|
ModuleCache.addImportPath(path);
|
||||||
|
writeln("Import directories: ", ModuleCache.getImportPaths());
|
||||||
|
|
||||||
ubyte[1024 * 1024 * 4] buffer = void; // 4 megabytes should be enough for anybody...
|
ubyte[1024 * 1024 * 4] buffer = void; // 4 megabytes should be enough for anybody...
|
||||||
|
|
||||||
|
writeln("Startup complete");
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
auto s = socket.accept();
|
auto s = socket.accept();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue