diff --git a/src/main/java/com/intellij/plugins/haxe/ide/actions/HaxeTypeAddImportIntentionAction.java b/src/main/java/com/intellij/plugins/haxe/ide/actions/HaxeTypeAddImportIntentionAction.java index 7d11f2a48..78a051f7b 100644 --- a/src/main/java/com/intellij/plugins/haxe/ide/actions/HaxeTypeAddImportIntentionAction.java +++ b/src/main/java/com/intellij/plugins/haxe/ide/actions/HaxeTypeAddImportIntentionAction.java @@ -19,6 +19,7 @@ import com.intellij.codeInsight.hint.HintManager; import com.intellij.codeInsight.hint.QuestionAction; +import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo; import com.intellij.codeInsight.navigation.PsiTargetNavigator; import com.intellij.codeInspection.HintAction; import com.intellij.codeInspection.LocalQuickFix; @@ -31,12 +32,14 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; import com.intellij.plugins.haxe.HaxeBundle; +import com.intellij.plugins.haxe.HaxeFileType; import com.intellij.plugins.haxe.lang.psi.HaxeClass; import com.intellij.plugins.haxe.lang.psi.HaxeComponent; import com.intellij.plugins.haxe.util.HaxeAddImportHelper; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.search.PsiElementProcessor; +import com.intellij.psi.util.PsiUtilCore; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; @@ -119,8 +122,25 @@ else if (!candidates.isEmpty()) { } private void doImport(final PsiElement component) { - WriteCommandAction.writeCommandAction(myType.getProject(), myType.getContainingFile()) - .run(() -> HaxeAddImportHelper.addImport(((HaxeClass)component).getQualifiedName(), myType.getContainingFile())); + PsiFile file = myType.getContainingFile(); + + WriteCommandAction.writeCommandAction(myType.getProject(), file) + .run(() -> { + HaxeAddImportHelper.addImport(((HaxeClass)component).getQualifiedName(), file); + PsiUtilCore.ensureValid(file); + }); + } + + @Override + public @NotNull IntentionPreviewInfo generatePreview(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile original) { + PsiFile file = (PsiFile)original.copy(); + + HaxeComponent next = candidates.iterator().next(); + if (next instanceof HaxeClass haxeClass) { + HaxeAddImportHelper.addImport((haxeClass).getQualifiedName(), file); + return new IntentionPreviewInfo.CustomDiff(HaxeFileType.INSTANCE, null, original.getText(), file.getText(), true); + } + return HintAction.super.generatePreview(project, editor, file); } @Override 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 04563690a..b1a1c880b 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 @@ -50,6 +50,7 @@ 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.ide.completion.HaxeKeywordCompletionUtil.addKeywords; import static com.intellij.plugins.haxe.ide.completion.KeywordCompletionData.keywordOnly; import static com.intellij.plugins.haxe.ide.completion.KeywordCompletionData.keywordWithSpace; import static com.intellij.plugins.haxe.lang.lexer.HaxeTokenTypeSets.*; @@ -95,6 +96,10 @@ private static void suggestKeywords(PsiElement position, @NotNull CompletionResu List lookupElements = new ArrayList<>(); + if(completionElementAsComment != null && completionElementAsComment.getParent() == completionElementAsComment.getContainingFile()) { + addKeywords(lookupElements, PACKAGE_KEYWORD); + } + boolean isPPExpression = psiElement().withElementType(PPEXPRESSION).accepts(position); // avoid showing keyword suggestions when not relevant if (allowLookupPattern.accepts(completionElementAsComment)) { diff --git a/src/main/java/com/intellij/plugins/haxe/ide/inspections/HaxeUnusedVarInspection.java b/src/main/java/com/intellij/plugins/haxe/ide/inspections/HaxeUnusedVarInspection.java index ce3488b21..6930e084a 100644 --- a/src/main/java/com/intellij/plugins/haxe/ide/inspections/HaxeUnusedVarInspection.java +++ b/src/main/java/com/intellij/plugins/haxe/ide/inspections/HaxeUnusedVarInspection.java @@ -43,7 +43,7 @@ public boolean isEnabledByDefault() { @NotNull @Override public String getShortName() { - return "HaxeUnusedVariable"; + return "HaxeUnusedVar"; } @Nullable diff --git a/src/main/java/com/intellij/plugins/haxe/lang/psi/impl/HaxeReferenceImpl.java b/src/main/java/com/intellij/plugins/haxe/lang/psi/impl/HaxeReferenceImpl.java index fee87e4a5..335556c1b 100644 --- a/src/main/java/com/intellij/plugins/haxe/lang/psi/impl/HaxeReferenceImpl.java +++ b/src/main/java/com/intellij/plugins/haxe/lang/psi/impl/HaxeReferenceImpl.java @@ -1066,12 +1066,60 @@ private void bindToClass(PsiClass element) { private void handleClassMovement(PsiClass element) { String thisFqn = getQualifiedName(); - //This reference is not a fully qualified name. Nothing to do. + String newFqn = getFqn((HaxeClassDeclaration)element); + //This reference is not a fully qualified name. if (!thisFqn.contains(".")) { - return; + // if ref is not import statement, check if an import statement is needed after move + // (ex. when changing from same package (no import required), to different packages (might be import required)) + if (PsiTreeUtil.getParentOfType(this, HaxeImportStatement.class) == null && + PsiTreeUtil.getParentOfType(this, HaxeUsingStatement.class) == null) { + if (missingImport(newFqn)) { + addImportFor(newFqn); + } + }else { + updateFullyQualifiedReference(newFqn); + } + }else { + updateFullyQualifiedReference(newFqn); + } + } + + private static String getFqn(HaxeClassDeclaration element) { + HaxeClassModel model = element.getModel(); + FullyQualifiedInfo info = model.getQualifiedInfo(); + if (info != null) return info.toShortendImportReferenceString(); + return model.haxeClass.getQualifiedName(); + } + + + private boolean missingImport(String fqn) { + PsiFile containingFile = this.getContainingFile(); + Collection imports = PsiTreeUtil.findChildrenOfType(containingFile, HaxeImportStatement.class); + + Set membersFqn = convertExposedMembersToFqn(imports); + + return !membersFqn.contains(fqn); + + } + + private Set convertExposedMembersToFqn(Collection imports) { + Set memberFqnList = new HashSet<>(); + for (HaxeImportStatement importStatement : imports) { + List exposedMembers = importStatement.getModel().getExposedMembers(); + for (HaxeModel member : exposedMembers) { + if (member instanceof HaxeExposableModel model) { + if(model.getQualifiedInfo() != null) { + memberFqnList.add(model.getQualifiedInfo().toShortendImportReferenceString()); + } + } + } } - String newFqn = ((HaxeClassDeclaration)element).getModel().haxeClass.getQualifiedName(); - updateFullyQualifiedReference(newFqn); + return memberFqnList; + } + + + private void addImportFor(String fqn) { + HaxeAddImportHelper.addImport(fqn, this.getContainingFile()); } private void bindToPackage(PsiPackage element) { diff --git a/src/main/java/com/intellij/plugins/haxe/model/FullyQualifiedInfo.java b/src/main/java/com/intellij/plugins/haxe/model/FullyQualifiedInfo.java index 6e99737ab..a0efc9714 100644 --- a/src/main/java/com/intellij/plugins/haxe/model/FullyQualifiedInfo.java +++ b/src/main/java/com/intellij/plugins/haxe/model/FullyQualifiedInfo.java @@ -173,6 +173,13 @@ public String getFilePath() { public FullyQualifiedInfo toPackageQualifiedName() { return new FullyQualifiedInfo(this.packagePath, null, null, null); } + // avoiding full path to class (MyPackage.MyClass.Myclass, where Myclass is both module name and classname) + public String toShortendImportReferenceString() { + if (fileName.equals(className)) { + return new FullyQualifiedInfo(packagePath, fileName, null, memberName).toString(); + } + return toString(); + } public boolean equalsToNamedPart(String name) { return equalsToMemberName(name) || equalsToClassName(name) || equalsToFileName(name); diff --git a/src/main/java/com/intellij/plugins/haxe/model/HaxeClassModel.java b/src/main/java/com/intellij/plugins/haxe/model/HaxeClassModel.java index e5cb70594..1c2a31d47 100644 --- a/src/main/java/com/intellij/plugins/haxe/model/HaxeClassModel.java +++ b/src/main/java/com/intellij/plugins/haxe/model/HaxeClassModel.java @@ -52,9 +52,6 @@ public class HaxeClassModel implements HaxeCommonMembersModel { private SpecificHaxeClassReference reference; - //for caching purposes - private FullyQualifiedInfo myQualifiedInfo; - public HaxeClassModel(@NotNull HaxeClass haxeClass) { this.haxeClass = haxeClass; } @@ -657,16 +654,14 @@ public HaxeExposableModel getExhibitor() { @Nullable @Override public FullyQualifiedInfo getQualifiedInfo() { - if (myQualifiedInfo == null) { HaxeExposableModel exhibitor = getExhibitor(); if (exhibitor != null) { FullyQualifiedInfo containerInfo = exhibitor.getQualifiedInfo(); if (containerInfo != null) { - myQualifiedInfo = new FullyQualifiedInfo(containerInfo.packagePath, containerInfo.fileName, getName(), null); + return new FullyQualifiedInfo(containerInfo.packagePath, containerInfo.fileName, getName(), null); } } - } - return myQualifiedInfo; + return null; } public void addMethodsFromPrototype(List methods) { diff --git a/src/main/java/com/intellij/plugins/haxe/model/HaxeSourceRootModel.java b/src/main/java/com/intellij/plugins/haxe/model/HaxeSourceRootModel.java index ec66bc8c8..bd47838ea 100644 --- a/src/main/java/com/intellij/plugins/haxe/model/HaxeSourceRootModel.java +++ b/src/main/java/com/intellij/plugins/haxe/model/HaxeSourceRootModel.java @@ -21,8 +21,10 @@ import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiFileSystemItem; import com.intellij.psi.PsiManager; +import lombok.CustomLog; import org.jetbrains.annotations.Nullable; +@CustomLog public class HaxeSourceRootModel { public static final HaxeSourceRootModel DUMMY = new HaxeSourceRootModel(null, null); public final HaxeProjectModel project; @@ -62,7 +64,12 @@ public PsiDirectory access(String packagePath) { PsiDirectory current = directory; for (String part : HaxeStringUtil.split(packagePath, '.')) { if (current == null) break; - current = current.findSubdirectory(part); + try { + current = current.findSubdirectory(part); + }catch (Exception e) { + log.warn(e); + current = null; + } } return current; } diff --git a/src/main/java/com/intellij/plugins/haxe/util/HaxeAddImportHelper.java b/src/main/java/com/intellij/plugins/haxe/util/HaxeAddImportHelper.java index d29efb384..0a7542a98 100644 --- a/src/main/java/com/intellij/plugins/haxe/util/HaxeAddImportHelper.java +++ b/src/main/java/com/intellij/plugins/haxe/util/HaxeAddImportHelper.java @@ -18,14 +18,17 @@ */ package com.intellij.plugins.haxe.util; +import com.intellij.openapi.project.Project; import com.intellij.plugins.haxe.lang.psi.HaxeImportStatement; import com.intellij.plugins.haxe.lang.psi.HaxePackageStatement; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiParserFacade; +import com.intellij.psi.codeStyle.CodeStyleManager; +import com.intellij.psi.util.PsiTreeUtil; import lombok.CustomLog; -import static com.intellij.plugins.haxe.util.UsefulPsiTreeUtil.isWhitespaceOrCommentButNotDocs; +import java.util.List; /** * @author: Fedor.Korotkov @@ -33,32 +36,13 @@ @CustomLog public class HaxeAddImportHelper { public static HaxeImportStatement addImport(String path, PsiFile file) { - int positionIndex = 0; - final PsiElement[] children = file.getChildren(); - - PsiElement child = children[positionIndex]; - // search for package or first import - while (!(child instanceof HaxePackageStatement) - && !(child instanceof HaxeImportStatement)) { - if (++positionIndex < children.length) { - child = children[positionIndex]; - } else { - child = null; - break; - } - } - // find last whitespace line before docs or code and after last import - while (child instanceof HaxePackageStatement - || child instanceof HaxeImportStatement - || isWhitespaceOrCommentButNotDocs(child)) { - if (++positionIndex < children.length) { - child = children[positionIndex]; - }else { - child = null; - break; - } + PsiElement child = PsiTreeUtil.findChildOfType(file, HaxePackageStatement.class); + List importStatements = PsiTreeUtil.findChildrenOfType(file, HaxeImportStatement.class).stream().toList(); + if(!importStatements.isEmpty()) { + child = importStatements.get(importStatements.size()-1); } + if(child != null) { return insertImportBefore(path, file, child); }else { @@ -75,21 +59,20 @@ private static HaxeImportStatement insertImportBefore(String path, PsiFile file, } final PsiElement newLineElement = PsiParserFacade.getInstance(file.getProject()).createWhiteSpaceFromText("\n"); - file.addBefore(newLineElement, child); - file.addBefore(importStatement, child); - return importStatement; + HaxeImportStatement element = (HaxeImportStatement)file.addAfter(importStatement, child); + file.addAfter(newLineElement, child); + return element; } private static HaxeImportStatement insertImportTop(String path, PsiFile file) { - final HaxeImportStatement importStatement = - HaxeElementGenerator.createImportStatementFromPath(file.getProject(), path); + HaxeImportStatement importStatement = HaxeElementGenerator.createImportStatementFromPath(file.getProject(), path); if (importStatement == null) { return null; } final PsiElement newLineElement = PsiParserFacade.getInstance(file.getProject()).createWhiteSpaceFromText("\n"); PsiElement child = file.getFirstChild(); - file.addBefore(newLineElement, child); - file.addBefore(importStatement, child); - return importStatement; + HaxeImportStatement element = (HaxeImportStatement)file.addBefore(importStatement.copy(), child); + file.addAfter(newLineElement.copy(), element); + return element; } } diff --git a/src/test/java/com/intellij/plugins/haxe/actions/HaxeTypeAddImportIntentionActionTest.java b/src/test/java/com/intellij/plugins/haxe/actions/HaxeTypeAddImportIntentionActionTest.java index 4e713f244..e12012978 100644 --- a/src/test/java/com/intellij/plugins/haxe/actions/HaxeTypeAddImportIntentionActionTest.java +++ b/src/test/java/com/intellij/plugins/haxe/actions/HaxeTypeAddImportIntentionActionTest.java @@ -18,13 +18,19 @@ package com.intellij.plugins.haxe.actions; import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.project.Project; import com.intellij.plugins.haxe.HaxeCodeInsightFixtureTestCase; +import com.intellij.plugins.haxe.HaxeFileType; +import com.intellij.plugins.haxe.HaxeLanguage; import com.intellij.plugins.haxe.ide.actions.HaxeTypeAddImportIntentionAction; import com.intellij.plugins.haxe.ide.index.HaxeComponentIndex; import com.intellij.plugins.haxe.lang.psi.HaxeType; import com.intellij.plugins.haxe.util.HaxeResolveUtil; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.codeStyle.CodeStyleSettingsManager; +import com.intellij.psi.codeStyle.CommonCodeStyleSettings; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.PsiTreeUtil; import org.junit.Test; @@ -38,6 +44,9 @@ protected String getBasePath() { return "/addImportIntention/"; } + protected CommonCodeStyleSettings myTestStyleSettings; + + public void doTest() { final PsiFile file = PsiDocumentManager.getInstance(myFixture.getProject()).getPsiFile(myFixture.getEditor().getDocument()); assertNotNull(file); @@ -51,6 +60,38 @@ public void doTest() { myFixture.checkResultByFile(getTestName(false) + ".txt"); } + @Override + protected void setUp() throws Exception { + super.setUp(); + setTestStyleSettings(); + } + + + @Override + public void setTestStyleSettings() { + Project project = getProject(); + CodeStyleSettings currSettings = CodeStyleSettingsManager.getSettings(project); + assertNotNull(currSettings); + CodeStyleSettings tempSettings = currSettings.clone(); + CodeStyleSettings.IndentOptions indentOptions = tempSettings.getIndentOptions(HaxeFileType.INSTANCE); + assertNotNull(indentOptions); + defineStyleSettings(tempSettings); + CodeStyleSettingsManager.getInstance(project).setTemporarySettings(tempSettings); + } + + protected void defineStyleSettings(CodeStyleSettings tempSettings) { + myTestStyleSettings = tempSettings.getCommonSettings(HaxeLanguage.INSTANCE); + myTestStyleSettings.KEEP_BLANK_LINES_IN_CODE = 2; + myTestStyleSettings.METHOD_BRACE_STYLE = CommonCodeStyleSettings.END_OF_LINE; + myTestStyleSettings.BRACE_STYLE = CommonCodeStyleSettings.END_OF_LINE; + myTestStyleSettings.ALIGN_MULTILINE_PARAMETERS = false; + myTestStyleSettings.ALIGN_MULTILINE_PARAMETERS_IN_CALLS = false; + myTestStyleSettings.KEEP_FIRST_COLUMN_COMMENT = false; + myTestStyleSettings.BLANK_LINES_AFTER_PACKAGE = 2; + myTestStyleSettings.BLANK_LINES_AFTER_IMPORTS = 2; + } + + @Test public void testSimple() throws Throwable { myFixture.configureByFiles(getTestName(false) + ".hx", "foo/Bar.hx"); diff --git a/src/test/resources/testData/addImportIntention/Helper.txt b/src/test/resources/testData/addImportIntention/Helper.txt index c3a625a4f..10de1d161 100644 --- a/src/test/resources/testData/addImportIntention/Helper.txt +++ b/src/test/resources/testData/addImportIntention/Helper.txt @@ -1,4 +1,5 @@ package; + import foo.Bar.Baz; /** diff --git a/src/test/resources/testData/addImportIntention/Module.txt b/src/test/resources/testData/addImportIntention/Module.txt index 97ab83ed2..6b5781fe2 100644 --- a/src/test/resources/testData/addImportIntention/Module.txt +++ b/src/test/resources/testData/addImportIntention/Module.txt @@ -1,6 +1,7 @@ package; import foo.Types.Type1; + class Module { var t:Type1; } diff --git a/src/test/resources/testData/addImportIntention/Simple.txt b/src/test/resources/testData/addImportIntention/Simple.txt index c473aec71..e2eb61981 100644 --- a/src/test/resources/testData/addImportIntention/Simple.txt +++ b/src/test/resources/testData/addImportIntention/Simple.txt @@ -1,6 +1,7 @@ package; import foo.Bar; + class Simple { var bar:Bar; } \ No newline at end of file diff --git a/src/test/resources/testData/move/moveFile1/after/Test.hx b/src/test/resources/testData/move/moveFile1/after/Test.hx index 445acaca8..d5263b5ca 100644 --- a/src/test/resources/testData/move/moveFile1/after/Test.hx +++ b/src/test/resources/testData/move/moveFile1/after/Test.hx @@ -1,6 +1,7 @@ package ; import bar.ArrayUtils; + class Test { static function main() { function is_c(val) { return val == "c"; }