Only split server code, no changes

This commit is contained in:
WebFreak001 2017-08-16 07:39:08 +02:00
parent cd4a027eac
commit 6c310d2d5e
5 changed files with 342 additions and 270 deletions

View File

@ -50,21 +50,26 @@ import common.messages;
* Returns: * Returns:
* the autocompletion response * the autocompletion response
*/ */
public AutocompleteResponse complete(const AutocompleteRequest request, ref ModuleCache moduleCache) public AutocompleteResponse complete(const AutocompleteRequest request,
ref ModuleCache moduleCache)
{ {
const(Token)[] tokenArray; const(Token)[] tokenArray;
auto stringCache = StringCache(StringCache.defaultBucketCount); auto stringCache = StringCache(StringCache.defaultBucketCount);
auto beforeTokens = getTokensBeforeCursor(request.sourceCode, auto beforeTokens = getTokensBeforeCursor(request.sourceCode,
request.cursorPosition, stringCache, tokenArray); request.cursorPosition, stringCache, tokenArray);
if (beforeTokens.length >= 2) if (beforeTokens.length >= 2)
{ {
if (beforeTokens[$ - 1] == tok!"(" || beforeTokens[$ - 1] == tok!"[" if (beforeTokens[$ - 1] == tok!"(" || beforeTokens[$ - 1] == tok!"[")
|| beforeTokens[$ - 1] == tok!",") {
return parenCompletion(beforeTokens, tokenArray, request.cursorPosition,
moduleCache);
}
else if (beforeTokens[$ - 1] == tok!",")
{ {
immutable size_t end = goBackToOpenParen(beforeTokens); immutable size_t end = goBackToOpenParen(beforeTokens);
if (end != size_t.max) if (end != size_t.max)
return parenCompletion(beforeTokens[0 .. end], tokenArray, return parenCompletion(beforeTokens[0 .. end], tokenArray,
request.cursorPosition, moduleCache); request.cursorPosition, moduleCache);
} }
else else
{ {
@ -72,9 +77,10 @@ public AutocompleteResponse complete(const AutocompleteRequest request, ref Modu
if (kind == ImportKind.neither) if (kind == ImportKind.neither)
{ {
if (beforeTokens.isUdaExpression) if (beforeTokens.isUdaExpression)
beforeTokens = beforeTokens[$ - 1 .. $]; beforeTokens = beforeTokens[$-1 .. $];
return dotCompletion(beforeTokens, tokenArray, request.cursorPosition, moduleCache); return dotCompletion(beforeTokens, tokenArray, request.cursorPosition,
} moduleCache);
}
else else
return importCompletion(beforeTokens, kind, moduleCache); return importCompletion(beforeTokens, kind, moduleCache);
} }
@ -92,7 +98,7 @@ public AutocompleteResponse complete(const AutocompleteRequest request, ref Modu
* the autocompletion response * the autocompletion response
*/ */
AutocompleteResponse dotCompletion(T)(T beforeTokens, const(Token)[] tokenArray, AutocompleteResponse dotCompletion(T)(T beforeTokens, const(Token)[] tokenArray,
size_t cursorPosition, ref ModuleCache moduleCache) size_t cursorPosition, ref ModuleCache moduleCache)
{ {
AutocompleteResponse response; AutocompleteResponse response;
@ -116,14 +122,16 @@ AutocompleteResponse dotCompletion(T)(T beforeTokens, const(Token)[] tokenArray,
significantTokenType = tok!"identifier"; significantTokenType = tok!"identifier";
beforeTokens = beforeTokens[0 .. $ - 1]; beforeTokens = beforeTokens[0 .. $ - 1];
} }
else if (beforeTokens.length >= 2 && beforeTokens[$ - 1] == tok!".") else if (beforeTokens.length >= 2 && beforeTokens[$ - 1] == tok!".")
significantTokenType = beforeTokens[$ - 2].type; significantTokenType = beforeTokens[$ - 2].type;
else else
return response; return response;
switch (significantTokenType) switch (significantTokenType)
{ {
mixin(STRING_LITERAL_CASES); case tok!"stringLiteral":
case tok!"wstringLiteral":
case tok!"dstringLiteral":
foreach (symbol; arraySymbols) foreach (symbol; arraySymbols)
{ {
response.completionKinds ~= symbol.kind; response.completionKinds ~= symbol.kind;
@ -131,17 +139,41 @@ AutocompleteResponse dotCompletion(T)(T beforeTokens, const(Token)[] tokenArray,
} }
response.completionType = CompletionType.identifiers; response.completionType = CompletionType.identifiers;
break; 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!"]": case tok!"]":
case tok!"this":
case tok!"super":
auto allocator = scoped!(ASTAllocator)(); auto allocator = scoped!(ASTAllocator)();
RollbackAllocator rba; RollbackAllocator rba;
ScopeSymbolPair pair = generateAutocompleteTrees(tokenArray, allocator, ScopeSymbolPair pair = generateAutocompleteTrees(tokenArray, allocator,
&rba, cursorPosition, moduleCache); &rba, cursorPosition, moduleCache);
scope (exit) scope(exit) pair.destroy();
pair.destroy();
response.setCompletions(pair.scope_, getExpression(beforeTokens), response.setCompletions(pair.scope_, getExpression(beforeTokens),
cursorPosition, CompletionType.identifiers, false, partial); cursorPosition, CompletionType.identifiers, false, partial);
break; break;
case tok!"(": case tok!"(":
case tok!"{": case tok!"{":
@ -165,7 +197,7 @@ AutocompleteResponse dotCompletion(T)(T beforeTokens, const(Token)[] tokenArray,
* the autocompletion response * the autocompletion response
*/ */
AutocompleteResponse parenCompletion(T)(T beforeTokens, 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; AutocompleteResponse response;
immutable(string)[] completions; immutable(string)[] completions;
@ -195,6 +227,7 @@ AutocompleteResponse parenCompletion(T)(T beforeTokens,
break; break;
case tok!"characterLiteral": case tok!"characterLiteral":
case tok!"doubleLiteral": case tok!"doubleLiteral":
case tok!"dstringLiteral":
case tok!"floatLiteral": case tok!"floatLiteral":
case tok!"identifier": case tok!"identifier":
case tok!"idoubleLiteral": case tok!"idoubleLiteral":
@ -203,22 +236,22 @@ AutocompleteResponse parenCompletion(T)(T beforeTokens,
case tok!"irealLiteral": case tok!"irealLiteral":
case tok!"longLiteral": case tok!"longLiteral":
case tok!"realLiteral": case tok!"realLiteral":
case tok!"stringLiteral":
case tok!"uintLiteral": case tok!"uintLiteral":
case tok!"ulongLiteral": case tok!"ulongLiteral":
case tok!"wstringLiteral":
case tok!"this": case tok!"this":
case tok!"super": case tok!"super":
case tok!")": case tok!")":
case tok!"]": case tok!"]":
mixin(STRING_LITERAL_CASES);
auto allocator = scoped!(ASTAllocator)(); auto allocator = scoped!(ASTAllocator)();
RollbackAllocator rba; RollbackAllocator rba;
ScopeSymbolPair pair = generateAutocompleteTrees(tokenArray, allocator, ScopeSymbolPair pair = generateAutocompleteTrees(tokenArray, allocator,
&rba, cursorPosition, moduleCache); &rba, cursorPosition, moduleCache);
scope (exit) scope(exit) pair.destroy();
pair.destroy();
auto expression = getExpression(beforeTokens[0 .. $ - 1]); auto expression = getExpression(beforeTokens[0 .. $ - 1]);
response.setCompletions(pair.scope_, expression, cursorPosition, response.setCompletions(pair.scope_, expression,
CompletionType.calltips, beforeTokens[$ - 1] == tok!"["); cursorPosition, CompletionType.calltips, beforeTokens[$ - 1] == tok!"[");
break; break;
default: default:
break; break;
@ -233,10 +266,10 @@ AutocompleteResponse parenCompletion(T)(T beforeTokens,
* --- * ---
*/ */
AutocompleteResponse importCompletion(T)(T beforeTokens, ImportKind kind, AutocompleteResponse importCompletion(T)(T beforeTokens, ImportKind kind,
ref ModuleCache moduleCache) ref ModuleCache moduleCache)
in in
{ {
assert(beforeTokens.length >= 2); assert (beforeTokens.length >= 2);
} }
body body
{ {
@ -249,8 +282,8 @@ body
if (kind == ImportKind.normal) if (kind == ImportKind.normal)
{ {
while (beforeTokens[i].type != tok!"," while (beforeTokens[i].type != tok!"," && beforeTokens[i].type != tok!"import"
&& beforeTokens[i].type != tok!"import" && beforeTokens[i].type != tok!"=") && beforeTokens[i].type != tok!"=" )
i--; i--;
setImportCompletions(beforeTokens[i .. $], response, moduleCache); setImportCompletions(beforeTokens[i .. $], response, moduleCache);
return response; return response;
@ -276,11 +309,8 @@ body
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)
{ {
case tok!":": case tok!":": break loop2;
break loop2; default: j++; break;
default:
j++;
break;
} }
if (i >= j) if (i >= j)
@ -289,8 +319,11 @@ body
return response; return response;
} }
immutable string path = beforeTokens[i + 1 .. j].filter!(token => token.type == tok!"identifier") immutable string path = beforeTokens[i + 1 .. j]
.map!(token => cast() token.text).joiner(dirSeparator).text(); .filter!(token => token.type == tok!"identifier")
.map!(token => cast() token.text)
.joiner(dirSeparator)
.text();
string resolvedLocation = moduleCache.resolveImportLocation(path); string resolvedLocation = moduleCache.resolveImportLocation(path);
if (resolvedLocation is null) if (resolvedLocation is null)
@ -301,15 +334,14 @@ body
auto symbols = moduleCache.getModuleSymbol(internString(resolvedLocation)); auto symbols = moduleCache.getModuleSymbol(internString(resolvedLocation));
import containers.hashset : HashSet; import containers.hashset : HashSet;
HashSet!string h; HashSet!string h;
void addSymbolToResponses(const(DSymbol)* sy) void addSymbolToResponses(const(DSymbol)* sy)
{ {
auto a = DSymbol(sy.name); auto a = DSymbol(sy.name);
if (!builtinSymbols.contains(&a) && sy.name !is null if (!builtinSymbols.contains(&a) && sy.name !is null && !h.contains(sy.name)
&& !h.contains(sy.name) && !sy.skipOver && !sy.skipOver && sy.name != CONSTRUCTOR_SYMBOL_NAME
&& sy.name != CONSTRUCTOR_SYMBOL_NAME && isPublicCompletionKind(sy.kind)) && isPublicCompletionKind(sy.kind))
{ {
response.completionKinds ~= sy.kind; response.completionKinds ~= sy.kind;
response.completions ~= sy.name; response.completions ~= sy.name;
@ -323,7 +355,7 @@ body
foreach (sy; s.type.opSlice().filter!(a => !a.skipOver)) foreach (sy; s.type.opSlice().filter!(a => !a.skipOver))
addSymbolToResponses(sy); addSymbolToResponses(sy);
else else
addSymbolToResponses(s); addSymbolToResponses(s);
} }
response.completionType = CompletionType.identifiers; response.completionType = CompletionType.identifiers;
return response; return response;
@ -335,7 +367,8 @@ body
* tokens = the tokens after the "import" keyword and before the cursor * tokens = the tokens after the "import" keyword and before the cursor
* response = the response that should be populated * 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; response.completionType = CompletionType.identifiers;
string partial = null; string partial = null;
@ -359,8 +392,8 @@ void setImportCompletions(T)(T tokens, ref AutocompleteResponse response, ref Mo
found = true; found = true;
auto n = importPath.baseName(".d").baseName(".di"); auto n = importPath.baseName(".d").baseName(".di");
if (isFile(importPath) && (importPath.endsWith(".d") if (isFile(importPath) && (importPath.endsWith(".d") || importPath.endsWith(".di"))
|| importPath.endsWith(".di")) && (partial is null || n.startsWith(partial))) && (partial is null || n.startsWith(partial)))
{ {
response.completions ~= n; response.completions ~= n;
response.completionKinds ~= CompletionKind.moduleName; response.completionKinds ~= CompletionKind.moduleName;
@ -374,32 +407,31 @@ void setImportCompletions(T)(T tokens, ref AutocompleteResponse response, ref Mo
found = true; found = true;
try try foreach (string name; dirEntries(p, SpanMode.shallow))
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; response.completions ~= n;
response.completionKinds ~= CompletionKind.moduleName;
if (name.baseName.startsWith(".#")) }
continue; else if (isDir(name))
{
auto n = name.baseName(".d").baseName(".di"); if (n[0] != '.' && (partial is null || n.startsWith(partial)))
if (isFile(name) && (name.endsWith(".d") || name.endsWith(".di"))
&& (partial is null || n.startsWith(partial)))
{ {
response.completions ~= n; response.completions ~= n;
response.completionKinds ~= CompletionKind.moduleName; response.completionKinds ~=
} exists(buildPath(name, "package.d")) || exists(buildPath(name, "package.di"))
else if (isDir(name)) ? CompletionKind.moduleName : CompletionKind.packageName;
{
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;
}
} }
} }
catch (FileException) }
catch(FileException)
{ {
warning("Cannot access import path: ", importPath); 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, void setCompletions(T)(ref AutocompleteResponse response,
size_t cursorPosition, CompletionType completionType, Scope* completionScope, T tokens, size_t cursorPosition,
bool isBracket = false, string partial = null) CompletionType completionType, bool isBracket = false, string partial = null)
{ {
static void addSymToResponse(const(DSymbol)* s, ref AutocompleteResponse r, static void addSymToResponse(const(DSymbol)* s, ref AutocompleteResponse r, string p,
string p, size_t[] circularGuard = []) size_t[] circularGuard = [])
{ {
if (circularGuard.canFind(cast(size_t) s)) if (circularGuard.canFind(cast(size_t) s))
return; return;
foreach (sym; s.opSlice()) foreach (sym; s.opSlice())
{ {
if (sym.name !is null && sym.name.length > 0 if (sym.name !is null && sym.name.length > 0 && isPublicCompletionKind(sym.kind)
&& isPublicCompletionKind(sym.kind) && (p is null ? true && (p is null ? true : toUpper(sym.name.data).startsWith(toUpper(p)))
: toUpper(sym.name.data).startsWith(toUpper(p))) && !r.completions.canFind(sym.name)
&& !r.completions.canFind(sym.name) && sym.name[0] != '*') && sym.name[0] != '*')
{ {
r.completionKinds ~= sym.kind; r.completionKinds ~= sym.kind;
r.completions ~= sym.name.dup; r.completions ~= sym.name.dup;
@ -455,7 +487,7 @@ void setCompletions(T)(ref AutocompleteResponse response, Scope* completionScope
return; return;
DSymbol*[] symbols = getSymbolsByTokenChain(completionScope, tokens, DSymbol*[] symbols = getSymbolsByTokenChain(completionScope, tokens,
cursorPosition, completionType); cursorPosition, completionType);
if (symbols.length == 0) if (symbols.length == 0)
return; return;
@ -467,7 +499,8 @@ void setCompletions(T)(ref AutocompleteResponse response, Scope* completionScope
|| symbols[0].kind == CompletionKind.importSymbol || symbols[0].kind == CompletionKind.importSymbol
|| symbols[0].kind == CompletionKind.aliasName) || 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) if (symbols.length == 0)
return; return;
} }
@ -477,7 +510,8 @@ void setCompletions(T)(ref AutocompleteResponse response, Scope* completionScope
else if (completionType == CompletionType.calltips) else if (completionType == CompletionType.calltips)
{ {
//trace("Showing call tips for ", symbols[0].name, " of kind ", symbols[0].kind); //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) 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 if (symbols[0].kind == CompletionKind.structName
== CompletionKind.className) || symbols[0].kind == CompletionKind.className)
{ {
auto constructor = symbols[0].getPartsByName(CONSTRUCTOR_SYMBOL_NAME); auto constructor = symbols[0].getPartsByName(CONSTRUCTOR_SYMBOL_NAME);
if (constructor.length == 0) if (constructor.length == 0)

View File

@ -40,81 +40,82 @@ import common.messages;
* Returns: * Returns:
* the autocompletion response. * the autocompletion response.
*/ */
public AutocompleteResponse findLocalUse(AutocompleteRequest request, ref ModuleCache moduleCache) public AutocompleteResponse findLocalUse(AutocompleteRequest request,
ref ModuleCache moduleCache)
{ {
AutocompleteResponse response; AutocompleteResponse response;
RollbackAllocator rba; RollbackAllocator rba;
auto allocator = scoped!(ASTAllocator)(); auto allocator = scoped!(ASTAllocator)();
auto cache = StringCache(StringCache.defaultBucketCount); auto cache = StringCache(StringCache.defaultBucketCount);
// patchs the original request for the subsequent requests // patchs the original request for the subsequent requests
request.kind = RequestKind.symbolLocation; request.kind = RequestKind.symbolLocation;
// getSymbolsForCompletion() copy to avoid repetitive parsing // getSymbolsForCompletion() copy to avoid repetitive parsing
LexerConfig config; LexerConfig config;
config.fileName = ""; config.fileName = "";
const(Token)[] tokenArray = getTokensForParser(cast(ubyte[]) request.sourceCode, config, &cache); const(Token)[] tokenArray = getTokensForParser(cast(ubyte[]) request.sourceCode,
SymbolStuff getSymbolsAtCursor(size_t cursorPosition) config, &cache);
{ SymbolStuff getSymbolsAtCursor(size_t cursorPosition)
auto sortedTokens = assumeSorted(tokenArray); {
auto beforeTokens = sortedTokens.lowerBound(cursorPosition); auto sortedTokens = assumeSorted(tokenArray);
ScopeSymbolPair pair = generateAutocompleteTrees(tokenArray, allocator, auto beforeTokens = sortedTokens.lowerBound(cursorPosition);
&rba, request.cursorPosition, moduleCache); ScopeSymbolPair pair = generateAutocompleteTrees(tokenArray, allocator,
auto expression = getExpression(beforeTokens); &rba, request.cursorPosition, moduleCache);
return SymbolStuff(getSymbolsByTokenChain(pair.scope_, expression, auto expression = getExpression(beforeTokens);
cursorPosition, CompletionType.location), pair.symbol, pair.scope_); return SymbolStuff(getSymbolsByTokenChain(pair.scope_, expression,
} cursorPosition, CompletionType.location), pair.symbol, pair.scope_);
}
// gets the symbol matching to cursor pos // gets the symbol matching to cursor pos
SymbolStuff stuff = getSymbolsAtCursor(cast(size_t) request.cursorPosition); SymbolStuff stuff = getSymbolsAtCursor(cast(size_t)request.cursorPosition);
scope (exit) scope(exit) stuff.destroy();
stuff.destroy();
// starts searching only if no ambiguity with the symbol // starts searching only if no ambiguity with the symbol
if (stuff.symbols.length == 1) if (stuff.symbols.length == 1)
{ {
const(DSymbol*) sourceSymbol = stuff.symbols[0]; const(DSymbol*) sourceSymbol = stuff.symbols[0];
response.symbolLocation = sourceSymbol.location; response.symbolLocation = sourceSymbol.location;
response.symbolFilePath = sourceSymbol.symbolFile.idup; response.symbolFilePath = sourceSymbol.symbolFile.idup;
// gets the source token to avoid too much getSymbolsAtCursor() // gets the source token to avoid too much getSymbolsAtCursor()
const(Token)* sourceToken; const(Token)* sourceToken;
foreach (i, t; tokenArray) foreach(i, t; tokenArray)
{ {
if (t.type != tok!"identifier") if (t.type != tok!"identifier")
continue; continue;
if (request.cursorPosition >= t.index && request.cursorPosition < t.index + t.text.length) if (request.cursorPosition >= t.index &&
{ request.cursorPosition < t.index + t.text.length)
sourceToken = tokenArray.ptr + i; {
break; sourceToken = tokenArray.ptr + i;
} break;
} }
}
// finds the tokens that match to the source symbol // finds the tokens that match to the source symbol
if (sourceToken != null) if (sourceToken != null) foreach (t; tokenArray)
foreach (t; tokenArray) {
{ if (t.type == tok!"identifier" && t.text == sourceToken.text)
if (t.type == tok!"identifier" && t.text == sourceToken.text) {
{ size_t pos = cast(size_t) t.index + 1; // place cursor inside the token
size_t pos = cast(size_t) t.index + 1; // place cursor inside the token SymbolStuff candidate = getSymbolsAtCursor(pos);
SymbolStuff candidate = getSymbolsAtCursor(pos); scope(exit) candidate.destroy();
scope (exit) if (candidate.symbols.length == 1 &&
candidate.destroy(); candidate.symbols[0].location == sourceSymbol.location &&
if (candidate.symbols.length == 1 && candidate.symbols[0].location == sourceSymbol.location candidate.symbols[0].symbolFile == sourceSymbol.symbolFile)
&& candidate.symbols[0].symbolFile == sourceSymbol.symbolFile) {
{ response.locations ~= t.index;
response.locations ~= t.index; }
} }
} }
} else
else {
{ warning("The source token is not an identifier");
warning("The source token is not an identifier"); }
} }
} else
else {
{ warning("No or ambiguous symbol for the identifier at cursor");
warning("No or ambiguous symbol for the identifier at cursor"); }
} return response;
return response;
} }

View File

@ -43,16 +43,15 @@ import containers.hashset;
* the autocompletion response * the autocompletion response
*/ */
public AutocompleteResponse findDeclaration(const AutocompleteRequest request, public AutocompleteResponse findDeclaration(const AutocompleteRequest request,
ref ModuleCache moduleCache) ref ModuleCache moduleCache)
{ {
AutocompleteResponse response; AutocompleteResponse response;
RollbackAllocator rba; RollbackAllocator rba;
auto allocator = scoped!(ASTAllocator)(); auto allocator = scoped!(ASTAllocator)();
auto cache = StringCache(StringCache.defaultBucketCount); auto cache = StringCache(StringCache.defaultBucketCount);
SymbolStuff stuff = getSymbolsForCompletion(request, CompletionType.location, SymbolStuff stuff = getSymbolsForCompletion(request,
allocator, &rba, cache, moduleCache); CompletionType.location, allocator, &rba, cache, moduleCache);
scope (exit) scope(exit) stuff.destroy();
stuff.destroy();
if (stuff.symbols.length > 0) if (stuff.symbols.length > 0)
{ {
response.symbolLocation = stuff.symbols[0].location; response.symbolLocation = stuff.symbols[0].location;
@ -67,20 +66,20 @@ public AutocompleteResponse findDeclaration(const AutocompleteRequest request,
* *
*/ */
public AutocompleteResponse symbolSearch(const AutocompleteRequest request, public AutocompleteResponse symbolSearch(const AutocompleteRequest request,
ref ModuleCache moduleCache) ref ModuleCache moduleCache)
{ {
import containers.ttree : TTree; import containers.ttree : TTree;
LexerConfig config; LexerConfig config;
config.fileName = ""; config.fileName = "";
auto cache = StringCache(StringCache.defaultBucketCount); 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)(); auto allocator = scoped!(ASTAllocator)();
RollbackAllocator rba; RollbackAllocator rba;
ScopeSymbolPair pair = generateAutocompleteTrees(tokenArray, allocator, ScopeSymbolPair pair = generateAutocompleteTrees(tokenArray, allocator,
&rba, request.cursorPosition, moduleCache); &rba, request.cursorPosition, moduleCache);
scope (exit) scope(exit) pair.destroy();
pair.destroy();
static struct SearchResults static struct SearchResults
{ {

View File

@ -147,15 +147,36 @@ SymbolStuff getSymbolsForCompletion(const AutocompleteRequest request,
request.cursorPosition, type), pair.symbol, pair.scope_); 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) bool isSliceExpression(T)(T tokens, size_t index)
{ {
while (index < tokens.length) switch (tokens[index].type) while (index < tokens.length) switch (tokens[index].type)
{ {
case tok!"[": case tok!"[":
tokens.skipParen(index, tok!"[", tok!"]"); skip!(tok!"[", tok!"]")(tokens, index);
break; break;
case tok!"(": case tok!"(":
tokens.skipParen(index, tok!"(", tok!")"); skip!(tok!"(", tok!")")(tokens, index);
break; break;
case tok!"]": case tok!"]":
case tok!"}": case tok!"}":
@ -179,6 +200,22 @@ DSymbol*[] getSymbolsByTokenChain(T)(Scope* completionScope,
//dumpTokens(tokens.release); //dumpTokens(tokens.release);
//writeln(">>>"); //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 // Find the symbol corresponding to the beginning of the chain
DSymbol*[] symbols; DSymbol*[] symbols;
if (tokens.length == 0) if (tokens.length == 0)
@ -187,8 +224,7 @@ DSymbol*[] getSymbolsByTokenChain(T)(Scope* completionScope,
// e.g. (a.b!c).d // e.g. (a.b!c).d
if (tokens[0] == tok!"(") if (tokens[0] == tok!"(")
{ {
size_t j = 0; immutable j = skipEnd(tokens, 0, tok!"(", tok!")");
tokens.skipParen(j, tok!"(", tok!")");
symbols = getSymbolsByTokenChain(completionScope, tokens[1 .. j], symbols = getSymbolsByTokenChain(completionScope, tokens[1 .. j],
cursorPosition, completionType); cursorPosition, completionType);
tokens = tokens[j + 1 .. $]; tokens = tokens[j + 1 .. $];
@ -271,7 +307,7 @@ DSymbol*[] getSymbolsByTokenChain(T)(Scope* completionScope,
{ {
void skip(IdType open, IdType close) void skip(IdType open, IdType close)
{ {
tokens.skipParen(i, open, close); i = skipEnd(tokens, i, open, close);
} }
switch (tokens[i].type) switch (tokens[i].type)
@ -402,7 +438,7 @@ DSymbol*[] getSymbolsByTokenChain(T)(Scope* completionScope,
return symbols; return symbols;
} }
enum TYPE_IDENT_CASES = q{ private enum TYPE_IDENT_AND_LITERAL_CASES = q{
case tok!"int": case tok!"int":
case tok!"uint": case tok!"uint":
case tok!"long": case tok!"long":
@ -429,16 +465,11 @@ enum TYPE_IDENT_CASES = q{
case tok!"this": case tok!"this":
case tok!"super": case tok!"super":
case tok!"identifier": case tok!"identifier":
};
enum STRING_LITERAL_CASES = q{
case tok!"stringLiteral": case tok!"stringLiteral":
case tok!"wstringLiteral": case tok!"wstringLiteral":
case tok!"dstringLiteral": 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: skip:
mixin (EXPRESSION_LOOP_BREAK); mixin (EXPRESSION_LOOP_BREAK);
immutable bookmark = i; 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++; skipCount++;
@ -532,7 +574,7 @@ T getExpression(T)(T beforeTokens)
*/ */
ImportKind determineImportKind(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!"," || tokens[i] == tok!"." if (!(tokens[i] == tok!":" || tokens[i] == tok!"," || tokens[i] == tok!"."
|| tokens[i] == tok!"identifier")) || tokens[i] == tok!"identifier"))
@ -580,7 +622,7 @@ bool isUdaExpression(T)(ref T tokens)
{ {
bool result; bool result;
ptrdiff_t skip; ptrdiff_t skip;
auto i = cast(ptrdiff_t) tokens.length - 2; ptrdiff_t i = tokens.length - 2;
if (i < 1) if (i < 1)
return result; return result;
@ -588,13 +630,13 @@ bool isUdaExpression(T)(ref T tokens)
// skips the UDA ctor // skips the UDA ctor
if (tokens[i].type == tok!")") if (tokens[i].type == tok!")")
{ {
skip++; ++skip;
i--; --i;
while (i >= 2) while (i >= 2)
{ {
skip += tokens[i].type == tok!")"; skip += tokens[i].type == tok!")";
skip -= tokens[i].type == tok!"("; skip -= tokens[i].type == tok!"(";
i--; --i;
if (skip == 0) if (skip == 0)
{ {
// @UDA!(TemplateParameters)(FunctionParameters) // @UDA!(TemplateParameters)(FunctionParameters)
@ -613,20 +655,20 @@ bool isUdaExpression(T)(ref T tokens)
{ {
// @UDA!SingleTemplateParameter // @UDA!SingleTemplateParameter
if (i > 2 && tokens[i].type == tok!"identifier" && tokens[i-1].type == tok!"!") if (i > 2 && tokens[i].type == tok!"identifier" && tokens[i-1].type == tok!"!")
{
i -= 2; i -= 2;
}
// @UDA // @UDA
if (i > 0 && tokens[i].type == tok!"identifier" && tokens[i-1].type == tok!"@") if (i > 0 && tokens[i].type == tok!"identifier" && tokens[i-1].type == tok!"@")
{
result = true; result = true;
}
} }
return result; 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) size_t goBackToOpenParen(T)(T beforeTokens)
in in
{ {
@ -635,7 +677,8 @@ in
body body
{ {
size_t i = beforeTokens.length - 1; size_t i = beforeTokens.length - 1;
IdType open;
IdType close;
while (true) switch (beforeTokens[i].type) while (true) switch (beforeTokens[i].type)
{ {
case tok!",": case tok!",":
@ -663,69 +706,36 @@ body
case tok!"[": case tok!"[":
return i + 1; return i + 1;
case tok!")": case tok!")":
beforeTokens.skipParenReverse!true(i, tok!")", tok!"("); open = tok!")";
break; close = tok!"(";
goto skip;
case tok!"}": case tok!"}":
beforeTokens.skipParenReverse!true(i, tok!"}", tok!"{"); open = tok!"}";
break; close = tok!"{";
goto skip;
case tok!"]": 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; break;
default: default:
return size_t.max; return size_t.max;
} }
} 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);
} }

View File

@ -113,7 +113,7 @@ int main(string[] args)
return 1; return 1;
} }
if (serverIsRunning(useTCP, socketFile, port)) if (serverIsRunning(useTCP, socketFile, port))
{ {
fatal("Another instance of DCD-server is already running"); fatal("Another instance of DCD-server is already running");
return 1; return 1;
@ -211,8 +211,7 @@ int main(string[] args)
s.shutdown(SocketShutdown.BOTH); s.shutdown(SocketShutdown.BOTH);
s.close(); s.close();
} }
ptrdiff_t bytesReceived = s.receive(buffer);
auto bytesReceived = s.receive(buffer);
auto requestWatch = StopWatch(AutoStart.yes); auto requestWatch = StopWatch(AutoStart.yes);
@ -238,7 +237,6 @@ int main(string[] args)
AutocompleteRequest request; AutocompleteRequest request;
msgpack.unpack(buffer[size_t.sizeof .. bytesReceived], request); msgpack.unpack(buffer[size_t.sizeof .. bytesReceived], request);
if (request.kind & RequestKind.clearCache) if (request.kind & RequestKind.clearCache)
{ {
info("Clearing cache."); info("Clearing cache.");
@ -251,39 +249,77 @@ int main(string[] args)
} }
else if (request.kind & RequestKind.query) else if (request.kind & RequestKind.query)
{ {
s.sendResponse(AutocompleteResponse.ack); AutocompleteResponse response;
response.completionType = "ack";
ubyte[] responseBytes = msgpack.pack(response);
s.send(responseBytes);
continue; continue;
} }
if (request.kind & RequestKind.addImport) if (request.kind & RequestKind.addImport)
{ {
cache.addImportPaths(request.importPaths); cache.addImportPaths(request.importPaths);
} }
if (request.kind & RequestKind.listImports) if (request.kind & RequestKind.listImports)
{ {
AutocompleteResponse response; AutocompleteResponse response;
response.importPaths = cache.getImportPaths().map!(a => cast() a).array(); response.importPaths = cache.getImportPaths().map!(a => cast() a).array();
ubyte[] responseBytes = msgpack.pack(response);
info("Returning import path list"); info("Returning import path list");
s.sendResponse(response); s.send(responseBytes);
} }
else if (request.kind & RequestKind.autocomplete) else if (request.kind & RequestKind.autocomplete)
{ {
info("Getting completions"); 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) else if (request.kind & RequestKind.doc)
{ {
info("Getting doc comment"); 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) 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) else if (request.kind & RequestKind.search)
s.sendResponse(symbolSearch(request, cache)); {
else if (request.kind & RequestKind.localUse) AutocompleteResponse response = symbolSearch(request, cache);
s.trySendResponse(findLocalUse(request, cache), "Could not find local usage"); 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"); info("Request processed in ", requestWatch.peek().to!("msecs", float), " milliseconds");
} }
return 0; return 0;
@ -298,24 +334,16 @@ union IPv4Union
uint i; uint i;
} }
/// Lazily evaluates a response with an exception handler and sends it to a socket or logs msg if evaluating response fails. import std.regex : ctRegex;
void trySendResponse(Socket socket, lazy AutocompleteResponse response, string msg) alias envVarRegex = ctRegex!(`\$\{([_a-zA-Z][_a-zA-Z 0-9]*)\}`);
{
try
{
sendResponse(socket, response);
}
catch (Exception e)
{
warningf("%s: %s", msg, e.msg);
}
}
/// Packs an AutocompleteResponse and sends it to a socket. private unittest
void sendResponse(Socket socket, AutocompleteResponse response)
{ {
ubyte[] responseBytes = msgpack.pack(response); import std.regex : replaceAll;
socket.send(responseBytes);
enum input = `${HOME}/aaa/${_bb_b}/ccc`;
assert(replaceAll!(m => m[1])(input, envVarRegex) == `HOME/aaa/_bb_b/ccc`);
} }
/** /**