Merge branch 'master' of https://github.com/Hackerpilot/DCD
This commit is contained in:
commit
67da8ce61b
|
@ -4,3 +4,5 @@ DCD
|
||||||
The D Completion Daemon is an auto-complete program for the D programming language
|
The D Completion Daemon is an auto-complete program for the D programming language
|
||||||
|
|
||||||
Or at least it will be once I write it.
|
Or at least it will be once I write it.
|
||||||
|
|
||||||
|
Be sure to run ```git submodule update --init``` after cloning this repository.
|
||||||
|
|
197
autocomplete.d
197
autocomplete.d
|
@ -1,11 +1,14 @@
|
||||||
module autocomplete;
|
module autocomplete;
|
||||||
|
|
||||||
|
import std.algorithm;
|
||||||
import std.array;
|
import std.array;
|
||||||
import std.stdio;
|
import std.conv;
|
||||||
|
import std.d.ast;
|
||||||
import std.d.lexer;
|
import std.d.lexer;
|
||||||
import std.d.parser;
|
import std.d.parser;
|
||||||
import std.d.ast;
|
|
||||||
import std.range;
|
import std.range;
|
||||||
|
import std.stdio;
|
||||||
|
import std.uni;
|
||||||
|
|
||||||
import messages;
|
import messages;
|
||||||
import importutils;
|
import importutils;
|
||||||
|
@ -22,37 +25,124 @@ AutocompleteResponse complete(AutocompleteRequest request, string[] importPaths)
|
||||||
auto sortedTokens = assumeSorted(tokenArray);
|
auto sortedTokens = assumeSorted(tokenArray);
|
||||||
|
|
||||||
auto beforeTokens = sortedTokens.lowerBound(cast(size_t) request.cursorPosition);
|
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;
|
response.completionType = CompletionType.identifiers;
|
||||||
for (size_t i = 0; i < traits.length; i++)
|
for (size_t i = 0; i < traits.length; i++)
|
||||||
{
|
{
|
||||||
response.completions ~= traits[i];
|
response.completions ~= traits[i];
|
||||||
response.completionKinds ~= CompletionKind.keyword;
|
response.completionKinds ~= CompletionKind.keyword;
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
else if (beforeTokens[$ - 2] == TokenType.scope_)
|
case TokenType.scope_:
|
||||||
{
|
|
||||||
response.completionType = CompletionType.identifiers;
|
response.completionType = CompletionType.identifiers;
|
||||||
for (size_t i = 0; i < scopes.length; i++)
|
for (size_t i = 0; i < scopes.length; i++)
|
||||||
{
|
{
|
||||||
response.completions ~= scopes[i];
|
response.completions ~= scopes[i];
|
||||||
response.completionKinds ~= CompletionKind.keyword;
|
response.completionKinds ~= CompletionKind.keyword;
|
||||||
}
|
}
|
||||||
}
|
case TokenType.version_:
|
||||||
else if (beforeTokens[$ - 2] == TokenType.version_)
|
|
||||||
{
|
|
||||||
response.completionType = CompletionType.identifiers;
|
response.completionType = CompletionType.identifiers;
|
||||||
for (size_t i = 0; i < versions.length; i++)
|
for (size_t i = 0; i < versions.length; i++)
|
||||||
{
|
{
|
||||||
response.completions ~= versions[i];
|
response.completions ~= versions[i];
|
||||||
response.completionKinds ~= CompletionKind.keyword;
|
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);
|
Module mod = parseModule(tokenArray, request.fileName, &messageFunction);
|
||||||
|
|
||||||
|
@ -66,3 +156,88 @@ void messageFunction(string fileName, int line, int column, string message)
|
||||||
{
|
{
|
||||||
// does nothing
|
// 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;
|
||||||
|
}
|
||||||
|
|
14
client.d
14
client.d
|
@ -12,7 +12,7 @@ int main(string[] args)
|
||||||
{
|
{
|
||||||
int cursorPos = -1;
|
int cursorPos = -1;
|
||||||
string[] importPaths;
|
string[] importPaths;
|
||||||
ushort port = 9090;
|
ushort port = 9166;
|
||||||
bool help;
|
bool help;
|
||||||
|
|
||||||
try
|
try
|
||||||
|
@ -58,9 +58,12 @@ int main(string[] args)
|
||||||
scope (exit) { socket.shutdown(SocketShutdown.BOTH); socket.close(); }
|
scope (exit) { socket.shutdown(SocketShutdown.BOTH); socket.close(); }
|
||||||
socket.connect(new InternetAddress("127.0.0.1", port));
|
socket.connect(new InternetAddress("127.0.0.1", port));
|
||||||
socket.blocking = true;
|
socket.blocking = true;
|
||||||
stderr.writeln("Sending ", message.length, " bytes");
|
socket.setOption(SocketOptionLevel.TCP, SocketOption.TCP_NODELAY, 1);
|
||||||
auto bytesSent = socket.send(message);
|
ubyte[] messageBuffer = new ubyte[message.length + message.length.sizeof];
|
||||||
stderr.writeln(bytesSent, " bytes sent");
|
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
|
// Get response and write it out
|
||||||
ubyte[1024 * 16] buffer;
|
ubyte[1024 * 16] buffer;
|
||||||
|
@ -88,7 +91,6 @@ int main(string[] args)
|
||||||
writeln(completion);
|
writeln(completion);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stderr.writeln("completed");
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,5 +121,5 @@ Options:
|
||||||
|
|
||||||
--port PORTNUMBER | -pPORTNUMBER
|
--port PORTNUMBER | -pPORTNUMBER
|
||||||
Uses PORTNUMBER to communicate with the server instead of the default
|
Uses PORTNUMBER to communicate with the server instead of the default
|
||||||
port 9091.`, programName);
|
port 9166.`, programName);
|
||||||
}
|
}
|
||||||
|
|
129
constants.d
129
constants.d
|
@ -1,9 +1,23 @@
|
||||||
module constants;
|
module constants;
|
||||||
|
|
||||||
|
// The lists in this module should be kept sorted.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pragma arguments
|
||||||
|
*/
|
||||||
|
immutable string[] pragmas = [
|
||||||
|
"lib",
|
||||||
|
"msg",
|
||||||
|
"startaddress"
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Traits arguments
|
||||||
|
*/
|
||||||
immutable string[] traits = [
|
immutable string[] traits = [
|
||||||
"allMembers",
|
"allMembers",
|
||||||
"classInstanceSize",
|
"classInstanceSize",
|
||||||
"compiles"
|
"compiles",
|
||||||
"derivedMembers",
|
"derivedMembers",
|
||||||
"getAttributes",
|
"getAttributes",
|
||||||
"getMember",
|
"getMember",
|
||||||
|
@ -126,5 +140,116 @@ immutable string[] versions = [
|
||||||
"Win64",
|
"Win64",
|
||||||
"Windows",
|
"Windows",
|
||||||
"X86",
|
"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"
|
||||||
];
|
];
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
19
server.d
19
server.d
|
@ -11,7 +11,7 @@ import autocomplete;
|
||||||
|
|
||||||
void main(string[] args)
|
void main(string[] args)
|
||||||
{
|
{
|
||||||
ushort port = 9090;
|
ushort port = 9166;
|
||||||
bool help;
|
bool help;
|
||||||
string[] importPaths;
|
string[] importPaths;
|
||||||
|
|
||||||
|
@ -36,6 +36,19 @@ void main(string[] args)
|
||||||
s.blocking = true;
|
s.blocking = true;
|
||||||
scope (exit) s.close();
|
scope (exit) s.close();
|
||||||
ptrdiff_t bytesReceived = s.receive(buffer);
|
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)
|
if (bytesReceived == Socket.ERROR)
|
||||||
{
|
{
|
||||||
|
@ -46,7 +59,7 @@ void main(string[] args)
|
||||||
{
|
{
|
||||||
AutocompleteRequest request;
|
AutocompleteRequest request;
|
||||||
writeln("Unpacking ", bytesReceived, "/", buffer.length, " bytes into a 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);
|
AutocompleteResponse response = complete(request, importPaths);
|
||||||
ubyte[] responseBytes = msgpack.pack(response);
|
ubyte[] responseBytes = msgpack.pack(response);
|
||||||
assert(s.send(responseBytes) == responseBytes.length);
|
assert(s.send(responseBytes) == responseBytes.length);
|
||||||
|
@ -65,5 +78,5 @@ options:
|
||||||
Includes path in the listing of paths that are searched for file imports
|
Includes path in the listing of paths that are searched for file imports
|
||||||
|
|
||||||
--port PORTNUMBER | -pPORTNUMBER
|
--port PORTNUMBER | -pPORTNUMBER
|
||||||
Listens on PORTNUMBER instead of the default port 9091.`, programName);
|
Listens on PORTNUMBER instead of the default port 9166.`, programName);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue