mirror of
https://github.com/dlang-community/DCD.git
synced 2025-04-25 21:00:02 +03:00
Add a new request to get inlay hints (#764)
Co-authored-by: ryuukk <ryuukk.dev@gmail.com>
This commit is contained in:
parent
6d635923f7
commit
b79982d509
9 changed files with 179 additions and 2 deletions
13
README.md
13
README.md
|
@ -314,6 +314,19 @@ Otherwise the client outputs _00000_ so that the length of the answer is guarant
|
||||||
45
|
45
|
||||||
133
|
133
|
||||||
|
|
||||||
|
## Inlay Hints
|
||||||
|
|
||||||
|
Build a list of extra annoations for your IDE to display.
|
||||||
|
You must submit the content of the current file displayed in your editor.
|
||||||
|
|
||||||
|
dcd-client --inlayHints
|
||||||
|
|
||||||
|
This is a W.I.P., currently it only provide annoatations about aliases for your variables,
|
||||||
|
more is planned.
|
||||||
|
|
||||||
|
#### Example output
|
||||||
|
|
||||||
|
l ->MyAlias->MyType 42
|
||||||
|
|
||||||
# Server
|
# Server
|
||||||
|
|
||||||
|
|
|
@ -78,10 +78,12 @@ enum RequestKind : ushort
|
||||||
localUse = 0b00000010_00000000,
|
localUse = 0b00000010_00000000,
|
||||||
/// Remove import directory from server
|
/// Remove import directory from server
|
||||||
removeImport = 0b00000100_00000000,
|
removeImport = 0b00000100_00000000,
|
||||||
|
/// Get inlay hints
|
||||||
|
inlayHints = 0b00001000_00000000,
|
||||||
|
|
||||||
/// These request kinds require source code and won't be executed if there
|
/// These request kinds require source code and won't be executed if there
|
||||||
/// is no source sent
|
/// is no source sent
|
||||||
requiresSourceCode = autocomplete | doc | symbolLocation | search | localUse,
|
requiresSourceCode = autocomplete | doc | symbolLocation | search | localUse | inlayHints,
|
||||||
// dfmt on
|
// dfmt on
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,7 @@ int runClient(string[] args)
|
||||||
bool clearCache;
|
bool clearCache;
|
||||||
bool symbolLocation;
|
bool symbolLocation;
|
||||||
bool doc;
|
bool doc;
|
||||||
|
bool inlayHints;
|
||||||
bool query;
|
bool query;
|
||||||
bool printVersion;
|
bool printVersion;
|
||||||
bool listImports;
|
bool listImports;
|
||||||
|
@ -86,6 +87,7 @@ int runClient(string[] args)
|
||||||
"R", &removedImportPaths, "port|p", &port, "help|h", &help,
|
"R", &removedImportPaths, "port|p", &port, "help|h", &help,
|
||||||
"shutdown", &shutdown, "clearCache", &clearCache,
|
"shutdown", &shutdown, "clearCache", &clearCache,
|
||||||
"symbolLocation|l", &symbolLocation, "doc|d", &doc,
|
"symbolLocation|l", &symbolLocation, "doc|d", &doc,
|
||||||
|
"inlayHints", &inlayHints,
|
||||||
"query|status|q", &query, "search|s", &search,
|
"query|status|q", &query, "search|s", &search,
|
||||||
"version", &printVersion, "listImports", &listImports,
|
"version", &printVersion, "listImports", &listImports,
|
||||||
"tcp", &useTCP, "socketFile", &socketFile,
|
"tcp", &useTCP, "socketFile", &socketFile,
|
||||||
|
@ -181,7 +183,7 @@ int runClient(string[] args)
|
||||||
printImportList(response);
|
printImportList(response);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
else if (search == null && cursorPos == size_t.max)
|
else if (search == null && !inlayHints && cursorPos == size_t.max)
|
||||||
{
|
{
|
||||||
// cursor position is a required argument
|
// cursor position is a required argument
|
||||||
printHelp(args[0]);
|
printHelp(args[0]);
|
||||||
|
@ -234,6 +236,8 @@ int runClient(string[] args)
|
||||||
request.kind |= RequestKind.search;
|
request.kind |= RequestKind.search;
|
||||||
else if(localUse)
|
else if(localUse)
|
||||||
request.kind |= RequestKind.localUse;
|
request.kind |= RequestKind.localUse;
|
||||||
|
else if (inlayHints)
|
||||||
|
request.kind |= RequestKind.inlayHints;
|
||||||
else
|
else
|
||||||
request.kind |= RequestKind.autocomplete;
|
request.kind |= RequestKind.autocomplete;
|
||||||
|
|
||||||
|
@ -255,6 +259,8 @@ int runClient(string[] args)
|
||||||
printSearchResponse(response);
|
printSearchResponse(response);
|
||||||
else if (localUse)
|
else if (localUse)
|
||||||
printLocalUse(response);
|
printLocalUse(response);
|
||||||
|
else if (inlayHints)
|
||||||
|
printInlayHintsResponse(response);
|
||||||
else
|
else
|
||||||
printCompletionResponse(response, fullOutput);
|
printCompletionResponse(response, fullOutput);
|
||||||
|
|
||||||
|
@ -295,6 +301,10 @@ Options:
|
||||||
Gets documentation comments associated with the symbol at the cursor
|
Gets documentation comments associated with the symbol at the cursor
|
||||||
location.
|
location.
|
||||||
|
|
||||||
|
--inlayHints
|
||||||
|
For all supported variable usages, show value types. Currently shows
|
||||||
|
alias definitions.
|
||||||
|
|
||||||
--search | -s symbolName
|
--search | -s symbolName
|
||||||
Searches for symbolName in both stdin / the given file name as well as
|
Searches for symbolName in both stdin / the given file name as well as
|
||||||
others files cached by the server.
|
others files cached by the server.
|
||||||
|
@ -384,6 +394,21 @@ void printLocationResponse(ref const AutocompleteResponse response)
|
||||||
writeln(makeTabSeparated(response.symbolFilePath, response.symbolLocation.to!string));
|
writeln(makeTabSeparated(response.symbolFilePath, response.symbolLocation.to!string));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void printInlayHintsResponse(ref const AutocompleteResponse response)
|
||||||
|
{
|
||||||
|
auto app = appender!(string[])();
|
||||||
|
foreach (ref completion; response.completions)
|
||||||
|
{
|
||||||
|
app.put(makeTabSeparated(
|
||||||
|
completion.kind == char.init ? "" : "" ~ completion.kind,
|
||||||
|
completion.identifier,
|
||||||
|
completion.symbolLocation.to!string
|
||||||
|
));
|
||||||
|
}
|
||||||
|
foreach (line; app.data)
|
||||||
|
writeln(line);
|
||||||
|
}
|
||||||
|
|
||||||
void printCompletionResponse(ref const AutocompleteResponse response, bool extended)
|
void printCompletionResponse(ref const AutocompleteResponse response, bool extended)
|
||||||
{
|
{
|
||||||
if (response.completions.length > 0)
|
if (response.completions.length > 0)
|
||||||
|
|
108
src/dcd/server/autocomplete/inlayhints.d
Normal file
108
src/dcd/server/autocomplete/inlayhints.d
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
/**
|
||||||
|
* This file is part of DCD, a development tool for the D programming language.
|
||||||
|
* Copyright (C) 2014 Brian Schott
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
module dcd.server.autocomplete.inlayhints;
|
||||||
|
|
||||||
|
import std.stdio;
|
||||||
|
import std.algorithm;
|
||||||
|
import std.array;
|
||||||
|
import std.experimental.allocator;
|
||||||
|
import std.experimental.logger;
|
||||||
|
import std.typecons;
|
||||||
|
|
||||||
|
import dcd.server.autocomplete.util;
|
||||||
|
|
||||||
|
import dparse.lexer;
|
||||||
|
import dparse.rollback_allocator;
|
||||||
|
|
||||||
|
import dsymbol.modulecache;
|
||||||
|
import dsymbol.symbol;
|
||||||
|
import dsymbol.scope_;
|
||||||
|
import dsymbol.conversion;
|
||||||
|
import dsymbol.string_interning;
|
||||||
|
|
||||||
|
import dcd.common.messages;
|
||||||
|
|
||||||
|
import containers.hashset;
|
||||||
|
|
||||||
|
public AutocompleteResponse getInlayHints(const AutocompleteRequest request,
|
||||||
|
ref ModuleCache moduleCache)
|
||||||
|
{
|
||||||
|
// trace("Getting inlay hints comments");
|
||||||
|
AutocompleteResponse response;
|
||||||
|
|
||||||
|
LexerConfig config;
|
||||||
|
config.fileName = "";
|
||||||
|
auto cache = StringCache(request.sourceCode.length.optimalBucketCount);
|
||||||
|
auto tokenArray = getTokensForParser(cast(ubyte[]) request.sourceCode, config, &cache);
|
||||||
|
RollbackAllocator rba;
|
||||||
|
auto pair = generateAutocompleteTrees(tokenArray, &rba, -1, moduleCache);
|
||||||
|
scope(exit) pair.destroy();
|
||||||
|
|
||||||
|
void check(DSymbol* it, ref HashSet!size_t visited)
|
||||||
|
{
|
||||||
|
if (visited.contains(cast(size_t) it))
|
||||||
|
return;
|
||||||
|
if (it.symbolFile != "stdin") return;
|
||||||
|
visited.insert(cast(size_t) it);
|
||||||
|
|
||||||
|
//writeln("sym: ", it.name," ", it.location, " kind: ", it.kind," qualifier: ", it.qualifier);
|
||||||
|
//if (auto type = it.type)
|
||||||
|
//{
|
||||||
|
// writeln(" ", type.name, " kind: ", type.kind, " qualifier", type.qualifier);
|
||||||
|
// if (auto ttype = type.type)
|
||||||
|
// writeln(" ", ttype.name, " kind: ", ttype.kind, " qualifier", ttype.qualifier);
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
// aliases
|
||||||
|
// struct Data {}
|
||||||
|
// alias Alias1 = Data;
|
||||||
|
// Alias1 var; becomes: Alias1 [-> Data] var;
|
||||||
|
if (it.kind == CompletionKind.variableName && it.type && it.type.kind == CompletionKind.aliasName)
|
||||||
|
{
|
||||||
|
AutocompleteResponse.Completion c;
|
||||||
|
c.symbolLocation = it.location - 1;
|
||||||
|
c.kind = CompletionKind.aliasName;
|
||||||
|
|
||||||
|
DSymbol* type = it.type;
|
||||||
|
|
||||||
|
while (type)
|
||||||
|
{
|
||||||
|
if (type.kind == CompletionKind.aliasName && type.type)
|
||||||
|
c.identifier ~= "->" ~ type.type.name;
|
||||||
|
if (type.type && type.type.kind != CompletionKind.aliasName) break;
|
||||||
|
type = type.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
response.completions ~= c;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach(part; it.opSlice())
|
||||||
|
check(part, visited);
|
||||||
|
}
|
||||||
|
|
||||||
|
HashSet!size_t visited;
|
||||||
|
foreach (symbol; pair.scope_.symbols)
|
||||||
|
{
|
||||||
|
check(symbol, visited);
|
||||||
|
foreach(part; symbol.opSlice())
|
||||||
|
check(part, visited);
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
|
@ -24,3 +24,4 @@ import dcd.server.autocomplete.complete;
|
||||||
import dcd.server.autocomplete.doc;
|
import dcd.server.autocomplete.doc;
|
||||||
import dcd.server.autocomplete.localuse;
|
import dcd.server.autocomplete.localuse;
|
||||||
import dcd.server.autocomplete.symbols;
|
import dcd.server.autocomplete.symbols;
|
||||||
|
import dcd.server.autocomplete.inlayhints;
|
||||||
|
|
|
@ -347,6 +347,11 @@ int runServer(string[] args)
|
||||||
s.trySendResponse(symbolSearch(request, cache), "Could not perform symbol search");
|
s.trySendResponse(symbolSearch(request, cache), "Could not perform symbol search");
|
||||||
else if (request.kind & RequestKind.localUse)
|
else if (request.kind & RequestKind.localUse)
|
||||||
s.trySendResponse(findLocalUse(request, cache), "Couldnot find local usage");
|
s.trySendResponse(findLocalUse(request, cache), "Couldnot find local usage");
|
||||||
|
else if (request.kind & RequestKind.inlayHints)
|
||||||
|
{
|
||||||
|
info("Getting inlay hints");
|
||||||
|
s.trySendResponse(getInlayHints(request, cache), "Could not get inlay hints");
|
||||||
|
}
|
||||||
else if (needResponse)
|
else if (needResponse)
|
||||||
s.trySendResponse(AutocompleteResponse.ack, "Could not send ack");
|
s.trySendResponse(AutocompleteResponse.ack, "Could not send ack");
|
||||||
}
|
}
|
||||||
|
|
1
tests/tc_inlay_hints/expected.txt
Normal file
1
tests/tc_inlay_hints/expected.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
l ->Point 208
|
17
tests/tc_inlay_hints/file.d
Normal file
17
tests/tc_inlay_hints/file.d
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
// when extending the inlayHints capabilities, don't forget to update the --help
|
||||||
|
// text inside client.d
|
||||||
|
|
||||||
|
import point;
|
||||||
|
import point : P = Point;
|
||||||
|
|
||||||
|
void foo(int x, int y) {}
|
||||||
|
void foo(Point point) {}
|
||||||
|
void bar(P point, int z = 1) {}
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
P p;
|
||||||
|
foo(1, 2);
|
||||||
|
foo(p);
|
||||||
|
bar(p, 3);
|
||||||
|
}
|
5
tests/tc_inlay_hints/run.sh
Executable file
5
tests/tc_inlay_hints/run.sh
Executable file
|
@ -0,0 +1,5 @@
|
||||||
|
set -e
|
||||||
|
set -u
|
||||||
|
|
||||||
|
../../bin/dcd-client $1 --inlayHints file.d > actual.txt
|
||||||
|
diff actual.txt expected.txt --strip-trailing-cr
|
Loading…
Add table
Add a link
Reference in a new issue