diff --git a/api/src/main/kotlin/io/spine/protodata/Compilation.kt b/api/src/main/kotlin/io/spine/protodata/Compilation.kt index 957e6bf96..0ae568365 100644 --- a/api/src/main/kotlin/io/spine/protodata/Compilation.kt +++ b/api/src/main/kotlin/io/spine/protodata/Compilation.kt @@ -33,11 +33,13 @@ import io.spine.protodata.Compilation.ERROR_EXIT_CODE import io.spine.protodata.Compilation.error import io.spine.protodata.ast.Span import io.spine.protodata.ast.toJava +import io.spine.string.Indent +import io.spine.string.Separator +import io.spine.string.indent import java.io.File import kotlin.system.exitProcess import io.spine.protodata.ast.File as PFile - /** * Provides functions to report compilation errors and warnings. * @@ -140,7 +142,7 @@ public object Compilation { @VisibleForTesting internal fun errorMessage(file: File, line: Int, column: Int, message: String) = - "$ERROR_PREFIX ${file.maybeUri()}:$line:$column: $message" + indentedMessage(ERROR_PREFIX, file, line, column, message) /** * Prints the warning diagnostics to [System.out]. @@ -191,7 +193,48 @@ public object Compilation { @VisibleForTesting internal fun warningMessage(file: File, line: Int, column: Int, message: String) = - "$WARNING_PREFIX ${file.maybeUri()}:$line:$column: $message" + indentedMessage(WARNING_PREFIX, file, line, column, message) + + /** + * Constructs a formatted compilation message with indentation + * for multi-line user messages. + * + * The returned string consists of: + * + * 1. The specified [prefix] followed by the file URI or path, line, and column. + * 2. The first line of the [message] appended directly to the header. + * 3. Any subsequent lines of the [message], each indented under the header + * by the length of the [prefix] plus one space. + * + * @throws IllegalArgumentException if [prefix] is blank. + */ + @VisibleForTesting + internal fun indentedMessage( + prefix: String, + file: File, + line: Int, + column: Int, + message: String + ): String { + require(prefix.isNotBlank()) { + "The compilation message must have a non-empty prefix specified." + } + + val messageLines = message.lines() + val messageHeader = messageLines.first() + + val indent = Indent(prefix.length + 1) + val indentedBody = messageLines.drop(1) + .indent(indent, level = 1) + + return buildString { + append("$prefix ${file.maybeUri()}:$line:$column: $messageHeader") + if (indentedBody.isNotEmpty()) { + append(Separator.nl()) + append(indentedBody) + } + } + } /** * The exception thrown by [Compilation.error] when the testing mode is on. diff --git a/api/src/test/kotlin/io/spine/protodata/CompilationSpec.kt b/api/src/test/kotlin/io/spine/protodata/CompilationSpec.kt index 5fa7ad39c..ac4a3f429 100644 --- a/api/src/test/kotlin/io/spine/protodata/CompilationSpec.kt +++ b/api/src/test/kotlin/io/spine/protodata/CompilationSpec.kt @@ -26,6 +26,7 @@ package io.spine.protodata +import io.kotest.matchers.shouldBe import io.kotest.matchers.string.shouldContain import io.kotest.matchers.string.shouldNotContain import io.kotest.matchers.string.shouldStartWith @@ -34,10 +35,13 @@ import io.spine.protodata.Compilation.ERROR_PREFIX import io.spine.protodata.Compilation.WARNING_PREFIX import io.spine.protodata.ast.Span import io.spine.protodata.ast.toAbsoluteFile +import io.spine.string.ti +import io.spine.string.tm import io.spine.testing.TestValues import java.io.File import java.nio.file.Paths import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertDoesNotThrow import org.junit.jupiter.api.assertThrows @@ -161,6 +165,73 @@ internal class CompilationSpec { Compilation.check(condition = true, file, span) { msg } } } + + @Nested inner class + `when indenting a user message` { + + private val file = File("with_error.proto") + private val prefix = "m:" + private val line = 1 + private val column = 1 + + @Test + fun `use the prefix length as a number of spaces`() { + val message = """ + The file contains one or more mistakes. + Mistake #1: illegal beginning. + Mistake #2: illegal ending. + """.ti() + + val result = Compilation.indentedMessage(prefix, file, line, column, message) + val expected = """ + m: with_error.proto:1:1: The file contains one or more mistakes. + Mistake #1: illegal beginning. + Mistake #2: illegal ending. + """.ti() + + result shouldBe expected + } + + @Test + fun `throw when given an empty prefix`() { + assertThrows { + Compilation.indentedMessage(prefix = "", file, line, column, message = "") + } + } + + @Test + fun `do not append trailing lines for one-line messages`() { + val message = "The file contains one or more mistakes." + val result = Compilation.indentedMessage(prefix, file, line, column, message) + result shouldBe "m: with_error.proto:1:1: The file contains one or more mistakes." + } + + @Test + fun `do nothing for empty messages`() { + val result = Compilation.indentedMessage(prefix, file, line, column, message = "") + result shouldBe "m: with_error.proto:1:1: " + } + + @Test + fun `preserve original blank lines and whitespaces`() { + val message = """ + First line. + + Third line after blank. + Fourth line with its own whitespaces. + """.ti() + + val result = Compilation.indentedMessage(prefix, file, line, column, message) + val expected = """ + |m: with_error.proto:1:1: First line. + | + | Third line after blank. + | Fourth line with its own whitespaces. + """.tm() + + result shouldBe expected + } + } } private fun File.uriRef(): String { diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/Base.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/Base.kt index a026130e1..06fd77197 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/Base.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/Base.kt @@ -33,8 +33,8 @@ package io.spine.dependency.local */ @Suppress("ConstPropertyName") object Base { - const val version = "2.0.0-SNAPSHOT.316" - const val versionForBuildScript = "2.0.0-SNAPSHOT.316" + const val version = "2.0.0-SNAPSHOT.318" + const val versionForBuildScript = "2.0.0-SNAPSHOT.318" const val group = Spine.group const val artifact = "spine-base" const val lib = "$group:$artifact:$version" diff --git a/dependencies.md b/dependencies.md index 854fcf7b1..b5709845d 100644 --- a/dependencies.md +++ b/dependencies.md @@ -1,6 +1,6 @@ -# Dependencies of `io.spine.protodata:protodata-api:0.95.0` +# Dependencies of `io.spine.protodata:protodata-api:0.95.1` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.18.3. @@ -1110,12 +1110,12 @@ The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu May 08 19:15:02 WEST 2025** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). +This report was generated on **Fri May 16 16:39:56 CEST 2025** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.protodata:protodata-api-tests:0.95.0` +# Dependencies of `io.spine.protodata:protodata-api-tests:0.95.1` ## Runtime 1. **Group** : org.jetbrains. **Name** : annotations. **Version** : 13.0. @@ -1956,12 +1956,12 @@ This report was generated on **Thu May 08 19:15:02 WEST 2025** using [Gradle-Lic The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu May 08 19:15:02 WEST 2025** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). +This report was generated on **Fri May 16 16:39:56 CEST 2025** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.protodata:protodata-backend:0.95.0` +# Dependencies of `io.spine.protodata:protodata-backend:0.95.1` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.18.3. @@ -3071,12 +3071,12 @@ This report was generated on **Thu May 08 19:15:02 WEST 2025** using [Gradle-Lic The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu May 08 19:15:03 WEST 2025** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). +This report was generated on **Fri May 16 16:39:57 CEST 2025** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.protodata:protodata-cli:0.95.0` +# Dependencies of `io.spine.protodata:protodata-cli:0.95.1` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.18.3. @@ -4225,12 +4225,12 @@ This report was generated on **Thu May 08 19:15:03 WEST 2025** using [Gradle-Lic The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu May 08 19:15:03 WEST 2025** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). +This report was generated on **Fri May 16 16:39:58 CEST 2025** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.protodata:protodata-gradle-api:0.95.0` +# Dependencies of `io.spine.protodata:protodata-gradle-api:0.95.1` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.18.3. @@ -5275,12 +5275,12 @@ This report was generated on **Thu May 08 19:15:03 WEST 2025** using [Gradle-Lic The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu May 08 19:15:03 WEST 2025** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). +This report was generated on **Fri May 16 16:39:59 CEST 2025** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.protodata:protodata-gradle-plugin:0.95.0` +# Dependencies of `io.spine.protodata:protodata-gradle-plugin:0.95.1` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.18.3. @@ -6381,12 +6381,12 @@ This report was generated on **Thu May 08 19:15:03 WEST 2025** using [Gradle-Lic The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu May 08 19:15:04 WEST 2025** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). +This report was generated on **Fri May 16 16:39:59 CEST 2025** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.protodata:protodata-java:0.95.0` +# Dependencies of `io.spine.protodata:protodata-java:0.95.1` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.18.3. @@ -7496,12 +7496,12 @@ This report was generated on **Thu May 08 19:15:04 WEST 2025** using [Gradle-Lic The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu May 08 19:15:04 WEST 2025** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). +This report was generated on **Fri May 16 16:40:00 CEST 2025** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.protodata:protodata-params:0.95.0` +# Dependencies of `io.spine.protodata:protodata-params:0.95.1` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.18.3. @@ -8618,12 +8618,12 @@ This report was generated on **Thu May 08 19:15:04 WEST 2025** using [Gradle-Lic The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu May 08 19:15:04 WEST 2025** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). +This report was generated on **Fri May 16 16:40:00 CEST 2025** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.protodata:protodata-protoc:0.95.0` +# Dependencies of `io.spine.protodata:protodata-protoc:0.95.1` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -9410,12 +9410,12 @@ This report was generated on **Thu May 08 19:15:04 WEST 2025** using [Gradle-Lic The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu May 08 19:15:04 WEST 2025** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). +This report was generated on **Fri May 16 16:40:00 CEST 2025** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.protodata:protodata-test-env:0.95.0` +# Dependencies of `io.spine.protodata:protodata-test-env:0.95.1` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.18.3. @@ -10536,12 +10536,12 @@ This report was generated on **Thu May 08 19:15:04 WEST 2025** using [Gradle-Lic The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu May 08 19:15:05 WEST 2025** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). +This report was generated on **Fri May 16 16:40:00 CEST 2025** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.protodata:protodata-testlib:0.95.0` +# Dependencies of `io.spine.protodata:protodata-testlib:0.95.1` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.18.3. @@ -11749,4 +11749,4 @@ This report was generated on **Thu May 08 19:15:05 WEST 2025** using [Gradle-Lic The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu May 08 19:15:05 WEST 2025** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). \ No newline at end of file +This report was generated on **Fri May 16 16:40:01 CEST 2025** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). \ No newline at end of file diff --git a/java/src/main/kotlin/io/spine/protodata/java/StringExpressions.kt b/java/src/main/kotlin/io/spine/protodata/java/StringExpressions.kt new file mode 100644 index 000000000..291ba6a69 --- /dev/null +++ b/java/src/main/kotlin/io/spine/protodata/java/StringExpressions.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2025, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.protodata.java + +/** + * Yields a new string [Expression] by concatenating this [Expression] + * with another [Expression]. + */ +public operator fun Expression.plus(value: Expression): Expression = + Expression("$this + $value") + +/** + * Yields a new string [Expression] by appending the given [String] + * literal to this string [Expression]. + */ +public operator fun Expression.plus(value: String): Expression = + plus(StringLiteral(value)) diff --git a/java/src/test/kotlin/io/spine/protodata/java/StringExpressionsSpec.kt b/java/src/test/kotlin/io/spine/protodata/java/StringExpressionsSpec.kt new file mode 100644 index 000000000..5f6dc0316 --- /dev/null +++ b/java/src/test/kotlin/io/spine/protodata/java/StringExpressionsSpec.kt @@ -0,0 +1,57 @@ +/* + * Copyright 2025, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.protodata.java + +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +@DisplayName("Extensions for `Expression` should") +internal class StringExpressionsSpec { + + @Test + fun `concatenate two string expressions`() { + val hello = StringLiteral("Hello ") + val world = Expression("object.toString()") + val expected = """ + "Hello " + object.toString() + """.trimIndent() + + (hello + world).code shouldBe expected + } + + @Test + fun `concatenate a string expression with a literal`() { + val hello = StringLiteral("Hello") + val world = "World!" + val expected = """ + "Hello" + "World!" + """.trimIndent() + + (hello + world).code shouldBe expected + } +} diff --git a/pom.xml b/pom.xml index eb30dd341..1a790e0ee 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ all modules and does not describe the project structure per-subproject. --> io.spine.protodata ProtoData -0.95.0 +0.95.1 2015 @@ -92,7 +92,7 @@ all modules and does not describe the project structure per-subproject. io.spine spine-base - 2.0.0-SNAPSHOT.316 + 2.0.0-SNAPSHOT.318 compile diff --git a/version.gradle.kts b/version.gradle.kts index 9c054d781..5ecf468c7 100644 --- a/version.gradle.kts +++ b/version.gradle.kts @@ -32,4 +32,4 @@ * * For dependencies on Spine SDK module please see [io.spine.dependency.local.Spine]. */ -val protoDataVersion: String by extra("0.95.0") +val protoDataVersion: String by extra("0.95.1")