parent
f95ed7ed1c
commit
b600aaa07a
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
@ -751,4 +758,4 @@ AutocompleteResponse.Completion makeSymbolCompletionInfo(const DSymbol* symbol,
|
||||||
// TODO: definition strings could include more information, like on classes inheritance
|
// TODO: definition strings could include more information, like on classes inheritance
|
||||||
return AutocompleteResponse.Completion(symbol.name, kind, definition,
|
return AutocompleteResponse.Completion(symbol.name, kind, definition,
|
||||||
symbol.symbolFile, symbol.location, symbol.doc);
|
symbol.symbolFile, symbol.location, symbol.doc);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
identifiers
|
||||||
|
g v
|
||||||
|
main f
|
||||||
|
noLocals f
|
||||||
|
other f
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
module foo.bar; int g; void main(); void other(); void noLocals(){int i; foo.bar. }
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
set -e
|
||||||
|
set -u
|
||||||
|
|
||||||
|
../../bin/dcd-client $1 file.d -c82 > actual.txt
|
||||||
|
diff actual.txt expected.txt
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
identifiers
|
||||||
|
g v
|
||||||
|
main f
|
||||||
|
noLocals f
|
||||||
|
other f
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
int g; void main(); void other(); void noLocals(){int i; . }
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
set -e
|
||||||
|
set -u
|
||||||
|
|
||||||
|
../../bin/dcd-client $1 file.d -c59 > actual.txt
|
||||||
|
diff actual.txt expected.txt
|
||||||
Loading…
Reference in New Issue