You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by gn...@apache.org on 2024/01/08 10:37:15 UTC
(maven) branch master updated: [MNG-7995] Switch to JLine to provide line editing (#1279)
This is an automated email from the ASF dual-hosted git repository.
gnodet pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven.git
The following commit(s) were added to refs/heads/master by this push:
new 782e8679bf [MNG-7995] Switch to JLine to provide line editing (#1279)
782e8679bf is described below
commit 782e8679bf487bcd6ad6ef36d1d1e92b00d418a4
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Mon Jan 8 11:37:09 2024 +0100
[MNG-7995] Switch to JLine to provide line editing (#1279)
---
apache-maven/pom.xml | 4 +-
.../licenses/unrecognized-jline-3.25.0.txt | 35 +
.../apache/maven/api/services/MessageBuilder.java | 107 ++-
.../maven/api/services/MessageBuilderFactory.java | 12 +-
.../maven/internal/impl/DefaultMessageBuilder.java | 100 +--
.../impl/DefaultMessageBuilderFactory.java | 6 +-
.../main/resources/META-INF/maven/extension.xml | 4 +
maven-embedder/pom.xml | 19 +-
.../main/java/org/apache/maven/cli/CLIManager.java | 2 +-
.../org/apache/maven/cli/CLIReportingUtils.java | 2 +-
.../main/java/org/apache/maven/cli/MavenCli.java | 8 +-
.../maven/cli/jansi/JansiMessageBuilder.java | 171 ----
.../cli/jansi/JansiMessageBuilderFactory.java | 55 --
.../org/apache/maven/cli/jansi/MessageUtils.java | 201 -----
.../java/org/apache/maven/cli/jansi/Style.java | 148 ----
.../cli/jline/JLineMessageBuilderFactory.java | 294 +++++++
.../org/apache/maven/cli/jline/MessageUtils.java | 101 +++
.../transfer/AbstractMavenTransferListener.java | 45 +-
.../cli/transfer/ConsoleMavenTransferListener.java | 6 +-
.../apache/maven/cli/transfer/FileSizeFormat.java | 34 +
.../src/main/java/org/fusesource/jansi/Ansi.java | 952 +++++++++++++++++++++
.../java/org/apache/maven/cli/MavenCliTest.java | 8 +-
.../maven/cli/event/ExecutionEventLoggerTest.java | 6 +-
.../transfer/ConsoleMavenTransferListenerTest.java | 2 +
.../java/org/slf4j/impl/MavenSimpleLogger.java | 2 +-
.../java/org/slf4j/impl/MavenSimpleLoggerTest.java | 16 +
pom.xml | 17 +-
27 files changed, 1601 insertions(+), 756 deletions(-)
diff --git a/apache-maven/pom.xml b/apache-maven/pom.xml
index 5f47f9dc57..eb198048e4 100644
--- a/apache-maven/pom.xml
+++ b/apache-maven/pom.xml
@@ -98,8 +98,8 @@ under the License.
<artifactId>maven-slf4j-provider</artifactId>
</dependency>
<dependency>
- <groupId>org.fusesource.jansi</groupId>
- <artifactId>jansi</artifactId>
+ <groupId>org.jline</groupId>
+ <artifactId>jline</artifactId>
</dependency>
<!-- DI Runtime -->
diff --git a/apache-maven/src/main/appended-resources/licenses/unrecognized-jline-3.25.0.txt b/apache-maven/src/main/appended-resources/licenses/unrecognized-jline-3.25.0.txt
new file mode 100644
index 0000000000..b62fe45716
--- /dev/null
+++ b/apache-maven/src/main/appended-resources/licenses/unrecognized-jline-3.25.0.txt
@@ -0,0 +1,35 @@
+Copyright (c) 2002-2023, the original author or authors.
+All rights reserved.
+
+https://opensource.org/licenses/BSD-3-Clause
+
+Redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the following
+conditions are met:
+
+Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with
+the distribution.
+
+Neither the name of JLine nor the names of its contributors
+may be used to endorse or promote products derived from this
+software without specific prior written permission.
+
+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.
+
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/MessageBuilder.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/MessageBuilder.java
index d00ad8d954..8a996032e2 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/MessageBuilder.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/MessageBuilder.java
@@ -26,7 +26,7 @@ import org.apache.maven.api.annotations.Nonnull;
* @since 4.0.0
* @see MessageBuilderFactory
*/
-public interface MessageBuilder {
+public interface MessageBuilder extends Appendable {
/**
* Append message content in trace style.
@@ -36,7 +36,9 @@ public interface MessageBuilder {
* @return the current builder
*/
@Nonnull
- MessageBuilder trace(Object message);
+ default MessageBuilder trace(Object message) {
+ return style(".trace:-bold,f:magenta", message);
+ }
/**
* Append message content in debug style.
@@ -46,7 +48,9 @@ public interface MessageBuilder {
* @return the current builder
*/
@Nonnull
- MessageBuilder debug(Object message);
+ default MessageBuilder debug(Object message) {
+ return style(".debug:-bold,f:cyan", message);
+ }
/**
* Append message content in info style.
@@ -56,7 +60,9 @@ public interface MessageBuilder {
* @return the current builder
*/
@Nonnull
- MessageBuilder info(Object message);
+ default MessageBuilder info(Object message) {
+ return style(".info:-bold,f:blue", message);
+ }
/**
* Append message content in warning style.
@@ -66,7 +72,9 @@ public interface MessageBuilder {
* @return the current builder
*/
@Nonnull
- MessageBuilder warning(Object message);
+ default MessageBuilder warning(Object message) {
+ return style(".warning:-bold,f:yellow", message);
+ }
/**
* Append message content in error style.
@@ -76,7 +84,9 @@ public interface MessageBuilder {
* @return the current builder
*/
@Nonnull
- MessageBuilder error(Object message);
+ default MessageBuilder error(Object message) {
+ return style(".error:-bold,f:red", message);
+ }
/**
* Append message content in success style.
@@ -86,7 +96,9 @@ public interface MessageBuilder {
* @return the current builder
*/
@Nonnull
- MessageBuilder success(Object message);
+ default MessageBuilder success(Object message) {
+ return style(".success:-bold,f:green", message);
+ }
/**
* Append message content in failure style.
@@ -96,7 +108,9 @@ public interface MessageBuilder {
* @return the current builder
*/
@Nonnull
- MessageBuilder failure(Object message);
+ default MessageBuilder failure(Object message) {
+ return style(".failure:-bold,f:red", message);
+ }
/**
* Append message content in strong style.
@@ -106,7 +120,9 @@ public interface MessageBuilder {
* @return the current builder
*/
@Nonnull
- MessageBuilder strong(Object message);
+ default MessageBuilder strong(Object message) {
+ return style(".strong:-bold", message);
+ }
/**
* Append message content in mojo style.
@@ -116,7 +132,9 @@ public interface MessageBuilder {
* @return the current builder
*/
@Nonnull
- MessageBuilder mojo(Object message);
+ default MessageBuilder mojo(Object message) {
+ return style(".mojo:-f:green", message);
+ }
/**
* Append message content in project style.
@@ -126,11 +144,35 @@ public interface MessageBuilder {
* @return the current builder
*/
@Nonnull
- MessageBuilder project(Object message);
+ default MessageBuilder project(Object message) {
+ return style(".project:-f:cyan", message);
+ }
+
+ @Nonnull
+ default MessageBuilder style(String style, Object message) {
+ return style(style).a(message).resetStyle();
+ }
+
+ MessageBuilder style(String style);
+
+ MessageBuilder resetStyle();
//
// message building methods modelled after Ansi methods
//
+
+ @Nonnull
+ @Override
+ MessageBuilder append(CharSequence cs);
+
+ @Nonnull
+ @Override
+ MessageBuilder append(CharSequence cs, int start, int end);
+
+ @Nonnull
+ @Override
+ MessageBuilder append(char c);
+
/**
* Append content to the message buffer.
*
@@ -140,7 +182,9 @@ public interface MessageBuilder {
* @return the current builder
*/
@Nonnull
- MessageBuilder a(char[] value, int offset, int len);
+ default MessageBuilder a(char[] value, int offset, int len) {
+ return append(String.valueOf(value, offset, len));
+ }
/**
* Append content to the message buffer.
@@ -149,7 +193,9 @@ public interface MessageBuilder {
* @return the current builder
*/
@Nonnull
- MessageBuilder a(char[] value);
+ default MessageBuilder a(char[] value) {
+ return append(String.valueOf(value));
+ }
/**
* Append content to the message buffer.
@@ -160,7 +206,9 @@ public interface MessageBuilder {
* @return the current builder
*/
@Nonnull
- MessageBuilder a(CharSequence value, int start, int end);
+ default MessageBuilder a(CharSequence value, int start, int end) {
+ return append(value, start, end);
+ }
/**
* Append content to the message buffer.
@@ -169,7 +217,9 @@ public interface MessageBuilder {
* @return the current builder
*/
@Nonnull
- MessageBuilder a(CharSequence value);
+ default MessageBuilder a(CharSequence value) {
+ return append(value);
+ }
/**
* Append content to the message buffer.
@@ -178,7 +228,9 @@ public interface MessageBuilder {
* @return the current builder
*/
@Nonnull
- MessageBuilder a(Object value);
+ default MessageBuilder a(Object value) {
+ return append(String.valueOf(value));
+ }
/**
* Append newline to the message buffer.
@@ -186,7 +238,9 @@ public interface MessageBuilder {
* @return the current builder
*/
@Nonnull
- MessageBuilder newline();
+ default MessageBuilder newline() {
+ return append(System.lineSeparator());
+ }
/**
* Append formatted content to the buffer.
@@ -197,20 +251,23 @@ public interface MessageBuilder {
* @return the current builder
*/
@Nonnull
- MessageBuilder format(String pattern, Object... args);
+ default MessageBuilder format(String pattern, Object... args) {
+ return append(String.format(pattern, args));
+ }
/**
- * Return the built message.
+ * Set the buffer length.
*
- * @return the message
+ * @param length the new length
+ * @return the current builder
*/
- @Nonnull
- String build();
+ MessageBuilder setLength(int length);
/**
- * Set the buffer length.
+ * Return the built message.
*
- * @param length the new length
+ * @return the message
*/
- void setLength(int length);
+ @Nonnull
+ String build();
}
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/MessageBuilderFactory.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/MessageBuilderFactory.java
index ac4691ecfb..9a595ae110 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/MessageBuilderFactory.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/MessageBuilderFactory.java
@@ -48,21 +48,11 @@ public interface MessageBuilderFactory extends Service {
@Nonnull
MessageBuilder builder();
- /**
- * Creates a new message builder backed by the given string builder.
- * @param stringBuilder a string builder
- * @return a new message builder
- */
- @Nonnull
- MessageBuilder builder(@Nonnull StringBuilder stringBuilder);
-
/**
* Creates a new message builder of the specified size.
* @param size the initial size of the message builder buffer
* @return a new message builder
*/
@Nonnull
- default MessageBuilder builder(int size) {
- return builder(new StringBuilder(size));
- }
+ MessageBuilder builder(int size);
}
diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultMessageBuilder.java b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultMessageBuilder.java
index 08f28d9553..692b1e37e5 100644
--- a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultMessageBuilder.java
+++ b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultMessageBuilder.java
@@ -36,111 +36,36 @@ public class DefaultMessageBuilder implements MessageBuilder {
}
@Override
- @Nonnull
- public MessageBuilder trace(Object o) {
- return a(o);
- }
-
- @Override
- @Nonnull
- public MessageBuilder debug(Object o) {
- return a(o);
- }
-
- @Override
- @Nonnull
- public MessageBuilder info(Object o) {
- return a(o);
- }
-
- @Override
- @Nonnull
- public MessageBuilder warning(Object o) {
- return a(o);
- }
-
- @Override
- @Nonnull
- public MessageBuilder error(Object o) {
- return a(o);
- }
-
- @Override
- @Nonnull
- public MessageBuilder success(Object o) {
- return a(o);
- }
-
- @Override
- @Nonnull
- public MessageBuilder failure(Object o) {
- return a(o);
- }
-
- @Override
- @Nonnull
- public MessageBuilder strong(Object o) {
- return a(o);
- }
-
- @Override
- @Nonnull
- public MessageBuilder mojo(Object o) {
- return a(o);
- }
-
- @Override
- @Nonnull
- public MessageBuilder project(Object o) {
- return a(o);
- }
-
- @Override
- @Nonnull
- public MessageBuilder a(char[] chars, int i, int i1) {
- buffer.append(chars, i, i1);
- return this;
- }
-
- @Override
- @Nonnull
- public MessageBuilder a(char[] chars) {
- buffer.append(chars);
+ public MessageBuilder style(String style) {
return this;
}
@Override
- @Nonnull
- public MessageBuilder a(CharSequence charSequence, int i, int i1) {
- buffer.append(charSequence, i, i1);
+ public MessageBuilder resetStyle() {
return this;
}
@Override
- @Nonnull
- public MessageBuilder a(CharSequence charSequence) {
- buffer.append(charSequence);
+ public MessageBuilder append(CharSequence cs) {
+ buffer.append(cs);
return this;
}
@Override
- @Nonnull
- public MessageBuilder a(Object o) {
- buffer.append(o);
+ public MessageBuilder append(CharSequence cs, int start, int end) {
+ buffer.append(cs, start, end);
return this;
}
@Override
- @Nonnull
- public MessageBuilder newline() {
- buffer.append(System.getProperty("line.separator"));
+ public MessageBuilder append(char c) {
+ buffer.append(c);
return this;
}
@Override
- @Nonnull
- public MessageBuilder format(String s, Object... objects) {
- buffer.append(String.format(s, objects));
+ public MessageBuilder setLength(int length) {
+ buffer.setLength(length);
return this;
}
@@ -154,9 +79,4 @@ public class DefaultMessageBuilder implements MessageBuilder {
public String toString() {
return build();
}
-
- @Override
- public void setLength(int length) {
- buffer.setLength(length);
- }
}
diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultMessageBuilderFactory.java b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultMessageBuilderFactory.java
index ea4c94f492..bb2f6c982e 100644
--- a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultMessageBuilderFactory.java
+++ b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultMessageBuilderFactory.java
@@ -22,8 +22,6 @@ import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
-import java.util.Objects;
-
import org.apache.maven.api.annotations.Experimental;
import org.apache.maven.api.annotations.Nonnull;
import org.apache.maven.api.services.MessageBuilder;
@@ -57,7 +55,7 @@ public class DefaultMessageBuilderFactory implements MessageBuilderFactory {
@Override
@Nonnull
- public MessageBuilder builder(@Nonnull StringBuilder stringBuilder) {
- return new DefaultMessageBuilder(Objects.requireNonNull(stringBuilder));
+ public MessageBuilder builder(int size) {
+ return new DefaultMessageBuilder(new StringBuilder(size));
}
}
diff --git a/maven-core/src/main/resources/META-INF/maven/extension.xml b/maven-core/src/main/resources/META-INF/maven/extension.xml
index 1823336d83..8a87ea31f3 100644
--- a/maven-core/src/main/resources/META-INF/maven/extension.xml
+++ b/maven-core/src/main/resources/META-INF/maven/extension.xml
@@ -99,6 +99,10 @@ under the License.
<exportedPackage>org.codehaus.plexus.logging</exportedPackage>
<exportedPackage>org.codehaus.plexus.personality</exportedPackage>
+ <!-- plexus-interactivity-api -->
+ <exportedPackage>org.codehaus.plexus.components.interactivity</exportedPackage>
+ <exportedPackage>org.fusesource.jansi.Ansi</exportedPackage>
+
<!-- javax.inject (JSR-330) -->
<exportedPackage>javax.inject.*</exportedPackage>
<!-- javax.enterprise.inject (JSR-299): Must never be exported if needed at plugin level, plugin adds it
diff --git a/maven-embedder/pom.xml b/maven-embedder/pom.xml
index be4ee0bee3..fcda8edbd4 100644
--- a/maven-embedder/pom.xml
+++ b/maven-embedder/pom.xml
@@ -88,6 +88,16 @@ under the License.
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-interpolation</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.codehaus.plexus</groupId>
+ <artifactId>plexus-interactivity-api</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>*</groupId>
+ <artifactId>*</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
@@ -97,11 +107,6 @@ under the License.
<artifactId>commons-cli</artifactId>
</dependency>
- <dependency>
- <groupId>org.fusesource.jansi</groupId>
- <artifactId>jansi</artifactId>
- <optional>true</optional>
- </dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
@@ -112,6 +117,10 @@ under the License.
<artifactId>slf4j-simple</artifactId>
<optional>true</optional>
</dependency>
+ <dependency>
+ <groupId>org.jline</groupId>
+ <artifactId>jline</artifactId>
+ </dependency>
<dependency>
<groupId>javax.inject</groupId>
diff --git a/maven-embedder/src/main/java/org/apache/maven/cli/CLIManager.java b/maven-embedder/src/main/java/org/apache/maven/cli/CLIManager.java
index baf1c2127c..cc356cca3c 100644
--- a/maven-embedder/src/main/java/org/apache/maven/cli/CLIManager.java
+++ b/maven-embedder/src/main/java/org/apache/maven/cli/CLIManager.java
@@ -28,7 +28,7 @@ import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
-import org.apache.maven.cli.jansi.MessageUtils;
+import org.apache.maven.cli.jline.MessageUtils;
/**
*/
diff --git a/maven-embedder/src/main/java/org/apache/maven/cli/CLIReportingUtils.java b/maven-embedder/src/main/java/org/apache/maven/cli/CLIReportingUtils.java
index 5fd319871d..a5538ca93c 100644
--- a/maven-embedder/src/main/java/org/apache/maven/cli/CLIReportingUtils.java
+++ b/maven-embedder/src/main/java/org/apache/maven/cli/CLIReportingUtils.java
@@ -25,7 +25,7 @@ import java.util.Date;
import java.util.Locale;
import java.util.Properties;
-import org.apache.maven.cli.jansi.MessageUtils;
+import org.apache.maven.cli.jline.MessageUtils;
import org.apache.maven.utils.Os;
import org.slf4j.Logger;
diff --git a/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java b/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java
index 67e458d829..a2b51a9c47 100644
--- a/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java
+++ b/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java
@@ -58,8 +58,8 @@ import org.apache.maven.cli.event.ExecutionEventLogger;
import org.apache.maven.cli.internal.BootstrapCoreExtensionManager;
import org.apache.maven.cli.internal.extension.io.CoreExtensionsStaxReader;
import org.apache.maven.cli.internal.extension.model.CoreExtension;
-import org.apache.maven.cli.jansi.JansiMessageBuilderFactory;
-import org.apache.maven.cli.jansi.MessageUtils;
+import org.apache.maven.cli.jline.JLineMessageBuilderFactory;
+import org.apache.maven.cli.jline.MessageUtils;
import org.apache.maven.cli.logging.Slf4jConfiguration;
import org.apache.maven.cli.logging.Slf4jConfigurationFactory;
import org.apache.maven.cli.logging.Slf4jLoggerManager;
@@ -188,7 +188,7 @@ public class MavenCli {
// This supports painless invocation by the Verifier during embedded execution of the core ITs
public MavenCli(ClassWorld classWorld) {
this.classWorld = classWorld;
- this.messageBuilderFactory = new JansiMessageBuilderFactory();
+ this.messageBuilderFactory = new JLineMessageBuilderFactory();
}
public static void main(String[] args) {
@@ -1676,7 +1676,7 @@ public class MavenCli {
//
protected TransferListener getConsoleTransferListener(boolean printResourceNames) {
- return new ConsoleMavenTransferListener(System.out, printResourceNames);
+ return new ConsoleMavenTransferListener(messageBuilderFactory, System.out, printResourceNames);
}
protected TransferListener getBatchTransferListener() {
diff --git a/maven-embedder/src/main/java/org/apache/maven/cli/jansi/JansiMessageBuilder.java b/maven-embedder/src/main/java/org/apache/maven/cli/jansi/JansiMessageBuilder.java
deleted file mode 100644
index be51f309b2..0000000000
--- a/maven-embedder/src/main/java/org/apache/maven/cli/jansi/JansiMessageBuilder.java
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.maven.cli.jansi;
-
-import org.apache.maven.api.annotations.Experimental;
-import org.apache.maven.api.annotations.Nonnull;
-import org.apache.maven.api.services.MessageBuilder;
-import org.fusesource.jansi.Ansi;
-
-@Experimental
-public class JansiMessageBuilder implements MessageBuilder {
- private final Ansi ansi;
- private StringBuilder sb;
-
- @SuppressWarnings("magicnumber")
- public JansiMessageBuilder() {
- this.sb = new StringBuilder(80);
- this.ansi = Ansi.ansi();
- }
-
- public JansiMessageBuilder(StringBuilder sb) {
- this.sb = sb;
- this.ansi = Ansi.ansi(sb);
- }
-
- @Override
- @Nonnull
- public MessageBuilder trace(Object o) {
- return style(Style.TRACE, o);
- }
-
- @Override
- @Nonnull
- public MessageBuilder debug(Object o) {
- return style(Style.DEBUG, o);
- }
-
- @Override
- @Nonnull
- public MessageBuilder info(Object o) {
- return style(Style.INFO, o);
- }
-
- @Override
- @Nonnull
- public MessageBuilder warning(Object o) {
- return style(Style.WARNING, o);
- }
-
- @Override
- @Nonnull
- public MessageBuilder error(Object o) {
- return style(Style.ERROR, o);
- }
-
- @Override
- @Nonnull
- public MessageBuilder success(Object o) {
- return style(Style.SUCCESS, o);
- }
-
- @Override
- @Nonnull
- public MessageBuilder failure(Object o) {
- return style(Style.FAILURE, o);
- }
-
- @Override
- @Nonnull
- public MessageBuilder strong(Object o) {
- return style(Style.STRONG, o);
- }
-
- @Override
- @Nonnull
- public MessageBuilder mojo(Object o) {
- return style(Style.MOJO, o);
- }
-
- @Override
- @Nonnull
- public MessageBuilder project(Object o) {
- return style(Style.PROJECT, o);
- }
-
- private MessageBuilder style(Style style, Object o) {
- style.apply(ansi).a(o).reset();
- return this;
- }
-
- @Override
- @Nonnull
- public MessageBuilder a(char[] chars, int i, int i1) {
- ansi.a(chars, i, i1);
- return this;
- }
-
- @Override
- @Nonnull
- public MessageBuilder a(char[] chars) {
- ansi.a(chars);
- return this;
- }
-
- @Override
- @Nonnull
- public MessageBuilder a(CharSequence charSequence, int i, int i1) {
- ansi.a(charSequence, i, i1);
- return this;
- }
-
- @Override
- @Nonnull
- public MessageBuilder a(CharSequence charSequence) {
- ansi.a(charSequence);
- return this;
- }
-
- @Override
- @Nonnull
- public MessageBuilder a(Object o) {
- ansi.a(o);
- return this;
- }
-
- @Override
- @Nonnull
- public MessageBuilder newline() {
- ansi.newline();
- return this;
- }
-
- @Override
- @Nonnull
- public MessageBuilder format(String s, Object... objects) {
- ansi.format(s, objects);
- return this;
- }
-
- @Override
- @Nonnull
- public String build() {
- return ansi.toString();
- }
-
- @Override
- public String toString() {
- return build();
- }
-
- @Override
- public void setLength(int length) {
- sb.setLength(length);
- }
-}
diff --git a/maven-embedder/src/main/java/org/apache/maven/cli/jansi/JansiMessageBuilderFactory.java b/maven-embedder/src/main/java/org/apache/maven/cli/jansi/JansiMessageBuilderFactory.java
deleted file mode 100644
index c7d3225183..0000000000
--- a/maven-embedder/src/main/java/org/apache/maven/cli/jansi/JansiMessageBuilderFactory.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.maven.cli.jansi;
-
-import javax.inject.Named;
-import javax.inject.Singleton;
-
-import org.apache.maven.api.annotations.Experimental;
-import org.apache.maven.api.annotations.Nonnull;
-import org.apache.maven.api.services.MessageBuilder;
-import org.apache.maven.api.services.MessageBuilderFactory;
-
-@Experimental
-@Named
-@Singleton
-public class JansiMessageBuilderFactory implements MessageBuilderFactory {
-
- @Override
- public boolean isColorEnabled() {
- return MessageUtils.isColorEnabled();
- }
-
- @Override
- public int getTerminalWidth() {
- return MessageUtils.getTerminalWidth();
- }
-
- @Override
- @Nonnull
- public MessageBuilder builder() {
- return builder(new StringBuilder());
- }
-
- @Override
- @Nonnull
- public MessageBuilder builder(@Nonnull StringBuilder stringBuilder) {
- return MessageUtils.builder(stringBuilder);
- }
-}
diff --git a/maven-embedder/src/main/java/org/apache/maven/cli/jansi/MessageUtils.java b/maven-embedder/src/main/java/org/apache/maven/cli/jansi/MessageUtils.java
deleted file mode 100644
index 3d0a5d41f7..0000000000
--- a/maven-embedder/src/main/java/org/apache/maven/cli/jansi/MessageUtils.java
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.maven.cli.jansi;
-
-import org.apache.maven.api.services.MessageBuilder;
-import org.apache.maven.internal.impl.DefaultMessageBuilder;
-import org.fusesource.jansi.Ansi;
-import org.fusesource.jansi.AnsiConsole;
-import org.fusesource.jansi.AnsiMode;
-
-/**
- * Colored message utils, to manage colors. This is the core implementation of the
- * {@link JansiMessageBuilderFactory} and {@link JansiMessageBuilder} classes.
- * This class should not be used outside of maven-embedder and the public
- * {@link org.apache.maven.api.services.MessageBuilderFactory} should be used instead.
- * <p>
- * Internally, <a href="http://fusesource.github.io/jansi/">Jansi</a> is used to render
- * <a href="https://en.wikipedia.org/wiki/ANSI_escape_code#Colors">ANSI colors</a> on any platform.
- * <p>
- *
- * @see MessageBuilder
- * @see org.apache.maven.api.services.MessageBuilderFactory
- * @see JansiMessageBuilderFactory
- * @see JansiMessageBuilder
- * @since 4.0.0
- */
-public class MessageUtils {
- private static final boolean JANSI;
-
- /** Reference to the JVM shutdown hook, if registered */
- private static Thread shutdownHook;
-
- /** Synchronization monitor for the "uninstall" */
- private static final Object STARTUP_SHUTDOWN_MONITOR = new Object();
-
- static {
- boolean jansi = true;
- try {
- // Jansi is provided by Maven core since 3.5.0
- Class.forName("org.fusesource.jansi.Ansi");
- } catch (ClassNotFoundException cnfe) {
- jansi = false;
- }
- JANSI = jansi;
- }
-
- /**
- * Install color support.
- * This method is called by Maven core, and calling it is not necessary in plugins.
- */
- public static void systemInstall() {
- if (JANSI) {
- AnsiConsole.systemInstall();
- }
- }
-
- /**
- * Undo a previous {@link #systemInstall()}. If {@link #systemInstall()} was called
- * multiple times, {@link #systemUninstall()} must be called call the same number of times before
- * it is actually uninstalled.
- */
- public static void systemUninstall() {
- synchronized (STARTUP_SHUTDOWN_MONITOR) {
- doSystemUninstall();
-
- // hook can only set when Jansi is true
- if (shutdownHook != null) {
- try {
- Runtime.getRuntime().removeShutdownHook(shutdownHook);
- } catch (IllegalStateException ex) {
- // ignore - VM is already shutting down
- }
- }
- }
- }
-
- private static void doSystemUninstall() {
- if (JANSI) {
- AnsiConsole.systemUninstall();
- }
- }
-
- /**
- * Enables message color (if Jansi is available).
- * @param flag to enable Jansi
- */
- public static void setColorEnabled(boolean flag) {
- if (JANSI) {
- AnsiConsole.out().setMode(flag ? AnsiMode.Force : AnsiMode.Strip);
- Ansi.setEnabled(flag);
- System.setProperty(
- AnsiConsole.JANSI_MODE, flag ? AnsiConsole.JANSI_MODE_FORCE : AnsiConsole.JANSI_MODE_STRIP);
- boolean installed = AnsiConsole.isInstalled();
- while (AnsiConsole.isInstalled()) {
- AnsiConsole.systemUninstall();
- }
- if (installed) {
- AnsiConsole.systemInstall();
- }
- }
- }
-
- /**
- * Is message color enabled: requires Jansi available (through Maven) and the color has not been disabled.
- * @return whether colored messages are enabled
- */
- public static boolean isColorEnabled() {
- return JANSI ? Ansi.isEnabled() : false;
- }
-
- /**
- * Create a default message buffer.
- * @return a new buffer
- */
- public static MessageBuilder builder() {
- return builder(new StringBuilder());
- }
-
- /**
- * Create a message buffer with an internal buffer of defined size.
- * @param size size of the buffer
- * @return a new buffer
- */
- public static MessageBuilder builder(int size) {
- return builder(new StringBuilder(size));
- }
-
- /**
- * Create a message buffer with defined String builder.
- * @param builder initial content of the message buffer
- * @return a new buffer
- */
- public static MessageBuilder builder(StringBuilder builder) {
- return JANSI && isColorEnabled() ? new JansiMessageBuilder(builder) : new DefaultMessageBuilder(builder);
- }
-
- /**
- * Remove any ANSI code from a message (colors or other escape sequences).
- * @param msg message eventually containing ANSI codes
- * @return the message with ANSI codes removed
- */
- public static String stripAnsiCodes(String msg) {
- return msg.replaceAll("\u001B\\[[;\\d]*[ -/]*[@-~]", "");
- }
-
- /**
- * Register a shutdown hook with the JVM runtime, uninstalling Ansi support on
- * JVM shutdown unless is has already been uninstalled at that time.
- * <p>Delegates to {@link #doSystemUninstall()} for the actual uninstall procedure
- *
- * @see Runtime#addShutdownHook(Thread)
- * @see MessageUtils#systemUninstall()
- * @see #doSystemUninstall()
- */
- public static void registerShutdownHook() {
- if (JANSI && shutdownHook == null) {
- // No shutdown hook registered yet.
- shutdownHook = new Thread() {
- @Override
- public void run() {
- synchronized (STARTUP_SHUTDOWN_MONITOR) {
- while (AnsiConsole.isInstalled()) {
- doSystemUninstall();
- }
- }
- }
- };
- Runtime.getRuntime().addShutdownHook(shutdownHook);
- }
- }
-
- /**
- * Get the terminal width or -1 if the width cannot be determined.
- *
- * @return the terminal width
- */
- public static int getTerminalWidth() {
- if (JANSI) {
- int width = AnsiConsole.getTerminalWidth();
- return width > 0 ? width : -1;
- } else {
- return -1;
- }
- }
-}
diff --git a/maven-embedder/src/main/java/org/apache/maven/cli/jansi/Style.java b/maven-embedder/src/main/java/org/apache/maven/cli/jansi/Style.java
deleted file mode 100644
index 16ae2ad428..0000000000
--- a/maven-embedder/src/main/java/org/apache/maven/cli/jansi/Style.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.maven.cli.jansi;
-
-import java.util.Locale;
-
-import org.fusesource.jansi.Ansi;
-import org.fusesource.jansi.Ansi.Color;
-
-/**
- * Configurable message styles.
- * @since 4.0.0
- */
-enum Style {
- TRACE("bold,magenta"),
- DEBUG("bold,cyan"),
- INFO("bold,blue"),
- WARNING("bold,yellow"),
- ERROR("bold,red"),
- SUCCESS("bold,green"),
- FAILURE("bold,red"),
- STRONG("bold"),
- MOJO("green"),
- PROJECT("cyan");
-
- private final boolean bold;
-
- private final boolean bright;
-
- private final Color color;
-
- private final boolean bgBright;
-
- private final Color bgColor;
-
- Style(String defaultValue) {
- boolean currentBold = false;
- boolean currentBright = false;
- Color currentColor = null;
- boolean currentBgBright = false;
- Color currentBgColor = null;
-
- String value = System.getProperty("style." + name().toLowerCase(Locale.ENGLISH), defaultValue)
- .toLowerCase(Locale.ENGLISH);
-
- for (String token : value.split(",")) {
- if ("bold".equals(token)) {
- currentBold = true;
- } else if (token.startsWith("bg")) {
- token = token.substring(2);
- if (token.startsWith("bright")) {
- currentBgBright = true;
- token = token.substring(6);
- }
- currentBgColor = toColor(token);
- } else {
- if (token.startsWith("bright")) {
- currentBright = true;
- token = token.substring(6);
- }
- currentColor = toColor(token);
- }
- }
-
- this.bold = currentBold;
- this.bright = currentBright;
- this.color = currentColor;
- this.bgBright = currentBgBright;
- this.bgColor = currentBgColor;
- }
-
- private static Color toColor(String token) {
- for (Color color : Color.values()) {
- if (color.toString().equalsIgnoreCase(token)) {
- return color;
- }
- }
- return null;
- }
-
- Ansi apply(Ansi ansi) {
- if (bold) {
- ansi.bold();
- }
- if (color != null) {
- if (bright) {
- ansi.fgBright(color);
- } else {
- ansi.fg(color);
- }
- }
- if (bgColor != null) {
- if (bgBright) {
- ansi.bgBright(bgColor);
- } else {
- ansi.bg(bgColor);
- }
- }
- return ansi;
- }
-
- @Override
- public String toString() {
- if (!bold && color == null && bgColor == null) {
- return name();
- }
- StringBuilder sb = new StringBuilder(name() + '=');
- if (bold) {
- sb.append("bold");
- }
- if (color != null) {
- if (sb.length() > 0) {
- sb.append(',');
- }
- if (bright) {
- sb.append("bright");
- }
- sb.append(color.name());
- }
- if (bgColor != null) {
- if (sb.length() > 0) {
- sb.append(',');
- }
- sb.append("bg");
- if (bgBright) {
- sb.append("bright");
- }
- sb.append(bgColor.name());
- }
- return sb.toString();
- }
-}
diff --git a/maven-embedder/src/main/java/org/apache/maven/cli/jline/JLineMessageBuilderFactory.java b/maven-embedder/src/main/java/org/apache/maven/cli/jline/JLineMessageBuilderFactory.java
new file mode 100644
index 0000000000..139b9bf4a4
--- /dev/null
+++ b/maven-embedder/src/main/java/org/apache/maven/cli/jline/JLineMessageBuilderFactory.java
@@ -0,0 +1,294 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.cli.jline;
+
+import javax.annotation.Priority;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.maven.api.annotations.Experimental;
+import org.apache.maven.api.services.MessageBuilder;
+import org.apache.maven.api.services.MessageBuilderFactory;
+import org.codehaus.plexus.components.interactivity.InputHandler;
+import org.codehaus.plexus.components.interactivity.OutputHandler;
+import org.codehaus.plexus.components.interactivity.Prompter;
+import org.codehaus.plexus.components.interactivity.PrompterException;
+import org.jline.utils.AttributedStringBuilder;
+import org.jline.utils.AttributedStyle;
+import org.jline.utils.StyleResolver;
+
+import static org.jline.utils.AttributedStyle.DEFAULT;
+
+@Experimental
+@Named
+@Singleton
+@Priority(10)
+public class JLineMessageBuilderFactory implements MessageBuilderFactory, Prompter, InputHandler, OutputHandler {
+
+ private final StyleResolver resolver;
+
+ public JLineMessageBuilderFactory() {
+ this.resolver = new MavenStyleResolver();
+ }
+
+ @Override
+ public boolean isColorEnabled() {
+ return false;
+ }
+
+ @Override
+ public int getTerminalWidth() {
+ return MessageUtils.getTerminalWidth();
+ }
+
+ @Override
+ public MessageBuilder builder() {
+ return new JlineMessageBuilder();
+ }
+
+ @Override
+ public MessageBuilder builder(int size) {
+ return new JlineMessageBuilder(size);
+ }
+
+ @Override
+ public String readLine() throws IOException {
+ return doPrompt(null, true);
+ }
+
+ @Override
+ public String readPassword() throws IOException {
+ return doPrompt(null, true);
+ }
+
+ @Override
+ public List<String> readMultipleLines() throws IOException {
+ List<String> lines = new ArrayList<>();
+ for (String line = this.readLine(); line != null && !line.isEmpty(); line = readLine()) {
+ lines.add(line);
+ }
+ return lines;
+ }
+
+ @Override
+ public void write(String line) throws IOException {
+ doDisplay(line);
+ }
+
+ @Override
+ public void writeLine(String line) throws IOException {
+ doDisplay(line + System.lineSeparator());
+ }
+
+ @Override
+ public String prompt(String message) throws PrompterException {
+ return prompt(message, null, null);
+ }
+
+ @Override
+ public String prompt(String message, String defaultReply) throws PrompterException {
+ return prompt(message, null, defaultReply);
+ }
+
+ @Override
+ public String prompt(String message, List possibleValues) throws PrompterException {
+ return prompt(message, possibleValues, null);
+ }
+
+ @Override
+ public String prompt(String message, List possibleValues, String defaultReply) throws PrompterException {
+ return doPrompt(message, possibleValues, defaultReply, false);
+ }
+
+ @Override
+ public String promptForPassword(String message) throws PrompterException {
+ return doPrompt(message, null, null, true);
+ }
+
+ @Override
+ public void showMessage(String message) throws PrompterException {
+ try {
+ doDisplay(message);
+ } catch (IOException e) {
+ throw new PrompterException("Failed to present prompt", e);
+ }
+ }
+
+ String doPrompt(String message, List<Object> possibleValues, String defaultReply, boolean password)
+ throws PrompterException {
+ String formattedMessage = formatMessage(message, possibleValues, defaultReply);
+ String line;
+ do {
+ try {
+ line = doPrompt(formattedMessage, password);
+ if (line == null && defaultReply == null) {
+ throw new IOException("EOF");
+ }
+ } catch (IOException e) {
+ throw new PrompterException("Failed to prompt user", e);
+ }
+ if (line == null || line.isEmpty()) {
+ line = defaultReply;
+ }
+ if (line != null && (possibleValues != null && !possibleValues.contains(line))) {
+ try {
+ doDisplay("Invalid selection.\n");
+ } catch (IOException e) {
+ throw new PrompterException("Failed to present feedback", e);
+ }
+ }
+ } while (line == null || (possibleValues != null && !possibleValues.contains(line)));
+ return line;
+ }
+
+ private String formatMessage(String message, List<Object> possibleValues, String defaultReply) {
+ StringBuilder formatted = new StringBuilder(message.length() * 2);
+ formatted.append(message);
+ if (possibleValues != null && !possibleValues.isEmpty()) {
+ formatted.append(" (");
+ for (Iterator<?> it = possibleValues.iterator(); it.hasNext(); ) {
+ String possibleValue = String.valueOf(it.next());
+ formatted.append(possibleValue);
+ if (it.hasNext()) {
+ formatted.append('/');
+ }
+ }
+ formatted.append(')');
+ }
+ if (defaultReply != null) {
+ formatted.append(' ').append(defaultReply).append(": ");
+ }
+ return formatted.toString();
+ }
+
+ private void doDisplay(String message) throws IOException {
+ try {
+ MessageUtils.terminal.writer().print(message);
+ MessageUtils.terminal.flush();
+ } catch (Exception e) {
+ throw new IOException("Unable to display message", e);
+ }
+ }
+
+ private String doPrompt(String message, boolean password) throws IOException {
+ try {
+ return MessageUtils.reader.readLine(message != null ? message + ": " : null, password ? '*' : null);
+ } catch (Exception e) {
+ throw new IOException("Unable to prompt user", e);
+ }
+ }
+
+ class JlineMessageBuilder implements MessageBuilder {
+
+ final AttributedStringBuilder builder;
+
+ JlineMessageBuilder() {
+ builder = new AttributedStringBuilder();
+ }
+
+ JlineMessageBuilder(int size) {
+ builder = new AttributedStringBuilder(size);
+ }
+
+ @Override
+ public MessageBuilder style(String style) {
+ if (MessageUtils.isColorEnabled()) {
+ builder.style(resolver.resolve(style));
+ }
+ return this;
+ }
+
+ @Override
+ public MessageBuilder resetStyle() {
+ builder.style(DEFAULT);
+ return this;
+ }
+
+ @Override
+ public MessageBuilder append(CharSequence cs) {
+ builder.append(cs);
+ return this;
+ }
+
+ @Override
+ public MessageBuilder append(CharSequence cs, int start, int end) {
+ builder.append(cs, start, end);
+ return this;
+ }
+
+ @Override
+ public MessageBuilder append(char c) {
+ builder.append(c);
+ return this;
+ }
+
+ @Override
+ public MessageBuilder setLength(int length) {
+ builder.setLength(length);
+ return this;
+ }
+
+ @Override
+ public String build() {
+ return builder.toAnsi(MessageUtils.terminal);
+ }
+
+ @Override
+ public String toString() {
+ return build();
+ }
+ }
+
+ static class MavenStyleResolver extends StyleResolver {
+
+ private final Map<String, AttributedStyle> styles = new ConcurrentHashMap<>();
+
+ MavenStyleResolver() {
+ super(s -> System.getProperty("style." + s));
+ }
+
+ @Override
+ public AttributedStyle resolve(String spec) {
+ return styles.computeIfAbsent(spec, this::doResolve);
+ }
+
+ @Override
+ public AttributedStyle resolve(String spec, String defaultSpec) {
+ return resolve(defaultSpec != null ? spec + ":-" + defaultSpec : spec);
+ }
+
+ private AttributedStyle doResolve(String spec) {
+ String def = null;
+ int i = spec.indexOf(":-");
+ if (i != -1) {
+ String[] parts = spec.split(":-");
+ spec = parts[0].trim();
+ def = parts[1].trim();
+ }
+ return super.resolve(spec, def);
+ }
+ }
+}
diff --git a/maven-embedder/src/main/java/org/apache/maven/cli/jline/MessageUtils.java b/maven-embedder/src/main/java/org/apache/maven/cli/jline/MessageUtils.java
new file mode 100644
index 0000000000..d1fb0f94e3
--- /dev/null
+++ b/maven-embedder/src/main/java/org/apache/maven/cli/jline/MessageUtils.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.cli.jline;
+
+import java.io.IOError;
+import java.io.IOException;
+import java.io.PrintStream;
+
+import org.apache.maven.api.services.MessageBuilder;
+import org.apache.maven.api.services.MessageBuilderFactory;
+import org.jline.jansi.AnsiConsole;
+import org.jline.reader.LineReader;
+import org.jline.reader.LineReaderBuilder;
+import org.jline.terminal.Terminal;
+import org.jline.terminal.TerminalBuilder;
+
+public class MessageUtils {
+
+ static Terminal terminal;
+ static LineReader reader;
+ static MessageBuilderFactory messageBuilderFactory = new JLineMessageBuilderFactory();
+ static boolean colorEnabled = true;
+ static Thread shutdownHook;
+ static final Object STARTUP_SHUTDOWN_MONITOR = new Object();
+
+ static PrintStream prevOut;
+ static PrintStream prevErr;
+
+ public static void systemInstall() {
+ try {
+ terminal = TerminalBuilder.builder().name("Maven").dumb(true).build();
+ reader = LineReaderBuilder.builder().terminal(terminal).build();
+ AnsiConsole.setTerminal(terminal);
+ AnsiConsole.systemInstall();
+ } catch (IOException e) {
+ throw new IOError(e);
+ }
+ }
+
+ public static void registerShutdownHook() {
+ if (shutdownHook == null) {
+ shutdownHook = new Thread(() -> {
+ synchronized (MessageUtils.STARTUP_SHUTDOWN_MONITOR) {
+ MessageUtils.doSystemUninstall();
+ }
+ });
+ Runtime.getRuntime().addShutdownHook(shutdownHook);
+ }
+ }
+
+ public static void systemUninstall() {
+ doSystemUninstall();
+ if (shutdownHook != null) {
+ try {
+ Runtime.getRuntime().removeShutdownHook(shutdownHook);
+ } catch (IllegalStateException var3) {
+ // ignore
+ }
+ }
+ }
+
+ private static void doSystemUninstall() {
+ try {
+ AnsiConsole.systemUninstall();
+ } finally {
+ terminal = null;
+ }
+ }
+
+ public static void setColorEnabled(boolean enabled) {
+ colorEnabled = enabled;
+ }
+
+ public static boolean isColorEnabled() {
+ return colorEnabled && terminal != null;
+ }
+
+ public static int getTerminalWidth() {
+ return terminal != null ? terminal.getWidth() : -1;
+ }
+
+ public static MessageBuilder builder() {
+ return messageBuilderFactory.builder();
+ }
+}
diff --git a/maven-embedder/src/main/java/org/apache/maven/cli/transfer/AbstractMavenTransferListener.java b/maven-embedder/src/main/java/org/apache/maven/cli/transfer/AbstractMavenTransferListener.java
index bc6526806c..c38e411e0d 100644
--- a/maven-embedder/src/main/java/org/apache/maven/cli/transfer/AbstractMavenTransferListener.java
+++ b/maven-embedder/src/main/java/org/apache/maven/cli/transfer/AbstractMavenTransferListener.java
@@ -21,7 +21,8 @@ package org.apache.maven.cli.transfer;
import java.io.PrintStream;
import java.util.Locale;
-import org.apache.maven.cli.jansi.MessageUtils;
+import org.apache.maven.api.services.MessageBuilder;
+import org.apache.maven.api.services.MessageBuilderFactory;
import org.eclipse.aether.transfer.AbstractTransferListener;
import org.eclipse.aether.transfer.TransferCancelledException;
import org.eclipse.aether.transfer.TransferEvent;
@@ -31,31 +32,27 @@ import org.eclipse.aether.transfer.TransferResource;
* AbstractMavenTransferListener
*/
public abstract class AbstractMavenTransferListener extends AbstractTransferListener {
+ public static final String STYLE = ".transfer:-faint";
- private static final String ESC = "\u001B";
- private static final String ANSI_DARK_SET = ESC + "[90m";
- private static final String ANSI_DARK_RESET = ESC + "[0m";
+ protected final MessageBuilderFactory messageBuilderFactory;
+ protected final PrintStream out;
- protected PrintStream out;
-
- protected AbstractMavenTransferListener(PrintStream out) {
+ protected AbstractMavenTransferListener(MessageBuilderFactory messageBuilderFactory, PrintStream out) {
+ this.messageBuilderFactory = messageBuilderFactory;
this.out = out;
}
@Override
public void transferInitiated(TransferEvent event) {
- String darkOn = MessageUtils.isColorEnabled() ? ANSI_DARK_SET : "";
- String darkOff = MessageUtils.isColorEnabled() ? ANSI_DARK_RESET : "";
-
String action = event.getRequestType() == TransferEvent.RequestType.PUT ? "Uploading" : "Downloading";
String direction = event.getRequestType() == TransferEvent.RequestType.PUT ? "to" : "from";
TransferResource resource = event.getResource();
- StringBuilder message = new StringBuilder();
- message.append(darkOn).append(action).append(' ').append(direction).append(' ');
- message.append(darkOff).append(resource.getRepositoryId());
- message.append(darkOn).append(": ").append(resource.getRepositoryUrl());
- message.append(darkOff).append(resource.getResourceName());
+ MessageBuilder message = messageBuilderFactory.builder();
+ message.style(STYLE).append(action).append(' ').append(direction).append(' ');
+ message.resetStyle().append(resource.getRepositoryId());
+ message.style(STYLE).append(": ").append(resource.getRepositoryUrl());
+ message.resetStyle().append(resource.getResourceName());
out.println(message.toString());
}
@@ -70,9 +67,6 @@ public abstract class AbstractMavenTransferListener extends AbstractTransferList
@Override
public void transferSucceeded(TransferEvent event) {
- String darkOn = MessageUtils.isColorEnabled() ? ANSI_DARK_SET : "";
- String darkOff = MessageUtils.isColorEnabled() ? ANSI_DARK_RESET : "";
-
String action = (event.getRequestType() == TransferEvent.RequestType.PUT ? "Uploaded" : "Downloaded");
String direction = event.getRequestType() == TransferEvent.RequestType.PUT ? "to" : "from";
@@ -80,13 +74,12 @@ public abstract class AbstractMavenTransferListener extends AbstractTransferList
long contentLength = event.getTransferredBytes();
FileSizeFormat format = new FileSizeFormat(Locale.ENGLISH);
- StringBuilder message = new StringBuilder();
- message.append(action).append(darkOn).append(' ').append(direction).append(' ');
- message.append(darkOff).append(resource.getRepositoryId());
- message.append(darkOn).append(": ").append(resource.getRepositoryUrl());
- message.append(darkOff).append(resource.getResourceName());
- message.append(darkOn).append(" (");
- format.format(message, contentLength);
+ MessageBuilder message = messageBuilderFactory.builder();
+ message.append(action).style(STYLE).append(' ').append(direction).append(' ');
+ message.resetStyle().append(resource.getRepositoryId());
+ message.style(STYLE).append(": ").append(resource.getRepositoryUrl());
+ message.resetStyle().append(resource.getResourceName());
+ message.style(STYLE).append(" (").append(format.format(contentLength));
long duration = System.currentTimeMillis() - resource.getTransferStartTime();
if (duration > 0L) {
@@ -96,7 +89,7 @@ public abstract class AbstractMavenTransferListener extends AbstractTransferList
message.append("/s");
}
- message.append(')').append(darkOff);
+ message.append(')').resetStyle();
out.println(message.toString());
}
}
diff --git a/maven-embedder/src/main/java/org/apache/maven/cli/transfer/ConsoleMavenTransferListener.java b/maven-embedder/src/main/java/org/apache/maven/cli/transfer/ConsoleMavenTransferListener.java
index 78addd1349..2d54f663f5 100644
--- a/maven-embedder/src/main/java/org/apache/maven/cli/transfer/ConsoleMavenTransferListener.java
+++ b/maven-embedder/src/main/java/org/apache/maven/cli/transfer/ConsoleMavenTransferListener.java
@@ -24,6 +24,7 @@ import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
+import org.apache.maven.api.services.MessageBuilderFactory;
import org.eclipse.aether.transfer.TransferCancelledException;
import org.eclipse.aether.transfer.TransferEvent;
import org.eclipse.aether.transfer.TransferResource;
@@ -41,8 +42,9 @@ public class ConsoleMavenTransferListener extends AbstractMavenTransferListener
private boolean printResourceNames;
private int lastLength;
- public ConsoleMavenTransferListener(PrintStream out, boolean printResourceNames) {
- super(out);
+ public ConsoleMavenTransferListener(
+ MessageBuilderFactory messageBuilderFactory, PrintStream out, boolean printResourceNames) {
+ super(messageBuilderFactory, out);
this.printResourceNames = printResourceNames;
}
diff --git a/maven-embedder/src/main/java/org/apache/maven/cli/transfer/FileSizeFormat.java b/maven-embedder/src/main/java/org/apache/maven/cli/transfer/FileSizeFormat.java
index 6ae2054e65..35078f9c0c 100644
--- a/maven-embedder/src/main/java/org/apache/maven/cli/transfer/FileSizeFormat.java
+++ b/maven-embedder/src/main/java/org/apache/maven/cli/transfer/FileSizeFormat.java
@@ -20,6 +20,8 @@ package org.apache.maven.cli.transfer;
import java.util.Locale;
+import org.apache.maven.api.services.MessageBuilder;
+
/**
* Formats file size with the associated <a href="https://en.wikipedia.org/wiki/Metric_prefix">SI</a> prefix
* (GB, MB, kB) and using the patterns <code>#0.0</code> for numbers between 1 and 10
@@ -147,6 +149,38 @@ public class FileSizeFormat {
}
}
+ public void format(MessageBuilder builder, long size) {
+ format(builder, size, null, false);
+ }
+
+ public void format(MessageBuilder builder, long size, ScaleUnit unit) {
+ format(builder, size, unit, false);
+ }
+
+ @SuppressWarnings("checkstyle:magicnumber")
+ private void format(MessageBuilder builder, long size, ScaleUnit unit, boolean omitSymbol) {
+ if (size < 0L) {
+ throw new IllegalArgumentException("file size cannot be negative: " + size);
+ }
+ if (unit == null) {
+ unit = ScaleUnit.getScaleUnit(size);
+ }
+
+ double scaledSize = (double) size / unit.bytes();
+
+ if (unit == ScaleUnit.BYTE) {
+ builder.append(Long.toString(size));
+ } else if (scaledSize < 0.05d || scaledSize >= 10.0d) {
+ builder.append(Long.toString(Math.round(scaledSize)));
+ } else {
+ builder.append(Double.toString(Math.round(scaledSize * 10d) / 10d));
+ }
+
+ if (!omitSymbol) {
+ builder.append(" ").append(unit.symbol());
+ }
+ }
+
public String formatProgress(long progressedSize, long size) {
StringBuilder sb = new StringBuilder();
formatProgress(sb, progressedSize, size);
diff --git a/maven-embedder/src/main/java/org/fusesource/jansi/Ansi.java b/maven-embedder/src/main/java/org/fusesource/jansi/Ansi.java
new file mode 100644
index 0000000000..8cf2051511
--- /dev/null
+++ b/maven-embedder/src/main/java/org/fusesource/jansi/Ansi.java
@@ -0,0 +1,952 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.fusesource.jansi;
+
+import java.util.ArrayList;
+
+/**
+ * Provides a fluent API for generating
+ * <a href="https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_sequences">ANSI escape sequences</a>.
+ *
+ * This class comes from Jansi and is provided for backward compatibility
+ * with maven-shared-utils, while Maven has migrated to JLine (into which Jansi has been merged
+ * since JLine 3.25.0).
+ */
+@SuppressWarnings({"checkstyle:MagicNumber", "unused"})
+public class Ansi implements Appendable {
+
+ private static final char FIRST_ESC_CHAR = 27;
+ private static final char SECOND_ESC_CHAR = '[';
+
+ /**
+ * <a href="https://en.wikipedia.org/wiki/ANSI_escape_code#Colors">ANSI 8 colors</a> for fluent API
+ */
+ public enum Color {
+ BLACK(0, "BLACK"),
+ RED(1, "RED"),
+ GREEN(2, "GREEN"),
+ YELLOW(3, "YELLOW"),
+ BLUE(4, "BLUE"),
+ MAGENTA(5, "MAGENTA"),
+ CYAN(6, "CYAN"),
+ WHITE(7, "WHITE"),
+ DEFAULT(9, "DEFAULT");
+
+ private final int value;
+ private final String name;
+
+ Color(int index, String name) {
+ this.value = index;
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+
+ public int value() {
+ return value;
+ }
+
+ public int fg() {
+ return value + 30;
+ }
+
+ public int bg() {
+ return value + 40;
+ }
+
+ public int fgBright() {
+ return value + 90;
+ }
+
+ public int bgBright() {
+ return value + 100;
+ }
+ }
+
+ /**
+ * Display attributes, also know as
+ * <a href="https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters">SGR
+ * (Select Graphic Rendition) parameters</a>.
+ */
+ public enum Attribute {
+ RESET(0, "RESET"),
+ INTENSITY_BOLD(1, "INTENSITY_BOLD"),
+ INTENSITY_FAINT(2, "INTENSITY_FAINT"),
+ ITALIC(3, "ITALIC_ON"),
+ UNDERLINE(4, "UNDERLINE_ON"),
+ BLINK_SLOW(5, "BLINK_SLOW"),
+ BLINK_FAST(6, "BLINK_FAST"),
+ NEGATIVE_ON(7, "NEGATIVE_ON"),
+ CONCEAL_ON(8, "CONCEAL_ON"),
+ STRIKETHROUGH_ON(9, "STRIKETHROUGH_ON"),
+ UNDERLINE_DOUBLE(21, "UNDERLINE_DOUBLE"),
+ INTENSITY_BOLD_OFF(22, "INTENSITY_BOLD_OFF"),
+ ITALIC_OFF(23, "ITALIC_OFF"),
+ UNDERLINE_OFF(24, "UNDERLINE_OFF"),
+ BLINK_OFF(25, "BLINK_OFF"),
+ NEGATIVE_OFF(27, "NEGATIVE_OFF"),
+ CONCEAL_OFF(28, "CONCEAL_OFF"),
+ STRIKETHROUGH_OFF(29, "STRIKETHROUGH_OFF");
+
+ private final int value;
+ private final String name;
+
+ Attribute(int index, String name) {
+ this.value = index;
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+
+ public int value() {
+ return value;
+ }
+ }
+
+ /**
+ * ED (Erase in Display) / EL (Erase in Line) parameter (see
+ * <a href="https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_sequences">CSI sequence J and K</a>)
+ * @see Ansi#eraseScreen(Erase)
+ * @see Ansi#eraseLine(Erase)
+ */
+ public enum Erase {
+ FORWARD(0, "FORWARD"),
+ BACKWARD(1, "BACKWARD"),
+ ALL(2, "ALL");
+
+ private final int value;
+ private final String name;
+
+ Erase(int index, String name) {
+ this.value = index;
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+
+ public int value() {
+ return value;
+ }
+ }
+
+ @FunctionalInterface
+ public interface Consumer {
+ void apply(Ansi ansi);
+ }
+
+ public static boolean isEnabled() {
+ return org.apache.maven.cli.jline.MessageUtils.isColorEnabled() && org.jline.jansi.Ansi.isEnabled();
+ }
+
+ public static Ansi ansi() {
+ if (isEnabled()) {
+ return new Ansi();
+ } else {
+ return new NoAnsi();
+ }
+ }
+
+ public static Ansi ansi(StringBuilder builder) {
+ if (isEnabled()) {
+ return new Ansi(builder);
+ } else {
+ return new NoAnsi(builder);
+ }
+ }
+
+ public static Ansi ansi(int size) {
+ if (isEnabled()) {
+ return new Ansi(size);
+ } else {
+ return new NoAnsi(size);
+ }
+ }
+
+ private static class NoAnsi extends Ansi {
+ NoAnsi() {
+ super();
+ }
+
+ NoAnsi(int size) {
+ super(size);
+ }
+
+ NoAnsi(StringBuilder builder) {
+ super(builder);
+ }
+
+ @Override
+ public Ansi fg(Color color) {
+ return this;
+ }
+
+ @Override
+ public Ansi bg(Color color) {
+ return this;
+ }
+
+ @Override
+ public Ansi fgBright(Color color) {
+ return this;
+ }
+
+ @Override
+ public Ansi bgBright(Color color) {
+ return this;
+ }
+
+ @Override
+ public Ansi fg(int color) {
+ return this;
+ }
+
+ @Override
+ public Ansi fgRgb(int r, int g, int b) {
+ return this;
+ }
+
+ @Override
+ public Ansi bg(int color) {
+ return this;
+ }
+
+ @Override
+ public Ansi bgRgb(int r, int g, int b) {
+ return this;
+ }
+
+ @Override
+ public Ansi a(Attribute attribute) {
+ return this;
+ }
+
+ @Override
+ public Ansi cursor(int row, int column) {
+ return this;
+ }
+
+ @Override
+ public Ansi cursorToColumn(int x) {
+ return this;
+ }
+
+ @Override
+ public Ansi cursorUp(int y) {
+ return this;
+ }
+
+ @Override
+ public Ansi cursorRight(int x) {
+ return this;
+ }
+
+ @Override
+ public Ansi cursorDown(int y) {
+ return this;
+ }
+
+ @Override
+ public Ansi cursorLeft(int x) {
+ return this;
+ }
+
+ @Override
+ public Ansi cursorDownLine() {
+ return this;
+ }
+
+ @Override
+ public Ansi cursorDownLine(final int n) {
+ return this;
+ }
+
+ @Override
+ public Ansi cursorUpLine() {
+ return this;
+ }
+
+ @Override
+ public Ansi cursorUpLine(final int n) {
+ return this;
+ }
+
+ @Override
+ public Ansi eraseScreen() {
+ return this;
+ }
+
+ @Override
+ public Ansi eraseScreen(Erase kind) {
+ return this;
+ }
+
+ @Override
+ public Ansi eraseLine() {
+ return this;
+ }
+
+ @Override
+ public Ansi eraseLine(Erase kind) {
+ return this;
+ }
+
+ @Override
+ public Ansi scrollUp(int rows) {
+ return this;
+ }
+
+ @Override
+ public Ansi scrollDown(int rows) {
+ return this;
+ }
+
+ @Override
+ public Ansi saveCursorPosition() {
+ return this;
+ }
+
+ @Override
+ @Deprecated
+ public Ansi restorCursorPosition() {
+ return this;
+ }
+
+ @Override
+ public Ansi restoreCursorPosition() {
+ return this;
+ }
+
+ @Override
+ public Ansi reset() {
+ return this;
+ }
+ }
+
+ private final StringBuilder builder;
+ private final ArrayList<Integer> attributeOptions = new ArrayList<>(5);
+
+ public Ansi() {
+ this(new StringBuilder(80));
+ }
+
+ public Ansi(Ansi parent) {
+ this(new StringBuilder(parent.builder));
+ attributeOptions.addAll(parent.attributeOptions);
+ }
+
+ public Ansi(int size) {
+ this(new StringBuilder(size));
+ }
+
+ public Ansi(StringBuilder builder) {
+ this.builder = builder;
+ }
+
+ public Ansi fg(Color color) {
+ attributeOptions.add(color.fg());
+ return this;
+ }
+
+ public Ansi fg(int color) {
+ attributeOptions.add(38);
+ attributeOptions.add(5);
+ attributeOptions.add(color & 0xff);
+ return this;
+ }
+
+ public Ansi fgRgb(int color) {
+ return fgRgb(color >> 16, color >> 8, color);
+ }
+
+ public Ansi fgRgb(int r, int g, int b) {
+ attributeOptions.add(38);
+ attributeOptions.add(2);
+ attributeOptions.add(r & 0xff);
+ attributeOptions.add(g & 0xff);
+ attributeOptions.add(b & 0xff);
+ return this;
+ }
+
+ public Ansi fgBlack() {
+ return this.fg(Color.BLACK);
+ }
+
+ public Ansi fgBlue() {
+ return this.fg(Color.BLUE);
+ }
+
+ public Ansi fgCyan() {
+ return this.fg(Color.CYAN);
+ }
+
+ public Ansi fgDefault() {
+ return this.fg(Color.DEFAULT);
+ }
+
+ public Ansi fgGreen() {
+ return this.fg(Color.GREEN);
+ }
+
+ public Ansi fgMagenta() {
+ return this.fg(Color.MAGENTA);
+ }
+
+ public Ansi fgRed() {
+ return this.fg(Color.RED);
+ }
+
+ public Ansi fgYellow() {
+ return this.fg(Color.YELLOW);
+ }
+
+ public Ansi bg(Color color) {
+ attributeOptions.add(color.bg());
+ return this;
+ }
+
+ public Ansi bg(int color) {
+ attributeOptions.add(48);
+ attributeOptions.add(5);
+ attributeOptions.add(color & 0xff);
+ return this;
+ }
+
+ public Ansi bgRgb(int color) {
+ return bgRgb(color >> 16, color >> 8, color);
+ }
+
+ public Ansi bgRgb(int r, int g, int b) {
+ attributeOptions.add(48);
+ attributeOptions.add(2);
+ attributeOptions.add(r & 0xff);
+ attributeOptions.add(g & 0xff);
+ attributeOptions.add(b & 0xff);
+ return this;
+ }
+
+ public Ansi bgCyan() {
+ return this.bg(Color.CYAN);
+ }
+
+ public Ansi bgDefault() {
+ return this.bg(Color.DEFAULT);
+ }
+
+ public Ansi bgGreen() {
+ return this.bg(Color.GREEN);
+ }
+
+ public Ansi bgMagenta() {
+ return this.bg(Color.MAGENTA);
+ }
+
+ public Ansi bgRed() {
+ return this.bg(Color.RED);
+ }
+
+ public Ansi bgYellow() {
+ return this.bg(Color.YELLOW);
+ }
+
+ public Ansi fgBright(Color color) {
+ attributeOptions.add(color.fgBright());
+ return this;
+ }
+
+ public Ansi fgBrightBlack() {
+ return this.fgBright(Color.BLACK);
+ }
+
+ public Ansi fgBrightBlue() {
+ return this.fgBright(Color.BLUE);
+ }
+
+ public Ansi fgBrightCyan() {
+ return this.fgBright(Color.CYAN);
+ }
+
+ public Ansi fgBrightDefault() {
+ return this.fgBright(Color.DEFAULT);
+ }
+
+ public Ansi fgBrightGreen() {
+ return this.fgBright(Color.GREEN);
+ }
+
+ public Ansi fgBrightMagenta() {
+ return this.fgBright(Color.MAGENTA);
+ }
+
+ public Ansi fgBrightRed() {
+ return this.fgBright(Color.RED);
+ }
+
+ public Ansi fgBrightYellow() {
+ return this.fgBright(Color.YELLOW);
+ }
+
+ public Ansi bgBright(Color color) {
+ attributeOptions.add(color.bgBright());
+ return this;
+ }
+
+ public Ansi bgBrightCyan() {
+ return this.bgBright(Color.CYAN);
+ }
+
+ public Ansi bgBrightDefault() {
+ return this.bgBright(Color.DEFAULT);
+ }
+
+ public Ansi bgBrightGreen() {
+ return this.bgBright(Color.GREEN);
+ }
+
+ public Ansi bgBrightMagenta() {
+ return this.bgBright(Color.MAGENTA);
+ }
+
+ public Ansi bgBrightRed() {
+ return this.bgBright(Color.RED);
+ }
+
+ public Ansi bgBrightYellow() {
+ return this.bgBright(Color.YELLOW);
+ }
+
+ public Ansi a(Attribute attribute) {
+ attributeOptions.add(attribute.value());
+ return this;
+ }
+
+ /**
+ * Moves the cursor to row n, column m. The values are 1-based.
+ * Any values less than 1 are mapped to 1.
+ *
+ * @param row row (1-based) from top
+ * @param column column (1 based) from left
+ * @return this Ansi instance
+ */
+ public Ansi cursor(final int row, final int column) {
+ return appendEscapeSequence('H', Math.max(1, row), Math.max(1, column));
+ }
+
+ /**
+ * Moves the cursor to column n. The parameter n is 1-based.
+ * If n is less than 1 it is moved to the first column.
+ *
+ * @param x the index (1-based) of the column to move to
+ * @return this Ansi instance
+ */
+ public Ansi cursorToColumn(final int x) {
+ return appendEscapeSequence('G', Math.max(1, x));
+ }
+
+ /**
+ * Moves the cursor up. If the parameter y is negative it moves the cursor down.
+ *
+ * @param y the number of lines to move up
+ * @return this Ansi instance
+ */
+ public Ansi cursorUp(final int y) {
+ return y > 0 ? appendEscapeSequence('A', y) : y < 0 ? cursorDown(-y) : this;
+ }
+
+ /**
+ * Moves the cursor down. If the parameter y is negative it moves the cursor up.
+ *
+ * @param y the number of lines to move down
+ * @return this Ansi instance
+ */
+ public Ansi cursorDown(final int y) {
+ return y > 0 ? appendEscapeSequence('B', y) : y < 0 ? cursorUp(-y) : this;
+ }
+
+ /**
+ * Moves the cursor right. If the parameter x is negative it moves the cursor left.
+ *
+ * @param x the number of characters to move right
+ * @return this Ansi instance
+ */
+ public Ansi cursorRight(final int x) {
+ return x > 0 ? appendEscapeSequence('C', x) : x < 0 ? cursorLeft(-x) : this;
+ }
+
+ /**
+ * Moves the cursor left. If the parameter x is negative it moves the cursor right.
+ *
+ * @param x the number of characters to move left
+ * @return this Ansi instance
+ */
+ public Ansi cursorLeft(final int x) {
+ return x > 0 ? appendEscapeSequence('D', x) : x < 0 ? cursorRight(-x) : this;
+ }
+
+ /**
+ * Moves the cursor relative to the current position. The cursor is moved right if x is
+ * positive, left if negative and down if y is positive and up if negative.
+ *
+ * @param x the number of characters to move horizontally
+ * @param y the number of lines to move vertically
+ * @return this Ansi instance
+ * @since 2.2
+ */
+ public Ansi cursorMove(final int x, final int y) {
+ return cursorRight(x).cursorDown(y);
+ }
+
+ /**
+ * Moves the cursor to the beginning of the line below.
+ *
+ * @return this Ansi instance
+ */
+ public Ansi cursorDownLine() {
+ return appendEscapeSequence('E');
+ }
+
+ /**
+ * Moves the cursor to the beginning of the n-th line below. If the parameter n is negative it
+ * moves the cursor to the beginning of the n-th line above.
+ *
+ * @param n the number of lines to move the cursor
+ * @return this Ansi instance
+ */
+ public Ansi cursorDownLine(final int n) {
+ return n < 0 ? cursorUpLine(-n) : appendEscapeSequence('E', n);
+ }
+
+ /**
+ * Moves the cursor to the beginning of the line above.
+ *
+ * @return this Ansi instance
+ */
+ public Ansi cursorUpLine() {
+ return appendEscapeSequence('F');
+ }
+
+ /**
+ * Moves the cursor to the beginning of the n-th line above. If the parameter n is negative it
+ * moves the cursor to the beginning of the n-th line below.
+ *
+ * @param n the number of lines to move the cursor
+ * @return this Ansi instance
+ */
+ public Ansi cursorUpLine(final int n) {
+ return n < 0 ? cursorDownLine(-n) : appendEscapeSequence('F', n);
+ }
+
+ public Ansi eraseScreen() {
+ return appendEscapeSequence('J', Erase.ALL.value());
+ }
+
+ public Ansi eraseScreen(final Erase kind) {
+ return appendEscapeSequence('J', kind.value());
+ }
+
+ public Ansi eraseLine() {
+ return appendEscapeSequence('K');
+ }
+
+ public Ansi eraseLine(final Erase kind) {
+ return appendEscapeSequence('K', kind.value());
+ }
+
+ public Ansi scrollUp(final int rows) {
+ if (rows == Integer.MIN_VALUE) {
+ return scrollDown(Integer.MAX_VALUE);
+ }
+ return rows > 0 ? appendEscapeSequence('S', rows) : rows < 0 ? scrollDown(-rows) : this;
+ }
+
+ public Ansi scrollDown(final int rows) {
+ if (rows == Integer.MIN_VALUE) {
+ return scrollUp(Integer.MAX_VALUE);
+ }
+ return rows > 0 ? appendEscapeSequence('T', rows) : rows < 0 ? scrollUp(-rows) : this;
+ }
+
+ @Deprecated
+ public Ansi restorCursorPosition() {
+ return restoreCursorPosition();
+ }
+
+ public Ansi saveCursorPosition() {
+ saveCursorPositionSCO();
+ return saveCursorPositionDEC();
+ }
+
+ // SCO command
+ public Ansi saveCursorPositionSCO() {
+ return appendEscapeSequence('s');
+ }
+
+ // DEC command
+ public Ansi saveCursorPositionDEC() {
+ builder.append(FIRST_ESC_CHAR);
+ builder.append('7');
+ return this;
+ }
+
+ public Ansi restoreCursorPosition() {
+ restoreCursorPositionSCO();
+ return restoreCursorPositionDEC();
+ }
+
+ // SCO command
+ public Ansi restoreCursorPositionSCO() {
+ return appendEscapeSequence('u');
+ }
+
+ // DEC command
+ public Ansi restoreCursorPositionDEC() {
+ builder.append(FIRST_ESC_CHAR);
+ builder.append('8');
+ return this;
+ }
+
+ public Ansi reset() {
+ return a(Attribute.RESET);
+ }
+
+ public Ansi bold() {
+ return a(Attribute.INTENSITY_BOLD);
+ }
+
+ public Ansi boldOff() {
+ return a(Attribute.INTENSITY_BOLD_OFF);
+ }
+
+ public Ansi a(String value) {
+ flushAttributes();
+ builder.append(value);
+ return this;
+ }
+
+ public Ansi a(boolean value) {
+ flushAttributes();
+ builder.append(value);
+ return this;
+ }
+
+ public Ansi a(char value) {
+ flushAttributes();
+ builder.append(value);
+ return this;
+ }
+
+ public Ansi a(char[] value, int offset, int len) {
+ flushAttributes();
+ builder.append(value, offset, len);
+ return this;
+ }
+
+ public Ansi a(char[] value) {
+ flushAttributes();
+ builder.append(value);
+ return this;
+ }
+
+ public Ansi a(CharSequence value, int start, int end) {
+ flushAttributes();
+ builder.append(value, start, end);
+ return this;
+ }
+
+ public Ansi a(CharSequence value) {
+ flushAttributes();
+ builder.append(value);
+ return this;
+ }
+
+ public Ansi a(double value) {
+ flushAttributes();
+ builder.append(value);
+ return this;
+ }
+
+ public Ansi a(float value) {
+ flushAttributes();
+ builder.append(value);
+ return this;
+ }
+
+ public Ansi a(int value) {
+ flushAttributes();
+ builder.append(value);
+ return this;
+ }
+
+ public Ansi a(long value) {
+ flushAttributes();
+ builder.append(value);
+ return this;
+ }
+
+ public Ansi a(Object value) {
+ flushAttributes();
+ builder.append(value);
+ return this;
+ }
+
+ public Ansi a(StringBuffer value) {
+ flushAttributes();
+ builder.append(value);
+ return this;
+ }
+
+ public Ansi newline() {
+ flushAttributes();
+ builder.append(System.getProperty("line.separator"));
+ return this;
+ }
+
+ public Ansi format(String pattern, Object... args) {
+ flushAttributes();
+ builder.append(String.format(pattern, args));
+ return this;
+ }
+
+ /**
+ * Applies another function to this Ansi instance.
+ *
+ * @param fun the function to apply
+ * @return this Ansi instance
+ * @since 2.2
+ */
+ public Ansi apply(Consumer fun) {
+ fun.apply(this);
+ return this;
+ }
+
+ /**
+ * Uses the {@link org.jline.jansi.AnsiRenderer}
+ * to generate the ANSI escape sequences for the supplied text.
+ *
+ * @param text text
+ * @return this
+ * @since 2.2
+ */
+ public Ansi render(final String text) {
+ a(new org.jline.jansi.Ansi().render(text).toString());
+ return this;
+ }
+
+ /**
+ * String formats and renders the supplied arguments. Uses the {@link org.jline.jansi.AnsiRenderer}
+ * to generate the ANSI escape sequences.
+ *
+ * @param text format
+ * @param args arguments
+ * @return this
+ * @since 2.2
+ */
+ public Ansi render(final String text, Object... args) {
+ a(String.format(new org.jline.jansi.Ansi().render(text).toString(), args));
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ flushAttributes();
+ return builder.toString();
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ // Private Helper Methods
+ ///////////////////////////////////////////////////////////////////
+
+ private Ansi appendEscapeSequence(char command) {
+ flushAttributes();
+ builder.append(FIRST_ESC_CHAR);
+ builder.append(SECOND_ESC_CHAR);
+ builder.append(command);
+ return this;
+ }
+
+ private Ansi appendEscapeSequence(char command, int option) {
+ flushAttributes();
+ builder.append(FIRST_ESC_CHAR);
+ builder.append(SECOND_ESC_CHAR);
+ builder.append(option);
+ builder.append(command);
+ return this;
+ }
+
+ private Ansi appendEscapeSequence(char command, Object... options) {
+ flushAttributes();
+ return doAppendEscapeSequence(command, options);
+ }
+
+ private void flushAttributes() {
+ if (attributeOptions.isEmpty()) {
+ return;
+ }
+ if (attributeOptions.size() == 1 && attributeOptions.get(0) == 0) {
+ builder.append(FIRST_ESC_CHAR);
+ builder.append(SECOND_ESC_CHAR);
+ builder.append('m');
+ } else {
+ doAppendEscapeSequence('m', attributeOptions.toArray());
+ }
+ attributeOptions.clear();
+ }
+
+ private Ansi doAppendEscapeSequence(char command, Object... options) {
+ builder.append(FIRST_ESC_CHAR);
+ builder.append(SECOND_ESC_CHAR);
+ int size = options.length;
+ for (int i = 0; i < size; i++) {
+ if (i != 0) {
+ builder.append(';');
+ }
+ if (options[i] != null) {
+ builder.append(options[i]);
+ }
+ }
+ builder.append(command);
+ return this;
+ }
+
+ @Override
+ public Ansi append(CharSequence csq) {
+ builder.append(csq);
+ return this;
+ }
+
+ @Override
+ public Ansi append(CharSequence csq, int start, int end) {
+ builder.append(csq, start, end);
+ return this;
+ }
+
+ @Override
+ public Ansi append(char c) {
+ builder.append(c);
+ return this;
+ }
+}
diff --git a/maven-embedder/src/test/java/org/apache/maven/cli/MavenCliTest.java b/maven-embedder/src/test/java/org/apache/maven/cli/MavenCliTest.java
index a68976bb37..318ca6a4f9 100644
--- a/maven-embedder/src/test/java/org/apache/maven/cli/MavenCliTest.java
+++ b/maven-embedder/src/test/java/org/apache/maven/cli/MavenCliTest.java
@@ -35,7 +35,7 @@ import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.maven.Maven;
-import org.apache.maven.cli.jansi.MessageUtils;
+import org.apache.maven.cli.jline.MessageUtils;
import org.apache.maven.cli.transfer.ConsoleMavenTransferListener;
import org.apache.maven.cli.transfer.QuietMavenTransferListener;
import org.apache.maven.cli.transfer.Slf4jMavenTransferListener;
@@ -500,7 +500,7 @@ class MavenCliTest {
String versionOut = new String(systemOut.toByteArray(), StandardCharsets.UTF_8);
// then
- assertEquals(MessageUtils.stripAnsiCodes(versionOut), versionOut);
+ assertEquals(stripAnsiCodes(versionOut), versionOut);
}
@Test
@@ -675,4 +675,8 @@ class MavenCliTest {
project.setArtifactId(artifactId);
return project;
}
+
+ static String stripAnsiCodes(String msg) {
+ return msg.replaceAll("\u001b\\[[;\\d]*[ -/]*[@-~]", "");
+ }
}
diff --git a/maven-embedder/src/test/java/org/apache/maven/cli/event/ExecutionEventLoggerTest.java b/maven-embedder/src/test/java/org/apache/maven/cli/event/ExecutionEventLoggerTest.java
index 3e260124b5..4ae5326100 100644
--- a/maven-embedder/src/test/java/org/apache/maven/cli/event/ExecutionEventLoggerTest.java
+++ b/maven-embedder/src/test/java/org/apache/maven/cli/event/ExecutionEventLoggerTest.java
@@ -22,8 +22,8 @@ import java.io.File;
import com.google.common.collect.ImmutableList;
import org.apache.commons.io.FilenameUtils;
-import org.apache.maven.cli.jansi.JansiMessageBuilderFactory;
-import org.apache.maven.cli.jansi.MessageUtils;
+import org.apache.maven.cli.jline.JLineMessageBuilderFactory;
+import org.apache.maven.cli.jline.MessageUtils;
import org.apache.maven.execution.ExecutionEvent;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.project.MavenProject;
@@ -45,7 +45,7 @@ class ExecutionEventLoggerTest {
private Logger logger;
private ExecutionEventLogger executionEventLogger;
- private JansiMessageBuilderFactory messageBuilderFactory = new JansiMessageBuilderFactory();
+ private JLineMessageBuilderFactory messageBuilderFactory = new JLineMessageBuilderFactory();
@BeforeAll
static void setUp() {
diff --git a/maven-embedder/src/test/java/org/apache/maven/cli/transfer/ConsoleMavenTransferListenerTest.java b/maven-embedder/src/test/java/org/apache/maven/cli/transfer/ConsoleMavenTransferListenerTest.java
index 095124052c..cc726cb71f 100644
--- a/maven-embedder/src/test/java/org/apache/maven/cli/transfer/ConsoleMavenTransferListenerTest.java
+++ b/maven-embedder/src/test/java/org/apache/maven/cli/transfer/ConsoleMavenTransferListenerTest.java
@@ -26,6 +26,7 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import org.apache.maven.cli.jline.JLineMessageBuilderFactory;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.transfer.TransferCancelledException;
import org.eclipse.aether.transfer.TransferEvent;
@@ -48,6 +49,7 @@ class ConsoleMavenTransferListenerTest {
Map<String, String> output = new ConcurrentHashMap<String, String>();
ConsoleMavenTransferListener listener = new ConsoleMavenTransferListener(
+ new JLineMessageBuilderFactory(),
new PrintStream(System.out) {
@Override
diff --git a/maven-slf4j-provider/src/main/java/org/slf4j/impl/MavenSimpleLogger.java b/maven-slf4j-provider/src/main/java/org/slf4j/impl/MavenSimpleLogger.java
index 988b2239bc..06a689146d 100644
--- a/maven-slf4j-provider/src/main/java/org/slf4j/impl/MavenSimpleLogger.java
+++ b/maven-slf4j-provider/src/main/java/org/slf4j/impl/MavenSimpleLogger.java
@@ -22,7 +22,7 @@ import java.io.PrintStream;
import org.apache.maven.api.services.MessageBuilder;
-import static org.apache.maven.cli.jansi.MessageUtils.builder;
+import static org.apache.maven.cli.jline.MessageUtils.builder;
/**
* Logger for Maven, that support colorization of levels and stacktraces. This class implements 2 methods introduced in
diff --git a/maven-slf4j-provider/src/test/java/org/slf4j/impl/MavenSimpleLoggerTest.java b/maven-slf4j-provider/src/test/java/org/slf4j/impl/MavenSimpleLoggerTest.java
index b51decb6b9..4fbb3d1498 100644
--- a/maven-slf4j-provider/src/test/java/org/slf4j/impl/MavenSimpleLoggerTest.java
+++ b/maven-slf4j-provider/src/test/java/org/slf4j/impl/MavenSimpleLoggerTest.java
@@ -24,6 +24,9 @@ import java.util.Arrays;
import java.util.List;
import java.util.NoSuchElementException;
+import org.apache.maven.cli.jline.MessageUtils;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
@@ -32,6 +35,19 @@ import static org.junit.jupiter.api.Assertions.assertLinesMatch;
class MavenSimpleLoggerTest {
+ boolean colorEnabled;
+
+ @BeforeEach
+ void setup() {
+ colorEnabled = MessageUtils.isColorEnabled();
+ MessageUtils.setColorEnabled(false);
+ }
+
+ @AfterEach
+ void taerdown() {
+ MessageUtils.setColorEnabled(colorEnabled);
+ }
+
@Test
void includesCauseAndSuppressedExceptionsWhenWritingThrowables(TestInfo testInfo) throws Exception {
Exception causeOfSuppressed = new NoSuchElementException("cause of suppressed");
diff --git a/pom.xml b/pom.xml
index eb32ccebcc..13d6a5844a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -167,12 +167,13 @@ under the License.
<guavafailureaccessVersion>1.0.1</guavafailureaccessVersion>
<hamcrestVersion>2.2</hamcrestVersion>
<jakartaInjectApiVersion>2.0.1</jakartaInjectApiVersion>
- <jansiVersion>2.4.1</jansiVersion>
<javaxAnnotationApiVersion>1.3.2</javaxAnnotationApiVersion>
+ <jlineVersion>3.25.0</jlineVersion>
<junitVersion>5.10.1</junitVersion>
<jxpathVersion>1.3</jxpathVersion>
<logbackClassicVersion>1.2.13</logbackClassicVersion>
<mockitoVersion>5.7.0</mockitoVersion>
+ <plexusInteractivityVersion>1.1</plexusInteractivityVersion>
<plexusInterpolationVersion>1.26</plexusInterpolationVersion>
<plexusTestingVersion>1.0.0</plexusTestingVersion>
<plexusXmlVersion>4.0.1</plexusXmlVersion>
@@ -309,9 +310,14 @@ under the License.
<version>${plexusInterpolationVersion}</version>
</dependency>
<dependency>
- <groupId>org.fusesource.jansi</groupId>
- <artifactId>jansi</artifactId>
- <version>${jansiVersion}</version>
+ <groupId>org.codehaus.plexus</groupId>
+ <artifactId>plexus-interactivity-api</artifactId>
+ <version>${plexusInteractivityVersion}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.jline</groupId>
+ <artifactId>jline</artifactId>
+ <version>${jlineVersion}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
@@ -690,6 +696,9 @@ under the License.
<ignoredScopes>
<ignoredScope>test</ignoredScope>
</ignoredScopes>
+ <excludes>
+ <exclude>org.jline:jline</exclude>
+ </excludes>
</enforceBytecodeVersion>
</rules>
<fail>true</fail>