From 0d7b95d3ff74a3c018478296aed8664990dda942 Mon Sep 17 00:00:00 2001 From: Thomas Purchas Date: Wed, 26 Jun 2024 23:15:05 +0100 Subject: [PATCH] Add colours to Pkl errors in Cli output To make error messages from Pkl eval easier to read, this change uses the Jansi library to colour the output, making it quicker and easier to scan error messages and understand what's happened. The Jansi library also detects if the CLI output is a terminal capable of handling colours, and will automatically strip out escape codes if the output won't support them (e.g. piping the output somewhere else). --- bench/gradle.lockfile | 1 + .../src/main/kotlin/pklKotlinTest.gradle.kts | 3 + docs/gradle.lockfile | 1 + pkl-cli/gradle.lockfile | 2 +- pkl-cli/pkl-cli.gradle.kts | 5 +- .../main/kotlin/org/pkl/cli/CliTestRunner.kt | 1 + pkl-codegen-java/gradle.lockfile | 1 + pkl-codegen-kotlin/gradle.lockfile | 1 + pkl-commons-cli/gradle.lockfile | 1 + pkl-commons-cli/pkl-commons-cli.gradle.kts | 2 +- .../kotlin/org/pkl/commons/cli/CliMain.kt | 4 ++ pkl-commons-test/gradle.lockfile | 1 + pkl-commons-test/pkl-commons-test.gradle.kts | 1 + pkl-config-java/gradle.lockfile | 1 + pkl-config-kotlin/gradle.lockfile | 1 + pkl-core/gradle.lockfile | 1 + pkl-core/pkl-core.gradle.kts | 2 + .../pkl/core/runtime/StackTraceRenderer.java | 62 ++++++++++++++----- .../pkl/core/runtime/VmExceptionRenderer.java | 11 +++- .../kotlin/org/pkl/core/EvaluateTestsTest.kt | 2 - .../test/kotlin/org/pkl/core/EvaluatorTest.kt | 2 +- .../org/pkl/core/project/ProjectTest.kt | 5 +- pkl-doc/gradle.lockfile | 1 + pkl-executor/gradle.lockfile | 1 + pkl-gradle/gradle.lockfile | 1 + pkl-gradle/pkl-gradle.gradle.kts | 6 ++ pkl-server/gradle.lockfile | 1 + 27 files changed, 93 insertions(+), 28 deletions(-) diff --git a/bench/gradle.lockfile b/bench/gradle.lockfile index 75232702..d89a76b1 100644 --- a/bench/gradle.lockfile +++ b/bench/gradle.lockfile @@ -8,6 +8,7 @@ net.sf.jopt-simple:jopt-simple:5.0.4=jmh,jmhCompileClasspath,jmhImplementationDe org.apache.commons:commons-math3:3.6.1=jmh,jmhCompileClasspath,jmhImplementationDependenciesMetadata,jmhRuntimeClasspath org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeOnlyDependenciesMetadata org.assertj:assertj-core:3.26.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath +org.fusesource.jansi:jansi:2.4.1=jmh,jmhRuntimeClasspath org.graalvm.compiler:compiler:23.0.2=graal org.graalvm.sdk:graal-sdk:23.0.2=graal,jmh,jmhRuntimeClasspath,truffle org.graalvm.truffle:truffle-api:23.0.2=graal,jmh,jmhRuntimeClasspath,truffle diff --git a/buildSrc/src/main/kotlin/pklKotlinTest.gradle.kts b/buildSrc/src/main/kotlin/pklKotlinTest.gradle.kts index 0b40c956..6e0a6059 100644 --- a/buildSrc/src/main/kotlin/pklKotlinTest.gradle.kts +++ b/buildSrc/src/main/kotlin/pklKotlinTest.gradle.kts @@ -23,6 +23,9 @@ tasks.withType().configureEach { // enable checking of stdlib return types systemProperty("org.pkl.testMode", "true") + + // Disable colour output in tests + systemProperty("org.fusesource.jansi.Ansi.disable", "true") reports.named("html") { enabled = true diff --git a/docs/gradle.lockfile b/docs/gradle.lockfile index 0b041bcb..c0b6532c 100644 --- a/docs/gradle.lockfile +++ b/docs/gradle.lockfile @@ -7,6 +7,7 @@ net.bytebuddy:byte-buddy:1.14.16=testCompileClasspath,testImplementationDependen net.java.dev.jna:jna:5.6.0=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeOnlyDependenciesMetadata org.assertj:assertj-core:3.26.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath +org.fusesource.jansi:jansi:2.4.1=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath org.graalvm.sdk:graal-sdk:23.0.2=testRuntimeClasspath org.graalvm.truffle:truffle-api:23.0.2=testRuntimeClasspath org.jetbrains.intellij.deps:trove4j:1.0.20200330=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath diff --git a/pkl-cli/gradle.lockfile b/pkl-cli/gradle.lockfile index e53e5780..6eab853a 100644 --- a/pkl-cli/gradle.lockfile +++ b/pkl-cli/gradle.lockfile @@ -101,4 +101,4 @@ org.xmlunit:xmlunit-core:2.10.0=testCompileClasspath,testImplementationDependenc org.xmlunit:xmlunit-legacy:2.10.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath org.xmlunit:xmlunit-placeholders:2.10.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath org.yaml:snakeyaml:2.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath -empty=annotationProcessor,archives,compile,intransitiveDependenciesMetadata,javaExecutable,kotlinCompilerPluginClasspath,kotlinNativeCompilerPluginClasspath,kotlinScriptDef,kotlinScriptDefExtensions,runtime,runtimeOnlyDependenciesMetadata,shadow,signatures,sourcesJar,stagedAlpineLinuxAmd64Executable,stagedLinuxAarch64Executable,stagedLinuxAmd64Executable,stagedMacAarch64Executable,stagedMacAmd64Executable,testAnnotationProcessor,testApiDependenciesMetadata,testCompile,testCompileOnly,testCompileOnlyDependenciesMetadata,testIntransitiveDependenciesMetadata,testKotlinScriptDef,testKotlinScriptDefExtensions,testRuntime +empty=annotationProcessor,archives,compile,intransitiveDependenciesMetadata,javaExecutable,kotlinCompilerPluginClasspath,kotlinNativeCompilerPluginClasspath,kotlinScriptDef,kotlinScriptDefExtensions,runtime,runtimeOnlyDependenciesMetadata,shadow,signatures,sourcesJar,stagedAlpineLinuxAmd64Executable,stagedLinuxAarch64Executable,stagedLinuxAmd64Executable,stagedMacAarch64Executable,stagedMacAmd64Executable,stagedWindowsAmd64Executable,testAnnotationProcessor,testApiDependenciesMetadata,testCompile,testCompileOnly,testCompileOnlyDependenciesMetadata,testIntransitiveDependenciesMetadata,testKotlinScriptDef,testKotlinScriptDefExtensions,testRuntime diff --git a/pkl-cli/pkl-cli.gradle.kts b/pkl-cli/pkl-cli.gradle.kts index d9d49511..c43199a0 100644 --- a/pkl-cli/pkl-cli.gradle.kts +++ b/pkl-cli/pkl-cli.gradle.kts @@ -54,7 +54,7 @@ dependencies { exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-jdk8") exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-common") } - + testImplementation(projects.pklCommonsTest) testImplementation(libs.wiremock) @@ -176,7 +176,8 @@ fun Exec.configureExecutable( // that the "initialize everything at build time" *CLI* option is likely here to stay add("--initialize-at-build-time=") // needed for messagepack-java (see https://github.com/msgpack/msgpack-java/issues/600) - add("--initialize-at-run-time=org.msgpack.core.buffer.DirectBufferAccess") + // needed for jansi (see https://github.com/fusesource/jansi/issues/199#issuecomment-1252268229) + add("--initialize-at-run-time=org.msgpack.core.buffer.DirectBufferAccess,org.fusesource.jansi.internal") add("--no-fallback") add("-H:IncludeResources=org/pkl/core/stdlib/.*\\.pkl") add("-H:IncludeResources=org/jline/utils/.*") diff --git a/pkl-cli/src/main/kotlin/org/pkl/cli/CliTestRunner.kt b/pkl-cli/src/main/kotlin/org/pkl/cli/CliTestRunner.kt index bd08a77c..2309a6e5 100644 --- a/pkl-cli/src/main/kotlin/org/pkl/cli/CliTestRunner.kt +++ b/pkl-cli/src/main/kotlin/org/pkl/cli/CliTestRunner.kt @@ -89,6 +89,7 @@ constructor( } catch (ex: Exception) { errWriter.appendLine("Error evaluating module ${moduleUri.path}:") errWriter.write(ex.message ?: "") + errWriter.write(ex.stackTraceToString()) if (moduleUri != sources.last()) { errWriter.appendLine() } diff --git a/pkl-codegen-java/gradle.lockfile b/pkl-codegen-java/gradle.lockfile index ac71e00a..f5cb2da2 100644 --- a/pkl-codegen-java/gradle.lockfile +++ b/pkl-codegen-java/gradle.lockfile @@ -10,6 +10,7 @@ net.bytebuddy:byte-buddy:1.14.16=testCompileClasspath,testImplementationDependen net.java.dev.jna:jna:5.6.0=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeOnlyDependenciesMetadata org.assertj:assertj-core:3.26.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath +org.fusesource.jansi:jansi:2.4.1=runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath org.graalvm.sdk:graal-sdk:23.0.2=default,runtimeClasspath,testRuntimeClasspath org.graalvm.truffle:truffle-api:23.0.2=default,runtimeClasspath,testRuntimeClasspath org.jetbrains.intellij.deps:trove4j:1.0.20200330=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath diff --git a/pkl-codegen-kotlin/gradle.lockfile b/pkl-codegen-kotlin/gradle.lockfile index 4aee4cca..9c316e3e 100644 --- a/pkl-codegen-kotlin/gradle.lockfile +++ b/pkl-codegen-kotlin/gradle.lockfile @@ -10,6 +10,7 @@ net.bytebuddy:byte-buddy:1.14.16=testCompileClasspath,testImplementationDependen net.java.dev.jna:jna:5.6.0=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath,testRuntimeClasspath org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeOnlyDependenciesMetadata org.assertj:assertj-core:3.26.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath +org.fusesource.jansi:jansi:2.4.1=runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath org.graalvm.sdk:graal-sdk:23.0.2=default,runtimeClasspath,testRuntimeClasspath org.graalvm.truffle:truffle-api:23.0.2=default,runtimeClasspath,testRuntimeClasspath org.jetbrains.intellij.deps:trove4j:1.0.20200330=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath,testRuntimeClasspath,testRuntimeOnlyDependenciesMetadata diff --git a/pkl-commons-cli/gradle.lockfile b/pkl-commons-cli/gradle.lockfile index b59c74b9..9284871d 100644 --- a/pkl-commons-cli/gradle.lockfile +++ b/pkl-commons-cli/gradle.lockfile @@ -8,6 +8,7 @@ net.bytebuddy:byte-buddy:1.14.16=testCompileClasspath,testImplementationDependen net.java.dev.jna:jna:5.6.0=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeOnlyDependenciesMetadata org.assertj:assertj-core:3.26.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath +org.fusesource.jansi:jansi:2.4.1=compileClasspath,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath org.graalvm.sdk:graal-sdk:23.0.2=default,runtimeClasspath,testRuntimeClasspath org.graalvm.truffle:truffle-api:23.0.2=default,runtimeClasspath,testRuntimeClasspath org.jetbrains.intellij.deps:trove4j:1.0.20200330=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath diff --git a/pkl-commons-cli/pkl-commons-cli.gradle.kts b/pkl-commons-cli/pkl-commons-cli.gradle.kts index d836b138..a718de00 100644 --- a/pkl-commons-cli/pkl-commons-cli.gradle.kts +++ b/pkl-commons-cli/pkl-commons-cli.gradle.kts @@ -11,7 +11,7 @@ dependencies { exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-jdk8") exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-common") } - + implementation(libs.jansi) implementation(projects.pklCommons) testImplementation(projects.pklCommonsTest) } diff --git a/pkl-commons-cli/src/main/kotlin/org/pkl/commons/cli/CliMain.kt b/pkl-commons-cli/src/main/kotlin/org/pkl/commons/cli/CliMain.kt index eb83d17c..94ece1cd 100644 --- a/pkl-commons-cli/src/main/kotlin/org/pkl/commons/cli/CliMain.kt +++ b/pkl-commons-cli/src/main/kotlin/org/pkl/commons/cli/CliMain.kt @@ -17,6 +17,7 @@ package org.pkl.commons.cli import java.io.PrintStream import kotlin.system.exitProcess +import org.fusesource.jansi.AnsiConsole /** Building block for CLIs. Intended to be called from a `main` method. */ fun cliMain(block: () -> Unit) { @@ -27,6 +28,9 @@ fun cliMain(block: () -> Unit) { if (!message.endsWith('\n')) stream.println() } + // Setup AnsiConsole. This will automatically strip escape codes if + // the target shell doesn't appear to support them. + AnsiConsole.systemInstall() // Force `native-image` to use system proxies (which does not happen with `-D`). System.setProperty("java.net.useSystemProxies", "true") try { diff --git a/pkl-commons-test/gradle.lockfile b/pkl-commons-test/gradle.lockfile index bcf786ff..d319efc2 100644 --- a/pkl-commons-test/gradle.lockfile +++ b/pkl-commons-test/gradle.lockfile @@ -7,6 +7,7 @@ net.java.dev.jna:jna:5.6.0=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath org.apiguardian:apiguardian-api:1.1.2=apiDependenciesMetadata,compileClasspath,implementationDependenciesMetadata,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeOnlyDependenciesMetadata org.assertj:assertj-core:3.24.2=default org.assertj:assertj-core:3.26.0=compileClasspath,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath +org.fusesource.jansi:jansi:2.4.1=apiDependenciesMetadata,compileClasspath,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath org.jetbrains.intellij.deps:trove4j:1.0.20200330=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath org.jetbrains.kotlin:kotlin-compiler-embeddable:1.7.10=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath org.jetbrains.kotlin:kotlin-daemon-embeddable:1.7.10=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath diff --git a/pkl-commons-test/pkl-commons-test.gradle.kts b/pkl-commons-test/pkl-commons-test.gradle.kts index a9927f86..72f290d0 100644 --- a/pkl-commons-test/pkl-commons-test.gradle.kts +++ b/pkl-commons-test/pkl-commons-test.gradle.kts @@ -11,6 +11,7 @@ dependencies { api(libs.junitApi) api(libs.junitEngine) api(libs.junitParams) + api(libs.jansi) api(projects.pklCommons) // for convenience implementation(libs.assertj) } diff --git a/pkl-config-java/gradle.lockfile b/pkl-config-java/gradle.lockfile index b5b7a592..56e5c430 100644 --- a/pkl-config-java/gradle.lockfile +++ b/pkl-config-java/gradle.lockfile @@ -12,6 +12,7 @@ net.bytebuddy:byte-buddy:1.14.16=testCompileClasspath,testImplementationDependen net.java.dev.jna:jna:5.6.0=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeOnlyDependenciesMetadata org.assertj:assertj-core:3.26.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath +org.fusesource.jansi:jansi:2.4.1=pklCodegenJava,runtimeClasspath,testRuntimeClasspath org.graalvm.sdk:graal-sdk:23.0.2=default,pklCodegenJava,runtimeClasspath,testRuntimeClasspath org.graalvm.truffle:truffle-api:23.0.2=default,pklCodegenJava,runtimeClasspath,testRuntimeClasspath org.jetbrains.intellij.deps:trove4j:1.0.20200330=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath diff --git a/pkl-config-kotlin/gradle.lockfile b/pkl-config-kotlin/gradle.lockfile index 9c811290..048fa5cf 100644 --- a/pkl-config-kotlin/gradle.lockfile +++ b/pkl-config-kotlin/gradle.lockfile @@ -11,6 +11,7 @@ net.bytebuddy:byte-buddy:1.14.16=testCompileClasspath,testImplementationDependen net.java.dev.jna:jna:5.6.0=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeOnlyDependenciesMetadata org.assertj:assertj-core:3.26.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath +org.fusesource.jansi:jansi:2.4.1=pklCodegenKotlin,pklConfigJava,runtimeClasspath,testRuntimeClasspath org.graalvm.sdk:graal-sdk:23.0.2=default,pklCodegenKotlin,pklConfigJava,runtimeClasspath,testRuntimeClasspath org.graalvm.truffle:truffle-api:23.0.2=default,pklCodegenKotlin,pklConfigJava,runtimeClasspath,testRuntimeClasspath org.jetbrains.intellij.deps:trove4j:1.0.20200330=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath diff --git a/pkl-core/gradle.lockfile b/pkl-core/gradle.lockfile index 5e741839..9e55c0ba 100644 --- a/pkl-core/gradle.lockfile +++ b/pkl-core/gradle.lockfile @@ -13,6 +13,7 @@ org.antlr:ST4:4.3=antlr org.antlr:antlr-runtime:3.5.2=antlr org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeOnlyDependenciesMetadata org.assertj:assertj-core:3.26.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath +org.fusesource.jansi:jansi:2.4.1=compileClasspath,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath org.graalvm.sdk:graal-sdk:23.0.2=compileClasspath,default,generatorCompileClasspath,generatorImplementationDependenciesMetadata,generatorRuntimeClasspath,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath org.graalvm.truffle:truffle-api:23.0.2=compileClasspath,default,generatorCompileClasspath,generatorImplementationDependenciesMetadata,generatorRuntimeClasspath,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath org.graalvm.truffle:truffle-dsl-processor:23.0.2=annotationProcessor diff --git a/pkl-core/pkl-core.gradle.kts b/pkl-core/pkl-core.gradle.kts index 1d759b46..4d77ae74 100644 --- a/pkl-core/pkl-core.gradle.kts +++ b/pkl-core/pkl-core.gradle.kts @@ -56,6 +56,8 @@ dependencies { } implementation(libs.snakeYaml) + + implementation(libs.jansi) testImplementation(projects.pklCommonsTest) diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/StackTraceRenderer.java b/pkl-core/src/main/java/org/pkl/core/runtime/StackTraceRenderer.java index 933dc19f..9f60fe7a 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/StackTraceRenderer.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/StackTraceRenderer.java @@ -15,15 +15,23 @@ */ package org.pkl.core.runtime; +import static org.fusesource.jansi.Ansi.ansi; + import java.util.ArrayList; import java.util.List; import java.util.function.Function; +import org.fusesource.jansi.Ansi; +import org.fusesource.jansi.Ansi.Color; import org.pkl.core.StackFrame; import org.pkl.core.util.Nullable; public final class StackTraceRenderer { private final Function frameTransformer; + private static final Ansi.Color frameColor = Color.YELLOW; + private static final Ansi.Color lineNumColor = Color.BLUE; + private static final Ansi.Color repetitionColor = Color.MAGENTA; + public StackTraceRenderer(Function frameTransformer) { this.frameTransformer = frameTransformer; } @@ -40,6 +48,7 @@ public final class StackTraceRenderer { StringBuilder builder, String leftMargin, boolean isFirstElement) { + var out = ansi(builder); for (var frame : frames) { if (frame instanceof StackFrameLoop loop) { // ensure a cycle of length 1 doesn't get rendered as a loop @@ -47,20 +56,28 @@ public final class StackTraceRenderer { doRender(loop.frames, null, builder, leftMargin, isFirstElement); } else { if (!isFirstElement) { - builder.append(leftMargin).append("\n"); + out.fgBright(frameColor).a(leftMargin).reset().a("\n"); } - builder.append(leftMargin).append("┌─ ").append(loop.count).append(" repetitions of:\n"); + out.fgBright(frameColor) + .a(leftMargin) + .a("┌─ ") + .reset() + .bold() + .fg(repetitionColor) + .a(Integer.toString(loop.count)) + .reset() + .a(" repetitions of:\n"); var newLeftMargin = leftMargin + "│ "; doRender(loop.frames, null, builder, newLeftMargin, isFirstElement); if (isFirstElement) { renderHint(hint, builder, newLeftMargin); isFirstElement = false; } - builder.append(leftMargin).append("└─\n"); + out.fgBright(frameColor).a(leftMargin).a("└─").reset().a("\n"); } } else { if (!isFirstElement) { - builder.append(leftMargin).append('\n'); + out.fgBright(frameColor).a(leftMargin).reset().a('\n'); } renderFrame((StackFrame) frame, builder, leftMargin); } @@ -80,14 +97,16 @@ public final class StackTraceRenderer { private void renderHint(@Nullable String hint, StringBuilder builder, String leftMargin) { if (hint == null || hint.isEmpty()) return; + var out = ansi(builder); - builder.append('\n'); - builder.append(leftMargin); - builder.append(hint); - builder.append('\n'); + out.a('\n'); + out.fgBright(frameColor).a(leftMargin); + out.fgBright(frameColor).bold().a(hint).reset(); + out.a('\n'); } private void renderSourceLine(StackFrame frame, StringBuilder builder, String leftMargin) { + var out = ansi(builder); var originalSourceLine = frame.getSourceLines().get(0); var leadingWhitespace = VmUtils.countLeadingWhitespace(originalSourceLine); var sourceLine = originalSourceLine.strip(); @@ -98,27 +117,36 @@ public final class StackTraceRenderer { : sourceLine.length(); var prefix = frame.getStartLine() + " | "; - builder.append(leftMargin).append(prefix).append(sourceLine).append('\n'); - builder.append(leftMargin); + out.fgBright(frameColor) + .a(leftMargin) + .fgBright(lineNumColor) + .a(prefix) + .reset() + .a(sourceLine) + .a('\n'); + out.fgBright(frameColor).a(leftMargin).reset(); //noinspection StringRepeatCanBeUsed for (int i = 1; i < prefix.length() + startColumn; i++) { - builder.append(' '); + out.append(' '); } + + out.fgRed(); //noinspection StringRepeatCanBeUsed for (int i = startColumn; i <= endColumn; i++) { - builder.append('^'); + out.a('^'); } - builder.append('\n'); + out.reset().a('\n'); } private void renderSourceLocation(StackFrame frame, StringBuilder builder, String leftMargin) { - builder.append(leftMargin).append("at "); + var out = ansi(builder); + out.fgBright(frameColor).a(leftMargin).reset().a("at "); if (frame.getMemberName() != null) { - builder.append(frame.getMemberName()); + out.a(frame.getMemberName()); } else { - builder.append(""); + out.a(""); } - builder.append(" (").append(frame.getModuleUri()).append(')').append('\n'); + out.a(" (").a(frame.getModuleUri()).a(')').a('\n'); } /** diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/VmExceptionRenderer.java b/pkl-core/src/main/java/org/pkl/core/runtime/VmExceptionRenderer.java index b1a6d3ea..b06e83fa 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/VmExceptionRenderer.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/VmExceptionRenderer.java @@ -15,10 +15,14 @@ */ package org.pkl.core.runtime; +import static org.fusesource.jansi.Ansi.ansi; + import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import java.io.PrintWriter; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; +import org.fusesource.jansi.Ansi; +import org.fusesource.jansi.Ansi.Color; import org.pkl.core.Release; import org.pkl.core.util.ErrorMessages; import org.pkl.core.util.Nullable; @@ -27,6 +31,8 @@ import org.pkl.core.util.StringBuilderWriter; public final class VmExceptionRenderer { private final @Nullable StackTraceRenderer stackTraceRenderer; + private static final Ansi.Color errorColor = Color.RED; + /** * Constructs an error renderer with the given stack trace renderer. If stack trace renderer is * {@code null}, stack traces will not be included in error output. @@ -73,7 +79,8 @@ public final class VmExceptionRenderer { } private void renderException(VmException exception, StringBuilder builder) { - var header = "–– Pkl Error ––"; + var out = ansi(builder); + out.fg(errorColor).a("–– Pkl Error ––").reset(); String message; var hint = exception.getHint(); @@ -94,7 +101,7 @@ public final class VmExceptionRenderer { message = exception.getMessage(); } - builder.append(header).append('\n').append(message).append('\n'); + out.a('\n').fgBright(errorColor).a(message).reset().a('\n'); // include cause's message unless it's the same as this exception's message if (exception.getCause() != null) { diff --git a/pkl-core/src/test/kotlin/org/pkl/core/EvaluateTestsTest.kt b/pkl-core/src/test/kotlin/org/pkl/core/EvaluateTestsTest.kt index 0bfd5bb2..2b725d57 100644 --- a/pkl-core/src/test/kotlin/org/pkl/core/EvaluateTestsTest.kt +++ b/pkl-core/src/test/kotlin/org/pkl/core/EvaluateTestsTest.kt @@ -3,7 +3,6 @@ package org.pkl.core import org.pkl.commons.createTempFile import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Assertions.assertFalse -import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test import org.junit.jupiter.api.io.TempDir import org.pkl.commons.writeString @@ -11,7 +10,6 @@ import org.pkl.core.ModuleSource.* import java.nio.file.Files import java.nio.file.Path import kotlin.io.path.createFile -import kotlin.io.path.name class EvaluateTestsTest { diff --git a/pkl-core/src/test/kotlin/org/pkl/core/EvaluatorTest.kt b/pkl-core/src/test/kotlin/org/pkl/core/EvaluatorTest.kt index ff091ef9..7f30218a 100644 --- a/pkl-core/src/test/kotlin/org/pkl/core/EvaluatorTest.kt +++ b/pkl-core/src/test/kotlin/org/pkl/core/EvaluatorTest.kt @@ -429,7 +429,7 @@ class EvaluatorTest { val evaluatorBuilder = EvaluatorBuilder.preconfigured().setModuleCacheDir(cacheDir) val project = Project.load(modulePath("/org/pkl/core/project/project6/PklProject")) evaluatorBuilder.setProjectDependencies(project.dependencies).build().use { evaluator -> - assertThatCode { + assertThatCode { evaluator.evaluateOutputText(modulePath("/org/pkl/core/project/project6/globWithinDependency.pkl")) }.hasMessageContaining(""" Cannot resolve import in local dependency because scheme `modulepath` is not globbable. diff --git a/pkl-core/src/test/kotlin/org/pkl/core/project/ProjectTest.kt b/pkl-core/src/test/kotlin/org/pkl/core/project/ProjectTest.kt index 258a3a01..8835225d 100644 --- a/pkl-core/src/test/kotlin/org/pkl/core/project/ProjectTest.kt +++ b/pkl-core/src/test/kotlin/org/pkl/core/project/ProjectTest.kt @@ -149,8 +149,9 @@ class ProjectTest { .setModuleCacheDir(null) .setHttpClient(httpClient) .build() - assertThatCode { evaluator.evaluate(ModuleSource.path(projectDir.resolve("bug.pkl"))) } - .hasMessageStartingWith(""" + assertThatCode { + evaluator.evaluate(ModuleSource.path(projectDir.resolve("bug.pkl"))) + }.hasMessageStartingWith(""" –– Pkl Error –– Cannot download package `package://localhost:0/fruit@1.0.5` because the computed checksum for package metadata does not match the expected checksum. diff --git a/pkl-doc/gradle.lockfile b/pkl-doc/gradle.lockfile index 2e644be8..ade022d0 100644 --- a/pkl-doc/gradle.lockfile +++ b/pkl-doc/gradle.lockfile @@ -40,6 +40,7 @@ org.commonmark:commonmark:0.21.0=default org.commonmark:commonmark:0.22.0=compileClasspath,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath org.eclipse.jetty:jetty-util-ajax:9.4.18.v20190429=validator org.eclipse.jetty:jetty-util:9.4.18.v20190429=validator +org.fusesource.jansi:jansi:2.4.1=testCompileClasspath,testRuntimeClasspath org.graalvm.js:js:23.0.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath org.graalvm.regex:regex:23.0.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath org.graalvm.sdk:graal-sdk:23.0.2=default,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath diff --git a/pkl-executor/gradle.lockfile b/pkl-executor/gradle.lockfile index 1d68115d..32eb4024 100644 --- a/pkl-executor/gradle.lockfile +++ b/pkl-executor/gradle.lockfile @@ -6,6 +6,7 @@ net.bytebuddy:byte-buddy:1.14.16=testCompileClasspath,testImplementationDependen net.java.dev.jna:jna:5.6.0=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeOnlyDependenciesMetadata org.assertj:assertj-core:3.26.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath +org.fusesource.jansi:jansi:2.4.1=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath org.graalvm.sdk:graal-sdk:23.0.2=testRuntimeClasspath org.graalvm.truffle:truffle-api:23.0.2=testRuntimeClasspath org.jetbrains.intellij.deps:trove4j:1.0.20200330=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath diff --git a/pkl-gradle/gradle.lockfile b/pkl-gradle/gradle.lockfile index e53f409d..8602fc17 100644 --- a/pkl-gradle/gradle.lockfile +++ b/pkl-gradle/gradle.lockfile @@ -7,6 +7,7 @@ net.bytebuddy:byte-buddy:1.14.16=testCompileClasspath,testImplementationDependen net.java.dev.jna:jna:5.6.0=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeOnlyDependenciesMetadata org.assertj:assertj-core:3.26.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath +org.fusesource.jansi:jansi:2.4.1=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath org.jetbrains.intellij.deps:trove4j:1.0.20200330=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath org.jetbrains.kotlin:kotlin-compiler-embeddable:1.7.10=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath org.jetbrains.kotlin:kotlin-daemon-embeddable:1.7.10=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath diff --git a/pkl-gradle/pkl-gradle.gradle.kts b/pkl-gradle/pkl-gradle.gradle.kts index 55a03fc8..c1375a0e 100644 --- a/pkl-gradle/pkl-gradle.gradle.kts +++ b/pkl-gradle/pkl-gradle.gradle.kts @@ -74,6 +74,12 @@ gradlePluginTests { skippedGradleVersions = listOf() } +tasks.withType().configureEach { + // Disable colour output in tests + // Need additional disablement here because of how Jansi is packaged in the plugin + systemProperty("org.pkl.thirdparty.jansiAnsi.disable", "true") +} + signing { publishing.publications.withType(MavenPublication::class.java).configureEach { if (name != "library") { diff --git a/pkl-server/gradle.lockfile b/pkl-server/gradle.lockfile index b3a77fa7..9a11c2ff 100644 --- a/pkl-server/gradle.lockfile +++ b/pkl-server/gradle.lockfile @@ -6,6 +6,7 @@ net.bytebuddy:byte-buddy:1.14.16=testCompileClasspath,testImplementationDependen net.java.dev.jna:jna:5.6.0=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeOnlyDependenciesMetadata org.assertj:assertj-core:3.26.0=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath +org.fusesource.jansi:jansi:2.4.1=testCompileClasspath,testRuntimeClasspath org.graalvm.sdk:graal-sdk:23.0.2=compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath org.graalvm.truffle:truffle-api:23.0.2=compileClasspath,default,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath org.jetbrains.intellij.deps:trove4j:1.0.20200330=kotlinCompilerClasspath,kotlinKlibCommonizerClasspath