Only split server code, no changes
This commit is contained in:
parent
cd4a027eac
commit
6c310d2d5e
|
@ -50,21 +50,26 @@ import common.messages;
|
|||
* Returns:
|
||||
* the autocompletion response
|
||||
*/
|
||||
public AutocompleteResponse complete(const AutocompleteRequest request, ref ModuleCache moduleCache)
|
||||
public AutocompleteResponse complete(const AutocompleteRequest request,
|
||||
ref ModuleCache moduleCache)
|
||||
{
|
||||
const(Token)[] tokenArray;
|
||||
auto stringCache = StringCache(StringCache.defaultBucketCount);
|
||||
auto beforeTokens = getTokensBeforeCursor(request.sourceCode,
|
||||
request.cursorPosition, stringCache, tokenArray);
|
||||
request.cursorPosition, stringCache, tokenArray);
|
||||
if (beforeTokens.length >= 2)
|
||||
{
|
||||
if (beforeTokens[$ - 1] == tok!"(" || beforeTokens[$ - 1] == tok!"["
|
||||
|| beforeTokens[$ - 1] == tok!",")
|
||||
if (beforeTokens[$ - 1] == tok!"(" || beforeTokens[$ - 1] == tok!"[")
|
||||
{
|
||||
return parenCompletion(beforeTokens, tokenArray, request.cursorPosition,
|
||||
moduleCache);
|
||||
}
|
||||
else if (beforeTokens[$ - 1] == tok!",")
|
||||
{
|
||||
immutable size_t end = goBackToOpenParen(beforeTokens);
|
||||
if (end != size_t.max)
|
||||
return parenCompletion(beforeTokens[0 .. end], tokenArray,
|
||||
request.cursorPosition, moduleCache);
|
||||
request.cursorPosition, moduleCache);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -72,9 +77,10 @@ public AutocompleteResponse complete(const AutocompleteRequest request, ref Modu
|
|||
if (kind == ImportKind.neither)
|
||||
{
|
||||
if (beforeTokens.isUdaExpression)
|
||||
beforeTokens = beforeTokens[$ - 1 .. $];
|
||||
return dotCompletion(beforeTokens, tokenArray, request.cursorPosition, moduleCache);
|
||||
}
|
||||
beforeTokens = beforeTokens[$-1 .. $];
|
||||
return dotCompletion(beforeTokens, tokenArray, request.cursorPosition,
|
||||
moduleCache);
|
||||
}
|
||||
else
|
||||
return importCompletion(beforeTokens, kind, moduleCache);
|
||||
}
|
||||
|
@ -92,7 +98,7 @@ public AutocompleteResponse complete(const AutocompleteRequest request, ref Modu
|
|||
* the autocompletion response
|
||||
*/
|
||||
AutocompleteResponse dotCompletion(T)(T beforeTokens, const(Token)[] tokenArray,
|
||||
size_t cursorPosition, ref ModuleCache moduleCache)
|
||||
size_t cursorPosition, ref ModuleCache moduleCache)
|
||||
{
|
||||
AutocompleteResponse response;
|
||||
|
||||
|
@ -116,14 +122,16 @@ AutocompleteResponse dotCompletion(T)(T beforeTokens, const(Token)[] tokenArray,
|
|||
significantTokenType = tok!"identifier";
|
||||
beforeTokens = beforeTokens[0 .. $ - 1];
|
||||
}
|
||||
else if (beforeTokens.length >= 2 && beforeTokens[$ - 1] == tok!".")
|
||||
else if (beforeTokens.length >= 2 && beforeTokens[$ - 1] == tok!".")
|
||||
significantTokenType = beforeTokens[$ - 2].type;
|
||||
else
|
||||
return response;
|
||||
|
||||
switch (significantTokenType)
|
||||
{
|
||||
mixin(STRING_LITERAL_CASES);
|
||||
case tok!"stringLiteral":
|
||||
case tok!"wstringLiteral":
|
||||
case tok!"dstringLiteral":
|
||||
foreach (symbol; arraySymbols)
|
||||
{
|
||||
response.completionKinds ~= symbol.kind;
|
||||
|
@ -131,17 +139,41 @@ AutocompleteResponse dotCompletion(T)(T beforeTokens, const(Token)[] tokenArray,
|
|||
}
|
||||
response.completionType = CompletionType.identifiers;
|
||||
break;
|
||||
mixin(TYPE_IDENT_CASES);
|
||||
case tok!"int":
|
||||
case tok!"uint":
|
||||
case tok!"long":
|
||||
case tok!"ulong":
|
||||
case tok!"char":
|
||||
case tok!"wchar":
|
||||
case tok!"dchar":
|
||||
case tok!"bool":
|
||||
case tok!"byte":
|
||||
case tok!"ubyte":
|
||||
case tok!"short":
|
||||
case tok!"ushort":
|
||||
case tok!"cent":
|
||||
case tok!"ucent":
|
||||
case tok!"float":
|
||||
case tok!"ifloat":
|
||||
case tok!"cfloat":
|
||||
case tok!"idouble":
|
||||
case tok!"cdouble":
|
||||
case tok!"double":
|
||||
case tok!"real":
|
||||
case tok!"ireal":
|
||||
case tok!"creal":
|
||||
case tok!"identifier":
|
||||
case tok!")":
|
||||
case tok!"]":
|
||||
case tok!"this":
|
||||
case tok!"super":
|
||||
auto allocator = scoped!(ASTAllocator)();
|
||||
RollbackAllocator rba;
|
||||
ScopeSymbolPair pair = generateAutocompleteTrees(tokenArray, allocator,
|
||||
&rba, cursorPosition, moduleCache);
|
||||
scope (exit)
|
||||
pair.destroy();
|
||||
&rba, cursorPosition, moduleCache);
|
||||
scope(exit) pair.destroy();
|
||||
response.setCompletions(pair.scope_, getExpression(beforeTokens),
|
||||
cursorPosition, CompletionType.identifiers, false, partial);
|
||||
cursorPosition, CompletionType.identifiers, false, partial);
|
||||
break;
|
||||
case tok!"(":
|
||||
case tok!"{":
|
||||
|
@ -165,7 +197,7 @@ AutocompleteResponse dotCompletion(T)(T beforeTokens, const(Token)[] tokenArray,
|
|||
* the autocompletion response
|
||||
*/
|
||||
AutocompleteResponse parenCompletion(T)(T beforeTokens,
|
||||
const(Token)[] tokenArray, size_t cursorPosition, ref ModuleCache moduleCache)
|
||||
const(Token)[] tokenArray, size_t cursorPosition, ref ModuleCache moduleCache)
|
||||
{
|
||||
AutocompleteResponse response;
|
||||
immutable(string)[] completions;
|
||||
|
@ -195,6 +227,7 @@ AutocompleteResponse parenCompletion(T)(T beforeTokens,
|
|||
break;
|
||||
case tok!"characterLiteral":
|
||||
case tok!"doubleLiteral":
|
||||
case tok!"dstringLiteral":
|
||||
case tok!"floatLiteral":
|
||||
case tok!"identifier":
|
||||
case tok!"idoubleLiteral":
|
||||
|
@ -203,22 +236,22 @@ AutocompleteResponse parenCompletion(T)(T beforeTokens,
|
|||
case tok!"irealLiteral":
|
||||
case tok!"longLiteral":
|
||||
case tok!"realLiteral":
|
||||
case tok!"stringLiteral":
|
||||
case tok!"uintLiteral":
|
||||
case tok!"ulongLiteral":
|
||||
case tok!"wstringLiteral":
|
||||
case tok!"this":
|
||||
case tok!"super":
|
||||
case tok!")":
|
||||
case tok!"]":
|
||||
mixin(STRING_LITERAL_CASES);
|
||||
auto allocator = scoped!(ASTAllocator)();
|
||||
RollbackAllocator rba;
|
||||
ScopeSymbolPair pair = generateAutocompleteTrees(tokenArray, allocator,
|
||||
&rba, cursorPosition, moduleCache);
|
||||
scope (exit)
|
||||
pair.destroy();
|
||||
&rba, cursorPosition, moduleCache);
|
||||
scope(exit) pair.destroy();
|
||||
auto expression = getExpression(beforeTokens[0 .. $ - 1]);
|
||||
response.setCompletions(pair.scope_, expression, cursorPosition,
|
||||
CompletionType.calltips, beforeTokens[$ - 1] == tok!"[");
|
||||
response.setCompletions(pair.scope_, expression,
|
||||
cursorPosition, CompletionType.calltips, beforeTokens[$ - 1] == tok!"[");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -233,10 +266,10 @@ AutocompleteResponse parenCompletion(T)(T beforeTokens,
|
|||
* ---
|
||||
*/
|
||||
AutocompleteResponse importCompletion(T)(T beforeTokens, ImportKind kind,
|
||||
ref ModuleCache moduleCache)
|
||||
ref ModuleCache moduleCache)
|
||||
in
|
||||
{
|
||||
assert(beforeTokens.length >= 2);
|
||||
assert (beforeTokens.length >= 2);
|
||||
}
|
||||
body
|
||||
{
|
||||
|
@ -249,8 +282,8 @@ body
|
|||
if (kind == ImportKind.normal)
|
||||
{
|
||||
|
||||
while (beforeTokens[i].type != tok!","
|
||||
&& beforeTokens[i].type != tok!"import" && beforeTokens[i].type != tok!"=")
|
||||
while (beforeTokens[i].type != tok!"," && beforeTokens[i].type != tok!"import"
|
||||
&& beforeTokens[i].type != tok!"=" )
|
||||
i--;
|
||||
setImportCompletions(beforeTokens[i .. $], response, moduleCache);
|
||||
return response;
|
||||
|
@ -276,11 +309,8 @@ body
|
|||
size_t j = i;
|
||||
loop2: while (j <= beforeTokens.length) switch (beforeTokens[j].type)
|
||||
{
|
||||
case tok!":":
|
||||
break loop2;
|
||||
default:
|
||||
j++;
|
||||
break;
|
||||
case tok!":": break loop2;
|
||||
default: j++; break;
|
||||
}
|
||||
|
||||
if (i >= j)
|
||||
|
@ -289,8 +319,11 @@ body
|
|||
return response;
|
||||
}
|
||||
|
||||
immutable string path = beforeTokens[i + 1 .. j].filter!(token => token.type == tok!"identifier")
|
||||
.map!(token => cast() token.text).joiner(dirSeparator).text();
|
||||
immutable string path = beforeTokens[i + 1 .. j]
|
||||
.filter!(token => token.type == tok!"identifier")
|
||||
.map!(token => cast() token.text)
|
||||
.joiner(dirSeparator)
|
||||
.text();
|
||||
|
||||
string resolvedLocation = moduleCache.resolveImportLocation(path);
|
||||
if (resolvedLocation is null)
|
||||
|
@ -301,15 +334,14 @@ body
|
|||
auto symbols = moduleCache.getModuleSymbol(internString(resolvedLocation));
|
||||
|
||||
import containers.hashset : HashSet;
|
||||
|
||||
HashSet!string h;
|
||||
|
||||
void addSymbolToResponses(const(DSymbol)* sy)
|
||||
{
|
||||
auto a = DSymbol(sy.name);
|
||||
if (!builtinSymbols.contains(&a) && sy.name !is null
|
||||
&& !h.contains(sy.name) && !sy.skipOver
|
||||
&& sy.name != CONSTRUCTOR_SYMBOL_NAME && isPublicCompletionKind(sy.kind))
|
||||
if (!builtinSymbols.contains(&a) && sy.name !is null && !h.contains(sy.name)
|
||||
&& !sy.skipOver && sy.name != CONSTRUCTOR_SYMBOL_NAME
|
||||
&& isPublicCompletionKind(sy.kind))
|
||||
{
|
||||
response.completionKinds ~= sy.kind;
|
||||
response.completions ~= sy.name;
|
||||
|
@ -323,7 +355,7 @@ body
|
|||
foreach (sy; s.type.opSlice().filter!(a => !a.skipOver))
|
||||
addSymbolToResponses(sy);
|
||||
else
|
||||
addSymbolToResponses(s);
|
||||
addSymbolToResponses(s);
|
||||
}
|
||||
response.completionType = CompletionType.identifiers;
|
||||
return response;
|
||||
|
@ -335,7 +367,8 @@ body
|
|||
* 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, ref ModuleCache cache)
|
||||
void setImportCompletions(T)(T tokens, ref AutocompleteResponse response,
|
||||
ref ModuleCache cache)
|
||||
{
|
||||
response.completionType = CompletionType.identifiers;
|
||||
string partial = null;
|
||||
|
@ -359,8 +392,8 @@ void setImportCompletions(T)(T tokens, ref AutocompleteResponse response, ref Mo
|
|||
found = true;
|
||||
|
||||
auto n = importPath.baseName(".d").baseName(".di");
|
||||
if (isFile(importPath) && (importPath.endsWith(".d")
|
||||
|| importPath.endsWith(".di")) && (partial is null || n.startsWith(partial)))
|
||||
if (isFile(importPath) && (importPath.endsWith(".d") || importPath.endsWith(".di"))
|
||||
&& (partial is null || n.startsWith(partial)))
|
||||
{
|
||||
response.completions ~= n;
|
||||
response.completionKinds ~= CompletionKind.moduleName;
|
||||
|
@ -374,32 +407,31 @@ void setImportCompletions(T)(T tokens, ref AutocompleteResponse response, ref Mo
|
|||
|
||||
found = true;
|
||||
|
||||
try
|
||||
foreach (string name; dirEntries(p, SpanMode.shallow))
|
||||
try foreach (string name; dirEntries(p, SpanMode.shallow))
|
||||
{
|
||||
import std.path: baseName;
|
||||
if (name.baseName.startsWith(".#"))
|
||||
continue;
|
||||
|
||||
auto n = name.baseName(".d").baseName(".di");
|
||||
if (isFile(name) && (name.endsWith(".d") || name.endsWith(".di"))
|
||||
&& (partial is null || n.startsWith(partial)))
|
||||
{
|
||||
import std.path : baseName;
|
||||
|
||||
if (name.baseName.startsWith(".#"))
|
||||
continue;
|
||||
|
||||
auto n = name.baseName(".d").baseName(".di");
|
||||
if (isFile(name) && (name.endsWith(".d") || name.endsWith(".di"))
|
||||
&& (partial is null || n.startsWith(partial)))
|
||||
response.completions ~= n;
|
||||
response.completionKinds ~= CompletionKind.moduleName;
|
||||
}
|
||||
else if (isDir(name))
|
||||
{
|
||||
if (n[0] != '.' && (partial is null || n.startsWith(partial)))
|
||||
{
|
||||
response.completions ~= n;
|
||||
response.completionKinds ~= CompletionKind.moduleName;
|
||||
}
|
||||
else if (isDir(name))
|
||||
{
|
||||
if (n[0] != '.' && (partial is null || n.startsWith(partial)))
|
||||
{
|
||||
response.completions ~= n;
|
||||
response.completionKinds ~= exists(buildPath(name, "package.d")) || exists(buildPath(name,
|
||||
"package.di")) ? CompletionKind.moduleName : CompletionKind.packageName;
|
||||
}
|
||||
response.completionKinds ~=
|
||||
exists(buildPath(name, "package.d")) || exists(buildPath(name, "package.di"))
|
||||
? CompletionKind.moduleName : CompletionKind.packageName;
|
||||
}
|
||||
}
|
||||
catch (FileException)
|
||||
}
|
||||
catch(FileException)
|
||||
{
|
||||
warning("Cannot access import path: ", importPath);
|
||||
}
|
||||
|
@ -412,21 +444,21 @@ void setImportCompletions(T)(T tokens, ref AutocompleteResponse response, ref Mo
|
|||
/**
|
||||
*
|
||||
*/
|
||||
void setCompletions(T)(ref AutocompleteResponse response, Scope* completionScope, T tokens,
|
||||
size_t cursorPosition, CompletionType completionType,
|
||||
bool isBracket = false, string partial = null)
|
||||
void setCompletions(T)(ref AutocompleteResponse response,
|
||||
Scope* completionScope, T tokens, size_t cursorPosition,
|
||||
CompletionType completionType, bool isBracket = false, string partial = null)
|
||||
{
|
||||
static void addSymToResponse(const(DSymbol)* s, ref AutocompleteResponse r,
|
||||
string p, size_t[] circularGuard = [])
|
||||
static void addSymToResponse(const(DSymbol)* s, ref AutocompleteResponse r, string p,
|
||||
size_t[] circularGuard = [])
|
||||
{
|
||||
if (circularGuard.canFind(cast(size_t) s))
|
||||
return;
|
||||
foreach (sym; s.opSlice())
|
||||
{
|
||||
if (sym.name !is null && sym.name.length > 0
|
||||
&& isPublicCompletionKind(sym.kind) && (p is null ? true
|
||||
: toUpper(sym.name.data).startsWith(toUpper(p)))
|
||||
&& !r.completions.canFind(sym.name) && sym.name[0] != '*')
|
||||
if (sym.name !is null && sym.name.length > 0 && isPublicCompletionKind(sym.kind)
|
||||
&& (p is null ? true : toUpper(sym.name.data).startsWith(toUpper(p)))
|
||||
&& !r.completions.canFind(sym.name)
|
||||
&& sym.name[0] != '*')
|
||||
{
|
||||
r.completionKinds ~= sym.kind;
|
||||
r.completions ~= sym.name.dup;
|
||||
|
@ -455,7 +487,7 @@ void setCompletions(T)(ref AutocompleteResponse response, Scope* completionScope
|
|||
return;
|
||||
|
||||
DSymbol*[] symbols = getSymbolsByTokenChain(completionScope, tokens,
|
||||
cursorPosition, completionType);
|
||||
cursorPosition, completionType);
|
||||
|
||||
if (symbols.length == 0)
|
||||
return;
|
||||
|
@ -467,7 +499,8 @@ void setCompletions(T)(ref AutocompleteResponse response, Scope* completionScope
|
|||
|| symbols[0].kind == CompletionKind.importSymbol
|
||||
|| symbols[0].kind == CompletionKind.aliasName)
|
||||
{
|
||||
symbols = symbols[0].type is null || symbols[0].type is symbols[0] ? [] : [symbols[0].type];
|
||||
symbols = symbols[0].type is null || symbols[0].type is symbols[0] ? []
|
||||
: [symbols[0].type];
|
||||
if (symbols.length == 0)
|
||||
return;
|
||||
}
|
||||
|
@ -477,7 +510,8 @@ void setCompletions(T)(ref AutocompleteResponse response, Scope* completionScope
|
|||
else if (completionType == CompletionType.calltips)
|
||||
{
|
||||
//trace("Showing call tips for ", symbols[0].name, " of kind ", symbols[0].kind);
|
||||
if (symbols[0].kind != CompletionKind.functionName && symbols[0].callTip is null)
|
||||
if (symbols[0].kind != CompletionKind.functionName
|
||||
&& symbols[0].callTip is null)
|
||||
{
|
||||
if (symbols[0].kind == CompletionKind.aliasName)
|
||||
{
|
||||
|
@ -512,8 +546,8 @@ void setCompletions(T)(ref AutocompleteResponse response, Scope* completionScope
|
|||
}
|
||||
}
|
||||
}
|
||||
if (symbols[0].kind == CompletionKind.structName || symbols[0].kind
|
||||
== CompletionKind.className)
|
||||
if (symbols[0].kind == CompletionKind.structName
|
||||
|| symbols[0].kind == CompletionKind.className)
|
||||
{
|
||||
auto constructor = symbols[0].getPartsByName(CONSTRUCTOR_SYMBOL_NAME);
|
||||
if (constructor.length == 0)
|
||||
|
|
|
@ -40,81 +40,82 @@ import common.messages;
|
|||
* Returns:
|
||||
* the autocompletion response.
|
||||
*/
|
||||
public AutocompleteResponse findLocalUse(AutocompleteRequest request, ref ModuleCache moduleCache)
|
||||
public AutocompleteResponse findLocalUse(AutocompleteRequest request,
|
||||
ref ModuleCache moduleCache)
|
||||
{
|
||||
AutocompleteResponse response;
|
||||
RollbackAllocator rba;
|
||||
auto allocator = scoped!(ASTAllocator)();
|
||||
auto cache = StringCache(StringCache.defaultBucketCount);
|
||||
AutocompleteResponse response;
|
||||
RollbackAllocator rba;
|
||||
auto allocator = scoped!(ASTAllocator)();
|
||||
auto cache = StringCache(StringCache.defaultBucketCount);
|
||||
|
||||
// patchs the original request for the subsequent requests
|
||||
request.kind = RequestKind.symbolLocation;
|
||||
// patchs the original request for the subsequent requests
|
||||
request.kind = RequestKind.symbolLocation;
|
||||
|
||||
// getSymbolsForCompletion() copy to avoid repetitive parsing
|
||||
LexerConfig config;
|
||||
config.fileName = "";
|
||||
const(Token)[] tokenArray = getTokensForParser(cast(ubyte[]) request.sourceCode, config, &cache);
|
||||
SymbolStuff getSymbolsAtCursor(size_t cursorPosition)
|
||||
{
|
||||
auto sortedTokens = assumeSorted(tokenArray);
|
||||
auto beforeTokens = sortedTokens.lowerBound(cursorPosition);
|
||||
ScopeSymbolPair pair = generateAutocompleteTrees(tokenArray, allocator,
|
||||
&rba, request.cursorPosition, moduleCache);
|
||||
auto expression = getExpression(beforeTokens);
|
||||
return SymbolStuff(getSymbolsByTokenChain(pair.scope_, expression,
|
||||
cursorPosition, CompletionType.location), pair.symbol, pair.scope_);
|
||||
}
|
||||
// getSymbolsForCompletion() copy to avoid repetitive parsing
|
||||
LexerConfig config;
|
||||
config.fileName = "";
|
||||
const(Token)[] tokenArray = getTokensForParser(cast(ubyte[]) request.sourceCode,
|
||||
config, &cache);
|
||||
SymbolStuff getSymbolsAtCursor(size_t cursorPosition)
|
||||
{
|
||||
auto sortedTokens = assumeSorted(tokenArray);
|
||||
auto beforeTokens = sortedTokens.lowerBound(cursorPosition);
|
||||
ScopeSymbolPair pair = generateAutocompleteTrees(tokenArray, allocator,
|
||||
&rba, request.cursorPosition, moduleCache);
|
||||
auto expression = getExpression(beforeTokens);
|
||||
return SymbolStuff(getSymbolsByTokenChain(pair.scope_, expression,
|
||||
cursorPosition, CompletionType.location), pair.symbol, pair.scope_);
|
||||
}
|
||||
|
||||
// gets the symbol matching to cursor pos
|
||||
SymbolStuff stuff = getSymbolsAtCursor(cast(size_t) request.cursorPosition);
|
||||
scope (exit)
|
||||
stuff.destroy();
|
||||
// gets the symbol matching to cursor pos
|
||||
SymbolStuff stuff = getSymbolsAtCursor(cast(size_t)request.cursorPosition);
|
||||
scope(exit) stuff.destroy();
|
||||
|
||||
// starts searching only if no ambiguity with the symbol
|
||||
if (stuff.symbols.length == 1)
|
||||
{
|
||||
const(DSymbol*) sourceSymbol = stuff.symbols[0];
|
||||
response.symbolLocation = sourceSymbol.location;
|
||||
response.symbolFilePath = sourceSymbol.symbolFile.idup;
|
||||
// starts searching only if no ambiguity with the symbol
|
||||
if (stuff.symbols.length == 1)
|
||||
{
|
||||
const(DSymbol*) sourceSymbol = stuff.symbols[0];
|
||||
response.symbolLocation = sourceSymbol.location;
|
||||
response.symbolFilePath = sourceSymbol.symbolFile.idup;
|
||||
|
||||
// gets the source token to avoid too much getSymbolsAtCursor()
|
||||
const(Token)* sourceToken;
|
||||
foreach (i, t; tokenArray)
|
||||
{
|
||||
if (t.type != tok!"identifier")
|
||||
continue;
|
||||
if (request.cursorPosition >= t.index && request.cursorPosition < t.index + t.text.length)
|
||||
{
|
||||
sourceToken = tokenArray.ptr + i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// gets the source token to avoid too much getSymbolsAtCursor()
|
||||
const(Token)* sourceToken;
|
||||
foreach(i, t; tokenArray)
|
||||
{
|
||||
if (t.type != tok!"identifier")
|
||||
continue;
|
||||
if (request.cursorPosition >= t.index &&
|
||||
request.cursorPosition < t.index + t.text.length)
|
||||
{
|
||||
sourceToken = tokenArray.ptr + i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// finds the tokens that match to the source symbol
|
||||
if (sourceToken != null)
|
||||
foreach (t; tokenArray)
|
||||
{
|
||||
if (t.type == tok!"identifier" && t.text == sourceToken.text)
|
||||
{
|
||||
size_t pos = cast(size_t) t.index + 1; // place cursor inside the token
|
||||
SymbolStuff candidate = getSymbolsAtCursor(pos);
|
||||
scope (exit)
|
||||
candidate.destroy();
|
||||
if (candidate.symbols.length == 1 && candidate.symbols[0].location == sourceSymbol.location
|
||||
&& candidate.symbols[0].symbolFile == sourceSymbol.symbolFile)
|
||||
{
|
||||
response.locations ~= t.index;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
warning("The source token is not an identifier");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
warning("No or ambiguous symbol for the identifier at cursor");
|
||||
}
|
||||
return response;
|
||||
// finds the tokens that match to the source symbol
|
||||
if (sourceToken != null) foreach (t; tokenArray)
|
||||
{
|
||||
if (t.type == tok!"identifier" && t.text == sourceToken.text)
|
||||
{
|
||||
size_t pos = cast(size_t) t.index + 1; // place cursor inside the token
|
||||
SymbolStuff candidate = getSymbolsAtCursor(pos);
|
||||
scope(exit) candidate.destroy();
|
||||
if (candidate.symbols.length == 1 &&
|
||||
candidate.symbols[0].location == sourceSymbol.location &&
|
||||
candidate.symbols[0].symbolFile == sourceSymbol.symbolFile)
|
||||
{
|
||||
response.locations ~= t.index;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
warning("The source token is not an identifier");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
warning("No or ambiguous symbol for the identifier at cursor");
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
|
|
@ -43,16 +43,15 @@ import containers.hashset;
|
|||
* the autocompletion response
|
||||
*/
|
||||
public AutocompleteResponse findDeclaration(const AutocompleteRequest request,
|
||||
ref ModuleCache moduleCache)
|
||||
ref ModuleCache moduleCache)
|
||||
{
|
||||
AutocompleteResponse response;
|
||||
RollbackAllocator rba;
|
||||
auto allocator = scoped!(ASTAllocator)();
|
||||
auto cache = StringCache(StringCache.defaultBucketCount);
|
||||
SymbolStuff stuff = getSymbolsForCompletion(request, CompletionType.location,
|
||||
allocator, &rba, cache, moduleCache);
|
||||
scope (exit)
|
||||
stuff.destroy();
|
||||
SymbolStuff stuff = getSymbolsForCompletion(request,
|
||||
CompletionType.location, allocator, &rba, cache, moduleCache);
|
||||
scope(exit) stuff.destroy();
|
||||
if (stuff.symbols.length > 0)
|
||||
{
|
||||
response.symbolLocation = stuff.symbols[0].location;
|
||||
|
@ -67,20 +66,20 @@ public AutocompleteResponse findDeclaration(const AutocompleteRequest request,
|
|||
*
|
||||
*/
|
||||
public AutocompleteResponse symbolSearch(const AutocompleteRequest request,
|
||||
ref ModuleCache moduleCache)
|
||||
ref ModuleCache moduleCache)
|
||||
{
|
||||
import containers.ttree : TTree;
|
||||
|
||||
LexerConfig config;
|
||||
config.fileName = "";
|
||||
auto cache = StringCache(StringCache.defaultBucketCount);
|
||||
const(Token)[] tokenArray = getTokensForParser(cast(ubyte[]) request.sourceCode, config, &cache);
|
||||
const(Token)[] tokenArray = getTokensForParser(cast(ubyte[]) request.sourceCode,
|
||||
config, &cache);
|
||||
auto allocator = scoped!(ASTAllocator)();
|
||||
RollbackAllocator rba;
|
||||
ScopeSymbolPair pair = generateAutocompleteTrees(tokenArray, allocator,
|
||||
&rba, request.cursorPosition, moduleCache);
|
||||
scope (exit)
|
||||
pair.destroy();
|
||||
&rba, request.cursorPosition, moduleCache);
|
||||
scope(exit) pair.destroy();
|
||||
|
||||
static struct SearchResults
|
||||
{
|
||||
|
|
|
@ -147,15 +147,36 @@ SymbolStuff getSymbolsForCompletion(const AutocompleteRequest request,
|
|||
request.cursorPosition, type), pair.symbol, pair.scope_);
|
||||
}
|
||||
|
||||
static void skip(alias O, alias C, T)(T t, ref size_t i)
|
||||
{
|
||||
int depth = 1;
|
||||
while (i < t.length) switch (t[i].type)
|
||||
{
|
||||
case O:
|
||||
i++;
|
||||
depth++;
|
||||
break;
|
||||
case C:
|
||||
i++;
|
||||
depth--;
|
||||
if (depth <= 0)
|
||||
return;
|
||||
break;
|
||||
default:
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool isSliceExpression(T)(T tokens, size_t index)
|
||||
{
|
||||
while (index < tokens.length) switch (tokens[index].type)
|
||||
{
|
||||
case tok!"[":
|
||||
tokens.skipParen(index, tok!"[", tok!"]");
|
||||
skip!(tok!"[", tok!"]")(tokens, index);
|
||||
break;
|
||||
case tok!"(":
|
||||
tokens.skipParen(index, tok!"(", tok!")");
|
||||
skip!(tok!"(", tok!")")(tokens, index);
|
||||
break;
|
||||
case tok!"]":
|
||||
case tok!"}":
|
||||
|
@ -179,6 +200,22 @@ DSymbol*[] getSymbolsByTokenChain(T)(Scope* completionScope,
|
|||
//dumpTokens(tokens.release);
|
||||
//writeln(">>>");
|
||||
|
||||
static size_t skipEnd(T tokenSlice, size_t i, IdType open, IdType close)
|
||||
{
|
||||
size_t j = i + 1;
|
||||
for (int depth = 1; depth > 0 && j < tokenSlice.length; j++)
|
||||
{
|
||||
if (tokenSlice[j].type == open)
|
||||
depth++;
|
||||
else if (tokenSlice[j].type == close)
|
||||
{
|
||||
depth--;
|
||||
if (depth == 0) break;
|
||||
}
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
// Find the symbol corresponding to the beginning of the chain
|
||||
DSymbol*[] symbols;
|
||||
if (tokens.length == 0)
|
||||
|
@ -187,8 +224,7 @@ DSymbol*[] getSymbolsByTokenChain(T)(Scope* completionScope,
|
|||
// e.g. (a.b!c).d
|
||||
if (tokens[0] == tok!"(")
|
||||
{
|
||||
size_t j = 0;
|
||||
tokens.skipParen(j, tok!"(", tok!")");
|
||||
immutable j = skipEnd(tokens, 0, tok!"(", tok!")");
|
||||
symbols = getSymbolsByTokenChain(completionScope, tokens[1 .. j],
|
||||
cursorPosition, completionType);
|
||||
tokens = tokens[j + 1 .. $];
|
||||
|
@ -271,7 +307,7 @@ DSymbol*[] getSymbolsByTokenChain(T)(Scope* completionScope,
|
|||
{
|
||||
void skip(IdType open, IdType close)
|
||||
{
|
||||
tokens.skipParen(i, open, close);
|
||||
i = skipEnd(tokens, i, open, close);
|
||||
}
|
||||
|
||||
switch (tokens[i].type)
|
||||
|
@ -402,7 +438,7 @@ DSymbol*[] getSymbolsByTokenChain(T)(Scope* completionScope,
|
|||
return symbols;
|
||||
}
|
||||
|
||||
enum TYPE_IDENT_CASES = q{
|
||||
private enum TYPE_IDENT_AND_LITERAL_CASES = q{
|
||||
case tok!"int":
|
||||
case tok!"uint":
|
||||
case tok!"long":
|
||||
|
@ -429,16 +465,11 @@ enum TYPE_IDENT_CASES = q{
|
|||
case tok!"this":
|
||||
case tok!"super":
|
||||
case tok!"identifier":
|
||||
};
|
||||
|
||||
enum STRING_LITERAL_CASES = q{
|
||||
case tok!"stringLiteral":
|
||||
case tok!"wstringLiteral":
|
||||
case tok!"dstringLiteral":
|
||||
};
|
||||
|
||||
enum TYPE_IDENT_AND_LITERAL_CASES = TYPE_IDENT_CASES ~ STRING_LITERAL_CASES;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
@ -485,7 +516,18 @@ T getExpression(T)(T beforeTokens)
|
|||
skip:
|
||||
mixin (EXPRESSION_LOOP_BREAK);
|
||||
immutable bookmark = i;
|
||||
beforeTokens.skipParenReverse(i, open, close);
|
||||
int depth = 1;
|
||||
do
|
||||
{
|
||||
if (depth == 0 || i == 0)
|
||||
break;
|
||||
else
|
||||
i--;
|
||||
if (beforeTokens[i].type == open)
|
||||
depth++;
|
||||
else if (beforeTokens[i].type == close)
|
||||
depth--;
|
||||
} while (true);
|
||||
|
||||
skipCount++;
|
||||
|
||||
|
@ -532,7 +574,7 @@ T getExpression(T)(T beforeTokens)
|
|||
*/
|
||||
ImportKind determineImportKind(T)(T tokens)
|
||||
{
|
||||
assert(tokens.length > 1);
|
||||
assert (tokens.length > 1);
|
||||
size_t i = tokens.length - 1;
|
||||
if (!(tokens[i] == tok!":" || tokens[i] == tok!"," || tokens[i] == tok!"."
|
||||
|| tokens[i] == tok!"identifier"))
|
||||
|
@ -580,7 +622,7 @@ bool isUdaExpression(T)(ref T tokens)
|
|||
{
|
||||
bool result;
|
||||
ptrdiff_t skip;
|
||||
auto i = cast(ptrdiff_t) tokens.length - 2;
|
||||
ptrdiff_t i = tokens.length - 2;
|
||||
|
||||
if (i < 1)
|
||||
return result;
|
||||
|
@ -588,13 +630,13 @@ bool isUdaExpression(T)(ref T tokens)
|
|||
// skips the UDA ctor
|
||||
if (tokens[i].type == tok!")")
|
||||
{
|
||||
skip++;
|
||||
i--;
|
||||
++skip;
|
||||
--i;
|
||||
while (i >= 2)
|
||||
{
|
||||
skip += tokens[i].type == tok!")";
|
||||
skip -= tokens[i].type == tok!"(";
|
||||
i--;
|
||||
--i;
|
||||
if (skip == 0)
|
||||
{
|
||||
// @UDA!(TemplateParameters)(FunctionParameters)
|
||||
|
@ -613,20 +655,20 @@ bool isUdaExpression(T)(ref T tokens)
|
|||
{
|
||||
// @UDA!SingleTemplateParameter
|
||||
if (i > 2 && tokens[i].type == tok!"identifier" && tokens[i-1].type == tok!"!")
|
||||
{
|
||||
i -= 2;
|
||||
}
|
||||
|
||||
// @UDA
|
||||
if (i > 0 && tokens[i].type == tok!"identifier" && tokens[i-1].type == tok!"@")
|
||||
{
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverses a token slice in reverse to find the opening parentheses or square bracket
|
||||
* that begins the block the last token is in.
|
||||
*/
|
||||
size_t goBackToOpenParen(T)(T beforeTokens)
|
||||
in
|
||||
{
|
||||
|
@ -635,7 +677,8 @@ in
|
|||
body
|
||||
{
|
||||
size_t i = beforeTokens.length - 1;
|
||||
|
||||
IdType open;
|
||||
IdType close;
|
||||
while (true) switch (beforeTokens[i].type)
|
||||
{
|
||||
case tok!",":
|
||||
|
@ -663,69 +706,36 @@ body
|
|||
case tok!"[":
|
||||
return i + 1;
|
||||
case tok!")":
|
||||
beforeTokens.skipParenReverse!true(i, tok!")", tok!"(");
|
||||
break;
|
||||
open = tok!")";
|
||||
close = tok!"(";
|
||||
goto skip;
|
||||
case tok!"}":
|
||||
beforeTokens.skipParenReverse!true(i, tok!"}", tok!"{");
|
||||
break;
|
||||
open = tok!"}";
|
||||
close = tok!"{";
|
||||
goto skip;
|
||||
case tok!"]":
|
||||
beforeTokens.skipParenReverse!true(i, tok!"]", tok!"[");
|
||||
open = tok!"]";
|
||||
close = tok!"[";
|
||||
skip:
|
||||
if (i == 0)
|
||||
return size_t.max;
|
||||
else
|
||||
i--;
|
||||
int depth = 1;
|
||||
do
|
||||
{
|
||||
if (depth == 0 || i == 0)
|
||||
break;
|
||||
else
|
||||
i--;
|
||||
if (beforeTokens[i].type == open)
|
||||
depth++;
|
||||
else if (beforeTokens[i].type == close)
|
||||
depth--;
|
||||
} while (true);
|
||||
break;
|
||||
default:
|
||||
return size_t.max;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips blocks of parentheses until the starting block has been closed
|
||||
*/
|
||||
void skipParen(T)(T tokenSlice, ref size_t i, IdType open, IdType close)
|
||||
{
|
||||
if (i >= tokenSlice.length || tokenSlice.length <= 0)
|
||||
return;
|
||||
int depth = 1;
|
||||
while (depth != 0 && i + 1 != tokenSlice.length)
|
||||
{
|
||||
i++;
|
||||
if (tokenSlice[i].type == open)
|
||||
depth++;
|
||||
else if (tokenSlice[i].type == close)
|
||||
depth--;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips blocks of parentheses in reverse until the starting block has been opened
|
||||
*/
|
||||
void skipParenReverse(bool before = false, T)(T beforeTokens, ref size_t i, IdType open, IdType close)
|
||||
{
|
||||
if (i == 0)
|
||||
return;
|
||||
int depth = 1;
|
||||
while (depth != 0 && i != 0)
|
||||
{
|
||||
i--;
|
||||
if (beforeTokens[i].type == open)
|
||||
depth++;
|
||||
else if (beforeTokens[i].type == close)
|
||||
depth--;
|
||||
}
|
||||
static if (before)
|
||||
if (i != 0)
|
||||
i--;
|
||||
}
|
||||
|
||||
///
|
||||
unittest
|
||||
{
|
||||
Token[] t = [
|
||||
Token(tok!"identifier"), Token(tok!"identifier"), Token(tok!"("),
|
||||
Token(tok!"identifier"), Token(tok!"("), Token(tok!")"), Token(tok!",")
|
||||
];
|
||||
size_t i = t.length - 1;
|
||||
skipParenReverse!false(t, i, tok!")", tok!"(");
|
||||
assert(i == 2);
|
||||
i = t.length - 1;
|
||||
skipParenReverse!true(t, i, tok!")", tok!"(");
|
||||
assert(i == 1);
|
||||
return size_t.max;
|
||||
}
|
||||
|
|
|
@ -113,7 +113,7 @@ int main(string[] args)
|
|||
return 1;
|
||||
}
|
||||
|
||||
if (serverIsRunning(useTCP, socketFile, port))
|
||||
if (serverIsRunning(useTCP, socketFile, port))
|
||||
{
|
||||
fatal("Another instance of DCD-server is already running");
|
||||
return 1;
|
||||
|
@ -211,8 +211,7 @@ int main(string[] args)
|
|||
s.shutdown(SocketShutdown.BOTH);
|
||||
s.close();
|
||||
}
|
||||
|
||||
auto bytesReceived = s.receive(buffer);
|
||||
ptrdiff_t bytesReceived = s.receive(buffer);
|
||||
|
||||
auto requestWatch = StopWatch(AutoStart.yes);
|
||||
|
||||
|
@ -238,7 +237,6 @@ int main(string[] args)
|
|||
|
||||
AutocompleteRequest request;
|
||||
msgpack.unpack(buffer[size_t.sizeof .. bytesReceived], request);
|
||||
|
||||
if (request.kind & RequestKind.clearCache)
|
||||
{
|
||||
info("Clearing cache.");
|
||||
|
@ -251,39 +249,77 @@ int main(string[] args)
|
|||
}
|
||||
else if (request.kind & RequestKind.query)
|
||||
{
|
||||
s.sendResponse(AutocompleteResponse.ack);
|
||||
AutocompleteResponse response;
|
||||
response.completionType = "ack";
|
||||
ubyte[] responseBytes = msgpack.pack(response);
|
||||
s.send(responseBytes);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (request.kind & RequestKind.addImport)
|
||||
{
|
||||
cache.addImportPaths(request.importPaths);
|
||||
}
|
||||
|
||||
if (request.kind & RequestKind.listImports)
|
||||
{
|
||||
AutocompleteResponse response;
|
||||
response.importPaths = cache.getImportPaths().map!(a => cast() a).array();
|
||||
ubyte[] responseBytes = msgpack.pack(response);
|
||||
info("Returning import path list");
|
||||
s.sendResponse(response);
|
||||
s.send(responseBytes);
|
||||
}
|
||||
else if (request.kind & RequestKind.autocomplete)
|
||||
{
|
||||
info("Getting completions");
|
||||
s.sendResponse(complete(request, cache));
|
||||
AutocompleteResponse response = complete(request, cache);
|
||||
ubyte[] responseBytes = msgpack.pack(response);
|
||||
s.send(responseBytes);
|
||||
}
|
||||
else if (request.kind & RequestKind.doc)
|
||||
{
|
||||
info("Getting doc comment");
|
||||
s.trySendResponse(getDoc(request, cache), "Could not get DDoc information");
|
||||
try
|
||||
{
|
||||
AutocompleteResponse response = getDoc(request, cache);
|
||||
ubyte[] responseBytes = msgpack.pack(response);
|
||||
s.send(responseBytes);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
warning("Could not get DDoc information", e.msg);
|
||||
}
|
||||
}
|
||||
else if (request.kind & RequestKind.symbolLocation)
|
||||
s.trySendResponse(findDeclaration(request, cache), "Could not get symbol location");
|
||||
{
|
||||
try
|
||||
{
|
||||
AutocompleteResponse response = findDeclaration(request, cache);
|
||||
ubyte[] responseBytes = msgpack.pack(response);
|
||||
s.send(responseBytes);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
warning("Could not get symbol location", e.msg);
|
||||
}
|
||||
}
|
||||
else if (request.kind & RequestKind.search)
|
||||
s.sendResponse(symbolSearch(request, cache));
|
||||
else if (request.kind & RequestKind.localUse)
|
||||
s.trySendResponse(findLocalUse(request, cache), "Could not find local usage");
|
||||
|
||||
{
|
||||
AutocompleteResponse response = symbolSearch(request, cache);
|
||||
ubyte[] responseBytes = msgpack.pack(response);
|
||||
s.send(responseBytes);
|
||||
}
|
||||
else if (request.kind & RequestKind.localUse)
|
||||
{
|
||||
try
|
||||
{
|
||||
AutocompleteResponse response = findLocalUse(request, cache);
|
||||
ubyte[] responseBytes = msgpack.pack(response);
|
||||
s.send(responseBytes);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
warning("Could not find local usage", e.msg);
|
||||
}
|
||||
}
|
||||
info("Request processed in ", requestWatch.peek().to!("msecs", float), " milliseconds");
|
||||
}
|
||||
return 0;
|
||||
|
@ -298,24 +334,16 @@ union IPv4Union
|
|||
uint i;
|
||||
}
|
||||
|
||||
/// Lazily evaluates a response with an exception handler and sends it to a socket or logs msg if evaluating response fails.
|
||||
void trySendResponse(Socket socket, lazy AutocompleteResponse response, string msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
sendResponse(socket, response);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
warningf("%s: %s", msg, e.msg);
|
||||
}
|
||||
}
|
||||
import std.regex : ctRegex;
|
||||
alias envVarRegex = ctRegex!(`\$\{([_a-zA-Z][_a-zA-Z 0-9]*)\}`);
|
||||
|
||||
/// Packs an AutocompleteResponse and sends it to a socket.
|
||||
void sendResponse(Socket socket, AutocompleteResponse response)
|
||||
private unittest
|
||||
{
|
||||
ubyte[] responseBytes = msgpack.pack(response);
|
||||
socket.send(responseBytes);
|
||||
import std.regex : replaceAll;
|
||||
|
||||
enum input = `${HOME}/aaa/${_bb_b}/ccc`;
|
||||
|
||||
assert(replaceAll!(m => m[1])(input, envVarRegex) == `HOME/aaa/_bb_b/ccc`);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue