Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,26 @@ class ExperimentalSettings : PersistentStateComponent<ExperimentalSettings.State
/**
* Use the new list-of-successes grammar engine (Combinator.parse / validate) for value
* validation instead of the original SyntacticMatch/SemanticMatch path.
*
* Release 1 of the rollout (GitHub #467) flips this default to `true` so every project that has
* not explicitly opted out lands on the new engine. The old engine remains reachable as an escape
* hatch: the settings checkbox, or the "Switch back to legacy validation" action on
* [NewGrammarEngineEditorNotificationProvider]. Once the bake period is over the old engine and
* this flag are removed and the new engine becomes unconditional.
*/
var useGrammarParseEngine: Boolean = false
var useGrammarParseEngine: Boolean = true

/**
* Set once the user has dismissed (or acted on) the "you are now on the new grammar engine"
* banner shown by [NewGrammarEngineEditorNotificationProvider], so it does not reappear on every
* unit file they open. Independent of [useGrammarParseEngine]: a user can dismiss the banner while
* staying on the new engine.
*/
var newEngineBannerDismissed: Boolean = false

/**
* Underline the KEY of every option whose value is backed by a grammar validator
* ([GrammarOptionValue]), a debug aid for seeing which keys the new engine covers. Independent of
* ([net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.Combinator]), a debug aid for seeing which keys the new engine covers. Independent of
* [useGrammarParseEngine]: the grammar validators exist in the registry regardless of which
* validation path is active, so this can be toggled on its own.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package net.sjrx.intellij.plugins.systemdunitfiles.settings

import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer
import com.intellij.openapi.fileEditor.FileEditor
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiManager
import com.intellij.ui.EditorNotificationPanel
import com.intellij.ui.EditorNotificationProvider
import com.intellij.ui.EditorNotifications
import net.sjrx.intellij.plugins.systemdunitfiles.UnitFileLanguage
import java.util.function.Function
import javax.swing.JComponent

/**
* Release 1 rollout banner (GitHub #467).
*
* Shown at the top of a systemd unit file once the new grammar engine has become the default, so
* active users know the validation engine changed and have a one-click way out if it misbehaves.
* Because [EditorNotificationProvider]s are evaluated lazily (only when a matching file is actually
* opened), users who never touch a unit file never see it.
*
* Template to copy from: [PodmanNetworkEditorNotificationProvider] — same shape (gate on settings,
* detect the file, return a panel with action labels).
*/
class NewGrammarEngineEditorNotificationProvider : EditorNotificationProvider {

override fun collectNotificationData(
project: Project,
file: VirtualFile
): Function<in FileEditor, out JComponent?>? {

val psiFile = PsiManager.getInstance(project).findFile(file) ?: return null

if (psiFile.language != UnitFileLanguage.INSTANCE) {
return null
}

val state = ExperimentalSettings.getInstance(project).state
if (!state.useGrammarParseEngine) {
return null
}

if (state.newEngineBannerDismissed) {
return null
}

return Function { fileEditor ->
val panel = EditorNotificationPanel(fileEditor, EditorNotificationPanel.Status.Info)

panel.text = "Unit File Support (systemd) use a new experimental engine for validating systemd values. Having problems, switch back and report it."
panel.createActionLabel("Dismiss") {
state.newEngineBannerDismissed = true
EditorNotifications.getInstance(project).updateAllNotifications()
DaemonCodeAnalyzer.getInstance(project).restart()
}

panel.createActionLabel("Switch back") {
state.useGrammarParseEngine = false
EditorNotifications.getInstance(project).updateAllNotifications()
DaemonCodeAnalyzer.getInstance(project).restart()
}
panel

}
}
}
1 change: 1 addition & 0 deletions src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
displayName="systemd Unit Files"
parentId="tools"/>
<editorNotificationProvider implementation="net.sjrx.intellij.plugins.systemdunitfiles.settings.PodmanNetworkEditorNotificationProvider"/>
<editorNotificationProvider implementation="net.sjrx.intellij.plugins.systemdunitfiles.settings.NewGrammarEngineEditorNotificationProvider"/>
</extensions>

<actions>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,34 @@ package net.sjrx.intellij.plugins.systemdunitfiles

import com.intellij.codeInsight.completion.CompletionType
import com.intellij.codeInsight.daemon.impl.HighlightInfo
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInspection.LocalInspectionTool
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.tree.IElementType
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.testFramework.fixtures.BasePlatformTestCase
import com.intellij.testFramework.utils.module.assertContains
import junit.framework.TestCase
import net.sjrx.intellij.plugins.systemdunitfiles.generated.UnitFileElementTypeHolder
import net.sjrx.intellij.plugins.systemdunitfiles.settings.ExperimentalSettings
import org.hamcrest.CoreMatchers.hasItem
import org.hamcrest.MatcherAssert.assertThat
import java.util.*
import java.util.stream.Collectors

abstract class AbstractUnitFileTest : BasePlatformTestCase() {

override fun setUp() {
super.setUp()
// Pin the grammar-engine flag to false (its historical default) so the shipped production default
// — flipped to true in #467 — does not leak into tests. This keeps the CI matrix meaningful:
// * -Dsystemd.unit.grammarParseEngine=false -> flag false + FORCE_PARSE_ENGINE false: the OLD
// validation engine is exercised (still reachable in production via "Switch back"), so it stays
// covered even though it is no longer the default.
// * -Dsystemd.unit.grammarParseEngine=true -> FORCE_PARSE_ENGINE forces the NEW validation
// engine regardless of the flag.
// Tests that need the new engine's flag-gated features (grammar coloring, completion, IPv6
// inspection) set useGrammarParseEngine = true explicitly, overriding this.
ExperimentalSettings.getInstance(project).state.useGrammarParseEngine = false
}

protected fun enableInspection(cls: Class<out LocalInspectionTool?>?) {
myFixture.enableInspections(cls)
}
Expand Down Expand Up @@ -63,15 +75,12 @@ abstract class AbstractUnitFileTest : BasePlatformTestCase() {

@JvmStatic
protected fun assertStringContains(subject: String, value: String) {
TestCase.assertTrue("Expected that $value contains $subject", value.contains(subject))
assertTrue("Expected that $value contains $subject", value.contains(subject))
}

@JvmStatic
protected fun assertContainsQuickfix(info: HighlightInfo, quickfixName: String) {

var found = false
val quickFixes = info.quickFixActionRanges.map {
it ->
it.first.action.text
}

Expand Down
Loading