This commit is contained in:
Hackerpilot 2013-07-19 00:01:45 +00:00
commit 67da8ce61b
6 changed files with 364 additions and 22 deletions

View File

@ -4,3 +4,5 @@ DCD
The D Completion Daemon is an auto-complete program for the D programming language
Or at least it will be once I write it.
Be sure to run ```git submodule update --init``` after cloning this repository.

View File

@ -1,11 +1,14 @@
module autocomplete;
import std.algorithm;
import std.array;
import std.stdio;
import std.conv;
import std.d.ast;
import std.d.lexer;
import std.d.parser;
import std.d.ast;
import std.range;
import std.stdio;
import std.uni;
import messages;
import importutils;
@ -22,37 +25,124 @@ AutocompleteResponse complete(AutocompleteRequest request, string[] importPaths)
auto sortedTokens = assumeSorted(tokenArray);
auto beforeTokens = sortedTokens.lowerBound(cast(size_t) request.cursorPosition);
if (beforeTokens[$ - 1] == TokenType.lParen)
if (beforeTokens[$ - 1] == TokenType.lParen && beforeTokens.length >= 2)
{
if (beforeTokens[$ - 2] == TokenType.traits)
switch (beforeTokens[$ - 2].type)
{
case TokenType.traits:
response.completionType = CompletionType.identifiers;
for (size_t i = 0; i < traits.length; i++)
{
response.completions ~= traits[i];
response.completionKinds ~= CompletionKind.keyword;
}
}
else if (beforeTokens[$ - 2] == TokenType.scope_)
{
break;
case TokenType.scope_:
response.completionType = CompletionType.identifiers;
for (size_t i = 0; i < scopes.length; i++)
{
response.completions ~= scopes[i];
response.completionKinds ~= CompletionKind.keyword;
}
}
else if (beforeTokens[$ - 2] == TokenType.version_)
{
case TokenType.version_:
response.completionType = CompletionType.identifiers;
for (size_t i = 0; i < versions.length; i++)
{
response.completions ~= versions[i];
response.completionKinds ~= CompletionKind.keyword;
}
break;
case TokenType.pragma_:
response.completionType = CompletionType.identifiers;
for (size_t i = 0; i < pragmas.length; i++)
{
response.completions ~= pragmas[i];
response.completionKinds ~= CompletionKind.keyword;
}
break;
case TokenType.identifier:
// TODO
break;
default:
break;
}
}
else
else if (beforeTokens[$ - 1] == TokenType.dot && beforeTokens.length >= 2)
{
switch (beforeTokens[$ - 2].type)
{
case TokenType.int_:
case TokenType.uint_:
case TokenType.long_:
case TokenType.ulong_:
case TokenType.char_:
case TokenType.wchar_:
case TokenType.dchar_:
case TokenType.bool_:
case TokenType.byte_:
case TokenType.ubyte_:
case TokenType.short_:
case TokenType.ushort_:
case TokenType.cent_:
case TokenType.ucent_:
response.completionType = CompletionType.identifiers;
for (size_t i = 0; i < integerProperties.length; i++)
{
response.completions ~= integerProperties[i];
response.completionKinds ~= CompletionKind.keyword;
}
break;
case TokenType.float_:
case TokenType.ifloat_:
case TokenType.cfloat_:
case TokenType.idouble_:
case TokenType.cdouble_:
case TokenType.double_:
case TokenType.real_:
case TokenType.ireal_:
case TokenType.creal_:
response.completionType = CompletionType.identifiers;
for (size_t i = 0; i < floatProperties.length; i++)
{
response.completions ~= floatProperties[i];
response.completionKinds ~= CompletionKind.keyword;
}
break;
case TokenType.stringLiteral:
case TokenType.wstringLiteral:
case TokenType.dstringLiteral:
response.completionType = CompletionType.identifiers;
for (size_t i = 0; i < arrayProperties.length; i++)
{
response.completions ~= arrayProperties[i];
response.completionKinds ~= CompletionKind.keyword;
}
break;
case TokenType.identifier:
// TODO: This is a placeholder
response.completionType = CompletionType.identifiers;
for (size_t i = 0; i < allProperties.length; i++)
{
response.completions ~= allProperties[i];
response.completionKinds ~= CompletionKind.keyword;
}
break;
case TokenType.lParen:
case TokenType.lBrace:
case TokenType.lBracket:
case TokenType.semicolon:
case TokenType.colon:
// TODO: global scope
break;
case TokenType.rParen:
case TokenType.rBrace:
case TokenType.rBracket:
default:
// TODO
break;
}
}
else if (beforeTokens[$ - 1] == TokenType.identifier)
{
Module mod = parseModule(tokenArray, request.fileName, &messageFunction);
@ -66,3 +156,88 @@ void messageFunction(string fileName, int line, int column, string message)
{
// does nothing
}
string createCamelCaseRegex(string input)
{
dstring output;
uint i;
foreach (dchar d; input)
{
if (isLower(d))
output ~= d;
else if (i > 0)
{
output ~= ".*";
output ~= d;
}
i++;
}
return to!string(output ~ ".*");
}
unittest
{
assert("ClNa".createCamelCaseRegex() == "Cl.*Na.*");
}
enum SymbolKind
{
className,
interfaceName,
enumName,
variableName,
structName,
unionName,
functionName
}
class Symbol
{
Symbol[] parts;
string name;
SymbolKind kind;
Type[string] templateParameters;
}
class Scope
{
public:
/**
* @return the innermost Scope that contains the given cursor position.
*/
const(Scope) findCurrentScope(size_t cursorPosition) const
{
if (cursorPosition < start || cursorPosition > end)
return null;
foreach (sc; children)
{
auto s = sc.findCurrentScope(cursorPosition);
if (s is null)
continue;
else
return s;
}
return this;
}
const(Symbol)[] getSymbolsInScope() const
{
return symbols ~ parent.getSymbolsInScope();
}
Symbol[] getSymbolsInScope(string name)
{
Symbol[] results;
symbols.filter!(x => x.name == name)().copy(results);
parent.getSymbolsInScope(name).copy(results);
return results;
}
private:
size_t start;
size_t end;
Symbol[] symbols;
Scope parent;
Scope[] children;
}

View File

@ -12,7 +12,7 @@ int main(string[] args)
{
int cursorPos = -1;
string[] importPaths;
ushort port = 9090;
ushort port = 9166;
bool help;
try
@ -58,9 +58,12 @@ int main(string[] args)
scope (exit) { socket.shutdown(SocketShutdown.BOTH); socket.close(); }
socket.connect(new InternetAddress("127.0.0.1", port));
socket.blocking = true;
stderr.writeln("Sending ", message.length, " bytes");
auto bytesSent = socket.send(message);
stderr.writeln(bytesSent, " bytes sent");
socket.setOption(SocketOptionLevel.TCP, SocketOption.TCP_NODELAY, 1);
ubyte[] messageBuffer = new ubyte[message.length + message.length.sizeof];
auto messageLength = message.length;
messageBuffer[0 .. 8] = (cast(ubyte*) &messageLength)[0 .. 8];
messageBuffer[8 .. $] = message;
auto bytesSent = socket.send(messageBuffer);
// Get response and write it out
ubyte[1024 * 16] buffer;
@ -88,7 +91,6 @@ int main(string[] args)
writeln(completion);
}
}
stderr.writeln("completed");
return 0;
}
@ -119,5 +121,5 @@ Options:
--port PORTNUMBER | -pPORTNUMBER
Uses PORTNUMBER to communicate with the server instead of the default
port 9091.`, programName);
port 9166.`, programName);
}

View File

@ -1,9 +1,23 @@
module constants;
// The lists in this module should be kept sorted.
/**
* Pragma arguments
*/
immutable string[] pragmas = [
"lib",
"msg",
"startaddress"
];
/**
* Traits arguments
*/
immutable string[] traits = [
"allMembers",
"classInstanceSize",
"compiles"
"compiles",
"derivedMembers",
"getAttributes",
"getMember",
@ -126,5 +140,116 @@ immutable string[] versions = [
"Win64",
"Windows",
"X86",
"X86_64",
"X86_64"
];
/**
* Properties of all types
*/
immutable string[] allProperties = [
"alignof",
"init",
"mangleof",
"sizeof",
"stringof"
];
/**
* Properties of integer types
*/
immutable string[] integerProperties = [
"alignof",
"init",
"mangleof",
"max",
"min",
"sizeof",
"stringof"
];
/**
* Properties of floating point types
*/
immutable string[] floatProperties = [
"alignof",
"dig",
"epsilon",
"im",
"infinity",
"init",
"mangleof",
"mant_dig",
"max",
"max_10_exp",
"max_exp",
"min_10_exp",
"min_exp",
"min_normal",
"nan",
"re",
"sizeof",
"stringof"
];
/**
* Properties of class types
*/
immutable string[] classProperties = [
"alignof",
"classinfo",
"init",
"mangleof",
"__monitor",
"sizeof",
"stringof",
"tupleof",
"__vptr",
];
/**
* Properties of struct types
*/
immutable string[] structProperties = [
"alignof",
"tupleof",
"init",
"mangleof",
"sizeof",
"stringof"
];
/**
* Properties of arrays
*/
immutable string[] arrayProperties = [
"alignof",
"dup",
"idup",
"init",
"length",
"mangleof",
"ptr",
"reverse",
"sizeof",
"sort",
"stringof"
];
/**
* Properties of associative arrays
*/
immutable string[] associativeArrayProperties = [
"alignof",
"byKey",
"byValue",
"dup",
"get",
"init",
"keys",
"length",
"mangleof",
"rehash",
"sizeof",
"stringof",
"values"
];

25
modulecache.d Normal file
View File

@ -0,0 +1,25 @@
module modulecache;
import std.file;
import std.datetime;
struct ModuleCache
{
@disable this();
bool needsReparsing(string mod)
{
if (!exists(mod))
return false;
if (mod !in modificationTimes)
return true;
SysTime access;
SysTime modification;
getTimes(mod, access, modification);
if (modificationTimes[mod] != modification)
return true;
return false;
}
SysTime[string] modificationTimes;
}

View File

@ -11,7 +11,7 @@ import autocomplete;
void main(string[] args)
{
ushort port = 9090;
ushort port = 9166;
bool help;
string[] importPaths;
@ -36,6 +36,19 @@ void main(string[] args)
s.blocking = true;
scope (exit) s.close();
ptrdiff_t bytesReceived = s.receive(buffer);
size_t messageLength;
// bit magic!
(cast(ubyte*) &messageLength)[0..8] = buffer[0..8];
while (bytesReceived < messageLength + 8)
{
auto b = s.receive(buffer[bytesReceived .. $]);
if (b == Socket.ERROR)
{
bytesReceived = Socket.ERROR;
break;
}
bytesReceived += b;
}
if (bytesReceived == Socket.ERROR)
{
@ -46,7 +59,7 @@ void main(string[] args)
{
AutocompleteRequest request;
writeln("Unpacking ", bytesReceived, "/", buffer.length, " bytes into a request");
msgpack.unpack(buffer[0 .. bytesReceived], request);
msgpack.unpack(buffer[8 .. bytesReceived], request);
AutocompleteResponse response = complete(request, importPaths);
ubyte[] responseBytes = msgpack.pack(response);
assert(s.send(responseBytes) == responseBytes.length);
@ -65,5 +78,5 @@ options:
Includes path in the listing of paths that are searched for file imports
--port PORTNUMBER | -pPORTNUMBER
Listens on PORTNUMBER instead of the default port 9091.`, programName);
Listens on PORTNUMBER instead of the default port 9166.`, programName);
}