LLDB mainline
AnsiTerminal.h
Go to the documentation of this file.
1//===---------------------AnsiTerminal.h ------------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#ifndef LLDB_UTILITY_ANSITERMINAL_H
10#define LLDB_UTILITY_ANSITERMINAL_H
11
12#define ANSI_FG_COLOR_BLACK 30
13#define ANSI_FG_COLOR_RED 31
14#define ANSI_FG_COLOR_GREEN 32
15#define ANSI_FG_COLOR_YELLOW 33
16#define ANSI_FG_COLOR_BLUE 34
17#define ANSI_FG_COLOR_PURPLE 35
18#define ANSI_FG_COLOR_CYAN 36
19#define ANSI_FG_COLOR_WHITE 37
20
21#define ANSI_FG_COLOR_BRIGHT_BLACK 90
22#define ANSI_FG_COLOR_BRIGHT_RED 91
23#define ANSI_FG_COLOR_BRIGHT_GREEN 92
24#define ANSI_FG_COLOR_BRIGHT_YELLOW 93
25#define ANSI_FG_COLOR_BRIGHT_BLUE 94
26#define ANSI_FG_COLOR_BRIGHT_PURPLE 95
27#define ANSI_FG_COLOR_BRIGHT_CYAN 96
28#define ANSI_FG_COLOR_BRIGHT_WHITE 97
29
30#define ANSI_BG_COLOR_BLACK 40
31#define ANSI_BG_COLOR_RED 41
32#define ANSI_BG_COLOR_GREEN 42
33#define ANSI_BG_COLOR_YELLOW 43
34#define ANSI_BG_COLOR_BLUE 44
35#define ANSI_BG_COLOR_PURPLE 45
36#define ANSI_BG_COLOR_CYAN 46
37#define ANSI_BG_COLOR_WHITE 47
38
39#define ANSI_BG_COLOR_BRIGHT_BLACK 100
40#define ANSI_BG_COLOR_BRIGHT_RED 101
41#define ANSI_BG_COLOR_BRIGHT_GREEN 102
42#define ANSI_BG_COLOR_BRIGHT_YELLOW 103
43#define ANSI_BG_COLOR_BRIGHT_BLUE 104
44#define ANSI_BG_COLOR_BRIGHT_PURPLE 105
45#define ANSI_BG_COLOR_BRIGHT_CYAN 106
46#define ANSI_BG_COLOR_BRIGHT_WHITE 107
47
48#define ANSI_SPECIAL_FRAMED 51
49#define ANSI_SPECIAL_ENCIRCLED 52
50
51#define ANSI_CTRL_NORMAL 0
52#define ANSI_CTRL_BOLD 1
53#define ANSI_CTRL_FAINT 2
54#define ANSI_CTRL_ITALIC 3
55#define ANSI_CTRL_UNDERLINE 4
56#define ANSI_CTRL_SLOW_BLINK 5
57#define ANSI_CTRL_FAST_BLINK 6
58#define ANSI_CTRL_IMAGE_NEGATIVE 7
59#define ANSI_CTRL_CONCEAL 8
60#define ANSI_CTRL_CROSSED_OUT 9
61
62#define ANSI_ESC_START "\033["
63#define ANSI_ESC_END "m"
64
65#define ANSI_STR(s) #s
66#define ANSI_DEF_STR(s) ANSI_STR(s)
67
68#define ANSI_ESCAPE1(s) ANSI_ESC_START ANSI_DEF_STR(s) ANSI_ESC_END
69
70#define ANSI_1_CTRL(ctrl1) "\033["##ctrl1 ANSI_ESC_END
71#define ANSI_2_CTRL(ctrl1, ctrl2) "\033["##ctrl1 ";"##ctrl2 ANSI_ESC_END
72
73#define ANSI_ESC_START_LEN 2
74
75// Cursor Position, set cursor to position [l, c] (default = [1, 1]).
76#define ANSI_CSI_CUP(...) ANSI_ESC_START #__VA_ARGS__ "H"
77// Reset cursor to position.
78#define ANSI_CSI_RESET_CURSOR ANSI_CSI_CUP()
79// Erase In Display.
80#define ANSI_CSI_ED(opt) ANSI_ESC_START #opt "J"
81// Erase complete viewport.
82#define ANSI_CSI_ERASE_VIEWPORT ANSI_CSI_ED(2)
83// Erase scrollback.
84#define ANSI_CSI_ERASE_SCROLLBACK ANSI_CSI_ED(3)
85
86// OSC (Operating System Commands)
87// https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
88#define OSC_ESCAPE_START "\033"
89#define OSC_ESCAPE_END "\x07"
90
91// https://conemu.github.io/en/AnsiEscapeCodes.html#ConEmu_specific_OSC
92#define OSC_PROGRESS_REMOVE OSC_ESCAPE_START "]9;4;0;0" OSC_ESCAPE_END
93#define OSC_PROGRESS_SHOW OSC_ESCAPE_START "]9;4;1;%u" OSC_ESCAPE_END
94#define OSC_PROGRESS_ERROR OSC_ESCAPE_START "]9;4;2;%u" OSC_ESCAPE_END
95#define OSC_PROGRESS_INDETERMINATE OSC_ESCAPE_START "]9;4;3;%u" OSC_ESCAPE_END
96
97#include "llvm/ADT/ArrayRef.h"
98#include "llvm/ADT/STLExtras.h"
99#include "llvm/ADT/StringRef.h"
100#include "llvm/Support/Locale.h"
101
102#include <string>
103
104namespace lldb_private {
105
106namespace ansi {
107
108inline std::string FormatAnsiTerminalCodes(llvm::StringRef format,
109 bool do_color = true) {
110 // Convert "${ansi.XXX}" tokens to ansi values or clear them if do_color is
111 // false.
112 // clang-format off
113 static const struct {
114 const char *name;
115 const char *value;
116 } g_color_tokens[] = {
117#define _TO_STR2(_val) #_val
118#define _TO_STR(_val) _TO_STR2(_val)
161#undef _TO_STR
162#undef _TO_STR2
163 };
164 // clang-format on
165 auto codes = llvm::ArrayRef(g_color_tokens);
166
167 static const char tok_hdr[] = "${ansi.";
168
169 std::string fmt;
170 while (!format.empty()) {
171 llvm::StringRef left, right;
172 std::tie(left, right) = format.split(tok_hdr);
173
174 fmt += left;
175
176 if (left == format && right.empty()) {
177 // The header was not found. Just exit.
178 break;
179 }
180
181 bool found_code = false;
182 for (const auto &code : codes) {
183 if (!right.consume_front(code.name))
184 continue;
185
186 if (do_color)
187 fmt.append(code.value);
188 found_code = true;
189 break;
190 }
191 format = right;
192 // If we haven't found a valid replacement value, we just copy the string
193 // to the result without any modifications.
194 if (!found_code)
195 fmt.append(tok_hdr);
196 }
197 return fmt;
198}
199
200inline std::tuple<llvm::StringRef, llvm::StringRef, llvm::StringRef>
201FindNextAnsiSequence(llvm::StringRef str) {
202 llvm::StringRef left;
203 llvm::StringRef right = str;
204
205 while (!right.empty()) {
206 const size_t start = right.find(ANSI_ESC_START);
207
208 // ANSI_ESC_START not found.
209 if (start == llvm::StringRef::npos)
210 return {str, {}, {}};
211
212 // Split the string around the current ANSI_ESC_START.
213 left = str.take_front(left.size() + start);
214 llvm::StringRef escape = right.substr(start);
215 right = right.substr(start + ANSI_ESC_START_LEN + 1);
216
217 const size_t end = right.find_first_not_of("0123456789;");
218
219 // ANSI_ESC_END found.
220 if (end < right.size() && (right[end] == 'm' || right[end] == 'G'))
221 return {left, escape.take_front(ANSI_ESC_START_LEN + 1 + end + 1),
222 right.substr(end + 1)};
223
224 // Maintain the invariant that str == left + right at the start of the loop.
225 left = str.take_front(left.size() + ANSI_ESC_START_LEN + 1);
226 }
227
228 return {str, {}, {}};
229}
230
231inline std::string StripAnsiTerminalCodes(llvm::StringRef str) {
232 std::string stripped;
233 while (!str.empty()) {
234 auto [left, escape, right] = FindNextAnsiSequence(str);
235 stripped += left;
236 str = right;
237 }
238 return stripped;
239}
240
241inline std::string TrimAndPad(llvm::StringRef str, size_t visible_length,
242 char padding = ' ') {
243 std::string result;
244 result.reserve(visible_length);
245 size_t result_visibile_length = 0;
246
247 // Trim the string to the given visible length.
248 while (!str.empty()) {
249 auto [left, escape, right] = FindNextAnsiSequence(str);
250 str = right;
251
252 // Compute the length of the string without escape codes. If it fits, append
253 // it together with the invisible escape code.
254 size_t column_width = llvm::sys::locale::columnWidth(left);
255 if (result_visibile_length + column_width <= visible_length) {
256 result.append(left).append(escape);
257 result_visibile_length += column_width;
258 continue;
259 }
260
261 // The string might contain unicode which means it's not safe to truncate.
262 // Repeatedly trim the string until it its valid unicode and fits.
263 llvm::StringRef trimmed = left;
264 while (!trimmed.empty()) {
265 // This relies on columnWidth returning -2 for invalid/partial unicode
266 // characters, which after conversion to size_t will be larger than the
267 // visible width.
268 column_width = llvm::sys::locale::columnWidth(trimmed);
269 if (result_visibile_length + column_width <= visible_length) {
270 result.append(trimmed);
271 result_visibile_length += column_width;
272 break;
273 }
274 trimmed = trimmed.drop_back();
275 }
276 }
277
278 // Pad the string.
279 if (result_visibile_length < visible_length)
280 result.append(visible_length - result_visibile_length, padding);
281
282 return result;
283}
284
285inline size_t ColumnWidth(llvm::StringRef str) {
286 std::string stripped = ansi::StripAnsiTerminalCodes(str);
287 return llvm::sys::locale::columnWidth(stripped);
288}
289
290} // namespace ansi
291} // namespace lldb_private
292
293#endif
#define ANSI_FG_COLOR_BRIGHT_GREEN
#define ANSI_BG_COLOR_BRIGHT_BLUE
#define ANSI_ESC_START
#define ANSI_BG_COLOR_PURPLE
#define ANSI_CTRL_UNDERLINE
#define ANSI_BG_COLOR_BRIGHT_WHITE
#define ANSI_BG_COLOR_BRIGHT_RED
#define ANSI_BG_COLOR_RED
#define ANSI_FG_COLOR_BLACK
#define ANSI_FG_COLOR_BRIGHT_CYAN
#define ANSI_FG_COLOR_BRIGHT_BLACK
#define ANSI_BG_COLOR_YELLOW
#define ANSI_FG_COLOR_BRIGHT_WHITE
#define ANSI_BG_COLOR_BLACK
#define ANSI_FG_COLOR_BRIGHT_YELLOW
#define ANSI_FG_COLOR_YELLOW
#define ANSI_FG_COLOR_PURPLE
#define ANSI_BG_COLOR_CYAN
#define ANSI_FG_COLOR_BRIGHT_RED
#define ANSI_BG_COLOR_BRIGHT_YELLOW
#define ANSI_BG_COLOR_GREEN
#define ANSI_CTRL_SLOW_BLINK
#define ANSI_FG_COLOR_RED
#define ANSI_BG_COLOR_BLUE
#define ANSI_CTRL_ITALIC
#define ANSI_FG_COLOR_BRIGHT_BLUE
#define ANSI_FG_COLOR_CYAN
#define ANSI_CTRL_FAST_BLINK
#define ANSI_CTRL_CROSSED_OUT
#define ANSI_CTRL_IMAGE_NEGATIVE
#define ANSI_BG_COLOR_WHITE
#define ANSI_CTRL_CONCEAL
#define ANSI_ESC_START_LEN
#define ANSI_FG_COLOR_GREEN
#define ANSI_CTRL_FAINT
#define ANSI_FG_COLOR_BLUE
#define ANSI_BG_COLOR_BRIGHT_GREEN
#define ANSI_FG_COLOR_BRIGHT_PURPLE
#define ANSI_CTRL_NORMAL
#define ANSI_BG_COLOR_BRIGHT_CYAN
#define ANSI_CTRL_BOLD
#define ANSI_FG_COLOR_WHITE
#define ANSI_BG_COLOR_BRIGHT_PURPLE
#define ANSI_ESC_END
#define ANSI_BG_COLOR_BRIGHT_BLACK
#define _TO_STR(_val)
std::string FormatAnsiTerminalCodes(llvm::StringRef format, bool do_color=true)
size_t ColumnWidth(llvm::StringRef str)
std::tuple< llvm::StringRef, llvm::StringRef, llvm::StringRef > FindNextAnsiSequence(llvm::StringRef str)
std::string TrimAndPad(llvm::StringRef str, size_t visible_length, char padding=' ')
std::string StripAnsiTerminalCodes(llvm::StringRef str)
A class that represents a running process on the host machine.