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
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
14
client.d
14
client.d
|
@ -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);
|
||||
}
|
||||
|
|
129
constants.d
129
constants.d
|
@ -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"
|
||||
];
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue