diff --git a/platform/lang-api/src/com/intellij/execution/target/TargetEnvironmentPaths.kt b/platform/lang-api/src/com/intellij/execution/target/TargetEnvironmentPaths.kt index b3c95f3b741b..8337bb6f3b89 100644 --- a/platform/lang-api/src/com/intellij/execution/target/TargetEnvironmentPaths.kt +++ b/platform/lang-api/src/com/intellij/execution/target/TargetEnvironmentPaths.kt @@ -54,8 +54,26 @@ private fun findPathVariants(mappings: Iterable, } internal fun joinTargetPaths(basePath: String, relativePath: String, fileSeparator: Char): String { - val resultCanonicalPath = FileUtil.toCanonicalPath("$basePath$fileSeparator$relativePath", fileSeparator) - // The method `FileUtil.toCanonicalPath()` returns the path with '/' no matter what `fileSeparator` is passed but let's make the result - // system-dependent - return FileUtil.toSystemDependentName(resultCanonicalPath, fileSeparator) -} \ No newline at end of file + val normalizedBasePathForJoining = basePath + .normalizeFileSeparatorCharacter(fileSeparator) + .removeRepetitiveFileSeparators(fileSeparator) + .ensureEndsWithFileSeparator(fileSeparator) + val normalizedRelativePath = relativePath + .normalizeFileSeparatorCharacter(fileSeparator) + .removeRepetitiveFileSeparators(fileSeparator) + .normalizeRelativePath(fileSeparator) + return "$normalizedBasePathForJoining$normalizedRelativePath" +} + +private fun String.normalizeFileSeparatorCharacter(fileSeparator: Char): String = if (fileSeparator == '\\') replace('/', fileSeparator) else this + +private fun String.removeRepetitiveFileSeparators(fileSeparator: Char): String = replace("$fileSeparator$fileSeparator", fileSeparator.toString()) + +private fun String.normalizeRelativePath(fileSeparator: Char): String = + when { + length == 1 && this[0] == '.' -> "" + startsWith(prefix = ".$fileSeparator") -> substring(startIndex = 2) + else -> this + }.removeSuffix(fileSeparator.toString()) + +private fun String.ensureEndsWithFileSeparator(fileSeparator: Char): String = if (endsWith(fileSeparator)) this else "$this$fileSeparator" \ No newline at end of file diff --git a/platform/lang-api/testSources/com/intellij/execution/target/TargetEnvironmentPathsJoinFunctionTest.kt b/platform/lang-api/testSources/com/intellij/execution/target/TargetEnvironmentPathsJoinFunctionTest.kt index 82f19394fa8d..86758d7dba99 100644 --- a/platform/lang-api/testSources/com/intellij/execution/target/TargetEnvironmentPathsJoinFunctionTest.kt +++ b/platform/lang-api/testSources/com/intellij/execution/target/TargetEnvironmentPathsJoinFunctionTest.kt @@ -27,18 +27,24 @@ class TargetEnvironmentPathsJoinFunctionTest(private val basePath: String, arrayOf("/foo/", "bar/", '/', "/foo/bar"), arrayOf("/", ".", '/', "/"), - arrayOf("/", "..", '/', "/"), arrayOf("C:\\", ".", '\\', "C:\\"), arrayOf("C:\\\\", ".", '\\', "C:\\"), - arrayOf("C:\\", "..", '\\', "C:\\"), - arrayOf("C:\\\\", "..", '\\', "C:\\"), arrayOf("C:\\", "foo", '\\', "C:\\foo"), arrayOf("C:\\", "foo\\", '\\', "C:\\foo"), + arrayOf("C:/", ".", '\\', "C:\\"), + arrayOf("C://", ".", '\\', "C:\\"), + arrayOf("C:/", "foo", '\\', "C:\\foo"), + arrayOf("C:", "foo/", '\\', "C:\\foo"), + arrayOf("C:\\", "Directory With Spaces\\", '\\', "C:\\Directory With Spaces"), arrayOf("C:\\Directory With Spaces", "File With Spaces.txt", '\\', "C:\\Directory With Spaces\\File With Spaces.txt"), - arrayOf("C:\\Directory With Spaces", "Subdirectory With Spaces\\", '\\', "C:\\Directory With Spaces\\Subdirectory With Spaces") + arrayOf("C:\\Directory With Spaces", "Subdirectory With Spaces\\", '\\', "C:\\Directory With Spaces\\Subdirectory With Spaces"), + + arrayOf("C:/", "Directory With Spaces/", '\\', "C:\\Directory With Spaces"), + arrayOf("C:/Directory With Spaces", "File With Spaces.txt", '\\', "C:\\Directory With Spaces\\File With Spaces.txt"), + arrayOf("C:/Directory With Spaces", "Subdirectory With Spaces/", '\\', "C:\\Directory With Spaces\\Subdirectory With Spaces"), ) } } \ No newline at end of file