Properly handle multilpe imports as well as selective imports

This commit is contained in:
Hackerpilot 2014-09-22 01:19:09 +00:00
parent 7c210d1237
commit 698808d553
1 changed files with 103 additions and 68 deletions

View File

@ -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