Skip to content

Commit d370de9

Browse files
author
Steve Ramage
committed
WIP #290 - Test
1 parent 8a11bae commit d370de9

File tree

9 files changed

+421
-172
lines changed

9 files changed

+421
-172
lines changed

README.md

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
Unit File Support for systemd
2-
-----------------------------
1+
systemd & Unit File Support
2+
---------------------------
33

44
## Introduction
55

6-
This plugin adds support for [systemd unit files](https://www.freedesktop.org/software/systemd/man/systemd.unit.html#) to IntelliJ.
6+
This plugin adds support for [systemd & unit files](https://www.freedesktop.org/software/systemd/man/systemd.unit.html#) to IntelliJ.
77

88
## Features
99
* Syntax highlighting for unit files.
@@ -28,24 +28,27 @@ This plugin adds support for [systemd unit files](https://www.freedesktop.org/so
2828
2929
## Usage
3030
To create a file simply right-click on a folder and <kbd>New</kbd> > <kbd>File</kbd>, and enter a file name ending any of:
31-
* `.automount`
32-
* `.device`
33-
* `.mount`
34-
* `.path`
35-
* `.service`
36-
* `.slice`
37-
* `.socket`
38-
* `.swap`
39-
* `.target`
40-
* `.timer`
31+
* [Unit Files](https://www.freedesktop.org/software/systemd/man/latest/systemd.unit.html)
32+
* `.automount`
33+
* `.device`
34+
* `.mount`
35+
* `.path`
36+
* `.service`
37+
* `.slice`
38+
* `.socket`
39+
* `.swap`
40+
* `.target`
41+
* `.timer`
42+
* [Nspawn Container Settings](https://www.freedesktop.org/software/systemd/man/latest/systemd.nspawn.html#)
43+
* `.nspawn`
4144

4245
The file should then be associated with this plugin and the above features should work.
43-
46+
4447
__NOTE__: `.scope` units are not configured via unit configuration files and so we don't support them.
4548

4649
## Installation
4750

48-
This plugin is avaliable to install at the [JetBrains Plugin Repository](https://plugins.jetbrains.com/plugin/11070-unit-file-support-systemd-).
51+
This plugin is available to install at the [JetBrains Plugin Repository](https://plugins.jetbrains.com/plugin/11070-unit-file-support-systemd-).
4952

5053
Contributors
5154
-------------

buildSrc/src/main/groovy/GenerateDataFromManPages.groovy

Lines changed: 111 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -55,75 +55,86 @@ class GenerateDataFromManPages extends DefaultTask {
5555
* Map that stores for each file name, the name of an option attribute
5656
*/
5757
@Internal
58-
def fileAndSectionTitleToSectionName = [
59-
'systemd.unit.xml' :
60-
['sections':
61-
['[Unit] Section Options' : ['Unit'],
62-
'[Install] Section Options': ['Install'],
63-
'Conditions and Asserts' : ['Unit']
64-
]
65-
],
66-
'systemd.service.xml' :
67-
['sections':
68-
['Options': ['Service']]
69-
],
70-
'systemd.timer.xml' :
71-
['sections':
72-
['Options': ['Timer']]
73-
],
74-
'systemd.automount.xml' :
75-
['sections':
76-
['Options': ['Automount']]
77-
],
78-
'systemd.mount.xml' :
79-
['sections':
80-
['Options': ['Mount']]
81-
],
82-
'systemd.path.xml' :
83-
['sections':
84-
['Options': ['Path']]
85-
],
86-
'systemd.socket.xml' :
87-
['sections':
88-
['Options': ['Socket']]
89-
],
90-
'systemd.swap.xml' :
91-
['sections':
92-
['Options': ['Swap']]
93-
],
94-
'systemd.resource-control.xml':
95-
['sections':
96-
[
97-
'Options' : ['Slice', 'Service', 'Socket', 'Mount', 'Swap'],
98-
'Deprecated Options': ['Slice', 'Service', 'Socket', 'Mount', 'Swap'],
99-
]
100-
],
101-
'systemd.kill.xml' :
102-
['sections':
103-
['Options': ['Service', "Socket", "Mount", "Swap"]]
104-
],
105-
'systemd.exec.xml' :
106-
['sections':
107-
[
108-
'Paths' : ['Service', 'Socket', 'Mount', 'Swap'],
109-
'Credentials' : ['Service', 'Socket', 'Mount', 'Swap'],
110-
'User/Group Identity' : ['Service', 'Socket', 'Mount', 'Swap'],
111-
'Capabilities' : ['Service', 'Socket', 'Mount', 'Swap'],
112-
'Security' : ['Service', 'Socket', 'Mount', 'Swap'],
113-
'Mandatory Access Control' : ['Service', 'Socket', 'Mount', 'Swap'],
114-
'Process Properties' : ['Service', 'Socket', 'Mount', 'Swap'],
115-
'Scheduling' : ['Service', 'Socket', 'Mount', 'Swap'],
116-
'Sandboxing' : ['Service', 'Socket', 'Mount', 'Swap'],
117-
'System Call Filtering' : ['Service', 'Socket', 'Mount', 'Swap'],
118-
'Environment' : ['Service', 'Socket', 'Mount', 'Swap'],
119-
'Logging and Standard Input/Output': ['Service', 'Socket', 'Mount', 'Swap'],
120-
'System V Compatibility' : ['Service', 'Socket', 'Mount', 'Swap'],
121-
]
122-
]
58+
def fileTypeToFileAndSectionTitleToSectionName = [
59+
'unit' : [
60+
'systemd.unit.xml' :
61+
['sections':
62+
['[Unit] Section Options' : ['Unit'],
63+
'[Install] Section Options': ['Install'],
64+
'Conditions and Asserts' : ['Unit']
65+
]
66+
],
67+
'systemd.service.xml' :
68+
['sections':
69+
['Options': ['Service']]
70+
],
71+
'systemd.timer.xml' :
72+
['sections':
73+
['Options': ['Timer']]
74+
],
75+
'systemd.automount.xml' :
76+
['sections':
77+
['Options': ['Automount']]
78+
],
79+
'systemd.mount.xml' :
80+
['sections':
81+
['Options': ['Mount']]
82+
],
83+
'systemd.path.xml' :
84+
['sections':
85+
['Options': ['Path']]
86+
],
87+
'systemd.socket.xml' :
88+
['sections':
89+
['Options': ['Socket']]
90+
],
91+
'systemd.swap.xml' :
92+
['sections':
93+
['Options': ['Swap']]
94+
],
95+
'systemd.resource-control.xml':
96+
['sections':
97+
[
98+
'Options' : ['Slice', 'Service', 'Socket', 'Mount', 'Swap'],
99+
'Deprecated Options': ['Slice', 'Service', 'Socket', 'Mount', 'Swap'],
100+
]
101+
],
102+
'systemd.kill.xml' :
103+
['sections':
104+
['Options': ['Service', "Socket", "Mount", "Swap"]]
105+
],
106+
'systemd.exec.xml' :
107+
['sections':
108+
[
109+
'Paths' : ['Service', 'Socket', 'Mount', 'Swap'],
110+
'Credentials' : ['Service', 'Socket', 'Mount', 'Swap'],
111+
'User/Group Identity' : ['Service', 'Socket', 'Mount', 'Swap'],
112+
'Capabilities' : ['Service', 'Socket', 'Mount', 'Swap'],
113+
'Security' : ['Service', 'Socket', 'Mount', 'Swap'],
114+
'Mandatory Access Control' : ['Service', 'Socket', 'Mount', 'Swap'],
115+
'Process Properties' : ['Service', 'Socket', 'Mount', 'Swap'],
116+
'Scheduling' : ['Service', 'Socket', 'Mount', 'Swap'],
117+
'Sandboxing' : ['Service', 'Socket', 'Mount', 'Swap'],
118+
'System Call Filtering' : ['Service', 'Socket', 'Mount', 'Swap'],
119+
'Environment' : ['Service', 'Socket', 'Mount', 'Swap'],
120+
'Logging and Standard Input/Output': ['Service', 'Socket', 'Mount', 'Swap'],
121+
'System V Compatibility' : ['Service', 'Socket', 'Mount', 'Swap'],
122+
]
123+
]],
124+
'nspawn': [
125+
'systemd.nspawn.xml':
126+
['sections':
127+
[
128+
'[Exec] Section Options' : ['Exec'],
129+
'[Files] Section Options' : ['Files'],
130+
'[Network] Section Options': ['Network'],
131+
]
132+
]]
133+
123134
]
124135

125136
@Internal
126-
Map<String /* Section */, Map<String /*Keyword*/, Map<String /*Attribute*/, String /*Value*/>>> sectionToKeyWordMapFromDoc = [:]
137+
Map<String /* File Type */, Map<String /* Section */, Map<String /*Keyword*/, Map<String /*Attribute*/, String /*Value*/>>>> fileTypeToSectionToKeyWordMapFromDoc = [:]
127138

128139
@Internal
129140
final XPath xpath
@@ -149,14 +160,22 @@ class GenerateDataFromManPages extends DefaultTask {
149160
logger.debug("Regenerating valid keys")
150161

151162

152-
fileAndSectionTitleToSectionName.keySet().each { file ->
153-
logger.debug("Starting $file")
154-
processFile(file)
163+
fileTypeToFileAndSectionTitleToSectionName.entrySet().each {
164+
fileToSectionToKeyWordMapFromDoc -> fileToSectionToKeyWordMapFromDoc.value.keySet().each {
165+
file ->
166+
logger.debug("Starting $file")
167+
var fileType = fileToSectionToKeyWordMapFromDoc.key
168+
processFile(fileType, file)
169+
}
170+
171+
155172
}
156173

174+
175+
157176
logger.debug("Complete")
158177

159-
def json = JsonOutput.toJson(this.sectionToKeyWordMapFromDoc)
178+
def json = JsonOutput.toJson(this.fileTypeToSectionToKeyWordMapFromDoc)
160179
json = JsonOutput.prettyPrint(json)
161180

162181
File outputData = new File(this.generatedJsonFileLocation.getAbsolutePath() + "/sectionToKeywordMapFromDoc.json")
@@ -173,20 +192,20 @@ class GenerateDataFromManPages extends DefaultTask {
173192
*
174193
* @param filename
175194
*/
176-
void processFile(String filename) {
195+
void processFile(String fileType, String filename) {
177196
File file = new File(this.systemdSourceCodeRoot.getAbsolutePath() + "/man/$filename")
178197

179-
generateKeywordAndValueJsonMapForFile(file)
198+
generateKeywordAndValueJsonMapForFile(fileType, file)
180199

181-
generateDocumentationHtmlFromManPages(file)
200+
generateDocumentationHtmlFromManPages(fileType, file)
182201
}
183202

184203
/**
185204
* Opens the file that will be scanned and extracts a list of variables from it storing it in JSON
186205
*
187206
* @param File file
188207
*/
189-
private void generateKeywordAndValueJsonMapForFile(File file) {
208+
private void generateKeywordAndValueJsonMapForFile(String fileType, File file) {
190209

191210
String filename = file.getName()
192211

@@ -206,12 +225,21 @@ class GenerateDataFromManPages extends DefaultTask {
206225
"/refentry/refsect1/variablelist[not(contains(@class,'environment-variables'))]/varlistentry",
207226
records, XPathConstants.NODESET);
208227
}
228+
else if (file.getAbsolutePath().endsWith("systemd.nspawn.xml")) {
229+
result = (NodeList)xpath.evaluate(
230+
"//variablelist[(contains(@class,'nspawn-directives'))]/varlistentry",
231+
records, XPathConstants.NODESET);
232+
}
209233
else {
210234
result = (NodeList)xpath.evaluate(
211235
"//variablelist[(contains(@class,'unit-directives'))]/varlistentry",
212236
records, XPathConstants.NODESET);
213237
}
214238

239+
if (result.getLength() == 0) {
240+
throw new IllegalStateException("Could not find variables under $filename")
241+
}
242+
215243

216244
for (int i = 0; i < result.getLength(); i++) {
217245
Node varListEntry = result.item(i)
@@ -227,19 +255,20 @@ class GenerateDataFromManPages extends DefaultTask {
227255
try {
228256

229257
String titleOfSection = xpath.evaluate("ancestor::refsect1/title[text()]", varListEntry)
230-
List<String> sections = fileAndSectionTitleToSectionName[filename]['sections'][titleOfSection]
258+
List<String> sections = fileTypeToFileAndSectionTitleToSectionName[fileType][filename]['sections'][titleOfSection]
231259

232260
String originalSection = xpath.evaluate("term/varname[text()]", varListEntry, XPathConstants.STRING)
233261

234262
String originalKeyName = getOptionNameAndValue(originalSection, filename)[0]
235263

236264
for (String section : sections) {
237265
logger.debug("Found options $section in $option in ${file.getAbsolutePath()}")
238-
sectionToKeyWordMapFromDoc.putIfAbsent(section, new TreeMap<>())
266+
fileTypeToSectionToKeyWordMapFromDoc.putIfAbsent(fileType, new TreeMap<>())
267+
fileTypeToSectionToKeyWordMapFromDoc.get(fileType).putIfAbsent(section, new TreeMap<>())
239268
def val = ["declaredInFile": filename]
240269
if (!keyValue.isEmpty()) val["values"] = keyValue
241270
if (keyName != originalKeyName) val["declaredUnderKeyword"] = originalKeyName
242-
sectionToKeyWordMapFromDoc[section][keyName] = val
271+
fileTypeToSectionToKeyWordMapFromDoc[fileType][section][keyName] = val
243272
}
244273
}
245274
catch (IllegalStateException e) {
@@ -272,14 +301,14 @@ class GenerateDataFromManPages extends DefaultTask {
272301
* @param File sourceFile - the source file to extract
273302
* @return
274303
*/
275-
private generateDocumentationHtmlFromManPages(File sourceFile) {
304+
private generateDocumentationHtmlFromManPages(String fileType, File sourceFile) {
276305
DocumentBuilder builder = dbf.newDocumentBuilder()
277306
Document document = builder.parse(sourceFile)
278307
Transformer transformer = getXsltTransformer()
279308

280309
String xsltOutput = transformDocument(document, transformer)
281310

282-
segmentParametersIntoFiles(sourceFile.getName(), xsltOutput)
311+
segmentParametersIntoFiles(fileType, sourceFile.getName(), xsltOutput)
283312
}
284313

285314
/**
@@ -339,7 +368,7 @@ class GenerateDataFromManPages extends DefaultTask {
339368
* @param sourceFileName - the name of the source file we pulled the data from
340369
* @param parameterInfoXMLAsString - A transformed XML document representing the documentation for systemd
341370
*/
342-
private void segmentParametersIntoFiles(String sourceFileName, String parameterInfoXMLAsString) {
371+
private void segmentParametersIntoFiles(String fileType, String sourceFileName, String parameterInfoXMLAsString) {
343372
def builder = dbf.newDocumentBuilder()
344373

345374
ByteArrayInputStream bis = new ByteArrayInputStream(parameterInfoXMLAsString.getBytes("UTF-8"))
@@ -368,11 +397,11 @@ class GenerateDataFromManPages extends DefaultTask {
368397

369398
String name = match.group(1)
370399

371-
List<String> foo = fileAndSectionTitleToSectionName[sourceFileName]['sections'][sectionTitle]
400+
List<String> foo = fileTypeToFileAndSectionTitleToSectionName[fileType][sourceFileName]['sections'][sectionTitle]
372401

373402
for (String sectionName : foo) {
374403
File outputFile = new File(
375-
this.generatedJsonFileLocation.getAbsolutePath() + "/documents/completion/" + sectionName + "/" + name + ".html")
404+
this.generatedJsonFileLocation.getAbsolutePath() + "/documents/completion/" + fileType + "/" + sectionName + "/" + name + ".html")
376405
outputFile.getParentFile().mkdirs()
377406

378407
Writer write = new BufferedWriter(new FileWriter(outputFile))

src/main/kotlin/net/sjrx/intellij/plugins/systemdunitfiles/UnitFileIcon.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ object UnitFileIcon {
77
@JvmField
88
val FILE = IconLoader.getIcon("/net/sjrx/intellij/plugins/systemdunitfiles/systemd.svg", UnitFileIcon::class.java)
99
val AUTOMOUNT = IconLoader.getIcon("/net/sjrx/intellij/plugins/systemdunitfiles/automount.svg", UnitFileIcon::class.java)
10+
val NSPAWN = IconLoader.getIcon("/net/sjrx/intellij/plugins/systemdunitfiles/nspawn.svg", UnitFileIcon::class.java)
1011
val DEVICE = IconLoader.getIcon("/net/sjrx/intellij/plugins/systemdunitfiles/device.svg", UnitFileIcon::class.java)
1112
val MOUNT = IconLoader.getIcon("/net/sjrx/intellij/plugins/systemdunitfiles/mount.svg", UnitFileIcon::class.java)
1213
val PATH = IconLoader.getIcon("/net/sjrx/intellij/plugins/systemdunitfiles/path.svg", UnitFileIcon::class.java)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package net.sjrx.intellij.plugins.systemdunitfiles.filetypes
2+
3+
import net.sjrx.intellij.plugins.systemdunitfiles.UnitFileIcon
4+
import net.sjrx.intellij.plugins.systemdunitfiles.UnitFileLanguage
5+
import org.jetbrains.annotations.Nls
6+
import javax.swing.Icon
7+
8+
class NSpawnFileType private constructor() : AbstractUnitFileType(UnitFileLanguage.INSTANCE) {
9+
override fun getName(): String {
10+
return "Nspawn container systemd container images"
11+
}
12+
13+
override fun getDescription(): String {
14+
return displayName
15+
}
16+
17+
override fun getDefaultExtension(): String {
18+
return "nspawn"
19+
}
20+
21+
override fun getIcon(): Icon? {
22+
return UnitFileIcon.NSPAWN
23+
}
24+
25+
override fun getDisplayName(): @Nls String {
26+
return "NSpawn container settings file"
27+
}
28+
29+
companion object {
30+
val INSTANCE = NSpawnFileType()
31+
}
32+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package net.sjrx.intellij.plugins.systemdunitfiles.semanticdata
2+
3+
4+
interface ConfigFile {
5+
val extension : String
6+
val allowedSections : List<String>
7+
val requiredSections : List<String>
8+
}

0 commit comments

Comments
 (0)