Finally implement #22
This commit is contained in:
parent
3c326a3f82
commit
87056b0c58
|
|
@ -5,6 +5,8 @@
|
|||
# *nix binaries
|
||||
dcd-client
|
||||
dcd-server
|
||||
dcd-client.o
|
||||
dcd-server.o
|
||||
|
||||
# Perf reports
|
||||
perf.data
|
||||
|
|
|
|||
|
|
@ -136,6 +136,11 @@ public:
|
|||
*/
|
||||
string symbolFile;
|
||||
|
||||
/**
|
||||
* Documentation for the symbol.
|
||||
*/
|
||||
string doc;
|
||||
|
||||
/**
|
||||
* The symbol that represents the type.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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 ? ""
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
}
|
||||
|
|
|
|||
21
client.d
21
client.d
|
|
@ -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,8 +138,10 @@ int main(string[] args)
|
|||
|
||||
if (symbolLocation)
|
||||
printLocationResponse(response);
|
||||
else if (doc)
|
||||
printDocResponse(response);
|
||||
else
|
||||
printCompletionResponse(response);
|
||||
printCompletionResponse(response);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
2
dscanner
2
dscanner
|
|
@ -1 +1 @@
|
|||
Subproject commit c01c51a61ea4f2e6a94a64396be0abd9e8249bab
|
||||
Subproject commit 27e91f12bb71b6e42f46fa103689cb364cd693cc
|
||||
|
|
@ -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 =[[
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
16
messages.d
16
messages.d
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
37
server.d
37
server.d
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue