Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support ANT-like glob patterns #1856

Merged
merged 12 commits into from
Dec 13, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,14 @@ import org.slf4j.event.Level
import java.io.OutputStream
import java.nio.file.Path
import java.nio.file.Paths
import kotlin.io.path.ExperimentalPathApi
import kotlin.io.path.PathWalkOption

import kotlin.io.path.createDirectories
import kotlin.io.path.exists
import kotlin.io.path.inputStream
import kotlin.io.path.outputStream
import kotlin.io.path.walk
import kotlin.system.exitProcess
import kotlinx.cli.ArgParser
import kotlinx.cli.ArgType
Expand Down Expand Up @@ -96,17 +99,28 @@ data class DiktatProperties(
)
}

private fun getFiles(sourceRootDir: Path): Collection<Path> = patterns
private fun getFiles(sourceRootDir: Path): Collection<Path> {
val (includePatterns, excludePatterns) = patterns.partition { !it.startsWith("!") }
val exclude by lazy {
getFiles(sourceRootDir, excludePatterns.map { it.removePrefix("!") })
.toSet()
}
return getFiles(sourceRootDir, includePatterns).filterNot { exclude.contains(it) }.toList()
}

@OptIn(ExperimentalPathApi::class)
private fun getFiles(sourceRootDir: Path, patterns: List<String>): Sequence<Path> = patterns
.asSequence()
.flatMap { pattern ->
pattern.tryToPathIfExists()?.let { sequenceOf(it) }
pattern.tryToPathIfExists()?.walk(PathWalkOption.INCLUDE_DIRECTORIES)
?: sourceRootDir.walkByGlob(pattern)
}
.filter { file -> file.isKotlinCodeOrScript() }
.map { it.normalize() }
.map { it.toAbsolutePath() }
.distinct()
.toList()



private fun getReporterOutput(): OutputStream? = output
?.let { Paths.get(it) }
Expand Down
35 changes: 20 additions & 15 deletions diktat-cli/src/main/kotlin/com/saveourtool/diktat/util/CliUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,33 @@ private val roots: Set<String> = FileSystems.getDefault()
.map { it.absolutePathString() }
.toSet()

private const val parentDirectoryPrefix = 3
private const val parentDirectoryUnix = "../"
private const val parentDirectoryWindows = "..\\"

/**
* Create a matcher and return a filter that uses it.
*
* @param glob glob pattern to filter files
* @return a sequence of files which matches to [glob]
*/
@OptIn(ExperimentalPathApi::class)
fun Path.walkByGlob(glob: String): Sequence<Path> = fileSystem.globMatcher(glob)
.let { matcher ->
this.walk(PathWalkOption.INCLUDE_DIRECTORIES)
.filter { matcher.matches(it) }
}
fun Path.walkByGlob(glob: String): Sequence<Path> = if (glob.startsWith(parentDirectoryUnix) || glob.startsWith(parentDirectoryWindows)) {
parent?.walkByGlob(glob.substring(parentDirectoryPrefix)) ?: emptySequence()
} else {
fileSystem.globMatcher(glob)
.let { matcher ->
walk(PathWalkOption.INCLUDE_DIRECTORIES).filter { matcher.matches(it) }
}
}

private fun FileSystem.globMatcher(glob: String): PathMatcher = if (isAbsoluteGlob(glob)) {
getPathMatcher("glob:$glob")
} else {
getPathMatcher("glob:**${File.separatorChar}$glob")
}

private fun isAbsoluteGlob(glob: String): Boolean = glob.startsWith("**") || roots.any { glob.startsWith(it, true) }

/**
* @return path or null if path is invalid or doesn't exist
Expand All @@ -45,13 +60,3 @@ fun String.tryToPathIfExists(): Path? = try {
} catch (e: InvalidPathException) {
null
}

private fun FileSystem.globMatcher(glob: String): PathMatcher = if (isAbsoluteGlob(glob)) {
getPathMatcher("glob:${glob.toUnixSeparator()}")
} else {
getPathMatcher("glob:**/${glob.toUnixSeparator()}")
}

private fun String.toUnixSeparator(): String = replace(File.separatorChar, '/')

private fun isAbsoluteGlob(glob: String): Boolean = glob.startsWith("**") || roots.any { glob.startsWith(it, true) }