Skip to content

Commit 9fd2a56

Browse files
authored
feat: console command runners (#12)
2 parents 546ce1c + 2156c26 commit 9fd2a56

12 files changed

+339
-1
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.github.tempest.framework.console.run
2+
3+
import com.github.tempest.framework.TempestBundle
4+
import com.github.tempest.framework.php.getConsoleCommandName
5+
import com.intellij.execution.lineMarker.ExecutorAction
6+
import com.intellij.execution.lineMarker.RunLineMarkerContributor
7+
import com.intellij.icons.AllIcons
8+
import com.intellij.openapi.util.text.StringUtil
9+
import com.intellij.psi.PsiElement
10+
import com.jetbrains.php.lang.psi.elements.Method
11+
12+
class ConsoleCommandLineMarkerProvider : RunLineMarkerContributor() {
13+
override fun getInfo(element: PsiElement) = when {
14+
element !is Method -> null
15+
else -> {
16+
val commandName = element.getConsoleCommandName() ?: return null
17+
Info(
18+
AllIcons.Actions.Execute,
19+
ExecutorAction.getActions(1),
20+
) {
21+
TempestBundle.message(
22+
"action.run.target.text",
23+
StringUtil.wrapWithDoubleQuote(TempestBundle.message("action.run.target.command", commandName)),
24+
)
25+
}
26+
}
27+
}
28+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.github.tempest.framework.console.run
2+
3+
import com.intellij.execution.configurations.ConfigurationFactory
4+
import com.intellij.execution.configurations.RunConfigurationOptions
5+
import com.intellij.openapi.project.Project
6+
import com.jetbrains.php.config.commandLine.PhpCommandSettings
7+
import com.jetbrains.php.run.PhpCommandLineRunConfiguration
8+
9+
class TempestConsoleCommandRunConfiguration(
10+
project: Project,
11+
factory: ConfigurationFactory,
12+
name: String
13+
) : PhpCommandLineRunConfiguration<TempestConsoleCommandRunConfigurationSettings>(project, factory, name) {
14+
override fun fillCommandSettings(
15+
envs: Map<String, String>,
16+
command: PhpCommandSettings
17+
) {
18+
val commandName = settings.commandName ?: return
19+
command.setScript("tempest", false)
20+
command.addArgument(commandName)
21+
22+
command.importCommandLineSettings(settings.commandLineSettings, command.workingDirectory)
23+
command.addEnvs(envs)
24+
}
25+
26+
override fun getOptions() = super.getOptions() as TempestConsoleCommandRunConfigurationSettings
27+
override fun getOptionsClass(): Class<out RunConfigurationOptions> {
28+
return TempestConsoleCommandRunConfigurationSettings::class.java
29+
}
30+
31+
override fun getConfigurationEditor() = TempestConsoleCommandSettingsEditor(project)
32+
33+
override fun createSettings() = TempestConsoleCommandRunConfigurationSettings().apply {
34+
}
35+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.github.tempest.framework.console.run
2+
3+
import com.intellij.execution.configurations.LocatableRunConfigurationOptions
4+
import com.jetbrains.php.run.PhpCommandLineSettings
5+
import com.jetbrains.php.run.PhpRunConfigurationSettings
6+
7+
class TempestConsoleCommandRunConfigurationSettings : PhpRunConfigurationSettings, LocatableRunConfigurationOptions() {
8+
private val myCommandName = string("").provideDelegate(this, "commandName")
9+
private val myBinary = string("./tempest").provideDelegate(this, "binary")
10+
private val myWorkingDirectory = string("").provideDelegate(this, "binary")
11+
12+
var commandName: String?
13+
get() = myCommandName.getValue(this)
14+
set(scriptName) {
15+
myCommandName.setValue(this, scriptName)
16+
}
17+
18+
var binary: String?
19+
get() = myBinary.getValue(this)
20+
set(scriptName) {
21+
myBinary.setValue(this, scriptName)
22+
}
23+
24+
var documentRoot: String?
25+
get() = myBinary.getValue(this)
26+
set(scriptName) {
27+
myBinary.setValue(this, scriptName)
28+
}
29+
30+
var commandLineSettings = PhpCommandLineSettings()
31+
32+
override fun getWorkingDirectory() = myWorkingDirectory.getValue(this)
33+
34+
override fun setWorkingDirectory(p0: String?) {
35+
myWorkingDirectory.setValue(this, p0)
36+
}
37+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.github.tempest.framework.console.run
2+
3+
import com.github.tempest.framework.TempestIcons
4+
import com.intellij.execution.configurations.ConfigurationFactory
5+
import com.intellij.execution.configurations.ConfigurationTypeBase
6+
import com.intellij.openapi.project.Project
7+
8+
class TempestConsoleCommandRunConfigurationType : ConfigurationTypeBase(
9+
ID,
10+
"Tempest Command",
11+
"Runs console command",
12+
TempestIcons.TEMPEST,
13+
) {
14+
init {
15+
addFactory(object : ConfigurationFactory(this) {
16+
override fun getId() = ID
17+
18+
override fun createTemplateConfiguration(project: Project) =
19+
TempestConsoleCommandRunConfiguration(project, this, "Tempest")
20+
21+
override fun getOptionsClass() = TempestConsoleCommandRunConfigurationSettings::class.java
22+
})
23+
}
24+
25+
companion object {
26+
const val ID = "TempestConsoleCommandRunConfiguration"
27+
28+
val INSTANCE = TempestConsoleCommandRunConfigurationType()
29+
}
30+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package com.github.tempest.framework.console.run
2+
3+
import com.intellij.openapi.options.SettingsEditor
4+
import com.intellij.openapi.ui.DialogPanel
5+
import com.intellij.ui.dsl.builder.Align
6+
import com.intellij.ui.dsl.builder.LabelPosition
7+
import com.intellij.ui.dsl.builder.TopGap
8+
import com.intellij.ui.dsl.builder.panel
9+
import com.jetbrains.php.run.PhpCommandLineConfigurationEditor
10+
import javax.swing.JPanel
11+
import javax.swing.JTextField
12+
13+
private fun PhpCommandLineConfigurationEditor.getMainPanel(): JPanel? {
14+
val reflection = PhpCommandLineConfigurationEditor::class.java.getDeclaredField("myMainPanel")
15+
reflection.isAccessible = true
16+
return reflection.get(this) as JPanel?
17+
}
18+
19+
class TempestConsoleCommandSettingsEditor private constructor(): SettingsEditor<TempestConsoleCommandRunConfiguration>() {
20+
private val commandNameField = JTextField()
21+
private val phpCommandLineConfigurationEditor = PhpCommandLineConfigurationEditor()
22+
23+
private lateinit var myPanel: DialogPanel
24+
25+
constructor(project: com.intellij.openapi.project.Project) : this() {
26+
myPanel = panel {
27+
row {
28+
cell(commandNameField)
29+
.label("Command:", LabelPosition.LEFT)
30+
.align(Align.FILL)
31+
}.topGap(TopGap.MEDIUM)
32+
33+
row {
34+
phpCommandLineConfigurationEditor.init(project, true)
35+
phpCommandLineConfigurationEditor.getMainPanel()?.apply {
36+
scrollCell(this).align(Align.FILL)
37+
}
38+
}.topGap(TopGap.MEDIUM)
39+
}
40+
}
41+
42+
override fun resetEditorFrom(tempestConsoleCommandRunConfiguration: TempestConsoleCommandRunConfiguration) {
43+
val settings = tempestConsoleCommandRunConfiguration.settings
44+
45+
myPanel.reset()
46+
commandNameField.text = settings.commandName
47+
phpCommandLineConfigurationEditor.resetEditorFrom(settings.commandLineSettings)
48+
}
49+
50+
override fun applyEditorTo(tempestConsoleCommandRunConfiguration: TempestConsoleCommandRunConfiguration) {
51+
val settings = tempestConsoleCommandRunConfiguration.settings
52+
53+
settings.commandName = commandNameField.text
54+
phpCommandLineConfigurationEditor.applyEditorTo(settings.commandLineSettings)
55+
}
56+
57+
override fun createEditor() = myPanel
58+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.github.tempest.framework.console.run
2+
3+
import com.github.tempest.framework.TempestBundle
4+
import com.github.tempest.framework.TempestIcons
5+
import com.github.tempest.framework.console.index.ConsoleCommandsIndex
6+
import com.intellij.icons.AllIcons
7+
import com.intellij.ide.actions.runAnything.activity.RunAnythingAnActionProvider
8+
import com.intellij.openapi.actionSystem.CommonDataKeys
9+
import com.intellij.openapi.actionSystem.DataContext
10+
import com.intellij.openapi.application.ReadAction
11+
import com.intellij.util.indexing.FileBasedIndex
12+
13+
class TempestRunAnythingProvider : RunAnythingAnActionProvider<TempestRunCommandAction>() {
14+
override fun getCommand(value: TempestRunCommandAction) =
15+
TempestBundle.message("action.run.target.command", value.commandName)
16+
17+
override fun getHelpCommandPlaceholder() = "tempest <command>"
18+
19+
override fun getCompletionGroupTitle() = "Tempest"
20+
21+
override fun getHelpCommand() = "tempest"
22+
23+
override fun getHelpGroupTitle() = "PHP"
24+
25+
override fun getHelpIcon() = TempestIcons.TEMPEST
26+
27+
override fun getIcon(value: TempestRunCommandAction) = AllIcons.Actions.Execute
28+
29+
override fun getValues(dataContext: DataContext, pattern: String): Collection<TempestRunCommandAction> {
30+
val project = CommonDataKeys.PROJECT.getData(dataContext) ?: return emptyList()
31+
32+
return ReadAction.compute<Collection<TempestRunCommandAction>, Throwable> {
33+
FileBasedIndex.getInstance()
34+
.getAllKeys(ConsoleCommandsIndex.key, project)
35+
.map { TempestRunCommandAction(it) }
36+
}
37+
}
38+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.github.tempest.framework.console.run
2+
3+
import com.github.tempest.framework.TempestBundle
4+
import com.intellij.execution.Executor
5+
import com.intellij.execution.RunManagerEx
6+
import com.intellij.execution.runners.ExecutionUtil
7+
import com.intellij.icons.AllIcons
8+
import com.intellij.openapi.actionSystem.AnAction
9+
import com.intellij.openapi.actionSystem.AnActionEvent
10+
11+
class TempestRunCommandAction(val commandName: String) : AnAction() {
12+
init {
13+
templatePresentation.setText(TempestBundle.message("action.run.target.text", commandName), false)
14+
templatePresentation.description = TempestBundle.message("action.run.target.description", commandName)
15+
templatePresentation.icon = AllIcons.Actions.Execute
16+
}
17+
18+
override fun actionPerformed(event: AnActionEvent) {
19+
val project = event.project ?: return
20+
21+
val runManager = RunManagerEx.getInstanceEx(project)
22+
val producer = TempestRunConfigurationProducer()
23+
val configurationFactory = producer.configurationFactory
24+
25+
val runConfiguration = TempestConsoleCommandRunConfiguration(
26+
project,
27+
configurationFactory,
28+
TempestBundle.message("action.run.target.command", commandName),
29+
)
30+
.apply { settings.commandName = commandName }
31+
32+
val configuration = runManager.createConfiguration(runConfiguration, configurationFactory)
33+
34+
runManager.setTemporaryConfiguration(configuration)
35+
ExecutionUtil.runConfiguration(configuration, Executor.EXECUTOR_EXTENSION_NAME.extensionList.first())
36+
}
37+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.github.tempest.framework.console.run
2+
3+
import com.intellij.execution.configurations.ConfigurationFactory
4+
import com.intellij.openapi.project.Project
5+
6+
class TempestRunConfigurationFactory(private val runConfigurationType: TempestConsoleCommandRunConfigurationType) :
7+
ConfigurationFactory(runConfigurationType) {
8+
override fun getId() = TempestConsoleCommandRunConfigurationType.ID
9+
override fun getName() = runConfigurationType.displayName
10+
11+
override fun createTemplateConfiguration(project: Project) =
12+
TempestConsoleCommandRunConfiguration(project, this, "name")
13+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.github.tempest.framework.console.run
2+
3+
import com.github.tempest.framework.TempestBundle
4+
import com.github.tempest.framework.php.getConsoleCommandName
5+
import com.intellij.execution.actions.ConfigurationContext
6+
import com.intellij.execution.actions.LazyRunConfigurationProducer
7+
import com.intellij.openapi.util.Ref
8+
import com.intellij.psi.PsiElement
9+
import com.jetbrains.php.lang.psi.elements.Method
10+
11+
class TempestRunConfigurationProducer : LazyRunConfigurationProducer<TempestConsoleCommandRunConfiguration>() {
12+
override fun setupConfigurationFromContext(
13+
configuration: TempestConsoleCommandRunConfiguration,
14+
context: ConfigurationContext,
15+
sourceElement: Ref<PsiElement>
16+
): Boolean {
17+
val element = context.psiLocation as? Method ?: return false
18+
val commandName = element.getConsoleCommandName() ?: return false
19+
20+
configuration.settings.commandName = commandName
21+
configuration.name = TempestBundle.message("action.run.target.command", commandName)
22+
23+
return true
24+
}
25+
26+
override fun isConfigurationFromContext(
27+
configuration: TempestConsoleCommandRunConfiguration,
28+
context: ConfigurationContext
29+
): Boolean {
30+
val method = context.psiLocation as? Method ?: return false
31+
32+
return configuration.settings.commandName == method.getConsoleCommandName()
33+
}
34+
35+
override fun getConfigurationFactory() =
36+
TempestRunConfigurationFactory(TempestConsoleCommandRunConfigurationType.INSTANCE)
37+
}

src/main/kotlin/com/github/tempest/framework/php/mixin.kt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package com.github.tempest.framework.php
22

3+
import com.github.tempest.framework.TempestFrameworkClasses
4+
import com.intellij.openapi.util.text.StringUtil
35
import com.intellij.psi.util.PsiTreeUtil
46
import com.jetbrains.php.lang.psi.PhpFile
7+
import com.jetbrains.php.lang.psi.elements.Method
58
import com.jetbrains.php.lang.psi.elements.Variable
69

710
fun PhpFile.getPhpViewVariables(): Set<Variable> {
@@ -11,3 +14,14 @@ fun PhpFile.getPhpViewVariables(): Set<Variable> {
1114
.distinctBy { it.name }
1215
.toSet()
1316
}
17+
18+
fun Method.getConsoleCommandName(): String? {
19+
return this
20+
.getAttributes(TempestFrameworkClasses.ConsoleCommand)
21+
.firstOrNull()
22+
?.arguments
23+
?.run { this.find { it.name == "name" } ?: firstOrNull() }
24+
?.argument
25+
?.value
26+
?.run { StringUtil.unquoteString(this) }
27+
}

0 commit comments

Comments
 (0)