Skip to content

Commit 0a2abe6

Browse files
SJrXSteve Ramage
andauthored
feat: Add support for validating a bunch of new options such as IOReadBandwidthMax=,IOWriteBandwidthMax=,CPUShares=,CPUWeight=,RootImagePolicy= (Resolves #292) (#293)
Co-authored-by: Steve Ramage <[email protected]>
1 parent 753e26d commit 0a2abe6

27 files changed

+1839
-2
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package net.sjrx.intellij.plugins.systemdunitfiles.intentions
2+
3+
import com.intellij.codeInspection.LocalQuickFix
4+
import com.intellij.codeInspection.ProblemDescriptor
5+
import com.intellij.openapi.project.Project
6+
import com.intellij.psi.util.PsiTreeUtil
7+
import net.sjrx.intellij.plugins.systemdunitfiles.psi.impl.UnitFilePropertyImpl
8+
9+
class ReplaceInvalidLiteralChoiceQuickFix(val offset: Int, val invalidToken : String, val replacementToken : String) : LocalQuickFix {
10+
11+
override fun getName(): String {
12+
return "Replace '${invalidToken}' with '${replacementToken}'"
13+
}
14+
15+
override fun getFamilyName(): String {
16+
return "Replace invalid value"
17+
}
18+
19+
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
20+
val fullPropertyValue = descriptor.psiElement.text
21+
22+
val newText = fullPropertyValue.substring(0, offset) + replacementToken + fullPropertyValue.substring(offset+invalidToken.length)
23+
val newElement = UnitElementFactory.createProperty(project, (descriptor.psiElement.parent as UnitFilePropertyImpl).key, newText)
24+
val property = PsiTreeUtil.getParentOfType(descriptor.psiElement, UnitFilePropertyImpl::class.java)?: return
25+
26+
property.replace(newElement)
27+
}
28+
}

src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/SemanticDataRepository.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,11 @@ class SemanticDataRepository private constructor() {
8888
validatorMap.putAll(TtySizeOptionValue.validators)
8989
validatorMap.putAll(ExecDirectoriesOptionValue.validators)
9090

91+
validatorMap.putAll(IOLimitOptionValue.validators)
92+
validatorMap.putAll(ImagePolicyOptionValue.validators)
93+
validatorMap.putAll(CPUWeightOptionValue.validators)
94+
validatorMap.putAll(CPUSharesOptionValue.validators)
95+
9196
// Scopes are not supported since they aren't standard unit files.
9297

9398
sectionNameToKeyValuesFromDoc.remove(SCOPE_KEYWORD)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues
2+
3+
import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.Validator
4+
import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.*
5+
6+
7+
class CPUSharesOptionValue : GrammarOptionValue(validatorName, GRAMMAR) {
8+
9+
companion object {
10+
val validatorName = "config_parse_cpu_shares"
11+
12+
val GRAMMAR =
13+
SequenceCombinator(
14+
IntegerTerminal(2, 262145),
15+
EOF()
16+
)
17+
18+
val validators = mapOf(
19+
Validator(validatorName, "0") to CPUSharesOptionValue()
20+
)
21+
}
22+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues
2+
3+
import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.Validator
4+
import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.*
5+
6+
7+
class CPUWeightOptionValue : GrammarOptionValue(validatorName, GRAMMAR) {
8+
9+
companion object {
10+
val validatorName = "config_parse_cg_cpu_weight"
11+
12+
val GRAMMAR =
13+
SequenceCombinator(
14+
AlternativeCombinator(
15+
FlexibleLiteralChoiceTerminal("idle"),
16+
IntegerTerminal(1, 10001)
17+
),
18+
EOF()
19+
)
20+
21+
val validators = mapOf(
22+
Validator(validatorName, "0") to CPUWeightOptionValue()
23+
)
24+
}
25+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues
2+
3+
import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.Validator
4+
import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.*
5+
6+
7+
8+
class IOLimitOptionValue() : GrammarOptionValue("config_parse_io_limit", GRAMMAR) {
9+
10+
companion object {
11+
val GRAMMAR = SequenceCombinator(OneOrMore(SequenceCombinator(DEVICE, BYTES)), EOF())
12+
13+
val validators = mapOf(
14+
Validator("config_parse_io_limit", "0") to IOLimitOptionValue()
15+
)
16+
}
17+
}
18+
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues
2+
3+
import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.Validator
4+
import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar.*
5+
6+
class ImagePolicyOptionValue : GrammarOptionValue(validatorName, IMAGE_POLICY_COMBINATOR) {
7+
8+
companion object {
9+
val validatorName = "config_parse_image_policy"
10+
11+
// Image Polcies
12+
// https://www.freedesktop.org/software/systemd/man/latest/systemd.image-policy.html
13+
val IMAGE_POLICY_SEPARATOR = LiteralChoiceTerminal(":")
14+
val PARTITION_IDENTIFIER = FlexibleLiteralChoiceTerminal( "root", "usr", "home", "srv", "esp", "xbootldr", "swap", "root-verity", "root-verity-sig", "usr-verity", "usr-verity-sig", "tmp", "var")
15+
val PARTITION_POLICY_FLAG = FlexibleLiteralChoiceTerminal("unprotected", "verity", "signed", "encrypted", "unused", "absent", "read-only-off", "read-only-on", "growfs-off", "growfs-on")
16+
val PARTITION_POLICY_FLAG_SEPARATOR = LiteralChoiceTerminal("+")
17+
val PARTITION_IDENTIFIER_FLAG_SEPARATOR = LiteralChoiceTerminal("=")
18+
19+
20+
val PARTITION_POLICY_FLAGS = SequenceCombinator(PARTITION_POLICY_FLAG, ZeroOrMore(SequenceCombinator(PARTITION_POLICY_FLAG_SEPARATOR, PARTITION_POLICY_FLAG)))
21+
22+
23+
val SINGLE_EXPLICIT_IMAGE_POLICY = SequenceCombinator(PARTITION_IDENTIFIER, PARTITION_IDENTIFIER_FLAG_SEPARATOR, PARTITION_POLICY_FLAGS)
24+
val DEFAULT_IMAGE_POLICY = SequenceCombinator(PARTITION_POLICY_FLAG_SEPARATOR, PARTITION_POLICY_FLAGS)
25+
var IMAGE_POLICY= AlternativeCombinator(SINGLE_EXPLICIT_IMAGE_POLICY, DEFAULT_IMAGE_POLICY)
26+
val IMAGE_POLICY_COMBINATOR = SequenceCombinator(IMAGE_POLICY, ZeroOrMore(SequenceCombinator(IMAGE_POLICY_SEPARATOR, IMAGE_POLICY)), EOF())
27+
28+
val validators = mapOf(
29+
Validator(validatorName, "0") to ImagePolicyOptionValue()
30+
)
31+
}
32+
33+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar
2+
3+
import kotlin.math.max
4+
5+
/**
6+
* This is a sequence of tokens that must match any of them.
7+
*/
8+
class AlternativeCombinator(vararg val tokens: Combinator) : Combinator {
9+
10+
fun match(value: String, offset: Int, f: (Combinator, String, Int) -> MatchResult): MatchResult {
11+
12+
13+
var longestTokenMatch = emptyList<String>()
14+
var longestTerminalMatch = emptyList<TerminalCombinator>()
15+
var maxLength = 0
16+
17+
for (token in tokens) {
18+
val match = f(token, value, offset)
19+
if (match.matchResult != -1) {
20+
return match
21+
}
22+
23+
24+
25+
if (match.tokens.size > longestTerminalMatch.size) {
26+
longestTerminalMatch = match.terminals
27+
longestTokenMatch = match.tokens
28+
maxLength = max(maxLength, match.longestMatch)
29+
}
30+
}
31+
32+
return MatchResult(longestTokenMatch, -1, longestTerminalMatch, maxLength)
33+
}
34+
35+
override fun SyntacticMatch(value: String, offset: Int): MatchResult {
36+
return match(value, offset, Combinator::SyntacticMatch)
37+
}
38+
39+
override fun SemanticMatch(value: String, offset: Int): MatchResult {
40+
return match(value, offset, Combinator::SemanticMatch)
41+
}
42+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar
2+
3+
interface Combinator {
4+
/**
5+
* WARNING: At the current time this combinator implementation doesn't necessarily guarantee a match.
6+
*
7+
* Seq(ZeroOrMore(Literal("fizz")), Literal("fizz"))
8+
*
9+
* If you try and match "fizz", the ZeroOrMore would greedily consume the fizz, and the second wouldn't match.
10+
*
11+
* I'm unclear if this will actually be a problem, and whether it's worth fixing.
12+
*/
13+
14+
/**
15+
* This checks the value string, starting at offset for a syntactic match.
16+
*
17+
* In a nutshell a syntactic match might accept things that we should color and try and analyze
18+
* but might be incorrect.
19+
*
20+
* For example if you something accepts a positive number, a syntactic regex should match any number even negative or floats
21+
*
22+
* The return value is -1 for no match, or a new offset if this token matched something.
23+
*/
24+
fun SyntacticMatch(value : String, offset: Int): MatchResult
25+
26+
/**
27+
* This checks the value string, starting at offset for a semantic match.
28+
*
29+
* In a nutshell a semantic match means we understood and it valid as far as the grammar is concerned.
30+
*
31+
* The return value is -1 for no match, or a new offset if this token matched something.
32+
*/
33+
fun SemanticMatch(value : String, offset: Int): MatchResult
34+
35+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar
2+
3+
val BYTES = RegexTerminal("[0-9]+[a-zA-Z]*\\s*", "[0-9]+[KMGT]?\\s*")
4+
val DEVICE = RegexTerminal("\\S+\\s*", "/[^\\u0000. ]+\\s*")
5+
val IOPS = RegexTerminal("[0-9]+[a-zA-Z]*\\s*", "[0-9]+[KMGT]?\\s*")
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.grammar
2+
3+
class EOF : Combinator {
4+
override fun SyntacticMatch(value: String, offset: Int): MatchResult {
5+
return if (offset == value.length) {
6+
MatchResult(emptyList(), offset, emptyList(), value.length)
7+
} else {
8+
NoMatch
9+
}
10+
}
11+
12+
override fun SemanticMatch(value: String, offset: Int): MatchResult {
13+
return if (offset == value.length) {
14+
MatchResult(emptyList(), offset, emptyList(), value.length)
15+
} else {
16+
NoMatch
17+
}
18+
}
19+
}

0 commit comments

Comments
 (0)