Skip to content

Commit

Permalink
Add support for ANSI colors on Windows
Browse files Browse the repository at this point in the history
Windows Terminal is the Default in Windows 11, so there is no reason to
not support ANSI colors on Windows.

Explicitly disable ANSI colors on WASI, since ANSI escape characters are
disabled for security reasons.
  • Loading branch information
perillo authored and DimitriPapadopoulos committed Dec 29, 2023
1 parent f6a0fcb commit 5facd6f
Showing 1 changed file with 41 additions and 4 deletions.
45 changes: 41 additions & 4 deletions codespell_lib/_codespell.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@

import argparse
import configparser
import ctypes
import fnmatch
import os
import re
import sys
import textwrap
from ctypes import wintypes
from typing import Any, Dict, List, Match, Optional, Pattern, Sequence, Set, Tuple

# autogenerated by setuptools_scm
Expand Down Expand Up @@ -120,6 +122,10 @@
EX_DATAERR = 65
EX_CONFIG = 78

# Windows specific constants
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004
STD_OUTPUT_HANDLE = wintypes.HANDLE(-11)

# OPTIONS:
#
# ARGUMENTS:
Expand Down Expand Up @@ -308,21 +314,52 @@ def _toml_to_parseconfig(toml_dict: Dict[str, Any]) -> Dict[str, Any]:
return {k: "" if v is True else v for k, v in toml_dict.items() if v is not False}


def _supports_ansi_colors() -> bool:
if sys.platform == "win32":
# Windows Terminal enables ANSI escape codes by default. In other cases
# it is disabled.
# See https://ss64.com/nt/syntax-ansi.html for more information.
kernel32 = ctypes.WinDLL("kernel32")

# fmt: off
kernel32.GetConsoleMode.argtypes = (
wintypes.HANDLE, # _In_ hConsoleHandle
wintypes.LPDWORD, # _Out_ lpMode
)
# fmt: on
kernel32.GetConsoleMode.restype = wintypes.BOOL

mode = wintypes.DWORD()
handle = kernel32.GetStdHandle(STD_OUTPUT_HANDLE)
if not kernel32.GetConsoleMode(handle, ctypes.byref(mode)):
# TODO: print a warning with the error message on stderr?
return False

return (mode.value & ENABLE_VIRTUAL_TERMINAL_PROCESSING) != 0
elif sys.platform == "wasi":
# WASI disables ANSI escape codes for security reasons.
# See https://github.com/WebAssembly/WASI/issues/162.
return False
elif sys.stdout.isatty():
return True

return False


def parse_options(
args: Sequence[str],
) -> Tuple[argparse.Namespace, argparse.ArgumentParser, List[str]]:
parser = argparse.ArgumentParser(formatter_class=NewlineHelpFormatter)

parser.set_defaults(colors=sys.stdout.isatty())
parser.set_defaults(colors=_supports_ansi_colors())
parser.add_argument("--version", action="version", version=VERSION)

parser.add_argument(
"-d",
"--disable-colors",
action="store_false",
dest="colors",
help="disable colors, even when printing to terminal "
"(always set for Windows)",
help="disable colors, even when printing to terminal",
)
parser.add_argument(
"-c",
Expand Down Expand Up @@ -1130,7 +1167,7 @@ def main(*args: str) -> int:
for dictionary in use_dictionaries:
build_dict(dictionary, misspellings, ignore_words)
colors = TermColors()
if not options.colors or sys.platform == "win32":
if not options.colors:
colors.disable()

if options.summary:
Expand Down

0 comments on commit 5facd6f

Please sign in to comment.