diff --git a/dsymbol/src/dsymbol/ufcs.d b/dsymbol/src/dsymbol/ufcs.d index aaa5856..cd9a108 100644 --- a/dsymbol/src/dsymbol/ufcs.d +++ b/dsymbol/src/dsymbol/ufcs.d @@ -30,11 +30,6 @@ struct TokenCursorResult string partialIdentifier; } -struct DeducedSymbolTypeResult{ - const(DSymbol)* deducedSymbolType; - bool success = false; -} - // https://dlang.org/spec/type.html#implicit-conversions enum string[string] INTEGER_PROMOTIONS = [ "bool": "int", @@ -49,20 +44,17 @@ enum string[string] INTEGER_PROMOTIONS = [ enum MAX_NUMBER_OF_MATCHING_RUNS = 50; -private DeducedSymbolTypeResult deduceSymbolTypeByToken(Scope* completionScope, scope ref const(Token) significantToken, size_t cursorPosition) +private const(DSymbol)* deduceSymbolTypeByToken(Scope* completionScope, scope ref const(Token) significantToken, size_t cursorPosition) { - DeducedSymbolTypeResult result; //Literal type deduction if (significantToken.type is tok!"stringLiteral"){ - result.deducedSymbolType = completionScope.getFirstSymbolByNameAndCursor(istring("string"), cursorPosition); - result.success = true; - return result; + return completionScope.getFirstSymbolByNameAndCursor(symbolNameToTypeName(STRING_LITERAL_SYMBOL_NAME), cursorPosition); } const(DSymbol)* symbol = completionScope.getFirstSymbolByNameAndCursor(istring(significantToken.text), cursorPosition); if (symbol is null) { - return result; + return null; } const(DSymbol)* symbolType = symbol.type; @@ -82,9 +74,7 @@ private DeducedSymbolTypeResult deduceSymbolTypeByToken(Scope* completionScope, } - result.deducedSymbolType = symbolType; - result.success = true; - return result; + return symbolType; } @@ -120,8 +110,14 @@ private TokenCursorResult getCursorToken(const(Token)[] tokens, size_t cursorPos && (sortedBeforeTokens[$ - 2].type is tok!"identifier" || sortedBeforeTokens[$ - 2].type is tok!"stringLiteral")) { // Check if it's UFCS dot completion - tokenCursorResult.completionContext = CompletionContext.DotCompletion; + auto expressionTokens = getExpression(sortedBeforeTokens); + if(expressionTokens[0] !is sortedBeforeTokens[$ - 2]){ + // If the expression is invalid as a dot token we return + return tokenCursorResult; + } + tokenCursorResult.significantToken = sortedBeforeTokens[$ - 2]; + tokenCursorResult.completionContext = CompletionContext.DotCompletion; return tokenCursorResult; } else if (!tokenCursorResult.partialIdentifier.length) @@ -212,14 +208,14 @@ DSymbol*[] getUFCSSymbolsForCursor(Scope* completionScope, scope ref const(Token return []; } - DeducedSymbolTypeResult deducedSymbolTypeResult = deduceSymbolTypeByToken(completionScope, tokenCursorResult.significantToken, cursorPosition); + const(DSymbol)* deducedSymbolType = deduceSymbolTypeByToken(completionScope, tokenCursorResult.significantToken, cursorPosition); - if (deducedSymbolTypeResult.deducedSymbolType is null) + if (deducedSymbolType is null) { return []; } - if (deducedSymbolTypeResult.deducedSymbolType.isInvalidForUFCSCompletion) + if (deducedSymbolType.isInvalidForUFCSCompletion) { trace("CursorSymbolType isn't valid for UFCS completion"); return []; @@ -227,11 +223,11 @@ DSymbol*[] getUFCSSymbolsForCursor(Scope* completionScope, scope ref const(Token if (tokenCursorResult.completionContext == CompletionContext.ParenCompletion) { - return getUFCSSymbolsForParenCompletion(deducedSymbolTypeResult.deducedSymbolType, completionScope, tokenCursorResult.functionName, cursorPosition); + return getUFCSSymbolsForParenCompletion(deducedSymbolType, completionScope, tokenCursorResult.functionName, cursorPosition); } else { - return getUFCSSymbolsForDotCompletion(deducedSymbolTypeResult.deducedSymbolType, completionScope, cursorPosition, tokenCursorResult.partialIdentifier); + return getUFCSSymbolsForDotCompletion(deducedSymbolType, completionScope, cursorPosition, tokenCursorResult.partialIdentifier); } } diff --git a/dsymbol/src/dsymbol/utils.d b/dsymbol/src/dsymbol/utils.d index b2c88d8..fe2df73 100644 --- a/dsymbol/src/dsymbol/utils.d +++ b/dsymbol/src/dsymbol/utils.d @@ -153,3 +153,91 @@ unittest i = skipParenReverseBefore(t, i, tok!")", tok!"("); assert(i == 1); } + +T getExpression(T)(T beforeTokens) +{ + enum EXPRESSION_LOOP_BREAK = q{ + if (i + 1 < beforeTokens.length) switch (beforeTokens[i + 1].type) + { + mixin (TYPE_IDENT_AND_LITERAL_CASES); + i++; + break expressionLoop; + default: + break; + } + }; + + if (beforeTokens.length == 0) + return beforeTokens[0 .. 0]; + size_t i = beforeTokens.length - 1; + size_t sliceEnd = beforeTokens.length; + IdType open; + IdType close; + uint skipCount = 0; + + expressionLoop: while (true) + { + switch (beforeTokens[i].type) + { + case tok!"import": + i++; + break expressionLoop; + mixin (TYPE_IDENT_AND_LITERAL_CASES); + mixin (EXPRESSION_LOOP_BREAK); + break; + case tok!".": + break; + case tok!")": + open = tok!")"; + close = tok!"("; + goto skip; + case tok!"]": + open = tok!"]"; + close = tok!"["; + skip: + mixin (EXPRESSION_LOOP_BREAK); + immutable bookmark = i; + i = beforeTokens.skipParenReverse(i, open, close); + + skipCount++; + + // check the current token after skipping parens to the left. + // if it's a loop keyword, pretend we never skipped the parens. + if (i > 0) switch (beforeTokens[i - 1].type) + { + case tok!"scope": + case tok!"if": + case tok!"while": + case tok!"for": + case tok!"foreach": + case tok!"foreach_reverse": + case tok!"do": + case tok!"cast": + case tok!"catch": + i = bookmark + 1; + break expressionLoop; + case tok!"!": + // only break if the bang is for a template instance + if (i - 2 >= 0 && beforeTokens[i - 2].type == tok!"identifier" && skipCount == 1) + { + sliceEnd = i - 1; + i -= 2; + break expressionLoop; + } + break; + default: + break; + } + break; + default: + i++; + break expressionLoop; + } + if (i == 0) + break; + else + i--; + } + return beforeTokens[i .. sliceEnd]; +} + diff --git a/src/dcd/server/autocomplete/complete.d b/src/dcd/server/autocomplete/complete.d index e7f4203..e5363c0 100644 --- a/src/dcd/server/autocomplete/complete.d +++ b/src/dcd/server/autocomplete/complete.d @@ -208,18 +208,9 @@ AutocompleteResponse dotCompletion(T)(T beforeTokens, const(Token)[] tokenArray, switch (significantTokenType) { mixin(STRING_LITERAL_CASES); - { - foreach (symbol; arraySymbols){ + foreach (symbol; arraySymbols) response.completions ~= makeSymbolCompletionInfo(symbol, symbol.kind); - } - response.completionType = CompletionType.identifiers; - RollbackAllocator rba; - ScopeSymbolPair pair = generateAutocompleteTrees(tokenArray, &rba, cursorPosition, moduleCache); - scope(exit) pair.destroy(); - response.completions ~= pair.ufcsSymbols.map!(s => makeSymbolCompletionInfo(s, CompletionKind.ufcsName)).array; - break; - } - + goto case; mixin(TYPE_IDENT_CASES); case tok!")": case tok!"]": diff --git a/src/dcd/server/autocomplete/localuse.d b/src/dcd/server/autocomplete/localuse.d index 00ef824..5974cfb 100644 --- a/src/dcd/server/autocomplete/localuse.d +++ b/src/dcd/server/autocomplete/localuse.d @@ -31,6 +31,7 @@ import dparse.rollback_allocator; import dsymbol.conversion; import dsymbol.modulecache; import dsymbol.symbol; +import dsymbol.utils; import dcd.common.messages; diff --git a/src/dcd/server/autocomplete/util.d b/src/dcd/server/autocomplete/util.d index 81dffbc..6622af1 100644 --- a/src/dcd/server/autocomplete/util.d +++ b/src/dcd/server/autocomplete/util.d @@ -422,96 +422,6 @@ DSymbol*[] getSymbolsByTokenChain(T)(Scope* completionScope, return symbols; } -/** - * - */ -T getExpression(T)(T beforeTokens) -{ - enum EXPRESSION_LOOP_BREAK = q{ - if (i + 1 < beforeTokens.length) switch (beforeTokens[i + 1].type) - { - mixin (TYPE_IDENT_AND_LITERAL_CASES); - i++; - break expressionLoop; - default: - break; - } - }; - - if (beforeTokens.length == 0) - return beforeTokens[0 .. 0]; - size_t i = beforeTokens.length - 1; - size_t sliceEnd = beforeTokens.length; - IdType open; - IdType close; - uint skipCount = 0; - - expressionLoop: while (true) - { - switch (beforeTokens[i].type) - { - case tok!"import": - i++; - break expressionLoop; - mixin (TYPE_IDENT_AND_LITERAL_CASES); - mixin (EXPRESSION_LOOP_BREAK); - break; - case tok!".": - break; - case tok!")": - open = tok!")"; - close = tok!"("; - goto skip; - case tok!"]": - open = tok!"]"; - close = tok!"["; - skip: - mixin (EXPRESSION_LOOP_BREAK); - immutable bookmark = i; - i = beforeTokens.skipParenReverse(i, open, close); - - skipCount++; - - // check the current token after skipping parens to the left. - // if it's a loop keyword, pretend we never skipped the parens. - if (i > 0) switch (beforeTokens[i - 1].type) - { - case tok!"scope": - case tok!"if": - case tok!"while": - case tok!"for": - case tok!"foreach": - case tok!"foreach_reverse": - case tok!"do": - case tok!"cast": - case tok!"catch": - i = bookmark + 1; - break expressionLoop; - case tok!"!": - // only break if the bang is for a template instance - if (i - 2 >= 0 && beforeTokens[i - 2].type == tok!"identifier" && skipCount == 1) - { - sliceEnd = i - 1; - i -= 2; - break expressionLoop; - } - break; - default: - break; - } - break; - default: - i++; - break expressionLoop; - } - if (i == 0) - break; - else - i--; - } - return beforeTokens[i .. sliceEnd]; -} - /** * Determines if an import is selective, whole-module, or neither. */ diff --git a/tests/tc_ufcs_string_and_string_literal_completion/expected_string_literal_test.txt b/tests/tc_ufcs_string_and_string_literal_completion/expected_string_literal_test.txt index 1580c7a..bed240a 100644 --- a/tests/tc_ufcs_string_and_string_literal_completion/expected_string_literal_test.txt +++ b/tests/tc_ufcs_string_and_string_literal_completion/expected_string_literal_test.txt @@ -8,6 +8,5 @@ mangleof k ptr k sizeof k stringof k -testUfcs F ufcsString F ufcsStringBar F diff --git a/tests/tc_ufcs_string_and_string_literal_completion/expected_string_test.txt b/tests/tc_ufcs_string_and_string_literal_completion/expected_string_test.txt index a10414d..e69de29 100644 --- a/tests/tc_ufcs_string_and_string_literal_completion/expected_string_test.txt +++ b/tests/tc_ufcs_string_and_string_literal_completion/expected_string_test.txt @@ -1,4 +0,0 @@ -identifiers -testUfcs F -ufcsString F -ufcsStringBar F diff --git a/tests/tc_ufcs_string_and_string_literal_completion/file.d b/tests/tc_ufcs_string_and_string_literal_completion/file.d index c21a30e..9d3bdec 100644 --- a/tests/tc_ufcs_string_and_string_literal_completion/file.d +++ b/tests/tc_ufcs_string_and_string_literal_completion/file.d @@ -1,6 +1,7 @@ +struct Hello { void hi(){} } void ufcsString(string input){} void ufcsStringBar(string input){} -void testUfcs(string x){ +void testUfcs(Hello x){ "foo". x. } diff --git a/tests/tc_ufcs_string_and_string_literal_completion/run.sh b/tests/tc_ufcs_string_and_string_literal_completion/run.sh index 261775b..8d19991 100755 --- a/tests/tc_ufcs_string_and_string_literal_completion/run.sh +++ b/tests/tc_ufcs_string_and_string_literal_completion/run.sh @@ -1,15 +1,8 @@ set -e set -u -#TEST CASE 0 -SOURCE_FILE_0=file.d -ACTUAL_FILE_NAME_0=actual_string_literal_test.txt -EXPECTED_FILE_NAME_0=expected_string_literal_test.txt +../../bin/dcd-client $1 -c127 file.d > actual_string_literal_test.txt +diff actual_string_literal_test.txt expected_string_literal_test.txt --strip-trailing-cr -../../bin/dcd-client $1 -c99 $SOURCE_FILE_0 > $ACTUAL_FILE_NAME_0 -diff $ACTUAL_FILE_NAME_0 $EXPECTED_FILE_NAME_0 --strip-trailing-cr - -ACTUAL_FILE_NAME_1=actual_string_test.txt -EXPECTED_FILE_NAME_1=expected_string_test.txt -../../bin/dcd-client $1 -c103 $SOURCE_FILE_0 > $ACTUAL_FILE_NAME_1 -diff $ACTUAL_FILE_NAME_1 $EXPECTED_FILE_NAME_1 --strip-trailing-cr \ No newline at end of file +../../bin/dcd-client $1 -c131 file.d > actual_string_test.txt +diff actual_string_test.txt expected_string_test.txt --strip-trailing-cr \ No newline at end of file diff --git a/tests/tc_ufcs_struct_completion/expected_should_not_complete_test.txt b/tests/tc_ufcs_struct_completion/expected_should_not_complete_test.txt new file mode 100644 index 0000000..e69de29 diff --git a/tests/tc_ufcs_struct_completion/expected_should_not_complete_test2.txt b/tests/tc_ufcs_struct_completion/expected_should_not_complete_test2.txt new file mode 100644 index 0000000..e69de29 diff --git a/tests/tc_ufcs_struct_completion/expected_struct_test.txt b/tests/tc_ufcs_struct_completion/expected_struct_test.txt index 01eae0a..8b98fa8 100644 --- a/tests/tc_ufcs_struct_completion/expected_struct_test.txt +++ b/tests/tc_ufcs_struct_completion/expected_struct_test.txt @@ -1,4 +1,5 @@ identifiers +aliasStruct F alignof k fooHey f hasArgname F diff --git a/tests/tc_ufcs_struct_completion/file.d b/tests/tc_ufcs_struct_completion/file.d index 0ea9005..b06084a 100644 --- a/tests/tc_ufcs_struct_completion/file.d +++ b/tests/tc_ufcs_struct_completion/file.d @@ -8,7 +8,9 @@ void main() foo. } -void aliasStruct() { +void aliasStruct(Foo f) { auto intAliased = IntAliased(); intAliased. + f. + f. } diff --git a/tests/tc_ufcs_struct_completion/run.sh b/tests/tc_ufcs_struct_completion/run.sh index ac90554..6e047b7 100755 --- a/tests/tc_ufcs_struct_completion/run.sh +++ b/tests/tc_ufcs_struct_completion/run.sh @@ -4,5 +4,11 @@ set -u ../../bin/dcd-client $1 -c82 -I"$PWD"/fooutils file.d > actual_struct_test.txt diff actual_struct_test.txt expected_struct_test.txt -../../bin/dcd-client $1 -c152 -I"$PWD"/fooutils file.d > actual_aliased_struct_test.txt -diff actual_aliased_struct_test.txt expected_aliased_struct_test.txt \ No newline at end of file +../../bin/dcd-client $1 -c157 -I"$PWD"/fooutils file.d > actual_aliased_struct_test.txt +diff actual_aliased_struct_test.txt expected_aliased_struct_test.txt + +../../bin/dcd-client $1 -c161 -I"$PWD"/fooutils file.d > actual_should_not_complete_test.txt +diff actual_should_not_complete_test.txt expected_should_not_complete_test.txt + +../../bin/dcd-client $1 -c165 -I"$PWD"/fooutils file.d > actual_should_not_complete_test2.txt +diff actual_should_not_complete_test2.txt expected_should_not_complete_test2.txt \ No newline at end of file