Skip to content

Commit 0ee2f44

Browse files
committed
feat: support for ANSI color sequences(#338)
1 parent fe81965 commit 0ee2f44

File tree

7 files changed

+1050
-3
lines changed

7 files changed

+1050
-3
lines changed

src/ui/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ add_library(
4646
${CMAKE_CURRENT_SOURCE_DIR}/include/decompressor.h
4747
${CMAKE_CURRENT_SOURCE_DIR}/include/fontutils.h
4848
${CMAKE_CURRENT_SOURCE_DIR}/include/colorlabelsmanager.h
49+
${CMAKE_CURRENT_SOURCE_DIR}/include/ANSI.h
50+
${CMAKE_CURRENT_SOURCE_DIR}/include/SGRParser.h
51+
${CMAKE_CURRENT_SOURCE_DIR}/include/ColorfulTextParser.h
4952
${CMAKE_CURRENT_SOURCE_DIR}/include/highlighteredit.ui
5053
${CMAKE_CURRENT_SOURCE_DIR}/include/highlightersetedit.ui
5154
${CMAKE_CURRENT_SOURCE_DIR}/include/highlightersdialog.ui
@@ -91,6 +94,8 @@ add_library(
9194
${CMAKE_CURRENT_SOURCE_DIR}/src/downloader.cpp
9295
${CMAKE_CURRENT_SOURCE_DIR}/src/decompressor.cpp
9396
${CMAKE_CURRENT_SOURCE_DIR}/src/colorlabelsmanager.cpp
97+
${CMAKE_CURRENT_SOURCE_DIR}/src/SGRParser.cpp
98+
${CMAKE_CURRENT_SOURCE_DIR}/src/ColorfulTextParser.cpp
9499
)
95100

96101
set_target_properties(klogg_ui PROPERTIES AUTOUIC ON)

src/ui/include/ANSI.h

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
//
2+
// Created by marvin on 23-7-18.
3+
//
4+
#pragma once
5+
6+
#include <cstdint>
7+
8+
namespace ANSI {
9+
10+
enum SequenceStartCnt {
11+
HEAD_CNT = 2,
12+
};
13+
14+
// reference: https://www.ecma-international.org/publications-and-standards/standards/ecma-48/
15+
enum SequenceFirst {
16+
EXC = 0x1B, // Escape(转义)
17+
};
18+
19+
// reference: https://www.ecma-international.org/publications-and-standards/standards/ecma-48/
20+
enum SequenceSecond {
21+
PAD = 0x40, // 0x80 Padding Character(填充字符)
22+
HOP = 0x41, // 0x81 High Octet Preset(高字节前置)
23+
BPH = 0x42, // 0x82 Break Permitted Here(此处允许中断)
24+
NBH = 0x43, // 0x83 No Break Here(此处禁止中断)
25+
IND = 0x44, // 0x84 Index(索引)
26+
NEL = 0x45, // 0x85 Next Line(下一行)
27+
SSA = 0x46, // 0x86 Start of Selected Area(选择区域开始)
28+
ESA = 0x47, // 0x87 End of Selected Area(选择区域结束)
29+
HTS = 0x48, // 0x88 Horizontal Tab Set(水平制表设置)
30+
HTJ = 0x49, // 0x89 Horizontal Tab Justified(水平制表调整)
31+
VTS = 0x4A, // 0x8A Vertical Tab Set(垂直制表设置)
32+
PLD = 0x4B, // 0x8B Partial Line Forward(部分行前移)
33+
PLU = 0x4C, // 0x8C Partial Line Backward(部分行后移)
34+
RI = 0x4D, // 0x8D Reverse Line Feed(逆向馈行)
35+
SS2 = 0x4E, // 0x8E Single-Shift 2(单个移动2)
36+
SS3 = 0x4F, // 0x8F Single-Shift 3(单个移动3)
37+
DCS = 0x50, // 0x90 Device Control String(设备控制串)
38+
PU1 = 0x51, // 0x91 Private Use 1(私用1)
39+
PU2 = 0x52, // 0x92 Private Use 2(私用2)
40+
STS = 0x53, // 0x93 Set Transmit State(发送规则设置)
41+
CCH = 0x54, // 0x94 Cancel Character(取消字符)
42+
MW = 0x55, // 0x95 Message Waiting(消息等待)
43+
SPA = 0x56, // 0x96 Start of Protected Area(保护区域开始)
44+
EPA = 0x57, // 0x97 End of Protected Area(保护区域结束)
45+
SOS = 0x58, // 0x98 Start of String(串开始)
46+
SGC = 0x59, // 0x99 Single Graphic Char Intro(单个图形字符描述)
47+
SCI = 0x5A, // 0x9A Single Char Intro(单个字符描述)
48+
CSI = 0x5B, // 0x9B Control Sequence Intro(控制顺序描述)
49+
ST = 0x5C, // 0x9C String Terminator(串终止)
50+
OSC = 0x5D, // 0x9D OS Command(操作系统指令)
51+
PM = 0x5E, // 0x9E Private Message(私讯)
52+
APC = 0x5F, // 0x9F App Program Command(应用程序命令)
53+
54+
SECOND_BYTE_BEGIN = PAD,
55+
SECOND_BYTE_END = APC,
56+
};
57+
58+
// reference: https://www.ecma-international.org/publications-and-standards/standards/ecma-48/
59+
enum CSIParameterBytes {
60+
// ASCII: 0–9:;<=>?
61+
NUM_BEGIN = 0x30,
62+
NUM_END = 0x39,
63+
SUB_PARA_SEPARATOR = 0x3A,
64+
PARA_SEPARATOR = 0x3B,
65+
STANDARDIZATION_KEEP_BEGIN = 0x3C,
66+
STANDARDIZATION_KEEP_END = 0x3F,
67+
68+
CSI_PARAMETER_BEGIN = 0x30,
69+
CSI_PARAMETER_END = 0x3F,
70+
};
71+
72+
enum CSIIntermediateBytes {
73+
// ASCII: Space、!"#$%&'()*+,-./
74+
CSIIntermediateBegin = 0x20,
75+
CSIIntermediateEnd = 0x2F,
76+
};
77+
78+
enum CSIFinalBytes {
79+
// ASCII: @A–Z[\]^_`a–z{|}~
80+
SGR = 0x6D, // m
81+
82+
CSIFinalBegin = 0x40,
83+
CSIFinalEnd = 0x7E,
84+
85+
CSIFinalExperimentalBegin = 0x70,
86+
CSIFinalExperimentalEnd = CSIFinalEnd,
87+
};
88+
89+
enum class Return {
90+
PARSE_ERROR = -1,
91+
PARSE_SUCC = 0,
92+
};
93+
94+
} // namespace ANSI

src/ui/include/ColorfulTextParser.h

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
//
2+
// Created by marvin on 23-7-24.
3+
//
4+
5+
#pragma once
6+
7+
#include <vector>
8+
9+
#include <QString>
10+
11+
#include "SGRParser.h"
12+
#include "highlightedmatch.h"
13+
14+
struct TextColorAttr {
15+
ANSI::Color color;
16+
size_t start;
17+
size_t len;
18+
operator HighlightedMatch();
19+
};
20+
21+
struct ColorfulText {
22+
std::string text;
23+
std::vector<TextColorAttr> color;
24+
};
25+
26+
class CSIFilter {
27+
public:
28+
CSIFilter() = default;
29+
~CSIFilter() = default;
30+
31+
// ANSI: start, data
32+
using SGRSequence = std::pair<size_t, std::string>;
33+
34+
static std::vector<SGRSequence> filter( QString& stdText );
35+
static std::vector<SGRSequence> filter( std::string& stdText );
36+
};
37+
38+
class ColorfulTextParser {
39+
public:
40+
enum class Mode {
41+
MARKED_TEXT,
42+
ALL_TEXT,
43+
};
44+
45+
public:
46+
explicit ColorfulTextParser( const ANSI::TextAttribute& defaultAttr,
47+
const ANSI::TextAttribute& currentAttr );
48+
49+
// QString
50+
ColorfulText parse( QString strings, Mode mode = Mode::ALL_TEXT );
51+
52+
std::vector<ColorfulText> parse( const std::vector<QString>& strings,
53+
Mode mode = Mode::ALL_TEXT );
54+
55+
// std::string
56+
ColorfulText parse( std::string strings, Mode mode = Mode::ALL_TEXT );
57+
58+
std::vector<ColorfulText> parse( const std::vector<std::string>& strings,
59+
Mode mode = Mode::ALL_TEXT );
60+
61+
private:
62+
// std::string
63+
void markedStringToText( std::vector<ColorfulText>& textList,
64+
const std::vector<CSIFilter::SGRSequence>& sgrSeqs,
65+
std::string&& string );
66+
void allStringToText( std::vector<ColorfulText>& textList,
67+
const std::vector<CSIFilter::SGRSequence>& sgrSeqs,
68+
std::string&& string );
69+
70+
private:
71+
ANSI::TextAttribute currentTextAttr_;
72+
ANSI::SGRParser sgrParser_;
73+
};

src/ui/include/SGRParser.h

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
//
2+
// Created by marvin on 23-7-19.
3+
//
4+
#pragma once
5+
6+
#include <map>
7+
#include <string>
8+
#include <utility>
9+
10+
#include "ANSI.h"
11+
12+
namespace ANSI {
13+
14+
struct RGB {
15+
uint8_t r, g, b;
16+
};
17+
18+
struct Color {
19+
RGB front;
20+
RGB back;
21+
};
22+
23+
struct TextAttribute {
24+
enum class State {
25+
DEFAULT,
26+
CUSTOM,
27+
};
28+
29+
State state;
30+
Color color;
31+
};
32+
33+
class SGRParser {
34+
public:
35+
using SGRParseReturn = std::pair<Return, TextAttribute>;
36+
37+
public:
38+
explicit SGRParser( const TextAttribute& defaultTextAttr );
39+
~SGRParser() = default;
40+
41+
SGRParser( const SGRParser& ) = delete;
42+
SGRParser( SGRParser&& ) = delete;
43+
SGRParser& operator=( const SGRParser& ) = delete;
44+
SGRParser& operator=( SGRParser&& ) = delete;
45+
46+
/*
47+
* @param currentTextAttr Properties of the current text
48+
* @param sequence SGR sequence, example: "\033[31m"
49+
* @return {return value, parsed text attribute}
50+
*
51+
* If the return value is ERROR, the parsed value is still guaranteed to be valid.
52+
*/
53+
SGRParseReturn parseSGRSequence( const TextAttribute& currentTextAttr,
54+
const std::string& sequence );
55+
56+
private:
57+
TextAttribute defaultTextAttr_;
58+
};
59+
60+
class ColorTable;
61+
62+
class SGRParseCore {
63+
friend class ColorTable;
64+
65+
public:
66+
enum class ReturnVal {
67+
RETURN_SUCCESS_BREAK,
68+
RETURN_SUCCESS_CONTINUE,
69+
70+
RETURN_ERROR_BREAK,
71+
RETURN_ERROR_CONTINUE,
72+
};
73+
74+
enum class ParseResult {
75+
RESULT_UNSUPPORTED_ATTR,
76+
RESULT_FRONT_COLOR,
77+
RESULT_BACK_COLOR,
78+
RESULT_DEFAULT_FRONT_COLOR,
79+
RESULT_DEFAULT_BACK_COLOR,
80+
RESULT_DEFAULT_TEXT_ATTR,
81+
RESULT_CURRENT_TEXT_ATTR,
82+
};
83+
84+
private:
85+
enum class ColorVersion : uint8_t {
86+
BIT_8 = 5,
87+
BIT_24 = 2,
88+
};
89+
90+
enum class ParseState {
91+
STATE_WAIT_FIRST_PARAMETER,
92+
STATE_WAIT_VERSION,
93+
STATE_WAIT_BIT_8_ARGS,
94+
STATE_WAIT_BIT_24_ARGS_R,
95+
STATE_WAIT_BIT_24_ARGS_G,
96+
STATE_WAIT_BIT_24_ARGS_B,
97+
};
98+
99+
public:
100+
SGRParseCore();
101+
~SGRParseCore() = default;
102+
103+
SGRParseCore( const SGRParseCore& ) = default;
104+
SGRParseCore( SGRParseCore&& ) = default;
105+
SGRParseCore& operator=( const SGRParseCore& ) = default;
106+
SGRParseCore& operator=( SGRParseCore&& ) = default;
107+
108+
ReturnVal parse( std::string_view& seqs );
109+
110+
inline void reset()
111+
{
112+
new ( this ) SGRParseCore();
113+
}
114+
115+
inline ParseResult result()
116+
{
117+
return result_;
118+
}
119+
120+
inline RGB color()
121+
{
122+
return color_;
123+
}
124+
125+
private:
126+
SGRParseCore( ParseResult result, RGB rgb,
127+
ParseState s = ParseState::STATE_WAIT_FIRST_PARAMETER );
128+
129+
ReturnVal stringToParameter( const std::string_view& in, uint8_t& out );
130+
131+
ReturnVal setFirstParameter( const std::string_view& num );
132+
ReturnVal setColorVersion( const std::string_view& num );
133+
ReturnVal setBit8Color( const std::string_view& num );
134+
ReturnVal setBit24Color( const std::string_view& num );
135+
136+
private:
137+
ParseResult result_;
138+
ParseState state_;
139+
RGB color_;
140+
bool bit24Valid_;
141+
};
142+
143+
class ColorTable {
144+
public:
145+
enum ColorIndex : uint8_t {
146+
RESET_DEFAULT = 0,
147+
148+
// 3/4-bit front color
149+
F_BLACK = 30,
150+
F_RED = 31,
151+
F_GREEN = 32,
152+
F_YELLOW = 33,
153+
F_BLUE = 34,
154+
F_MAGENTA = 35,
155+
F_CYAN = 36,
156+
F_WHITE = 37,
157+
158+
// custom front color
159+
F_CUSTOM_COLOR = 38,
160+
// default front color
161+
F_DEFAULT_COLOR = 39,
162+
163+
// 3/4-bit back color
164+
B_BLACK = 40,
165+
B_RED = 41,
166+
B_GREEN = 42,
167+
B_YELLOW = 43,
168+
B_BLUE = 44,
169+
B_MAGENTA = 45,
170+
B_CYAN = 46,
171+
B_WHITE = 47,
172+
173+
// custom back color
174+
B_CUSTOM_COLOR = 48,
175+
// default back color
176+
B_DEFAULT_COLOR = 49,
177+
178+
// 3/4-bit front bright color
179+
F_BRIGHT_BLACK = 90,
180+
F_BRIGHT_RED = 91,
181+
F_BRIGHT_GREEN = 92,
182+
F_BRIGHT_YELLOW = 93,
183+
F_BRIGHT_BLUE = 94,
184+
F_BRIGHT_MAGENTA = 95,
185+
F_BRIGHT_CYAN = 96,
186+
F_BRIGHT_WHITE = 97,
187+
188+
// 3/4-bit back bright color
189+
B_BRIGHT_BLACK = 100,
190+
B_BRIGHT_RED = 101,
191+
B_BRIGHT_GREEN = 102,
192+
B_BRIGHT_YELLOW = 103,
193+
B_BRIGHT_BLUE = 104,
194+
B_BRIGHT_MAGENTA = 105,
195+
B_BRIGHT_CYAN = 106,
196+
B_BRIGHT_WHITE = 107,
197+
};
198+
199+
public:
200+
static SGRParseCore index( ColorIndex num );
201+
202+
private:
203+
static std::map<ColorIndex, SGRParseCore> colorTable;
204+
};
205+
206+
} // namespace ANSI

0 commit comments

Comments
 (0)