Skip to content

Instantly share code, notes, and snippets.

@tkoz0
Created January 18, 2025 12:35
Show Gist options
  • Save tkoz0/6648c5ee1573bd1fb5bc424f340a99de to your computer and use it in GitHub Desktop.
Save tkoz0/6648c5ee1573bd1fb5bc424f340a99de to your computer and use it in GitHub Desktop.
improved ansi escape codes for terminal color and text formatting
'''
basic utils for ansi escape codes in terminal
- foreground color
- background color
- font styles
- 8 bit color
- 24 bit rgb color
- cursor movement
- screen clearing
- scrolling
'''
class _escm:
# internal class for ansi escape codes
# only supports the 'm' command for text color/format
# support concatenations with each other
# support concatenations with strings
# example: red '\\033[31m'
# example: rgb '\\033[38;2;127;255;0m'
def __init__(self, *l: int):
self.l: tuple[int,...] = l
def __eq__(self, l):
return isinstance(l,_escm) and self.l == l.l
def __str__(self) -> str:
return f'\033[{";".join(str(n) for n in self.l)}m'
def __repr__(self) -> str:
return f'{type(self).__name__}({",".join(str(n) for n in self.l)})'
def __iadd__(self, l) -> '_escm':
if isinstance(l,_escm):
self.l += l.l
return self
else:
return NotImplemented
def __add__(self, l) -> '_escm|str':
if isinstance(l,str):
return str(self) + l
elif isinstance(l,_escm):
return _escm(*self.l,*l.l)
else:
return NotImplemented
def __radd__(self, l) -> '_escm|str':
if isinstance(l,str):
return l + str(self)
elif isinstance(l,_escm):
return _escm(*l.l,*self.l)
else:
return NotImplemented
class TEXT_STYLE:
'''
text styles
'''
RESET = _escm(0)
BOLD = _escm(1)
FAINT = _escm(2)
ITALIC = _escm(3)
ULINE = _escm(4)
BLINK_SLOW = _escm(5)
BLINK_FAST = _escm(6)
INVERT_ON = _escm(7)
HIDE_ON = _escm(8)
CROSS_ON = _escm(9)
ULINE2 = _escm(21)
NORM_INTENSITY = _escm(22)
NORM_STYLE = _escm(23)
ULINE_OFF = _escm(24)
BLINK_OFF = _escm(25)
INVERT_OFF = _escm(27)
HIDE_OFF = _escm(28)
CROSS_OFF = _escm(29)
@staticmethod
def FONT(n: int = 0) -> _escm:
'''
font style 0-10, probably not supported in terminal
'''
if n < 0 or n >= 10:
raise ValueError()
return _escm(10+n)
def _fg8(n: int) -> _escm:
if n < 0 or n >= 256:
raise ValueError()
return _escm(38,5,n)
def _fg24(r: int, g: int, b: int) -> _escm:
if r < 0 or r >= 256 or g < 0 or g >= 256 or b < 0 or b >= 256:
raise ValueError()
return _escm(38,2,r,g,b)
class FG_COLOR:
'''
foreground colors
- 8 colors (3 bit)
- bright (B_) colors (extends to 4 bit)
'''
BLACK = _escm(30)
RED = _escm(31)
GREEN = _escm(32)
YELLOW = _escm(33)
BLUE = _escm(34)
MAGENTA = _escm(35)
CYAN = _escm(36)
WHITE = _escm(37)
B_BLACK = _escm(90)
B_RED = _escm(91)
B_GREEN = _escm(92)
B_YELLOW = _escm(93)
B_BLUE = _escm(94)
B_MAGENTA = _escm(95)
B_CYAN = _escm(96)
B_WHITE = _escm(97)
@staticmethod
def FG_8(n: int) -> _escm:
'''
8 bit color value
- 0-7 = standard
- 8-15 = bright
- 16-231 = 6x66 rgb cube (16+36*r+6*g+b with 0<=r,g,b<6)
- 232-255 = grayscale (black to white)
'''
return _fg8(n)
@staticmethod
def FG_8_GRAY(n: int) -> _escm:
'''
8 bit gray color (0-23 black to white)
'''
if n < 0 or n >= 24:
raise ValueError()
return _fg8(232+n)
@staticmethod
def FG_8_RGB(r: int, g: int, b: int) -> _escm:
'''
8 bit rgb color (each component is 0-5)
'''
if r < 0 or r >= 6 or g < 0 or g >= 6 or b < 0 or b >= 6:
raise ValueError()
return _fg8(16+36*r+6*g+b)
@staticmethod
def FG_24(r: int, g: int, b: int) -> _escm:
'''
standard 24 bit rgb color
'''
return _fg24(r,g,b)
def _bg8(n: int) -> _escm:
if n < 0 or n >= 256:
raise ValueError()
return _escm(48,5,n)
def _bg24(r: int, g: int, b: int) -> _escm:
if r < 0 or r >= 256 or g < 0 or g >= 256 or b < 0 or b >= 256:
raise ValueError()
return _escm(48,2,r,g,b)
class BG_COLOR:
'''
background colors
- 8 colors (3 bit)
- bright (B_) colors (extends to 4 bit)
'''
BLACK = _escm(40)
RED = _escm(41)
GREEN = _escm(42)
YELLOW = _escm(43)
BLUE = _escm(44)
MAGENTA = _escm(45)
CYAN = _escm(46)
WHITE = _escm(47)
B_BLACK = _escm(100)
B_RED = _escm(101)
B_GREEN = _escm(102)
B_YELLOW = _escm(103)
B_BLUE = _escm(104)
B_MAGENTA = _escm(105)
B_CYAN = _escm(106)
B_WHITE = _escm(107)
@staticmethod
def BG_8(n: int) -> _escm:
'''
8 bit color value
- 0-7 = standard
- 8-15 = bright
- 16-231 = 6x66 rgb cube (16+36*r+6*g+b with 0<=r,g,b<6)
- 232-255 = grayscale (black to white)
'''
return _bg8(n)
@staticmethod
def BG_8_GRAY(n: int) -> _escm:
'''
8 bit gray color (0-23 black to white)
'''
if n < 0 or n >= 24:
raise ValueError()
return _bg8(232+n)
@staticmethod
def BG_8_RGB(r: int, g: int, b: int) -> _escm:
'''
8 bit rgb color (each component is 0-5)
'''
if r < 0 or r >= 6 or g < 0 or g >= 6 or b < 0 or b >= 6:
raise ValueError()
return _bg8(16+36*r+6*g+b)
@staticmethod
def BG_24(r: int, g: int, b: int) -> _escm:
'''
standard 24 bit rgb color
'''
return _bg24(r,g,b)
FG_ARR_BRIGHT = [_escm(n) for n in range(90,98)]
FG_ARR_4BIT = [_escm(n) for n in range(30,38)] + FG_ARR_BRIGHT
BG_ARR_BRIGHT = [_escm(n) for n in range(100,108)]
BG_ARR_4BIT = [_escm(n) for n in range(40,48)] + BG_ARR_BRIGHT
class CURSOR:
'''
cursor movement
'''
@staticmethod
def MOVE_TO(r: int, c: int) -> str:
if r < 0 or c < 0:
raise ValueError()
return f'\033[{r};{c}H'
@staticmethod
def MOVE_UP(n: int) -> str:
if n < 0:
raise ValueError()
return f'\033[{n}A'
@staticmethod
def MOVE_DOWN(n: int) -> str:
if n < 0:
raise ValueError()
return f'\033[{n}B'
@staticmethod
def MOVE_RIGHT(n: int) -> str:
if n < 0:
raise ValueError()
return f'\033[{n}C'
@staticmethod
def MOVE_LEFT(n: int) -> str:
if n < 0:
raise ValueError()
return f'\033[{n}D'
@staticmethod
def MOVE_LINE(n: int) -> str:
if n == 0:
raise ValueError()
elif n > 0:
return f'\033[{n}E'
else:
return f'\033[{n}F'
@staticmethod
def MOVE_COL(n: int) -> str:
if n < 0:
raise ValueError()
return f'\033[{n}G'
class CLEAR:
'''
screen/line clearing
'''
SCR_TO_END = '\033[0J'
SCR_TO_BEG = '\033[1J'
SCR_ALL = '\033[2J'
SCR_BUF = '\033[3J'
LINE_TO_END = '\033[0K'
LINE_TO_BEG = '\033[1K'
LINE_ALL = '\033[2K'
class SCROLL:
'''
scrolling
'''
@staticmethod
def UP(n: int) -> str:
if n < 1:
raise ValueError()
return f'\033[{n}S'
@staticmethod
def DOWN(n: int) -> str:
if n < 1:
raise ValueError()
return f'\033[{n}T'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment