From 7c430f18fd077a090c4c979287001ad6131a0f0c Mon Sep 17 00:00:00 2001 From: paul-dingemans Date: Sat, 24 Sep 2022 19:09:23 +0200 Subject: [PATCH 01/10] Add API for retrieving the list of files that will be accessed when linting or formatting a given path Closes #1446 --- CHANGELOG.md | 8 + .../com/pinterest/ktlint/core/KtLint.kt | 11 ++ .../core/internal/EditorConfigFinder.kt | 85 +++++++++ .../internal/ThreadSafeEditorConfigCache.kt | 9 + .../core/internal/EditorConfigFinderTest.kt | 168 ++++++++++++++++++ 5 files changed, 281 insertions(+) create mode 100644 ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinder.kt create mode 100644 ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinderTest.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index f48c25d863..1b85d70447 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,14 @@ This project adheres to [Semantic Versioning](https://semver.org/). ### API Changes & RuleSet providers +#### Retrieve input files + +The list of files which will be accessed by KtLint when linting or formatting a given path can now be retrieved with the new API `KtLint.getInputPaths(path: Path): List`. Currently, the `.editorconfig` files are the only files which will be accessed during linting or formatting in addition to the source files itself. + +This API can be called with either a file or a directory. It's intended usage is that it is called once with a directory path before actually linting or formatting files. When called with a directory path, all `.editorconfig` in the directory or any of its subdirectories (except hidden directories) are returned. In case the given directory does not contain an `.editorconfig` file or if it does not contain the `root=true` setting, the parent directories are scanned as well until a root `.editorconfig` file is found. + +Calling this API with a file path results in the `.editorconfig` files that will be accessed when processing that specific file. In case the directory in which the file resides does not contain an `.editorconfig` file or if it does not contain the `root=true` setting, the parent directories are scanned until a root `.editorconfig` file is found. + ### Added ### Fixed diff --git a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/KtLint.kt b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/KtLint.kt index f1c349e1c5..121db5ad37 100644 --- a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/KtLint.kt +++ b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/KtLint.kt @@ -8,6 +8,7 @@ import com.pinterest.ktlint.core.api.EditorConfigOverride import com.pinterest.ktlint.core.api.EditorConfigOverride.Companion.emptyEditorConfigOverride import com.pinterest.ktlint.core.api.EditorConfigProperties import com.pinterest.ktlint.core.api.UsesEditorConfigProperties +import com.pinterest.ktlint.core.internal.EditorConfigFinder import com.pinterest.ktlint.core.internal.EditorConfigGenerator import com.pinterest.ktlint.core.internal.EditorConfigLoader import com.pinterest.ktlint.core.internal.RuleExecutionContext @@ -21,6 +22,7 @@ import java.nio.file.FileSystems import java.nio.file.Path import java.nio.file.Paths import java.util.Locale +import kotlin.io.path.isDirectory import org.ec4j.core.Resource import org.ec4j.core.model.PropertyType import org.jetbrains.kotlin.com.intellij.lang.ASTNode @@ -340,6 +342,15 @@ public object KtLint { ) } + /** + * Get the list of files which will be accessed by KtLint when linting or formatting the given file or directory. + */ + public fun getInputPaths(path: Path): List = + EditorConfigFinder().findEditorConfigs(path) + + listOfNotNull( + path.takeIf { !it.isDirectory() }, + ) + /** * Generates Kotlin `.editorconfig` file section content based on [ExperimentalParams]. * diff --git a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinder.kt b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinder.kt new file mode 100644 index 0000000000..c029bad115 --- /dev/null +++ b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinder.kt @@ -0,0 +1,85 @@ +package com.pinterest.ktlint.core.internal + +import com.pinterest.ktlint.core.initKtLintKLogger +import com.pinterest.ktlint.core.internal.ThreadSafeEditorConfigCache.Companion.threadSafeEditorConfigCache +import java.nio.charset.StandardCharsets +import java.nio.file.FileVisitResult +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.SimpleFileVisitor +import java.nio.file.attribute.BasicFileAttributes +import kotlin.io.path.isDirectory +import mu.KotlinLogging +import org.ec4j.core.Resource +import org.ec4j.core.ResourcePropertiesService +import org.ec4j.core.model.Version +import org.jetbrains.kotlin.konan.file.File + +private val logger = KotlinLogging.logger {}.initKtLintKLogger() + +internal class EditorConfigFinder { + /** + * Finds all relevant ".editorconfig" files for the given path. + */ + fun findEditorConfigs(path: Path): List { + val result = mutableListOf() + val normalizedPath = path.normalize().toAbsolutePath() + if (path.isDirectory()) { + result += findEditorConfigsInSubDirectories(normalizedPath) + } + result += findEditorConfigsInParentDirectories(normalizedPath) + return result.toList() + } + + private fun findEditorConfigsInSubDirectories(path: Path): List { + val result = mutableListOf() + + Files.walkFileTree( + path, + object : SimpleFileVisitor() { + override fun visitFile( + filePath: Path, + fileAttrs: BasicFileAttributes, + ): FileVisitResult { + if (filePath.File().name == ".editorconfig") { + logger.trace { "- File: $filePath: add to list of accessed files" } + result.add(filePath) + } + return FileVisitResult.CONTINUE + } + + override fun preVisitDirectory( + dirPath: Path, + dirAttr: BasicFileAttributes, + ): FileVisitResult { + return if (Files.isHidden(dirPath)) { + logger.trace { "- Dir: $dirPath: Ignore" } + FileVisitResult.SKIP_SUBTREE + } else { + logger.trace { "- Dir: $dirPath: Traverse" } + FileVisitResult.CONTINUE + } + } + }, + ) + + return result.toList() + } + + private fun findEditorConfigsInParentDirectories(path: Path): List { + // The logic to load parental ".editorconfig" files resides in the ec4j library. This library however uses a + // cache provided by KtLint. As of this the list of parental ".editorconfig" files can be extracted from the + // cache. + createLoaderService().queryProperties(path.resource()) + return threadSafeEditorConfigCache.getPaths() + } + + private fun Path?.resource() = + Resource.Resources.ofPath(this, StandardCharsets.UTF_8) + + private fun createLoaderService() = + ResourcePropertiesService.builder() + .cache(threadSafeEditorConfigCache) + .loader(org.ec4j.core.EditorConfigLoader.of(Version.CURRENT)) + .build() +} diff --git a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/ThreadSafeEditorConfigCache.kt b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/ThreadSafeEditorConfigCache.kt index 1eeb2c9a37..67ca34672c 100644 --- a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/ThreadSafeEditorConfigCache.kt +++ b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/ThreadSafeEditorConfigCache.kt @@ -1,6 +1,7 @@ package com.pinterest.ktlint.core.internal import com.pinterest.ktlint.core.initKtLintKLogger +import java.nio.file.Paths import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.read import kotlin.concurrent.write @@ -72,6 +73,14 @@ internal class ThreadSafeEditorConfigCache : Cache { }.clear() } + /** + * Get the paths of files stored in the cache. + */ + fun getPaths() = + inMemoryMap + .keys + .map { Paths.get(it.path.toString()) } + private data class CacheValue( val editorConfigLoader: EditorConfigLoader, val editConfig: EditorConfig, diff --git a/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinderTest.kt b/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinderTest.kt new file mode 100644 index 0000000000..b1c4bf378e --- /dev/null +++ b/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinderTest.kt @@ -0,0 +1,168 @@ +package com.pinterest.ktlint.core.internal + +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.Paths +import kotlin.io.path.absolutePathString +import kotlin.io.path.writeText +import org.assertj.core.api.Assertions.assertThat +import org.jetbrains.kotlin.konan.file.File +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.io.TempDir + +class EditorConfigFinderTest { + @TempDir + private lateinit var tempDir: Path + + @Nested + inner class FindByFile { + @Test + fun `Given a kotlin file in a subdirectory and a root-editorconfig file in the same directory then get the path of that editorconfig file`() { + val someSubDir = "some-project/src/main/kotlin" + createFile("$someSubDir/.editorconfig", "root=true") + val kotlinFilePath = createFile("$someSubDir/test.kt", "val foo = 42") + + val actual = + EditorConfigFinder() + .findEditorConfigs(kotlinFilePath) + .map { it.toPathStringWithoutTempDirPrefix() } + + assertThat(actual).contains("$someSubDir/.editorconfig") + } + + @Test + fun `Given a kotlin file in a subdirectory and a root-editorconfig file in a parent directory then get the path of that parent editorconfig file`() { + val someProjectDirectory = "some-project" + createFile("$someProjectDirectory/.editorconfig", "root=true") + val kotlinFilePath = createFile("$someProjectDirectory/src/main/kotlin/test.kt", "val foo = 42") + + val actual = + EditorConfigFinder() + .findEditorConfigs(kotlinFilePath) + .map { it.toPathStringWithoutTempDirPrefix() } + + assertThat(actual).contains("$someProjectDirectory/.editorconfig") + } + + @Test + fun `Given a kotlin file in a subdirectory and a non-root-editorconfig file in that same directory and a root-editorconfig file in a parent directory then get the paths of both editorconfig files`() { + val someProjectDirectory = "some-project" + createFile("$someProjectDirectory/.editorconfig", "root=true") + createFile("$someProjectDirectory/src/main/.editorconfig", "root=false") + val kotlinFilePath = createFile("$someProjectDirectory/src/main/kotlin/test.kt", "val foo = 42") + + val actual = + EditorConfigFinder() + .findEditorConfigs(kotlinFilePath) + .map { it.toPathStringWithoutTempDirPrefix() } + + assertThat(actual).contains( + "$someProjectDirectory/.editorconfig", + "$someProjectDirectory/src/main/.editorconfig", + ) + } + + @Test + fun `Given a kotlin file in a subdirectory and a root-editorconfig file in the parent directory and another root-editorconfig file in a great-parent directory then get the paths of editorconfig files excluding root-editorconfig once the first one is found`() { + val someProjectDirectory = "some-project" + createFile("$someProjectDirectory/src/main/.editorconfig", "root=false") + createFile("$someProjectDirectory/src/.editorconfig", "root=true") + createFile("$someProjectDirectory/.editorconfig", "root=true") + val kotlinFilePath = createFile("$someProjectDirectory/src/main/kotlin/test.kt", "val foo = 42") + + val actual = + EditorConfigFinder() + .findEditorConfigs(kotlinFilePath) + .map { it.toPathStringWithoutTempDirPrefix() } + + assertThat(actual) + .contains( + "$someProjectDirectory/src/main/.editorconfig", + "$someProjectDirectory/src/.editorconfig", + ).doesNotContain( + "$someProjectDirectory/.editorconfig", + ) + } + } + + @Nested + inner class FindByDirectory { + @Test + fun `Given a directory containing a root-editorconfig file and a subdirectory containing a editorconfig file then get the paths of both editorconfig files`() { + val someDirectory = "some-project" + createFile("$someDirectory/.editorconfig", "root=true") + createFile("$someDirectory/src/main/kotlin/.editorconfig", "some-property=some-value") + + val actual = + EditorConfigFinder() + .findEditorConfigs(toTempDirPath(someDirectory)) + .map { it.toPathStringWithoutTempDirPrefix() } + + assertThat(actual).contains( + "$someDirectory/.editorconfig", + "$someDirectory/src/main/kotlin/.editorconfig", + ) + } + + @Test + fun `Given a subdirectory containing an editorconfig file and a sibling subdirectory contain a editorconfig file in a parent directory then get the path of all editorconfig file except of the sibling subdirectory`() { + val someProjectDirectory = "some-project" + createFile("$someProjectDirectory/.editorconfig", "root=true") + createFile("$someProjectDirectory/src/main/kotlin/.editorconfig", "some-property=some-value") + createFile("$someProjectDirectory/src/test/kotlin/.editorconfig", "some-property=some-value") + + val actual = + EditorConfigFinder() + .findEditorConfigs(toTempDirPath("$someProjectDirectory/src/main/kotlin")) + .map { it.toPathStringWithoutTempDirPrefix() } + + assertThat(actual) + .contains( + "$someProjectDirectory/.editorconfig", + "$someProjectDirectory/src/main/kotlin/.editorconfig", + ).doesNotContain( + "$someProjectDirectory/src/test/kotlin/.editorconfig", + ) + } + + @Test + fun `Given a directory containing an editorconfig file and multiple subdirectores containing a editorconfig file then get the path of all editorconfig files`() { + val someProjectDirectory = "some-project" + createFile("$someProjectDirectory/.editorconfig", "root=true") + createFile("$someProjectDirectory/src/main/kotlin/.editorconfig", "some-property=some-value") + createFile("$someProjectDirectory/src/test/kotlin/.editorconfig", "some-property=some-value") + + val actual = + EditorConfigFinder() + .findEditorConfigs(toTempDirPath("$someProjectDirectory")) + .map { it.toPathStringWithoutTempDirPrefix() } + + assertThat(actual).contains( + "$someProjectDirectory/.editorconfig", + "$someProjectDirectory/src/main/kotlin/.editorconfig", + "$someProjectDirectory/src/test/kotlin/.editorconfig", + ) + } + } + + private fun createFile(fileName: String, content: String): Path { + val dirPath = fileName.substringBeforeLast("/", "") + Files.createDirectories(toTempDirPath(dirPath)) + return Files + .createFile(toTempDirPath(fileName)) + .also { it.writeText(content) } + } + + private fun toTempDirPath(subPath: String): Path = + tempDir + .absolutePathString() + .plus(File.separator) + .plus(subPath) + .let { Paths.get(it) } + + private fun Path.toPathStringWithoutTempDirPrefix() = + absolutePathString() + .removePrefix(tempDir.absolutePathString()) + .removePrefix(File.separator) +} From 78aa4c3c23653086e7ded610f662ab0ddbc7159e Mon Sep 17 00:00:00 2001 From: paul-dingemans Date: Sun, 25 Sep 2022 09:58:26 +0200 Subject: [PATCH 02/10] Fix failing tests on WindowsOS --- .../com/pinterest/ktlint/core/internal/EditorConfigFinderTest.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinderTest.kt b/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinderTest.kt index b1c4bf378e..6b9516bbb4 100644 --- a/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinderTest.kt +++ b/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinderTest.kt @@ -165,4 +165,5 @@ class EditorConfigFinderTest { absolutePathString() .removePrefix(tempDir.absolutePathString()) .removePrefix(File.separator) + .replace(tempDir.fileSystem.separator, "/") } From fc13ea1caae1cdaf96f54a80f10ad276c023c82f Mon Sep 17 00:00:00 2001 From: paul-dingemans Date: Sun, 25 Sep 2022 16:28:29 +0200 Subject: [PATCH 03/10] Fix failing tests on WindowsOS - inject tempDir in each method and filter results to files in the tempdir only as (on WindowsOS) it seems that the result may also contain files created for other tests --- .../core/internal/EditorConfigFinderTest.kt | 120 +++++++++++------- 1 file changed, 71 insertions(+), 49 deletions(-) diff --git a/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinderTest.kt b/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinderTest.kt index 6b9516bbb4..5be33d1d7f 100644 --- a/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinderTest.kt +++ b/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinderTest.kt @@ -12,50 +12,56 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.io.TempDir class EditorConfigFinderTest { - @TempDir - private lateinit var tempDir: Path - @Nested inner class FindByFile { @Test - fun `Given a kotlin file in a subdirectory and a root-editorconfig file in the same directory then get the path of that editorconfig file`() { + fun `Given a kotlin file in a subdirectory and a root-editorconfig file in the same directory then get the path of that editorconfig file`( + @TempDir + tempDir: Path, + ) { val someSubDir = "some-project/src/main/kotlin" - createFile("$someSubDir/.editorconfig", "root=true") - val kotlinFilePath = createFile("$someSubDir/test.kt", "val foo = 42") + tempDir.createFile("$someSubDir/.editorconfig", "root=true") + val kotlinFilePath = tempDir.createFile("$someSubDir/test.kt", "val foo = 42") val actual = EditorConfigFinder() .findEditorConfigs(kotlinFilePath) - .map { it.toPathStringWithoutTempDirPrefix() } + .mapNotNull { it.toRelativePathStringIn(tempDir) } assertThat(actual).contains("$someSubDir/.editorconfig") } @Test - fun `Given a kotlin file in a subdirectory and a root-editorconfig file in a parent directory then get the path of that parent editorconfig file`() { + fun `Given a kotlin file in a subdirectory and a root-editorconfig file in a parent directory then get the path of that parent editorconfig file`( + @TempDir + tempDir: Path, + ) { val someProjectDirectory = "some-project" - createFile("$someProjectDirectory/.editorconfig", "root=true") - val kotlinFilePath = createFile("$someProjectDirectory/src/main/kotlin/test.kt", "val foo = 42") + tempDir.createFile("$someProjectDirectory/.editorconfig", "root=true") + val kotlinFilePath = tempDir.createFile("$someProjectDirectory/src/main/kotlin/test.kt", "val foo = 42") val actual = EditorConfigFinder() .findEditorConfigs(kotlinFilePath) - .map { it.toPathStringWithoutTempDirPrefix() } + .mapNotNull { it.toRelativePathStringIn(tempDir) } assertThat(actual).contains("$someProjectDirectory/.editorconfig") } @Test - fun `Given a kotlin file in a subdirectory and a non-root-editorconfig file in that same directory and a root-editorconfig file in a parent directory then get the paths of both editorconfig files`() { + fun `Given a kotlin file in a subdirectory and a non-root-editorconfig file in that same directory and a root-editorconfig file in a parent directory then get the paths of both editorconfig files`( + @TempDir + tempDir: Path, + ) { val someProjectDirectory = "some-project" - createFile("$someProjectDirectory/.editorconfig", "root=true") - createFile("$someProjectDirectory/src/main/.editorconfig", "root=false") - val kotlinFilePath = createFile("$someProjectDirectory/src/main/kotlin/test.kt", "val foo = 42") + tempDir.createFile("$someProjectDirectory/.editorconfig", "root=true") + tempDir.createFile("$someProjectDirectory/src/main/.editorconfig", "root=false") + val kotlinFilePath = tempDir.createFile("$someProjectDirectory/src/main/kotlin/test.kt", "val foo = 42") val actual = EditorConfigFinder() .findEditorConfigs(kotlinFilePath) - .map { it.toPathStringWithoutTempDirPrefix() } + .mapNotNull { it.toRelativePathStringIn(tempDir) } assertThat(actual).contains( "$someProjectDirectory/.editorconfig", @@ -64,17 +70,20 @@ class EditorConfigFinderTest { } @Test - fun `Given a kotlin file in a subdirectory and a root-editorconfig file in the parent directory and another root-editorconfig file in a great-parent directory then get the paths of editorconfig files excluding root-editorconfig once the first one is found`() { + fun `Given a kotlin file in a subdirectory and a root-editorconfig file in the parent directory and another root-editorconfig file in a great-parent directory then get the paths of editorconfig files excluding root-editorconfig once the first one is found`( + @TempDir + tempDir: Path, + ) { val someProjectDirectory = "some-project" - createFile("$someProjectDirectory/src/main/.editorconfig", "root=false") - createFile("$someProjectDirectory/src/.editorconfig", "root=true") - createFile("$someProjectDirectory/.editorconfig", "root=true") - val kotlinFilePath = createFile("$someProjectDirectory/src/main/kotlin/test.kt", "val foo = 42") + tempDir.createFile("$someProjectDirectory/src/main/.editorconfig", "root=false") + tempDir.createFile("$someProjectDirectory/src/.editorconfig", "root=true") + tempDir.createFile("$someProjectDirectory/.editorconfig", "root=true") + val kotlinFilePath = tempDir.createFile("$someProjectDirectory/src/main/kotlin/test.kt", "val foo = 42") val actual = EditorConfigFinder() .findEditorConfigs(kotlinFilePath) - .map { it.toPathStringWithoutTempDirPrefix() } + .mapNotNull { it.toRelativePathStringIn(tempDir) } assertThat(actual) .contains( @@ -89,15 +98,18 @@ class EditorConfigFinderTest { @Nested inner class FindByDirectory { @Test - fun `Given a directory containing a root-editorconfig file and a subdirectory containing a editorconfig file then get the paths of both editorconfig files`() { + fun `Given a directory containing a root-editorconfig file and a subdirectory containing a editorconfig file then get the paths of both editorconfig files`( + @TempDir + tempDir: Path, + ) { val someDirectory = "some-project" - createFile("$someDirectory/.editorconfig", "root=true") - createFile("$someDirectory/src/main/kotlin/.editorconfig", "some-property=some-value") + tempDir.createFile("$someDirectory/.editorconfig", "root=true") + tempDir.createFile("$someDirectory/src/main/kotlin/.editorconfig", "some-property=some-value") val actual = EditorConfigFinder() - .findEditorConfigs(toTempDirPath(someDirectory)) - .map { it.toPathStringWithoutTempDirPrefix() } + .findEditorConfigs(tempDir.plus(someDirectory)) + .mapNotNull { it.toRelativePathStringIn(tempDir) } assertThat(actual).contains( "$someDirectory/.editorconfig", @@ -106,16 +118,19 @@ class EditorConfigFinderTest { } @Test - fun `Given a subdirectory containing an editorconfig file and a sibling subdirectory contain a editorconfig file in a parent directory then get the path of all editorconfig file except of the sibling subdirectory`() { + fun `Given a subdirectory containing an editorconfig file and a sibling subdirectory contain a editorconfig file in a parent directory then get the path of all editorconfig file except of the sibling subdirectory`( + @TempDir + tempDir: Path, + ) { val someProjectDirectory = "some-project" - createFile("$someProjectDirectory/.editorconfig", "root=true") - createFile("$someProjectDirectory/src/main/kotlin/.editorconfig", "some-property=some-value") - createFile("$someProjectDirectory/src/test/kotlin/.editorconfig", "some-property=some-value") + tempDir.createFile("$someProjectDirectory/.editorconfig", "root=true") + tempDir.createFile("$someProjectDirectory/src/main/kotlin/.editorconfig", "some-property=some-value") + tempDir.createFile("$someProjectDirectory/src/test/kotlin/.editorconfig", "some-property=some-value") val actual = EditorConfigFinder() - .findEditorConfigs(toTempDirPath("$someProjectDirectory/src/main/kotlin")) - .map { it.toPathStringWithoutTempDirPrefix() } + .findEditorConfigs(tempDir.plus("$someProjectDirectory/src/main/kotlin")) + .mapNotNull { it.toRelativePathStringIn(tempDir) } assertThat(actual) .contains( @@ -127,16 +142,19 @@ class EditorConfigFinderTest { } @Test - fun `Given a directory containing an editorconfig file and multiple subdirectores containing a editorconfig file then get the path of all editorconfig files`() { + fun `Given a directory containing an editorconfig file and multiple subdirectores containing a editorconfig file then get the path of all editorconfig files`( + @TempDir + tempDir: Path, + ) { val someProjectDirectory = "some-project" - createFile("$someProjectDirectory/.editorconfig", "root=true") - createFile("$someProjectDirectory/src/main/kotlin/.editorconfig", "some-property=some-value") - createFile("$someProjectDirectory/src/test/kotlin/.editorconfig", "some-property=some-value") + tempDir.createFile("$someProjectDirectory/.editorconfig", "root=true") + tempDir.createFile("$someProjectDirectory/src/main/kotlin/.editorconfig", "some-property=some-value") + tempDir.createFile("$someProjectDirectory/src/test/kotlin/.editorconfig", "some-property=some-value") val actual = EditorConfigFinder() - .findEditorConfigs(toTempDirPath("$someProjectDirectory")) - .map { it.toPathStringWithoutTempDirPrefix() } + .findEditorConfigs(tempDir.plus(someProjectDirectory)) + .mapNotNull { it.toRelativePathStringIn(tempDir) } assertThat(actual).contains( "$someProjectDirectory/.editorconfig", @@ -146,24 +164,28 @@ class EditorConfigFinderTest { } } - private fun createFile(fileName: String, content: String): Path { + private fun Path.createFile(fileName: String, content: String): Path { val dirPath = fileName.substringBeforeLast("/", "") - Files.createDirectories(toTempDirPath(dirPath)) + Files.createDirectories(plus(dirPath)) return Files - .createFile(toTempDirPath(fileName)) + .createFile(plus(fileName)) .also { it.writeText(content) } } - private fun toTempDirPath(subPath: String): Path = - tempDir + private fun Path.plus(subPath: String): Path = + this .absolutePathString() .plus(File.separator) .plus(subPath) .let { Paths.get(it) } - private fun Path.toPathStringWithoutTempDirPrefix() = - absolutePathString() - .removePrefix(tempDir.absolutePathString()) - .removePrefix(File.separator) - .replace(tempDir.fileSystem.separator, "/") + private fun Path.toRelativePathStringIn(tempDir: Path): String? = + this + .takeIf { + // Ignore files created in temp dirs of other tests + it.startsWith(tempDir) + }?.absolutePathString() + ?.replace(tempDir.fileSystem.separator, "/") + ?.removePrefix(tempDir.absolutePathString()) + ?.removePrefix(File.separator) } From d762fac9f6645c4b7c0c897cb148151ad910c828 Mon Sep 17 00:00:00 2001 From: paul-dingemans Date: Sun, 25 Sep 2022 16:47:59 +0200 Subject: [PATCH 04/10] Fix failing tests on WindowsOS --- .../pinterest/ktlint/core/internal/EditorConfigFinderTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinderTest.kt b/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinderTest.kt index 5be33d1d7f..f8ec690b5a 100644 --- a/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinderTest.kt +++ b/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinderTest.kt @@ -185,7 +185,7 @@ class EditorConfigFinderTest { // Ignore files created in temp dirs of other tests it.startsWith(tempDir) }?.absolutePathString() - ?.replace(tempDir.fileSystem.separator, "/") ?.removePrefix(tempDir.absolutePathString()) ?.removePrefix(File.separator) + ?.replace(tempDir.fileSystem.separator, "/") } From 45570b58bf0f546095d1368b9942ffb862baded6 Mon Sep 17 00:00:00 2001 From: paul-dingemans Date: Sun, 25 Sep 2022 18:07:33 +0200 Subject: [PATCH 05/10] Fix failing tests on WindowsOS - use dedicated cache to exclude interference of other tests --- .../ktlint/core/internal/EditorConfigFinder.kt | 9 ++++++--- .../core/internal/EditorConfigFinderTest.kt | 4 ++-- .../ThreadSafeEditorConfigCacheTest.kt | 18 +++++++++--------- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinder.kt b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinder.kt index c029bad115..87fd12f0f2 100644 --- a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinder.kt +++ b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinder.kt @@ -1,7 +1,6 @@ package com.pinterest.ktlint.core.internal import com.pinterest.ktlint.core.initKtLintKLogger -import com.pinterest.ktlint.core.internal.ThreadSafeEditorConfigCache.Companion.threadSafeEditorConfigCache import java.nio.charset.StandardCharsets import java.nio.file.FileVisitResult import java.nio.file.Files @@ -18,6 +17,10 @@ import org.jetbrains.kotlin.konan.file.File private val logger = KotlinLogging.logger {}.initKtLintKLogger() internal class EditorConfigFinder { + // Do not reuse the generic threadSageEditorConfigCache to prevent that results are incorrect due to other calls to + // KtLint that result in changing the cache + private val editorConfigCache = ThreadSafeEditorConfigCache() + /** * Finds all relevant ".editorconfig" files for the given path. */ @@ -71,7 +74,7 @@ internal class EditorConfigFinder { // cache provided by KtLint. As of this the list of parental ".editorconfig" files can be extracted from the // cache. createLoaderService().queryProperties(path.resource()) - return threadSafeEditorConfigCache.getPaths() + return editorConfigCache.getPaths() } private fun Path?.resource() = @@ -79,7 +82,7 @@ internal class EditorConfigFinder { private fun createLoaderService() = ResourcePropertiesService.builder() - .cache(threadSafeEditorConfigCache) + .cache(editorConfigCache) .loader(org.ec4j.core.EditorConfigLoader.of(Version.CURRENT)) .build() } diff --git a/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinderTest.kt b/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinderTest.kt index f8ec690b5a..db006091fd 100644 --- a/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinderTest.kt +++ b/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinderTest.kt @@ -166,9 +166,9 @@ class EditorConfigFinderTest { private fun Path.createFile(fileName: String, content: String): Path { val dirPath = fileName.substringBeforeLast("/", "") - Files.createDirectories(plus(dirPath)) + Files.createDirectories(this.plus(dirPath)) return Files - .createFile(plus(fileName)) + .createFile(this.plus(fileName)) .also { it.writeText(content) } } diff --git a/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/ThreadSafeEditorConfigCacheTest.kt b/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/ThreadSafeEditorConfigCacheTest.kt index 02db3aae07..47a150e6e6 100644 --- a/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/ThreadSafeEditorConfigCacheTest.kt +++ b/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/ThreadSafeEditorConfigCacheTest.kt @@ -71,28 +71,28 @@ class ThreadSafeEditorConfigCacheTest { } @Test - fun `Given that a file is stored in the cache and then the cache is cleared and the file is requested again then the file is to be reloaded`() { + fun `Given that a file is stored in the cache and then file is explicitly reloaded`() { val threadSafeEditorConfigCache = ThreadSafeEditorConfigCache() val editorConfigLoaderFile1 = EditorConfigLoaderMock(EDIT_CONFIG_1) threadSafeEditorConfigCache.get(FILE_1, editorConfigLoaderFile1) - threadSafeEditorConfigCache.clear() - threadSafeEditorConfigCache.get(FILE_1, editorConfigLoaderFile1) - threadSafeEditorConfigCache.get(FILE_1, editorConfigLoaderFile1) + threadSafeEditorConfigCache.reloadIfExists(FILE_1) + threadSafeEditorConfigCache.reloadIfExists(FILE_1) - assertThat(editorConfigLoaderFile1.loadCount).isEqualTo(2) + assertThat(editorConfigLoaderFile1.loadCount).isEqualTo(3) } @Test - fun `Given that a file is stored in the cache and then file is explicitly reloaded`() { + fun `Given that a file is stored in the cache and then the cache is cleared and the file is requested again then the file is to be reloaded`() { val threadSafeEditorConfigCache = ThreadSafeEditorConfigCache() val editorConfigLoaderFile1 = EditorConfigLoaderMock(EDIT_CONFIG_1) threadSafeEditorConfigCache.get(FILE_1, editorConfigLoaderFile1) - threadSafeEditorConfigCache.reloadIfExists(FILE_1) - threadSafeEditorConfigCache.reloadIfExists(FILE_1) + threadSafeEditorConfigCache.clear() + threadSafeEditorConfigCache.get(FILE_1, editorConfigLoaderFile1) + threadSafeEditorConfigCache.get(FILE_1, editorConfigLoaderFile1) - assertThat(editorConfigLoaderFile1.loadCount).isEqualTo(3) + assertThat(editorConfigLoaderFile1.loadCount).isEqualTo(2) } private companion object { From 7d5e63336f7c21ca970ae860c1b1b22e46bd2846 Mon Sep 17 00:00:00 2001 From: paul-dingemans Date: Sun, 25 Sep 2022 20:52:34 +0200 Subject: [PATCH 06/10] Fix failing tests on WindowsOS --- .../pinterest/ktlint/core/internal/EditorConfigFinder.kt | 2 +- .../ktlint/core/internal/EditorConfigFinderTest.kt | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinder.kt b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinder.kt index 87fd12f0f2..33e97736cc 100644 --- a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinder.kt +++ b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinder.kt @@ -17,7 +17,7 @@ import org.jetbrains.kotlin.konan.file.File private val logger = KotlinLogging.logger {}.initKtLintKLogger() internal class EditorConfigFinder { - // Do not reuse the generic threadSageEditorConfigCache to prevent that results are incorrect due to other calls to + // Do not reuse the generic threadSafeEditorConfigCache to prevent that results are incorrect due to other calls to // KtLint that result in changing the cache private val editorConfigCache = ThreadSafeEditorConfigCache() diff --git a/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinderTest.kt b/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinderTest.kt index db006091fd..2113ab42a7 100644 --- a/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinderTest.kt +++ b/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinderTest.kt @@ -181,11 +181,11 @@ class EditorConfigFinderTest { private fun Path.toRelativePathStringIn(tempDir: Path): String? = this + .absolutePathString() .takeIf { // Ignore files created in temp dirs of other tests - it.startsWith(tempDir) - }?.absolutePathString() - ?.removePrefix(tempDir.absolutePathString()) + it.startsWith(tempDir.absolutePathString()) + }?.removePrefix(tempDir.absolutePathString()) ?.removePrefix(File.separator) ?.replace(tempDir.fileSystem.separator, "/") } From 340abdd3e026b31952fdddad92fc83b02f4dfb80 Mon Sep 17 00:00:00 2001 From: paul-dingemans Date: Mon, 26 Sep 2022 16:07:50 +0200 Subject: [PATCH 07/10] Fix failing tests on WindowsOS --- .../core/internal/EditorConfigFinderTest.kt | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinderTest.kt b/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinderTest.kt index 2113ab42a7..f7444c95bf 100644 --- a/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinderTest.kt +++ b/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinderTest.kt @@ -6,7 +6,6 @@ import java.nio.file.Paths import kotlin.io.path.absolutePathString import kotlin.io.path.writeText import org.assertj.core.api.Assertions.assertThat -import org.jetbrains.kotlin.konan.file.File import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import org.junit.jupiter.api.io.TempDir @@ -26,7 +25,7 @@ class EditorConfigFinderTest { val actual = EditorConfigFinder() .findEditorConfigs(kotlinFilePath) - .mapNotNull { it.toRelativePathStringIn(tempDir) } + .mapNotNull { it.removePrefixOrNull(tempDir) } assertThat(actual).contains("$someSubDir/.editorconfig") } @@ -43,7 +42,7 @@ class EditorConfigFinderTest { val actual = EditorConfigFinder() .findEditorConfigs(kotlinFilePath) - .mapNotNull { it.toRelativePathStringIn(tempDir) } + .mapNotNull { it.removePrefixOrNull(tempDir) } assertThat(actual).contains("$someProjectDirectory/.editorconfig") } @@ -61,7 +60,7 @@ class EditorConfigFinderTest { val actual = EditorConfigFinder() .findEditorConfigs(kotlinFilePath) - .mapNotNull { it.toRelativePathStringIn(tempDir) } + .mapNotNull { it.removePrefixOrNull(tempDir) } assertThat(actual).contains( "$someProjectDirectory/.editorconfig", @@ -83,7 +82,7 @@ class EditorConfigFinderTest { val actual = EditorConfigFinder() .findEditorConfigs(kotlinFilePath) - .mapNotNull { it.toRelativePathStringIn(tempDir) } + .mapNotNull { it.removePrefixOrNull(tempDir) } assertThat(actual) .contains( @@ -109,7 +108,7 @@ class EditorConfigFinderTest { val actual = EditorConfigFinder() .findEditorConfigs(tempDir.plus(someDirectory)) - .mapNotNull { it.toRelativePathStringIn(tempDir) } + .mapNotNull { it.removePrefixOrNull(tempDir) } assertThat(actual).contains( "$someDirectory/.editorconfig", @@ -130,7 +129,7 @@ class EditorConfigFinderTest { val actual = EditorConfigFinder() .findEditorConfigs(tempDir.plus("$someProjectDirectory/src/main/kotlin")) - .mapNotNull { it.toRelativePathStringIn(tempDir) } + .mapNotNull { it.removePrefixOrNull(tempDir) } assertThat(actual) .contains( @@ -154,7 +153,7 @@ class EditorConfigFinderTest { val actual = EditorConfigFinder() .findEditorConfigs(tempDir.plus(someProjectDirectory)) - .mapNotNull { it.toRelativePathStringIn(tempDir) } + .mapNotNull { it.removePrefixOrNull(tempDir) } assertThat(actual).contains( "$someProjectDirectory/.editorconfig", @@ -175,17 +174,17 @@ class EditorConfigFinderTest { private fun Path.plus(subPath: String): Path = this .absolutePathString() - .plus(File.separator) + .plus(this.fileSystem.separator) .plus(subPath) .let { Paths.get(it) } - private fun Path.toRelativePathStringIn(tempDir: Path): String? = + private fun Path.removePrefixOrNull(tempDir: Path): String? = this .absolutePathString() .takeIf { // Ignore files created in temp dirs of other tests it.startsWith(tempDir.absolutePathString()) }?.removePrefix(tempDir.absolutePathString()) - ?.removePrefix(File.separator) + ?.removePrefix(tempDir.fileSystem.separator) ?.replace(tempDir.fileSystem.separator, "/") } From cd9ec99c8a31c9e1a90c09d95ed03444e713a840 Mon Sep 17 00:00:00 2001 From: paul-dingemans Date: Mon, 26 Sep 2022 16:44:48 +0200 Subject: [PATCH 08/10] Add some dummy tests to find out what is happening on WindowsOS --- .../core/internal/EditorConfigFinderTest.kt | 171 ++++++++++++++++++ 1 file changed, 171 insertions(+) diff --git a/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinderTest.kt b/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinderTest.kt index f7444c95bf..340a71b7d1 100644 --- a/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinderTest.kt +++ b/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinderTest.kt @@ -8,6 +8,9 @@ import kotlin.io.path.writeText import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertAll +import org.junit.jupiter.api.condition.EnabledOnOs +import org.junit.jupiter.api.condition.OS import org.junit.jupiter.api.io.TempDir class EditorConfigFinderTest { @@ -92,6 +95,174 @@ class EditorConfigFinderTest { "$someProjectDirectory/.editorconfig", ) } + + @Test + fun `What is happening here 0`( + @TempDir + tempDir: Path, + ) { + val someProjectDirectory = "some-project" + tempDir.createFile("$someProjectDirectory/src/main/.editorconfig", "root=false") + tempDir.createFile("$someProjectDirectory/src/.editorconfig", "root=true") + tempDir.createFile("$someProjectDirectory/.editorconfig", "root=true") + val kotlinFilePath = tempDir.createFile("$someProjectDirectory/src/main/kotlin/test.kt", "val foo = 42") + + val actual = + EditorConfigFinder() + .findEditorConfigs(kotlinFilePath) + + assertThat(actual) + .contains( + tempDir.plus("$someProjectDirectory/src/main/.editorconfig"), + tempDir.plus("$someProjectDirectory/src/.editorconfig"), + ).doesNotContain( + tempDir.plus("$someProjectDirectory/.editorconfig"), + ) + } + + @EnabledOnOs(OS.WINDOWS) + @Test + fun `What is happening here 1`( + @TempDir + tempDir: Path, + ) { + val someProjectDirectory = "some-project" + tempDir.createFile("$someProjectDirectory/src/main/.editorconfig", "root=false") + tempDir.createFile("$someProjectDirectory/src/.editorconfig", "root=true") + tempDir.createFile("$someProjectDirectory/.editorconfig", "root=true") + val kotlinFilePath = tempDir.createFile("$someProjectDirectory/src/main/kotlin/test.kt", "val foo = 42") + + val actual = + EditorConfigFinder() + .findEditorConfigs(kotlinFilePath) + .map { it.absolutePathString() } + + assertThat(actual) + .contains( + "$someProjectDirectory/src/main/.editorconfig", + "$someProjectDirectory/src/.editorconfig", + ).doesNotContain( + "$someProjectDirectory/.editorconfig", + ) + } + + @EnabledOnOs(OS.WINDOWS) + @Test + fun `What is happening here 2`( + @TempDir + tempDir: Path, + ) { + val someProjectDirectory = "some-project" + tempDir.createFile("$someProjectDirectory/src/main/.editorconfig", "root=false") + tempDir.createFile("$someProjectDirectory/src/.editorconfig", "root=true") + tempDir.createFile("$someProjectDirectory/.editorconfig", "root=true") + val kotlinFilePath = tempDir.createFile("$someProjectDirectory/src/main/kotlin/test.kt", "val foo = 42") + + val actual = + EditorConfigFinder() + .findEditorConfigs(kotlinFilePath) + .mapNotNull { + it + .absolutePathString() + .takeIf { + // Ignore files created in temp dirs of other tests + it.startsWith(tempDir.absolutePathString()) + }?.removePrefix(tempDir.absolutePathString()) + ?.removePrefix(tempDir.fileSystem.separator) + ?.replace(tempDir.fileSystem.separator, "/") + } + + assertAll( + { + assertThat(tempDir.absolutePathString()).isEqualTo("something-else") + }, + { + assertThat(tempDir.fileSystem.separator) + .withFailMessage("Path separator") + .isEqualTo("something-else") + }, + { + assertThat(actual) + .withFailMessage("Actual result") + .contains( + "$someProjectDirectory/src/main/.editorconfig", + "$someProjectDirectory/src/.editorconfig", + ).doesNotContain( + "$someProjectDirectory/.editorconfig", + ) + }, + ) + } + + @EnabledOnOs(OS.WINDOWS) + @Test + fun `What is happening here 3`( + @TempDir + tempDir: Path, + ) { + val someProjectDirectory = "some-project" + tempDir.createFile("$someProjectDirectory/src/main/.editorconfig", "root=false") + tempDir.createFile("$someProjectDirectory/src/.editorconfig", "root=true") + tempDir.createFile("$someProjectDirectory/.editorconfig", "root=true") + val kotlinFilePath = tempDir.createFile("$someProjectDirectory/src/main/kotlin/test.kt", "val foo = 42") + + val actual = + EditorConfigFinder() + .findEditorConfigs(kotlinFilePath) + .mapNotNull { + it + .absolutePathString() + .takeIf { + // Ignore files created in temp dirs of other tests + it.startsWith(tempDir.absolutePathString()) + }?.removePrefix(tempDir.absolutePathString()) + ?.removePrefix(tempDir.fileSystem.separator) + ?.replace(tempDir.fileSystem.separator, "/") + } + + assertThat(actual) + .contains( + "$someProjectDirectory/src/main/.editorconfig", + "$someProjectDirectory/src/.editorconfig", + ).doesNotContain( + "$someProjectDirectory/.editorconfig", + ) + } + + @EnabledOnOs(OS.WINDOWS) + @Test + fun `What is happening here 4`( + @TempDir + tempDir: Path, + ) { + val someProjectDirectory = "some-project" + tempDir.createFile("$someProjectDirectory/src/main/.editorconfig", "root=false") + tempDir.createFile("$someProjectDirectory/src/.editorconfig", "root=true") + tempDir.createFile("$someProjectDirectory/.editorconfig", "root=true") + val kotlinFilePath = tempDir.createFile("$someProjectDirectory/src/main/kotlin/test.kt", "val foo = 42") + + val actual = + EditorConfigFinder() + .findEditorConfigs(kotlinFilePath) + .mapNotNull { + it + .absolutePathString() + .takeIf { + // Ignore files created in temp dirs of other tests + it.startsWith(tempDir.absolutePathString()) + }?.removePrefix(tempDir.absolutePathString()) + ?.removePrefix(tempDir.fileSystem.separator) + ?.replace(tempDir.fileSystem.separator, "/") + } + + assertThat(actual) + .contains( + "$someProjectDirectory/src/main/.editorconfig", + "$someProjectDirectory/src/.editorconfig", + ).doesNotContain( + "$someProjectDirectory/.editorconfig", + ) + } } @Nested From ecba87904413b77708074eb9b0817d4e521bfb34 Mon Sep 17 00:00:00 2001 From: paul-dingemans Date: Mon, 26 Sep 2022 17:32:56 +0200 Subject: [PATCH 09/10] Resolve against original path as the drive letter seems to get lost on WindowsOs --- .../pinterest/ktlint/core/internal/EditorConfigFinder.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinder.kt b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinder.kt index 33e97736cc..41fda5909f 100644 --- a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinder.kt +++ b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinder.kt @@ -31,7 +31,11 @@ internal class EditorConfigFinder { result += findEditorConfigsInSubDirectories(normalizedPath) } result += findEditorConfigsInParentDirectories(normalizedPath) - return result.toList() + return result + .map { + // Resolve against original path as the drive letter seems to get lost on WindowsOs + path.resolve(it) + }.toList() } private fun findEditorConfigsInSubDirectories(path: Path): List { From 3e622bfd1a4ec97ea555e5e131803d18cc722b53 Mon Sep 17 00:00:00 2001 From: paul-dingemans Date: Mon, 26 Sep 2022 18:08:11 +0200 Subject: [PATCH 10/10] cleanup --- CHANGELOG.md | 6 +- .../com/pinterest/ktlint/core/KtLint.kt | 24 +- .../core/internal/EditorConfigFinderTest.kt | 244 ++---------------- 3 files changed, 39 insertions(+), 235 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b85d70447..57a782c980 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,11 @@ This project adheres to [Semantic Versioning](https://semver.org/). ### API Changes & RuleSet providers -#### Retrieve input files +#### Retrieve `.editorconfig`s -The list of files which will be accessed by KtLint when linting or formatting a given path can now be retrieved with the new API `KtLint.getInputPaths(path: Path): List`. Currently, the `.editorconfig` files are the only files which will be accessed during linting or formatting in addition to the source files itself. +The list of `.editorconfig` files which will be accessed by KtLint when linting or formatting a given path can now be retrieved with the new API `KtLint.editorConfigFilePaths(path: Path): List`. -This API can be called with either a file or a directory. It's intended usage is that it is called once with a directory path before actually linting or formatting files. When called with a directory path, all `.editorconfig` in the directory or any of its subdirectories (except hidden directories) are returned. In case the given directory does not contain an `.editorconfig` file or if it does not contain the `root=true` setting, the parent directories are scanned as well until a root `.editorconfig` file is found. +This API can be called with either a file or a directory. It's intended usage is that it is called once with the root directory of a project before actually linting or formatting files of that project. When called with a directory path, all `.editorconfig` files in the directory or any of its subdirectories (except hidden directories) are returned. In case the given directory does not contain an `.editorconfig` file or if it does not contain the `root=true` setting, the parent directories are scanned as well until a root `.editorconfig` file is found. Calling this API with a file path results in the `.editorconfig` files that will be accessed when processing that specific file. In case the directory in which the file resides does not contain an `.editorconfig` file or if it does not contain the `root=true` setting, the parent directories are scanned until a root `.editorconfig` file is found. diff --git a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/KtLint.kt b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/KtLint.kt index 121db5ad37..7633b19e54 100644 --- a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/KtLint.kt +++ b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/KtLint.kt @@ -22,13 +22,13 @@ import java.nio.file.FileSystems import java.nio.file.Path import java.nio.file.Paths import java.util.Locale -import kotlin.io.path.isDirectory import org.ec4j.core.Resource import org.ec4j.core.model.PropertyType import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.openapi.util.Key import org.jetbrains.kotlin.utils.addToStdlib.safeAs +@Suppress("MemberVisibilityCanBePrivate") public object KtLint { public val FILE_PATH_USER_DATA_KEY: Key = Key("FILE_PATH") @@ -331,10 +331,21 @@ public object KtLint { threadSafeEditorConfigCache.clear() } + /** + * Get the list of files which will be accessed by KtLint when linting or formatting the given file or directory. + * The API consumer can use this list to observe changes in '.editorconfig` files. Whenever such a change is + * observed, the API consumer should call [reloadEditorConfigFile]. + * To avoid unnecessary access to the file system, it is best to call this method only once for the root of the + * project which is to be [lint] or [format]. + */ + public fun editorConfigFilePaths(path: Path): List = + EditorConfigFinder().findEditorConfigs(path) + /** * Reloads an '.editorconfig' file given that it is currently loaded into the KtLint cache. This method is intended * to be called by the API consumer when it is aware of changes in the '.editorconfig' file that should be taken - * into account with next calls to [lint] and/or [format]. + * into account with next calls to [lint] and/or [format]. See [editorConfigFilePaths] to get the list of + * '.editorconfig' files which need to be observed. */ public fun reloadEditorConfigFile(path: Path) { threadSafeEditorConfigCache.reloadIfExists( @@ -342,15 +353,6 @@ public object KtLint { ) } - /** - * Get the list of files which will be accessed by KtLint when linting or formatting the given file or directory. - */ - public fun getInputPaths(path: Path): List = - EditorConfigFinder().findEditorConfigs(path) + - listOfNotNull( - path.takeIf { !it.isDirectory() }, - ) - /** * Generates Kotlin `.editorconfig` file section content based on [ExperimentalParams]. * diff --git a/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinderTest.kt b/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinderTest.kt index 340a71b7d1..67cb51287d 100644 --- a/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinderTest.kt +++ b/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/internal/EditorConfigFinderTest.kt @@ -8,9 +8,6 @@ import kotlin.io.path.writeText import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertAll -import org.junit.jupiter.api.condition.EnabledOnOs -import org.junit.jupiter.api.condition.OS import org.junit.jupiter.api.io.TempDir class EditorConfigFinderTest { @@ -25,12 +22,11 @@ class EditorConfigFinderTest { tempDir.createFile("$someSubDir/.editorconfig", "root=true") val kotlinFilePath = tempDir.createFile("$someSubDir/test.kt", "val foo = 42") - val actual = - EditorConfigFinder() - .findEditorConfigs(kotlinFilePath) - .mapNotNull { it.removePrefixOrNull(tempDir) } + val actual = EditorConfigFinder().findEditorConfigs(kotlinFilePath) - assertThat(actual).contains("$someSubDir/.editorconfig") + assertThat(actual).contains( + tempDir.plus("$someSubDir/.editorconfig"), + ) } @Test @@ -42,12 +38,11 @@ class EditorConfigFinderTest { tempDir.createFile("$someProjectDirectory/.editorconfig", "root=true") val kotlinFilePath = tempDir.createFile("$someProjectDirectory/src/main/kotlin/test.kt", "val foo = 42") - val actual = - EditorConfigFinder() - .findEditorConfigs(kotlinFilePath) - .mapNotNull { it.removePrefixOrNull(tempDir) } + val actual = EditorConfigFinder().findEditorConfigs(kotlinFilePath) - assertThat(actual).contains("$someProjectDirectory/.editorconfig") + assertThat(actual).contains( + tempDir.plus("$someProjectDirectory/.editorconfig"), + ) } @Test @@ -60,14 +55,11 @@ class EditorConfigFinderTest { tempDir.createFile("$someProjectDirectory/src/main/.editorconfig", "root=false") val kotlinFilePath = tempDir.createFile("$someProjectDirectory/src/main/kotlin/test.kt", "val foo = 42") - val actual = - EditorConfigFinder() - .findEditorConfigs(kotlinFilePath) - .mapNotNull { it.removePrefixOrNull(tempDir) } + val actual = EditorConfigFinder().findEditorConfigs(kotlinFilePath) assertThat(actual).contains( - "$someProjectDirectory/.editorconfig", - "$someProjectDirectory/src/main/.editorconfig", + tempDir.plus("$someProjectDirectory/.editorconfig"), + tempDir.plus("$someProjectDirectory/src/main/.editorconfig"), ) } @@ -82,34 +74,7 @@ class EditorConfigFinderTest { tempDir.createFile("$someProjectDirectory/.editorconfig", "root=true") val kotlinFilePath = tempDir.createFile("$someProjectDirectory/src/main/kotlin/test.kt", "val foo = 42") - val actual = - EditorConfigFinder() - .findEditorConfigs(kotlinFilePath) - .mapNotNull { it.removePrefixOrNull(tempDir) } - - assertThat(actual) - .contains( - "$someProjectDirectory/src/main/.editorconfig", - "$someProjectDirectory/src/.editorconfig", - ).doesNotContain( - "$someProjectDirectory/.editorconfig", - ) - } - - @Test - fun `What is happening here 0`( - @TempDir - tempDir: Path, - ) { - val someProjectDirectory = "some-project" - tempDir.createFile("$someProjectDirectory/src/main/.editorconfig", "root=false") - tempDir.createFile("$someProjectDirectory/src/.editorconfig", "root=true") - tempDir.createFile("$someProjectDirectory/.editorconfig", "root=true") - val kotlinFilePath = tempDir.createFile("$someProjectDirectory/src/main/kotlin/test.kt", "val foo = 42") - - val actual = - EditorConfigFinder() - .findEditorConfigs(kotlinFilePath) + val actual = EditorConfigFinder().findEditorConfigs(kotlinFilePath) assertThat(actual) .contains( @@ -119,150 +84,6 @@ class EditorConfigFinderTest { tempDir.plus("$someProjectDirectory/.editorconfig"), ) } - - @EnabledOnOs(OS.WINDOWS) - @Test - fun `What is happening here 1`( - @TempDir - tempDir: Path, - ) { - val someProjectDirectory = "some-project" - tempDir.createFile("$someProjectDirectory/src/main/.editorconfig", "root=false") - tempDir.createFile("$someProjectDirectory/src/.editorconfig", "root=true") - tempDir.createFile("$someProjectDirectory/.editorconfig", "root=true") - val kotlinFilePath = tempDir.createFile("$someProjectDirectory/src/main/kotlin/test.kt", "val foo = 42") - - val actual = - EditorConfigFinder() - .findEditorConfigs(kotlinFilePath) - .map { it.absolutePathString() } - - assertThat(actual) - .contains( - "$someProjectDirectory/src/main/.editorconfig", - "$someProjectDirectory/src/.editorconfig", - ).doesNotContain( - "$someProjectDirectory/.editorconfig", - ) - } - - @EnabledOnOs(OS.WINDOWS) - @Test - fun `What is happening here 2`( - @TempDir - tempDir: Path, - ) { - val someProjectDirectory = "some-project" - tempDir.createFile("$someProjectDirectory/src/main/.editorconfig", "root=false") - tempDir.createFile("$someProjectDirectory/src/.editorconfig", "root=true") - tempDir.createFile("$someProjectDirectory/.editorconfig", "root=true") - val kotlinFilePath = tempDir.createFile("$someProjectDirectory/src/main/kotlin/test.kt", "val foo = 42") - - val actual = - EditorConfigFinder() - .findEditorConfigs(kotlinFilePath) - .mapNotNull { - it - .absolutePathString() - .takeIf { - // Ignore files created in temp dirs of other tests - it.startsWith(tempDir.absolutePathString()) - }?.removePrefix(tempDir.absolutePathString()) - ?.removePrefix(tempDir.fileSystem.separator) - ?.replace(tempDir.fileSystem.separator, "/") - } - - assertAll( - { - assertThat(tempDir.absolutePathString()).isEqualTo("something-else") - }, - { - assertThat(tempDir.fileSystem.separator) - .withFailMessage("Path separator") - .isEqualTo("something-else") - }, - { - assertThat(actual) - .withFailMessage("Actual result") - .contains( - "$someProjectDirectory/src/main/.editorconfig", - "$someProjectDirectory/src/.editorconfig", - ).doesNotContain( - "$someProjectDirectory/.editorconfig", - ) - }, - ) - } - - @EnabledOnOs(OS.WINDOWS) - @Test - fun `What is happening here 3`( - @TempDir - tempDir: Path, - ) { - val someProjectDirectory = "some-project" - tempDir.createFile("$someProjectDirectory/src/main/.editorconfig", "root=false") - tempDir.createFile("$someProjectDirectory/src/.editorconfig", "root=true") - tempDir.createFile("$someProjectDirectory/.editorconfig", "root=true") - val kotlinFilePath = tempDir.createFile("$someProjectDirectory/src/main/kotlin/test.kt", "val foo = 42") - - val actual = - EditorConfigFinder() - .findEditorConfigs(kotlinFilePath) - .mapNotNull { - it - .absolutePathString() - .takeIf { - // Ignore files created in temp dirs of other tests - it.startsWith(tempDir.absolutePathString()) - }?.removePrefix(tempDir.absolutePathString()) - ?.removePrefix(tempDir.fileSystem.separator) - ?.replace(tempDir.fileSystem.separator, "/") - } - - assertThat(actual) - .contains( - "$someProjectDirectory/src/main/.editorconfig", - "$someProjectDirectory/src/.editorconfig", - ).doesNotContain( - "$someProjectDirectory/.editorconfig", - ) - } - - @EnabledOnOs(OS.WINDOWS) - @Test - fun `What is happening here 4`( - @TempDir - tempDir: Path, - ) { - val someProjectDirectory = "some-project" - tempDir.createFile("$someProjectDirectory/src/main/.editorconfig", "root=false") - tempDir.createFile("$someProjectDirectory/src/.editorconfig", "root=true") - tempDir.createFile("$someProjectDirectory/.editorconfig", "root=true") - val kotlinFilePath = tempDir.createFile("$someProjectDirectory/src/main/kotlin/test.kt", "val foo = 42") - - val actual = - EditorConfigFinder() - .findEditorConfigs(kotlinFilePath) - .mapNotNull { - it - .absolutePathString() - .takeIf { - // Ignore files created in temp dirs of other tests - it.startsWith(tempDir.absolutePathString()) - }?.removePrefix(tempDir.absolutePathString()) - ?.removePrefix(tempDir.fileSystem.separator) - ?.replace(tempDir.fileSystem.separator, "/") - } - - assertThat(actual) - .contains( - "$someProjectDirectory/src/main/.editorconfig", - "$someProjectDirectory/src/.editorconfig", - ).doesNotContain( - "$someProjectDirectory/.editorconfig", - ) - } } @Nested @@ -276,14 +97,11 @@ class EditorConfigFinderTest { tempDir.createFile("$someDirectory/.editorconfig", "root=true") tempDir.createFile("$someDirectory/src/main/kotlin/.editorconfig", "some-property=some-value") - val actual = - EditorConfigFinder() - .findEditorConfigs(tempDir.plus(someDirectory)) - .mapNotNull { it.removePrefixOrNull(tempDir) } + val actual = EditorConfigFinder().findEditorConfigs(tempDir.plus(someDirectory)) assertThat(actual).contains( - "$someDirectory/.editorconfig", - "$someDirectory/src/main/kotlin/.editorconfig", + tempDir.plus("$someDirectory/.editorconfig"), + tempDir.plus("$someDirectory/src/main/kotlin/.editorconfig"), ) } @@ -297,17 +115,14 @@ class EditorConfigFinderTest { tempDir.createFile("$someProjectDirectory/src/main/kotlin/.editorconfig", "some-property=some-value") tempDir.createFile("$someProjectDirectory/src/test/kotlin/.editorconfig", "some-property=some-value") - val actual = - EditorConfigFinder() - .findEditorConfigs(tempDir.plus("$someProjectDirectory/src/main/kotlin")) - .mapNotNull { it.removePrefixOrNull(tempDir) } + val actual = EditorConfigFinder().findEditorConfigs(tempDir.plus("$someProjectDirectory/src/main/kotlin")) assertThat(actual) .contains( - "$someProjectDirectory/.editorconfig", - "$someProjectDirectory/src/main/kotlin/.editorconfig", + tempDir.plus("$someProjectDirectory/.editorconfig"), + tempDir.plus("$someProjectDirectory/src/main/kotlin/.editorconfig"), ).doesNotContain( - "$someProjectDirectory/src/test/kotlin/.editorconfig", + tempDir.plus("$someProjectDirectory/src/test/kotlin/.editorconfig"), ) } @@ -321,15 +136,12 @@ class EditorConfigFinderTest { tempDir.createFile("$someProjectDirectory/src/main/kotlin/.editorconfig", "some-property=some-value") tempDir.createFile("$someProjectDirectory/src/test/kotlin/.editorconfig", "some-property=some-value") - val actual = - EditorConfigFinder() - .findEditorConfigs(tempDir.plus(someProjectDirectory)) - .mapNotNull { it.removePrefixOrNull(tempDir) } + val actual = EditorConfigFinder().findEditorConfigs(tempDir.plus(someProjectDirectory)) assertThat(actual).contains( - "$someProjectDirectory/.editorconfig", - "$someProjectDirectory/src/main/kotlin/.editorconfig", - "$someProjectDirectory/src/test/kotlin/.editorconfig", + tempDir.plus("$someProjectDirectory/.editorconfig"), + tempDir.plus("$someProjectDirectory/src/main/kotlin/.editorconfig"), + tempDir.plus("$someProjectDirectory/src/test/kotlin/.editorconfig"), ) } } @@ -348,14 +160,4 @@ class EditorConfigFinderTest { .plus(this.fileSystem.separator) .plus(subPath) .let { Paths.get(it) } - - private fun Path.removePrefixOrNull(tempDir: Path): String? = - this - .absolutePathString() - .takeIf { - // Ignore files created in temp dirs of other tests - it.startsWith(tempDir.absolutePathString()) - }?.removePrefix(tempDir.absolutePathString()) - ?.removePrefix(tempDir.fileSystem.separator) - ?.replace(tempDir.fileSystem.separator, "/") }