void () { if (SUPER_OR_THIS_PATTERN.accepts(myPosition)) { final boolean afterDot = AFTER_DOT.accepts(myPosition); final boolean insideQualifierClass = isInsideQualifierClass(); final boolean insideInheritorClass = HighlightingFeature.EXTENSION_METHODS.isAvailable(myPosition) && isInsideInheritorClass(); if (!afterDot || insideQualifierClass || insideInheritorClass) { if (!afterDot || insideQualifierClass) { addKeyword(createKeyword(PsiKeyword.THIS)); } final LookupElement superItem = createKeyword(PsiKeyword.SUPER); if (psiElement().afterLeaf(psiElement().withText("{").withSuperParent(2, psiMethod().constructor(true))).accepts(myPosition)) { final PsiMethod method = PsiTreeUtil.getParentOfType(myPosition, PsiMethod.class, false, PsiClass.class); assert method != null; final boolean hasParams = superConstructorHasParameters(method); addKeyword(LookupElementDecorator.withInsertHandler(superItem, new ParenthesesInsertHandler<LookupElement>() { @Override protected boolean placeCaretInsideParentheses(InsertionContext context, LookupElement item) { return hasParams; } @Override public void handleInsert(@NotNull InsertionContext context, @NotNull LookupElement item) { super.handleInsert(context, item); TailType.insertChar(context.getEditor(), context.getTailOffset(), ';'); } })); return; } addKeyword(superItem); } } } private void addExpressionKeywords(boolean statementPosition) { if (isExpressionPosition(myPosition)) { PsiElement parent = myPosition.getParent(); PsiElement grandParent = parent == null ? null : parent.getParent(); boolean allowExprKeywords = !(grandParent instanceof PsiExpressionStatement) && !(grandParent instanceof PsiUnaryExpression); if (PsiTreeUtil.getParentOfType(myPosition, PsiAnnotation.class) == null) { if (!statementPosition) { addKeyword(TailTypeDecorator.withTail(createKeyword(PsiKeyword.NEW), TailTypes.insertSpaceType())); if (HighlightingFeature.ENHANCED_SWITCH.isAvailable(myPosition)) { addKeyword(new OverridableSpace(createKeyword(PsiKeyword.SWITCH), JavaTailTypes.SWITCH_LPARENTH)); } } if (allowExprKeywords) { addKeyword(createKeyword(PsiKeyword.NULL)); } } if (allowExprKeywords && mayExpectBoolean(myParameters)) { addKeyword(createKeyword(PsiKeyword.TRUE)); addKeyword(createKeyword(PsiKeyword.FALSE)); } } if (isQualifiedNewContext()) { addKeyword(createKeyword(PsiKeyword.NEW)); } } private boolean isQualifiedNewContext() { if (myPosition.getParent() instanceof PsiReferenceExpression) { PsiExpression qualifier = ((PsiReferenceExpression)myPosition.getParent()).getQualifierExpression(); PsiClass qualifierClass = PsiUtil.resolveClassInClassTypeOnly(qualifier == null ? null : qualifier.getType()); return qualifierClass != null && ContainerUtil.exists(qualifierClass.getAllInnerClasses(), inner -> canBeCreatedInQualifiedNew(qualifierClass, inner)); } return false; } private boolean canBeCreatedInQualifiedNew(PsiClass outer, PsiClass inner) { PsiMethod[] constructors = inner.getConstructors(); return !inner.hasModifierProperty(PsiModifier.STATIC) && PsiUtil.isAccessible(inner, myPosition, outer) && (constructors.length == 0 || ContainerUtil.exists(constructors, c -> PsiUtil.isAccessible(c, myPosition, outer))); } private void addFileHeaderKeywords() { PsiFile file = myPosition.getContainingFile(); assert file != null; if (!(file instanceof PsiExpressionCodeFragment) && !(file instanceof PsiJavaCodeReferenceCodeFragment) && !(file instanceof PsiTypeCodeFragment)) { if (myPrevLeaf == null) { addKeyword(new OverridableSpace(createKeyword(PsiKeyword.PACKAGE), TailTypes.humbleSpaceBeforeWordType())); addKeyword(new OverridableSpace(createKeyword(PsiKeyword.IMPORT), TailTypes.humbleSpaceBeforeWordType())); } else if (psiElement().inside(psiAnnotation().withParents(PsiModifierList.class, PsiFile.class)).accepts(myPrevLeaf) && PsiPackage.PACKAGE_INFO_FILE.equals(file.getName())) { addKeyword(new OverridableSpace(createKeyword(PsiKeyword.PACKAGE), TailTypes.humbleSpaceBeforeWordType())); } else if (isEndOfBlock(myPosition) && PsiTreeUtil.getParentOfType(myPosition, PsiMember.class) == null) { addKeyword(new OverridableSpace(createKeyword(PsiKeyword.IMPORT), TailTypes.humbleSpaceBeforeWordType())); } } if (HighlightingFeature.STATIC_IMPORTS.isAvailable(file) && myPrevLeaf != null && myPrevLeaf.textMatches(PsiKeyword.IMPORT)) { addKeyword(new OverridableSpace(createKeyword(PsiKeyword.STATIC), TailTypes.humbleSpaceBeforeWordType())); } } private void addInstanceof() { if (isInstanceofPlace(myPosition)) { addKeyword(LookupElementDecorator.withInsertHandler( createKeyword(PsiKeyword.INSTANCEOF), (context, item) -> { TailType tailType = TailTypes.humbleSpaceBeforeWordType(); if (tailType.isApplicable(context)) { tailType.processTail(context.getEditor(), context.getTailOffset()); } if ('!' == context.getCompletionChar()) { context.setAddCompletionChar(false); } context.commitDocument(); PsiInstanceOfExpression expr = PsiTreeUtil.findElementOfClassAtOffset(context.getFile(), context.getStartOffset(), PsiInstanceOfExpression.class, false); if (expr != null) { PsiExpression operand = expr.getOperand(); if (operand instanceof PsiPrefixExpression && ((PsiPrefixExpression)operand).getOperationTokenType().equals(JavaTokenType.EXCL)) { PsiExpression negated = ((PsiPrefixExpression)operand).getOperand(); if (negated != null) { String space = CompletionStyleUtil.getCodeStyleSettings(context).SPACE_WITHIN_PARENTHESES ? " " : ""; context.getDocument().insertString(negated.getTextRange().getStartOffset(), "(" + space); context.getDocument().insertString(context.getTailOffset(), space + ")"); } } else if ('!' == context.getCompletionChar()) { String space = CompletionStyleUtil.getCodeStyleSettings(context).SPACE_WITHIN_PARENTHESES ? " " : ""; context.getDocument().insertString(expr.getTextRange().getStartOffset(), "!(" + space); context.getDocument().insertString(context.getTailOffset(), space + ")"); } } })); } } private void addClassKeywords() { if (isSuitableForClass(myPosition)) { for (String s : ModifierChooser.getKeywords(myPosition)) { addKeyword(new OverridableSpace(createKeyword(s), TailTypes.humbleSpaceBeforeWordType())); } if (psiElement().insideStarting(psiElement(PsiLocalVariable.class, PsiExpressionStatement.class)).accepts(myPosition)) { addKeyword(new OverridableSpace(createKeyword(PsiKeyword.CLASS), TailTypes.humbleSpaceBeforeWordType())); addKeyword(new OverridableSpace(LookupElementBuilder.create("abstract class").bold(), TailTypes.humbleSpaceBeforeWordType())); if (HighlightingFeature.RECORDS.isAvailable(myPosition)) { addKeyword(new OverridableSpace(createKeyword(PsiKeyword.RECORD), TailTypes.humbleSpaceBeforeWordType())); } if (HighlightingFeature.LOCAL_ENUMS.isAvailable(myPosition)) { addKeyword(new OverridableSpace(createKeyword(PsiKeyword.ENUM), TailTypes.humbleSpaceBeforeWordType())); } if (HighlightingFeature.LOCAL_INTERFACES.isAvailable(myPosition)) { addKeyword(new OverridableSpace(createKeyword(PsiKeyword.INTERFACE), TailTypes.humbleSpaceBeforeWordType())); } } if (PsiTreeUtil.getParentOfType(myPosition, PsiExpression.class, true, PsiMember.class) == null && PsiTreeUtil.getParentOfType(myPosition, PsiCodeBlock.class, true, PsiMember.class) == null) { List<String> keywords = new ArrayList<>(); keywords.add(PsiKeyword.CLASS); keywords.add(PsiKeyword.INTERFACE); if (HighlightingFeature.RECORDS.isAvailable(myPosition)) { keywords.add(PsiKeyword.RECORD); } if (PsiUtil.isLanguageLevel5OrHigher(myPosition)) { keywords.add(PsiKeyword.ENUM); } String className = recommendClassName(); for (String keyword : keywords) { if (className == null) { addKeyword(new OverridableSpace(createKeyword(keyword), TailTypes.humbleSpaceBeforeWordType())); } else { addKeyword(createTypeDeclaration(keyword, className)); } } } } if (psiElement().withText("@").andNot(psiElement().inside(PsiParameterList.class)).andNot(psiElement().inside(psiNameValuePair())) .accepts(myPrevLeaf)) { addKeyword(new OverridableSpace(createKeyword(PsiKeyword.INTERFACE), TailTypes.humbleSpaceBeforeWordType())); } } @NotNull private LookupElement createTypeDeclaration(String keyword, String className) { LookupElement element; PsiElement nextElement = PsiTreeUtil.skipWhitespacesAndCommentsForward(PsiTreeUtil.nextLeaf(myPosition)); IElementType nextToken; if (nextElement instanceof PsiJavaToken) { nextToken = ((PsiJavaToken)nextElement).getTokenType(); } else { if (nextElement instanceof PsiParameterList l && l.getFirstChild() instanceof PsiJavaToken t) { nextToken = t.getTokenType(); } else if (nextElement instanceof PsiCodeBlock b && b.getFirstChild() instanceof PsiJavaToken t) { nextToken = t.getTokenType(); } else { nextToken = null; } } element = LookupElementBuilder.create(keyword + " " + className).withPresentableText(keyword).bold() .withTailText(" " + className, false) .withIcon(CreateClassKind.valueOf(keyword.toUpperCase(Locale.ROOT)).getKindIcon()) .withInsertHandler((context, item) -> { Document document = context.getDocument(); int offset = context.getTailOffset(); String suffix = " "; if (keyword.equals(PsiKeyword.RECORD)) { if (JavaTokenType.LPARENTH.equals(nextToken)) { suffix = ""; } else if (JavaTokenType.LBRACE.equals(nextToken)) { suffix = "() "; } else { suffix = "() {\n}"; } } else if (!JavaTokenType.LBRACE.equals(nextToken)) { suffix = " {\n}"; } if (offset < document.getTextLength() && document.getCharsSequence().charAt(offset) == ' ') { suffix = suffix.trim(); } document.insertString(offset, suffix); context.getEditor().getCaretModel().moveToOffset(offset + 1); }); return element; } @Nullable private String recommendClassName() { if (myPrevLeaf == null) return null; if (!myPrevLeaf.textMatches(PsiKeyword.PUBLIC) || !(myPrevLeaf.getParent() instanceof PsiModifierList)) return null; if (nextIsIdentifier(myPosition)) return null; PsiJavaFile file = getFileForDeclaration(myPrevLeaf); if (file == null) return null; String name = file.getName(); if (!StringUtil.endsWithIgnoreCase(name, JavaFileType.DOT_DEFAULT_EXTENSION)) return null; String candidate = name.substring(0, name.length() - JavaFileType.DOT_DEFAULT_EXTENSION.length()); if (StringUtil.isJavaIdentifier(candidate) && !ContainerUtil.exists(file.getClasses(), c -> candidate.equals(c.getName()))) { return candidate; } return null; } private static boolean nextIsIdentifier(@NotNull PsiElement position) { PsiElement nextLeaf = PsiTreeUtil.nextLeaf(position); if (nextLeaf == null) return false; PsiElement parent = nextLeaf.getParent(); if (!(parent instanceof PsiJavaCodeReferenceElement)) return false; PsiElement grandParent = parent.getParent(); if (!(grandParent instanceof PsiTypeElement)) return false; return PsiTreeUtil.skipWhitespacesAndCommentsForward(grandParent) instanceof PsiIdentifier; } @Nullable private static PsiJavaFile getFileForDeclaration(@NotNull PsiElement elementBeforeName) { PsiElement parent = elementBeforeName.getParent(); if (parent == null) return null; PsiElement grandParent = parent.getParent(); if (grandParent == null) return null; if (grandParent instanceof PsiJavaFile f) { return f; } PsiElement grandGrandParent = grandParent.getParent(); if (grandGrandParent == null) return null; return ObjectUtils.tryCast(grandGrandParent.getParent(), PsiJavaFile.class); } private void addClassLiteral() { if (isAfterTypeDot(myPosition)) { addKeyword(createKeyword(PsiKeyword.CLASS)); } } private void addExtendsImplements() { if (myPrevLeaf == null || !(myPrevLeaf instanceof PsiIdentifier || myPrevLeaf.textMatches(">") || myPrevLeaf.textMatches(")"))) { return; } PsiClass psiClass = null; PsiElement prevParent = myPrevLeaf.getParent(); if (myPrevLeaf instanceof PsiIdentifier && prevParent instanceof PsiClass) { psiClass = (PsiClass)prevParent; } else { PsiReferenceList referenceList = PsiTreeUtil.getParentOfType(myPrevLeaf, PsiReferenceList.class); if (referenceList != null && referenceList.getParent() instanceof PsiClass) { psiClass = (PsiClass)referenceList.getParent(); } else if ((prevParent instanceof PsiTypeParameterList || prevParent instanceof PsiRecordHeader) && prevParent.getParent() instanceof PsiClass) { psiClass = (PsiClass)prevParent.getParent(); } } if (psiClass != null) { if (!psiClass.isEnum() && !psiClass.isRecord()) { addKeyword(new OverridableSpace(createKeyword(PsiKeyword.EXTENDS), TailTypes.humbleSpaceBeforeWordType())); if (HighlightingFeature.SEALED_CLASSES.isAvailable(psiClass)) { PsiModifierList modifiers = psiClass.getModifierList(); if (myParameters.getInvocationCount() > 1 || (modifiers != null && !modifiers.hasExplicitModifier(PsiModifier.FINAL) && !modifiers.hasExplicitModifier(PsiModifier.NON_SEALED))) { InsertHandler<LookupElement> handler = (context, item) -> { PsiClass aClass = PsiTreeUtil.findElementOfClassAtOffset(context.getFile(), context.getStartOffset(), PsiClass.class, false); if (aClass != null) { PsiModifierList modifierList = aClass.getModifierList(); if (modifierList != null) { modifierList.setModifierProperty(PsiModifier.SEALED, true); } } }; LookupElement element = new OverridableSpace(LookupElementDecorator.withInsertHandler(createKeyword(PsiKeyword.PERMITS), handler), TailTypes.humbleSpaceBeforeWordType()); addKeyword(element); } } } if (!psiClass.isInterface()) { addKeyword(new OverridableSpace(createKeyword(PsiKeyword.IMPLEMENTS), TailTypes.humbleSpaceBeforeWordType())); } } } private static boolean mayExpectBoolean(CompletionParameters parameters) { for (ExpectedTypeInfo info : JavaSmartCompletionContributor.getExpectedTypes(parameters)) { PsiType type = info.getType(); if (type instanceof PsiClassType || PsiTypes.booleanType().equals(type)) return true; } return false; } private static boolean isExpressionPosition(PsiElement position) { if (psiElement().insideStarting(psiElement(PsiClassObjectAccessExpression.class)).accepts(position)) return true; PsiElement parent = position.getParent(); if (!(parent instanceof PsiReferenceExpression) || ((PsiReferenceExpression)parent).isQualified() || JavaCompletionContributor.IN_SWITCH_LABEL.accepts(position)) { return false; } if (parent.getParent() instanceof PsiExpressionStatement) { PsiElement previous = PsiTreeUtil.skipWhitespacesBackward(parent.getParent()); return previous == null || previous.getLastChild() == null || !(PsiTreeUtil.getDeepestLast(previous.getLastChild()) instanceof PsiErrorElement); } return true; } public static boolean isInstanceofPlace(PsiElement position) { PsiElement prev = PsiTreeUtil.prevVisibleLeaf(position); if (prev == null) return false; PsiElement expr = PsiTreeUtil.getParentOfType(prev, PsiExpression.class); if (expr != null && expr.getTextRange().getEndOffset() == prev.getTextRange().getEndOffset() && PsiTreeUtil.getParentOfType(expr, PsiAnnotation.class) == null) { return true; } if (position instanceof PsiIdentifier && position.getParent() instanceof PsiLocalVariable) { PsiType type = ((PsiLocalVariable)position.getParent()).getType(); if (type instanceof PsiClassType && ((PsiClassType)type).resolve() == null) { PsiElement grandParent = position.getParent().getParent(); return !(grandParent instanceof PsiDeclarationStatement) || !(grandParent.getParent() instanceof PsiForStatement) || ((PsiForStatement)grandParent.getParent()).getInitialization() != grandParent; } } return false; } public static boolean isSuitableForClass(PsiElement position) { if (psiElement().afterLeaf("@").accepts(position) || PsiTreeUtil.getNonStrictParentOfType(position, PsiLiteralExpression.class, PsiComment.class, PsiExpressionCodeFragment.class) != null) { return false; } PsiElement prev = prevSignificantLeaf(position); if (prev == null) { return true; } if (psiElement().withoutText(".").inside( psiElement(PsiModifierList.class).withParent( not(psiElement(PsiParameter.class)).andNot(psiElement(PsiParameterList.class)))).accepts(prev) && (!psiElement().inside(PsiAnnotationParameterList.class).accepts(prev) || prev.textMatches(")"))) { return true; } if (psiElement().withParents(PsiErrorElement.class, PsiFile.class).accepts(position)) { return true; } return isEndOfBlock(position); } static boolean isAfterPrimitiveOrArrayType(PsiElement element) { return psiElement().withParent( psiReferenceExpression().withFirstChild( psiElement(PsiClassObjectAccessExpression.class).withLastChild( not(psiElement().withText(PsiKeyword.CLASS))))).accepts(element); } static boolean isAfterTypeDot(PsiElement position) { if (isInsideParameterList(position) || position.getContainingFile() instanceof PsiJavaCodeReferenceCodeFragment) { return false; } return psiElement().afterLeaf(psiElement().withText(".").afterLeaf(CLASS_REFERENCE)).accepts(position) || isAfterPrimitiveOrArrayType(position); } static void addPrimitiveTypes(Consumer<? super LookupElement> result, PsiElement position, JavaCompletionSession session) { if (AFTER_DOT.accepts(position) || psiElement().inside(psiAnnotation()).accepts(position) && !expectsClassLiteral(position)) { return; } boolean afterNew = JavaSmartCompletionContributor.AFTER_NEW.accepts(position) && !psiElement().afterLeaf(psiElement().afterLeaf(".")).accepts(position); if (afterNew) { Set<PsiType> expected = ContainerUtil.map2Set(JavaSmartCompletionContributor.getExpectedTypes(position, false), ExpectedTypeInfo::getDefaultType); boolean addAll = expected.isEmpty() || ContainerUtil.exists(expected, t -> t.equalsToText(CommonClassNames.JAVA_LANG_OBJECT) || t.equalsToText(CommonClassNames.JAVA_IO_SERIALIZABLE)); PsiElementFactory factory = JavaPsiFacade.getElementFactory(position.getProject()); for (String primitiveType : PRIMITIVE_TYPES) { PsiType array = factory.createTypeFromText(primitiveType + "[]", null); if (addAll || expected.contains(array)) { result.consume(PsiTypeLookupItem.createLookupItem(array, null)); } } return; } boolean inCast = psiElement() .afterLeaf(psiElement().withText("(").withParent(psiElement(PsiParenthesizedExpression.class, PsiTypeCastExpression.class))) .accepts(position); boolean typeFragment = position.getContainingFile() instanceof PsiTypeCodeFragment && PsiTreeUtil.prevVisibleLeaf(position) == null; boolean declaration = isDeclarationStart(position); boolean expressionPosition = isExpressionPosition(position); boolean inGenerics = PsiTreeUtil.getParentOfType(position, PsiReferenceParameterList.class) != null; if (isVariableTypePosition(position) || inGenerics || inCast || declaration || typeFragment || expressionPosition) { for (String primitiveType : PRIMITIVE_TYPES) { if (!session.isKeywordAlreadyProcessed(primitiveType)) { result.consume(BasicExpressionCompletionContributor.createKeywordLookupItem(position, primitiveType)); } } if (expressionPosition && !session.isKeywordAlreadyProcessed(PsiKeyword.VOID)) { result.consume(BasicExpressionCompletionContributor.createKeywordLookupItem(position, PsiKeyword.VOID)); } } if (declaration) { LookupElement item = BasicExpressionCompletionContributor.createKeywordLookupItem(position, PsiKeyword.VOID); result.consume(new OverridableSpace(item, TailTypes.humbleSpaceBeforeWordType())); } else if (typeFragment && ((PsiTypeCodeFragment)position.getContainingFile()).isVoidValid()) { result.consume(BasicExpressionCompletionContributor.createKeywordLookupItem(position, PsiKeyword.VOID)); } } private static boolean isVariableTypePosition(PsiElement position) { PsiElement parent = position.getParent(); if (parent instanceof PsiJavaCodeReferenceElement && parent.getParent() instanceof PsiTypeElement && parent.getParent().getParent() instanceof PsiDeclarationStatement) { return true; } return START_FOR.accepts(position) || isInsideParameterList(position) || INSIDE_RECORD_HEADER.accepts(position) || VARIABLE_AFTER_FINAL.accepts(position) || isStatementPosition(position); } static boolean isDeclarationStart(@NotNull PsiElement position) { if (psiElement().afterLeaf("@", ".").accepts(position)) return false; PsiElement parent = position.getParent(); if (parent instanceof PsiJavaCodeReferenceElement && parent.getParent() instanceof PsiTypeElement) { PsiElement typeHolder = psiApi().parents(parent.getParent()).skipWhile(Conditions.instanceOf(PsiTypeElement.class)).first(); return typeHolder instanceof PsiMember || typeHolder instanceof PsiClassLevelDeclarationStatement; } return false; } private static boolean expectsClassLiteral(PsiElement position) { return ContainerUtil.find(JavaSmartCompletionContributor.getExpectedTypes(position, false), info -> InheritanceUtil.isInheritor(info.getType(), CommonClassNames.JAVA_LANG_CLASS)) != null; } private static boolean isAtCatchOrResourceVariableStart(PsiElement position) { PsiElement type = PsiTreeUtil.getParentOfType(position, PsiTypeElement.class); if (type != null && type.getTextRange().getStartOffset() == position.getTextRange().getStartOffset()) { PsiElement parent = type.getParent(); if (parent instanceof PsiVariable) parent = parent.getParent(); return parent instanceof PsiCatchSection || parent instanceof PsiResourceList; } return psiElement().insideStarting(psiElement(PsiResourceExpression.class)).accepts(position); } private void addBreakContinue() { PsiLoopStatement loop = PsiTreeUtil.getParentOfType(myPosition, PsiLoopStatement.class, true, PsiLambdaExpression.class, PsiMember.class); LookupElement br = createKeyword(PsiKeyword.BREAK); LookupElement cont = createKeyword(PsiKeyword.CONTINUE); TailType tailType; if (psiElement().insideSequence(true, psiElement(PsiLabeledStatement.class), or(psiElement(PsiFile.class), psiElement(PsiMethod.class), psiElement(PsiClassInitializer.class))).accepts(myPosition)) { tailType = TailTypes.humbleSpaceBeforeWordType(); } else { tailType = TailTypes.semicolonType(); } br = TailTypeDecorator.withTail(br, tailType); cont = TailTypeDecorator.withTail(cont, tailType); if (loop != null && PsiTreeUtil.isAncestor(loop.getBody(), myPosition, false)) { addKeyword(br); addKeyword(cont); } if (psiElement().inside(PsiSwitchStatement.class).accepts(myPosition)) { addKeyword(br); } else if (psiElement().inside(PsiSwitchExpression.class).accepts(myPosition) && HighlightingFeature.SWITCH_EXPRESSION.isAvailable(myPosition)) { addKeyword(TailTypeDecorator.withTail(createKeyword(PsiKeyword.YIELD), TailTypes.insertSpaceType())); } for (PsiLabeledStatement labeled : psiApi().parents(myPosition).takeWhile(notInstanceOf(PsiMember.class)) .filter(PsiLabeledStatement.class)) { addKeyword(TailTypeDecorator.withTail(LookupElementBuilder.create("break " + labeled.getName()).bold(), TailTypes.semicolonType())); } } private static boolean isStatementPosition(PsiElement position) { if (psiElement() .withSuperParent(2, PsiConditionalExpression.class) .andNot(psiElement().insideStarting(psiElement(PsiConditionalExpression.class))) .accepts(position)) { return false; } if (isEndOfBlock(position) && PsiTreeUtil.getParentOfType(position, PsiCodeBlock.class, true, PsiMember.class) != null) { return !isForLoopMachinery(position); } if (psiElement().withParents(PsiReferenceExpression.class, PsiExpressionStatement.class, PsiIfStatement.class).andNot( psiElement().afterLeaf(".")).accepts(position)) { PsiElement stmt = position.getParent().getParent(); PsiIfStatement ifStatement = (PsiIfStatement)stmt.getParent(); return ifStatement.getElseBranch() == stmt || ifStatement.getThenBranch() == stmt; } return false; } private static boolean isForLoopMachinery(PsiElement position) { PsiStatement statement = PsiTreeUtil.getParentOfType(position, PsiStatement.class); if (statement == null) return false; return statement instanceof PsiForStatement || statement.getParent() instanceof PsiForStatement && statement != ((PsiForStatement)statement.getParent()).getBody(); } private LookupElement createKeyword(String keyword) { return BasicExpressionCompletionContributor.createKeywordLookupItem(myPosition, keyword); } private boolean isInsideQualifierClass() { if (myPosition.getParent() instanceof PsiJavaCodeReferenceElement) { final PsiElement qualifier = ((PsiJavaCodeReferenceElement)myPosition.getParent()).getQualifier(); if (qualifier instanceof PsiJavaCodeReferenceElement) { final PsiElement qualifierClass = ((PsiJavaCodeReferenceElement)qualifier).resolve(); if (qualifierClass instanceof PsiClass) { PsiElement parent = myPosition; final PsiManager psiManager = myPosition.getManager(); while ((parent = PsiTreeUtil.getParentOfType(parent, PsiClass.class, true)) != null) { if (psiManager.areElementsEquivalent(parent, qualifierClass)) { return true; } } } } } return false; } private boolean isInsideInheritorClass() { if (myPosition.getParent() instanceof PsiJavaCodeReferenceElement) { final PsiElement qualifier = ((PsiJavaCodeReferenceElement)myPosition.getParent()).getQualifier(); if (qualifier instanceof PsiJavaCodeReferenceElement) { final PsiElement qualifierClass = ((PsiJavaCodeReferenceElement)qualifier).resolve(); if (qualifierClass instanceof PsiClass && ((PsiClass)qualifierClass).isInterface()) { PsiElement parent = myPosition; while ((parent = PsiTreeUtil.getParentOfType(parent, PsiClass.class, true)) != null) { if (PsiUtil.getEnclosingStaticElement(myPosition, (PsiClass)parent) == null && ((PsiClass)parent).isInheritor((PsiClass)qualifierClass, true)) { return true; } } } } } return false; } private static boolean superConstructorHasParameters(PsiMethod method) { final PsiClass psiClass = method.getContainingClass(); if (psiClass == null) { return false; } final PsiClass superClass = psiClass.getSuperClass(); if (superClass != null) { for (final PsiMethod psiMethod : superClass.getConstructors()) { final PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(method.getProject()).getResolveHelper(); if (resolveHelper.isAccessible(psiMethod, method, null) && !psiMethod.getParameterList().isEmpty()) { return true; } } } return false; } private void addModuleKeywords() { PsiElement context = PsiTreeUtil.skipParentsOfType(myPosition.getParent(), PsiErrorElement.class, PsiJavaCodeReferenceElement.class, PsiTypeElement.class); PsiElement prevElement = PsiTreeUtil.skipWhitespacesAndCommentsBackward(myPosition.getParent()); if (context instanceof PsiJavaFile && !(prevElement instanceof PsiJavaModule) || context instanceof PsiImportList) { addKeyword(new OverridableSpace(createKeyword(PsiKeyword.MODULE), TailTypes.humbleSpaceBeforeWordType())); if (myPrevLeaf == null || !myPrevLeaf.textMatches(PsiKeyword.OPEN)) { addKeyword(new OverridableSpace(createKeyword(PsiKeyword.OPEN), TailTypes.humbleSpaceBeforeWordType())); } } else if (context instanceof PsiJavaModule) { if (prevElement instanceof PsiPackageAccessibilityStatement && !myPrevLeaf.textMatches(";")) { addKeyword(new OverridableSpace(createKeyword(PsiKeyword.TO), TailTypes.humbleSpaceBeforeWordType())); } else if (!PsiUtil.isJavaToken(prevElement, JavaTokenType.MODULE_KEYWORD)) { addKeyword(new OverridableSpace(createKeyword(PsiKeyword.REQUIRES), TailTypes.humbleSpaceBeforeWordType())); addKeyword(new OverridableSpace(createKeyword(PsiKeyword.EXPORTS), TailTypes.humbleSpaceBeforeWordType())); addKeyword(new OverridableSpace(createKeyword(PsiKeyword.OPENS), TailTypes.humbleSpaceBeforeWordType())); addKeyword(new OverridableSpace(createKeyword(PsiKeyword.USES), TailTypes.humbleSpaceBeforeWordType())); addKeyword(new OverridableSpace(createKeyword(PsiKeyword.PROVIDES), TailTypes.humbleSpaceBeforeWordType())); } } else if (context instanceof PsiRequiresStatement) { if (!myPrevLeaf.textMatches(PsiKeyword.TRANSITIVE)) { addKeyword(new OverridableSpace(createKeyword(PsiKeyword.TRANSITIVE), TailTypes.humbleSpaceBeforeWordType())); } if (!myPrevLeaf.textMatches(PsiKeyword.STATIC)) { addKeyword(new OverridableSpace(createKeyword(PsiKeyword.STATIC), TailTypes.humbleSpaceBeforeWordType())); } } else if (context instanceof PsiProvidesStatement && prevElement instanceof PsiJavaCodeReferenceElement) { addKeyword(new OverridableSpace(createKeyword(PsiKeyword.WITH), TailTypes.humbleSpaceBeforeWordType())); } } public static class OverridableSpace extends TailTypeDecorator<LookupElement> { private final TailType myTail; public OverridableSpace(LookupElement keyword, TailType tail) { super(keyword); myTail = tail; } @Override protected TailType computeTailType(InsertionContext context) { return context.shouldAddCompletionChar() ? TailTypes.noneType() : myTail; } } }