|
2 | 2 | # SPDX-FileCopyrightText: 2021 The Elixir Team |
3 | 3 | # SPDX-FileCopyrightText: 2012 Plataformatec |
4 | 4 |
|
5 | | -defmodule IO.ANSI.Sequence do |
6 | | - @moduledoc false |
7 | | - |
8 | | - defmacro defsequence(name, code, terminator \\ "m") do |
9 | | - quote bind_quoted: [name: name, code: code, terminator: terminator] do |
10 | | - @spec unquote(name)() :: String.t() |
11 | | - def unquote(name)() do |
12 | | - "\e[#{unquote(code)}#{unquote(terminator)}" |
13 | | - end |
14 | | - |
15 | | - defp format_sequence(unquote(name)) do |
16 | | - unquote(name)() |
17 | | - end |
18 | | - end |
19 | | - end |
20 | | -end |
21 | | - |
22 | 5 | defmodule IO.ANSI do |
23 | 6 | @moduledoc """ |
24 | 7 | Functionality to render ANSI escape sequences. |
@@ -53,8 +36,6 @@ defmodule IO.ANSI do |
53 | 36 | In case ANSI is disabled, the ANSI escape sequences are simply discarded. |
54 | 37 | """ |
55 | 38 |
|
56 | | - import IO.ANSI.Sequence |
57 | | - |
58 | 39 | @type ansicode :: atom |
59 | 40 | @type ansilist :: |
60 | 41 | maybe_improper_list(char | ansicode | binary | ansilist, binary | ansicode | []) |
@@ -129,104 +110,121 @@ defmodule IO.ANSI do |
129 | 110 | color_background(16 + 36 * r + 6 * g + b) |
130 | 111 | end |
131 | 112 |
|
| 113 | + defsequence = fn name, code, terminator -> |
| 114 | + @spec unquote(name)() :: String.t() |
| 115 | + def unquote(name)() do |
| 116 | + "\e[#{unquote(code)}#{unquote(terminator)}" |
| 117 | + end |
| 118 | + |
| 119 | + defp format_sequence(unquote(name)) do |
| 120 | + unquote(name)() |
| 121 | + end |
| 122 | + end |
| 123 | + |
132 | 124 | @doc "Resets all attributes." |
133 | | - defsequence(:reset, 0) |
| 125 | + defsequence.(:reset, 0, "m") |
134 | 126 |
|
135 | 127 | @doc "Bright (increased intensity) or bold." |
136 | | - defsequence(:bright, 1) |
| 128 | + defsequence.(:bright, 1, "m") |
137 | 129 |
|
138 | 130 | @doc "Faint (decreased intensity). Not widely supported." |
139 | | - defsequence(:faint, 2) |
| 131 | + defsequence.(:faint, 2, "m") |
140 | 132 |
|
141 | 133 | @doc "Italic: on. Not widely supported. Sometimes treated as inverse." |
142 | | - defsequence(:italic, 3) |
| 134 | + defsequence.(:italic, 3, "m") |
143 | 135 |
|
144 | 136 | @doc "Underline: single." |
145 | | - defsequence(:underline, 4) |
| 137 | + defsequence.(:underline, 4, "m") |
146 | 138 |
|
147 | 139 | @doc "Blink: slow. Less than 150 per minute." |
148 | | - defsequence(:blink_slow, 5) |
| 140 | + defsequence.(:blink_slow, 5, "m") |
149 | 141 |
|
150 | 142 | @doc "Blink: rapid. MS-DOS ANSI.SYS; 150 per minute or more; not widely supported." |
151 | | - defsequence(:blink_rapid, 6) |
| 143 | + defsequence.(:blink_rapid, 6, "m") |
152 | 144 |
|
153 | 145 | @doc "Image: negative. Swap foreground and background." |
154 | | - defsequence(:inverse, 7) |
| 146 | + defsequence.(:inverse, 7, "m") |
155 | 147 |
|
156 | 148 | @doc "Image: negative. Swap foreground and background." |
157 | | - defsequence(:reverse, 7) |
| 149 | + defsequence.(:reverse, 7, "m") |
158 | 150 |
|
159 | 151 | @doc "Conceal. Not widely supported." |
160 | | - defsequence(:conceal, 8) |
| 152 | + defsequence.(:conceal, 8, "m") |
161 | 153 |
|
162 | 154 | @doc "Crossed-out. Characters legible, but marked for deletion. Not widely supported." |
163 | | - defsequence(:crossed_out, 9) |
| 155 | + defsequence.(:crossed_out, 9, "m") |
164 | 156 |
|
165 | 157 | @doc "Sets primary (default) font." |
166 | | - defsequence(:primary_font, 10) |
| 158 | + defsequence.(:primary_font, 10, "m") |
167 | 159 |
|
168 | 160 | for font_n <- [1, 2, 3, 4, 5, 6, 7, 8, 9] do |
169 | 161 | @doc "Sets alternative font #{font_n}." |
170 | | - defsequence(:"font_#{font_n}", font_n + 10) |
| 162 | + defsequence.(:"font_#{font_n}", font_n + 10, "m") |
171 | 163 | end |
172 | 164 |
|
173 | 165 | @doc "Normal color or intensity." |
174 | | - defsequence(:normal, 22) |
| 166 | + defsequence.(:normal, 22, "m") |
175 | 167 |
|
176 | 168 | @doc "Not italic." |
177 | | - defsequence(:not_italic, 23) |
| 169 | + defsequence.(:not_italic, 23, "m") |
178 | 170 |
|
179 | 171 | @doc "Underline: none." |
180 | | - defsequence(:no_underline, 24) |
| 172 | + defsequence.(:no_underline, 24, "m") |
181 | 173 |
|
182 | 174 | @doc "Blink: off." |
183 | | - defsequence(:blink_off, 25) |
| 175 | + defsequence.(:blink_off, 25, "m") |
184 | 176 |
|
185 | 177 | @doc "Image: positive. Normal foreground and background." |
186 | | - defsequence(:inverse_off, 27) |
| 178 | + defsequence.(:inverse_off, 27, "m") |
187 | 179 |
|
188 | 180 | @doc "Image: positive. Normal foreground and background." |
189 | | - defsequence(:reverse_off, 27) |
| 181 | + defsequence.(:reverse_off, 27, "m") |
190 | 182 |
|
191 | 183 | colors = [:black, :red, :green, :yellow, :blue, :magenta, :cyan, :white] |
192 | 184 |
|
193 | 185 | for {color, code} <- Enum.with_index(colors) do |
194 | 186 | @doc "Sets foreground color to #{color}." |
195 | | - defsequence(color, code + 30) |
| 187 | + defsequence.(color, code + 30, "m") |
196 | 188 |
|
197 | 189 | @doc "Sets foreground color to light #{color}." |
198 | | - defsequence(:"light_#{color}", code + 90) |
| 190 | + defsequence.(:"light_#{color}", code + 90, "m") |
199 | 191 |
|
200 | 192 | @doc "Sets background color to #{color}." |
201 | | - defsequence(:"#{color}_background", code + 40) |
| 193 | + defsequence.(:"#{color}_background", code + 40, "m") |
202 | 194 |
|
203 | 195 | @doc "Sets background color to light #{color}." |
204 | | - defsequence(:"light_#{color}_background", code + 100) |
| 196 | + defsequence.(:"light_#{color}_background", code + 100, "m") |
205 | 197 | end |
206 | 198 |
|
207 | 199 | @doc "Default text color." |
208 | | - defsequence(:default_color, 39) |
| 200 | + defsequence.(:default_color, 39, "m") |
209 | 201 |
|
210 | 202 | @doc "Default background color." |
211 | | - defsequence(:default_background, 49) |
| 203 | + defsequence.(:default_background, 49, "m") |
212 | 204 |
|
213 | 205 | @doc "Framed." |
214 | | - defsequence(:framed, 51) |
| 206 | + defsequence.(:framed, 51, "m") |
215 | 207 |
|
216 | 208 | @doc "Encircled." |
217 | | - defsequence(:encircled, 52) |
| 209 | + defsequence.(:encircled, 52, "m") |
218 | 210 |
|
219 | 211 | @doc "Overlined." |
220 | | - defsequence(:overlined, 53) |
| 212 | + defsequence.(:overlined, 53, "m") |
221 | 213 |
|
222 | 214 | @doc "Not framed or encircled." |
223 | | - defsequence(:not_framed_encircled, 54) |
| 215 | + defsequence.(:not_framed_encircled, 54, "m") |
224 | 216 |
|
225 | 217 | @doc "Not overlined." |
226 | | - defsequence(:not_overlined, 55) |
| 218 | + defsequence.(:not_overlined, 55, "m") |
| 219 | + |
| 220 | + @doc "Clears screen." |
| 221 | + defsequence.(:clear, "2", "J") |
| 222 | + |
| 223 | + @doc "Clears line." |
| 224 | + defsequence.(:clear_line, "2", "K") |
227 | 225 |
|
228 | 226 | @doc "Sends cursor home." |
229 | | - defsequence(:home, "", "H") |
| 227 | + defsequence.(:home, "", "H") |
230 | 228 |
|
231 | 229 | @doc """ |
232 | 230 | Sends cursor to the absolute position specified by `line` and `column`. |
@@ -255,12 +253,6 @@ defmodule IO.ANSI do |
255 | 253 | @spec cursor_left(pos_integer) :: String.t() |
256 | 254 | def cursor_left(columns \\ 1) when is_integer(columns) and columns >= 1, do: "\e[#{columns}D" |
257 | 255 |
|
258 | | - @doc "Clears screen." |
259 | | - defsequence(:clear, "2", "J") |
260 | | - |
261 | | - @doc "Clears line." |
262 | | - defsequence(:clear_line, "2", "K") |
263 | | - |
264 | 256 | defp format_sequence(other) do |
265 | 257 | raise ArgumentError, "invalid ANSI sequence specification: #{inspect(other)}" |
266 | 258 | end |
|
0 commit comments