From 8050ac76bf880577ccb2d3c151d69070b96ff851 Mon Sep 17 00:00:00 2001 From: Hackerpilot Date: Wed, 17 Jul 2013 07:51:35 +0000 Subject: [PATCH 1/2] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 5f70763..2e9daa4 100644 --- a/README.md +++ b/README.md @@ -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. From 28729908172c58576731f5d499638e80598ac584 Mon Sep 17 00:00:00 2001 From: Hackerpilot Date: Thu, 18 Jul 2013 17:02:03 -0700 Subject: [PATCH 2/2] Basic proof of concept is working --- autocomplete.d | 197 ++++++++++++++++++++++++++++++++++++++++++++++--- client.d | 14 ++-- constants.d | 129 +++++++++++++++++++++++++++++++- modulecache.d | 25 +++++++ server.d | 19 ++++- 5 files changed, 362 insertions(+), 22 deletions(-) create mode 100644 modulecache.d diff --git a/autocomplete.d b/autocomplete.d index 59fa457..ee3b62c 100644 --- a/autocomplete.d +++ b/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; +} diff --git a/client.d b/client.d index e472cf9..b93549a 100644 --- a/client.d +++ b/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.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); } diff --git a/constants.d b/constants.d index 222331d..a853fd5 100644 --- a/constants.d +++ b/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" ]; diff --git a/modulecache.d b/modulecache.d new file mode 100644 index 0000000..b235a53 --- /dev/null +++ b/modulecache.d @@ -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; +} diff --git a/server.d b/server.d index 2202af3..cceb9ea 100644 --- a/server.d +++ b/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); }