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);
|
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
|
else
|
||||||
{
|
{
|
||||||
|
@ -183,6 +187,13 @@ public AutocompleteResponse symbolSearch(const AutocompleteRequest request)
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
enum ImportKind
|
||||||
|
{
|
||||||
|
selective,
|
||||||
|
normal,
|
||||||
|
neither
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles dot completion for identifiers and types.
|
* Handles dot completion for identifiers and types.
|
||||||
* Params:
|
* Params:
|
||||||
|
@ -371,12 +382,12 @@ AutocompleteResponse parenCompletion(T)(T beforeTokens,
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isSelectiveImport(T)(T tokens)
|
ImportKind determineImportKind(T)(T tokens)
|
||||||
{
|
{
|
||||||
assert (tokens.length > 1);
|
assert (tokens.length > 1);
|
||||||
size_t i = tokens.length - 1;
|
size_t i = tokens.length - 1;
|
||||||
if (!(tokens[i] == tok!":" || tokens[i] == tok!","))
|
if (!(tokens[i] == tok!":" || tokens[i] == tok!"," || tokens[i] == tok!"." || tokens[i] == tok!"identifier"))
|
||||||
return false;
|
return ImportKind.neither;
|
||||||
bool foundColon = false;
|
bool foundColon = false;
|
||||||
loop: while (true) switch (tokens[i].type)
|
loop: while (true) switch (tokens[i].type)
|
||||||
{
|
{
|
||||||
|
@ -388,16 +399,16 @@ bool isSelectiveImport(T)(T tokens)
|
||||||
case tok!".":
|
case tok!".":
|
||||||
case tok!",":
|
case tok!",":
|
||||||
if (i == 0)
|
if (i == 0)
|
||||||
return false;
|
return ImportKind.neither;
|
||||||
else
|
else
|
||||||
i--;
|
i--;
|
||||||
break;
|
break;
|
||||||
case tok!"import":
|
case tok!"import":
|
||||||
return foundColon;
|
return foundColon ? ImportKind.selective : ImportKind.normal;
|
||||||
default:
|
default:
|
||||||
return false;
|
return ImportKind.neither;
|
||||||
}
|
}
|
||||||
return false;
|
return ImportKind.neither;
|
||||||
}
|
}
|
||||||
|
|
||||||
unittest
|
unittest
|
||||||
|
@ -410,13 +421,13 @@ unittest
|
||||||
t ~= Token(tok!":");
|
t ~= Token(tok!":");
|
||||||
t ~= Token(tok!"identifier");
|
t ~= Token(tok!"identifier");
|
||||||
t ~= Token(tok!",");
|
t ~= Token(tok!",");
|
||||||
assert (isSelectiveImport(t));
|
assert (determineImportKind(t) == ImportKind.selective);
|
||||||
Token[] t2;
|
Token[] t2;
|
||||||
t2 ~= Token(tok!"else");
|
t2 ~= Token(tok!"else");
|
||||||
t2 ~= Token(tok!":");
|
t2 ~= Token(tok!":");
|
||||||
assert (!isSelectiveImport(t2));
|
assert (determineImportKind(t2) == ImportKind.neither);
|
||||||
import std.stdio;
|
import std.stdio;
|
||||||
writeln("Unittest for isSelectiveImport() passed");
|
writeln("Unittest for determineImportKind() passed");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -425,7 +436,7 @@ unittest
|
||||||
* import std.algorithm: balancedParens;
|
* import std.algorithm: balancedParens;
|
||||||
* ---
|
* ---
|
||||||
*/
|
*/
|
||||||
AutocompleteResponse selectiveImportCompletion(T)(T beforeTokens)
|
AutocompleteResponse importCompletion(T)(T beforeTokens, ImportKind kind)
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
assert (beforeTokens.length >= 2);
|
assert (beforeTokens.length >= 2);
|
||||||
|
@ -433,19 +444,36 @@ in
|
||||||
body
|
body
|
||||||
{
|
{
|
||||||
AutocompleteResponse response;
|
AutocompleteResponse response;
|
||||||
size_t i = beforeTokens.length - 2;
|
if (beforeTokens.length <= 2)
|
||||||
loop: while (i > 0) switch (beforeTokens[i].type)
|
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!"identifier":
|
||||||
case tok!"=":
|
case tok!"=":
|
||||||
case tok!",":
|
case tok!",":
|
||||||
case tok!".":
|
case tok!".":
|
||||||
case tok!":":
|
|
||||||
i--;
|
i--;
|
||||||
break;
|
break;
|
||||||
|
case tok!":":
|
||||||
|
i--;
|
||||||
|
while (beforeTokens[i].type == tok!"identifier" || beforeTokens[i].type == tok!".")
|
||||||
|
i--;
|
||||||
|
break loop;
|
||||||
default:
|
default:
|
||||||
break loop;
|
break loop;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t j = i;
|
size_t j = i;
|
||||||
loop2: while (j <= beforeTokens.length) switch (beforeTokens[j].type)
|
loop2: while (j <= beforeTokens.length) switch (beforeTokens[j].type)
|
||||||
{
|
{
|
||||||
|
@ -467,7 +495,15 @@ body
|
||||||
k++;
|
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;
|
import containers.hashset;
|
||||||
HashSet!string h;
|
HashSet!string h;
|
||||||
|
|
||||||
|
@ -493,6 +529,56 @@ body
|
||||||
return response;
|
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,
|
Scope* completionScope, T tokens, size_t cursorPosition,
|
||||||
CompletionType completionType, bool isBracket = false, string partial = null)
|
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
|
// Handle the simple case where we get all symbols in scope and filter it
|
||||||
// based on the currently entered text.
|
// based on the currently entered text.
|
||||||
if (partial !is null && tokens.length == 0)
|
if (partial !is null && tokens.length == 0)
|
||||||
|
@ -905,49 +983,6 @@ T getExpression(T)(T beforeTokens)
|
||||||
return beforeTokens[i .. $];
|
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:
|
* Params:
|
||||||
* completionType = the completion type being requested
|
* completionType = the completion type being requested
|
||||||
|
|
Loading…
Reference in New Issue