Skip to content

Commit

Permalink
PHP 8.4 Support: Asymmetric Visibility v2 (Part 4)
Browse files Browse the repository at this point in the history
- #8035
- https://wiki.php.net/rfc#php_84
- https://wiki.php.net/rfc/asymmetric-visibility-v2

- Fix Code Completion
- Fix/Add unit tests

Note: CC does not work correctly on the following caret position because set visibility keywords contain a brace.
```php
class Example {
    public private(se^ // ^: caret
}
```
  • Loading branch information
junichi11 committed Jan 23, 2025
1 parent ca1f310 commit ce14c99
Show file tree
Hide file tree
Showing 234 changed files with 2,553 additions and 63 deletions.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@
import org.netbeans.modules.php.editor.parser.astnodes.ClassInstanceCreation;
import org.netbeans.modules.php.editor.parser.astnodes.EnumDeclaration;
import org.netbeans.modules.php.editor.parser.astnodes.Expression;
import org.netbeans.modules.php.editor.parser.astnodes.InterfaceDeclaration;
import org.netbeans.modules.php.editor.parser.astnodes.NamespaceName;
import org.netbeans.modules.php.editor.parser.astnodes.TraitDeclaration;
import org.netbeans.modules.php.editor.parser.astnodes.TypeDeclaration;
Expand Down Expand Up @@ -168,6 +169,12 @@ private static enum UseType {
PHP_KEYWORDS.put("public", KeywordCompletionType.ENDS_WITH_SPACE); //NOI18N
PHP_KEYWORDS.put("private", KeywordCompletionType.ENDS_WITH_SPACE); //NOI18N
PHP_KEYWORDS.put("protected", KeywordCompletionType.ENDS_WITH_SPACE); //NOI18N
PHP_KEYWORDS.put("public(set)", KeywordCompletionType.ENDS_WITH_SPACE); //NOI18N
PHP_KEYWORDS.put("private(set)", KeywordCompletionType.ENDS_WITH_SPACE); //NOI18N
PHP_KEYWORDS.put("protected(set)", KeywordCompletionType.ENDS_WITH_SPACE); //NOI18N
PHP_KEYWORDS.put("public protected(set)", KeywordCompletionType.ENDS_WITH_SPACE); //NOI18N
PHP_KEYWORDS.put("public private(set)", KeywordCompletionType.ENDS_WITH_SPACE); //NOI18N
PHP_KEYWORDS.put("protected private(set)", KeywordCompletionType.ENDS_WITH_SPACE); //NOI18N
PHP_KEYWORDS.put("abstract", KeywordCompletionType.ENDS_WITH_SPACE); //NOI18N
PHP_KEYWORDS.put("readonly", KeywordCompletionType.ENDS_WITH_SPACE); //NOI18N
PHP_KEYWORDS.put("clone", KeywordCompletionType.ENDS_WITH_SPACE); //NOI18N
Expand Down Expand Up @@ -256,6 +263,16 @@ private static enum UseType {
"protected", // NOI18N
"private" // NOI18N
);
private static final List<String> PHP_SET_VISIBILITY_KEYWORDS = Arrays.asList(
"public(set)", // NOI18N
"protected(set)", // NOI18N
"private(set)" // NOI18N
);
private static final List<String> PHP_ASYMMETRIC_VISIBILITY_KEYWORDS = Arrays.asList(
"public private(set)", // NOI18N
"public protected(set)", // NOI18N
"protected private(set)" // NOI18N
);
private static final Collection<Character> AUTOPOPUP_STOP_CHARS = new TreeSet<>(
Arrays.asList('=', ';', '+', '-', '*', '/',
'%', '(', ')', '[', ']', '{', '}', '?'));
Expand All @@ -265,9 +282,11 @@ private static enum UseType {
Arrays.asList(new String[]{"__construct", "__destruct", "__call", "__callStatic",
"__clone", "__get", "__invoke", "__isset", "__set", "__set_state",
"__sleep", "__toString", "__unset", "__wakeup"}); //NOI18N
private static final List<String> CLASS_CONTEXT_KEYWORD_PROPOSAL =
Arrays.asList(new String[]{"abstract", "const", "function", "private", "final",
"protected", "public", "static", "var", "readonly"}); //NOI18N
private static final List<String> CLASS_CONTEXT_KEYWORD_PROPOSAL = Arrays.asList(
"abstract", "const", "function", "final", // NOI18N
"private", "protected", "public", // NOI18N
"static", "var", "readonly" // NOI18N
);
private static final List<String> INTERFACE_CONTEXT_KEYWORD_PROPOSAL =
Arrays.asList(new String[]{"const", "function", "public", "static"}); //NOI18N
private static final List<String> INHERITANCE_KEYWORDS =
Expand Down Expand Up @@ -557,6 +576,7 @@ public CodeCompletionResult complete(CodeCompletionContext completionContext) {
break;
case VISIBILITY_MODIFIER_OR_TYPE_NAME: // no break
autoCompleteKeywords(completionResult, request, PHP_VISIBILITY_KEYWORDS);
autoCompleteKeywords(completionResult, request, PHP_SET_VISIBILITY_KEYWORDS);
autoCompleteKeywords(completionResult, request, Arrays.asList("readonly")); // NOI18N
case TYPE_NAME:
autoCompleteNamespaces(completionResult, request);
Expand Down Expand Up @@ -1301,7 +1321,30 @@ private void autoCompleteInClassContext(
TokenSequence<PHPTokenId> tokenSequence = th.tokenSequence(PHPTokenId.language());
assert tokenSequence != null;

boolean foundFunction = false;
boolean foundConst = false;
tokenSequence.move(caretOffset);
while (tokenSequence.moveNext()) {
Token<PHPTokenId> token = tokenSequence.token();
if (token.id() == PHPTokenId.PHP_LINE_COMMENT
|| TokenUtilities.indexOf(token.text(), '\n') != -1) {
break;
}
if (token.id() == PHPTokenId.PHP_FUNCTION) {
foundFunction = true;
break;
}
if (token.id() == PHPTokenId.PHP_CONST) {
foundConst = true;
break;
}
}

autoCompleteKeywords(completionResult, request, CLASS_CONTEXT_KEYWORD_PROPOSAL);
if (!foundConst && !foundFunction) {
autoCompleteKeywords(completionResult, request, PHP_SET_VISIBILITY_KEYWORDS);
autoCompleteKeywords(completionResult, request, PHP_ASYMMETRIC_VISIBILITY_KEYWORDS);
}
if (offerMagicAndInherited(tokenSequence, caretOffset, th)) {
EnclosingClass enclosingClass = findEnclosingClass(info, lexerToASTOffset(info, caretOffset));
if (enclosingClass != null) {
Expand Down Expand Up @@ -1387,15 +1430,24 @@ private void autoCompleteFieldType(ParserResult info, int caretOffset, final PHP
TokenSequence<PHPTokenId> tokenSequence = th.tokenSequence(PHPTokenId.language());
assert tokenSequence != null;
tokenSequence.move(caretOffset);
boolean addFinalKeyword = false;
boolean addStaticKeyword = false;
boolean addReadonlyKeyword = false;
boolean addVisibilityKeyword = false;
boolean addSetVisibilityKeyword = false;
if (!(!tokenSequence.moveNext() && !tokenSequence.movePrevious())) {
Token<PHPTokenId> token = tokenSequence.token();
int tokenIdOffset = tokenSequence.token().offset(th);
addFinalKeyword = !CompletionContextFinder.lineContainsAny(token, caretOffset - tokenIdOffset, tokenSequence, Arrays.asList(
PHPTokenId.PHP_FINAL,
PHPTokenId.PHP_OPERATOR // "|"
));
addStaticKeyword = !CompletionContextFinder.lineContainsAny(token, caretOffset - tokenIdOffset, tokenSequence, Arrays.asList(
PHPTokenId.PHP_STATIC,
PHPTokenId.PHP_READONLY,
PHPTokenId.PHP_READONLY, // cannot use with readonly
PHPTokenId.PHP_PUBLIC_SET, // cannot use with set visibility
PHPTokenId.PHP_PRIVATE_SET,
PHPTokenId.PHP_PROTECTED_SET,
PHPTokenId.PHP_OPERATOR // "|"
));
addReadonlyKeyword = !CompletionContextFinder.lineContainsAny(token, caretOffset - tokenIdOffset, tokenSequence, Arrays.asList(
Expand All @@ -1408,6 +1460,16 @@ private void autoCompleteFieldType(ParserResult info, int caretOffset, final PHP
PHPTokenId.PHP_PROTECTED,
PHPTokenId.PHP_OPERATOR // "|"
));
addSetVisibilityKeyword = !CompletionContextFinder.lineContainsAny(token, caretOffset - tokenIdOffset, tokenSequence, Arrays.asList(
PHPTokenId.PHP_PUBLIC_SET,
PHPTokenId.PHP_PRIVATE_SET,
PHPTokenId.PHP_PROTECTED_SET,
PHPTokenId.PHP_STATIC, // cannot use with static
PHPTokenId.PHP_OPERATOR // "|"
));
}
if (addFinalKeyword) {
keywords.add("final"); // NOI18N
}
if (addStaticKeyword) {
keywords.add("static"); // NOI18N
Expand All @@ -1418,6 +1480,12 @@ private void autoCompleteFieldType(ParserResult info, int caretOffset, final PHP
if (addVisibilityKeyword) {
keywords.addAll(PHP_VISIBILITY_KEYWORDS);
}
if (addSetVisibilityKeyword) {
keywords.addAll(PHP_SET_VISIBILITY_KEYWORDS);
}
if (addVisibilityKeyword && addSetVisibilityKeyword) {
keywords.addAll(PHP_ASYMMETRIC_VISIBILITY_KEYWORDS);
}
}
if (isNullableType) {
// ?false, ?true is OK since PHP 8.2
Expand All @@ -1439,6 +1507,10 @@ private boolean offerMagicAndInherited(TokenSequence<PHPTokenId> tokenSequence,
PHPTokenId.PHP_PRIVATE,
PHPTokenId.PHP_PUBLIC,
PHPTokenId.PHP_PROTECTED,
PHPTokenId.PHP_PRIVATE_SET,
PHPTokenId.PHP_PUBLIC_SET,
PHPTokenId.PHP_PROTECTED_SET,
PHPTokenId.PHP_FINAL,
PHPTokenId.PHP_ABSTRACT,
PHPTokenId.PHP_VAR,
PHPTokenId.PHP_STATIC,
Expand Down Expand Up @@ -2038,6 +2110,11 @@ private static boolean isInType(CompletionRequest request) {
return findEnclosingType(request.info, lexerToASTOffset(request.result, request.anchor)) != null;
}

private static boolean isInInterface(CompletionRequest request) {
EnclosingType enclosingType = findEnclosingType(request.info, lexerToASTOffset(request.result, request.anchor));
return enclosingType != null && enclosingType.isInterface();
}

@CheckForNull
private static NamespaceName findNamespaceName(ParserResult info, int offset) {
List<ASTNode> nodes = NavUtils.underCaret(info, offset);
Expand Down Expand Up @@ -2182,6 +2259,10 @@ private void autoCompleteExpression(final PHPCompletionResult completionResult,
autoCompleteNamespaces(completionResult, request);
List<String> defaultKeywords = new ArrayList<>(PHP_KEYWORDS.keySet());
defaultKeywords.remove("default =>"); // NOI18N
defaultKeywords.removeAll(PHP_ASYMMETRIC_VISIBILITY_KEYWORDS);
if (isInInterface(request)) {
defaultKeywords.removeAll(PHP_SET_VISIBILITY_KEYWORDS);
}
autoCompleteExpression(completionResult, request, defaultKeywords);
}

Expand Down Expand Up @@ -2801,6 +2882,8 @@ private interface EnclosingType {

boolean isEnumDeclaration();

boolean isInterface();

String extractTypeName();

//~ Factories
Expand All @@ -2822,6 +2905,11 @@ public boolean isEnumDeclaration() {
return typeDeclaration instanceof EnumDeclaration;
}

@Override
public boolean isInterface() {
return typeDeclaration instanceof InterfaceDeclaration;
}

@Override
public String extractTypeName() {
return CodeUtils.extractTypeName(typeDeclaration);
Expand All @@ -2846,6 +2934,11 @@ public boolean isEnumDeclaration() {
return tokenId == PHPTokenId.PHP_ENUM;
}

@Override
public boolean isInterface() {
return tokenId == PHPTokenId.PHP_INTERFACE;
}

@Override
public String extractTypeName() {
return typeName;
Expand All @@ -2871,6 +2964,11 @@ public boolean isEnumDeclaration() {
return false;
}

@Override
public boolean isInterface() {
return false;
}

@Override
public String extractTypeName() {
return CodeUtils.extractClassName(classInstanceCreation);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ priv|ate $fld = 0;
(QueryType=COMPLETION, prefixSearch=true, caseSensitive=true)
------------------------------------
KEYWORD private null
KEYWORD private(set) null
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ privat|e $afterDocComment = 3;
(QueryType=COMPLETION, prefixSearch=true, caseSensitive=true)
------------------------------------
KEYWORD private null
KEYWORD private(set) null
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,11 @@ KEYWORD new null
KEYWORD or null
KEYWORD print ''; Language Construct
KEYWORD private null
KEYWORD private(set) null
KEYWORD protected null
KEYWORD protected(set) null
KEYWORD public null
KEYWORD public(set) null
KEYWORD readonly null
KEYWORD require ''; Language Construct
KEYWORD require_once ''; Language Construct
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,11 @@ KEYWORD new null
KEYWORD or null
KEYWORD print ''; Language Construct
KEYWORD private null
KEYWORD private(set) null
KEYWORD protected null
KEYWORD protected(set) null
KEYWORD public null
KEYWORD public(set) null
KEYWORD readonly null
KEYWORD require ''; Language Construct
KEYWORD require_once ''; Language Construct
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,11 @@ KEYWORD new null
KEYWORD or null
KEYWORD print ''; Language Construct
KEYWORD private null
KEYWORD private(set) null
KEYWORD protected null
KEYWORD protected(set) null
KEYWORD public null
KEYWORD public(set) null
KEYWORD readonly null
KEYWORD require ''; Language Construct
KEYWORD require_once ''; Language Construct
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ VARIABLE ? $protectedTraitStaticPropert [PROTECTE Trait1855
VARIABLE ? protectedTraitProperty [PROTECTE Trait1855
------------------------------------
KEYWORD protected null
KEYWORD protected(set) null
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,11 @@ KEYWORD new null
KEYWORD or null
KEYWORD print ''; Language Construct
KEYWORD private null
KEYWORD private(set) null
KEYWORD protected null
KEYWORD protected(set) null
KEYWORD public null
KEYWORD public(set) null
KEYWORD readonly null
KEYWORD require ''; Language Construct
KEYWORD require_once ''; Language Construct
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ CONSTANT PRIVATE_CONSTANT "private cons [PRIVATE] Class1855
------------------------------------
KEYWORD print ''; Language Construct
KEYWORD private null
KEYWORD private(set) null
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,11 @@ KEYWORD new null
KEYWORD or null
KEYWORD print ''; Language Construct
KEYWORD private null
KEYWORD private(set) null
KEYWORD protected null
KEYWORD protected(set) null
KEYWORD public null
KEYWORD public(set) null
KEYWORD readonly null
KEYWORD require ''; Language Construct
KEYWORD require_once ''; Language Construct
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,14 @@ KEYWORD const null
KEYWORD final null
KEYWORD function null
KEYWORD private null
KEYWORD private(set) null
KEYWORD protected null
KEYWORD protected private(set) null
KEYWORD protected(set) null
KEYWORD public null
KEYWORD public private(set) null
KEYWORD public protected(set) null
KEYWORD public(set) null
KEYWORD readonly null
KEYWORD static null
KEYWORD var null
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,11 @@ KEYWORD new null
KEYWORD or null
KEYWORD print ''; Language Construct
KEYWORD private null
KEYWORD private(set) null
KEYWORD protected null
KEYWORD protected(set) null
KEYWORD public null
KEYWORD public(set) null
KEYWORD readonly null
KEYWORD require ''; Language Construct
KEYWORD require_once ''; Language Construct
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,11 @@ KEYWORD new null
KEYWORD or null
KEYWORD print ''; Language Construct
KEYWORD private null
KEYWORD private(set) null
KEYWORD protected null
KEYWORD protected(set) null
KEYWORD public null
KEYWORD public(set) null
KEYWORD readonly null
KEYWORD require ''; Language Construct
KEYWORD require_once ''; Language Construct
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,11 @@ KEYWORD new null
KEYWORD or null
KEYWORD print ''; Language Construct
KEYWORD private null
KEYWORD private(set) null
KEYWORD protected null
KEYWORD protected(set) null
KEYWORD public null
KEYWORD public(set) null
KEYWORD readonly null
KEYWORD require ''; Language Construct
KEYWORD require_once ''; Language Construct
Expand Down
Loading

0 comments on commit ce14c99

Please sign in to comment.