Properly handle multilpe imports as well as selective imports
This commit is contained in:
parent
7c210d1237
commit
698808d553
|
@ -105,9 +105,13 @@ public AutocompleteResponse complete(const AutocompleteRequest request)
|
|||
{
|
||||
return parenCompletion(beforeTokens, tokenArray, request.cursorPosition);
|
||||
}
|
||||
else if (beforeTokens.length >= 2 && isSelectiveImport(beforeTokens))
|
||||
else if (beforeTokens.length >= 2)
|
||||
{
|
||||
return selectiveImportCompletion(beforeTokens);
|
||||
ImportKind kind = determineImportKind(beforeTokens);
|
||||
if (kind == ImportKind.neither)
|
||||
return dotCompletion(beforeTokens, tokenArray, request.cursorPosition);
|
||||
else
|
||||
return importCompletion(beforeTokens, kind);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -183,6 +187,13 @@ public AutocompleteResponse symbolSearch(const AutocompleteRequest request)
|
|||
/******************************************************************************/
|
||||
private:
|
||||
|
||||
enum ImportKind
|
||||
{
|
||||
selective,
|
||||
normal,
|
||||
neither
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles dot completion for identifiers and types.
|
||||
* Params:
|
||||
|
@ -371,12 +382,12 @@ AutocompleteResponse parenCompletion(T)(T beforeTokens,
|
|||
return response;
|
||||
}
|
||||
|
||||
bool isSelectiveImport(T)(T tokens)
|
||||
ImportKind determineImportKind(T)(T tokens)
|
||||
{
|
||||
assert (tokens.length > 1);
|
||||
size_t i = tokens.length - 1;
|
||||
if (!(tokens[i] == tok!":" || tokens[i] == tok!","))
|
||||
return false;
|
||||
if (!(tokens[i] == tok!":" || tokens[i] == tok!"," || tokens[i] == tok!"." || tokens[i] == tok!"identifier"))
|
||||
return ImportKind.neither;
|
||||
bool foundColon = false;
|
||||
loop: while (true) switch (tokens[i].type)
|
||||
{
|
||||
|
@ -388,16 +399,16 @@ bool isSelectiveImport(T)(T tokens)
|
|||
case tok!".":
|
||||
case tok!",":
|
||||
if (i == 0)
|
||||
return false;
|
||||
return ImportKind.neither;
|
||||
else
|
||||
i--;
|
||||
break;
|
||||
case tok!"import":
|
||||
return foundColon;
|
||||
return foundColon ? ImportKind.selective : ImportKind.normal;
|
||||
default:
|
||||
return false;
|
||||
return ImportKind.neither;
|
||||
}
|
||||
return false;
|
||||
return ImportKind.neither;
|
||||
}
|
||||
|
||||
unittest
|
||||
|
@ -410,13 +421,13 @@ unittest
|
|||
t ~= Token(tok!":");
|
||||
t ~= Token(tok!"identifier");
|
||||
t ~= Token(tok!",");
|
||||
assert (isSelectiveImport(t));
|
||||
assert (determineImportKind(t) == ImportKind.selective);
|
||||
Token[] t2;
|
||||
t2 ~= Token(tok!"else");
|
||||
t2 ~= Token(tok!":");
|
||||
assert (!isSelectiveImport(t2));
|
||||
assert (determineImportKind(t2) == ImportKind.neither);
|
||||
import std.stdio;
|
||||
writeln("Unittest for isSelectiveImport() passed");
|
||||
writeln("Unittest for determineImportKind() passed");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -425,7 +436,7 @@ unittest
|
|||
* import std.algorithm: balancedParens;
|
||||
* ---
|
||||
*/
|
||||
AutocompleteResponse selectiveImportCompletion(T)(T beforeTokens)
|
||||
AutocompleteResponse importCompletion(T)(T beforeTokens, ImportKind kind)
|
||||
in
|
||||
{
|
||||
assert (beforeTokens.length >= 2);
|
||||
|
@ -433,19 +444,36 @@ in
|
|||
body
|
||||
{
|
||||
AutocompleteResponse response;
|
||||
size_t i = beforeTokens.length - 2;
|
||||
loop: while (i > 0) switch (beforeTokens[i].type)
|
||||
if (beforeTokens.length <= 2)
|
||||
return response;
|
||||
|
||||
size_t i = beforeTokens.length - 1;
|
||||
|
||||
if (kind == ImportKind.normal)
|
||||
{
|
||||
|
||||
while (beforeTokens[i].type != tok!"," && beforeTokens[i].type != tok!"import") i--;
|
||||
setImportCompletions(beforeTokens[i .. $], response);
|
||||
return response;
|
||||
}
|
||||
|
||||
loop: while (true) switch (beforeTokens[i].type)
|
||||
{
|
||||
case tok!"identifier":
|
||||
case tok!"=":
|
||||
case tok!",":
|
||||
case tok!".":
|
||||
case tok!":":
|
||||
i--;
|
||||
break;
|
||||
case tok!":":
|
||||
i--;
|
||||
while (beforeTokens[i].type == tok!"identifier" || beforeTokens[i].type == tok!".")
|
||||
i--;
|
||||
break loop;
|
||||
default:
|
||||
break loop;
|
||||
}
|
||||
|
||||
size_t j = i;
|
||||
loop2: while (j <= beforeTokens.length) switch (beforeTokens[j].type)
|
||||
{
|
||||
|
@ -467,7 +495,15 @@ body
|
|||
k++;
|
||||
}
|
||||
}
|
||||
auto symbols = ModuleCache.getModuleSymbol(ModuleCache.resolveImportLoctation(path));
|
||||
|
||||
string resolvedLocation = ModuleCache.resolveImportLoctation(path);
|
||||
if (resolvedLocation is null)
|
||||
{
|
||||
Log.error("Could not resolve location of ", path);
|
||||
return response;
|
||||
}
|
||||
auto symbols = ModuleCache.getModuleSymbol(resolvedLocation);
|
||||
|
||||
import containers.hashset;
|
||||
HashSet!string h;
|
||||
|
||||
|
@ -493,6 +529,56 @@ body
|
|||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the response with completion information for an import statement
|
||||
* Params:
|
||||
* tokens = the tokens after the "import" keyword and before the cursor
|
||||
* response = the response that should be populated
|
||||
*/
|
||||
void setImportCompletions(T)(T tokens, ref AutocompleteResponse response)
|
||||
{
|
||||
response.completionType = CompletionType.identifiers;
|
||||
auto moduleParts = tokens.filter!(a => a.type == tok!"identifier").map!("a.text").array();
|
||||
string path = buildPath(moduleParts);
|
||||
|
||||
bool found = false;
|
||||
|
||||
foreach (importDirectory; ModuleCache.getImportPaths())
|
||||
{
|
||||
string p = buildPath(importDirectory, path);
|
||||
if (!exists(p))
|
||||
continue;
|
||||
|
||||
found = true;
|
||||
|
||||
foreach (string name; dirEntries(p, SpanMode.shallow))
|
||||
{
|
||||
import std.path: baseName;
|
||||
if (name.baseName.startsWith(".#"))
|
||||
continue;
|
||||
|
||||
if (isFile(name) && (name.endsWith(".d") || name.endsWith(".di")))
|
||||
{
|
||||
response.completions ~= name.baseName(".d").baseName(".di");
|
||||
response.completionKinds ~= CompletionKind.moduleName;
|
||||
}
|
||||
else if (isDir(name))
|
||||
{
|
||||
string n = name.baseName();
|
||||
if (n[0] != '.')
|
||||
{
|
||||
response.completions ~= n;
|
||||
response.completionKinds ~=
|
||||
exists(buildPath(name, "package.d")) || exists(buildPath(name, "package.di"))
|
||||
? CompletionKind.moduleName : CompletionKind.packageName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
Log.error("Could not find ", moduleParts);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
@ -666,14 +752,6 @@ void setCompletions(T)(ref AutocompleteResponse response,
|
|||
Scope* completionScope, T tokens, size_t cursorPosition,
|
||||
CompletionType completionType, bool isBracket = false, string partial = null)
|
||||
{
|
||||
// Autocomplete module imports instead of symbols
|
||||
if (tokens.length > 0 && tokens[0].type == tok!"import")
|
||||
{
|
||||
if (completionType == CompletionType.identifiers)
|
||||
setImportCompletions(tokens, response);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle the simple case where we get all symbols in scope and filter it
|
||||
// based on the currently entered text.
|
||||
if (partial !is null && tokens.length == 0)
|
||||
|
@ -905,49 +983,6 @@ T getExpression(T)(T beforeTokens)
|
|||
return beforeTokens[i .. $];
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the response with completion information for an import statement
|
||||
* Params:
|
||||
* tokens = the tokens after the "import" keyword and before the cursor
|
||||
* response = the response that should be populated
|
||||
*/
|
||||
void setImportCompletions(T)(T tokens, ref AutocompleteResponse response)
|
||||
{
|
||||
response.completionType = CompletionType.identifiers;
|
||||
auto moduleParts = tokens.filter!(a => a.type == tok!"identifier").map!("a.text").array();
|
||||
string path = buildPath(moduleParts);
|
||||
|
||||
foreach (importDirectory; ModuleCache.getImportPaths())
|
||||
{
|
||||
string p = buildPath(importDirectory, path);
|
||||
// Log.trace("Checking for ", p);
|
||||
if (!exists(p))
|
||||
continue;
|
||||
|
||||
foreach (string name; dirEntries(p, SpanMode.shallow))
|
||||
{
|
||||
import std.path: baseName;
|
||||
if (name.baseName.startsWith(".#")) continue;
|
||||
if (isFile(name) && (name.endsWith(".d") || name.endsWith(".di")))
|
||||
{
|
||||
response.completions ~= name.baseName(".d").baseName(".di");
|
||||
response.completionKinds ~= CompletionKind.moduleName;
|
||||
}
|
||||
else if (isDir(name))
|
||||
{
|
||||
string n = name.baseName();
|
||||
if (n[0] != '.')
|
||||
{
|
||||
response.completions ~= n;
|
||||
response.completionKinds ~=
|
||||
exists(buildPath(name, "package.d")) || exists(buildPath(name, "package.di"))
|
||||
? CompletionKind.moduleName : CompletionKind.packageName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Params:
|
||||
* completionType = the completion type being requested
|
||||
|
|
Loading…
Reference in New Issue