Skip to content

Commit

Permalink
[#1186] Auto-enable ANSI colors on MSYS2
Browse files Browse the repository at this point in the history
(Git for Windows, MSYS2-based Windows Terminal shells, etc.)
  • Loading branch information
remkop committed Sep 30, 2020
1 parent a6108ca commit cec5b86
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 6 deletions.
1 change: 1 addition & 0 deletions RELEASE-NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Picocli follows [semantic versioning](http://semver.org/).


## <a name="4.5.2-fixes"></a> Fixed issues
* [#1186] Enhancement: Auto-enable ANSI colors on MSYS2 (Git for Windows, MSYS2-based Windows Terminal shells, etc.). Thanks to [Sysmat](https://github.com/sysmat) for raising this.
* [#1162] Bugfix: Abbreviated options are not matched if value attached with '=' separator (like `-x=3`). Thanks to [Chris Laprun](https://github.com/metacosm) for raising this.
* [#1156][#1172] Bugfix: the built-in `HelpCommand` now respects subcommands case-sensitivity and abbreviations. Thanks to [NewbieOrange](https://github.com/NewbieOrange) for the pull request.
* [#1158] DOC: Fix broken links to GraalVM repo. Thanks to [Andreas Deininger](https://github.com/deining) for the pull request.
Expand Down
4 changes: 2 additions & 2 deletions docs/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -4353,12 +4353,12 @@ Below is the exact sequence of steps picocli uses to determine whether or not to
. ANSI is enabled when system property `os.name` starts with `"Windows"` and JAnsi Console is https://github.com/fusesource/jansi[installed].
. ANSI is disabled when environment variable https://bixense.com/clicolors/[`CLICOLOR == 0`].
. ANSI is disabled when environment variable https://conemu.github.io/en/AnsiEscapeCodes.html#Environment_variable[`ConEmuANSI == OFF`].
. ANSI is disabled when Picocli https://stackoverflow.com/questions/1403772/how-can-i-check-if-a-java-programs-input-output-streams-are-connected-to-a-term[_guesses_] the program's output stream is not connected to a terminal: when `System.console()` returns `null`. This check is omitted if picocli _guesses_ the program is running in a Windows https://www.cygwin.com/[Cygwin] or http://www.mingw.org/wiki/MSYS[MSYS] environment: when system property `os.name` starts with `"Windows"` and either environment variable `TERM` starts with `xterm` or environment variable `OSTYPE` is defined.
. ANSI is disabled when Picocli https://stackoverflow.com/questions/1403772/how-can-i-check-if-a-java-programs-input-output-streams-are-connected-to-a-term[_guesses_] the program's output stream is not connected to a terminal: when `System.console()` returns `null`. This check is omitted if picocli _guesses_ the program is running in a Windows https://www.cygwin.com/[Cygwin], http://www.mingw.org/wiki/MSYS[MSYS] or https://www.msys2.org/[MSYS2] environment: when system property `os.name` starts with `"Windows"` and either environment variable `TERM` contains `cygwin` or starts with `xterm` or environment variable `OSTYPE` is defined.
. ANSI is enabled when environment variable https://github.com/adoxa/ansicon/blob/master/readme.txt[`ANSICON`] is defined.
. ANSI is enabled when environment variable https://bixense.com/clicolors/[`CLICOLOR == 1`].
. ANSI is enabled when environment variable https://conemu.github.io/en/AnsiEscapeCodes.html#Environment_variable[`ConEmuANSI == ON`].
. ANSI is enabled when picocli detects the program is running in a non-Windows OS (system property `os.name` does not start with `"Windows"`).
. ANSI is enabled when picocli _guesses_ the program is running in a https://www.cygwin.com/[Cygwin] or http://www.mingw.org/wiki/MSYS[MSYS] environment (either environment variable `TERM` starts with `xterm` or environment variable `OSTYPE` is defined).
. ANSI is enabled when picocli _guesses_ the program is running in a https://www.cygwin.com/[Cygwin], http://www.mingw.org/wiki/MSYS[MSYS] or https://www.msys2.org/[MSYS2] environment (either environment variable `TERM` contains `cygwin` or starts with `xterm` or environment variable `OSTYPE` is defined).

ANSI escape codes are not emitted if none of the above apply.

Expand Down
5 changes: 3 additions & 2 deletions src/main/java/picocli/CommandLine.java
Original file line number Diff line number Diff line change
Expand Up @@ -16402,6 +16402,7 @@ static boolean isTTY() {
static final boolean isWindows() { return System.getProperty("os.name").toLowerCase().contains("win"); }
static final boolean isMac() { return System.getProperty("os.name").toLowerCase().contains("mac"); }
static final boolean isXterm() { return System.getenv("TERM") != null && System.getenv("TERM").startsWith("xterm"); }
static final boolean isCygwin() { return System.getenv("TERM") != null && System.getenv("TERM").toLowerCase(ENGLISH).contains("cygwin"); }
// null on Windows unless on Cygwin or MSYS
static final boolean hasOsType() { return System.getenv("OSTYPE") != null; }

Expand All @@ -16428,15 +16429,15 @@ static boolean calcTTY() {
catch (Throwable reflectionFailed) { return true; }
}
/** Cygwin and MSYS use pseudo-tty and console is always null... */
static boolean isPseudoTTY() { return isWindows() && (isXterm() || hasOsType()); }
static boolean isPseudoTTY() { return isWindows() && (isXterm() || isCygwin() || hasOsType()); }

static boolean ansiPossible() {
if (forceDisabled()) { return false; }
if (forceEnabled()) { return true; }
if (isWindows() && isJansiConsoleInstalled()) { return true; } // #630 JVM crash loading jansi.AnsiConsole on Linux
if (hintDisabled()) { return false; }
if (!isTTY() && !isPseudoTTY()) { return false; }
return hintEnabled() || !isWindows() || isXterm() || hasOsType();
return hintEnabled() || !isWindows() || isXterm() || isCygwin() || hasOsType();
}
static boolean isJansiConsoleInstalled() {
try {
Expand Down
32 changes: 30 additions & 2 deletions src/test/java/picocli/HelpAnsiTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Locale;

import static org.junit.Assert.*;
import static picocli.TestUtil.usageString;
Expand Down Expand Up @@ -110,10 +111,11 @@ public void testAnsiEnabled() {
System.clearProperty("picocli.ansi");
boolean isWindows = System.getProperty("os.name").startsWith("Windows");
boolean isXterm = System.getenv("TERM") != null && System.getenv("TERM").startsWith("xterm");
boolean isCygwin = System.getenv("TERM") != null && System.getenv("TERM").toLowerCase(Locale.ENGLISH).contains("cygwin");
boolean hasOsType = System.getenv("OSTYPE") != null; // null on Windows unless on Cygwin or MSYS
boolean isAtty = (isWindows && (isXterm || hasOsType)) // cygwin pseudo-tty
|| hasConsole();
assertEquals((isAtty && (!isWindows || isXterm || hasOsType)) || isJansiConsoleInstalled(), Ansi.AUTO.enabled());
assertEquals((isAtty && (!isWindows || isXterm || isCygwin || hasOsType)) || isJansiConsoleInstalled(), Ansi.AUTO.enabled());

if (isWindows && !Ansi.AUTO.enabled()) {
AnsiConsole.systemInstall();
Expand Down Expand Up @@ -674,6 +676,24 @@ public void testAnsiIsXtermDependsOnEnvironmentVariable() {
assertTrue(Ansi.isXterm());
}

@Test
public void testAnsiIsCygwinDependsOnEnvironmentVariable() {
environmentVariables.clear(ANSI_ENVIRONMENT_VARIABLES);
assertFalse(Ansi.isCygwin());

environmentVariables.set("TERM", "random value");
assertFalse(Ansi.isCygwin());

environmentVariables.set("TERM", "xterm");
assertFalse(Ansi.isCygwin());

environmentVariables.set("TERM", "xterm cygwin");
assertTrue(Ansi.isCygwin());

environmentVariables.set("TERM", "cygwin");
assertTrue(Ansi.isCygwin());
}

@Test
public void testAnsiHasOstypeDependsOnEnvironmentVariable() {
environmentVariables.clear(ANSI_ENVIRONMENT_VARIABLES);
Expand All @@ -687,7 +707,7 @@ public void testAnsiHasOstypeDependsOnEnvironmentVariable() {
}

@Test
public void testAnsiIsPseudoTtyDependsOnWindowsXtermOrOsType() {
public void testAnsiIsPseudoTtyDependsOnWindowsXtermOrCygwinOrOsType() {
System.setProperty("os.name", "MMIX");
environmentVariables.clear(ANSI_ENVIRONMENT_VARIABLES);
assertFalse("OSTYPE and XTERM are not set", Ansi.isPseudoTTY());
Expand All @@ -704,6 +724,8 @@ public void testAnsiIsPseudoTtyDependsOnWindowsXtermOrOsType() {
assertTrue("restored", Ansi.isPseudoTTY());
environmentVariables.clear("OSTYPE");
assertTrue("Missing OSTYPE, but TERM=xterm", Ansi.isPseudoTTY());
environmentVariables.set("TERM", "abcygwinxyz");
assertTrue("Missing OSTYPE, but TERM=cygwin", Ansi.isPseudoTTY());

environmentVariables.set("OSTYPE", "anything");
assertTrue("restored", Ansi.isPseudoTTY());
Expand Down Expand Up @@ -951,6 +973,8 @@ public void testAnsiAutoHintDisabledOverridesHintEnabled() {
assertTrue(Ansi.isWindows());
environmentVariables.set("TERM", "xterm"); // fake Cygwin
assertTrue(Ansi.isPseudoTTY());
environmentVariables.set("TERM", "cygwin"); // fake Cygwin
assertTrue(Ansi.isPseudoTTY());

assertFalse(Ansi.isJansiConsoleInstalled());

Expand Down Expand Up @@ -1012,6 +1036,10 @@ public void testAnsiAutoEnabledIfWindowsPseudoTTY() {
assertTrue(Ansi.isPseudoTTY());
assertTrue("If have Cygwin pseudo-TTY, enabled on Windows", Ansi.AUTO.enabled());

environmentVariables.set("TERM", "cygwin");
assertTrue(Ansi.isPseudoTTY());
assertTrue("If have Cygwin pseudo-TTY, enabled on Windows", Ansi.AUTO.enabled());

environmentVariables.clear(ANSI_ENVIRONMENT_VARIABLES);
environmentVariables.set("OSTYPE", "Windows");
assertTrue(Ansi.isPseudoTTY());
Expand Down

0 comments on commit cec5b86

Please sign in to comment.