diff --git a/idea/src/org/jetbrains/kotlin/idea/actions/NewKotlinFileAction.kt b/idea/src/org/jetbrains/kotlin/idea/actions/NewKotlinFileAction.kt index 7f035ca3246..0e3698a28ca 100644 --- a/idea/src/org/jetbrains/kotlin/idea/actions/NewKotlinFileAction.kt +++ b/idea/src/org/jetbrains/kotlin/idea/actions/NewKotlinFileAction.kt @@ -35,9 +35,11 @@ import com.intellij.openapi.project.DumbAware import com.intellij.openapi.project.DumbService import com.intellij.openapi.project.Project import com.intellij.openapi.roots.ProjectRootManager +import com.intellij.openapi.ui.InputValidatorEx import com.intellij.psi.PsiDirectory import com.intellij.psi.PsiFile import com.intellij.util.IncorrectOperationException +import org.jetbrains.annotations.TestOnly import org.jetbrains.kotlin.idea.KotlinFileType import org.jetbrains.kotlin.idea.KotlinIcons import org.jetbrains.kotlin.idea.util.application.runWriteAction @@ -86,6 +88,8 @@ class NewKotlinFileAction : CreateFileFromTemplateAction( .addKind("Interface", KotlinIcons.INTERFACE, "Kotlin Interface") .addKind("Enum class", KotlinIcons.ENUM, "Kotlin Enum") .addKind("Object", KotlinIcons.OBJECT, "Kotlin Object") + + builder.setValidator(NameValidator) } override fun getActionName(directory: PsiDirectory, newName: String, templateName: String) = "Kotlin File/Class" @@ -114,7 +118,34 @@ class NewKotlinFileAction : CreateFileFromTemplateAction( Companion.createFileFromTemplate(name, template, dir) companion object { - private fun findOrCreateTarget(dir: PsiDirectory, name: String, directorySeparators: Array): Pair { + private object NameValidator : InputValidatorEx { + override fun getErrorText(inputString: String): String? { + if (inputString.trim().isEmpty()) { + return "Name can't be empty" + } + + val parts: List = inputString.split(*FQNAME_SEPARATORS) + if (parts.any { it.trim().isEmpty() }) { + return "Name can't have empty parts" + } + + return null + } + + override fun checkInput(inputString: String): Boolean { + return true + } + + override fun canClose(inputString: String): Boolean { + return getErrorText(inputString) == null + } + } + + @get:TestOnly + val nameValidator: InputValidatorEx + get() = NameValidator + + private fun findOrCreateTarget(dir: PsiDirectory, name: String, directorySeparators: CharArray): Pair { var className = name.substringBeforeLast(".kt") var targetDir = dir @@ -157,8 +188,11 @@ class NewKotlinFileAction : CreateFileFromTemplateAction( return element?.containingFile } + private val FILE_SEPARATORS = charArrayOf('/', '\\') + private val FQNAME_SEPARATORS = charArrayOf('/', '\\', '.') + fun createFileFromTemplate(name: String, template: FileTemplate, dir: PsiDirectory): PsiFile? { - val directorySeparators = if (template.name == "Kotlin File") arrayOf('/', '\\') else arrayOf('/', '\\', '.') + val directorySeparators = if (template.name == "Kotlin File") FILE_SEPARATORS else FQNAME_SEPARATORS val (className, targetDir) = findOrCreateTarget(dir, name, directorySeparators) val service = DumbService.getInstance(dir.project) diff --git a/idea/tests/org/jetbrains/kotlin/idea/actions/NewKotlinFileActionTest.kt b/idea/tests/org/jetbrains/kotlin/idea/actions/NewKotlinFileActionTest.kt new file mode 100644 index 00000000000..9f3f871bd7e --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/actions/NewKotlinFileActionTest.kt @@ -0,0 +1,57 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.idea.actions + +import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase +import org.junit.Assert + +class NewKotlinFileActionTest: LightCodeInsightFixtureTestCase() { + companion object { + private val EMPTY_PARTS_ERROR = "Name can't have empty parts" + private val EMPTY_ERROR = "Name can't be empty" + } + + fun testEmptyName() { + validateName("", EMPTY_ERROR) + } + + fun testSpaces() { + validateName(" ", EMPTY_ERROR) + } + + fun testEmptyEnd() { + validateName("Foo/", EMPTY_PARTS_ERROR) + } + + fun testEmptyPartInQualified() { + validateName("a..b", EMPTY_PARTS_ERROR) + } + + fun testFileWithKt() { + validateName("test.kt", null) + } + + fun testFileWithUnixPathKt() { + validateName("a/b/test.kt", null) + } + + fun testFileWithWinPathKt() { + validateName("a\\b\\test.kt", null) + } + + fun testSimpleFile() { + validateName("some", null) + } + + fun testSimpleFileWithPath() { + validateName("a/bb\\some", null) + } + + private fun validateName(name: String, errorMessage: String?) { + val actualError = NewKotlinFileAction.nameValidator.getErrorText(name) + Assert.assertEquals("Invalid error message", errorMessage, actualError) + } +} \ No newline at end of file