diff --git a/src/server/autocomplete/complete.d b/src/server/autocomplete/complete.d index f3e0b73..83f7a91 100644 --- a/src/server/autocomplete/complete.d +++ b/src/server/autocomplete/complete.d @@ -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) diff --git a/src/server/autocomplete/localuse.d b/src/server/autocomplete/localuse.d index 3dca1bf..b09816b 100644 --- a/src/server/autocomplete/localuse.d +++ b/src/server/autocomplete/localuse.d @@ -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; } diff --git a/src/server/autocomplete/symbols.d b/src/server/autocomplete/symbols.d index 17e5a13..cf28b74 100644 --- a/src/server/autocomplete/symbols.d +++ b/src/server/autocomplete/symbols.d @@ -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 { diff --git a/src/server/autocomplete/util.d b/src/server/autocomplete/util.d index c7cd966..d2da8c9 100644 --- a/src/server/autocomplete/util.d +++ b/src/server/autocomplete/util.d @@ -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; } diff --git a/src/server/main.d b/src/server/main.d index 1bc688d..2095957 100644 --- a/src/server/main.d +++ b/src/server/main.d @@ -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`); } /** @@ -353,4 +381,4 @@ options: --socketFile FILENAME Use the given FILENAME as the path to the UNIX domain socket. Using this switch is an error on Windows.`, programName); -} +} \ No newline at end of file