Finally implement #22

This commit is contained in:
Hackerpilot 2014-01-20 04:12:37 -08:00
parent 3c326a3f82
commit 87056b0c58
10 changed files with 147 additions and 56 deletions

2
.gitignore vendored
View File

@ -5,6 +5,8 @@
# *nix binaries # *nix binaries
dcd-client dcd-client
dcd-server dcd-server
dcd-client.o
dcd-server.o
# Perf reports # Perf reports
perf.data perf.data

View File

@ -136,6 +136,11 @@ public:
*/ */
string symbolFile; string symbolFile;
/**
* Documentation for the symbol.
*/
string doc;
/** /**
* The symbol that represents the type. * The symbol that represents the type.
*/ */

View File

@ -122,6 +122,7 @@ final class FirstPass : ASTVisitor
dec.parameters, dec.comment); dec.parameters, dec.comment);
symbol.protection = protection; symbol.protection = protection;
symbol.parent = currentSymbol; symbol.parent = currentSymbol;
symbol.acSymbol.doc = formatComment(dec.comment);
currentSymbol.addChild(symbol); currentSymbol.addChild(symbol);
if (dec.functionBody !is null) if (dec.functionBody !is null)
{ {
@ -176,6 +177,7 @@ final class FirstPass : ASTVisitor
symbol.type = t; symbol.type = t;
symbol.protection = protection; symbol.protection = protection;
symbol.parent = currentSymbol; symbol.parent = currentSymbol;
symbol.acSymbol.doc = formatComment(dec.comment);
currentSymbol.addChild(symbol); currentSymbol.addChild(symbol);
} }
} }
@ -260,6 +262,7 @@ final class FirstPass : ASTVisitor
CompletionKind.enumName, symbolFile, dec.name.index); CompletionKind.enumName, symbolFile, dec.name.index);
symbol.type = dec.type; symbol.type = dec.type;
symbol.parent = currentSymbol; symbol.parent = currentSymbol;
symbol.acSymbol.doc = formatComment(dec.comment);
currentSymbol = symbol; currentSymbol = symbol;
if (dec.enumBody !is null) if (dec.enumBody !is null)
dec.enumBody.accept(this); dec.enumBody.accept(this);
@ -274,6 +277,7 @@ final class FirstPass : ASTVisitor
CompletionKind.enumMember, symbolFile, member.name.index); CompletionKind.enumMember, symbolFile, member.name.index);
symbol.type = member.type; symbol.type = member.type;
symbol.parent = currentSymbol; symbol.parent = currentSymbol;
symbol.acSymbol.doc = formatComment(member.comment);
currentSymbol.addChild(symbol); currentSymbol.addChild(symbol);
} }
@ -370,7 +374,7 @@ final class FirstPass : ASTVisitor
versionCondition.accept(this); versionCondition.accept(this);
} }
alias ASTVisitor.visit visit; alias visit = ASTVisitor.visit;
private: private:
@ -382,6 +386,7 @@ private:
symbol.acSymbol.parts ~= classSymbols; symbol.acSymbol.parts ~= classSymbols;
symbol.parent = currentSymbol; symbol.parent = currentSymbol;
symbol.protection = protection; symbol.protection = protection;
symbol.acSymbol.doc = formatComment(dec.comment);
currentSymbol = symbol; currentSymbol = symbol;
dec.accept(this); dec.accept(this);
currentSymbol = symbol.parent; currentSymbol = symbol.parent;
@ -396,6 +401,7 @@ private:
processParameters(symbol, null, "this", parameters, doc); processParameters(symbol, null, "this", parameters, doc);
symbol.protection = protection; symbol.protection = protection;
symbol.parent = currentSymbol; symbol.parent = currentSymbol;
symbol.acSymbol.doc = formatComment(doc);
currentSymbol.addChild(symbol); currentSymbol.addChild(symbol);
if (functionBody !is null) if (functionBody !is null)
{ {
@ -412,6 +418,7 @@ private:
symbol.acSymbol.callTip = /*formatComment(doc) ~*/ "~this()"; symbol.acSymbol.callTip = /*formatComment(doc) ~*/ "~this()";
symbol.protection = protection; symbol.protection = protection;
symbol.parent = currentSymbol; symbol.parent = currentSymbol;
symbol.acSymbol.doc = formatComment(doc);
currentSymbol.addChild(symbol); currentSymbol.addChild(symbol);
if (functionBody !is null) if (functionBody !is null)
{ {
@ -553,6 +560,7 @@ private:
s.parts = cast(typeof(s.parts)) symbol.parts; s.parts = cast(typeof(s.parts)) symbol.parts;
// TODO: Re-format callTip with new name? // TODO: Re-format callTip with new name?
s.callTip = symbol.callTip; s.callTip = symbol.callTip;
s.doc = symbol.doc;
s.qualifier = symbol.qualifier; s.qualifier = symbol.qualifier;
s.location = symbol.location; s.location = symbol.location;
s.symbolFile = symbol.symbolFile; s.symbolFile = symbol.symbolFile;
@ -915,12 +923,15 @@ string formatComment(string comment)
re = slashPlusRegex; re = slashPlusRegex;
else else
re = slashStarRegex; re = slashStarRegex;
return (comment.replaceAll(regex(re), "") ~ "\n\n") return (comment.replaceAll(regex(re), ""))
.replaceFirst(regex("^\n"), "") .replaceFirst(regex("^\n"), "")
.replaceAll(regex(`\\`), `\\`) .replaceAll(regex(`\\`), `\\`)
.replaceAll(regex("\n"), `\n`).outdent(); .replaceAll(regex("\n"), `\n`).outdent();
} }
/**
* Dummy doc comment for getCached
*/
string getCached(string s) string getCached(string s)
{ {
return s.length == 0 ? "" return s.length == 0 ? ""

View File

@ -40,6 +40,48 @@ import modulecache;
import astconverter; import astconverter;
import stupidlog; import stupidlog;
AutocompleteResponse getDoc(const AutocompleteRequest request)
{
Log.trace("Getting doc comments");
AutocompleteResponse response;
LexerConfig config;
config.fileName = "stdin";
StringCache* cache = new StringCache(StringCache.defaultBucketCount);
auto tokens = byToken(cast(ubyte[]) request.sourceCode, config, cache);
const(Token)[] tokenArray = void;
try {
tokenArray = tokens.array();
} catch (Exception e) {
Log.error("Could not provide autocomplete due to lexing exception: ", e.msg);
return response;
}
auto sortedTokens = assumeSorted(tokenArray);
string partial;
auto beforeTokens = sortedTokens.lowerBound(cast(size_t) request.cursorPosition);
Log.trace("Token at cursor: ", beforeTokens[$ - 1].text);
const(Scope)* completionScope = generateAutocompleteTrees(tokenArray, "stdin");
auto expression = getExpression(beforeTokens);
const(ACSymbol)*[] symbols = getSymbolsByTokenChain(completionScope, expression,
request.cursorPosition, CompletionType.ddoc);
if (symbols.length == 0)
Log.error("Could not find symbol");
else foreach (symbol; symbols)
{
Log.trace("Adding doc comment for ", symbol.name, ": ", symbol.doc);
response.docComments ~= symbol.doc;
}
return response;
}
/**
* Finds the declaration of the symbol at the cursor position.
*/
AutocompleteResponse findDeclaration(const AutocompleteRequest request) AutocompleteResponse findDeclaration(const AutocompleteRequest request)
{ {
Log.trace("Finding declaration"); Log.trace("Finding declaration");
@ -86,7 +128,7 @@ AutocompleteResponse findDeclaration(const AutocompleteRequest request)
const(ACSymbol)*[] getSymbolsByTokenChain(T)(const(Scope)* completionScope, const(ACSymbol)*[] getSymbolsByTokenChain(T)(const(Scope)* completionScope,
T tokens, size_t cursorPosition, CompletionType completionType) T tokens, size_t cursorPosition, CompletionType completionType)
{ {
Log.trace("Getting symbols from token chain", tokens); Log.trace("Getting symbols from token chain", tokens.map!"a.text");
// Find the symbol corresponding to the beginning of the chain // Find the symbol corresponding to the beginning of the chain
const(ACSymbol)*[] symbols = completionScope.getSymbolsByNameAndCursor( const(ACSymbol)*[] symbols = completionScope.getSymbolsByNameAndCursor(
tokens[0].text, cursorPosition); tokens[0].text, cursorPosition);
@ -178,12 +220,12 @@ const(ACSymbol)*[] getSymbolsByTokenChain(T)(const(Scope)* completionScope,
Log.trace("Couldn't find it."); Log.trace("Couldn't find it.");
break loop; break loop;
} }
if (symbols[0].kind == CompletionKind.variableName if ((symbols[0].kind == CompletionKind.variableName
|| symbols[0].kind == CompletionKind.memberVariableName || symbols[0].kind == CompletionKind.memberVariableName
|| symbols[0].kind == CompletionKind.enumMember || symbols[0].kind == CompletionKind.enumMember
|| (symbols[0].kind == CompletionKind.functionName || symbols[0].kind == CompletionKind.functionName)
&& (completionType == CompletionType.identifiers && (completionType == CompletionType.identifiers
|| i + 1 < tokens.length))) || i + 1 < tokens.length))
{ {
symbols = symbols[0].type is null ? [] : [symbols[0].type]; symbols = symbols[0].type is null ? [] : [symbols[0].type];
} }

View File

@ -41,12 +41,14 @@ int main(string[] args)
bool shutdown; bool shutdown;
bool clearCache; bool clearCache;
bool symbolLocation; bool symbolLocation;
bool doc;
try try
{ {
getopt(args, "cursorPos|c", &cursorPos, "I", &importPaths, getopt(args, "cursorPos|c", &cursorPos, "I", &importPaths,
"port|p", &port, "help|h", &help, "shutdown", &shutdown, "port|p", &port, "help|h", &help, "shutdown", &shutdown,
"clearCache", &clearCache, "symbolLocation|l", &symbolLocation); "clearCache", &clearCache, "symbolLocation|l", &symbolLocation,
"doc|d", &doc);
} }
catch (Exception e) catch (Exception e)
{ {
@ -119,7 +121,12 @@ int main(string[] args)
request.importPaths = importPaths; request.importPaths = importPaths;
request.sourceCode = sourceCode; request.sourceCode = sourceCode;
request.cursorPosition = cursorPos; request.cursorPosition = cursorPos;
request.kind = symbolLocation ? RequestKind.symbolLocation : RequestKind.autocomplete; if (symbolLocation)
request.kind = RequestKind.symbolLocation;
else if (doc)
request.kind = RequestKind.doc;
else
request.kind = RequestKind.autocomplete;
// Send message to server // Send message to server
TcpSocket socket = createSocket(port); TcpSocket socket = createSocket(port);
@ -131,6 +138,8 @@ int main(string[] args)
if (symbolLocation) if (symbolLocation)
printLocationResponse(response); printLocationResponse(response);
else if (doc)
printDocResponse(response);
else else
printCompletionResponse(response); printCompletionResponse(response);
@ -212,6 +221,12 @@ AutocompleteResponse getResponse(TcpSocket socket)
return response; return response;
} }
void printDocResponse(AutocompleteResponse response)
{
foreach (doc; response.docComments)
writeln(doc);
}
void printLocationResponse(AutocompleteResponse response) void printLocationResponse(AutocompleteResponse response)
{ {
if (response.symbolFilePath is null) if (response.symbolFilePath is null)

@ -1 +1 @@
Subproject commit c01c51a61ea4f2e6a94a64396be0abd9e8249bab Subproject commit 27e91f12bb71b6e42f46fa103689cb364cd693cc

View File

@ -101,15 +101,15 @@ function M.cycleCalltips(delta)
showCurrentCallTip() showCurrentCallTip()
end end
function M.gotoDeclaration() local function runDCDClient(args)
local fileName = os.tmpname() local fileName = os.tmpname()
local mode = "w" local mode = "w"
if _G.WIN32 then if _G.WIN32 then
fileName = os.getenv('TEMP') .. fileName fileName = os.getenv('TEMP') .. fileName
mode = "wb" mode = "wb"
end end
local command = M.PATH_TO_DCD_CLIENT .. " -l -c" .. buffer.current_pos .. local command = M.PATH_TO_DCD_CLIENT .. " " .. args
" > \"" .. fileName .. "\"" .. " -c" .. buffer.current_pos .. " > \"" .. fileName .. "\""
local p = io.popen(command, mode) local p = io.popen(command, mode)
p:write(buffer:get_text()) p:write(buffer:get_text())
p:flush() p:flush()
@ -117,6 +117,20 @@ function M.gotoDeclaration()
local tmpFile = io.open(fileName, "r") local tmpFile = io.open(fileName, "r")
local r = tmpFile:read("*a") local r = tmpFile:read("*a")
tmpFile:close() tmpFile:close()
os.remove(fileName)
return r
end
function M.showDoc()
local r = runDCDClient("-d")
if r ~= "\n" then
print(r)
showCalltips(r)
end
end
function M.gotoDeclaration()
local r = runDCDClient("-l")
if r ~= "Not found\n" then if r ~= "Not found\n" then
path, position = r:match("^(.-)\t(%d+)") path, position = r:match("^(.-)\t(%d+)")
if (path ~= nil and position ~= nil) then if (path ~= nil and position ~= nil) then
@ -127,7 +141,6 @@ function M.gotoDeclaration()
buffer:word_right_end_extend() buffer:word_right_end_extend()
end end
end end
os.remove(fileName)
end end
events.connect(events.CALL_TIP_CLICK, function(arrow) events.connect(events.CALL_TIP_CLICK, function(arrow)
@ -139,23 +152,9 @@ events.connect(events.CALL_TIP_CLICK, function(arrow)
end end
end) end)
function M.autocomplete(ch) function M.autocomplete()
if buffer:get_lexer() ~= "dmd" then return end if buffer:get_lexer() ~= "dmd" then return end
local fileName = os.tmpname() local r = runDCDClient("")
local mode = "w"
if _G.WIN32 then
fileName = os.getenv('TEMP') .. fileName
mode = "wb"
end
local command = M.PATH_TO_DCD_CLIENT .. " -c" .. buffer.current_pos ..
" > \"" .. fileName .. "\""
local p = io.popen(command, mode)
p:write(buffer:get_text())
p:flush()
p:close()
local tmpFile = io.open(fileName, "r")
local r = tmpFile:read("*a")
tmpFile:close()
if r ~= "\n" then if r ~= "\n" then
if r:match("^identifiers.*") then if r:match("^identifiers.*") then
showCompletionList(r) showCompletionList(r)
@ -163,7 +162,6 @@ function M.autocomplete(ch)
showCalltips(r) showCalltips(r)
end end
end end
os.remove(fileName)
end end
M.ALIAS =[[ M.ALIAS =[[

View File

@ -1,6 +1,6 @@
local M = {} local M = {}
_M.dcd = require "dmd.dcd" local dcd = require "dmd.dcd"
if type(_G.snippets) == 'table' then if type(_G.snippets) == 'table' then
_G.snippets.dmd = {} _G.snippets.dmd = {}
@ -12,13 +12,13 @@ end
events.connect(events.CHAR_ADDED, function(ch) events.connect(events.CHAR_ADDED, function(ch)
if string.char(ch) == '(' or string.char(ch) == '.' then if string.char(ch) == '(' or string.char(ch) == '.' then
_M.dcd.autocomplete(ch) dcd.autocomplete()
end end
end) end)
local function autocomplete() local function autocomplete()
_M.dcd.registerImages() dcd.registerImages()
_M.dcd.autocomplete() dcd.autocomplete()
if not buffer:auto_c_active() then if not buffer:auto_c_active() then
textadept.editing.autocomplete_word(keywords) textadept.editing.autocomplete_word(keywords)
end end
@ -31,9 +31,10 @@ keys.dmd = {
(_USERHOME..'/modules/dmd/init.lua'):iconv('UTF-8', _CHARSET) }, (_USERHOME..'/modules/dmd/init.lua'):iconv('UTF-8', _CHARSET) },
}, },
['c\n'] = {autocomplete}, ['c\n'] = {autocomplete},
['cG'] = {_M.dcd.gotoDeclaration}, ['ch'] = {dcd.showDoc},
['down'] = {_M.dcd.cycleCalltips, 1}, ['cG'] = {dcd.gotoDeclaration},
['up'] = {_M.dcd.cycleCalltips, -1}, ['down'] = {dcd.cycleCalltips, 1},
['up'] = {dcd.cycleCalltips, -1},
} }
function M.set_buffer_properties() function M.set_buffer_properties()

View File

@ -98,7 +98,12 @@ enum CompletionType : string
/** /**
* The response contains the location of a symbol declaration. * The response contains the location of a symbol declaration.
*/ */
location = "location" location = "location",
/**
* The response contains documentation comments for the symbol.
*/
ddoc = "ddoc"
} }
/** /**
@ -115,7 +120,9 @@ enum RequestKind : ubyte
/// Shut down the server /// Shut down the server
shutdown, shutdown,
/// Get declaration location of given symbol /// Get declaration location of given symbol
symbolLocation symbolLocation,
/// Get the doc comments for the symbol
doc
} }
/** /**
@ -169,6 +176,11 @@ struct AutocompleteResponse
*/ */
size_t symbolLocation; size_t symbolLocation;
/**
* The documentation comment
*/
string[] docComments;
/** /**
* The completions * The completions
*/ */

View File

@ -102,7 +102,7 @@ int main(string[] args)
// No relative paths // No relative paths
version (Posix) chdir("/"); version (Posix) chdir("/");
while (true) serverLoop: while (true)
{ {
auto s = socket.accept(); auto s = socket.accept();
s.blocking = true; s.blocking = true;
@ -140,28 +140,33 @@ int main(string[] args)
AutocompleteRequest request; AutocompleteRequest request;
msgpack.unpack(buffer[size_t.sizeof .. bytesReceived], request); msgpack.unpack(buffer[size_t.sizeof .. bytesReceived], request);
if (request.kind == RequestKind.addImport) final switch (request.kind)
{ {
case RequestKind.addImport:
ModuleCache.addImportPaths(request.importPaths); ModuleCache.addImportPaths(request.importPaths);
} break;
else if (request.kind == RequestKind.clearCache) case RequestKind.clearCache:
{
Log.info("Clearing cache."); Log.info("Clearing cache.");
ModuleCache.clear(); ModuleCache.clear();
}
else if (request.kind == RequestKind.shutdown)
{
Log.info("Shutting down.");
break; break;
} case RequestKind.shutdown:
else Log.info("Shutting down.");
{ break serverLoop;
AutocompleteResponse response = case RequestKind.autocomplete:
request.kind == RequestKind.autocomplete AutocompleteResponse response = complete(request);
? complete(request)
: findDeclaration(request);
ubyte[] responseBytes = msgpack.pack(response); ubyte[] responseBytes = msgpack.pack(response);
s.send(responseBytes); s.send(responseBytes);
break;
case RequestKind.doc:
AutocompleteResponse response = getDoc(request);
ubyte[] responseBytes = msgpack.pack(response);
s.send(responseBytes);
break;
case RequestKind.symbolLocation:
AutocompleteResponse response = findDeclaration(request);
ubyte[] responseBytes = msgpack.pack(response);
s.send(responseBytes);
break;
} }
Log.info("Request processed in ", requestWatch.peek().to!("msecs", float), " milliseconds"); Log.info("Request processed in ", requestWatch.peek().to!("msecs", float), " milliseconds");
} }