Able to autocomplete some symbols from other modules now. Still crashes a lot

This commit is contained in:
Hackerpilot 2013-08-08 02:40:15 +00:00
parent 814dcbd26a
commit ffe8169ead
7 changed files with 151 additions and 36 deletions

View File

@ -18,6 +18,7 @@
module actypes; module actypes;
import stdx.d.lexer;
import stdx.d.ast; import stdx.d.ast;
import std.algorithm; import std.algorithm;
import std.stdio; import std.stdio;
@ -161,6 +162,34 @@ public:
return null; return null;
} }
void resolveSymbolTypes()
{
foreach (s; symbols.filter!(a => a.kind == CompletionKind.variableName)())
{
Type type = s.type;
if (type.typeSuffixes.length == 0)
{
if (type.type2.builtinType != TokenType.invalid)
{
s.resolvedType = findSymbolInCurrentScope(s.location,
getTokenValue(type.type2.builtinType));
}
else if (type.type2.symbol !is null)
{
Symbol sym = type.type2.symbol;
if (sym.identifierOrTemplateChain.identifiersOrTemplateInstances.length != 1)
return;
s.resolvedType = findSymbolInCurrentScope(s.location,
sym.identifierOrTemplateChain.identifiersOrTemplateInstances[0].identifier.value);
}
}
}
foreach (c; children)
{
c.resolveSymbolTypes();
}
}
/** /**
* Index of the opening brace * Index of the opening brace
*/ */

View File

@ -26,8 +26,9 @@ import std.stdio;
import actypes; import actypes;
import messages; import messages;
import modulecache;
class AutoCompleteVisitor : ASTVisitor class AutocompleteVisitor : ASTVisitor
{ {
alias ASTVisitor.visit visit; alias ASTVisitor.visit visit;
@ -35,6 +36,7 @@ class AutoCompleteVisitor : ASTVisitor
{ {
auto symbol = new ACSymbol; auto symbol = new ACSymbol;
symbol.name = dec.name.value; symbol.name = dec.name.value;
symbol.location = dec.name.startIndex;
symbol.kind = CompletionKind.structName; symbol.kind = CompletionKind.structName;
mixin (visitAndAdd); mixin (visitAndAdd);
} }
@ -43,6 +45,7 @@ class AutoCompleteVisitor : ASTVisitor
{ {
auto symbol = new ACSymbol; auto symbol = new ACSymbol;
symbol.name = dec.name.value; symbol.name = dec.name.value;
symbol.location = dec.name.startIndex;
symbol.kind = CompletionKind.className; symbol.kind = CompletionKind.className;
mixin (visitAndAdd); mixin (visitAndAdd);
} }
@ -51,6 +54,7 @@ class AutoCompleteVisitor : ASTVisitor
{ {
auto symbol = new ACSymbol; auto symbol = new ACSymbol;
symbol.name = dec.name.value; symbol.name = dec.name.value;
symbol.location = dec.name.startIndex;
symbol.kind = CompletionKind.interfaceName; symbol.kind = CompletionKind.interfaceName;
mixin (visitAndAdd); mixin (visitAndAdd);
} }
@ -59,6 +63,8 @@ class AutoCompleteVisitor : ASTVisitor
{ {
auto s = scope_; auto s = scope_;
scope_ = new Scope(structBody.startLocation, structBody.endLocation); scope_ = new Scope(structBody.startLocation, structBody.endLocation);
scope_.symbols ~= new ACSymbol("this", CompletionKind.variableName,
parentSymbol);
scope_.parent = s; scope_.parent = s;
structBody.accept(this); structBody.accept(this);
scope_ = s; scope_ = s;
@ -68,15 +74,16 @@ class AutoCompleteVisitor : ASTVisitor
{ {
auto symbol = new ACSymbol; auto symbol = new ACSymbol;
symbol.name = dec.name.value; symbol.name = dec.name.value;
symbol.location = dec.name.startIndex;
symbol.kind = CompletionKind.enumName; symbol.kind = CompletionKind.enumName;
mixin (visitAndAdd); mixin (visitAndAdd);
} }
override void visit(FunctionDeclaration dec) override void visit(FunctionDeclaration dec)
{ {
writeln("Found function declaration ", dec.name.value);
auto symbol = new ACSymbol; auto symbol = new ACSymbol;
symbol.name = dec.name.value; symbol.name = dec.name.value;
symbol.location = dec.name.startIndex;
symbol.kind = CompletionKind.functionName; symbol.kind = CompletionKind.functionName;
mixin (visitAndAdd); mixin (visitAndAdd);
} }
@ -86,7 +93,7 @@ class AutoCompleteVisitor : ASTVisitor
auto s = new ACSymbol; auto s = new ACSymbol;
s.kind = CompletionKind.enumMember; s.kind = CompletionKind.enumMember;
s.name = member.name.value; s.name = member.name.value;
// writeln("Added enum member ", s.name); s.location = member.name.startIndex;
if (parentSymbol !is null) if (parentSymbol !is null)
parentSymbol.parts ~= s; parentSymbol.parts ~= s;
} }
@ -95,10 +102,10 @@ class AutoCompleteVisitor : ASTVisitor
{ {
foreach (d; dec.declarators) foreach (d; dec.declarators)
{ {
writeln("Found variable declaration ", d.name.value);
auto symbol = new ACSymbol; auto symbol = new ACSymbol;
symbol.type = dec.type; symbol.type = dec.type;
symbol.name = d.name.value; symbol.name = d.name.value;
symbol.location = d.name.startIndex;
symbol.kind = CompletionKind.variableName; symbol.kind = CompletionKind.variableName;
if (parentSymbol is null) if (parentSymbol is null)
symbols ~= symbol; symbols ~= symbol;
@ -110,19 +117,22 @@ class AutoCompleteVisitor : ASTVisitor
override void visit(ImportDeclaration dec) override void visit(ImportDeclaration dec)
{ {
if (!currentFile) return;
foreach (singleImport; dec.singleImports) foreach (singleImport; dec.singleImports)
{ {
imports ~= convertChainToImportPath(singleImport.identifierChain); scope_.symbols ~= ModuleCache.getSymbolsInModule(
convertChainToImportPath(singleImport.identifierChain));
} }
if (dec.importBindings !is null) if (dec.importBindings !is null)
{ {
imports ~= convertChainToImportPath(dec.importBindings.singleImport.identifierChain); scope_.symbols ~= ModuleCache.getSymbolsInModule(
convertChainToImportPath(
dec.importBindings.singleImport.identifierChain));
} }
} }
override void visit(BlockStatement blockStatement) override void visit(BlockStatement blockStatement)
{ {
writeln("Processing block statement");
auto s = scope_; auto s = scope_;
scope_ = new Scope(blockStatement.startLocation, scope_ = new Scope(blockStatement.startLocation,
blockStatement.endLocation); blockStatement.endLocation);
@ -157,6 +167,7 @@ class AutoCompleteVisitor : ASTVisitor
ACSymbol parentSymbol; ACSymbol parentSymbol;
Scope scope_; Scope scope_;
string[] imports = ["object"]; string[] imports = ["object"];
bool currentFile = false;
private: private:
static enum string visitAndAdd = q{ static enum string visitAndAdd = q{
@ -174,10 +185,11 @@ private:
void doesNothing(string, int, int, string) {} void doesNothing(string, int, int, string) {}
AutoCompleteVisitor processModule(const(Token)[] tokens) AutocompleteVisitor processModule(const(Token)[] tokens)
{ {
Module mod = parseModule(tokens, "", null/*&doesNothing*/); Module mod = parseModule(tokens, "", &doesNothing);
auto visitor = new AutoCompleteVisitor; auto visitor = new AutocompleteVisitor;
visitor.currentFile = true;
visitor.visit(mod); visitor.visit(mod);
return visitor; return visitor;
} }

View File

@ -1,2 +1,2 @@
dmd client.d messages.d msgpack-d/src/msgpack.d -Imsgpack-d/src -ofdcd-client dmd -wi client.d messages.d msgpack-d/src/msgpack.d -Imsgpack-d/src -ofdcd-client
dmd server.d actypes.d messages.d constants.d acvisitor.d autocomplete.d ../dscanner/stdx/d/ast.d ../dscanner/stdx/d/parser.d ../dscanner/stdx/d/lexer.d ../dscanner/stdx/d/entities.d msgpack-d/src/msgpack.d -Imsgpack-d/src -I../dscanner/ -ofdcd-server dmd -wi server.d modulecache.d actypes.d messages.d constants.d acvisitor.d autocomplete.d ../dscanner/stdx/d/ast.d ../dscanner/stdx/d/parser.d ../dscanner/stdx/d/lexer.d ../dscanner/stdx/d/entities.d msgpack-d/src/msgpack.d -Imsgpack-d/src -I../dscanner/ -ofdcd-server

View File

@ -32,11 +32,12 @@ int main(string[] args)
string[] importPaths; string[] importPaths;
ushort port = 9166; ushort port = 9166;
bool help; bool help;
bool shutdown;
try try
{ {
getopt(args, "cursorPos|c", &cursorPos, "I", &importPaths, getopt(args, "cursorPos|c", &cursorPos, "I", &importPaths,
"port|p", &port, "help|h", &help); "port|p", &port, "help|h", &help, "shutdown", &shutdown);
} }
catch (Exception e) catch (Exception e)
{ {
@ -49,6 +50,23 @@ int main(string[] args)
return 0; return 0;
} }
if (shutdown)
{
AutocompleteRequest request;
request.kind = RequestKind.shutdown;
auto socket = new TcpSocket(AddressFamily.INET);
scope (exit) { socket.shutdown(SocketShutdown.BOTH); socket.close(); }
socket.connect(new InternetAddress("127.0.0.1", port));
socket.blocking = true;
socket.setOption(SocketOptionLevel.TCP, SocketOption.TCP_NODELAY, 1);
ubyte[] message = msgpack.pack(request);
ubyte[] messageBuffer = new ubyte[message.length + message.length.sizeof];
auto messageLength = message.length;
messageBuffer[0 .. 8] = (cast(ubyte*) &messageLength)[0 .. 8];
messageBuffer[8 .. $] = message[];
return socket.send(messageBuffer) == messageBuffer.length ? 0 : 1;
}
// cursor position is a required argument // cursor position is a required argument
if (cursorPos == size_t.max) if (cursorPos == size_t.max)
{ {
@ -118,24 +136,32 @@ void printHelp(string programName)
` `
Usage: %1$s --cursorPos NUMBER [options] [FILENAME] Usage: %1$s --cursorPos NUMBER [options] [FILENAME]
or: %1$s -cNUMBER [options] [FILENAME] or: %1$s -cNUMBER [options] [FILENAME]
or: %1$s --clearCache
or: %1$s --shutdown
A file name is optional. If it is given, autocomplete information will be A file name is optional. If it is given, autocomplete information will be
given for the file specified. If it is missing, input will be read from given for the file specified. If it is missing, input will be read from
stdin instead. stdin instead.
Source code is assumed to be UTF-8 encoded. Source code is assumed to be UTF-8 encoded and must not exceed 4 megabytes.
Mandatory Arguments:
--cursorPos | -c position
Provides auto-completion at the given cursor position. The cursor
position is measured in bytes from the beginning of the source code.
Options: Options:
--help | -h --help | -h
Displays this help message Displays this help message
--cursorPos | -c position
Provides auto-completion at the given cursor position. The cursor
position is measured in bytes from the beginning of the source code.
--clearCache
Instructs the server to clear out its autocompletion cache.
--shutdown
Instructs the server to shut down.
-IPATH -IPATH
Includes PATH in the listing of paths that are searched for file imports Instructs the server to add PATH to its list of paths searced for
imported modules.
--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

View File

@ -87,7 +87,8 @@ enum RequestKind
{ {
autocomplete, autocomplete,
clearCache, clearCache,
addImport addImport,
shutdown
} }
/** /**

View File

@ -20,8 +20,14 @@ module modulecache;
import std.file; import std.file;
import std.datetime; import std.datetime;
import stdx.d.lexer;
import stdx.d.parser;
import stdx.d.ast;
import std.stdio;
import std.array;
import acvisitor; import acvisitor;
import actypes;
struct CacheEntry struct CacheEntry
{ {
@ -39,58 +45,80 @@ struct ModuleCache
/** /**
* Clears the completion cache * Clears the completion cache
*/ */
void clear() static void clear()
{ {
cache = []; cache.clear();
} }
/** /**
* Adds the given path to the list of directories checked for imports * Adds the given path to the list of directories checked for imports
*/ */
void addImportPath(string path) static void addImportPath(string path)
{ {
importPaths ~= path; importPaths ~= path;
} }
/** /**
* Params: * Params:
* moduleName = the name of the module in "a.b.c" form * moduleName = the name of the module in "a/b.d" form
* Returns: * Returns:
* The symbols defined in the given module * The symbols defined in the given module
*/ */
ACSymbol[] getSymbolsInModule(string moduleName) static ACSymbol[] getSymbolsInModule(string moduleName)
{ {
string location = resolveImportLoctation(moduleName); string location = resolveImportLoctation(moduleName);
if (location is null)
return [];
if (!needsReparsing(location)) if (!needsReparsing(location))
return; return cache[location].symbols;
File f = File(location);
ubyte[] source = uninitializedArray!(ubyte[])(f.size);
f.rawRead(source);
LexerConfig config;
auto tokens = source.byToken(config).array();
Module mod = parseModule(tokens, location, &doesNothing); Module mod = parseModule(tokens, location, &doesNothing);
auto visitor = new AutocompleteVisitor; auto visitor = new AutocompleteVisitor;
visitor.visit(mod); visitor.visit(mod);
cache[location].mod = visitor.symbols; SysTime access;
SysTime modification;
getTimes(location, access, modification);
if (location !in cache)
cache[location] = CacheEntry.init;
cache[location].modificationTime = modification;
cache[location].symbols = visitor.symbols;
return cache[location].symbols;
} }
/** /**
* Params: * Params:
* moduleName the name of the module being imported, in "a.b.c" style * moduleName the name of the module being imported, in "a/b/c.d" style
* Returns: * Returns:
* The absolute path to the file that contains the module, or null if * The absolute path to the file that contains the module, or null if
* not found. * not found.
*/ */
string resolveImportLoctation(string moduleName) static string resolveImportLoctation(string moduleName)
{ {
writeln("Resolving location of ", moduleName);
foreach (path; importPaths) foreach (path; importPaths)
{ {
string filePath = path ~ "/" ~ imp; string filePath = path ~ "/" ~ moduleName;
if (filePath.exists()) if (filePath.exists())
return filePath; return filePath;
filePath ~= "i"; // check for x.di if x.d isn't found filePath ~= "i"; // check for x.di if x.d isn't found
if (filePath.exists()) if (filePath.exists())
return filePath; return filePath;
} }
writeln("Could not find ", moduleName);
return null; return null;
} }
static const(string[]) getImportPaths()
{
return cast(const(string[])) importPaths;
}
private: private:
/** /**
@ -99,7 +127,7 @@ private:
* Returns: * Returns:
* true if the module needs to be reparsed, false otherwise * true if the module needs to be reparsed, false otherwise
*/ */
bool needsReparsing(string mod) static bool needsReparsing(string mod)
{ {
if (!exists(mod) || mod !in cache) if (!exists(mod) || mod !in cache)
return true; return true;
@ -110,8 +138,8 @@ private:
} }
// Mapping of file paths to their cached symbols. // Mapping of file paths to their cached symbols.
CacheEntry[string] cache; static CacheEntry[string] cache;
// Listing of paths to check for imports // Listing of paths to check for imports
string[] importPaths; static string[] importPaths;
} }

View File

@ -21,11 +21,13 @@ module server;
import std.socket; import std.socket;
import std.stdio; import std.stdio;
import std.getopt; import std.getopt;
import std.algorithm;
import msgpack; import msgpack;
import messages; import messages;
import autocomplete; import autocomplete;
import modulecache;
int main(string[] args) int main(string[] args)
{ {
@ -44,6 +46,18 @@ int main(string[] args)
return 1; return 1;
} }
// begin hack
importPaths ~= "/home/alaran/src/dcd";
importPaths ~= "/home/alaran/src/dscanner";
importPaths ~= "/usr/include/d2/core";
importPaths ~= "/usr/include/d2/phobos";
importPaths ~= "/usr/include/d2/druntime/import";
// end hack
foreach (path; importPaths)
ModuleCache.addImportPath(path);
writeln(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));
@ -84,11 +98,16 @@ int main(string[] args)
msgpack.unpack(buffer[8 .. bytesReceived], request); msgpack.unpack(buffer[8 .. bytesReceived], request);
if (request.kind == RequestKind.addImport) if (request.kind == RequestKind.addImport)
{ {
//ModuleCache.addImportPath();
} }
else if (request.kind == RequestKind.clearCache) else if (request.kind == RequestKind.clearCache)
{ {
ModuleCache.clear();
}
else if (request.kind == RequestKind.shutdown)
{
writeln("Shutting down.");
break;
} }
else else
{ {