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
dcd-client
dcd-server
dcd-client.o
dcd-server.o
# Perf reports
perf.data

View File

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

View File

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

View File

@ -40,6 +40,48 @@ import modulecache;
import astconverter;
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)
{
Log.trace("Finding declaration");
@ -86,7 +128,7 @@ AutocompleteResponse findDeclaration(const AutocompleteRequest request)
const(ACSymbol)*[] getSymbolsByTokenChain(T)(const(Scope)* completionScope,
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
const(ACSymbol)*[] symbols = completionScope.getSymbolsByNameAndCursor(
tokens[0].text, cursorPosition);
@ -178,12 +220,12 @@ const(ACSymbol)*[] getSymbolsByTokenChain(T)(const(Scope)* completionScope,
Log.trace("Couldn't find it.");
break loop;
}
if (symbols[0].kind == CompletionKind.variableName
if ((symbols[0].kind == CompletionKind.variableName
|| symbols[0].kind == CompletionKind.memberVariableName
|| symbols[0].kind == CompletionKind.enumMember
|| (symbols[0].kind == CompletionKind.functionName
|| symbols[0].kind == CompletionKind.functionName)
&& (completionType == CompletionType.identifiers
|| i + 1 < tokens.length)))
|| i + 1 < tokens.length))
{
symbols = symbols[0].type is null ? [] : [symbols[0].type];
}

View File

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

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

View File

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

View File

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

View File

@ -98,7 +98,12 @@ enum CompletionType : string
/**
* 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
shutdown,
/// 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;
/**
* The documentation comment
*/
string[] docComments;
/**
* The completions
*/

View File

@ -102,7 +102,7 @@ int main(string[] args)
// No relative paths
version (Posix) chdir("/");
while (true)
serverLoop: while (true)
{
auto s = socket.accept();
s.blocking = true;
@ -140,28 +140,33 @@ int main(string[] args)
AutocompleteRequest 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);
}
else if (request.kind == RequestKind.clearCache)
{
break;
case RequestKind.clearCache:
Log.info("Clearing cache.");
ModuleCache.clear();
}
else if (request.kind == RequestKind.shutdown)
{
Log.info("Shutting down.");
break;
}
else
{
AutocompleteResponse response =
request.kind == RequestKind.autocomplete
? complete(request)
: findDeclaration(request);
case RequestKind.shutdown:
Log.info("Shutting down.");
break serverLoop;
case RequestKind.autocomplete:
AutocompleteResponse response = complete(request);
ubyte[] responseBytes = msgpack.pack(response);
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");
}