diff --git a/src/main/java/com/intellij/plugins/haxe/ide/completion/HaxeKeywordCompletionContributor.java b/src/main/java/com/intellij/plugins/haxe/ide/completion/HaxeKeywordCompletionContributor.java index 05383e587..cf0cafb6d 100644 --- a/src/main/java/com/intellij/plugins/haxe/ide/completion/HaxeKeywordCompletionContributor.java +++ b/src/main/java/com/intellij/plugins/haxe/ide/completion/HaxeKeywordCompletionContributor.java @@ -19,33 +19,30 @@ package com.intellij.plugins.haxe.ide.completion; import com.intellij.codeInsight.completion.*; -import com.intellij.codeInsight.lookup.LookupElementBuilder; -import com.intellij.lang.parser.GeneratedParserUtilBase; -import com.intellij.openapi.util.Pair; +import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.openapi.util.TextRange; import com.intellij.patterns.PsiElementPattern; import com.intellij.patterns.StandardPatterns; import com.intellij.plugins.haxe.HaxeLanguage; import com.intellij.plugins.haxe.lang.lexer.HaxeTokenTypeSets; -import com.intellij.plugins.haxe.lang.lexer.HaxeTokenTypes; import com.intellij.plugins.haxe.lang.psi.*; -import com.intellij.plugins.haxe.util.HaxeCodeGenerateUtil; -import com.intellij.plugins.haxe.util.UsefulPsiTreeUtil; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiErrorElement; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiFileFactory; +import com.intellij.plugins.haxe.util.HaxeElementGenerator; +import com.intellij.psi.*; import com.intellij.psi.impl.source.tree.TreeUtil; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.ProcessingContext; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.util.*; import static com.intellij.patterns.PlatformPatterns.psiElement; import static com.intellij.plugins.haxe.ide.completion.HaxeCommonCompletionPattern.*; +import static com.intellij.plugins.haxe.ide.completion.HaxeKeywordCompletionPatterns.*; +import static com.intellij.plugins.haxe.ide.completion.HaxeKeywordCompletionUtil.*; +import static com.intellij.plugins.haxe.lang.lexer.HaxeTokenTypeSets.PROPERTY_GET; +import static com.intellij.plugins.haxe.lang.lexer.HaxeTokenTypeSets.PROPERTY_SET; +import static com.intellij.plugins.haxe.lang.lexer.HaxeTokenTypes.*; /** * @author: Fedor.Korotkov @@ -67,109 +64,136 @@ public HaxeKeywordCompletionContributor() { psiElement().inFile(StandardPatterns.instanceOf(HaxeFile.class)) .withSuperParent(1, PsiErrorElement.class).and(psiElement().withSuperParent(2, HaxeInheritList.class)); - extend(CompletionType.BASIC, - psiElement().andOr(psiElement().withSuperParent(1, PsiErrorElement.class), - psiElement().withSuperParent(1, GeneratedParserUtilBase.DummyBlock.class)). - andOr(psiElement().withSuperParent(2, HaxeClassBody.class), psiElement().withSuperParent(2, HaxeInheritList.class)), - new CompletionProvider() { - @Override - protected void addCompletions(@NotNull CompletionParameters parameters, - ProcessingContext context, - @NotNull CompletionResultSet result) { - result.addElement(LookupElementBuilder.create("extends")); - result.addElement(LookupElementBuilder.create("implements")); - } - }); - - //TODO mlo: this is not working as the fake identifier created by intellij makes the switch case into a broken code block element. - extend(CompletionType.BASIC, - psiElement() - .inside(HaxeSwitchBlock.class) - .andNot(psiElement().inside(HaxeSwitchCase.class)), - new CompletionProvider<>() { - @Override - protected void addCompletions(@NotNull CompletionParameters parameters, - ProcessingContext context, - @NotNull CompletionResultSet result) { - result.addElement(PrioritizedLookupElement.withPriority(LookupElementBuilder.create("case"), 1.2)); - result.addElement(PrioritizedLookupElement.withPriority(LookupElementBuilder.create("default"), 1.1)); - } - }); - // foo.b - bad // i - good extend(CompletionType.BASIC, - psiElement().inFile(StandardPatterns.instanceOf(HaxeFile.class)).andNot(idInExpression.and(inComplexExpression)) - .andNot(inheritPattern), - new CompletionProvider() { + psiElement().inFile(StandardPatterns.instanceOf(HaxeFile.class)).andNot(idInExpression.and(inComplexExpression)), + new CompletionProvider<>() { @Override protected void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext context, @NotNull CompletionResultSet result) { - final Collection suggestedKeywords = suggestKeywords(parameters.getPosition()); - suggestedKeywords.retainAll(allowedKeywords); - for (String keyword : suggestedKeywords) { - result.addElement(LookupElementBuilder.create(keyword)); - } + suggestKeywords(parameters.getPosition(), result, context); } }); } - private static Collection suggestKeywords(PsiElement position) { - final TextRange posRange = position.getTextRange(); - final HaxeFile posFile = (HaxeFile)position.getContainingFile(); + private static void suggestKeywords(PsiElement position, @NotNull CompletionResultSet result, ProcessingContext context) { + List keywordsFromParser = new ArrayList<>(); + final HaxeFile cloneFile = createCopyWithFakeIdentifierAsComment(position, keywordsFromParser); + PsiElement completionElementAsComment = cloneFile.findElementAt(position.getTextOffset()); + + List lookupElements = new ArrayList<>(); + - final List pathToBlockStatement = UsefulPsiTreeUtil.getPathToParentOfType(position, HaxeBlockStatement.class); + if (packageExpected.accepts(completionElementAsComment)) { + addKeywords(lookupElements, PACKAGE_KEYWORD); + return; + } - final HaxePsiCompositeElement classInterfaceEnum = - PsiTreeUtil.getParentOfType(position, HaxeClassBody.class, HaxeInterfaceBody.class, HaxeEnumBody.class); + if (toplevelScope.accepts(completionElementAsComment)) { + addKeywords(lookupElements, TOP_LEVEL_KEYWORDS); + } - final String text; - final int offset; - if (pathToBlockStatement != null) { - final Pair pair = HaxeCodeGenerateUtil.wrapStatement(posRange.substring(posFile.getText())); - text = pair.getFirst(); - offset = pair.getSecond(); + if (moduleScope.accepts(completionElementAsComment)) { + addKeywords(lookupElements, MODULE_STRUCTURES_KEYWORDS); + addKeywords(lookupElements, VISIBILITY_KEYWORDS); } - else if (classInterfaceEnum != null) { - final Pair pair = HaxeCodeGenerateUtil.wrapFunction(posRange.substring(posFile.getText())); - text = pair.getFirst(); - offset = pair.getSecond(); + + if (classDeclarationScope.accepts(completionElementAsComment)) { + addKeywords(lookupElements, CLASS_DEFINITION_KEYWORDS); } - else { - text = posFile.getText().substring(0, posRange.getStartOffset()); - offset = 0; + if (interfaceDeclarationScope.accepts(completionElementAsComment)) { + addKeywords(lookupElements, INTERFACE_DEFINITION_KEYWORDS); } - final List result = new ArrayList(); - if (pathToBlockStatement != null && pathToBlockStatement.size() > 1) { - final PsiElement blockChild = pathToBlockStatement.get(pathToBlockStatement.size() - 2); - result.addAll(suggestBySibling(UsefulPsiTreeUtil.getPrevSiblingSkipWhiteSpacesAndComments(blockChild, true))); + if (abstractTypeDeclarationScope.accepts(completionElementAsComment)) { + addKeywords(lookupElements, ABSTRACT_DEFINITION_KEYWORDS); } - PsiFile file = PsiFileFactory.getInstance(posFile.getProject()).createFileFromText("a.hx", HaxeLanguage.INSTANCE, text, true, false); - GeneratedParserUtilBase.CompletionState state = new GeneratedParserUtilBase.CompletionState(text.length() - offset); - file.putUserData(GeneratedParserUtilBase.COMPLETION_STATE_KEY, state); - TreeUtil.ensureParsed(file.getNode()); - result.addAll(state.items); - - // always - result.add(HaxeTokenTypes.PPIF.toString()); - result.add(HaxeTokenTypes.PPELSE.toString()); - result.add(HaxeTokenTypes.PPELSEIF.toString()); - result.add(HaxeTokenTypes.PPERROR.toString()); - return result; - } + if (interfaceBodyScope.accepts(completionElementAsComment)) { + addKeywords(lookupElements, INTERFACE_BODY_KEYWORDS); + } - @NotNull - private static Collection suggestBySibling(@Nullable PsiElement sibling) { - if (HaxeIfStatement.class.isInstance(sibling)) { - return Collections.singletonList(HaxeTokenTypes.KELSE.toString()); + if (classBodyScope.accepts(completionElementAsComment)) { + addKeywords(lookupElements, CLASS_BODY_KEYWORDS); + addKeywords(lookupElements, VISIBILITY_KEYWORDS); + addKeywords(lookupElements, ACCESSIBILITY_KEYWORDS); } - else if (HaxeTryStatement.class.isInstance(sibling) || HaxeCatchStatement.class.isInstance(sibling)) { - return Collections.singletonList(HaxeTokenTypes.KCATCH.toString()); + + if (functionBodyScope.accepts(completionElementAsComment)) { + addKeywords(lookupElements, METHOD_BODY_KEYWORDS); } - return Collections.emptyList(); + if (insideSwitchCase.accepts(completionElementAsComment)) { + addKeywords(lookupElements, SWITCH_BODY_KEYWORDS); + } + + if (isAfterIfStatement.accepts(completionElementAsComment)) { + addKeywords(lookupElements, Set.of(KELSE)); + } + + if (isInsideLoopBlock.accepts(completionElementAsComment)) { + addKeywords(lookupElements, LOOP_BODY_KEYWORDS); + } + + if (isInsideForIterator.accepts(completionElementAsComment)) { + addKeywords(lookupElements, LOOP_ITERATOR_KEYWORDS); + } + + HaxePropertyAccessor propertyAccessor = PsiTreeUtil.getParentOfType(position, HaxePropertyAccessor.class); + if (isPropertyGetterValue.accepts(propertyAccessor)) { + result.stopHere(); + lookupElements.clear(); + addKeywords(lookupElements, PROPERTY_KEYWORDS, 1.1f); + addKeywords(lookupElements, Set.of(PROPERTY_GET), 1.2f); + } + if (isPropertySetterValue.accepts(propertyAccessor)) { + result.stopHere(); + lookupElements.clear(); + addKeywords(lookupElements, PROPERTY_KEYWORDS, 1.1f); + addKeywords(lookupElements, Set.of(PROPERTY_SET), 1.2f); + } + + + + addKeywords(lookupElements, Set.of(KMACRO2, KUNTYPED), -0.1f); + addKeywords(lookupElements, PP_KEYWORDS, -0.2f, false, true); + + + result.addAllElements(lookupElements); + } + + + + private static HaxeFile createCopyWithFakeIdentifierAsComment(PsiElement position, List keywordsFromParser) { + + final HaxeFile posFile = (HaxeFile)position.getContainingFile(); + final TextRange posRange = position.getTextRange(); + + // clone original content + HaxeFile clonedFile = (HaxeFile)posFile.copy(); + int offset = posRange.getStartOffset(); + + // replace dummy identifier with comment so it does not affect the parsing and psi structure + PsiElement dummyIdentifier = clonedFile.findElementAt(offset); + PsiElement comment = HaxeElementGenerator.createDummyComment(posFile.getProject(), dummyIdentifier.getTextLength()); + PsiElement elementToReplace = dummyIdentifier; + + //make sure we replace the "root" element of the identifier + // we dont want to replace identifier inside a reference and keep the reference etc. + while (elementToReplace.getPrevSibling() == null && elementToReplace.getParent() != null) { + PsiElement parent = elementToReplace.getParent(); + if (parent == clonedFile) break; + elementToReplace = parent; + } + elementToReplace.replace(comment); + + // reparse content + HaxeFile file = (HaxeFile)PsiFileFactory.getInstance(posFile.getProject()).createFileFromText("a.hx", HaxeLanguage.INSTANCE, clonedFile.getText(), true, false); + TreeUtil.ensureParsed(file.getNode()); + return file; } + + } diff --git a/src/main/java/com/intellij/plugins/haxe/ide/completion/HaxeKeywordCompletionPatterns.java b/src/main/java/com/intellij/plugins/haxe/ide/completion/HaxeKeywordCompletionPatterns.java new file mode 100644 index 000000000..6b1d85b41 --- /dev/null +++ b/src/main/java/com/intellij/plugins/haxe/ide/completion/HaxeKeywordCompletionPatterns.java @@ -0,0 +1,73 @@ +package com.intellij.plugins.haxe.ide.completion; + +import com.intellij.patterns.PsiElementPattern; +import com.intellij.plugins.haxe.lang.psi.*; +import com.intellij.psi.PsiElement; + +import static com.intellij.patterns.PlatformPatterns.psiElement; +import static com.intellij.patterns.StandardPatterns.string; +import static com.intellij.plugins.haxe.lang.lexer.HaxeTokenTypes.*; + +public class HaxeKeywordCompletionPatterns { + + public static final PsiElementPattern.Capture packageExpected = + psiElement().inside(HaxeFile.class).atStartOf(psiElement(HaxeFile.class)); + public static final PsiElementPattern.Capture toplevelScope = + psiElement().andNot(psiElement().inside(psiElement(HaxeModule.class))); + + public static final PsiElementPattern.Capture moduleScope = + psiElement() + .andNot(psiElement().inside(psiElement(HaxeClass.class))) + .andNot(psiElement().inside(psiElement(HaxeMethod.class))) + .andNot(psiElement().afterLeafSkipping(psiElement().whitespaceCommentEmptyOrError(), psiElement() + .andOr(psiElement().withElementType(KFUNCTION), psiElement().withElementType(KVAR)) + )); + + public static final PsiElementPattern.Capture classDeclarationScope = + psiElement() + .and(psiElement().inside(psiElement(HaxeClassDeclaration.class))) + .andNot(psiElement().inside(psiElement(HaxeClassBody.class))); + public static final PsiElementPattern.Capture interfaceDeclarationScope = + psiElement() + .and(psiElement().inside(psiElement(HaxeInterfaceDeclaration.class))) + .andNot(psiElement().inside(psiElement(HaxeInterfaceBody.class))); + public static final PsiElementPattern.Capture abstractTypeDeclarationScope = + psiElement() + .and(psiElement().inside(psiElement(HaxeAbstractTypeDeclaration.class))) + .andNot(psiElement().inside(psiElement(HaxeClassBody.class))); + public static final PsiElementPattern.Capture interfaceBodyScope = + psiElement() + .and(psiElement().inside(psiElement(HaxeInterfaceBody.class))) + .andNot(psiElement().inside(psiElement(HaxeMethod.class))); + public static final PsiElementPattern.Capture classBodyScope = + psiElement() + .and(psiElement().inside(psiElement(HaxeClassBody.class))) + .andNot(psiElement().inside(psiElement(HaxeMethod.class))); + public static final PsiElementPattern.Capture functionBodyScope = + psiElement().inside(psiElement(HaxeMethod.class)); + + public static final PsiElementPattern.Capture insideSwitchCase = psiElement() + .inside(HaxeSwitchBlock.class) + .andNot(psiElement().inside(HaxeSwitchCase.class)); + public static final PsiElementPattern.Capture isInsideLoopBlock = psiElement() + .andOr( + psiElement().inside(HaxeForStatement.class), + psiElement().inside(HaxeWhileStatement.class), + psiElement().inside(HaxeDoWhileBody.class) + ); + public static final PsiElementPattern.Capture isInsideForIterator = psiElement() + .inside(HaxeForStatement.class) + .and(psiElement().afterSiblingSkipping(psiElement().whitespaceCommentEmptyOrError(), psiElement().withElementType(COMPONENT_NAME))) + .andNot(psiElement().afterLeafSkipping(psiElement().whitespaceCommentEmptyOrError(), psiElement().withText(")"))); + + public static final PsiElementPattern.Capture isPropertyGetterValue = psiElement() + .inside(HaxePropertyDeclaration.class) + .andNot(psiElement().afterSiblingSkipping(psiElement().whitespace().and(string().oneOf(",")), psiElement(HaxePropertyAccessor.class))); + public static final PsiElementPattern.Capture isPropertySetterValue = psiElement() + .inside(HaxePropertyDeclaration.class) + .and(psiElement().afterSiblingSkipping(psiElement().whitespace().and(string().oneOf(",")), psiElement(HaxePropertyAccessor.class))); + + + public static final PsiElementPattern.Capture isAfterIfStatement = psiElement() + .afterSiblingSkipping(psiElement().whitespaceCommentEmptyOrError(), psiElement(HaxeIfStatement.class)); +} diff --git a/src/main/java/com/intellij/plugins/haxe/ide/completion/HaxeKeywordCompletionUtil.java b/src/main/java/com/intellij/plugins/haxe/ide/completion/HaxeKeywordCompletionUtil.java new file mode 100644 index 000000000..1e674ae01 --- /dev/null +++ b/src/main/java/com/intellij/plugins/haxe/ide/completion/HaxeKeywordCompletionUtil.java @@ -0,0 +1,132 @@ +package com.intellij.plugins.haxe.ide.completion; + +import com.intellij.codeInsight.completion.InsertionContext; +import com.intellij.codeInsight.completion.PrioritizedLookupElement; +import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.codeInsight.lookup.LookupElementBuilder; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiFile; +import com.intellij.psi.codeStyle.CodeStyleManager; +import com.intellij.psi.tree.IElementType; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Set; + +import static com.intellij.plugins.haxe.lang.lexer.HaxeTokenTypeSets.IN_KEYWORD; +import static com.intellij.plugins.haxe.lang.lexer.HaxeTokenTypeSets.IS_KEYWORD; +import static com.intellij.plugins.haxe.lang.lexer.HaxeTokenTypes.*; + +public class HaxeKeywordCompletionUtil { + + private static final String CARET = ""; + + public static final Set PACKAGE_KEYWORD = Set.of(KPACKAGE); + public static final Set TOP_LEVEL_KEYWORDS = Set.of(KIMPORT, KUSING); + public static final Set VISIBILITY_KEYWORDS = Set.of(KPRIVATE, KPUBLIC); + public static final Set ACCESSIBILITY_KEYWORDS = Set.of(KFINAL, KABSTRACT, KINLINE, KEXTERN, KDYNAMIC); + public static final Set MODULE_STRUCTURES_KEYWORDS = Set.of(KCLASS, KABSTRACT, KINTERFACE, KENUM, KEXTERN, KTYPEDEF, KVAR); + + public static final Set CLASS_DEFINITION_KEYWORDS = Set.of(KEXTENDS, KIMPLEMENTS); + public static final Set INTERFACE_DEFINITION_KEYWORDS = Set.of(KEXTENDS); + public static final Set INTERFACE_BODY_KEYWORDS = Set.of(KFUNCTION, KVAR); + public static final Set ABSTRACT_DEFINITION_KEYWORDS = Set.of(KTO, KFROM); + public static final Set CLASS_BODY_KEYWORDS = Set.of(KVAR, KFUNCTION, KOVERLOAD, KOVERRIDE); + public static final Set METHOD_BODY_KEYWORDS = + Set.of(IS_KEYWORD, ONEW, KIF, KVAR, KFUNCTION, KINLINE, KFINAL, KSWITCH, KTHROW, KTRY, KCATCH, KTHIS, KSUPER, KFOR, KWHILE, KDO, KRETURN, KSTATIC, KCAST); + + public static final Set SWITCH_BODY_KEYWORDS = Set.of(KCASE, KDEFAULT); + public static final Set LOOP_BODY_KEYWORDS = Set.of(KBREAK, KCONTINUE); + public static final Set LOOP_ITERATOR_KEYWORDS = Set.of(IN_KEYWORD); + + public static final Set PROPERTY_KEYWORDS = Set.of(KDEFAULT, KNULL, KNEVER, KDYNAMIC); + + public static final Set PP_KEYWORDS = Set.of(PPIF, PPELSE, PPELSEIF, PPEND, PPERROR); + + public static final List insertParentheses = List.of(KIF.toString(), KCATCH.toString(), KFOR.toString(), KWHILE.toString()); + public static final List insertbrackets = List.of(KTRY.toString(), KDO.toString()); + + public static void addKeywords(List result, Set keywords) { + for (IElementType keyword : keywords) { + result.add(keyword(keyword,true, false)); + } + } + public static void addKeywords(List result, Set keywords, float priority) { + for (IElementType keyword : keywords) { + result.add(keyword(keyword, priority,true, false)); + } + } + public static void addKeywords(List result, Set keywords, float priority, boolean bold, boolean italic) { + for (IElementType keyword : keywords) { + result.add(keyword(keyword, priority,bold, italic)); + } + } + + public static @NotNull LookupElement keyword(IElementType keywordElement, float priority, boolean bold, boolean italic) { + return PrioritizedLookupElement.withPriority(keyword(keywordElement, bold, italic), priority); + } + + + public static @NotNull LookupElement keyword(IElementType keywordElement, boolean bold, boolean italic) { + + String elementString = keywordElement.toString(); + boolean addPar = insertParentheses.contains(elementString); + boolean addBracket = insertbrackets.contains(elementString); + + StringBuilder stringBuilder = new StringBuilder().append(keywordElement).append(" "); + if (addPar) stringBuilder.append("(" + CARET + ")"); + if (addBracket) stringBuilder.append("{\n" + CARET + "\n}"); + + LookupElementBuilder builder = LookupElementBuilder.create(keywordElement, stringBuilder.toString()) + .withBoldness(bold) + .withItemTextItalic(italic) + .withPresentableText(elementString); + + builder = builder.withInsertHandler((context, item) -> { + Editor editor = context.getEditor(); + Project project = editor.getProject(); + + String template = item.getLookupString(); + int caretOffset = template.lastIndexOf(CARET); + String content = template.replaceAll(CARET, ""); + + Document document = editor.getDocument(); + int startOffset = context.getStartOffset(); + document.replaceString(startOffset, context.getSelectionEndOffset(), content); + + if (caretOffset != -1) { + TextRange range = new TextRange(startOffset, startOffset + content.length()); + int caret = startOffset + caretOffset; + editor.getCaretModel().moveToOffset(caret); + + flushChanges(project, document); + reformatAndAdjustIndent(context, range); + } + }); + + + return builder; + } + + private static void flushChanges(Project project, Document document) { + PsiDocumentManager instance = PsiDocumentManager.getInstance(project); + instance.doPostponedOperationsAndUnblockDocument(document); + instance.commitDocument(document); + } + + private static void reformatAndAdjustIndent(InsertionContext context, TextRange range) { + Editor editor = context.getEditor(); + Project project = editor.getProject(); + PsiFile file = context.getFile(); + + CodeStyleManager styleManager = CodeStyleManager.getInstance(project); + styleManager.reformatRange(file, range.getStartOffset(), range.getEndOffset()); + styleManager.adjustLineIndent(file, editor.getCaretModel().getOffset()); + } + + +} diff --git a/src/main/java/com/intellij/plugins/haxe/lang/lexer/HaxeTokenTypeSets.java b/src/main/java/com/intellij/plugins/haxe/lang/lexer/HaxeTokenTypeSets.java index 4dd018e1b..957280c58 100644 --- a/src/main/java/com/intellij/plugins/haxe/lang/lexer/HaxeTokenTypeSets.java +++ b/src/main/java/com/intellij/plugins/haxe/lang/lexer/HaxeTokenTypeSets.java @@ -40,6 +40,12 @@ public interface HaxeTokenTypeSets { IElementType SINGLE_QUOTE = new HaxeElementType("'"); IElementType DOUBLE_QUOTE = new HaxeElementType("\""); + IElementType PROPERTY_GET = new HaxeElementType("get"); + IElementType PROPERTY_SET = new HaxeElementType("set"); + + IElementType IS_KEYWORD = new HaxeElementType("is"); + IElementType IN_KEYWORD = new HaxeElementType("in"); + TokenSet WHITESPACES = TokenSet.create( WSNLS, TokenType.WHITE_SPACE, diff --git a/src/main/java/com/intellij/plugins/haxe/lang/psi/impl/HaxePsiCompositeElementImpl.java b/src/main/java/com/intellij/plugins/haxe/lang/psi/impl/HaxePsiCompositeElementImpl.java index 4b4bc6577..552e88581 100644 --- a/src/main/java/com/intellij/plugins/haxe/lang/psi/impl/HaxePsiCompositeElementImpl.java +++ b/src/main/java/com/intellij/plugins/haxe/lang/psi/impl/HaxePsiCompositeElementImpl.java @@ -31,12 +31,15 @@ import com.intellij.plugins.haxe.metadata.psi.impl.HaxeMetadataTypeName; import com.intellij.plugins.haxe.metadata.util.HaxeMetadataUtils; import com.intellij.plugins.haxe.util.UsefulPsiTreeUtil; +import com.intellij.psi.PsiComment; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiModifier; import com.intellij.psi.ResolveState; +import com.intellij.psi.impl.source.tree.CompositeElement; import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtilCore; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -250,4 +253,26 @@ public HaxeMetadataList getMetadataList(@Nullable Class meta public boolean hasMetadata(HaxeMetadataTypeName name, @Nullable Class metadataType) { return HaxeMetadataUtils.hasMeta(this, metadataType, name); } + + @Override + public PsiElement @NotNull [] getChildren() { + PsiElement psiChild = getFirstChild(); + if (psiChild == null) return PsiElement.EMPTY_ARRAY; + + List result = null; + while (psiChild != null) { + // we want to include comments when listing children as its usefull in a lot of places + // we could get children with comments by using custom code looping getNextSibling manually, but its more convenient + // to just override this method and solve the need everywhere, and if we dont want Comments we can always filter the results later. + // (including Comments here will for instance allow us to use Comments in Pattern matching for autocompletion) + if (psiChild.getNode() instanceof CompositeElement || psiChild.getNode() instanceof PsiComment) { + if (result == null) { + result = new ArrayList<>(); + } + result.add(psiChild); + } + psiChild = psiChild.getNextSibling(); + } + return result == null ? PsiElement.EMPTY_ARRAY : PsiUtilCore.toPsiElementArray(result); + } } diff --git a/src/main/java/com/intellij/plugins/haxe/util/HaxeElementGenerator.java b/src/main/java/com/intellij/plugins/haxe/util/HaxeElementGenerator.java index 5942e5e6a..c3bee513e 100644 --- a/src/main/java/com/intellij/plugins/haxe/util/HaxeElementGenerator.java +++ b/src/main/java/com/intellij/plugins/haxe/util/HaxeElementGenerator.java @@ -166,6 +166,13 @@ public static HaxeFile createDummyFile(Project myProject, String text) { assert psiFile != null; return psiFile; } + public static PsiElement createDummyComment(Project myProject, int length) { + StringBuilder builder = new StringBuilder(); + builder.append("/*"); + builder.append("*".repeat(Math.max(0, length - 4))); + builder.append("*/"); + return HaxeElementGenerator.createDummyFile(myProject, builder.toString()).getChildren()[0]; + } public static PsiFile createExpressionCodeFragment(Project myProject, String text, PsiElement context, boolean resolveScope) { final String name = "dummy." + HaxeFileType.INSTANCE.getDefaultExtension(); diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index ad9fc883a..e8284f271 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -246,8 +246,8 @@ - - + +