aboutsummaryrefslogtreecommitdiff
path: root/main.py
blob: 406549d3aa4f7ed97c48ee1bc3051af63bc37822 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#!/usr/bin/env python3

"""Simple script which adds matching rainbow colors to data read on stdin."""

import sys
from typing import Literal
from enum import auto, Enum
from dataclasses import dataclass, field

CSI = '\033['


def SGR(*xs):
    """
    Build a CSI escape sequence.

    https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_(Control_Sequence_Introducer)_sequences
    """
    return CSI + ';'.join(str(x) for x in xs) + 'm'


class Color(Enum):
    """Known CSI colors."""

    # BLACK = 30
    RED = 31
    GREEN = auto()
    YELLOW = auto()
    BLUE = auto()
    MAGENTA = auto()
    CYAN = auto()


@dataclass
class ColorGenerator:
    """
    Handles colors by "depth".

    :param colors:
        List of colors to use.
    :param _idx:
        Current index.
    """

    colors: list[Color] = field(default_factory=lambda: list(Color))
    _idx: int = 0

    def __call__(self, c: int):
        """
        Update the current color, and return it.

        :param c:
            Any positive or negative number changes the interval value
            by 1 or -1. 0 simply returns the current value.
        """
        if c > 0:
            ret = self.colors[self._idx % len(self.colors)]
            self._idx += 1
        elif c < 0:
            self._idx -= 1
            ret = self.colors[self._idx % len(self.colors)]
        else:
            ret = self.colors[self._idx % len(self.colors)]
        return ret


def color(color: Color, c: str):
    """Write a highlighted string."""
    sys.stdout.write(SGR(1, color.value))
    sys.stdout.write(c)
    sys.stdout.write(SGR())


def __main() -> None:
    in_string: Literal[False] | Literal['"'] | Literal["'"] = False

    paren = 0
    brace = 0
    brack = 0

    col = ColorGenerator()

    while c := sys.stdin.read(1):
        match c:
            case '(':
                color(col(1), c)
                paren += 1
            case ')':
                color(col(-1), c)
                paren -= 1
            case '[':
                color(col(1), c)
                brack += 1
            case ']':
                color(col(-1), c)
                brack -= 1
            case '{':
                color(col(1), c)
                brace += 1
            case '}':
                color(col(-1), c)
                brace -= 1
            case "'":
                if in_string == "'":
                    in_string = False
                else:
                    in_string = "'"
                sys.stdout.write(c)
            case '"':
                if in_string == '"':
                    in_string = False
                else:
                    in_string = '"'
                sys.stdout.write(c)
            case '\\':
                sys.stdout.write(sys.stdin.read(1))
            case c:
                sys.stdout.write(c)


if __name__ == '__main__':
    __main()