diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/completion/CompletionContextFinder.java b/php/php.editor/src/org/netbeans/modules/php/editor/completion/CompletionContextFinder.java index 74651920cede..94bdce30c0dc 100644 --- a/php/php.editor/src/org/netbeans/modules/php/editor/completion/CompletionContextFinder.java +++ b/php/php.editor/src/org/netbeans/modules/php/editor/completion/CompletionContextFinder.java @@ -61,6 +61,7 @@ final class CompletionContextFinder { private static final String MULTI_CATCH_EXCEPTION_TOKENS = "MULTI_CATCH_EXCEPTION_TOKENS"; //NOI18N private static final String COMBINED_USE_STATEMENT_TOKENS = "COMBINED_USE_STATEMENT_TOKENS"; //NOI18N private static final String CONST_STATEMENT_TOKENS = "CONST_STATEMENT_TOKENS"; //NOI18N + private static final String ENUM_CASE_STATEMENT_TOKENS = "ENUM_CASE_STATEMENT_TOKENS"; //NOI18N private static final String FIELD_UNION_OR_INTERSECTION_TYPE_TOKENS = "FIELD_UNION_TYPE_TOKENS"; //NOI18N private static final String FIELD_MODIFIERS_TOKENS = "FIELD_MODIFIERS_TOKENS"; //NOI18N private static final String OBJECT_OPERATOR_TOKEN = "OBJECT_OPERATOR_TOKEN"; //NOI18N @@ -204,6 +205,10 @@ final class CompletionContextFinder { new Object[]{PHPTokenId.PHP_CONST, PHPTokenId.WHITESPACE, PHPTokenId.PHP_STRING, PHPTokenId.WHITESPACE, CONST_STATEMENT_TOKENS}, new Object[]{PHPTokenId.PHP_CONST, PHPTokenId.WHITESPACE, PHPTokenId.PHP_STRING, CONST_STATEMENT_TOKENS} ); + private static final List ENUM_CASE_TOKENCHAINS = Arrays.asList( + new Object[]{PHPTokenId.PHP_CASE, PHPTokenId.WHITESPACE, PHPTokenId.PHP_STRING, PHPTokenId.WHITESPACE, CONST_STATEMENT_TOKENS}, + new Object[]{PHPTokenId.PHP_CASE, PHPTokenId.WHITESPACE, PHPTokenId.PHP_STRING, CONST_STATEMENT_TOKENS} + ); private static final List SERVER_ARRAY_TOKENCHAINS = Collections.singletonList( new Object[]{PHPTokenId.PHP_VARIABLE, PHPTokenId.PHP_TOKEN}); private static final List SERVER_ARRAY_TOKENTEXTS = @@ -211,7 +216,7 @@ final class CompletionContextFinder { public static enum CompletionContext { - EXPRESSION, GLOBAL_CONST_EXPRESSION, CLASS_CONST_EXPRESSION, MATCH_EXPRESSION, + EXPRESSION, GLOBAL_CONST_EXPRESSION, CLASS_CONST_EXPRESSION, MATCH_EXPRESSION, ENUM_CASE_EXPRESSION, HTML, CLASS_NAME, INTERFACE_NAME, BACKING_TYPE, TYPE_NAME, RETURN_TYPE_NAME, RETURN_UNION_OR_INTERSECTION_TYPE_NAME, FIELD_TYPE_NAME, VISIBILITY_MODIFIER_OR_TYPE_NAME, STRING, CLASS_MEMBER, STATIC_CLASS_MEMBER, PHPDOC, INHERITANCE, EXTENDS, IMPLEMENTS, METHOD_NAME, @@ -328,6 +333,8 @@ static CompletionContext findCompletionContext(ParserResult info, int caretOffse return CompletionContext.FIELD_TYPE_NAME; } else if (acceptTokenChains(tokenSequence, CONST_TOKENCHAINS, moveNextSucces)) { return CompletionContext.CLASS_CONST_EXPRESSION; + } else if (acceptTokenChains(tokenSequence, ENUM_CASE_TOKENCHAINS, moveNextSucces)) { + return CompletionContext.ENUM_CASE_EXPRESSION; } else if (acceptTokenChains(tokenSequence, CLASS_CONTEXT_KEYWORDS_TOKENCHAINS, moveNextSucces)) { return CompletionContext.CLASS_CONTEXT_KEYWORDS; } @@ -540,6 +547,11 @@ private static boolean acceptTokenChain(TokenSequence tokenSequence, Object[] to accept = false; break; } + } else if (tokenID == ENUM_CASE_STATEMENT_TOKENS) { + if (!consumeUntilEnumCaseEqual(tokenSequence)) { + accept = false; + break; + } } else if (tokenID == OBJECT_OPERATOR_TOKEN) { if (!consumeObjectOperator(tokenSequence)) { accept = false; @@ -749,9 +761,25 @@ private static boolean consumeUntilTypeKeyword(TokenSequence tokenSequence) { private static boolean consumeUntilConstEqual(TokenSequence tokenSequence) { boolean hasEqual = false; do { - if (tokenSequence.token().id() == PHPTokenId.PHP_SEMICOLON - || tokenSequence.token().id() == PHPTokenId.PHP_CURLY_OPEN - || tokenSequence.token().id() == PHPTokenId.PHP_CURLY_CLOSE) { + if (tokenSequence.token().id() == PHPTokenId.PHP_CONST + || tokenSequence.token().id() == PHPTokenId.PHP_SEMICOLON) { + break; + } + if (isEqualSign(tokenSequence.token())) { + hasEqual = true; + tokenSequence.movePrevious(); + break; + } + } while (tokenSequence.movePrevious()); + + return hasEqual; + } + + private static boolean consumeUntilEnumCaseEqual(TokenSequence tokenSequence) { + boolean hasEqual = false; + do { + if (tokenSequence.token().id() == PHPTokenId.PHP_CASE + || tokenSequence.token().id() == PHPTokenId.PHP_SEMICOLON) { break; } if (isEqualSign(tokenSequence.token())) { diff --git a/php/php.editor/src/org/netbeans/modules/php/editor/completion/PHPCodeCompletion.java b/php/php.editor/src/org/netbeans/modules/php/editor/completion/PHPCodeCompletion.java index 8508907eb7d0..d710c11283d0 100644 --- a/php/php.editor/src/org/netbeans/modules/php/editor/completion/PHPCodeCompletion.java +++ b/php/php.editor/src/org/netbeans/modules/php/editor/completion/PHPCodeCompletion.java @@ -447,7 +447,8 @@ public CodeCompletionResult complete(CodeCompletionContext completionContext) { autoCompleteConstants(completionResult, request); autoCompleteKeywords(completionResult, request, PHP_GLOBAL_CONST_KEYWORDS); break; - case CLASS_CONST_EXPRESSION: + case CLASS_CONST_EXPRESSION: // no break + case ENUM_CASE_EXPRESSION: autoCompleteNamespaces(completionResult, request); autoCompleteTypeNames(completionResult, request, null, true); autoCompleteConstants(completionResult, request); diff --git a/php/php.editor/test/unit/data/testfiles/completion/lib/php83/testDynamicClassConstantFetch/dynamicClassConstantFetch.php b/php/php.editor/test/unit/data/testfiles/completion/lib/php83/testDynamicClassConstantFetch/dynamicClassConstantFetch.php new file mode 100644 index 000000000000..d9859f9dce72 --- /dev/null +++ b/php/php.editor/test/unit/data/testfiles/completion/lib/php83/testDynamicClassConstantFetch/dynamicClassConstantFetch.php @@ -0,0 +1,58 @@ + createClassPathsForTest() { + return Collections.singletonMap( + PhpSourcePath.SOURCE_CP, + ClassPathSupport.createClassPath(new FileObject[]{ + FileUtil.toFileObject(new File(getDataDir(), "/testfiles/completion/lib/php83/" + getTestDirName())) + }) + ); + } + + private String getTestDirName() { + String name = getName(); + int indexOf = name.indexOf("_"); + if (indexOf != -1) { + name = name.substring(0, indexOf); + } + return name; + } + + private String getTestPath(String fileName) { + return String.format("testfiles/completion/lib/php83/%s/%s.php", getTestDirName(), fileName); + } + + private void checkCompletion(String fileName, String caretPosition) throws Exception { + checkCompletion(getTestPath(fileName), caretPosition, false); + } + + public void testDynamicClassConstantFetchInConstDeclTyping_01() throws Exception { + checkCompletion("inConstantDeclarationTyping", " public const TEST1 = self::{^};"); + } + + public void testDynamicClassConstantFetchInEnumCaseTyping_01() throws Exception { + checkCompletion("inEnumCaseTyping", " case TEST1 = self::{^};"); + } + + public void testDynamicClassConstantFetchTyping_01() throws Exception { + checkCompletion("dynamicClassConstantFetchTyping", "Test::{^};"); + } + + public void testDynamicClassConstantFetch_01() throws Exception { + checkCompletion("dynamicClassConstantFetch", " public const TEST3 = self::{self::T^ES . self::T};"); + } + + public void testDynamicClassConstantFetch_02() throws Exception { + checkCompletion("dynamicClassConstantFetch", "Test::{$^variable};"); + } + + public void testDynamicClassConstantFetch_03() throws Exception { + checkCompletion("dynamicClassConstantFetch", "Test::{$var^iable};"); + } + + public void testDynamicClassConstantFetch_04() throws Exception { + checkCompletion("dynamicClassConstantFetch", "$test::{$variable . $^e};"); + } + + public void testDynamicClassConstantFetch_05() throws Exception { + checkCompletion("dynamicClassConstantFetch", "$test::{$variable . $e^};"); + } + + public void testDynamicClassConstantFetch_06a() throws Exception { + checkCompletion("dynamicClassConstantFetch", "Test::{Tes^t::method()}::{test($variable)};"); + } + + public void testDynamicClassConstantFetch_06b() throws Exception { + checkCompletion("dynamicClassConstantFetch", "Test::{Test::met^hod()}::{test($variable)};"); + } + + public void testDynamicClassConstantFetch_06c() throws Exception { + checkCompletion("dynamicClassConstantFetch", "Test::{Test::method()}::{te^st($variable)};"); + } + + public void testDynamicClassConstantFetch_06d() throws Exception { + checkCompletion("dynamicClassConstantFetch", "Test::{Test::method()}::{test($var^iable)};"); + } + + public void testDynamicClassConstantFetch_07a() throws Exception { + checkCompletion("dynamicClassConstantFetch", " case TEST3 = self::^{self::TES . self::T};"); + } + + public void testDynamicClassConstantFetch_07b() throws Exception { + checkCompletion("dynamicClassConstantFetch", " case TEST3 = self::{sel^f::TES . self::T};"); + } + + public void testDynamicClassConstantFetch_07c() throws Exception { + checkCompletion("dynamicClassConstantFetch", " case TEST3 = self::{self::TE^S . self::T};"); + } + + public void testDynamicClassConstantFetch_08a() throws Exception { + checkCompletion("dynamicClassConstantFetch", " case TEST4 = EnumTe^st::{self::{self::TES} . self::T};"); + } + + public void testDynamicClassConstantFetch_08b() throws Exception { + checkCompletion("dynamicClassConstantFetch", " case TEST4 = EnumTest::{se^lf::{self::TES} . self::T};"); + } + + public void testDynamicClassConstantFetch_08c() throws Exception { + checkCompletion("dynamicClassConstantFetch", " case TEST4 = EnumTest::{self::{sel^f::TES} . self::T};"); + } + + public void testDynamicClassConstantFetch_08d() throws Exception { + checkCompletion("dynamicClassConstantFetch", " case TEST4 = EnumTest::{self::{self::TE^S} . self::T};"); + } + + public void testDynamicClassConstantFetch_08e() throws Exception { + checkCompletion("dynamicClassConstantFetch", " case TEST4 = EnumTest::{self::{self::TES} . self::T^};"); + } +}