add support for the Module Scope Operator, close #548, close #306

This commit is contained in:
Basile Burg 2018-12-18 21:24:23 +01:00
parent f95ed7ed1c
commit b600aaa07a
8 changed files with 98 additions and 8 deletions

View File

@ -24,6 +24,7 @@ import std.conv;
import std.experimental.logger; import std.experimental.logger;
import std.file; import std.file;
import std.path; import std.path;
import std.range : assumeSorted;
import std.string; import std.string;
import std.typecons; import std.typecons;
@ -67,6 +68,42 @@ public AutocompleteResponse complete(const AutocompleteRequest request,
fakeIdent.type = tok!"identifier"; fakeIdent.type = tok!"identifier";
} }
// detects if the completion request uses the current module `ModuleDeclaration`
// as access chain. In this case removes this access chain, and just keep the dot
// because within a module semantic is the same (`myModule.stuff` -> `.stuff`).
if (tokenArray.length >= 3 && tokenArray[0] == tok!"module" &&
beforeTokens[$-1] == tok!".")
{
const upper = tokenArray.countUntil!(a => a.type == tok!";");
bool isSame = true;
// enough room for the module decl and the fqn...
if (upper != -1 && beforeTokens.length >= upper * 2)
foreach (immutable i; 0 .. upper)
{
const j = beforeTokens.length - upper + i - 1;
// verify that the chain is well located after an expr or a decl
if (i == 0)
{
if (beforeTokens[j].type.among(tok!"{", tok!"}", tok!";"))
continue;
}
// compare the end of the "before tokens" (access chain)
// with the firsts (ModuleDeclaration)
else if ((tokenArray[i].type == tok!"." && beforeTokens[j].type == tok!".") ||
(tokenArray[i].type == tok!"identifier" && tokenArray[i].text == beforeTokens[j].text))
{
continue;
}
isSame = false;
break;
}
// replace the "before tokens" with a pattern making the remaining
// part of the completions think that it's a "Module Scope Operator".
if (isSame)
beforeTokens = assumeSorted([const Token(tok!"{"), const Token(tok!".")]);
}
if (beforeTokens.length >= 2) if (beforeTokens.length >= 2)
{ {
if (beforeTokens[$ - 1] == tok!"(" || beforeTokens[$ - 1] == tok!"[" if (beforeTokens[$ - 1] == tok!"(" || beforeTokens[$ - 1] == tok!"["
@ -164,11 +201,21 @@ AutocompleteResponse dotCompletion(T)(T beforeTokens, const(Token)[] tokenArray,
cursorPosition, CompletionType.identifiers, false, partial); cursorPosition, CompletionType.identifiers, false, partial);
break; break;
case tok!"(": case tok!"(":
case tok!"{":
case tok!"[": case tok!"[":
case tok!";":
case tok!":": case tok!":":
break; break;
// these tokens before a "." means "Module Scope Operator"
case tok!"{":
case tok!";":
case tok!"}":
auto allocator = scoped!(ASTAllocator)();
RollbackAllocator rba;
ScopeSymbolPair pair = generateAutocompleteTrees(tokenArray, allocator,
&rba, 1, moduleCache);
scope(exit) pair.destroy();
response.setCompletions(pair.scope_, getExpression(beforeTokens),
1, CompletionType.identifiers, false, "stdin");
break;
default: default:
break; break;
} }
@ -466,6 +513,20 @@ void setCompletions(T)(ref AutocompleteResponse response,
response.completionType = CompletionType.identifiers; response.completionType = CompletionType.identifiers;
return; return;
} }
// "Module Scope Operator" : filter module decls
else if (tokens.length == 1 && tokens[0] == tok!".")
{
auto currentSymbols = completionScope.getSymbolsInCursorScope(cursorPosition);
foreach (s; currentSymbols.filter!(a => isPublicCompletionKind(a.kind)
&& a.kind != CompletionKind.importSymbol
&& a.kind != CompletionKind.dummy
&& a.symbolFile == partial))
{
response.completions ~= makeSymbolCompletionInfo(s, s.kind);
}
response.completionType = CompletionType.identifiers;
return;
}
if (tokens.length == 0) if (tokens.length == 0)
return; return;

View File

@ -198,12 +198,19 @@ DSymbol*[] getSymbolsByTokenChain(T)(Scope* completionScope,
if (tokens.length == 0) // workaround (#371) if (tokens.length == 0) // workaround (#371)
return []; return [];
} }
else if (tokens[0] == tok!"." && tokens.length > 1) else if (tokens[0] == tok!"." && tokens.length >= 1)
{ {
tokens = tokens[1 .. $]; if (tokens.length == 1)
if (tokens.length == 0) // workaround (#371) {
return []; // Module Scope Operator
symbols = completionScope.getSymbolsAtGlobalScope(stringToken(tokens[0])); auto s = completionScope.getScopeByCursor(1);
return s.symbols.map!(a => a.ptr).filter!(a => a !is null).array;
}
else
{
tokens = tokens[1 .. $];
symbols = completionScope.getSymbolsAtGlobalScope(stringToken(tokens[0]));
}
} }
else else
symbols = completionScope.getSymbolsByNameAndCursor(stringToken(tokens[0]), cursorPosition); symbols = completionScope.getSymbolsByNameAndCursor(stringToken(tokens[0]), cursorPosition);

View File

@ -0,0 +1,5 @@
identifiers
g v
main f
noLocals f
other f

View File

@ -0,0 +1 @@
module foo.bar; int g; void main(); void other(); void noLocals(){int i; foo.bar. }

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

@ -0,0 +1,5 @@
set -e
set -u
../../bin/dcd-client $1 file.d -c82 > actual.txt
diff actual.txt expected.txt

View File

@ -0,0 +1,5 @@
identifiers
g v
main f
noLocals f
other f

View File

@ -0,0 +1 @@
int g; void main(); void other(); void noLocals(){int i; . }

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

@ -0,0 +1,5 @@
set -e
set -u
../../bin/dcd-client $1 file.d -c59 > actual.txt
diff actual.txt expected.txt