Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions cli-processor/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,6 @@
<groupId>commons-cli</groupId>
<artifactId>commons-cli</artifactId>
</dependency>
<dependency>
<groupId>org.jline</groupId>
<artifactId>jansi-core</artifactId>
</dependency>
<dependency>
<groupId>nl.talsmasoftware</groupId>
<artifactId>lazy4j</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

package dev.metaschema.cli.processor;

import static org.jline.jansi.Ansi.ansi;
import static dev.metaschema.cli.processor.ansi.Ansi.ansi;

import org.apache.commons.cli.Option;
import org.apache.logging.log4j.Level;
Expand All @@ -15,7 +15,6 @@
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.eclipse.jdt.annotation.NotOwning;
import org.jline.jansi.Ansi;

import java.io.PrintStream;
import java.util.Arrays;
Expand All @@ -25,6 +24,7 @@
import java.util.function.Function;
import java.util.stream.Collectors;

import dev.metaschema.cli.processor.ansi.Ansi;
import dev.metaschema.cli.processor.command.CommandService;
import dev.metaschema.cli.processor.command.ICommand;
import dev.metaschema.core.util.CollectionUtil;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

package dev.metaschema.cli.processor;

import static org.jline.jansi.Ansi.ansi;
import static dev.metaschema.cli.processor.ansi.Ansi.ansi;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.DefaultParser;
Expand Down Expand Up @@ -431,9 +431,14 @@ private String buildHelpFooter(int terminalWidth) {
String wrappedDesc = wrapText(command.getDescription(), descWidth, continuationIndent);
builder.append(
ansi()
.render(String.format(" @|bold %-" + commandColWidth + "s|@ %s%n",
command.getName(),
wrappedDesc)));
.a(" ")
.bold()
.format("%-" + commandColWidth + "s", command.getName())
.boldOff()
.a(' ')
.a(wrappedDesc)
.a(System.lineSeparator())
.toString());
}
builder
.append(System.lineSeparator())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
/*
* SPDX-FileCopyrightText: none
* SPDX-License-Identifier: CC0-1.0
*/

package dev.metaschema.cli.processor.ansi;

import java.util.concurrent.atomic.AtomicBoolean;

import edu.umd.cs.findbugs.annotations.NonNull;

/**
* A minimal ANSI escape-code builder used by the CLI to emit colored and
* formatted terminal output.
* <p>
* Emits a subset of
* <a href= "https://en.wikipedia.org/wiki/ANSI_escape_code#Colors">Select
* Graphic Rendition</a> codes for foreground colors, bold, and reset. When
* globally disabled via {@link #setEnabled(boolean)}, escape sequences are
* suppressed while appended literal text is preserved so output remains
* readable on terminals that do not interpret ANSI codes.
*/
public final class Ansi {
private static final String ESC = "\u001B[";
private static final String RESET_SEQ = ESC + "0m";
private static final String BOLD_SEQ = ESC + "1m";
private static final String BOLD_OFF_SEQ = ESC + "22m";

private static final AtomicBoolean ENABLED = new AtomicBoolean(true);

@NonNull
private final StringBuilder buffer = new StringBuilder();

private Ansi() {
// use ansi() factory
}

/**
* Create a new builder.
*
* @return a fresh builder with empty contents
*/
@NonNull
public static Ansi ansi() {
return new Ansi();
}

/**
* Globally enable or disable emission of ANSI escape codes.
* <p>
* When disabled, all color and style methods are no-ops; literal appended text
* is still emitted.
*
* @param enable
* {@code true} to emit escape sequences, {@code false} to suppress
* them
*/
public static void setEnabled(boolean enable) {
ENABLED.set(enable);
}

/**
* Indicates whether ANSI escape code emission is currently enabled.
*
* @return {@code true} if enabled
*/
public static boolean isEnabled() {
return ENABLED.get();
}

@NonNull
private Ansi emit(@NonNull String sequence) {
if (ENABLED.get()) {
buffer.append(sequence);
}
return this;
}

/**
* Append a single literal character.
*
* @param ch
* the character to append
* @return {@code this} for chaining
*/
@NonNull
public Ansi a(char ch) {
buffer.append(ch);
return this;
}

/**
* Append a literal string.
*
* @param text
* the text to append
* @return {@code this} for chaining
*/
@NonNull
public Ansi a(@NonNull CharSequence text) {
buffer.append(text);
return this;
}

/**
* Append formatted text using {@link String#format(String, Object...)}
* semantics.
*
* @param format
* the format string
* @param args
* the format arguments
* @return {@code this} for chaining
*/
@NonNull
public Ansi format(@NonNull String format, Object... args) {
buffer.append(String.format(format, args));
return this;
}

/**
* Emit the ANSI reset sequence, clearing any active color or style.
*
* @return {@code this} for chaining
*/
@NonNull
public Ansi reset() {
return emit(RESET_SEQ);
}

/**
* Enable bold rendering for subsequent appended text.
*
* @return {@code this} for chaining
*/
@NonNull
public Ansi bold() {
return emit(BOLD_SEQ);
}

/**
* Disable bold rendering for subsequent appended text.
*
* @return {@code this} for chaining
*/
@NonNull
public Ansi boldOff() {
return emit(BOLD_OFF_SEQ);
}

/**
* Set the foreground color to red.
*
* @return {@code this} for chaining
*/
@NonNull
public Ansi fgRed() {
return emit(ESC + "31m");
}

/**
* Set the foreground color to bright red.
*
* @return {@code this} for chaining
*/
@NonNull
public Ansi fgBrightRed() {
return emit(ESC + "91m");
}

/**
* Set the foreground color to bright yellow.
*
* @return {@code this} for chaining
*/
@NonNull
public Ansi fgBrightYellow() {
return emit(ESC + "93m");
}

/**
* Set the foreground color to bright blue.
*
* @return {@code this} for chaining
*/
@NonNull
public Ansi fgBrightBlue() {
return emit(ESC + "94m");
}

/**
* Set the foreground color to bright cyan.
*
* @return {@code this} for chaining
*/
@NonNull
public Ansi fgBrightCyan() {
return emit(ESC + "96m");
}

/**
* Set the foreground color to the bright variant of the supplied color.
*
* @param color
* the base color
* @return {@code this} for chaining
*/
@NonNull
public Ansi fgBright(@NonNull Color color) {
return emit(ESC + (90 + color.ordinal()) + "m");
}

@Override
public String toString() {
return buffer.toString();
}

/**
* Standard 8 ANSI foreground colors. Ordinals align with the standard color
* codes (0-7) so bright variants are derived by adding 90.
*/
public enum Color {
/** Black (code 30, bright 90). */
BLACK,
/** Red (code 31, bright 91). */
RED,
/** Green (code 32, bright 92). */
GREEN,
/** Yellow (code 33, bright 93). */
YELLOW,
/** Blue (code 34, bright 94). */
BLUE,
/** Magenta (code 35, bright 95). */
MAGENTA,
/** Cyan (code 36, bright 96). */
CYAN,
/** White (code 37, bright 97). */
WHITE;
}
}
2 changes: 1 addition & 1 deletion cli-processor/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@
requires static org.eclipse.jdt.annotation;
requires static com.github.spotbugs.annotations;

requires org.jansi.core;
requires nl.talsmasoftware.lazy4j;
requires org.apache.logging.log4j;
requires org.apache.logging.log4j.core;
requires org.apache.logging.log4j.jul;

exports dev.metaschema.cli.processor;
exports dev.metaschema.cli.processor.ansi;
exports dev.metaschema.cli.processor.command;
exports dev.metaschema.cli.processor.command.impl;
exports dev.metaschema.cli.processor.completion;
Expand Down
Loading
Loading