diff --git a/src/server/autocomplete/complete.d b/src/server/autocomplete/complete.d index 83f7a91..8e7b079 100644 --- a/src/server/autocomplete/complete.d +++ b/src/server/autocomplete/complete.d @@ -59,12 +59,8 @@ public AutocompleteResponse complete(const AutocompleteRequest request, request.cursorPosition, stringCache, tokenArray); if (beforeTokens.length >= 2) { - if (beforeTokens[$ - 1] == tok!"(" || beforeTokens[$ - 1] == tok!"[") - { - return parenCompletion(beforeTokens, tokenArray, request.cursorPosition, - moduleCache); - } - else if (beforeTokens[$ - 1] == tok!",") + if (beforeTokens[$ - 1] == tok!"(" || beforeTokens[$ - 1] == tok!"[" + || beforeTokens[$ - 1] == tok!",") { immutable size_t end = goBackToOpenParen(beforeTokens); if (end != size_t.max) @@ -80,7 +76,7 @@ public AutocompleteResponse complete(const AutocompleteRequest request, beforeTokens = beforeTokens[$-1 .. $]; return dotCompletion(beforeTokens, tokenArray, request.cursorPosition, moduleCache); - } + } else return importCompletion(beforeTokens, kind, moduleCache); } @@ -122,16 +118,14 @@ 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) { - case tok!"stringLiteral": - case tok!"wstringLiteral": - case tok!"dstringLiteral": + mixin(STRING_LITERAL_CASES); foreach (symbol; arraySymbols) { response.completionKinds ~= symbol.kind; @@ -139,34 +133,9 @@ AutocompleteResponse dotCompletion(T)(T beforeTokens, const(Token)[] tokenArray, } response.completionType = CompletionType.identifiers; break; - 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": + mixin(TYPE_IDENT_CASES); case tok!")": case tok!"]": - case tok!"this": - case tok!"super": auto allocator = scoped!(ASTAllocator)(); RollbackAllocator rba; ScopeSymbolPair pair = generateAutocompleteTrees(tokenArray, allocator, @@ -227,7 +196,6 @@ AutocompleteResponse parenCompletion(T)(T beforeTokens, break; case tok!"characterLiteral": case tok!"doubleLiteral": - case tok!"dstringLiteral": case tok!"floatLiteral": case tok!"identifier": case tok!"idoubleLiteral": @@ -236,14 +204,13 @@ 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, diff --git a/src/server/autocomplete/util.d b/src/server/autocomplete/util.d index d2da8c9..f9f1263 100644 --- a/src/server/autocomplete/util.d +++ b/src/server/autocomplete/util.d @@ -147,36 +147,15 @@ 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!"[": - skip!(tok!"[", tok!"]")(tokens, index); + tokens.skipParen(index, tok!"[", tok!"]"); break; case tok!"(": - skip!(tok!"(", tok!")")(tokens, index); + tokens.skipParen(index, tok!"(", tok!")"); break; case tok!"]": case tok!"}": @@ -200,22 +179,6 @@ 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) @@ -224,7 +187,8 @@ DSymbol*[] getSymbolsByTokenChain(T)(Scope* completionScope, // e.g. (a.b!c).d if (tokens[0] == tok!"(") { - immutable j = skipEnd(tokens, 0, tok!"(", tok!")"); + size_t j; + tokens.skipParen(j, tok!"(", tok!")"); symbols = getSymbolsByTokenChain(completionScope, tokens[1 .. j], cursorPosition, completionType); tokens = tokens[j + 1 .. $]; @@ -307,7 +271,7 @@ DSymbol*[] getSymbolsByTokenChain(T)(Scope* completionScope, { void skip(IdType open, IdType close) { - i = skipEnd(tokens, i, open, close); + tokens.skipParen(i, open, close); } switch (tokens[i].type) @@ -438,7 +402,7 @@ DSymbol*[] getSymbolsByTokenChain(T)(Scope* completionScope, return symbols; } -private enum TYPE_IDENT_AND_LITERAL_CASES = q{ +enum TYPE_IDENT_CASES = q{ case tok!"int": case tok!"uint": case tok!"long": @@ -465,11 +429,16 @@ private enum TYPE_IDENT_AND_LITERAL_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; + /** * */ @@ -516,18 +485,7 @@ T getExpression(T)(T beforeTokens) skip: mixin (EXPRESSION_LOOP_BREAK); immutable bookmark = 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); + i = beforeTokens.skipParenReverse(i, open, close); skipCount++; @@ -622,11 +580,11 @@ bool isUdaExpression(T)(ref T tokens) { bool result; ptrdiff_t skip; - ptrdiff_t i = tokens.length - 2; - + auto i = cast(ptrdiff_t) tokens.length - 2; + if (i < 1) return result; - + // skips the UDA ctor if (tokens[i].type == tok!")") { @@ -650,7 +608,7 @@ bool isUdaExpression(T)(ref T tokens) } } } - + if (skip == 0) { // @UDA!SingleTemplateParameter @@ -665,10 +623,14 @@ bool isUdaExpression(T)(ref T tokens) 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 { @@ -677,8 +639,6 @@ in body { size_t i = beforeTokens.length - 1; - IdType open; - IdType close; while (true) switch (beforeTokens[i].type) { case tok!",": @@ -706,36 +666,75 @@ body case tok!"[": return i + 1; case tok!")": - open = tok!")"; - close = tok!"("; - goto skip; + i = beforeTokens.skipParenReverseBefore(i, tok!")", tok!"("); + break; case tok!"}": - open = tok!"}"; - close = tok!"{"; - goto skip; + i = beforeTokens.skipParenReverseBefore(i, tok!"}", tok!"{"); + break; case 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); + i = beforeTokens.skipParenReverseBefore(i, tok!"]", tok!"["); break; default: 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 + */ +size_t skipParenReverse(T)(T beforeTokens, size_t i, IdType open, IdType close) +{ + if (i == 0) + return 0; + int depth = 1; + while (depth != 0 && i != 0) + { + i--; + if (beforeTokens[i].type == open) + depth++; + else if (beforeTokens[i].type == close) + depth--; + } + return i; +} + +size_t skipParenReverseBefore(T)(T beforeTokens, size_t i, IdType open, IdType close) +{ + i = skipParenReverse(beforeTokens, i, open, close); + if (i != 0) + i--; + return 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; + i = skipParenReverse(t, i, tok!")", tok!"("); + assert(i == 2); + i = t.length - 1; + i = skipParenReverseBefore(t, i, tok!")", tok!"("); + assert(i == 1); } diff --git a/tests/tc058/expected1.txt b/tests/tc058/expected1.txt new file mode 100644 index 0000000..8bc98d2 --- /dev/null +++ b/tests/tc058/expected1.txt @@ -0,0 +1,2 @@ +calltips +void doStuff(int a, int b) diff --git a/tests/tc058/file.d b/tests/tc058/file.d new file mode 100644 index 0000000..80072ce --- /dev/null +++ b/tests/tc058/file.d @@ -0,0 +1,8 @@ +void doStuff(int a, int b) { return; } +int getInt(int i, int[] b) { return i; } +int b(int a) { return a; } +void main(string[] args) +{ + doStuff(getInt(1, [b(3)]),); + auto x = 10, 20; +} diff --git a/tests/tc058/run.sh b/tests/tc058/run.sh new file mode 100755 index 0000000..3376eea --- /dev/null +++ b/tests/tc058/run.sh @@ -0,0 +1,5 @@ +set -e +set -u + +../../bin/dcd-client $1 file.d -c161 > actual1.txt +diff actual1.txt expected1.txt