Merge pull request #385 from BBasile/reboot-localusage

find local usage of symbol located at cursor
This commit is contained in:
Basile Burg 2017-06-17 12:45:43 +02:00 committed by GitHub
commit 1fd445a80b
15 changed files with 177 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,30 @@ in place of a file being edited.)
/usr/include/dmd/phobos/std/conv.d f 9494
```
## Find the use of the symbol at the cursor
```dcd-client --localUse -c 123```
The "--localUse" or "-u" flags cause the client to instruct the server
to return all the uses, within the same module, of the symbol located at the given cursor position.
#### Output format
When uses 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.
Following the first line is a list of all known uses of the symbol in the current file.
The list is composed of lines each containing a single number that indicates the byte offset
from the start of the file to the i-th use.
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 localUse;
string search;
version(Windows)
{
@ -70,7 +71,9 @@ 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", &localUse, // TODO:remove this line in Nov. 2017
"localUse|u", &localUse);
}
catch (ConvException e)
{
@ -212,6 +215,8 @@ int main(string[] args)
request.kind |= RequestKind.doc;
else if (search)
request.kind |= RequestKind.search;
else if(localUse)
request.kind |= RequestKind.localUse;
else
request.kind |= RequestKind.autocomplete;
@ -231,6 +236,8 @@ int main(string[] args)
printDocResponse(response);
else if (search !is null)
printSearchResponse(response);
else if (localUse)
printLocalUse(response);
else
printCompletionResponse(response);
@ -277,6 +284,10 @@ Options:
Searches for symbolName in both stdin / the given file name as well as
others files cached by the server.
--localUse | -u
Searches for all the uses 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 +396,17 @@ void printSearchResponse(const AutocompleteResponse response)
}
}
void printLocalUse(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
localUse = 0b00000010_00000000,
// dfmt on
}

View File

@ -50,6 +50,93 @@ import common.messages;
import containers.hashset;
/**
* Finds the uses of the symbol at the cursor position within a single document.
* Params:
* request = the autocompletion request.
* Returns:
* the autocompletion response.
*/
public AutocompleteResponse findLocalUse(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.localUse)
{
try
{
AutocompleteResponse response = findLocalUse(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