find local usage of symbol located at cursor

(partially based on unmainted PR 202)
This commit is contained in:
Basile Burg 2016-08-05 21:11:08 +02:00
parent 42c5358940
commit 1ab8a8ec08
No known key found for this signature in database
GPG Key ID: 1868039F415CB8CF
15 changed files with 173 additions and 3 deletions

3
.gitignore vendored
View File

@ -5,8 +5,7 @@
*.dll
# *nix binaries
bin/dcd-client
bin/dcd-server
bin
*.o
*.a
*.so

View File

@ -213,6 +213,27 @@ in place of a file being edited.)
/usr/include/dmd/phobos/std/conv.d f 9494
```
## Find usage of symbol at cursor
```dcd-client --localUsage -c 123```
The "--localUsage" or "-u" flags cause the client to instruct the server
to return all the local usages of the symbol located at the given cursor position.
#### Output format
When usages exist, if the source symbol is an identifier (a type, a variable name, etc.) and if the symbol is not ambiguous then
the first line contains the location of the symbol (a file name or literally _stdin_), a tab then the offset to the symbol declaration,
each following line contains a byte offset, always relative to the supplied file, to a usage of the symbol.
Otherwise the client outputs _00000_ so that the length of the answer is guaranteed to be at least 5 bytes.
#### Example output
```
stdin 45
26
45
133
```
#Server
The server must be running for the DCD client to provide autocomplete information.
In future versions the client may start the server if it is not running, but for

View File

@ -50,6 +50,7 @@ int main(string[] args)
bool printVersion;
bool listImports;
bool getIdentifier;
bool localUsage;
string search;
version(Windows)
{
@ -70,7 +71,8 @@ int main(string[] args)
"doc|d", &doc, "query|status|q", &query, "search|s", &search,
"version", &printVersion, "listImports", &listImports,
"tcp", &useTCP, "socketFile", &socketFile,
"getIdentifier", &getIdentifier);
"getIdentifier", &getIdentifier,
"localUsage|u", &localUsage);
}
catch (ConvException e)
{
@ -212,6 +214,8 @@ int main(string[] args)
request.kind |= RequestKind.doc;
else if (search)
request.kind |= RequestKind.search;
else if(localUsage)
request.kind |= RequestKind.localUsage;
else
request.kind |= RequestKind.autocomplete;
@ -231,6 +235,8 @@ int main(string[] args)
printDocResponse(response);
else if (search !is null)
printSearchResponse(response);
else if (localUsage)
printLocalUsage(response);
else
printCompletionResponse(response);
@ -277,6 +283,10 @@ Options:
Searches for symbolName in both stdin / the given file name as well as
others files cached by the server.
--localUsage | -u
Searches for all the usages of the symbol at the cursor location
in the given filename (or stdin).
--query | -q | --status
Query the server statis. Returns 0 if the server is running. Returns
1 if the server could not be contacted.
@ -385,6 +395,17 @@ void printSearchResponse(const AutocompleteResponse response)
}
}
void printLocalUsage(const AutocompleteResponse response)
{
if (response.symbolFilePath.length)
{
writeln(response.symbolFilePath, '\t', response.symbolLocation);
foreach(loc; response.locations)
writeln(loc);
}
else write("00000");
}
void printImportList(const AutocompleteResponse response)
{
import std.algorithm.iteration : each;

View File

@ -74,6 +74,8 @@ enum RequestKind : ushort
search = 0b00000000_10000000,
/// List import directories
listImports = 0b00000001_00000000,
/// local symbol usage
localUsage = 0b00000010_00000000,
// dfmt on
}

View File

@ -50,6 +50,93 @@ import common.messages;
import containers.hashset;
/**
* Finds usage of the symbol at the cursor position in a single document.
* Params:
* request = the autocompletion request
* Returns:
* the autocompletion response
*/
public AutocompleteResponse findLocalUsage(AutocompleteRequest request,
ref ModuleCache moduleCache)
{
AutocompleteResponse response;
RollbackAllocator rba;
auto allocator = scoped!(ASTAllocator)();
auto cache = StringCache(StringCache.defaultBucketCount);
// patchs the original request for the subsequent requests
request.kind = RequestKind.symbolLocation;
// getSymbolsForCompletion() copy to avoid repetitive parsing
LexerConfig config;
config.fileName = "";
const(Token)[] tokenArray = getTokensForParser(cast(ubyte[]) request.sourceCode,
config, &cache);
SymbolStuff getSymbolsAtCursor(size_t cursorPosition)
{
auto sortedTokens = assumeSorted(tokenArray);
auto beforeTokens = sortedTokens.lowerBound(cursorPosition);
ScopeSymbolPair pair = generateAutocompleteTrees(tokenArray, allocator,
&rba, request.cursorPosition, moduleCache);
auto expression = getExpression(beforeTokens);
return SymbolStuff(getSymbolsByTokenChain(pair.scope_, expression,
cursorPosition, CompletionType.location), pair.symbol, pair.scope_);
}
// gets the symbol matching to cursor pos
SymbolStuff stuff = getSymbolsAtCursor(cast(size_t)request.cursorPosition);
scope(exit) stuff.destroy();
// starts searching only if no ambiguity with the symbol
if (stuff.symbols.length == 1)
{
const(DSymbol*) sourceSymbol = stuff.symbols[0];
response.symbolLocation = sourceSymbol.location;
response.symbolFilePath = sourceSymbol.symbolFile.idup;
// gets the source token to avoid too much getSymbolsAtCursor()
const(Token)* sourceToken;
foreach(i, t; tokenArray)
{
if (t.type != tok!"identifier")
continue;
if (request.cursorPosition >= t.index &&
request.cursorPosition < t.index + t.text.length)
{
sourceToken = tokenArray.ptr + i;
break;
}
}
// finds the tokens that match to the source symbol
if (sourceToken != null) foreach (t; tokenArray)
{
if (t.type == tok!"identifier" && t.text == sourceToken.text)
{
size_t pos = cast(size_t) t.index + 1; // place cursor inside the token
SymbolStuff candidate = getSymbolsAtCursor(pos);
scope(exit) candidate.destroy();
if (candidate.symbols.length == 1 &&
candidate.symbols[0].location == sourceSymbol.location &&
candidate.symbols[0].symbolFile == sourceSymbol.symbolFile)
{
response.locations ~= t.index;
}
}
}
else
{
warning("The source token is not an identifier");
}
}
else
{
warning("No or ambiguous symbol for the identifier at cursor");
}
return response;
}
/**
* Gets documentation for the symbol at the cursor
* Params:

View File

@ -318,6 +318,19 @@ int main(string[] args)
ubyte[] responseBytes = msgpack.pack(response);
s.send(responseBytes);
}
else if (request.kind & RequestKind.localUsage)
{
try
{
AutocompleteResponse response = findLocalUsage(request, cache);
ubyte[] responseBytes = msgpack.pack(response);
s.send(responseBytes);
}
catch (Exception e)
{
warning("Could not find local usage", e.msg);
}
}
info("Request processed in ", requestWatch.peek().to!("msecs", float), " milliseconds");
}
return 0;

View File

@ -0,0 +1,5 @@
stdin 5
5
21
27
33

1
tests/tc033/file.d Normal file
View File

@ -0,0 +1 @@
uint var; void foo(){var=0;var=0;var=0;}

5
tests/tc033/run.sh Executable file
View File

@ -0,0 +1,5 @@
set -e
set -u
../../bin/dcd-client $1 file.d -u -c22 | sed s\""$(dirname "$(pwd)")"\"\" > actual1.txt
diff actual1.txt expected1.txt

View File

@ -0,0 +1,3 @@
/imports/object.d 22
0
12

1
tests/tc034/file.d Normal file
View File

@ -0,0 +1 @@
string str; string txt;

5
tests/tc034/run.sh Executable file
View File

@ -0,0 +1,5 @@
set -e
set -u
../../bin/dcd-client $1 file.d -u -c1 | sed s\""$(dirname "$(pwd)")"\"\" > actual1.txt
diff actual1.txt expected1.txt

View File

@ -0,0 +1 @@
00000

1
tests/tc035/file.d Normal file
View File

@ -0,0 +1 @@
struct Foo; struct Foo;

5
tests/tc035/run.sh Executable file
View File

@ -0,0 +1,5 @@
set -e
set -u
../../bin/dcd-client $1 file.d -u -c8 | sed s\""$(dirname "$(pwd)")"\"\" > actual1.txt
diff actual1.txt expected1.txt