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 })
}
}