[kotlin] specify script type when create script action

#KTIJ-33209


(cherry picked from commit 5ba94725e06edd678432ff1649feeb559dc306c8)

IJ-CR-156458

GitOrigin-RevId: c67567a7061d3e98bb45cc94799e2e8c52d22b21
This commit is contained in:
Vladimir Dolzhenko
2025-02-23 19:38:15 +01:00
committed by intellij-monorepo-bot
parent cb14a05149
commit 63cc596337
11 changed files with 133 additions and 16 deletions

View File

@@ -381,7 +381,7 @@ action.Kotlin.NewScript.text=Kotlin Script
action.Kotlin.NewScript.description=Creates new Kotlin script or worksheet
action.new.file.dialog.title=New Kotlin Class/File
action.new.script.dialog.title=New Kotlin File
action.new.script.dialog.title=New Kotlin Script File
action.new.file.dialog.file.title=File
action.new.file.dialog.class.title=Class

View File

@@ -6,6 +6,8 @@ import com.intellij.openapi.projectRoots.JavaSdkType
import com.intellij.openapi.projectRoots.ex.PathUtilEx
import com.intellij.openapi.roots.ProjectRootManager
import org.jetbrains.kotlin.idea.base.plugin.artifacts.KotlinArtifacts
import org.jetbrains.kotlin.idea.core.script.k2.NewScriptFileInfo
import org.jetbrains.kotlin.idea.core.script.k2.kotlinScriptTemplateInfo
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinition
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionsSource
import java.io.File
@@ -46,6 +48,10 @@ val Project.defaultDefinition: ScriptDefinition
displayName("Default Kotlin Script")
hostConfiguration(defaultJvmScriptingHostConfiguration)
ide.dependenciesSources(JvmDependency(KotlinArtifacts.kotlinStdlibSources))
ide.kotlinScriptTemplateInfo(NewScriptFileInfo().apply{
id = "default-kts"
title = ".kts"
})
}
)

View File

@@ -32,6 +32,11 @@ class MainKtsScriptDefinitionSource(val project: Project) : ScriptDefinitionsSou
ReportingExternalDependenciesResolver(it, DependencyResolutionService.getInstance(project))
}.with {
ide.dependenciesSources(JvmDependency(KotlinArtifacts.kotlinStdlibSources))
ide.kotlinScriptTemplateInfo(NewScriptFileInfo().apply {
id = "main-kts"
title = ".main.kts"
templateName = "Kotlin Script MainKts"
})
}
ScriptDefinition.FromConfigurations(

View File

@@ -0,0 +1,30 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.kotlin.idea.core.script.k2
import org.jetbrains.kotlin.idea.KotlinIcons
import javax.swing.Icon
import kotlin.script.experimental.api.IdeScriptCompilationConfigurationKeys
import kotlin.script.experimental.util.PropertiesCollection
class NewScriptFileInfo(
var id: String = "",
var title: String = "",
var templateName: String = "Kotlin Script",
var icon: Icon = KotlinIcons.SCRIPT
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as NewScriptFileInfo
return id == other.id
}
override fun hashCode(): Int {
return id.hashCode()
}
}
val IdeScriptCompilationConfigurationKeys.kotlinScriptTemplateInfo: PropertiesCollection.Key<NewScriptFileInfo> by PropertiesCollection.key()

View File

@@ -5,6 +5,8 @@ import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
import com.intellij.openapi.project.Project
import com.intellij.util.EnvironmentUtil
import org.jetbrains.kotlin.config.LanguageVersion
import org.jetbrains.kotlin.idea.core.script.k2.NewScriptFileInfo
import org.jetbrains.kotlin.idea.core.script.k2.kotlinScriptTemplateInfo
import org.jetbrains.kotlin.idea.core.script.loadDefinitionsFromTemplatesByPaths
import org.jetbrains.kotlin.idea.core.script.scriptingDebugLog
import org.jetbrains.kotlin.idea.core.script.scriptingInfoLog
@@ -231,6 +233,11 @@ class GradleKotlinScriptDefinitionWrapper(
ScriptCompilationConfiguration.ide.acceptedLocations.put(listOf(ScriptAcceptedLocation.Project))
@Suppress("DEPRECATION_ERROR")
ScriptCompilationConfiguration.fileNamePattern.put(legacyDefinition.scriptFilePattern.pattern)
ide.kotlinScriptTemplateInfo(NewScriptFileInfo().apply{
id = "gradle-kts"
title = ".gradle.kts"
templateName = "Kotlin Script Gradle"
})
}
}

View File

@@ -0,0 +1,2 @@
#!/usr/bin/env kotlin

View File

@@ -29,5 +29,8 @@
<orderEntry type="module" module-name="intellij.platform.util.jdom" />
<orderEntry type="module" module-name="kotlin.code-insight.impl-base" />
<orderEntry type="module" module-name="intellij.platform.diff.impl" />
<orderEntry type="library" name="kotlinc.kotlin-scripting-common" level="project" />
<orderEntry type="library" name="kotlinc.kotlin-scripting-compiler-impl" level="project" />
<orderEntry type="module" module-name="kotlin.base.scripting" />
</component>
</module>

View File

@@ -9,6 +9,7 @@ import com.intellij.ide.actions.JavaCreateTemplateInPackageAction
import com.intellij.ide.fileTemplates.FileTemplate
import com.intellij.ide.fileTemplates.FileTemplateManager
import com.intellij.ide.fileTemplates.actions.AttributesDefaults
import com.intellij.ide.fileTemplates.impl.BundledFileTemplate
import com.intellij.ide.fileTemplates.ui.CreateFromTemplateDialog
import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.openapi.actionSystem.DataContext
@@ -26,6 +27,7 @@ import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ProjectRootManager
import com.intellij.openapi.ui.InputValidatorEx
import com.intellij.openapi.util.NlsContexts
import com.intellij.openapi.util.NlsSafe
import com.intellij.openapi.util.registry.RegistryManager
import com.intellij.psi.PsiDirectory
import com.intellij.psi.PsiFile
@@ -211,19 +213,24 @@ private fun List<String>.cutExistentPath(targetDir: PsiDirectory): List<String>
const val KOTLIN_WORKSHEET_EXTENSION: String = "ws.kts"
private fun removeKotlinExtensionIfPresent(name: String): String = when {
name.endsWith(".$KOTLIN_WORKSHEET_EXTENSION") -> name.removeSuffix(".$KOTLIN_WORKSHEET_EXTENSION")
name.endsWith(".$STD_SCRIPT_SUFFIX") -> name.removeSuffix(".$STD_SCRIPT_SUFFIX")
name.endsWith(".${KotlinFileType.EXTENSION}") -> name.removeSuffix(".${KotlinFileType.EXTENSION}")
else -> name
}
private val KOTLIN_KNOWS_SUFFIXES = arrayOf(
".$KOTLIN_WORKSHEET_EXTENSION",
".gradle.$STD_SCRIPT_SUFFIX",
".main.$STD_SCRIPT_SUFFIX",
".$STD_SCRIPT_SUFFIX",
".${KotlinFileType.EXTENSION}"
)
private fun removeKotlinExtensionIfPresent(name: String): String =
KOTLIN_KNOWS_SUFFIXES.firstOrNull { name.endsWith(it) }?.let { name.removeSuffix(it) } ?: name
private fun createKotlinFileFromTemplate(dir: PsiDirectory, fileName: String, template: FileTemplate): PsiFile? {
val project = dir.project
val extraExtension = template.name.substringAfterLast('.', "").takeIf { it.isNotEmpty() }?.let { ".$it" } ?: ""
val className = fileName.substringBefore('.')
val attributesDefaults = AttributesDefaults(className).withFixedName(true)
val defaultProperties = FileTemplateManager.getInstance(project).defaultProperties.apply {
put(FileTemplate.ATTRIBUTE_FILE_NAME, fileName)
put(FileTemplate.ATTRIBUTE_FILE_NAME, fileName + extraExtension)
}
val element = try {
CreateFromTemplateDialog(
@@ -246,15 +253,34 @@ private val FQNAME_SEPARATORS: CharArray = charArrayOf('/', '\\', '.')
internal fun createFileFromTemplateWithStat(name: String, template: FileTemplate, dir: PsiDirectory): PsiFile? {
KotlinJ2KOnboardingFUSCollector.logFirstKtFileCreated(dir.project) // implementation checks if it is actually the first
KotlinCreateFileFUSCollector.logFileTemplate(template.name)
KotlinCreateFileFUSCollector.logFileTemplate(template.correctName())
return createKotlinFileFromTemplate(name, template, dir)
}
/*
Kotlin Script file templates have '.kts' extension instead of ".main.kts" or ".gradle.kts"
That leads to incorrect template naming such as 'Kotlin Script MainKts.main'
//TODO enumify template names
*/
private fun FileTemplate.correctName(): @NlsSafe String {
if (this !is BundledFileTemplate) return name
if (extension != "kts") return name
return when (qualifiedName) {
"Kotlin Script MainKts.main.kts" -> "Kotlin Script MainKts"
"Kotlin Script Gradle.gradle.kts" -> "Kotlin Script Gradle"
else -> name
}
}
fun createKotlinFileFromTemplate(name: String, template: FileTemplate, dir: PsiDirectory): PsiFile? {
val directorySeparators = when (template.name) {
val correctName = template.correctName()
val directorySeparators = when (correctName) {
"Kotlin File" -> FILE_SEPARATORS
"Kotlin Worksheet" -> FILE_SEPARATORS
"Kotlin Script" -> FILE_SEPARATORS
"Kotlin Script Gradle" -> FILE_SEPARATORS
"Kotlin Script MainKts" -> FILE_SEPARATORS
else -> FQNAME_SEPARATORS
}
@@ -262,7 +288,12 @@ fun createKotlinFileFromTemplate(name: String, template: FileTemplate, dir: PsiD
val service = DumbService.getInstance(dir.project)
return service.computeWithAlternativeResolveEnabled<PsiFile?, Throwable> {
val adjustedDir = CreateTemplateInPackageAction.adjustDirectory(targetDir, JavaModuleSourceRootTypes.SOURCES)
val adjustedDir =
if (KotlinScriptFileTemplate.entries.any { it.fileName == correctName }) {
targetDir
} else {
CreateTemplateInPackageAction.adjustDirectory(targetDir, JavaModuleSourceRootTypes.SOURCES)
}
val psiFile = createKotlinFileFromTemplate(adjustedDir, fileName, template)
if (psiFile is KtFile) {
val singleClass = psiFile.declarations.singleOrNull() as? KtClass
@@ -277,10 +308,16 @@ fun createKotlinFileFromTemplate(name: String, template: FileTemplate, dir: PsiD
}
}
internal fun CreateFileFromTemplateDialog.Builder.addKind(t: KotlinFileTemplate) =
internal fun CreateFileFromTemplateDialog.Builder.addKind(t: KotlinTemplate) =
addKind(t.title, t.icon, t.fileName)
internal enum class KotlinFileTemplate(@NlsContexts.ListItem val title: String, val icon: Icon, val fileName: String) {
internal interface KotlinTemplate {
val title: String
val icon: Icon
val fileName: String
}
internal enum class KotlinFileTemplate(@NlsContexts.ListItem override val title: String, override val icon: Icon, override val fileName: String): KotlinTemplate {
Class(KotlinBundle.message("action.new.file.dialog.class.title"), KotlinIcons.CLASS, "Kotlin Class"),
File(
KotlinBundle.message("action.new.file.dialog.file.title"),

View File

@@ -10,9 +10,16 @@ import com.intellij.openapi.actionSystem.LangDataKeys
import com.intellij.openapi.project.DumbAware
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ProjectRootManager
import com.intellij.openapi.util.NlsContexts
import com.intellij.psi.PsiDirectory
import org.jetbrains.kotlin.idea.KotlinIcons
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
import kotlin.collections.none
import org.jetbrains.kotlin.idea.core.script.k2.K2ScriptDefinitionProvider
import org.jetbrains.kotlin.idea.core.script.k2.kotlinScriptTemplateInfo
import org.jetbrains.kotlin.scripting.definitions.ScriptDefinitionProvider
import javax.swing.Icon
import kotlin.script.experimental.api.ScriptCompilationConfiguration
import kotlin.script.experimental.api.ide
internal class NewKotlinScriptAction : AbstractNewKotlinFileAction(), DumbAware {
override fun isAvailable(dataContext: DataContext): Boolean {
@@ -31,11 +38,31 @@ internal class NewKotlinScriptAction : AbstractNewKotlinFileAction(), DumbAware
override fun buildDialog(project: Project, directory: PsiDirectory, builder: CreateFileFromTemplateDialog.Builder) {
with(builder) {
setTitle(KotlinBundle.message("action.new.script.dialog.title"))
addKind(KotlinFileTemplate.Script)
val scriptDefinitionProvider = ScriptDefinitionProvider.getInstance(project) as? K2ScriptDefinitionProvider
if (scriptDefinitionProvider != null) {
K2ScriptDefinitionProvider.getInstance(project).getAllDefinitions().mapNotNull {
it.compilationConfiguration[ScriptCompilationConfiguration.ide.kotlinScriptTemplateInfo]
}.distinct().forEach {
builder.addKind(it.title, it.icon, it.templateName)
}
} else {
builder
.addKind(KotlinScriptFileTemplate.GradleKts)
.addKind(KotlinScriptFileTemplate.MainKts)
.addKind(KotlinScriptFileTemplate.Kts)
}
setValidator(NewKotlinFileNameValidator)
}
}
override fun getActionName(directory: PsiDirectory, newName: String, templateName: String): String =
KotlinBundle.message("action.Kotlin.NewScript.text")
}
internal enum class KotlinScriptFileTemplate(@NlsContexts.ListItem override val title: String, override val icon: Icon, override val fileName: String): KotlinTemplate {
GradleKts(".gradle.kts", KotlinIcons.GRADLE_SCRIPT, "Kotlin Script Gradle"),
MainKts(".main.kts", KotlinIcons.SCRIPT, "Kotlin Script MainKts"),
Kts(".kts", KotlinIcons.SCRIPT, "Kotlin Script"),
}

View File

@@ -595,7 +595,7 @@
<action id="Kotlin.NewScript" class="org.jetbrains.kotlin.idea.actions.NewKotlinScriptAction"
icon="org.jetbrains.kotlin.idea.KotlinIcons.SCRIPT">
<add-to-group group-id="NewGroup" anchor="before" relative-to-action="NewFromTemplate"/>
<add-to-group group-id="NewGroup" anchor="before" relative-to-action="NewWebDevelopment"/>
</action>
<group id="KotlinToolsGroup" popup="true" icon="org.jetbrains.kotlin.idea.KotlinIcons.SMALL_LOGO"