diff --git a/platform/platform-tests/testSrc/com/intellij/module/GroupModulesByQualifiedNamesTest.kt b/platform/platform-tests/testSrc/com/intellij/module/GroupModulesByQualifiedNamesTest.kt index d785e23d7db2..1c493bc7b05a 100644 --- a/platform/platform-tests/testSrc/com/intellij/module/GroupModulesByQualifiedNamesTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/module/GroupModulesByQualifiedNamesTest.kt @@ -87,6 +87,20 @@ class GroupModulesByQualifiedNamesTest : PlatformTestCase() { assertEmpty(parentGroup.childGroups(grouper)) } + fun `test names with incorrect chars after dots`() { + val module1 = createModule("a.foo-1.2") + val module2 = createModule("a.foo-1.3") + + assertEquals("foo-1.2", grouper.getShortenedName(module1)) + assertEquals("foo-1.3", grouper.getShortenedName(module2)) + + val parentGroup = ModuleGroup(listOf("a")) + assertSameElements(parentGroup.modulesInGroup(grouper, false), module1, module2) + assertSameElements(parentGroup.modulesInGroup(grouper, true), module1, module2) + + assertEmpty(parentGroup.childGroups(grouper)) + } + private val grouper: ModuleGrouper get() = getQualifiedNameModuleGrouper(myProject) } diff --git a/platform/platform-tests/testSrc/com/intellij/module/SplitStringByDotsJoiningIncorrectIdentifiersTest.kt b/platform/platform-tests/testSrc/com/intellij/module/SplitStringByDotsJoiningIncorrectIdentifiersTest.kt new file mode 100644 index 000000000000..80cbc5452ab8 --- /dev/null +++ b/platform/platform-tests/testSrc/com/intellij/module/SplitStringByDotsJoiningIncorrectIdentifiersTest.kt @@ -0,0 +1,57 @@ +// Copyright 2000-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 file. +package com.intellij.module + +import com.intellij.openapi.module.impl.splitStringByDotsJoiningIncorrectIdentifiers +import org.junit.Assert.assertEquals +import org.junit.Test + +/** + * @author nik + */ +class SplitStringByDotsJoiningIncorrectIdentifiersTest { + @Test + fun simple() { + splitAndCheck("a", "a") + splitAndCheck("a.b", "a", "b") + splitAndCheck("foo", "foo") + splitAndCheck("foo.bar", "foo", "bar") + } + + @Test + fun `incorrect chars`() { + assertDoNotSplit("a.1") + assertDoNotSplit("a-1.2") + assertDoNotSplit("a. b.") + splitAndCheck("foo.bar-1.2.3", "foo", "bar-1.2.3") + splitAndCheck("a..b", "a.", "b") + } + + @Test + fun `corner cases`() { + assertDoNotSplit("") + assertDoNotSplit(".") + assertDoNotSplit(" ") + assertDoNotSplit("..") + assertDoNotSplit("...") + assertDoNotSplit(".foo") + splitAndCheck("..foo", ".", "foo") + assertDoNotSplit("foo.") + assertDoNotSplit(".foo.") + assertDoNotSplit("foo..") + } + + private fun assertDoNotSplit(s: String) { + splitAndCheck(s, s) + } + + private fun splitAndCheck(s: String, vararg expected: String) { + val (list, last) = splitStringByDotsJoiningIncorrectIdentifiers(s) + if (list.isNotEmpty()) { + assertEquals(list.last(), last) + } + else { + assertEquals("", last) + } + assertEquals(expected.toList(), list) + } +} \ No newline at end of file diff --git a/platform/projectModel-impl/src/com/intellij/openapi/module/impl/moduleGroupers.kt b/platform/projectModel-impl/src/com/intellij/openapi/module/impl/moduleGroupers.kt index 188e111a6155..c28d94165ae1 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/module/impl/moduleGroupers.kt +++ b/platform/projectModel-impl/src/com/intellij/openapi/module/impl/moduleGroupers.kt @@ -3,7 +3,7 @@ package com.intellij.openapi.module.impl import com.intellij.openapi.module.* import com.intellij.openapi.project.Project -import com.intellij.openapi.util.text.StringUtil +import org.jetbrains.annotations.TestOnly import java.util.* /** @@ -26,19 +26,17 @@ private abstract class ModuleGrouperBase(protected val project: Project, protect } private class QualifiedNameGrouper(project: Project, model: ModifiableModuleModel?) : ModuleGrouperBase(project, model) { - override fun getGroupPath(module: Module): List { - return getGroupPathByModuleName(getModuleName(module)) - } + override fun getGroupPath(module: Module) = getGroupPathByModuleName(getModuleName(module)) override fun getGroupPath(description: ModuleDescription) = getGroupPathByModuleName(description.name) - override fun getShortenedNameByFullModuleName(name: String) = StringUtil.getShortName(name) + override fun getShortenedNameByFullModuleName(name: String) = name.substringAfterLastDotNotFollowedByIncorrectChar() - override fun getGroupPathByModuleName(name: String) = name.split('.').dropLast(1) + override fun getGroupPathByModuleName(name: String) = name.splitByDotsJoiningIncorrectIdentifiers().dropLast(1) - override fun getModuleAsGroupPath(module: Module) = getModuleName(module).split('.') + override fun getModuleAsGroupPath(module: Module) = getModuleName(module).splitByDotsJoiningIncorrectIdentifiers() - override fun getModuleAsGroupPath(description: ModuleDescription) = description.name.split('.') + override fun getModuleAsGroupPath(description: ModuleDescription) = description.name.splitByDotsJoiningIncorrectIdentifiers() } private class ExplicitModuleGrouper(project: Project, model: ModifiableModuleModel?): ModuleGrouperBase(project, model) { @@ -61,3 +59,43 @@ private class ExplicitModuleGrouper(project: Project, model: ModifiableModuleMod override fun getModuleAsGroupPath(description: ModuleDescription) = null } + +/** + * Split by dots where it doesn't lead to incorrect identifiers (i.e. "a.b-1.1" will be split to "a" and "b-1.1", not to "a", "b-1" and "1") + */ +private fun String.splitByDotsJoiningIncorrectIdentifiers(): List { + var start = 0 + var next = 1 + val names = ArrayList() + while (next < length) { + val end = indexOf('.', next) + if (end == -1 || end == length - 1) { + break + } + next = end + 1 + if (this[end + 1].isJavaIdentifierStart()) { + names.add(substring(start, end)) + start = end + 1 + } + } + names.add(substring(start)) + return names +} + +/** + * Returns the same value as `splitByDotsNotFollowedByIncorrectChars().last()` but works more efficiently + */ +private fun String.substringAfterLastDotNotFollowedByIncorrectChar(): String { + if (length <= 1) return this + var i = lastIndexOf('.', length - 2) + while (i != -1 && !this[i + 1].isJavaIdentifierStart()) { + i = lastIndexOf('.', i-1) + } + if (i <= 0) return this + return substring(i+1) +} + +@TestOnly +public fun splitStringByDotsJoiningIncorrectIdentifiers(string: String): Pair, String> { + return Pair(string.splitByDotsJoiningIncorrectIdentifiers(), string.substringAfterLastDotNotFollowedByIncorrectChar()) +} \ No newline at end of file