diff --git a/README.md b/README.md index 5bf6f41a..16d5d5f7 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ -Unit File Support for systemd ------------------------------ +systemd & Unit File Support +--------------------------- ## Introduction -This plugin adds support for [systemd unit files](https://www.freedesktop.org/software/systemd/man/systemd.unit.html#) to IntelliJ. +This plugin adds support for [systemd & unit files](https://www.freedesktop.org/software/systemd/man/systemd.unit.html#) to IntelliJ. ## Features * Syntax highlighting for unit files. @@ -28,24 +28,27 @@ This plugin adds support for [systemd unit files](https://www.freedesktop.org/so ## Usage To create a file simply right-click on a folder and New > File, and enter a file name ending any of: - * `.automount` - * `.device` - * `.mount` - * `.path` - * `.service` - * `.slice` - * `.socket` - * `.swap` - * `.target` - * `.timer` + * [Unit Files](https://www.freedesktop.org/software/systemd/man/latest/systemd.unit.html) + * `.automount` + * `.device` + * `.mount` + * `.path` + * `.service` + * `.slice` + * `.socket` + * `.swap` + * `.target` + * `.timer` + * [Nspawn Container Settings](https://www.freedesktop.org/software/systemd/man/latest/systemd.nspawn.html#) + * `.nspawn` The file should then be associated with this plugin and the above features should work. - + __NOTE__: `.scope` units are not configured via unit configuration files and so we don't support them. ## Installation -This plugin is avaliable to install at the [JetBrains Plugin Repository](https://plugins.jetbrains.com/plugin/11070-unit-file-support-systemd-). +This plugin is available to install at the [JetBrains Plugin Repository](https://plugins.jetbrains.com/plugin/11070-unit-file-support-systemd-). Contributors ------------- diff --git a/build.gradle.kts b/build.gradle.kts index cb782670..4f62b85a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -202,7 +202,21 @@ tasks.register("generateParserTask") { tasks.register("generateOptionValidator") { - from("./systemd-build/build/load-fragment-gperf.gperf") + listOf( + "journald-gperf.gperf", + "link-config-gperf.gperf", + "load-fragment-gperf.gperf", + "logind-gperf.gperf", + "netdev-gperf.gperf", + "networkd-gperf.gperf", + "networkd-network-gperf.gperf", + "nspawn-gperf.gperf", + "resolved-dnssd-gperf.gperf", + "resolved-gperf.gperf", + "timesyncd-gperf.gperf").forEach { + fileName -> from("./systemd-build/build/${fileName}") + } + into("${sourceSets["main"].output.resourcesDir?.getAbsolutePath()}/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/") } diff --git a/buildSrc/src/main/groovy/GenerateDataFromManPages.groovy b/buildSrc/src/main/groovy/GenerateDataFromManPages.groovy index 7049044f..4a59a592 100644 --- a/buildSrc/src/main/groovy/GenerateDataFromManPages.groovy +++ b/buildSrc/src/main/groovy/GenerateDataFromManPages.groovy @@ -55,75 +55,86 @@ class GenerateDataFromManPages extends DefaultTask { * Map that stores for each file name, the name of an option attribute */ @Internal - def fileAndSectionTitleToSectionName = [ - 'systemd.unit.xml' : - ['sections': - ['[Unit] Section Options' : ['Unit'], - '[Install] Section Options': ['Install'], - 'Conditions and Asserts' : ['Unit'] - ] - ], - 'systemd.service.xml' : - ['sections': - ['Options': ['Service']] - ], - 'systemd.timer.xml' : - ['sections': - ['Options': ['Timer']] - ], - 'systemd.automount.xml' : - ['sections': - ['Options': ['Automount']] - ], - 'systemd.mount.xml' : - ['sections': - ['Options': ['Mount']] - ], - 'systemd.path.xml' : - ['sections': - ['Options': ['Path']] - ], - 'systemd.socket.xml' : - ['sections': - ['Options': ['Socket']] - ], - 'systemd.swap.xml' : - ['sections': - ['Options': ['Swap']] - ], - 'systemd.resource-control.xml': - ['sections': - [ - 'Options' : ['Slice', 'Service', 'Socket', 'Mount', 'Swap'], - 'Deprecated Options': ['Slice', 'Service', 'Socket', 'Mount', 'Swap'], - ] - ], - 'systemd.kill.xml' : - ['sections': - ['Options': ['Service', "Socket", "Mount", "Swap"]] - ], - 'systemd.exec.xml' : - ['sections': - [ - 'Paths' : ['Service', 'Socket', 'Mount', 'Swap'], - 'Credentials' : ['Service', 'Socket', 'Mount', 'Swap'], - 'User/Group Identity' : ['Service', 'Socket', 'Mount', 'Swap'], - 'Capabilities' : ['Service', 'Socket', 'Mount', 'Swap'], - 'Security' : ['Service', 'Socket', 'Mount', 'Swap'], - 'Mandatory Access Control' : ['Service', 'Socket', 'Mount', 'Swap'], - 'Process Properties' : ['Service', 'Socket', 'Mount', 'Swap'], - 'Scheduling' : ['Service', 'Socket', 'Mount', 'Swap'], - 'Sandboxing' : ['Service', 'Socket', 'Mount', 'Swap'], - 'System Call Filtering' : ['Service', 'Socket', 'Mount', 'Swap'], - 'Environment' : ['Service', 'Socket', 'Mount', 'Swap'], - 'Logging and Standard Input/Output': ['Service', 'Socket', 'Mount', 'Swap'], - 'System V Compatibility' : ['Service', 'Socket', 'Mount', 'Swap'], - ] - ] + def fileTypeToFileAndSectionTitleToSectionName = [ + 'unit' : [ + 'systemd.unit.xml' : + ['sections': + ['[Unit] Section Options' : ['Unit'], + '[Install] Section Options': ['Install'], + 'Conditions and Asserts' : ['Unit'] + ] + ], + 'systemd.service.xml' : + ['sections': + ['Options': ['Service']] + ], + 'systemd.timer.xml' : + ['sections': + ['Options': ['Timer']] + ], + 'systemd.automount.xml' : + ['sections': + ['Options': ['Automount']] + ], + 'systemd.mount.xml' : + ['sections': + ['Options': ['Mount']] + ], + 'systemd.path.xml' : + ['sections': + ['Options': ['Path']] + ], + 'systemd.socket.xml' : + ['sections': + ['Options': ['Socket']] + ], + 'systemd.swap.xml' : + ['sections': + ['Options': ['Swap']] + ], + 'systemd.resource-control.xml': + ['sections': + [ + 'Options' : ['Slice', 'Service', 'Socket', 'Mount', 'Swap'], + 'Deprecated Options': ['Slice', 'Service', 'Socket', 'Mount', 'Swap'], + ] + ], + 'systemd.kill.xml' : + ['sections': + ['Options': ['Service', "Socket", "Mount", "Swap"]] + ], + 'systemd.exec.xml' : + ['sections': + [ + 'Paths' : ['Service', 'Socket', 'Mount', 'Swap'], + 'Credentials' : ['Service', 'Socket', 'Mount', 'Swap'], + 'User/Group Identity' : ['Service', 'Socket', 'Mount', 'Swap'], + 'Capabilities' : ['Service', 'Socket', 'Mount', 'Swap'], + 'Security' : ['Service', 'Socket', 'Mount', 'Swap'], + 'Mandatory Access Control' : ['Service', 'Socket', 'Mount', 'Swap'], + 'Process Properties' : ['Service', 'Socket', 'Mount', 'Swap'], + 'Scheduling' : ['Service', 'Socket', 'Mount', 'Swap'], + 'Sandboxing' : ['Service', 'Socket', 'Mount', 'Swap'], + 'System Call Filtering' : ['Service', 'Socket', 'Mount', 'Swap'], + 'Environment' : ['Service', 'Socket', 'Mount', 'Swap'], + 'Logging and Standard Input/Output': ['Service', 'Socket', 'Mount', 'Swap'], + 'System V Compatibility' : ['Service', 'Socket', 'Mount', 'Swap'], + ] + ]], + 'nspawn': [ + 'systemd.nspawn.xml': + ['sections': + [ + '[Exec] Section Options' : ['Exec'], + '[Files] Section Options' : ['Files'], + '[Network] Section Options': ['Network'], + ] + ]] + ] @Internal - Map>> sectionToKeyWordMapFromDoc = [:] + Map>>> fileTypeToSectionToKeyWordMapFromDoc = [:] @Internal final XPath xpath @@ -149,14 +160,22 @@ class GenerateDataFromManPages extends DefaultTask { logger.debug("Regenerating valid keys") - fileAndSectionTitleToSectionName.keySet().each { file -> - logger.debug("Starting $file") - processFile(file) + fileTypeToFileAndSectionTitleToSectionName.entrySet().each { + fileToSectionToKeyWordMapFromDoc -> fileToSectionToKeyWordMapFromDoc.value.keySet().each { + file -> + logger.debug("Starting $file") + var fileType = fileToSectionToKeyWordMapFromDoc.key + processFile(fileType, file) + } + + } + + logger.debug("Complete") - def json = JsonOutput.toJson(this.sectionToKeyWordMapFromDoc) + def json = JsonOutput.toJson(this.fileTypeToSectionToKeyWordMapFromDoc) json = JsonOutput.prettyPrint(json) File outputData = new File(this.generatedJsonFileLocation.getAbsolutePath() + "/sectionToKeywordMapFromDoc.json") @@ -173,12 +192,12 @@ class GenerateDataFromManPages extends DefaultTask { * * @param filename */ - void processFile(String filename) { + void processFile(String fileType, String filename) { File file = new File(this.systemdSourceCodeRoot.getAbsolutePath() + "/man/$filename") - generateKeywordAndValueJsonMapForFile(file) + generateKeywordAndValueJsonMapForFile(fileType, file) - generateDocumentationHtmlFromManPages(file) + generateDocumentationHtmlFromManPages(fileType, file) } /** @@ -186,7 +205,7 @@ class GenerateDataFromManPages extends DefaultTask { * * @param File file */ - private void generateKeywordAndValueJsonMapForFile(File file) { + private void generateKeywordAndValueJsonMapForFile(String fileType, File file) { String filename = file.getName() @@ -206,12 +225,21 @@ class GenerateDataFromManPages extends DefaultTask { "/refentry/refsect1/variablelist[not(contains(@class,'environment-variables'))]/varlistentry", records, XPathConstants.NODESET); } + else if (file.getAbsolutePath().endsWith("systemd.nspawn.xml")) { + result = (NodeList)xpath.evaluate( + "//variablelist[(contains(@class,'nspawn-directives'))]/varlistentry", + records, XPathConstants.NODESET); + } else { result = (NodeList)xpath.evaluate( "//variablelist[(contains(@class,'unit-directives'))]/varlistentry", records, XPathConstants.NODESET); } + if (result.getLength() == 0) { + throw new IllegalStateException("Could not find variables under $filename") + } + for (int i = 0; i < result.getLength(); i++) { Node varListEntry = result.item(i) @@ -227,7 +255,7 @@ class GenerateDataFromManPages extends DefaultTask { try { String titleOfSection = xpath.evaluate("ancestor::refsect1/title[text()]", varListEntry) - List sections = fileAndSectionTitleToSectionName[filename]['sections'][titleOfSection] + List sections = fileTypeToFileAndSectionTitleToSectionName[fileType][filename]['sections'][titleOfSection] String originalSection = xpath.evaluate("term/varname[text()]", varListEntry, XPathConstants.STRING) @@ -235,11 +263,12 @@ class GenerateDataFromManPages extends DefaultTask { for (String section : sections) { logger.debug("Found options $section in $option in ${file.getAbsolutePath()}") - sectionToKeyWordMapFromDoc.putIfAbsent(section, new TreeMap<>()) + fileTypeToSectionToKeyWordMapFromDoc.putIfAbsent(fileType, new TreeMap<>()) + fileTypeToSectionToKeyWordMapFromDoc.get(fileType).putIfAbsent(section, new TreeMap<>()) def val = ["declaredInFile": filename] if (!keyValue.isEmpty()) val["values"] = keyValue if (keyName != originalKeyName) val["declaredUnderKeyword"] = originalKeyName - sectionToKeyWordMapFromDoc[section][keyName] = val + fileTypeToSectionToKeyWordMapFromDoc[fileType][section][keyName] = val } } catch (IllegalStateException e) { @@ -272,14 +301,14 @@ class GenerateDataFromManPages extends DefaultTask { * @param File sourceFile - the source file to extract * @return */ - private generateDocumentationHtmlFromManPages(File sourceFile) { + private generateDocumentationHtmlFromManPages(String fileType, File sourceFile) { DocumentBuilder builder = dbf.newDocumentBuilder() Document document = builder.parse(sourceFile) Transformer transformer = getXsltTransformer() String xsltOutput = transformDocument(document, transformer) - segmentParametersIntoFiles(sourceFile.getName(), xsltOutput) + segmentParametersIntoFiles(fileType, sourceFile.getName(), xsltOutput) } /** @@ -339,7 +368,7 @@ class GenerateDataFromManPages extends DefaultTask { * @param sourceFileName - the name of the source file we pulled the data from * @param parameterInfoXMLAsString - A transformed XML document representing the documentation for systemd */ - private void segmentParametersIntoFiles(String sourceFileName, String parameterInfoXMLAsString) { + private void segmentParametersIntoFiles(String fileType, String sourceFileName, String parameterInfoXMLAsString) { def builder = dbf.newDocumentBuilder() ByteArrayInputStream bis = new ByteArrayInputStream(parameterInfoXMLAsString.getBytes("UTF-8")) @@ -368,11 +397,11 @@ class GenerateDataFromManPages extends DefaultTask { String name = match.group(1) - List foo = fileAndSectionTitleToSectionName[sourceFileName]['sections'][sectionTitle] + List foo = fileTypeToFileAndSectionTitleToSectionName[fileType][sourceFileName]['sections'][sectionTitle] for (String sectionName : foo) { File outputFile = new File( - this.generatedJsonFileLocation.getAbsolutePath() + "/documents/completion/" + sectionName + "/" + name + ".html") + this.generatedJsonFileLocation.getAbsolutePath() + "/documents/completion/" + fileType + "/" + sectionName + "/" + name + ".html") outputFile.getParentFile().mkdirs() Writer write = new BufferedWriter(new FileWriter(outputFile)) diff --git a/ci/release.Jenkinsfile b/ci/release.Jenkinsfile index ec1206d7..dfe43a7f 100644 --- a/ci/release.Jenkinsfile +++ b/ci/release.Jenkinsfile @@ -310,7 +310,9 @@ pipeline { sh(""" mkdir -p ./systemd-build/build - cp /opt/systemd-source/systemd/*.gperf ./systemd-build/build + cat /opt/systemd-source/systemd/last_commit_date + cat /opt/systemd-source/systemd/last_commit_hash + cp -R /mount/* ./systemd-build/build cp /opt/systemd-source/systemd/last_commit_date /opt/systemd-source/systemd/last_commit_hash ./systemd-build/build cp -R /opt/systemd-source/systemd/man ./systemd-build/build """) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/UnitFileIcon.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/UnitFileIcon.kt index b098d8f2..676879a6 100644 --- a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/UnitFileIcon.kt +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/UnitFileIcon.kt @@ -7,6 +7,7 @@ object UnitFileIcon { @JvmField val FILE = IconLoader.getIcon("/net/sjrx/intellij/plugins/systemdunitfiles/systemd.svg", UnitFileIcon::class.java) val AUTOMOUNT = IconLoader.getIcon("/net/sjrx/intellij/plugins/systemdunitfiles/automount.svg", UnitFileIcon::class.java) + val NSPAWN = IconLoader.getIcon("/net/sjrx/intellij/plugins/systemdunitfiles/nspawn.svg", UnitFileIcon::class.java) val DEVICE = IconLoader.getIcon("/net/sjrx/intellij/plugins/systemdunitfiles/device.svg", UnitFileIcon::class.java) val MOUNT = IconLoader.getIcon("/net/sjrx/intellij/plugins/systemdunitfiles/mount.svg", UnitFileIcon::class.java) val PATH = IconLoader.getIcon("/net/sjrx/intellij/plugins/systemdunitfiles/path.svg", UnitFileIcon::class.java) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/completion/UnitFileKeyCompletionContributor.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/completion/UnitFileKeyCompletionContributor.kt index e15dc807..5e77d3e2 100644 --- a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/completion/UnitFileKeyCompletionContributor.kt +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/completion/UnitFileKeyCompletionContributor.kt @@ -14,6 +14,8 @@ import net.sjrx.intellij.plugins.systemdunitfiles.generated.UnitFileElementTypeH import net.sjrx.intellij.plugins.systemdunitfiles.psi.UnitFileProperty import net.sjrx.intellij.plugins.systemdunitfiles.psi.UnitFileSectionGroups import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.SemanticDataRepository +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.fileClass + import java.util.stream.Collectors /** @@ -35,9 +37,12 @@ class UnitFileKeyCompletionContributor : CompletionContributor() { val sectionName = section.sectionName val definedKeys = section.propertyList.stream().map { obj: UnitFileProperty -> obj.key }.collect(Collectors.toSet()) val sdr = SemanticDataRepository.instance - for (keyword in sdr.getDocumentedKeywordsInSection(sectionName)) { + + val fileClass = section.containingFile.fileClass() + + for (keyword in sdr.getDocumentedKeywordsInSection(fileClass, sectionName)) { if (definedKeys.contains(keyword)) continue - val deprecated = sdr.isDeprecated(sectionName, keyword) + val deprecated = sdr.isDeprecated(fileClass, sectionName, keyword) val builder = LookupElementBuilder .create(keyword) .withInsertHandler(KeyInsertHandler()) diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/completion/UnitFileValueCompletionContributor.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/completion/UnitFileValueCompletionContributor.kt index 3607eae6..7b29e5fa 100644 --- a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/completion/UnitFileValueCompletionContributor.kt +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/completion/UnitFileValueCompletionContributor.kt @@ -10,6 +10,7 @@ import net.sjrx.intellij.plugins.systemdunitfiles.generated.UnitFileElementTypeH import net.sjrx.intellij.plugins.systemdunitfiles.psi.UnitFileProperty import net.sjrx.intellij.plugins.systemdunitfiles.psi.UnitFileSectionGroups import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.SemanticDataRepository +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.fileClass import java.util.function.Supplier import java.util.stream.Collectors @@ -44,7 +45,9 @@ class UnitFileValueCompletionContributor : CompletionContributor() { val sectionName = section.sectionName val keyName = property.key val sdr = SemanticDataRepository.instance - val validator = sdr.getOptionValidator(sectionName, keyName) + val fileClass = section.containingFile.fileClass() + + val validator = sdr.getOptionValidator(fileClass, sectionName, keyName) resultSet.addAllElements( validator.getAutoCompleteOptions(property.project) .stream() diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/documentation/UnitFileDocumentationProvider.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/documentation/UnitFileDocumentationProvider.kt index 04d53ff4..abb260b9 100644 --- a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/documentation/UnitFileDocumentationProvider.kt +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/documentation/UnitFileDocumentationProvider.kt @@ -11,6 +11,7 @@ import net.sjrx.intellij.plugins.systemdunitfiles.generated.UnitFileElementTypeH import net.sjrx.intellij.plugins.systemdunitfiles.psi.UnitFilePropertyType import net.sjrx.intellij.plugins.systemdunitfiles.psi.UnitFileSectionType import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.SemanticDataRepository +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.fileClass class UnitFileDocumentationProvider : AbstractDocumentationProvider() { override fun getQuickNavigateInfo(element: PsiElement, originalElement: PsiElement): String? { @@ -26,7 +27,9 @@ class UnitFileDocumentationProvider : AbstractDocumentationProvider() { val sectionName = section.sectionName val keyName = element.node.text val sdr: SemanticDataRepository = SemanticDataRepository.instance - val keyComment = sdr.getDocumentationContentForKeyInSection(sectionName, keyName) + val fileClass = section.containingFile.fileClass() + + val keyComment = sdr.getDocumentationContentForKeyInSection(fileClass, sectionName, keyName) if (keyComment != null) { return DocumentationMarkup.DEFINITION_START + keyName + DocumentationMarkup.DEFINITION_END + DocumentationMarkup.CONTENT_START + keyComment + DocumentationMarkup.CONTENT_END } @@ -53,13 +56,15 @@ class UnitFileDocumentationProvider : AbstractDocumentationProvider() { val section = PsiTreeUtil.getParentOfType(element, UnitFileSectionType::class.java) ?: return null val sectionName = section.sectionName val keyName = element.text + val fileClass = section.containingFile.fileClass() + val sdr: SemanticDataRepository = SemanticDataRepository.instance - val url = sdr.getKeywordDocumentationUrl(sectionName, keyName) + val url = sdr.getKeywordDocumentationUrl(fileClass, sectionName, keyName) if (url != null) { return listOf(url) } - val keyNameToPointTo = sdr.getKeywordLocationInDocumentation(sectionName, keyName) - val filename = sdr.getKeywordFileLocationInDocumentation(sectionName, keyName) + val keyNameToPointTo = sdr.getKeywordLocationInDocumentation(fileClass, sectionName, keyName) + val filename = sdr.getKeywordFileLocationInDocumentation(fileClass, sectionName, keyName) if (keyNameToPointTo != null && filename != null) { return listOf( "https://www.freedesktop.org/software/systemd/man/" + filename.replaceFirst(".xml$".toRegex(), ".html") + "#" diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/filetypes/NSpawnFileType.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/filetypes/NSpawnFileType.kt new file mode 100644 index 00000000..a6f6d82b --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/filetypes/NSpawnFileType.kt @@ -0,0 +1,32 @@ +package net.sjrx.intellij.plugins.systemdunitfiles.filetypes + +import net.sjrx.intellij.plugins.systemdunitfiles.UnitFileIcon +import net.sjrx.intellij.plugins.systemdunitfiles.UnitFileLanguage +import org.jetbrains.annotations.Nls +import javax.swing.Icon + +class NSpawnFileType private constructor() : AbstractUnitFileType(UnitFileLanguage.INSTANCE) { + override fun getName(): String { + return "Nspawn container systemd container images" + } + + override fun getDescription(): String { + return displayName + } + + override fun getDefaultExtension(): String { + return "nspawn" + } + + override fun getIcon(): Icon? { + return UnitFileIcon.NSPAWN + } + + override fun getDisplayName(): @Nls String { + return "NSpawn container settings file" + } + + companion object { + val INSTANCE = NSpawnFileType() + } +} diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/DeprecatedOptionsInspection.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/DeprecatedOptionsInspection.kt index 0165ca42..17ab6248 100644 --- a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/DeprecatedOptionsInspection.kt +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/DeprecatedOptionsInspection.kt @@ -11,6 +11,7 @@ import net.sjrx.intellij.plugins.systemdunitfiles.psi.UnitFilePropertyType import net.sjrx.intellij.plugins.systemdunitfiles.psi.UnitFileSectionGroups import net.sjrx.intellij.plugins.systemdunitfiles.psi.UnitFileVisitor import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.SemanticDataRepository +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.fileClass class DeprecatedOptionsInspection : LocalInspectionTool() { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { @@ -26,8 +27,10 @@ class DeprecatedOptionsInspection : LocalInspectionTool() { val sdr = SemanticDataRepository.instance val sectionName = section.sectionName val key = property.key - if (sdr.isDeprecated(sectionName, key)) { - val text = sdr.getDeprecationReason(sectionName, key, false) + val fileClass = section.containingFile.fileClass() + + if (sdr.isDeprecated(fileClass, sectionName, key)) { + val text = sdr.getDeprecationReason(fileClass, sectionName, key, false) holder.registerProblem(property.keyNode.psi, text!!, ProblemHighlightType.LIKE_DEPRECATED) } } diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/InvalidValueInspection.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/InvalidValueInspection.kt index bd2b9ba1..904e607c 100644 --- a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/InvalidValueInspection.kt +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/InvalidValueInspection.kt @@ -10,6 +10,7 @@ import net.sjrx.intellij.plugins.systemdunitfiles.psi.UnitFilePropertyType import net.sjrx.intellij.plugins.systemdunitfiles.psi.UnitFileSectionGroups import net.sjrx.intellij.plugins.systemdunitfiles.psi.UnitFileVisitor import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.SemanticDataRepository +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.fileClass class InvalidValueInspection : LocalInspectionTool() { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { @@ -24,7 +25,8 @@ class InvalidValueInspection : LocalInspectionTool() { val section = PsiTreeUtil.getParentOfType(property, UnitFileSectionGroups::class.java) ?: return property.valueText ?: return val key = property.key - val ovi = SemanticDataRepository.instance.getOptionValidator(section.sectionName, key) + val fileClass = section.containingFile.fileClass() + val ovi = SemanticDataRepository.instance.getOptionValidator(fileClass, section.sectionName, key) ovi.generateProblemDescriptors(property, holder) } } diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ShellSyntaxInExecDirectiveInspection.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ShellSyntaxInExecDirectiveInspection.kt index f7be8730..fa7dd5f0 100644 --- a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ShellSyntaxInExecDirectiveInspection.kt +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/ShellSyntaxInExecDirectiveInspection.kt @@ -12,6 +12,7 @@ import net.sjrx.intellij.plugins.systemdunitfiles.psi.UnitFilePropertyType import net.sjrx.intellij.plugins.systemdunitfiles.psi.UnitFileSectionGroups import net.sjrx.intellij.plugins.systemdunitfiles.psi.UnitFileVisitor import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.SemanticDataRepository +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.fileClass import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.ExecOptionValue import java.util.regex.Pattern @@ -38,7 +39,8 @@ class ShellSyntaxInExecDirectiveInspection : LocalInspectionTool() { val section = PsiTreeUtil.getParentOfType(property, UnitFileSectionGroups::class.java) ?: return val value = property.valueText ?: return val key = property.key - if (SemanticDataRepository.instance.getOptionValidator(section.sectionName, key) !is ExecOptionValue) return + val fileClass = section.containingFile.fileClass() + if (SemanticDataRepository.instance.getOptionValidator(fileClass, section.sectionName, key) !is ExecOptionValue) return val completedString = StringBuilder() val im = holder.manager while (completedString.length < value.length) { diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/UnknownKeyInSectionInspection.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/UnknownKeyInSectionInspection.kt index e69983bd..f55bc9e0 100644 --- a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/UnknownKeyInSectionInspection.kt +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/UnknownKeyInSectionInspection.kt @@ -11,6 +11,7 @@ import net.sjrx.intellij.plugins.systemdunitfiles.psi.UnitFilePropertyType import net.sjrx.intellij.plugins.systemdunitfiles.psi.UnitFileSectionGroups import net.sjrx.intellij.plugins.systemdunitfiles.psi.UnitFileVisitor import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.SemanticDataRepository +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.fileClass /** * The purpose of this inspection is to catch any warnings that may be generated by systemd when processing a unit file. @@ -45,7 +46,8 @@ class UnknownKeyInSectionInspection : LocalInspectionTool() { return } - if (!SemanticDataRepository.instance.getAllowedKeywordsInSectionFromValidators(sectionName).contains(key)) { + val fileClass = section.containingFile.fileClass() + if (!SemanticDataRepository.instance.getAllowedKeywordsInSectionFromValidators(fileClass, sectionName).contains(key)) { // TODO Figure out what highlight to use holder.registerProblem(property.keyNode.psi, INSPECTION_TOOL_TIP_TEXT, ProblemHighlightType.GENERIC_ERROR_OR_WARNING) } diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/ConfigFiles.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/ConfigFiles.kt new file mode 100644 index 00000000..996c89ae --- /dev/null +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/ConfigFiles.kt @@ -0,0 +1,8 @@ +package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata + + +interface ConfigFile { + val extension : String + val allowedSections : List + val requiredSections : List +} diff --git a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/SemanticDataRepository.kt b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/SemanticDataRepository.kt index 69b60c0f..7796b386 100644 --- a/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/SemanticDataRepository.kt +++ b/src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/SemanticDataRepository.kt @@ -10,21 +10,37 @@ import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.util.NotNullLazyValue import com.intellij.openapi.util.text.StringUtil +import com.intellij.psi.PsiFile import com.intellij.util.ObjectUtils import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.* import org.apache.commons.io.IOUtils import java.io.BufferedReader +import java.io.File import java.io.IOException import java.io.InputStreamReader import java.nio.charset.StandardCharsets import java.util.* import java.util.regex.Pattern +import kotlin.collections.HashMap + +enum class FileClass(val fileClass: String, val gperfFile: String) { + UNIT_FILE("unit", "load-fragment-gperf.gperf"), + NSPAWN("nspawn", "nspawn-gperf.gperf"); +} + + +fun PsiFile.fileClass() : FileClass { + return SemanticDataRepository.getFileClassForFilename(this.name) +} class SemanticDataRepository private constructor() { private val validatorMap: Map - private val sectionToKeyAndValidatorMap: MutableMap> = HashMap() - private val sectionNameToKeyValuesFromDoc: MutableMap> - private val undocumentedOptionInfo: Map> + + private val fileClassToSectionToKeyAndValidatorMap: MutableMap>> = HashMap() + + private val fileClassToSectionNameToKeyValuesFromDoc: MutableMap>> + + private val fileClassToUndocumentedOptionInfo: MutableMap>> class KeywordData { var declaredUnderKeyword: String? = null @@ -42,71 +58,74 @@ class SemanticDataRepository private constructor() { val cache: Map = HashMap() val cache2: Map = HashMap() - sectionNameToKeyValuesFromDoc = loadSemanticDataFromJsonFile(SEMANTIC_DATA_ROOT + "sectionToKeywordMapFromDoc.json", cache) - undocumentedOptionInfo = loadSemanticDataFromJsonFile(SEMANTIC_DATA_ROOT + "undocumentedSectionToKeywordMap.json", cache) - validatorMap = HashMap() - val resource = this.javaClass.classLoader.getResourceAsStream(SEMANTIC_DATA_ROOT + "load-fragment-gperf.gperf") - if (resource != null) { - try { - BufferedReader(InputStreamReader(resource)).use { fr -> - var line: String? - while (fr.readLine().also { line = it } != null) { - val m = LINE_MATCHER.matcher(line) - if (m.find()) { - val section = m.group("Section") - val key = m.group("Key") - val validator = m.group("Validator") - val mysteryValue = m.group("MysteryColumn") - when (mysteryValue) { - LEGACY_PARAMETERS_KEY, EXPERIMENTAL_PARAMETERS_KEY -> {} - - - else -> { - val myValidator = Validator(validator, mysteryValue) - sectionToKeyAndValidatorMap.computeIfAbsent(intern(cache, section)) { _: String? -> HashMap() }[intern(cache, key)] = intern(cache2, myValidator) + fileClassToSectionNameToKeyValuesFromDoc = loadSemanticDataFromJsonFile(SEMANTIC_DATA_ROOT + "sectionToKeywordMapFromDoc.json", cache) + fileClassToUndocumentedOptionInfo = loadSemanticDataFromJsonFile(SEMANTIC_DATA_ROOT + "undocumentedSectionToKeywordMap.json", cache) + + + for (fileClass in FileClass.entries) { + val name = SEMANTIC_DATA_ROOT + fileClass.gperfFile + val resource = this.javaClass.classLoader.getResourceAsStream(name) + if (resource != null) { + try { + BufferedReader(InputStreamReader(resource)).use { fr -> + var line: String? + while (fr.readLine().also { line = it } != null) { + val m = LINE_MATCHER.matcher(line) + if (m.find()) { + val section = m.group("Section") + val key = m.group("Key") + val validator = m.group("Validator") + val mysteryValue = m.group("MysteryColumn") + when (mysteryValue) { + LEGACY_PARAMETERS_KEY, EXPERIMENTAL_PARAMETERS_KEY -> {} + + + else -> { + val myValidator = Validator(validator, mysteryValue) + fileClassToSectionToKeyAndValidatorMap.computeIfAbsent(fileClass.fileClass, { _: String? -> HashMap() }).computeIfAbsent(intern(cache, section)) { _: String? -> HashMap() }[intern(cache, key)] = intern(cache2, myValidator) + } } } } } - - validatorMap.putAll(BooleanOptionValue.validators) - validatorMap.putAll(TriStateOptionValue.validators) - validatorMap.putAll(DocumentationOptionValue.validators) - validatorMap.putAll(ModeStringOptionValue.validators) - validatorMap.putAll(ExecOptionValue.validators) - validatorMap.putAll(ExecOutputOptionValue.validators) - validatorMap.putAll(UnitDependencyOptionValue.validators) - validatorMap.putAll(NullOptionValue.validators) - validatorMap.putAll(MemoryLimitOptionValue.validators) - validatorMap.putAll(SignalOptionValue.validators) - validatorMap.putAll(NamespacePathOptionValue.validators) - validatorMap.putAll(UnsignedIntegerOptionValue.validators) - validatorMap.putAll(PathOptionValue.validators) - validatorMap.putAll(EnumOptionValues.validators) - validatorMap.putAll(ConfigParseSecValidators.validators) - validatorMap.putAll(AllowedCpuSetOptionValue.validators) - validatorMap.putAll(TtySizeOptionValue.validators) - validatorMap.putAll(ExecDirectoriesOptionValue.validators) - - validatorMap.putAll(IOLimitOptionValue.validators) - validatorMap.putAll(ImagePolicyOptionValue.validators) - validatorMap.putAll(CPUWeightOptionValue.validators) - validatorMap.putAll(CPUSharesOptionValue.validators) - - // Scopes are not supported since they aren't standard unit files. - - sectionNameToKeyValuesFromDoc.remove(SCOPE_KEYWORD) - sectionToKeyAndValidatorMap.remove(SCOPE_KEYWORD) + } catch (e: IOException) { + LOG.error("Unable to initialize data for systemd inspections plugin", e) } - } catch (e: IOException) { - LOG.error("Unable to initialize data for systemd inspections plugin", e) + } else { + LOG.error("Unable to initialize data for systemd inspections plugin, resource $name not found") } - } else { - LOG.error("Unable to initialize data for systemd inspections plugin, resource not found") } + + validatorMap = HashMap() + validatorMap.putAll(BooleanOptionValue.validators) + validatorMap.putAll(TriStateOptionValue.validators) + validatorMap.putAll(DocumentationOptionValue.validators) + validatorMap.putAll(ModeStringOptionValue.validators) + validatorMap.putAll(ExecOptionValue.validators) + validatorMap.putAll(ExecOutputOptionValue.validators) + validatorMap.putAll(UnitDependencyOptionValue.validators) + validatorMap.putAll(NullOptionValue.validators) + validatorMap.putAll(MemoryLimitOptionValue.validators) + validatorMap.putAll(SignalOptionValue.validators) + validatorMap.putAll(NamespacePathOptionValue.validators) + validatorMap.putAll(UnsignedIntegerOptionValue.validators) + validatorMap.putAll(PathOptionValue.validators) + validatorMap.putAll(EnumOptionValues.validators) + validatorMap.putAll(ConfigParseSecValidators.validators) + validatorMap.putAll(AllowedCpuSetOptionValue.validators) + validatorMap.putAll(TtySizeOptionValue.validators) + validatorMap.putAll(ExecDirectoriesOptionValue.validators) + + validatorMap.putAll(IOLimitOptionValue.validators) + validatorMap.putAll(ImagePolicyOptionValue.validators) + validatorMap.putAll(CPUWeightOptionValue.validators) + validatorMap.putAll(CPUSharesOptionValue.validators) + // Scopes are not supported since they aren't standard unit files. + fileClassToSectionNameToKeyValuesFromDoc["unit"]?.remove(SCOPE_KEYWORD) + fileClassToSectionToKeyAndValidatorMap["unit"]?.remove(SCOPE_KEYWORD) } - private fun loadSemanticDataFromJsonFile(filename: String, cache: Map): MutableMap> { + private fun loadSemanticDataFromJsonFile(filename: String, cache: Map): MutableMap>> { val sectionToKeywordMapFromDocJsonFile = this.javaClass.classLoader.getResource(filename) val factory = JsonFactoryBuilder().disable(JsonFactory.Feature.INTERN_FIELD_NAMES).build() if (!ApplicationManager.getApplication().isUnitTestMode) { @@ -117,15 +136,21 @@ class SemanticDataRepository private constructor() { mapper.registerModule(KotlinModule.Builder().build()) try { - val read : Map> = mapper.readValue(sectionToKeywordMapFromDocJsonFile) + val read : Map>> = mapper.readValue(sectionToKeywordMapFromDocJsonFile) // intern strings in maps - val output: MutableMap> = HashMap() + val output: MutableMap>> = HashMap() + + read.forEach { (fileClass: String, m0: Map>) -> + val fileClassMap = HashMap>() + output[fileClass] = fileClassMap - read.forEach { (section: String, m1: Map) -> - val converted = HashMap() - m1.forEach { (keyword: String, m2: KeywordData) -> converted[intern(cache, keyword)] = m2 } - output[intern(cache, section)] = converted + + m0.forEach { (section: String, m1: Map) -> + val converted = HashMap() + m1.forEach { (keyword: String, m2: KeywordData) -> converted[intern(cache, keyword)] = m2 } + fileClassMap[intern(cache, section)] = converted + } } return output @@ -137,13 +162,10 @@ class SemanticDataRepository private constructor() { } } - val sectionNamesFromDocumentation: Set - /** - * Returns the allowed section names. - * - * @return a set of allow section names - */ - get() = Collections.unmodifiableSet(sectionNameToKeyValuesFromDoc.keys) + fun getSectionNamesForFile(fileClass: String): Set { + return Collections.unmodifiableSet(fileClassToSectionNameToKeyValuesFromDoc.getOrDefault(fileClass, emptyMap()).keys) + } + /** * Returns the allowed keywords by a given section name. @@ -151,9 +173,9 @@ class SemanticDataRepository private constructor() { * @param section the section name (e.g., Unit, Install, Service) * @return set of allowed names. */ - fun getDocumentedKeywordsInSection(section: String): Set { - val keys: MutableSet = HashSet(getKeyValuePairsForSectionFromDocumentation(section).keys) - keys.addAll(getKeyValuePairsForSectionFromUndocumentedInformation(section).keys) + fun getDocumentedKeywordsInSection(fileclass: FileClass, section: String): Set { + val keys: MutableSet = HashSet(getKeyValuePairsForSectionFromDocumentation(fileclass, section).keys) + keys.addAll(getKeyValuePairsForSectionFromUndocumentedInformation(fileclass, section).keys) return Collections.unmodifiableSet(keys) } @@ -164,12 +186,12 @@ class SemanticDataRepository private constructor() { * @param keyName - key name * @return String or null */ - fun getKeywordDocumentationUrl(section: String, keyName: String): String? { - var data = getKeyValuePairsForSectionFromDocumentation(section)[keyName] + fun getKeywordDocumentationUrl(fileclass: FileClass, section: String, keyName: String): String? { + var data = getKeyValuePairsForSectionFromDocumentation(fileclass, section)[keyName] if (data != null && data.documentationLink != null) { return data.documentationLink } - data = getKeyValuePairsForSectionFromUndocumentedInformation(section)[keyName] + data = getKeyValuePairsForSectionFromUndocumentedInformation(fileclass, section)[keyName] return if (data != null && data.documentationLink != null) { data.documentationLink } else null @@ -186,8 +208,8 @@ class SemanticDataRepository private constructor() { * @param keyName - key name * @return String or null */ - fun getKeywordLocationInDocumentation(section: String, keyName: String): String? { - val data = getKeyValuePairsForSectionFromDocumentation(section)[keyName] ?: return null + fun getKeywordLocationInDocumentation(fileclass: FileClass, section: String, keyName: String): String? { + val data = getKeyValuePairsForSectionFromDocumentation(fileclass, section)[keyName] ?: return null return ObjectUtils.notNull(data.declaredUnderKeyword, keyName) } @@ -202,17 +224,17 @@ class SemanticDataRepository private constructor() { * @param keyName - key name * @return String or null */ - fun getKeywordFileLocationInDocumentation(section: String, keyName: String): String? { - val data = getKeyValuePairsForSectionFromDocumentation(section)[keyName] + fun getKeywordFileLocationInDocumentation(fileclass: FileClass, section: String, keyName: String): String? { + val data = getKeyValuePairsForSectionFromDocumentation(fileclass, section)[keyName] return data?.declaredInFile } - fun getKeyValuePairsForSectionFromDocumentation(section: String): Map { - return sectionNameToKeyValuesFromDoc[section] ?: emptyMap() + fun getKeyValuePairsForSectionFromDocumentation(fileClass: FileClass, section: String): Map { + return fileClassToSectionNameToKeyValuesFromDoc[fileClass.fileClass]?.get(section) ?: emptyMap() } - fun getKeyValuePairsForSectionFromUndocumentedInformation(section: String): Map { - return undocumentedOptionInfo[section] ?: emptyMap() + fun getKeyValuePairsForSectionFromUndocumentedInformation(fileClass: FileClass, section: String): Map { + return fileClassToUndocumentedOptionInfo[fileClass.fileClass]?.get(section) ?: emptyMap() } /** @@ -233,6 +255,9 @@ class SemanticDataRepository private constructor() { "Swap" -> "https://www.freedesktop.org/software/systemd/man/systemd.swap.html" "Timer" -> "https://www.freedesktop.org/software/systemd/man/systemd.timer.html" "Unit" -> "https://www.freedesktop.org/software/systemd/man/systemd.unit.html#%5BUnit%5D%20Section%20Options" + "Exec" -> "https://www.freedesktop.org/software/systemd/man/latest/systemd.nspawn.html#%5BExec%5D%20Section%20Options" + "Files" -> "https://www.freedesktop.org/software/systemd/man/latest/systemd.nspawn.html#%5BFiles%5D%20Section%20Options" + "Network" -> "https://www.freedesktop.org/software/systemd/man/latest/systemd.nspawn.html#%5BNetwork%5D%20Section%20Options" else -> null } } @@ -283,12 +308,15 @@ unit types. These options are documented in systemd.exec(5) and systemd.kill(5)""" - "Timer" -> """ Timer files must include a [Timer] section, which carries information + "Timer" -> """Timer files must include a [Timer] section, which carries information about the timer it defines.""" "Unit" -> """The unit file may include a [Unit] section, which carries generic information about the unit that is not dependent on the type of unit.""" + "Exec" -> """The [Exec] section controls various execution parameters.""" + "Files" -> """The [Files] section, carries various parameters configuring the filesystem of the container.""" + "Network" -> """The [Network] section carries various parameters configuring the network connectivity of the container.""" else -> null } } @@ -300,11 +328,14 @@ unit types. These options are documented in { if (!html) String.format("'%s' in section '%s' is not officially supported", keyName, sectionName) else ("

" + keyName + " in section " + sectionName + " is not officially supported.

" @@ -371,8 +402,8 @@ unit types. These options are documented in = validatorMap - val sectionNamesFromValidators: Set - /** - * Return the section names from the validators. - * - * @return set - */ - get() = Collections.unmodifiableSet(sectionToKeyAndValidatorMap.keys) + /** + * Return the section names from the validators. + * + * @return set + */ + fun getSectionNamesForSectionAndKey(fileClass: FileClass): MutableSet { + return Collections.unmodifiableSet(fileClassToSectionToKeyAndValidatorMap.getOrDefault(fileClass.fileClass, emptyMap()).keys) + } - fun getValidatorForSectionAndKey(sectionName: String, keyName: String) : Validator { - var validatorKey = sectionToKeyAndValidatorMap.getOrDefault(sectionName, emptyMap())[keyName] + + fun getValidatorForSectionAndKey(fileClass: FileClass, sectionName: String, keyName: String) : Validator { + + var validatorKey = fileClassToSectionToKeyAndValidatorMap.getOrDefault(fileClass.fileClass, emptyMap()).getOrDefault(sectionName, emptyMap())[keyName] if (sectionName.trim { it <= ' ' } == "Install") { if (keyName.trim { it <= ' ' } == "WantedBy" || keyName.trim { it <= ' ' } == "Also" || keyName.trim { it <= ' ' } == "RequiredBy") { // Override the validators for these cases since there is no validation done now, but really they should be units. @@ -427,8 +461,8 @@ unit types. These options are documented in setOf("Unit", "Install", "Socket") "Swap" -> setOf("Unit", "Install", "Swap") "Timer" -> setOf("Unit", "Install", "Timer") + "Nspawn" -> setOf("Exec", "Files", "Network") else -> setOf() } @@ -497,6 +532,17 @@ unit types. These options are documented in FileClass.NSPAWN + "service", "socket", "target", "device", "mount", "automount", "path", "swap", "timer", "slice"-> FileClass.UNIT_FILE + + else -> FileClass.UNIT_FILE + } + } + + } } diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 5c28b06d..39925a99 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -47,6 +47,7 @@ + diff --git a/src/main/resources/net/sjrx/intellij/plugins/systemdunitfiles/nspawn.svg b/src/main/resources/net/sjrx/intellij/plugins/systemdunitfiles/nspawn.svg new file mode 100644 index 00000000..f4cf4ce3 --- /dev/null +++ b/src/main/resources/net/sjrx/intellij/plugins/systemdunitfiles/nspawn.svg @@ -0,0 +1,140 @@ + + + + + + image/svg+xml + + + + + + + + + xml + + + + + + + + + OK + + + + diff --git a/src/main/resources/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/undocumentedSectionToKeywordMap.json b/src/main/resources/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/undocumentedSectionToKeywordMap.json index 52c9db56..ff48311a 100644 --- a/src/main/resources/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/undocumentedSectionToKeywordMap.json +++ b/src/main/resources/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/undocumentedSectionToKeywordMap.json @@ -1,419 +1,430 @@ { - "Mount": { - "BlockIOAccounting": { - "reason": "moved", - "replacedWithKey": "IOAccounting", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOAccounting=" - }, - "BlockIODeviceWeight": { - "reason": "moved", - "replacedWithKey": "IODeviceWeight", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIODeviceWeight=device%20weight" - }, - "BlockIOReadBandwidth": { - "reason": "moved", - "replacedWithKey": "IOReadBandwidthMax", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOReadBandwidth=device%20bytes" - }, - "BlockIOWeight": { - "reason": "moved", - "replacedWithKey": "IOWeight", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOWeight=weight" - }, - "BlockIOWriteBandwidth": { - "reason": "moved", - "replacedWithKey": "IOWriteBandwidthMax", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOReadBandwidth=device%20bytes" - }, - "CPUShares": { - "reason": "moved", - "replacedWithKey": "CPUWeight", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#CPUShares=weight" - }, - "DefaultMemoryLow": { - "reason": "manual", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#MemoryLow=bytes", - "description": "

DefaultMemoryLow= controls the 'memory.low' value of children. This setting has the same semantics as MemoryLow=. For details about this control attribute see cgroup-v2.txt

" - }, - "DefaultMemoryMin": { - "reason": "manual", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#MemoryMin=bytes", - "description": "

DefaultMemoryMin= controls the 'memory.min' value of children. This setting has the same semantics as MemoryMin=. For details about this control attribute see cgroup-v2.txt

" - }, - "InaccessibleDirectories": { - "reason": "moved", - "replacedWithKey": "InaccessiblePath", - "documentationLink": "https://github.com/systemd/systemd/blob/v241/NEWS#L2799" - }, - "MemoryLimit": { - "reason": "moved", - "replacedWithKey": "MemoryMax", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#MemoryLimit=bytes" - }, - "ReadOnlyDirectories": { - "reason": "moved", - "replacedWithKey": "ReadOnlyPaths", - "documentationLink": "https://github.com/systemd/systemd/blob/v241/NEWS#L2799" - }, - "ReadWriteDirectories": { - "reason": "moved", - "replacedWithKey": "ReadWritePaths", - "documentationLink": "https://github.com/systemd/systemd/blob/v241/NEWS#L2799" - }, - "StartupBlockIOWeight": { - "reason": "moved", - "replacedWithKey": "StartupIOWeight", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOWeight=weight" - }, - "StartupCPUShares": { - "reason": "moved", - "replacedWithKey": "StartupCPUWeight", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#CPUShares=weight" - } - }, - "Service": { - "BlockIOAccounting": { - "reason": "moved", - "replacedWithKey": "IOAccounting", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOAccounting=" - }, - "BlockIODeviceWeight": { - "reason": "moved", - "replacedWithKey": "IODeviceWeight", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIODeviceWeight=device%20weight" - }, - "BlockIOReadBandwidth": { - "reason": "moved", - "replacedWithKey": "IOReadBandwidthMax", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOReadBandwidth=device%20bytes" - }, - "BlockIOWeight": { - "reason": "moved", - "replacedWithKey": "IOWeight", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOWeight=weight" - }, - "BlockIOWriteBandwidth": { - "reason": "moved", - "replacedWithKey": "IOWriteBandwidthMax", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOReadBandwidth=device%20bytes" - }, - "CPUShares": { - "reason": "moved", - "replacedWithKey": "CPUWeight", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#CPUShares=weight" - }, - "DefaultMemoryLow": { - "reason": "manual", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#MemoryLow=bytes", - "description": "

DefaultMemoryLow= controls the 'memory.low' value of children. This setting has the same semantics as MemoryLow=. For details about this control attribute see cgroup-v2.txt

" - }, - "DefaultMemoryMin": { - "reason": "manual", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#MemoryMin=bytes", - "description": "

DefaultMemoryMin= controls the 'memory.min' value of children. This setting has the same semantics as MemoryMin=. For details about this control attribute see cgroup-v2.txt

" - }, - "FailureAction": { - "reason": "moved", - "replacedWithSection": "Unit", - "documentationLink": "https://github.com/systemd/systemd/commit/53c35a766f" - }, - "InaccessibleDirectories": { - "reason": "moved", - "replacedWithKey": "InaccessiblePath", - "documentationLink": "https://github.com/systemd/systemd/blob/v241/NEWS#L2799" - }, - "MemoryLimit": { - "reason": "moved", - "replacedWithKey": "MemoryMax", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#MemoryLimit=bytes" - }, - "PermissionsStartOnly": { - "reason": "unsupported", - "documentationLink": "https://github.com/systemd/systemd/blob/v241/NEWS#L561" - }, - "ReadOnlyDirectories": { - "reason": "moved", - "replacedWithKey": "ReadOnlyPaths", - "documentationLink": "https://github.com/systemd/systemd/blob/v241/NEWS#L2799" - }, - "ReadWriteDirectories": { - "reason": "moved", - "replacedWithKey": "ReadWritePaths", - "documentationLink": "https://github.com/systemd/systemd/blob/v241/NEWS#L2799" - }, - "RebootArgument": { - "reason": "moved", - "replacedWithKey": "RebootArgument", - "replacedWithSection": "Unit", - "documentationLink": "https://github.com/systemd/systemd/blob/v241/NEWS#L3443" - }, - "StartLimitAction": { - "reason": "moved", - "replacedWithSection": "Unit", - "documentationLink": "https://github.com/systemd/systemd/blob/v241/NEWS#L3443" - }, - "StartLimitInterval": { - "reason": "moved", - "replacedWithSection": "Unit", - "documentationLink": "https://github.com/systemd/systemd/blob/v241/NEWS#L3443" - }, - "StartLimitBurst": { - "reason": "moved", - "replacedWithSection": "Unit", - "documentationLink": "https://github.com/systemd/systemd/blob/v241/NEWS#L3443" - }, - "StartupBlockIOWeight": { - "reason": "moved", - "replacedWithKey": "StartupIOWeight", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOWeight=weight" - }, - "StartupCPUShares": { - "reason": "moved", - "replacedWithKey": "StartupCPUWeight", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#CPUShares=weight" + "nspawn": { + "Files": { + "PrivateUsersChown":{ + "reason": "moved", + "replacedWithKey": "PrivateUsersOwnership", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/latest/systemd.nspawn.html#PrivateUsersOwnership=" + } } }, - "Slice": { - "BlockIOAccounting": { - "reason": "moved", - "replacedWithKey": "IOAccounting", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOAccounting=" - }, - "BlockIODeviceWeight": { - "reason": "moved", - "replacedWithKey": "IODeviceWeight", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIODeviceWeight=device%20weight" - }, - "BlockIOReadBandwidth": { - "reason": "moved", - "replacedWithKey": "IOReadBandwidthMax", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOReadBandwidth=device%20bytes" - }, - "BlockIOWeight": { - "reason": "moved", - "replacedWithKey": "IOWeight", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOWeight=weight" - }, - "BlockIOWriteBandwidth": { - "reason": "moved", - "replacedWithKey": "IOWriteBandwidthMax", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOReadBandwidth=device%20bytes" - }, - "CPUShares": { - "reason": "moved", - "replacedWithKey": "CPUWeight", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#CPUShares=weight" - }, - "DefaultMemoryLow": { - "reason": "manual", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#MemoryLow=bytes", - "description": "

DefaultMemoryLow= controls the 'memory.low' value of children. This setting has the same semantics as MemoryLow=. For details about this control attribute see cgroup-v2.txt

" - }, - "DefaultMemoryMin": { - "reason": "manual", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#MemoryMin=bytes", - "description": "

DefaultMemoryMin= controls the 'memory.min' value of children. This setting has the same semantics as MemoryMin=. For details about this control attribute see cgroup-v2.txt

" - }, - "MemoryLimit": { - "reason": "moved", - "replacedWithKey": "MemoryMax", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#MemoryLimit=bytes" - }, - "StartupBlockIOWeight": { - "reason": "moved", - "replacedWithKey": "StartupIOWeight", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOWeight=weight" - }, - "StartupCPUShares": { - "reason": "moved", - "replacedWithKey": "StartupCPUWeight", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#CPUShares=weight" - } - }, - "Socket": { - "BlockIOAccounting": { - "reason": "moved", - "replacedWithKey": "IOAccounting", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOAccounting=" - }, - "BlockIODeviceWeight": { - "reason": "moved", - "replacedWithKey": "IODeviceWeight", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIODeviceWeight=device%20weight" - }, - "BlockIOReadBandwidth": { - "reason": "moved", - "replacedWithKey": "IOReadBandwidthMax", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOReadBandwidth=device%20bytes" - }, - "BlockIOWeight": { - "reason": "moved", - "replacedWithKey": "IOWeight", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOWeight=weight" - }, - "BlockIOWriteBandwidth": { - "reason": "moved", - "replacedWithKey": "IOWriteBandwidthMax", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOReadBandwidth=device%20bytes" - }, - "CPUShares": { - "reason": "moved", - "replacedWithKey": "CPUWeight", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#CPUShares=weight" - }, - "DefaultMemoryLow": { - "reason": "manual", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#MemoryLow=bytes", - "description": "

DefaultMemoryLow= controls the 'memory.low' value of children. This setting has the same semantics as MemoryLow=. For details about this control attribute see cgroup-v2.txt

" - }, - "DefaultMemoryMin": { - "reason": "manual", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#MemoryMin=bytes", - "description": "

DefaultMemoryMin= controls the 'memory.min' value of children. This setting has the same semantics as MemoryMin=. For details about this control attribute see cgroup-v2.txt

" - }, - "InaccessibleDirectories": { - "reason": "moved", - "replacedWithKey": "InaccessiblePath", - "documentationLink": "https://github.com/systemd/systemd/blob/v241/NEWS#L2799" - }, - "MemoryLimit": { - "reason": "moved", - "replacedWithKey": "MemoryMax", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#MemoryLimit=bytes" - }, - "ReadOnlyDirectories": { - "reason": "moved", - "replacedWithKey": "ReadOnlyPaths", - "documentationLink": "https://github.com/systemd/systemd/blob/v241/NEWS#L2799" - }, - "ReadWriteDirectories": { - "reason": "moved", - "replacedWithKey": "ReadWritePaths", - "documentationLink": "https://github.com/systemd/systemd/blob/v241/NEWS#L2799" - }, - "StartupBlockIOWeight": { - "reason": "moved", - "replacedWithKey": "StartupIOWeight", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOWeight=weight" - }, - "StartupCPUShares": { - "reason": "moved", - "replacedWithKey": "StartupCPUWeight", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#CPUShares=weight" - } - }, - "Swap": { - "BlockIOAccounting": { - "reason": "moved", - "replacedWithKey": "IOAccounting", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOAccounting=" - }, - "BlockIODeviceWeight": { - "reason": "moved", - "replacedWithKey": "IODeviceWeight", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIODeviceWeight=device%20weight" - }, - "BlockIOReadBandwidth": { - "reason": "moved", - "replacedWithKey": "IOReadBandwidthMax", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOReadBandwidth=device%20bytes" - }, - "BlockIOWeight": { - "reason": "moved", - "replacedWithKey": "IOWeight", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOWeight=weight" - }, - "BlockIOWriteBandwidth": { - "reason": "moved", - "replacedWithKey": "IOWriteBandwidthMax", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOReadBandwidth=device%20bytes" - }, - "CPUShares": { - "reason": "moved", - "replacedWithKey": "CPUWeight", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#CPUShares=weight" - }, - "DefaultMemoryLow": { - "reason": "manual", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#MemoryLow=bytes", - "description": "

DefaultMemoryLow= controls the 'memory.low' value of children. This setting has the same semantics as MemoryLow=. For details about this control attribute see cgroup-v2.txt

" - }, - "DefaultMemoryMin": { - "reason": "manual", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#MemoryMin=bytes", - "description": "

DefaultMemoryMin= controls the 'memory.min' value of children. This setting has the same semantics as MemoryMin=. For details about this control attribute see cgroup-v2.txt

" - }, - "InaccessibleDirectories": { - "reason": "moved", - "replacedWithKey": "InaccessiblePath", - "documentationLink": "https://github.com/systemd/systemd/blob/v241/NEWS#L2799" - }, - "MemoryLimit": { - "reason": "moved", - "replacedWithKey": "MemoryMax", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#MemoryLimit=bytes" - }, - "ReadOnlyDirectories": { - "reason": "moved", - "replacedWithKey": "ReadOnlyPaths", - "documentationLink": "https://github.com/systemd/systemd/blob/v241/NEWS#L2799" - }, - "ReadWriteDirectories": { - "reason": "moved", - "replacedWithKey": "ReadWritePaths", - "documentationLink": "https://github.com/systemd/systemd/blob/v241/NEWS#L2799" - }, - "StartupBlockIOWeight": { - "reason": "moved", - "replacedWithKey": "StartupIOWeight", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOWeight=weight" - }, - "StartupCPUShares": { - "reason": "moved", - "replacedWithKey": "StartupCPUWeight", - "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#CPUShares=weight" - } - }, - "Unit": { - "BindTo": { - "reason": "moved", - "replacedWithKey": "BindsTo", - "documentationLink": "https://github.com/systemd/systemd/commit/7f2cddae09" - }, - "PropagateReloadTo": { - "reason": "moved", - "replacedWithKey": "PropagateReloadsTo", - "documentationLink": "https://github.com/systemd/systemd/commit/7f2cddae09" - }, - "PropagateReloadFrom": { - "reason": "moved", - "replacedWithKey": "ReloadPropagatedFrom", - "documentationLink": "https://github.com/systemd/systemd/commit/7f2cddae09" - }, - "OnFailureIsolate": { - "reason": "moved", - "replacedWithKey": "OnFailureJobMode", - "documentationLink": "https://github.com/systemd/systemd/commit/d420282b28" - }, - "RequiresOverridable": { - "reason": "unsupported", - "unsupported": true, - "documentationLink": "https://github.com/systemd/systemd/blob/v241/NEWS#L3608" - }, - "RequisiteOverridable": { - "reason": "unsupported", - "unsupported": true, - "documentationLink": "https://github.com/systemd/systemd/blob/v241/NEWS#L3608" - }, - "OnSuccessJobMode": { - "reason": "unsupported", - "unsupported": true, - "documentationLink": "https://github.com/systemd/systemd/blob/14c811ff4a3025a3ba8b969f7228c05d31eb3796/man/org.freedesktop.systemd1.xml#L1872" - }, - "StartLimitInterval": { - "reason": "moved", - "replacedWithKey": "StartLimitIntervalSec", - "documentationLink": "https://github.com/systemd/systemd/pull/3148/commits/f0367da7d1a61ad698a55d17b5c28ddce0dc265a" + "unit": { + "Mount": { + "BlockIOAccounting": { + "reason": "moved", + "replacedWithKey": "IOAccounting", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOAccounting=" + }, + "BlockIODeviceWeight": { + "reason": "moved", + "replacedWithKey": "IODeviceWeight", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIODeviceWeight=device%20weight" + }, + "BlockIOReadBandwidth": { + "reason": "moved", + "replacedWithKey": "IOReadBandwidthMax", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOReadBandwidth=device%20bytes" + }, + "BlockIOWeight": { + "reason": "moved", + "replacedWithKey": "IOWeight", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOWeight=weight" + }, + "BlockIOWriteBandwidth": { + "reason": "moved", + "replacedWithKey": "IOWriteBandwidthMax", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOReadBandwidth=device%20bytes" + }, + "CPUShares": { + "reason": "moved", + "replacedWithKey": "CPUWeight", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#CPUShares=weight" + }, + "DefaultMemoryLow": { + "reason": "manual", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#MemoryLow=bytes", + "description": "

DefaultMemoryLow= controls the 'memory.low' value of children. This setting has the same semantics as MemoryLow=. For details about this control attribute see cgroup-v2.txt

" + }, + "DefaultMemoryMin": { + "reason": "manual", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#MemoryMin=bytes", + "description": "

DefaultMemoryMin= controls the 'memory.min' value of children. This setting has the same semantics as MemoryMin=. For details about this control attribute see cgroup-v2.txt

" + }, + "InaccessibleDirectories": { + "reason": "moved", + "replacedWithKey": "InaccessiblePath", + "documentationLink": "https://github.com/systemd/systemd/blob/v241/NEWS#L2799" + }, + "MemoryLimit": { + "reason": "moved", + "replacedWithKey": "MemoryMax", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#MemoryLimit=bytes" + }, + "ReadOnlyDirectories": { + "reason": "moved", + "replacedWithKey": "ReadOnlyPaths", + "documentationLink": "https://github.com/systemd/systemd/blob/v241/NEWS#L2799" + }, + "ReadWriteDirectories": { + "reason": "moved", + "replacedWithKey": "ReadWritePaths", + "documentationLink": "https://github.com/systemd/systemd/blob/v241/NEWS#L2799" + }, + "StartupBlockIOWeight": { + "reason": "moved", + "replacedWithKey": "StartupIOWeight", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOWeight=weight" + }, + "StartupCPUShares": { + "reason": "moved", + "replacedWithKey": "StartupCPUWeight", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#CPUShares=weight" + } + }, + "Service": { + "BlockIOAccounting": { + "reason": "moved", + "replacedWithKey": "IOAccounting", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOAccounting=" + }, + "BlockIODeviceWeight": { + "reason": "moved", + "replacedWithKey": "IODeviceWeight", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIODeviceWeight=device%20weight" + }, + "BlockIOReadBandwidth": { + "reason": "moved", + "replacedWithKey": "IOReadBandwidthMax", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOReadBandwidth=device%20bytes" + }, + "BlockIOWeight": { + "reason": "moved", + "replacedWithKey": "IOWeight", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOWeight=weight" + }, + "BlockIOWriteBandwidth": { + "reason": "moved", + "replacedWithKey": "IOWriteBandwidthMax", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOReadBandwidth=device%20bytes" + }, + "CPUShares": { + "reason": "moved", + "replacedWithKey": "CPUWeight", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#CPUShares=weight" + }, + "DefaultMemoryLow": { + "reason": "manual", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#MemoryLow=bytes", + "description": "

DefaultMemoryLow= controls the 'memory.low' value of children. This setting has the same semantics as MemoryLow=. For details about this control attribute see cgroup-v2.txt

" + }, + "DefaultMemoryMin": { + "reason": "manual", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#MemoryMin=bytes", + "description": "

DefaultMemoryMin= controls the 'memory.min' value of children. This setting has the same semantics as MemoryMin=. For details about this control attribute see cgroup-v2.txt

" + }, + "FailureAction": { + "reason": "moved", + "replacedWithSection": "Unit", + "documentationLink": "https://github.com/systemd/systemd/commit/53c35a766f" + }, + "InaccessibleDirectories": { + "reason": "moved", + "replacedWithKey": "InaccessiblePath", + "documentationLink": "https://github.com/systemd/systemd/blob/v241/NEWS#L2799" + }, + "MemoryLimit": { + "reason": "moved", + "replacedWithKey": "MemoryMax", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#MemoryLimit=bytes" + }, + "PermissionsStartOnly": { + "reason": "unsupported", + "documentationLink": "https://github.com/systemd/systemd/blob/v241/NEWS#L561" + }, + "ReadOnlyDirectories": { + "reason": "moved", + "replacedWithKey": "ReadOnlyPaths", + "documentationLink": "https://github.com/systemd/systemd/blob/v241/NEWS#L2799" + }, + "ReadWriteDirectories": { + "reason": "moved", + "replacedWithKey": "ReadWritePaths", + "documentationLink": "https://github.com/systemd/systemd/blob/v241/NEWS#L2799" + }, + "RebootArgument": { + "reason": "moved", + "replacedWithKey": "RebootArgument", + "replacedWithSection": "Unit", + "documentationLink": "https://github.com/systemd/systemd/blob/v241/NEWS#L3443" + }, + "StartLimitAction": { + "reason": "moved", + "replacedWithSection": "Unit", + "documentationLink": "https://github.com/systemd/systemd/blob/v241/NEWS#L3443" + }, + "StartLimitInterval": { + "reason": "moved", + "replacedWithSection": "Unit", + "documentationLink": "https://github.com/systemd/systemd/blob/v241/NEWS#L3443" + }, + "StartLimitBurst": { + "reason": "moved", + "replacedWithSection": "Unit", + "documentationLink": "https://github.com/systemd/systemd/blob/v241/NEWS#L3443" + }, + "StartupBlockIOWeight": { + "reason": "moved", + "replacedWithKey": "StartupIOWeight", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOWeight=weight" + }, + "StartupCPUShares": { + "reason": "moved", + "replacedWithKey": "StartupCPUWeight", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#CPUShares=weight" + } + }, + "Slice": { + "BlockIOAccounting": { + "reason": "moved", + "replacedWithKey": "IOAccounting", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOAccounting=" + }, + "BlockIODeviceWeight": { + "reason": "moved", + "replacedWithKey": "IODeviceWeight", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIODeviceWeight=device%20weight" + }, + "BlockIOReadBandwidth": { + "reason": "moved", + "replacedWithKey": "IOReadBandwidthMax", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOReadBandwidth=device%20bytes" + }, + "BlockIOWeight": { + "reason": "moved", + "replacedWithKey": "IOWeight", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOWeight=weight" + }, + "BlockIOWriteBandwidth": { + "reason": "moved", + "replacedWithKey": "IOWriteBandwidthMax", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOReadBandwidth=device%20bytes" + }, + "CPUShares": { + "reason": "moved", + "replacedWithKey": "CPUWeight", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#CPUShares=weight" + }, + "DefaultMemoryLow": { + "reason": "manual", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#MemoryLow=bytes", + "description": "

DefaultMemoryLow= controls the 'memory.low' value of children. This setting has the same semantics as MemoryLow=. For details about this control attribute see cgroup-v2.txt

" + }, + "DefaultMemoryMin": { + "reason": "manual", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#MemoryMin=bytes", + "description": "

DefaultMemoryMin= controls the 'memory.min' value of children. This setting has the same semantics as MemoryMin=. For details about this control attribute see cgroup-v2.txt

" + }, + "MemoryLimit": { + "reason": "moved", + "replacedWithKey": "MemoryMax", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#MemoryLimit=bytes" + }, + "StartupBlockIOWeight": { + "reason": "moved", + "replacedWithKey": "StartupIOWeight", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOWeight=weight" + }, + "StartupCPUShares": { + "reason": "moved", + "replacedWithKey": "StartupCPUWeight", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#CPUShares=weight" + } + }, + "Socket": { + "BlockIOAccounting": { + "reason": "moved", + "replacedWithKey": "IOAccounting", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOAccounting=" + }, + "BlockIODeviceWeight": { + "reason": "moved", + "replacedWithKey": "IODeviceWeight", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIODeviceWeight=device%20weight" + }, + "BlockIOReadBandwidth": { + "reason": "moved", + "replacedWithKey": "IOReadBandwidthMax", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOReadBandwidth=device%20bytes" + }, + "BlockIOWeight": { + "reason": "moved", + "replacedWithKey": "IOWeight", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOWeight=weight" + }, + "BlockIOWriteBandwidth": { + "reason": "moved", + "replacedWithKey": "IOWriteBandwidthMax", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOReadBandwidth=device%20bytes" + }, + "CPUShares": { + "reason": "moved", + "replacedWithKey": "CPUWeight", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#CPUShares=weight" + }, + "DefaultMemoryLow": { + "reason": "manual", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#MemoryLow=bytes", + "description": "

DefaultMemoryLow= controls the 'memory.low' value of children. This setting has the same semantics as MemoryLow=. For details about this control attribute see cgroup-v2.txt

" + }, + "DefaultMemoryMin": { + "reason": "manual", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#MemoryMin=bytes", + "description": "

DefaultMemoryMin= controls the 'memory.min' value of children. This setting has the same semantics as MemoryMin=. For details about this control attribute see cgroup-v2.txt

" + }, + "InaccessibleDirectories": { + "reason": "moved", + "replacedWithKey": "InaccessiblePath", + "documentationLink": "https://github.com/systemd/systemd/blob/v241/NEWS#L2799" + }, + "MemoryLimit": { + "reason": "moved", + "replacedWithKey": "MemoryMax", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#MemoryLimit=bytes" + }, + "ReadOnlyDirectories": { + "reason": "moved", + "replacedWithKey": "ReadOnlyPaths", + "documentationLink": "https://github.com/systemd/systemd/blob/v241/NEWS#L2799" + }, + "ReadWriteDirectories": { + "reason": "moved", + "replacedWithKey": "ReadWritePaths", + "documentationLink": "https://github.com/systemd/systemd/blob/v241/NEWS#L2799" + }, + "StartupBlockIOWeight": { + "reason": "moved", + "replacedWithKey": "StartupIOWeight", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOWeight=weight" + }, + "StartupCPUShares": { + "reason": "moved", + "replacedWithKey": "StartupCPUWeight", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#CPUShares=weight" + } + }, + "Swap": { + "BlockIOAccounting": { + "reason": "moved", + "replacedWithKey": "IOAccounting", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOAccounting=" + }, + "BlockIODeviceWeight": { + "reason": "moved", + "replacedWithKey": "IODeviceWeight", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIODeviceWeight=device%20weight" + }, + "BlockIOReadBandwidth": { + "reason": "moved", + "replacedWithKey": "IOReadBandwidthMax", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOReadBandwidth=device%20bytes" + }, + "BlockIOWeight": { + "reason": "moved", + "replacedWithKey": "IOWeight", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOWeight=weight" + }, + "BlockIOWriteBandwidth": { + "reason": "moved", + "replacedWithKey": "IOWriteBandwidthMax", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOReadBandwidth=device%20bytes" + }, + "CPUShares": { + "reason": "moved", + "replacedWithKey": "CPUWeight", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#CPUShares=weight" + }, + "DefaultMemoryLow": { + "reason": "manual", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#MemoryLow=bytes", + "description": "

DefaultMemoryLow= controls the 'memory.low' value of children. This setting has the same semantics as MemoryLow=. For details about this control attribute see cgroup-v2.txt

" + }, + "DefaultMemoryMin": { + "reason": "manual", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#MemoryMin=bytes", + "description": "

DefaultMemoryMin= controls the 'memory.min' value of children. This setting has the same semantics as MemoryMin=. For details about this control attribute see cgroup-v2.txt

" + }, + "InaccessibleDirectories": { + "reason": "moved", + "replacedWithKey": "InaccessiblePath", + "documentationLink": "https://github.com/systemd/systemd/blob/v241/NEWS#L2799" + }, + "MemoryLimit": { + "reason": "moved", + "replacedWithKey": "MemoryMax", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#MemoryLimit=bytes" + }, + "ReadOnlyDirectories": { + "reason": "moved", + "replacedWithKey": "ReadOnlyPaths", + "documentationLink": "https://github.com/systemd/systemd/blob/v241/NEWS#L2799" + }, + "ReadWriteDirectories": { + "reason": "moved", + "replacedWithKey": "ReadWritePaths", + "documentationLink": "https://github.com/systemd/systemd/blob/v241/NEWS#L2799" + }, + "StartupBlockIOWeight": { + "reason": "moved", + "replacedWithKey": "StartupIOWeight", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#BlockIOWeight=weight" + }, + "StartupCPUShares": { + "reason": "moved", + "replacedWithKey": "StartupCPUWeight", + "documentationLink": "https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#CPUShares=weight" + } + }, + "Unit": { + "BindTo": { + "reason": "moved", + "replacedWithKey": "BindsTo", + "documentationLink": "https://github.com/systemd/systemd/commit/7f2cddae09" + }, + "PropagateReloadTo": { + "reason": "moved", + "replacedWithKey": "PropagateReloadsTo", + "documentationLink": "https://github.com/systemd/systemd/commit/7f2cddae09" + }, + "PropagateReloadFrom": { + "reason": "moved", + "replacedWithKey": "ReloadPropagatedFrom", + "documentationLink": "https://github.com/systemd/systemd/commit/7f2cddae09" + }, + "OnFailureIsolate": { + "reason": "moved", + "replacedWithKey": "OnFailureJobMode", + "documentationLink": "https://github.com/systemd/systemd/commit/d420282b28" + }, + "RequiresOverridable": { + "reason": "unsupported", + "unsupported": true, + "documentationLink": "https://github.com/systemd/systemd/blob/v241/NEWS#L3608" + }, + "RequisiteOverridable": { + "reason": "unsupported", + "unsupported": true, + "documentationLink": "https://github.com/systemd/systemd/blob/v241/NEWS#L3608" + }, + "OnSuccessJobMode": { + "reason": "unsupported", + "unsupported": true, + "documentationLink": "https://github.com/systemd/systemd/blob/14c811ff4a3025a3ba8b969f7228c05d31eb3796/man/org.freedesktop.systemd1.xml#L1872" + }, + "StartLimitInterval": { + "reason": "moved", + "replacedWithKey": "StartLimitIntervalSec", + "documentationLink": "https://github.com/systemd/systemd/pull/3148/commits/f0367da7d1a61ad698a55d17b5c28ddce0dc265a" + } } } } diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/annotators/InvalidSectionHeaderNameAnnotatorTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/annotators/InvalidSectionHeaderNameAnnotatorTest.kt index 6d4f9ad9..a8acea32 100644 --- a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/annotators/InvalidSectionHeaderNameAnnotatorTest.kt +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/annotators/InvalidSectionHeaderNameAnnotatorTest.kt @@ -380,4 +380,92 @@ class InvalidSectionHeaderNameAnnotatorTest : AbstractUnitFileTest() { } + fun testNSpawnSectionsInNSpawnFileHasNoWarnings() { + /* + * Fixture Setup + */ + //language=unit file (systemd) + val file = """ + [Exec] + + [Files] + + [Network] + """.trimIndent() + + /* + * Exercise SUT + */ + setupFileInEditor("file.nspawn", file) + val highlights = myFixture.doHighlighting() + + /* + * Verification + */ + assertSize(0, highlights) + } + + fun testNSpawnSectionsInServiceFileHasWarnings() { + /* + * Fixture Setup + */ + //language=unit file (systemd) + val file = """ + [Exec] + + [Files] + + [Network] + """.trimIndent() + + /* + * Exercise SUT + */ + setupFileInEditor("file.service", file) + val highlights = myFixture.doHighlighting() + + /* + * Verification + */ + assertSize(3, highlights) + + val highlightTexts = highlights.map { it.description } + + assertContainsElements(highlightTexts, "The section Exec is not allowed in Service files, only the following are allowed: [Unit, Install, Service]") + assertContainsElements(highlightTexts, "The section Files is not allowed in Service files, only the following are allowed: [Unit, Install, Service]") + assertContainsElements(highlightTexts, "The section Network is not allowed in Service files, only the following are allowed: [Unit, Install, Service]") + } + + fun testServiceSectionsInNSpawnFileHasWarnings() { + /* + * Fixture Setup + */ + //language=unit file (systemd) + val file = """ + [Unit] + + [Install] + + [Service] + """.trimIndent() + + /* + * Exercise SUT + */ + setupFileInEditor("file.nspawn", file) + val highlights = myFixture.doHighlighting() + + /* + * Verification + */ + assertSize(3, highlights) + + val highlightTexts = highlights.map { it.description } + + assertContainsElements(highlightTexts, "The section Unit is not allowed in Nspawn files, only the following are allowed: [Exec, Files, Network]") + assertContainsElements(highlightTexts, "The section Install is not allowed in Nspawn files, only the following are allowed: [Exec, Files, Network]") + assertContainsElements(highlightTexts, "The section Service is not allowed in Nspawn files, only the following are allowed: [Exec, Files, Network]") + } + } + diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/completion/UnitFileKeyCompletionContributorTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/completion/UnitFileKeyCompletionContributorTest.kt index ea704a8b..0ce47e8d 100644 --- a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/completion/UnitFileKeyCompletionContributorTest.kt +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/completion/UnitFileKeyCompletionContributorTest.kt @@ -145,4 +145,21 @@ class UnitFileKeyCompletionContributorTest : AbstractUnitFileTest() { // Verification assertContainsElements(completions, "DirectoryMode", "DirectoryNotEmpty", "MakeDirectory", "PathModified") } + + fun testCompletionForNSpawnFileInFilesSectionReturnsExpectedValues() { + // Fixture Setup + val file = """ + [Files] + Bi$COMPLETION_POSITION + Inaccessible=True + """.trimIndent() + myFixture.configureByText("file.nspawn", file) + + // Exercise SUT + val completions = basicCompletionResultStrings + + // Verification + assertContainsElements(completions, "Bind", "BindReadOnly", "BindUser") + } + } diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/completion/UnitFileSectionCompletionContributorTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/completion/UnitFileSectionCompletionContributorTest.kt index 74ac12fd..66cc406c 100644 --- a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/completion/UnitFileSectionCompletionContributorTest.kt +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/completion/UnitFileSectionCompletionContributorTest.kt @@ -121,6 +121,19 @@ class UnitFileSectionCompletionContributorTest : AbstractUnitFileTest() { assertContainsElements(completions, "Install", "Unit", "Timer") } + fun testCompletionOfNewSectionHeaderReturnsExpectedValuesInNSpawn() { + // Fixture Setup + val file = """ + [Files] + Whatevs=Foo + + [$COMPLETION_POSITION + """.trimIndent() + myFixture.configureByText("file.nspawn", file) + val completions = basicCompletionResultStrings + assertContainsElements(completions, "Exec", "Files", "Network") + } + fun testCompletionOfNewSectionInUnknownFileTypeIsEmpty() { // Fixture Setup val file = """ diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/completion/UnitFileValueCompletionContributorTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/completion/UnitFileValueCompletionContributorTest.kt index 97462bd9..3af25705 100644 --- a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/completion/UnitFileValueCompletionContributorTest.kt +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/completion/UnitFileValueCompletionContributorTest.kt @@ -126,4 +126,20 @@ class UnitFileValueCompletionContributorTest : AbstractUnitFileTest() { // Verification assertContainsElements(completions, "NetworkManager.service") } + + fun testCompletionOfBooleanOptionReturnsValuesInNspawnFile() { + // Fixture Setup + val file = """ + [Network] + Private=$COMPLETION_POSITION + + """.trimIndent() + myFixture.configureByText("file.nspawn", file) + + // Execute SUT + val completions = basicCompletionResultStrings + + // Verification + assertContainsElements(completions, "on", "off", "true", "false", "yes", "no") + } } diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/documentation/UnitFileDocumentationProviderTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/documentation/UnitFileDocumentationProviderTest.kt index 8ba6ee2e..973b48db 100644 --- a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/documentation/UnitFileDocumentationProviderTest.kt +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/documentation/UnitFileDocumentationProviderTest.kt @@ -1,8 +1,6 @@ package net.sjrx.intellij.plugins.systemdunitfiles.documentation import com.intellij.lang.documentation.DocumentationProviderEx -import com.intellij.psi.PsiElement -import com.intellij.psi.impl.FakePsiElement import junit.framework.TestCase import net.sjrx.intellij.plugins.systemdunitfiles.AbstractUnitFileTest @@ -215,8 +213,43 @@ class UnitFileDocumentationProviderTest : AbstractUnitFileTest() { val psiFile = setupFileInEditor("file.service", file) // Exercise SUT - val unknownSectionHeader = getAllSectionInFile(psiFile)[0] - val doc = sut.generateDoc(unknownSectionHeader, unknownSectionHeader) + val sectionHeader = getAllSectionInFile(psiFile)[0] + val doc = sut.generateDoc(sectionHeader, sectionHeader) + + // Verification + TestCase.assertNotNull(doc) + TestCase.assertTrue(doc!!.length > 1) + } + + fun testGenerateDocForKnownSectionReturnsSomeValidTextInNSpawnFile() { + // Fixture Setup + val file = """ + [Network] + Private=true + """.trimIndent() + val psiFile = setupFileInEditor("file.nspawn", file) + + // Exercise SUT + val section = getAllSectionInFile(psiFile)[0] + val doc = sut.generateDoc(section, section) + + // Verification + TestCase.assertNotNull(doc) + TestCase.assertTrue(doc!!.length > 1) + } + + fun testGenerateDocKnownKeySeparatorReturnsSomeValidTextInNSpawnFile() { + // Fixture Setup + val file = """ + [Network] + Private=true + """.trimIndent() + val psiFile = setupFileInEditor("file.nspawn", file) + + // Exercise SUT + + val documentationKey = getAllKeysInFile(psiFile)[0] + val doc = sut.generateDoc(documentationKey, documentationKey) // Verification TestCase.assertNotNull(doc) diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/DeprecatedOptionsInspectionTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/DeprecatedOptionsInspectionTest.kt index a719207f..1fdc97d7 100644 --- a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/DeprecatedOptionsInspectionTest.kt +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/DeprecatedOptionsInspectionTest.kt @@ -5,6 +5,7 @@ import net.sjrx.intellij.plugins.systemdunitfiles.AbstractUnitFileTest class DeprecatedOptionsInspectionTest : AbstractUnitFileTest() { fun testNonDeprecatedOptionDoesNotThrowError() { + // language="unit file (systemd)" val file = """ [Service] ExecStart=/bin/bash @@ -23,6 +24,7 @@ class DeprecatedOptionsInspectionTest : AbstractUnitFileTest() { } fun testUnknownOptionDoesNotThrowError() { + // language="unit file (systemd)" val file = """ [Service] SomeOption=/bin/bash @@ -41,6 +43,7 @@ class DeprecatedOptionsInspectionTest : AbstractUnitFileTest() { } fun testSingleExampleThrowsWarning() { + // language="unit file (systemd)" val file = """ [Service] MemoryLimit=8 @@ -64,6 +67,7 @@ class DeprecatedOptionsInspectionTest : AbstractUnitFileTest() { } fun testAllDocumentedDeprecatedOptionsInServiceAsOfV240ThrowsWarning() { + // language="unit file (systemd)" val file = """ [Service] CPUShares=52 @@ -91,6 +95,7 @@ class DeprecatedOptionsInspectionTest : AbstractUnitFileTest() { } fun testAllDocumentedDeprecatedOptionsInMountAsOfV240ThrowsWarning() { + // language="unit file (systemd)" val file = """ [Mount] CPUShares=52 @@ -118,6 +123,7 @@ class DeprecatedOptionsInspectionTest : AbstractUnitFileTest() { } fun testAllDocumentedDeprecatedOptionsInSocketAsOfV240ThrowsWarning() { + // language="unit file (systemd)" val file = """ [Socket] CPUShares=52 @@ -145,6 +151,7 @@ class DeprecatedOptionsInspectionTest : AbstractUnitFileTest() { } fun testAllDocumentedDeprecatedOptionsInSwapAsOfV240ThrowsWarning() { + // language="unit file (systemd)" val file = """ [Swap] CPUShares=52 @@ -172,6 +179,7 @@ class DeprecatedOptionsInspectionTest : AbstractUnitFileTest() { } fun testAllDocumentedDeprecatedOptionsInSliceAsOfV240ThrowsWarning() { + // language="unit file (systemd)" val file = """ [Slice] CPUShares=52 @@ -197,4 +205,29 @@ class DeprecatedOptionsInspectionTest : AbstractUnitFileTest() { // Verification assertSize(9, highlights) } + + fun testSingleExampleInNSpawnFileThrowsWarning() { + + // language="unit file (systemd)" + val file = """ + [Files] + PrivateUsersChown=true + """.trimIndent() + + // Exercise SUT + setupFileInEditor("file.nspawn", file) + enableInspection(DeprecatedOptionsInspection::class.java) + + // Verification + val highlights = myFixture.doHighlighting() + + + // Verification + assertSize(1, highlights) + val info = highlights[0] + TestCase.assertEquals("'PrivateUsersChown' in section 'Files' has been renamed to 'PrivateUsersOwnership'", info!!.description) + val highlightElement = myFixture.file.findElementAt(info.getStartOffset()) + TestCase.assertNotNull(highlightElement) + TestCase.assertEquals("PrivateUsersChown", highlightElement!!.text) + } } diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/MissingRequiredKeyInspectionTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/MissingRequiredKeyInspectionTest.kt index 10e6dc6b..27fd2726 100644 --- a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/MissingRequiredKeyInspectionTest.kt +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/MissingRequiredKeyInspectionTest.kt @@ -29,7 +29,7 @@ class MissingRequiredKeyInspectionTest : AbstractUnitFileTest() { assertSize(2, highlights) } - fun testServiceHasNoWarningsARequiredOptionInUnitSectionPresent() { + fun testServiceHasNoWarningsEnoughRequiredOptionsInUnitSectionArePresent() { // Fixture Setup // language="unit file (systemd)" @@ -37,7 +37,7 @@ class MissingRequiredKeyInspectionTest : AbstractUnitFileTest() { # SPDX-License-Identifier: LGPL-2.1-or-later [Unit] Description=Daughter Service - SuccessAction=foo + SuccessAction=reboot [Service] Type=oneshot @@ -358,4 +358,29 @@ class MissingRequiredKeyInspectionTest : AbstractUnitFileTest() { assertSize(0, highlights) } + + fun testNSpawnFileHasNoWarningsWhenEmpty() { + + // Fixture Setup + // language="unit file (systemd)" + val file = """ + # SPDX-License-Identifier: LGPL-2.1-or-later + [Files] + + + [Network] + + [Exec] + """.trimIndent() + + // Exercise SUT + setupFileInEditor("file.nspawn", file) + enableInspection(MissingRequiredKeyInspection::class.java) + + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(0, highlights) + } + } diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/UnknownKeyInSectionInspectionTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/UnknownKeyInSectionInspectionTest.kt index e8c5fcde..c0ff9d6d 100644 --- a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/UnknownKeyInSectionInspectionTest.kt +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/inspections/UnknownKeyInSectionInspectionTest.kt @@ -682,6 +682,44 @@ class UnknownKeyInSectionInspectionTest : AbstractUnitFileTest() { TestCase.assertEquals("BusName", highlightElement!!.text) } + fun testNSpawnFileTypeThrowsWarningWithKeyFromServiceFile() { + // Fixture Setup + val file = """ + [Exec] + BusName=yes + """.trimIndent() + enableInspection(UnknownKeyInSectionInspection::class.java) + setupFileInEditor("some.nspawn", file) + + // Exercise SUT + val highlights = myFixture.doHighlighting() + + // Verification + assertSize(1, highlights) + val info = highlights[0] + TestCase.assertEquals(UnknownKeyInSectionInspection.INSPECTION_TOOL_TIP_TEXT, info!!.description) + TestCase.assertEquals(HighlightInfoType.WARNING, info.type) + val highlightElement = myFixture.file.findElementAt(info.getStartOffset()) + TestCase.assertEquals("BusName", highlightElement!!.text) + } + + fun testNSpawnFileTypeHasNoWarningsWithKnownKey() { + // Fixture Setup + val file = """ + [Timer] + RandomizedDelaySec=50 + """.trimIndent() + enableInspection(UnknownKeyInSectionInspection::class.java) + setupFileInEditor("some.timer", file) + + // Exercise SUT + val highlights = myFixture.doHighlighting() + + // Verification + assertEmpty(highlights) + } + + fun testSomeNewKeysFromSystemdV240HasNoWarnings() { // Fixture Setup val file = """ diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/SemanticDataDocumentationCompletionTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/SemanticDataDocumentationCompletionTest.kt index 8de53d94..2d3b9571 100644 --- a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/SemanticDataDocumentationCompletionTest.kt +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/SemanticDataDocumentationCompletionTest.kt @@ -7,19 +7,26 @@ class SemanticDataDocumentationCompletionTest : AbstractUnitFileTest() { fun testAllOptions() { val sdr = SemanticDataRepository.instance val doc: MutableSet = TreeSet() - for (sectionName in sdr.sectionNamesFromDocumentation) { - for (keyName in sdr.getDocumentedKeywordsInSection(sectionName)) { - doc.add("$sectionName.$keyName") + + for (fileClass in FileClass.entries) { + for (sectionName in sdr.getSectionNamesForFile(fileClass.fileClass)) { + for (keyName in sdr.getDocumentedKeywordsInSection(fileClass, sectionName)) { + doc.add("${fileClass.fileClass}.$sectionName.$keyName") + } } } val code: MutableSet = TreeSet() - for (sectionName in sdr.sectionNamesFromValidators) { - for (keyName in sdr.getAllowedKeywordsInSectionFromValidators(sectionName)) { - code.add("$sectionName.$keyName") + + for (fileClass in FileClass.entries) { + for (sectionName in sdr.getSectionNamesForFile(fileClass.fileClass)) { + for (keyName in sdr.getAllowedKeywordsInSectionFromValidators(fileClass, sectionName)) { + code.add("${fileClass.fileClass}.$sectionName.$keyName") + } } } - println(doc.size) - println(code.size) + + println(doc.size) //0 + println(code.size) //1131 val codeButNotDoc: MutableSet = TreeSet(code) val docButNotCode: MutableSet = TreeSet(doc) codeButNotDoc.removeAll(doc) diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/SemanticDataRepositoryTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/SemanticDataRepositoryTest.kt index 1fd05890..6fa74585 100644 --- a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/SemanticDataRepositoryTest.kt +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/SemanticDataRepositoryTest.kt @@ -6,28 +6,32 @@ import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues.* class SemanticDataRepositoryTest : AbstractUnitFileTest() { fun testInteresting() { val sdr = SemanticDataRepository.instance - assertInstanceOf(sdr.getOptionValidator("Socket", "SendSIGKILL"), BooleanOptionValue::class.java) - assertInstanceOf(sdr.getOptionValidator("Unit", "Documentation"), DocumentationOptionValue::class.java) - assertInstanceOf(sdr.getOptionValidator("Service", "KillMode"), KillModeOptionValue::class.java) - assertInstanceOf(sdr.getOptionValidator("Mount", "KillMode"), KillModeOptionValue::class.java) - assertInstanceOf(sdr.getOptionValidator("Socket", "DirectoryMode"), ModeStringOptionValue::class.java) - assertInstanceOf(sdr.getOptionValidator("Unit", "XXXX"), NullOptionValue::class.java) - assertInstanceOf(sdr.getOptionValidator("XXXX", "Yes"), NullOptionValue::class.java) - assertInstanceOf(sdr.getOptionValidator("Service", "Restart"), RestartOptionValue::class.java) - assertInstanceOf(sdr.getOptionValidator("Service", "Type"), ServiceTypeOptionValue::class.java) + assertInstanceOf(sdr.getOptionValidator(FileClass.UNIT_FILE,"Socket", "SendSIGKILL"), BooleanOptionValue::class.java) + assertInstanceOf(sdr.getOptionValidator(FileClass.UNIT_FILE,"Unit", "Documentation"), DocumentationOptionValue::class.java) + assertInstanceOf(sdr.getOptionValidator(FileClass.UNIT_FILE, "Service", "KillMode"), KillModeOptionValue::class.java) + assertInstanceOf(sdr.getOptionValidator(FileClass.UNIT_FILE,"Mount", "KillMode"), KillModeOptionValue::class.java) + assertInstanceOf(sdr.getOptionValidator(FileClass.UNIT_FILE, "Socket", "DirectoryMode"), ModeStringOptionValue::class.java) + assertInstanceOf(sdr.getOptionValidator(FileClass.UNIT_FILE , "Unit", "XXXX"), NullOptionValue::class.java) + assertInstanceOf(sdr.getOptionValidator(FileClass.UNIT_FILE, "XXXX", "Yes"), NullOptionValue::class.java) + assertInstanceOf(sdr.getOptionValidator(FileClass.UNIT_FILE, "Service", "Restart"), RestartOptionValue::class.java) + assertInstanceOf(sdr.getOptionValidator(FileClass.UNIT_FILE, "Service", "Type"), ServiceTypeOptionValue::class.java) } fun testDeclaredUnderKeywordDiffers() { val sdr = SemanticDataRepository.instance - for (section in sdr.sectionNamesFromDocumentation) { - val data = sdr.getKeyValuePairsForSectionFromDocumentation(section) - for ((key, value) in data) { - val declaredUnderKeyword = value.declaredUnderKeyword - if (declaredUnderKeyword != null && declaredUnderKeyword != key) { - println("Mismatch: $section.$key: $declaredUnderKeyword") + + for (fileClass in FileClass.entries) { + for (section in sdr.getSectionNamesForFile(fileClass.name)) { + val data = sdr.getKeyValuePairsForSectionFromDocumentation(fileClass, section) + for ((key, value) in data) { + val declaredUnderKeyword = value.declaredUnderKeyword + if (declaredUnderKeyword != null && declaredUnderKeyword != key) { + println("Mismatch: $section.$key: $declaredUnderKeyword") + } } } } + } fun testAllRequiredOptionsExists () { @@ -58,7 +62,7 @@ class SemanticDataRepositoryTest : AbstractUnitFileTest() { for (key in keys) { val sectionAndKey = key.split('.') - val validKeys = sdr.getAllowedKeywordsInSectionFromValidators(sectionAndKey[0]) + val validKeys = sdr.getAllowedKeywordsInSectionFromValidators(FileClass.UNIT_FILE, sectionAndKey[0]) assertContainsElements(validKeys, sectionAndKey[1]) } diff --git a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/OptionValueTest.kt b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/OptionValueTest.kt index bdb97a0c..068c1953 100644 --- a/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/OptionValueTest.kt +++ b/src/test/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/semanticdata/optionvalues/OptionValueTest.kt @@ -1,6 +1,7 @@ package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.optionvalues import net.sjrx.intellij.plugins.systemdunitfiles.AbstractUnitFileTest +import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.FileClass import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.SemanticDataRepository import net.sjrx.intellij.plugins.systemdunitfiles.semanticdata.Validator @@ -13,30 +14,34 @@ class OptionValueTest : AbstractUnitFileTest() { val missingValidators = hashMapOf() var totalMissingValidators = 0 var totalFoundValidators = 0 - for (sectionName in SemanticDataRepository.instance.sectionNamesFromValidators) { - for (key in SemanticDataRepository.instance.getAllowedKeywordsInSectionFromValidators(sectionName)) { - - - val validator = SemanticDataRepository.instance.getValidatorForSectionAndKey(sectionName, key) - - if (!validatorMap.containsKey(validator)) { - missingValidators[validator] = (missingValidators[validator] ?: 0) + 1 - totalMissingValidators++ - } else { - totalFoundValidators++ + for (fileClass in FileClass.entries) { + for (sectionName in SemanticDataRepository.instance.getSectionNamesForFile(fileClass.fileClass)) { + for (key in SemanticDataRepository.instance.getAllowedKeywordsInSectionFromValidators(fileClass, sectionName)) { + + val validator = SemanticDataRepository.instance.getValidatorForSectionAndKey(fileClass, sectionName, key) + + if (!validatorMap.containsKey(validator)) { + missingValidators[validator] = (missingValidators[validator] ?: 0) + 1 + totalMissingValidators++ + } else { + totalFoundValidators++ + } } } } - val missingValidatorList = missingValidators.map { "${String.format("%05d", it.value)}, ${it.key}" } val sortedList = missingValidatorList.sortedDescending().joinToString("\n") println("Missing:$totalMissingValidators") println("Found:$totalFoundValidators") - if (totalMissingValidators > 560) { + if (totalMissingValidators > 611) { assertEquals("Number of missing validators is too high at ${totalMissingValidators} vs. found ${totalFoundValidators}", sortedList, "") } + if (totalFoundValidators == 0) { + fail("There are no found validators, something is wrong") + } + } } diff --git a/systemd-build/systemd-build.sh b/systemd-build/systemd-build.sh index 7fb4f896..9f310aa3 100755 --- a/systemd-build/systemd-build.sh +++ b/systemd-build/systemd-build.sh @@ -1,12 +1,21 @@ #!/usr/bin/env bash - echo "Git Pull" && \ git pull && \ echo "Run jinja2" && \ python3 ./tools/meson-render-jinja2.py ./build/config.h ./src/core/load-fragment-gperf.gperf.in load-fragment-gperf.gperf && \ echo "Copy file(s)" && \ cp load-fragment-gperf.gperf /mount/load-fragment-gperf.gperf && \ + cp ./src/journal/journald-gperf.gperf /mount/ && \ + cp ./src/login/logind-gperf.gperf /mount/ && \ + cp ./src/nspawn/nspawn-gperf.gperf /mount/ && \ + cp ./src/timesync/timesyncd-gperf.gperf /mount/ && \ + cp ./src/udev/net/link-config-gperf.gperf /mount/ && \ + cp ./src/resolve/resolved-gperf.gperf /mount/ && \ + cp ./src/resolve/resolved-dnssd-gperf.gperf /mount/ && \ + cp ./src/network/netdev/netdev-gperf.gperf /mount/ && \ + cp ./src/network/networkd-network-gperf.gperf /mount/ && \ + cp ./src/network/networkd-gperf.gperf /mount/ && \ cp -R ./man /mount/ && \ git log --format="%at" | sort | tail -n 1 | xargs -I{} date -d @{} +%Y-%m-%d > last_commit_date && \ git rev-parse --short=10 HEAD > last_commit_hash && \