diff --git a/plugins/env-files-support/src/main/java/ru/adelf/idea/dotenv/inspections/DuplicateKeyInspection.java b/plugins/env-files-support/src/main/java/ru/adelf/idea/dotenv/inspections/DuplicateKeyInspection.java index 5acc7753ee2e..0faead0b4c81 100644 --- a/plugins/env-files-support/src/main/java/ru/adelf/idea/dotenv/inspections/DuplicateKeyInspection.java +++ b/plugins/env-files-support/src/main/java/ru/adelf/idea/dotenv/inspections/DuplicateKeyInspection.java @@ -15,6 +15,14 @@ import ru.adelf.idea.dotenv.psi.DotEnvFile; import java.util.*; public class DuplicateKeyInspection extends LocalInspectionTool { + // Change the display name within the plugin.xml + // This needs to be here as otherwise the tests will throw errors. + @NotNull + @Override + public String getDisplayName() { + return "Duplicate Key"; + } + @Override public boolean runForWholeFile() { return true; diff --git a/plugins/env-files-support/src/main/java/ru/adelf/idea/dotenv/inspections/ExtraBlankLineInspection.java b/plugins/env-files-support/src/main/java/ru/adelf/idea/dotenv/inspections/ExtraBlankLineInspection.java new file mode 100644 index 000000000000..d32690be0222 --- /dev/null +++ b/plugins/env-files-support/src/main/java/ru/adelf/idea/dotenv/inspections/ExtraBlankLineInspection.java @@ -0,0 +1,91 @@ +package ru.adelf.idea.dotenv.inspections; + +import com.intellij.codeInspection.*; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.impl.source.tree.ElementType; +import com.intellij.psi.impl.source.tree.PsiWhiteSpaceImpl; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import ru.adelf.idea.dotenv.DotEnvFactory; +import ru.adelf.idea.dotenv.psi.DotEnvFile; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ExtraBlankLineInspection extends LocalInspectionTool { + // Change the display name within the plugin.xml + // This needs to be here as otherwise the tests will throw errors. + @NotNull + @Override + public String getDisplayName() { + return "Extra blank line"; + } + + @Override + public boolean runForWholeFile() { + return true; + } + + @Nullable + @Override + public ProblemDescriptor[] checkFile(@NotNull PsiFile file, @NotNull InspectionManager manager, boolean isOnTheFly) { + if (!(file instanceof DotEnvFile)) { + return null; + } + + return analyzeFile(file, manager, isOnTheFly).getResultsArray(); + } + + @NotNull + private ProblemsHolder analyzeFile(@NotNull PsiFile file, @NotNull InspectionManager manager, boolean isOnTheFly) { + ProblemsHolder problemsHolder = new ProblemsHolder(manager, file, isOnTheFly); + + PsiTreeUtil.findChildrenOfType(file, PsiWhiteSpaceImpl.class).forEach(whiteSpace -> { + Pattern pattern = Pattern.compile("\r\n|\r|\n"); + Matcher matcher = pattern.matcher(whiteSpace.getText()); + + int count = 0; + while (matcher.find()) + count++; + + if (count > 2) { + problemsHolder.registerProblem(whiteSpace, + "Only one extra line allowed between properties", + new RemoveExtraBlankLineQuickFix()); + } + }); + + return problemsHolder; + } + + private static class RemoveExtraBlankLineQuickFix implements LocalQuickFix { + + @NotNull + @Override + public String getName() { + return "Remove extra blank line"; + } + + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + try { + PsiElement psiElement = descriptor.getPsiElement(); + + PsiElement newPsiElement = DotEnvFactory.createFromText(project, ElementType.WHITE_SPACE, "\n\n"); + + psiElement.replace(newPsiElement); + } catch (IncorrectOperationException e) { + Logger.getInstance(ExtraBlankLineInspection.class).error(e); + } + } + + @NotNull + public String getFamilyName() { + return getName(); + } + } +} diff --git a/plugins/env-files-support/src/main/java/ru/adelf/idea/dotenv/inspections/IncorrectDelimiterInspection.java b/plugins/env-files-support/src/main/java/ru/adelf/idea/dotenv/inspections/IncorrectDelimiterInspection.java new file mode 100644 index 000000000000..11d5f17c832e --- /dev/null +++ b/plugins/env-files-support/src/main/java/ru/adelf/idea/dotenv/inspections/IncorrectDelimiterInspection.java @@ -0,0 +1,80 @@ +package ru.adelf.idea.dotenv.inspections; + +import com.intellij.codeInspection.*; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import ru.adelf.idea.dotenv.DotEnvFactory; +import ru.adelf.idea.dotenv.psi.DotEnvFile; +import ru.adelf.idea.dotenv.psi.DotEnvTypes; +import ru.adelf.idea.dotenv.psi.impl.DotEnvKeyImpl; + +public class IncorrectDelimiterInspection extends LocalInspectionTool { + // Change the display name within the plugin.xml + // This needs to be here as otherwise the tests will throw errors. + @NotNull + @Override + public String getDisplayName() { + return "Incorrect delimiter"; + } + + @Override + public boolean runForWholeFile() { + return true; + } + + @Nullable + @Override + public ProblemDescriptor[] checkFile(@NotNull PsiFile file, @NotNull InspectionManager manager, boolean isOnTheFly) { + if (!(file instanceof DotEnvFile)) { + return null; + } + + return analyzeFile(file, manager, isOnTheFly).getResultsArray(); + } + + @NotNull + private ProblemsHolder analyzeFile(@NotNull PsiFile file, @NotNull InspectionManager manager, boolean isOnTheFly) { + ProblemsHolder problemsHolder = new ProblemsHolder(manager, file, isOnTheFly); + + PsiTreeUtil.findChildrenOfType(file, DotEnvKeyImpl.class).forEach(key -> { + if (key.getText().contains("-")) { + problemsHolder.registerProblem(key, "Expected: '_' Found: '-'", new ReplaceDelimiterQuickFix()); + } + }); + + return problemsHolder; + } + + private static class ReplaceDelimiterQuickFix implements LocalQuickFix { + + @NotNull + @Override + public String getName() { + return "Replace delimiter"; + } + + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + try { + PsiElement psiElement = descriptor.getPsiElement(); + + PsiElement newPsiElement = DotEnvFactory.createFromText(project, DotEnvTypes.KEY, + psiElement.getText().replace("-","_")+"=dummy"); + + psiElement.replace(newPsiElement); + } catch (IncorrectOperationException e) { + Logger.getInstance(IncorrectDelimiterInspection.class).error(e); + } + } + + @NotNull + public String getFamilyName() { + return getName(); + } + } +} diff --git a/plugins/env-files-support/src/main/java/ru/adelf/idea/dotenv/inspections/SpaceInsideNonQuotedInspection.java b/plugins/env-files-support/src/main/java/ru/adelf/idea/dotenv/inspections/SpaceInsideNonQuotedInspection.java index 65d37553e128..bfaae7f21ba3 100644 --- a/plugins/env-files-support/src/main/java/ru/adelf/idea/dotenv/inspections/SpaceInsideNonQuotedInspection.java +++ b/plugins/env-files-support/src/main/java/ru/adelf/idea/dotenv/inspections/SpaceInsideNonQuotedInspection.java @@ -16,6 +16,13 @@ import ru.adelf.idea.dotenv.psi.DotEnvTypes; import ru.adelf.idea.dotenv.psi.DotEnvValue; public class SpaceInsideNonQuotedInspection extends LocalInspectionTool { + // Change the display name within the plugin.xml + // This needs to be here as otherwise the tests will throw errors. + @NotNull + @Override + public String getDisplayName() { + return "Space inside non-quoted value"; + } private AddQuotesQuickFix addQuotesQuickFix = new AddQuotesQuickFix(); diff --git a/plugins/env-files-support/src/main/resources/META-INF/plugin.xml b/plugins/env-files-support/src/main/resources/META-INF/plugin.xml index 1fa17964e851..cc455e50a3ac 100644 --- a/plugins/env-files-support/src/main/resources/META-INF/plugin.xml +++ b/plugins/env-files-support/src/main/resources/META-INF/plugin.xml @@ -102,6 +102,22 @@ level="WARNING" implementationClass="ru.adelf.idea.dotenv.inspections.SpaceInsideNonQuotedInspection"/> + + + + diff --git a/plugins/env-files-support/src/main/resources/inspectionDescriptions/DotEnvExtraBlankLineInspection.html b/plugins/env-files-support/src/main/resources/inspectionDescriptions/DotEnvExtraBlankLineInspection.html new file mode 100644 index 000000000000..3fc8ddc59ca5 --- /dev/null +++ b/plugins/env-files-support/src/main/resources/inspectionDescriptions/DotEnvExtraBlankLineInspection.html @@ -0,0 +1,5 @@ + + +Reports extra blank lines in .env files. + + \ No newline at end of file diff --git a/plugins/env-files-support/src/main/resources/inspectionDescriptions/DotEnvIncorrectDelimiterInspection.html b/plugins/env-files-support/src/main/resources/inspectionDescriptions/DotEnvIncorrectDelimiterInspection.html new file mode 100644 index 000000000000..656e6688896f --- /dev/null +++ b/plugins/env-files-support/src/main/resources/inspectionDescriptions/DotEnvIncorrectDelimiterInspection.html @@ -0,0 +1,5 @@ + + +Reports incorrect delimiter in .env files ('-' instead of'_'). + + \ No newline at end of file diff --git a/plugins/env-files-support/src/test/java/ru/adelf/idea/dotenv/tests/dotenv/InspectionsTest.java b/plugins/env-files-support/src/test/java/ru/adelf/idea/dotenv/tests/dotenv/InspectionsTest.java new file mode 100644 index 000000000000..8d55be221140 --- /dev/null +++ b/plugins/env-files-support/src/test/java/ru/adelf/idea/dotenv/tests/dotenv/InspectionsTest.java @@ -0,0 +1,69 @@ +package ru.adelf.idea.dotenv.tests.dotenv; + +import com.intellij.codeInsight.daemon.impl.HighlightInfo; +import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.codeInspection.InspectionProfileEntry; +import ru.adelf.idea.dotenv.inspections.DuplicateKeyInspection; +import ru.adelf.idea.dotenv.inspections.ExtraBlankLineInspection; +import ru.adelf.idea.dotenv.inspections.IncorrectDelimiterInspection; +import ru.adelf.idea.dotenv.inspections.SpaceInsideNonQuotedInspection; +import ru.adelf.idea.dotenv.tests.DotEnvLightCodeInsightFixtureTestCase; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class InspectionsTest extends DotEnvLightCodeInsightFixtureTestCase { + + @Override + public void setUp() throws Exception { + super.setUp(); + myFixture.configureFromExistingVirtualFile(myFixture.copyFileToProject("inspections.env")); + } + + protected String getTestDataPath() { + return basePath + "dotenv/fixtures"; + } + + // Test for each Inspection + + public void testDuplicateKey() { + doInspectionTest(new DuplicateKeyInspection(), Arrays.asList("DUPLICATE_KEY=test", "DUPLICATE_KEY=test2")); + } + + public void testSpaceInsideNonQuoted() { + doInspectionTest(new SpaceInsideNonQuotedInspection(), Collections.singletonList("spaces without quotes")); + } + + public void testExtraBlankLine() { + doInspectionTest(new ExtraBlankLineInspection(), Collections.singletonList("\n\n\n")); + } + + public void testIncorrectDelimiterInspection() { + doInspectionTest(new IncorrectDelimiterInspection(), Collections.singletonList("INCORRECT-DELIMITER")); + } + + // Every available quickfix from every inspection is getting applied + public void testQuickFixes() { + myFixture.enableInspections(new SpaceInsideNonQuotedInspection()); + myFixture.enableInspections(new ExtraBlankLineInspection()); + myFixture.enableInspections(new IncorrectDelimiterInspection()); + + myFixture.doHighlighting(); + List intentionActions = myFixture.getAllQuickFixes(); + intentionActions.forEach(intentionAction -> myFixture.launchAction(intentionAction)); + myFixture.checkResultByFile("quickFix.env"); + } + + private void doInspectionTest(InspectionProfileEntry entry, List expectedHighlightedText) { + myFixture.enableInspections(entry); + + List highlightInfoList = myFixture.doHighlighting(); + List actualHighlightedText = new ArrayList<>(); + + highlightInfoList.forEach( highlightInfo -> actualHighlightedText.add(highlightInfo.getText())); + + assertEquals(expectedHighlightedText, actualHighlightedText); + } +} diff --git a/plugins/env-files-support/src/test/java/ru/adelf/idea/dotenv/tests/dotenv/fixtures/inspections.env b/plugins/env-files-support/src/test/java/ru/adelf/idea/dotenv/tests/dotenv/fixtures/inspections.env new file mode 100644 index 000000000000..46a4f60baff3 --- /dev/null +++ b/plugins/env-files-support/src/test/java/ru/adelf/idea/dotenv/tests/dotenv/fixtures/inspections.env @@ -0,0 +1,11 @@ +DUPLICATE_KEY=test +DUPLICATE_KEY=test2 + +SPACE_INSIDE_NON_QUOTED=spaces without quotes + +# extra blank lines test START + + +# extra blank lines test END + +INCORRECT-DELIMITER=test-test \ No newline at end of file diff --git a/plugins/env-files-support/src/test/java/ru/adelf/idea/dotenv/tests/dotenv/fixtures/quickFix.env b/plugins/env-files-support/src/test/java/ru/adelf/idea/dotenv/tests/dotenv/fixtures/quickFix.env new file mode 100644 index 000000000000..d7b46e610019 --- /dev/null +++ b/plugins/env-files-support/src/test/java/ru/adelf/idea/dotenv/tests/dotenv/fixtures/quickFix.env @@ -0,0 +1,10 @@ +DUPLICATE_KEY=test +DUPLICATE_KEY=test2 + +SPACE_INSIDE_NON_QUOTED="spaces without quotes" + +# extra blank lines test START + +# extra blank lines test END + +INCORRECT_DELIMITER=test-test \ No newline at end of file