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`);
} }
/** /**
@ -353,4 +381,4 @@ options:
--socketFile FILENAME --socketFile FILENAME
Use the given FILENAME as the path to the UNIX domain socket. Using Use the given FILENAME as the path to the UNIX domain socket. Using
this switch is an error on Windows.`, programName); this switch is an error on Windows.`, programName);
} }