-
Notifications
You must be signed in to change notification settings - Fork 220
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Printing from the CLI previously often resulted in hard to read output because messages were flushed on each newline rather than each log message/write to stdout/stderr, causing contiguous strings of text that contain newlines (like model validation events) to be interleaved between threads. This change updates the CLI to use a PrintWriter instead of a PrintStream to fix this. Additionally, added a new API for creating grouped sections of text from a CliPrinter that includes styling methods. Forcing color/no_color is now done only through environment variables (FORCE_COLOR, NO_COLOR). This matches lots of other CLI tools, and removes the clutter of the old options from help output. If the old options are used, a warning is logged and they are ignored. The way in which colors are detected and rendered is updated too. This change introduces an Ansi enum that contains AUTO, FORCE_COLOR, and NO_COLOR members. AUTO will auto-detect whether colors are supported using a heuristic, and dispatch to FORCE_COLOR and NO_COLOR based on if colors are supported. CliPrinter instances have a reference to Ansi and use it to style text + provide a public API to get the Ansi color setting. The hueristic used to detect colors is now: 1. Is FORCE_COLOR set? colors 2. Is NO_COLOR set? no colors 3. Is TERM set and set to "dumb"? no colors 4. Is TERM null and the OS is Windows? no colors 5. Is a System console detected (i.e., interactive CLI that isn't redirected)? colors 6. no colors I also updated checkstyle to allow for empty methods with braces on the same line to accomodate some of the implementations I was added. I then went and made similar changes to classes in the CLI package to use this approach.
- Loading branch information
Showing
20 changed files
with
515 additions
and
293 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
150 changes: 150 additions & 0 deletions
150
smithy-cli/src/main/java/software/amazon/smithy/cli/Ansi.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
/* | ||
* Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"). | ||
* You may not use this file except in compliance with the License. | ||
* A copy of the License is located at | ||
* | ||
* http://aws.amazon.com/apache2.0 | ||
* | ||
* or in the "license" file accompanying this file. This file 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 software.amazon.smithy.cli; | ||
|
||
import java.io.IOException; | ||
import java.io.UncheckedIOException; | ||
import java.util.Objects; | ||
|
||
/** | ||
* Styles text using ANSI color codes. | ||
*/ | ||
public enum Ansi { | ||
|
||
/** | ||
* Writes using ANSI colors if it detects that the environment supports color. | ||
*/ | ||
AUTO { | ||
private final Ansi delegate = Ansi.detect(); | ||
|
||
@Override | ||
public String style(String text, Style... styles) { | ||
return delegate.style(text, styles); | ||
} | ||
|
||
@Override | ||
public void style(Appendable appendable, String text, Style... styles) { | ||
delegate.style(appendable, text, styles); | ||
} | ||
}, | ||
|
||
/** | ||
* Does not write any color. | ||
*/ | ||
NO_COLOR { | ||
@Override | ||
public String style(String text, Style... styles) { | ||
return text; | ||
} | ||
|
||
@Override | ||
public void style(Appendable appendable, String text, Style... styles) { | ||
try { | ||
appendable.append(text); | ||
} catch (IOException e) { | ||
throw new UncheckedIOException(e); | ||
} | ||
} | ||
}, | ||
|
||
/** | ||
* Writes with ANSI colors. | ||
*/ | ||
FORCE_COLOR { | ||
@Override | ||
public String style(String text, Style... styles) { | ||
StringBuilder builder = new StringBuilder(); | ||
style(builder, text, styles); | ||
return builder.toString(); | ||
} | ||
|
||
@Override | ||
public void style(Appendable appendable, String text, Style... styles) { | ||
try { | ||
appendable.append("\033["); | ||
boolean isAfterFirst = false; | ||
for (Style style : styles) { | ||
if (isAfterFirst) { | ||
appendable.append(';'); | ||
} | ||
appendable.append(style.toString()); | ||
isAfterFirst = true; | ||
} | ||
appendable.append('m'); | ||
appendable.append(text); | ||
appendable.append("\033[0m"); | ||
} catch (IOException e) { | ||
throw new CliError("Error writing output", 2, e); | ||
} | ||
} | ||
}; | ||
|
||
/** | ||
* Detects if ANSI colors are supported and returns the appropriate Ansi enum variant. | ||
* | ||
* <p>This method differs from using the {@link Ansi#AUTO} variant directly because it will detect any changes | ||
* to the environment that might enable or disable colors. | ||
* | ||
* @return Returns the detected ANSI color enum variant. | ||
*/ | ||
public static Ansi detect() { | ||
return isAnsiEnabled() ? FORCE_COLOR : NO_COLOR; | ||
} | ||
|
||
/** | ||
* Styles text using ANSI color codes. | ||
* | ||
* @param text Text to style. | ||
* @param styles Styles to apply. | ||
* @return Returns the styled text. | ||
*/ | ||
public abstract String style(String text, Style... styles); | ||
|
||
/** | ||
* Styles text using ANSI color codes and writes it to an Appendable. | ||
* | ||
* @param appendable Where to write styled text. | ||
* @param text Text to write. | ||
* @param styles Styles to apply. | ||
*/ | ||
public abstract void style(Appendable appendable, String text, Style... styles); | ||
|
||
private static boolean isAnsiEnabled() { | ||
if (EnvironmentVariable.FORCE_COLOR.isSet()) { | ||
return true; | ||
} | ||
|
||
// Disable colors if NO_COLOR is set to anything. | ||
if (EnvironmentVariable.NO_COLOR.isSet()) { | ||
return false; | ||
} | ||
|
||
String term = EnvironmentVariable.TERM.get(); | ||
|
||
// If term is set to "dumb", then don't use colors. | ||
if (Objects.equals(term, "dumb")) { | ||
return false; | ||
} | ||
|
||
// If TERM isn't set at all and Windows is detected, then don't use colors. | ||
if (term == null && System.getProperty("os.name").contains("win")) { | ||
return false; | ||
} | ||
|
||
// Disable colors if no console is associated. | ||
return System.console() != null; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.