diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/annotators/GrammarEngineKeyAnnotator.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/annotators/GrammarEngineKeyAnnotator.kt new file mode 100644 index 00000000..4942a841 --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/annotators/GrammarEngineKeyAnnotator.kt @@ -0,0 +1,49 @@ +package net.sjrx.intellij.plugins.systemdunitfiles.annotators + +import com.intellij.lang.annotation.AnnotationHolder +import com.intellij.lang.annotation.Annotator +import com.intellij.lang.annotation.HighlightSeverity +import com.intellij.openapi.editor.DefaultLanguageHighlighterColors +import com.intellij.openapi.editor.colors.TextAttributesKey +import com.intellij.psi.PsiElement +import com.intellij.psi.util.PsiTreeUtil +import net.sjrx.intellij.plugins.systemdunitfiles.psi.UnitFileProperty +import net.sjrx.intellij.plugins.systemdunitfiles.psi.UnitFileSectionType +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.SemanticDataRepository +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.fileClass +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.GrammarOptionValue +import net.sjrx.intellij.plugins.systemdunitfiles.settings.ExperimentalSettings + +/** + * Debug aid (#467): while the experimental grammar engine is enabled, mark the KEY of every option + * whose value is validated by that engine (a [GrammarOptionValue]), so it is obvious at a glance + * which keys exercise the new parse() path versus the original SyntacticMatch/SemanticMatch one. + * + * Does nothing when the flag is off, so it has no effect for normal users. + */ +class GrammarEngineKeyAnnotator : Annotator { + + override fun annotate(element: PsiElement, holder: AnnotationHolder) { + if (element !is UnitFileProperty) return + if (!ExperimentalSettings.getInstance(element.project).state.useGrammarParseEngine) return + + val section = PsiTreeUtil.getParentOfType(element, UnitFileSectionType::class.java) ?: return + val fileClass = element.containingFile.fileClass() + val validator = SemanticDataRepository.instance.getOptionValidator(fileClass, section.sectionName, element.key) + + if (validator is GrammarOptionValue) { + holder.newSilentAnnotation(HighlightSeverity.INFORMATION) + .range(element.keyNode.psi) + .textAttributes(NEW_ENGINE_KEY) + .create() + } + } + + companion object { + // Layered on top of the normal key color; METADATA gives a distinct, theme-aware tint. + val NEW_ENGINE_KEY: TextAttributesKey = TextAttributesKey.createTextAttributesKey( + "SYSTEMD_UNIT_FILE_NEW_GRAMMAR_ENGINE_KEY", + DefaultLanguageHighlighterColors.METADATA, + ) + } +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 94771c62..2fdc75e5 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -60,6 +60,7 @@ + , key: String) = + highlights.any { it.severity == HighlightSeverity.INFORMATION && it.text == key } + + @Test + fun testGrammarBackedKeyIsMarkedWhenFlagOn() { + ExperimentalSettings.getInstance(project).state.useGrammarParseEngine = true + // RestrictAddressFamilies is validated by a GrammarOptionValue; PrivateTmp's value is not. + setupFileInEditor("file.service", "[Service]\nRestrictAddressFamilies=AF_INET\n") + + val highlights = myFixture.doHighlighting() + assertTrue(markedKey(highlights, "RestrictAddressFamilies")) + } + + @Test + fun testNotMarkedWhenFlagOff() { + setupFileInEditor("file.service", "[Service]\nRestrictAddressFamilies=AF_INET\n") + + val highlights = myFixture.doHighlighting() + assertFalse(markedKey(highlights, "RestrictAddressFamilies")) + } +} diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/GrammarParseEngineInspectionTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/GrammarParseEngineInspectionTest.kt index 1a3831c7..7d822977 100644 --- a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/GrammarParseEngineInspectionTest.kt +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/GrammarParseEngineInspectionTest.kt @@ -1,5 +1,6 @@ package net.sjrx.intellij.plugins.systemdunitfiles.inspections +import com.intellij.lang.annotation.HighlightSeverity import net.sjrx.intellij.plugins.systemdunitfiles.AbstractUnitFileTest import net.sjrx.intellij.plugins.systemdunitfiles.settings.ExperimentalSettings import org.junit.Test @@ -42,7 +43,7 @@ class GrammarParseEngineInspectionTest : AbstractUnitFileTest() { setupFileInEditor("file.service", file) enableInspection(InvalidValueInspection::class.java) - assertSize(0, myFixture.doHighlighting()) + assertSize(0, myFixture.doHighlighting().filter { it.severity != HighlightSeverity.INFORMATION }) } @Test @@ -61,7 +62,7 @@ class GrammarParseEngineInspectionTest : AbstractUnitFileTest() { setupFileInEditor("file.service", file) enableInspection(InvalidValueInspection::class.java) - assertSize(3, myFixture.doHighlighting()) + assertSize(3, myFixture.doHighlighting().filter { it.severity != HighlightSeverity.INFORMATION }) } @Test @@ -76,6 +77,6 @@ class GrammarParseEngineInspectionTest : AbstractUnitFileTest() { setupFileInEditor("file.service", file) enableInspection(InvalidValueInspection::class.java) - assertSize(0, myFixture.doHighlighting()) + assertSize(0, myFixture.doHighlighting().filter { it.severity != HighlightSeverity.INFORMATION }) } }