Skip to content

Commit dc316e3

Browse files
authored
Merge pull request #11 from nickalie/master
added run icon for Taskfile.yml tasks
2 parents a7582e6 + a083446 commit dc316e3

File tree

4 files changed

+191
-1
lines changed

4 files changed

+191
-1
lines changed

build.gradle

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ apply plugin: 'kotlin'
88
apply plugin: 'org.jetbrains.intellij'
99

1010
group 'lechuck'
11-
version '1.5.0'
11+
version '1.6.0'
1212

1313
sourceCompatibility = 17
1414
targetCompatibility = 17
@@ -39,6 +39,7 @@ dependencies {
3939
intellij {
4040
version = '2023.1'
4141
updateSinceUntilBuild = false
42+
plugins = ['org.jetbrains.plugins.yaml']
4243
}
4344

4445
buildSearchableOptions {
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package lechuck.intellij
2+
3+
import com.intellij.execution.ExecutionException
4+
import com.intellij.execution.ExecutorRegistry
5+
import com.intellij.execution.executors.DefaultRunExecutor
6+
import com.intellij.execution.runners.ExecutionEnvironmentBuilder
7+
import com.intellij.execution.impl.RunManagerImpl
8+
import com.intellij.openapi.actionSystem.AnAction
9+
import com.intellij.openapi.actionSystem.AnActionEvent
10+
import com.intellij.openapi.project.Project
11+
import com.intellij.psi.PsiElement
12+
import com.intellij.execution.lineMarker.RunLineMarkerContributor
13+
import com.intellij.icons.AllIcons
14+
import org.jetbrains.yaml.psi.YAMLKeyValue
15+
import org.jetbrains.yaml.psi.YAMLMapping
16+
17+
class TaskLineMarkerProvider : RunLineMarkerContributor() {
18+
companion object {
19+
val TASKFILE_PATTERN = Regex("taskfile(?:\\.dist)?\\.ya?ml", RegexOption.IGNORE_CASE)
20+
}
21+
22+
override fun getInfo(element: PsiElement): Info? {
23+
// Only process YAML files named: Taskfile.yml, taskfile.yml, Taskfile.yaml, taskfile.yaml,
24+
// Taskfile.dist.yml, taskfile.dist.yml, Taskfile.dist.yaml, taskfile.dist.yaml
25+
val file = element.containingFile
26+
if (!file.name.matches(TASKFILE_PATTERN)) {
27+
return null
28+
}
29+
30+
// We want to match only the key element of a task
31+
if (element.parent !is YAMLKeyValue) {
32+
return null
33+
}
34+
val keyValue = element.parent as YAMLKeyValue
35+
36+
// Check if this key is directly under the tasks section
37+
val tasksSection = keyValue.parent?.parent
38+
if (tasksSection !is YAMLKeyValue || tasksSection.keyText != "tasks") {
39+
return null
40+
}
41+
42+
// Check if the value is a mapping (has child elements)
43+
if (keyValue.value !is YAMLMapping) {
44+
return null
45+
}
46+
47+
// Check if we're on the key element itself
48+
if (element != keyValue.key) {
49+
return null
50+
}
51+
52+
// This is a task definition, create a run action
53+
val taskName = keyValue.keyText
54+
return Info(
55+
AllIcons.Actions.Execute,
56+
{ "Run Task: $taskName" },
57+
TaskRunAction(taskName, element.project, file.virtualFile.path)
58+
)
59+
}
60+
61+
private class TaskRunAction(
62+
private val taskName: String,
63+
private val project: Project,
64+
private val taskfilePath: String
65+
) : AnAction() {
66+
override fun actionPerformed(e: AnActionEvent) {
67+
val runManager = RunManagerImpl.getInstanceImpl(project)
68+
val configurationType = TaskRunConfigurationType()
69+
70+
val configurationName = "Task: $taskName"
71+
val existingConfiguration = runManager.findConfigurationByName(configurationName)
72+
73+
val configuration = if (existingConfiguration != null) {
74+
existingConfiguration
75+
} else {
76+
val factory = configurationType.configurationFactories[0]
77+
runManager.createConfiguration(configurationName, factory)
78+
}
79+
80+
val runConfig = configuration.configuration as TaskRunConfiguration
81+
runConfig.task = taskName
82+
runConfig.filename = taskfilePath
83+
84+
if (existingConfiguration == null) {
85+
runManager.addConfiguration(configuration)
86+
}
87+
88+
runManager.selectedConfiguration = configuration
89+
90+
try {
91+
val executor = ExecutorRegistry.getInstance().getExecutorById(DefaultRunExecutor.EXECUTOR_ID)
92+
if (executor != null) {
93+
ExecutionEnvironmentBuilder.create(executor, configuration)?.buildAndExecute()
94+
}
95+
} catch (ex: ExecutionException) {
96+
ex.printStackTrace()
97+
}
98+
}
99+
}
100+
}

src/main/resources/META-INF/plugin.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,13 @@
6363
<!-- please see https://plugins.jetbrains.com/docs/intellij/plugin-compatibility.html
6464
on how to target different products -->
6565
<depends>com.intellij.modules.platform</depends>
66+
<depends>org.jetbrains.plugins.yaml</depends>
6667

6768
<extensions defaultExtensionNs="com.intellij">
6869
<configurationType implementation="lechuck.intellij.TaskRunConfigurationType"/>
70+
<runLineMarkerContributor
71+
language="yaml"
72+
implementationClass="lechuck.intellij.TaskLineMarkerProvider"/>
6973
</extensions>
7074

7175
<actions>
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package lechuck.intellij
2+
3+
import com.intellij.testFramework.fixtures.BasePlatformTestCase
4+
import com.intellij.psi.PsiFile
5+
import com.intellij.psi.PsiElement
6+
import com.intellij.psi.util.PsiTreeUtil
7+
import org.jetbrains.yaml.psi.YAMLKeyValue
8+
9+
class TaskLineMarkerProviderTest : BasePlatformTestCase() {
10+
private val provider = TaskLineMarkerProvider()
11+
12+
fun testTaskfilePatternMatching() {
13+
val validNames = listOf(
14+
"Taskfile.yml",
15+
"taskfile.yml",
16+
"Taskfile.yaml",
17+
"taskfile.yaml",
18+
"Taskfile.dist.yml",
19+
"taskfile.dist.yml",
20+
"Taskfile.dist.yaml",
21+
"taskfile.dist.yaml"
22+
)
23+
24+
val invalidNames = listOf(
25+
"other.yml",
26+
"taskfile.json",
27+
"taskfile.yaml.bak",
28+
"mytaskfile.yml"
29+
)
30+
31+
validNames.forEach { name ->
32+
assertTrue("Should match: $name", name.matches(TaskLineMarkerProvider.TASKFILE_PATTERN))
33+
}
34+
35+
invalidNames.forEach { name ->
36+
assertFalse("Should not match: $name", name.matches(TaskLineMarkerProvider.TASKFILE_PATTERN))
37+
}
38+
}
39+
40+
fun testGetInfoForValidTaskKey() {
41+
val yamlFile = """
42+
tasks:
43+
test:
44+
cmds:
45+
- echo "test"
46+
""".trimIndent()
47+
48+
val file = myFixture.configureByText("Taskfile.yml", yamlFile)
49+
val taskKey = findTaskKey(file, "test")
50+
51+
assertNotNull(taskKey)
52+
val info = provider.getInfo(taskKey!!)
53+
assertNotNull("Should return Info for valid task key", info)
54+
assertEquals("Run Task: test", info?.tooltipProvider?.apply(taskKey))
55+
}
56+
57+
fun testGetInfoForNonTaskKey() {
58+
val yamlFile = """
59+
version: '3'
60+
tasks:
61+
test:
62+
cmds:
63+
- echo "test"
64+
""".trimIndent()
65+
66+
val file = myFixture.configureByText("Taskfile.yml", yamlFile)
67+
val versionKey = findKey(file, "version")
68+
69+
assertNotNull(versionKey)
70+
val info = provider.getInfo(versionKey!!)
71+
assertNull("Should return null for non-task key", info)
72+
}
73+
74+
private fun findTaskKey(file: PsiFile, taskName: String): PsiElement? {
75+
return PsiTreeUtil.findChildrenOfType(file, YAMLKeyValue::class.java)
76+
.find { it.keyText == taskName && it.parent?.parent is YAMLKeyValue }
77+
?.key
78+
}
79+
80+
private fun findKey(file: PsiFile, keyName: String): PsiElement? {
81+
return PsiTreeUtil.findChildrenOfType(file, YAMLKeyValue::class.java)
82+
.find { it.keyText == keyName }
83+
?.key
84+
}
85+
}

0 commit comments

Comments
 (0)