Skip to content

Commit 698808d

Browse files
committed
Properly handle multilpe imports as well as selective imports
1 parent 7c210d1 commit 698808d

File tree

1 file changed

+103
-68
lines changed

1 file changed

+103
-68
lines changed

src/autocomplete.d

Lines changed: 103 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,13 @@ public AutocompleteResponse complete(const AutocompleteRequest request)
105105
{
106106
return parenCompletion(beforeTokens, tokenArray, request.cursorPosition);
107107
}
108-
else if (beforeTokens.length >= 2 && isSelectiveImport(beforeTokens))
108+
else if (beforeTokens.length >= 2)
109109
{
110-
return selectiveImportCompletion(beforeTokens);
110+
ImportKind kind = determineImportKind(beforeTokens);
111+
if (kind == ImportKind.neither)
112+
return dotCompletion(beforeTokens, tokenArray, request.cursorPosition);
113+
else
114+
return importCompletion(beforeTokens, kind);
111115
}
112116
else
113117
{
@@ -183,6 +187,13 @@ public AutocompleteResponse symbolSearch(const AutocompleteRequest request)
183187
/******************************************************************************/
184188
private:
185189

190+
enum ImportKind
191+
{
192+
selective,
193+
normal,
194+
neither
195+
}
196+
186197
/**
187198
* Handles dot completion for identifiers and types.
188199
* Params:
@@ -371,12 +382,12 @@ AutocompleteResponse parenCompletion(T)(T beforeTokens,
371382
return response;
372383
}
373384

374-
bool isSelectiveImport(T)(T tokens)
385+
ImportKind determineImportKind(T)(T tokens)
375386
{
376387
assert (tokens.length > 1);
377388
size_t i = tokens.length - 1;
378-
if (!(tokens[i] == tok!":" || tokens[i] == tok!","))
379-
return false;
389+
if (!(tokens[i] == tok!":" || tokens[i] == tok!"," || tokens[i] == tok!"." || tokens[i] == tok!"identifier"))
390+
return ImportKind.neither;
380391
bool foundColon = false;
381392
loop: while (true) switch (tokens[i].type)
382393
{
@@ -388,16 +399,16 @@ bool isSelectiveImport(T)(T tokens)
388399
case tok!".":
389400
case tok!",":
390401
if (i == 0)
391-
return false;
402+
return ImportKind.neither;
392403
else
393404
i--;
394405
break;
395406
case tok!"import":
396-
return foundColon;
407+
return foundColon ? ImportKind.selective : ImportKind.normal;
397408
default:
398-
return false;
409+
return ImportKind.neither;
399410
}
400-
return false;
411+
return ImportKind.neither;
401412
}
402413

403414
unittest
@@ -410,13 +421,13 @@ unittest
410421
t ~= Token(tok!":");
411422
t ~= Token(tok!"identifier");
412423
t ~= Token(tok!",");
413-
assert (isSelectiveImport(t));
424+
assert (determineImportKind(t) == ImportKind.selective);
414425
Token[] t2;
415426
t2 ~= Token(tok!"else");
416427
t2 ~= Token(tok!":");
417-
assert (!isSelectiveImport(t2));
428+
assert (determineImportKind(t2) == ImportKind.neither);
418429
import std.stdio;
419-
writeln("Unittest for isSelectiveImport() passed");
430+
writeln("Unittest for determineImportKind() passed");
420431
}
421432

422433
/**
@@ -425,27 +436,44 @@ unittest
425436
* import std.algorithm: balancedParens;
426437
* ---
427438
*/
428-
AutocompleteResponse selectiveImportCompletion(T)(T beforeTokens)
439+
AutocompleteResponse importCompletion(T)(T beforeTokens, ImportKind kind)
429440
in
430441
{
431442
assert (beforeTokens.length >= 2);
432443
}
433444
body
434445
{
435446
AutocompleteResponse response;
436-
size_t i = beforeTokens.length - 2;
437-
loop: while (i > 0) switch (beforeTokens[i].type)
447+
if (beforeTokens.length <= 2)
448+
return response;
449+
450+
size_t i = beforeTokens.length - 1;
451+
452+
if (kind == ImportKind.normal)
453+
{
454+
455+
while (beforeTokens[i].type != tok!"," && beforeTokens[i].type != tok!"import") i--;
456+
setImportCompletions(beforeTokens[i .. $], response);
457+
return response;
458+
}
459+
460+
loop: while (true) switch (beforeTokens[i].type)
438461
{
439462
case tok!"identifier":
440463
case tok!"=":
441464
case tok!",":
442465
case tok!".":
443-
case tok!":":
444466
i--;
445467
break;
468+
case tok!":":
469+
i--;
470+
while (beforeTokens[i].type == tok!"identifier" || beforeTokens[i].type == tok!".")
471+
i--;
472+
break loop;
446473
default:
447474
break loop;
448475
}
476+
449477
size_t j = i;
450478
loop2: while (j <= beforeTokens.length) switch (beforeTokens[j].type)
451479
{
@@ -467,7 +495,15 @@ body
467495
k++;
468496
}
469497
}
470-
auto symbols = ModuleCache.getModuleSymbol(ModuleCache.resolveImportLoctation(path));
498+
499+
string resolvedLocation = ModuleCache.resolveImportLoctation(path);
500+
if (resolvedLocation is null)
501+
{
502+
Log.error("Could not resolve location of ", path);
503+
return response;
504+
}
505+
auto symbols = ModuleCache.getModuleSymbol(resolvedLocation);
506+
471507
import containers.hashset;
472508
HashSet!string h;
473509

@@ -493,6 +529,56 @@ body
493529
return response;
494530
}
495531

532+
/**
533+
* Populates the response with completion information for an import statement
534+
* Params:
535+
* tokens = the tokens after the "import" keyword and before the cursor
536+
* response = the response that should be populated
537+
*/
538+
void setImportCompletions(T)(T tokens, ref AutocompleteResponse response)
539+
{
540+
response.completionType = CompletionType.identifiers;
541+
auto moduleParts = tokens.filter!(a => a.type == tok!"identifier").map!("a.text").array();
542+
string path = buildPath(moduleParts);
543+
544+
bool found = false;
545+
546+
foreach (importDirectory; ModuleCache.getImportPaths())
547+
{
548+
string p = buildPath(importDirectory, path);
549+
if (!exists(p))
550+
continue;
551+
552+
found = true;
553+
554+
foreach (string name; dirEntries(p, SpanMode.shallow))
555+
{
556+
import std.path: baseName;
557+
if (name.baseName.startsWith(".#"))
558+
continue;
559+
560+
if (isFile(name) && (name.endsWith(".d") || name.endsWith(".di")))
561+
{
562+
response.completions ~= name.baseName(".d").baseName(".di");
563+
response.completionKinds ~= CompletionKind.moduleName;
564+
}
565+
else if (isDir(name))
566+
{
567+
string n = name.baseName();
568+
if (n[0] != '.')
569+
{
570+
response.completions ~= n;
571+
response.completionKinds ~=
572+
exists(buildPath(name, "package.d")) || exists(buildPath(name, "package.di"))
573+
? CompletionKind.moduleName : CompletionKind.packageName;
574+
}
575+
}
576+
}
577+
}
578+
if (!found)
579+
Log.error("Could not find ", moduleParts);
580+
}
581+
496582
/**
497583
*
498584
*/
@@ -666,14 +752,6 @@ void setCompletions(T)(ref AutocompleteResponse response,
666752
Scope* completionScope, T tokens, size_t cursorPosition,
667753
CompletionType completionType, bool isBracket = false, string partial = null)
668754
{
669-
// Autocomplete module imports instead of symbols
670-
if (tokens.length > 0 && tokens[0].type == tok!"import")
671-
{
672-
if (completionType == CompletionType.identifiers)
673-
setImportCompletions(tokens, response);
674-
return;
675-
}
676-
677755
// Handle the simple case where we get all symbols in scope and filter it
678756
// based on the currently entered text.
679757
if (partial !is null && tokens.length == 0)
@@ -905,49 +983,6 @@ T getExpression(T)(T beforeTokens)
905983
return beforeTokens[i .. $];
906984
}
907985

908-
/**
909-
* Populates the response with completion information for an import statement
910-
* Params:
911-
* tokens = the tokens after the "import" keyword and before the cursor
912-
* response = the response that should be populated
913-
*/
914-
void setImportCompletions(T)(T tokens, ref AutocompleteResponse response)
915-
{
916-
response.completionType = CompletionType.identifiers;
917-
auto moduleParts = tokens.filter!(a => a.type == tok!"identifier").map!("a.text").array();
918-
string path = buildPath(moduleParts);
919-
920-
foreach (importDirectory; ModuleCache.getImportPaths())
921-
{
922-
string p = buildPath(importDirectory, path);
923-
// Log.trace("Checking for ", p);
924-
if (!exists(p))
925-
continue;
926-
927-
foreach (string name; dirEntries(p, SpanMode.shallow))
928-
{
929-
import std.path: baseName;
930-
if (name.baseName.startsWith(".#")) continue;
931-
if (isFile(name) && (name.endsWith(".d") || name.endsWith(".di")))
932-
{
933-
response.completions ~= name.baseName(".d").baseName(".di");
934-
response.completionKinds ~= CompletionKind.moduleName;
935-
}
936-
else if (isDir(name))
937-
{
938-
string n = name.baseName();
939-
if (n[0] != '.')
940-
{
941-
response.completions ~= n;
942-
response.completionKinds ~=
943-
exists(buildPath(name, "package.d")) || exists(buildPath(name, "package.di"))
944-
? CompletionKind.moduleName : CompletionKind.packageName;
945-
}
946-
}
947-
}
948-
}
949-
}
950-
951986
/**
952987
* Params:
953988
* completionType = the completion type being requested

0 commit comments

Comments
 (0)