diff --git a/documentation/snapshot/docs/install/cli.md b/documentation/snapshot/docs/install/cli.md index 726b3bcd15..926af784f9 100644 --- a/documentation/snapshot/docs/install/cli.md +++ b/documentation/snapshot/docs/install/cli.md @@ -168,9 +168,14 @@ ktlint --stdin -F ``` !!! tip "Suppress logging and error output" - Logging output printed to `stdout` can be suppressed by setting `--log-level=none` (see [logging](#logging)). - Output printed to `stderr` can be suppressed in different ways. To ignore all error output, add `2> /dev/null` to the end of the command line. Otherwise, specify a [reporter](#violation-reporting) to write the error output to a file. +Logging output printed to `stdout` can be suppressed by setting `--log-level=none` (see [logging](#logging)). +Output printed to `stderr` can be suppressed in different ways. To ignore all error output, add `2> /dev/null` to the end of the command line. Otherwise, specify a [reporter](#violation-reporting) to write the error output to a file. +If input from `stdin` represents the contents of a file, the file path can be supplied with `stdin-path`. This path is made available for rules to use, the `--format` option will not modify this file. + +```shell title="file path from stdin-path" +ktlint --stdin --stdin-path /path/to/file/Foo.kt +``` ### Git hooks diff --git a/ktlint-cli/src/main/kotlin/com/pinterest/ktlint/cli/internal/KtlintCommandLine.kt b/ktlint-cli/src/main/kotlin/com/pinterest/ktlint/cli/internal/KtlintCommandLine.kt index b63b2a40f0..a14ed20b3a 100644 --- a/ktlint-cli/src/main/kotlin/com/pinterest/ktlint/cli/internal/KtlintCommandLine.kt +++ b/ktlint-cli/src/main/kotlin/com/pinterest/ktlint/cli/internal/KtlintCommandLine.kt @@ -182,6 +182,12 @@ internal class KtlintCommandLine : CliktCommand(name = "ktlint") { help = "Read file from stdin", ).flag() + private val stdinPath: String? by + option( + "--stdin-path", + help = "Virtual file location for stdin. When combined with option '--format' the actual file will not be overwritten", + ) + private val patternsFromStdin: String? by option( "--patterns-from-stdin", @@ -263,7 +269,7 @@ internal class KtlintCommandLine : CliktCommand(name = "ktlint") { val editorConfigOverride = EditorConfigOverride .EMPTY_EDITOR_CONFIG_OVERRIDE - .applyIf(stdin) { + .applyIf(stdin && stdinPath.isNullOrBlank()) { logger.debug { "Add editor config override to disable 'filename' rule which can not be used in combination with reading from " + "" @@ -427,11 +433,17 @@ internal class KtlintCommandLine : CliktCommand(name = "ktlint") { ktLintRuleEngine: KtLintRuleEngine, reporter: ReporterV2, ) { + val code = + stdinPath + ?.expandTildeToFullPath() + ?.let { path -> Paths.get(path) } + ?.let { Code.fromStdin(it) } + ?: Code.fromStdin() report( KtLintRuleEngine.STDIN_FILE, process( ktLintRuleEngine = ktLintRuleEngine, - code = Code.fromStdin(), + code = code, baselineLintErrors = emptyList(), ), reporter, diff --git a/ktlint-cli/src/test/kotlin/com/pinterest/ktlint/cli/SimpleCLITest.kt b/ktlint-cli/src/test/kotlin/com/pinterest/ktlint/cli/SimpleCLITest.kt index 3912c56af3..a62c368b30 100644 --- a/ktlint-cli/src/test/kotlin/com/pinterest/ktlint/cli/SimpleCLITest.kt +++ b/ktlint-cli/src/test/kotlin/com/pinterest/ktlint/cli/SimpleCLITest.kt @@ -398,6 +398,21 @@ class SimpleCLITest { } } + @Test + fun `Issue 1123 - Enable filename rule when --stdin and --stdin-path is used`( + @TempDir + tempDir: Path, + ) { + CommandLineTestRunner(tempDir) + .run( + testProjectName = "too-many-empty-lines", + arguments = listOf("--stdin", "--stdin-path", "/foo/Foo.kt"), + stdin = ByteArrayInputStream("fun foo() = 42".toByteArray()), + ) { + assertThat(normalOutput).doesNotContainLineMatching(Regex(".*ktlint_standard_filename: disabled.*")) + } + } + @Test fun `Issue 1832 - Given stdin input containing Kotlin Script resulting in a KtLintParseException when linted as Kotlin code then process the input as Kotlin Script`( @TempDir diff --git a/ktlint-rule-engine/api/ktlint-rule-engine.api b/ktlint-rule-engine/api/ktlint-rule-engine.api index 4f98578583..0fe4c7d1eb 100644 --- a/ktlint-rule-engine/api/ktlint-rule-engine.api +++ b/ktlint-rule-engine/api/ktlint-rule-engine.api @@ -18,6 +18,7 @@ public final class com/pinterest/ktlint/rule/engine/api/Code$Companion { public final fun fromSnippetWithPath (Ljava/lang/String;Ljava/nio/file/Path;)Lcom/pinterest/ktlint/rule/engine/api/Code; public static synthetic fun fromSnippetWithPath$default (Lcom/pinterest/ktlint/rule/engine/api/Code$Companion;Ljava/lang/String;Ljava/nio/file/Path;ILjava/lang/Object;)Lcom/pinterest/ktlint/rule/engine/api/Code; public final fun fromStdin ()Lcom/pinterest/ktlint/rule/engine/api/Code; + public final fun fromStdin (Ljava/nio/file/Path;)Lcom/pinterest/ktlint/rule/engine/api/Code; } public final class com/pinterest/ktlint/rule/engine/api/EditorConfigDefaults { diff --git a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/api/Code.kt b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/api/Code.kt index c703e6e1dc..762f892365 100644 --- a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/api/Code.kt +++ b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/api/Code.kt @@ -22,14 +22,14 @@ public class Code private constructor( ) { public fun fileNameOrStdin(): String = if (isStdIn) { - STDIN_FILE + fileName ?: STDIN_FILE } else { fileName.orEmpty() } public fun filePathOrStdin(): String = if (isStdIn) { - STDIN_FILE + filePath?.pathString ?: STDIN_FILE } else { filePath?.pathString.orEmpty() } @@ -115,6 +115,13 @@ public class Code private constructor( * filesystem are ignored as the snippet is not associated with a file path. Use [Code.fromFile] for scanning a file while at the * same time respecting the '.editorconfig' files on the path to the file. */ - public fun fromStdin(): Code = fromSnippet(String(System.`in`.readBytes())) + public fun fromStdin(): Code = fromSnippetWithPath(String(System.`in`.readBytes())) + + /** + * Create [Code] by reading the snippet from 'stdin'. The code snippet is associated with the given path. The actual file does not + * need to exist, neither do the contents of the actual file have to match with the content specified via 'stdin'. The + * '.editorconfig' files on the [virtualPath] are respected. + */ + public fun fromStdin(virtualPath: Path): Code = fromSnippetWithPath(String(System.`in`.readBytes()), virtualPath = virtualPath) } }