1/*
2 SPDX-FileCopyrightText: 2020 Jonathan Poelen <jonathan.poelen@gmail.com>
3
4 SPDX-License-Identifier: MIT
5*/
6
7#include "ansihighlighter.h"
8#include "abstracthighlighter_p.h"
9#include "context_p.h"
10#include "definition.h"
11#include "definition_p.h"
12#include "format.h"
13#include "ksyntaxhighlighting_logging.h"
14#include "state.h"
15#include "state_p.h"
16
17#include <QColor>
18#include <QFile>
19#include <QHash>
20#include <QIODevice>
21#include <QTextStream>
22
23#include <cmath>
24#include <vector>
25
26using namespace KSyntaxHighlighting;
27
28namespace
29{
30struct Lab {
31 double L;
32 double a;
33 double b;
34};
35
36// clang-format off
37 // xterm color reference
38 // constexpr Rgb888 xterm256Colors[] {
39 // {0x00, 0x00, 0x00}, {0x80, 0x00, 0x00}, {0x00, 0x80, 0x00}, {0x80, 0x80, 0x00},
40 // {0x00, 0x00, 0x80}, {0x80, 0x00, 0x80}, {0x00, 0x80, 0x80}, {0xc0, 0xc0, 0xc0},
41 // {0x80, 0x80, 0x80}, {0xff, 0x00, 0x00}, {0x00, 0xff, 0x00}, {0xff, 0xff, 0x00},
42 // {0x00, 0x00, 0xff}, {0xff, 0x00, 0xff}, {0x00, 0xff, 0xff}, {0xff, 0xff, 0xff},
43 // {0x00, 0x00, 0x00}, {0x00, 0x00, 0x5f}, {0x00, 0x00, 0x87}, {0x00, 0x00, 0xaf},
44 // {0x00, 0x00, 0xd7}, {0x00, 0x00, 0xff}, {0x00, 0x5f, 0x00}, {0x00, 0x5f, 0x5f},
45 // {0x00, 0x5f, 0x87}, {0x00, 0x5f, 0xaf}, {0x00, 0x5f, 0xd7}, {0x00, 0x5f, 0xff},
46 // {0x00, 0x87, 0x00}, {0x00, 0x87, 0x5f}, {0x00, 0x87, 0x87}, {0x00, 0x87, 0xaf},
47 // {0x00, 0x87, 0xd7}, {0x00, 0x87, 0xff}, {0x00, 0xaf, 0x00}, {0x00, 0xaf, 0x5f},
48 // {0x00, 0xaf, 0x87}, {0x00, 0xaf, 0xaf}, {0x00, 0xaf, 0xd7}, {0x00, 0xaf, 0xff},
49 // {0x00, 0xd7, 0x00}, {0x00, 0xd7, 0x5f}, {0x00, 0xd7, 0x87}, {0x00, 0xd7, 0xaf},
50 // {0x00, 0xd7, 0xd7}, {0x00, 0xd7, 0xff}, {0x00, 0xff, 0x00}, {0x00, 0xff, 0x5f},
51 // {0x00, 0xff, 0x87}, {0x00, 0xff, 0xaf}, {0x00, 0xff, 0xd7}, {0x00, 0xff, 0xff},
52 // {0x5f, 0x00, 0x00}, {0x5f, 0x00, 0x5f}, {0x5f, 0x00, 0x87}, {0x5f, 0x00, 0xaf},
53 // {0x5f, 0x00, 0xd7}, {0x5f, 0x00, 0xff}, {0x5f, 0x5f, 0x00}, {0x5f, 0x5f, 0x5f},
54 // {0x5f, 0x5f, 0x87}, {0x5f, 0x5f, 0xaf}, {0x5f, 0x5f, 0xd7}, {0x5f, 0x5f, 0xff},
55 // {0x5f, 0x87, 0x00}, {0x5f, 0x87, 0x5f}, {0x5f, 0x87, 0x87}, {0x5f, 0x87, 0xaf},
56 // {0x5f, 0x87, 0xd7}, {0x5f, 0x87, 0xff}, {0x5f, 0xaf, 0x00}, {0x5f, 0xaf, 0x5f},
57 // {0x5f, 0xaf, 0x87}, {0x5f, 0xaf, 0xaf}, {0x5f, 0xaf, 0xd7}, {0x5f, 0xaf, 0xff},
58 // {0x5f, 0xd7, 0x00}, {0x5f, 0xd7, 0x5f}, {0x5f, 0xd7, 0x87}, {0x5f, 0xd7, 0xaf},
59 // {0x5f, 0xd7, 0xd7}, {0x5f, 0xd7, 0xff}, {0x5f, 0xff, 0x00}, {0x5f, 0xff, 0x5f},
60 // {0x5f, 0xff, 0x87}, {0x5f, 0xff, 0xaf}, {0x5f, 0xff, 0xd7}, {0x5f, 0xff, 0xff},
61 // {0x87, 0x00, 0x00}, {0x87, 0x00, 0x5f}, {0x87, 0x00, 0x87}, {0x87, 0x00, 0xaf},
62 // {0x87, 0x00, 0xd7}, {0x87, 0x00, 0xff}, {0x87, 0x5f, 0x00}, {0x87, 0x5f, 0x5f},
63 // {0x87, 0x5f, 0x87}, {0x87, 0x5f, 0xaf}, {0x87, 0x5f, 0xd7}, {0x87, 0x5f, 0xff},
64 // {0x87, 0x87, 0x00}, {0x87, 0x87, 0x5f}, {0x87, 0x87, 0x87}, {0x87, 0x87, 0xaf},
65 // {0x87, 0x87, 0xd7}, {0x87, 0x87, 0xff}, {0x87, 0xaf, 0x00}, {0x87, 0xaf, 0x5f},
66 // {0x87, 0xaf, 0x87}, {0x87, 0xaf, 0xaf}, {0x87, 0xaf, 0xd7}, {0x87, 0xaf, 0xff},
67 // {0x87, 0xd7, 0x00}, {0x87, 0xd7, 0x5f}, {0x87, 0xd7, 0x87}, {0x87, 0xd7, 0xaf},
68 // {0x87, 0xd7, 0xd7}, {0x87, 0xd7, 0xff}, {0x87, 0xff, 0x00}, {0x87, 0xff, 0x5f},
69 // {0x87, 0xff, 0x87}, {0x87, 0xff, 0xaf}, {0x87, 0xff, 0xd7}, {0x87, 0xff, 0xff},
70 // {0xaf, 0x00, 0x00}, {0xaf, 0x00, 0x5f}, {0xaf, 0x00, 0x87}, {0xaf, 0x00, 0xaf},
71 // {0xaf, 0x00, 0xd7}, {0xaf, 0x00, 0xff}, {0xaf, 0x5f, 0x00}, {0xaf, 0x5f, 0x5f},
72 // {0xaf, 0x5f, 0x87}, {0xaf, 0x5f, 0xaf}, {0xaf, 0x5f, 0xd7}, {0xaf, 0x5f, 0xff},
73 // {0xaf, 0x87, 0x00}, {0xaf, 0x87, 0x5f}, {0xaf, 0x87, 0x87}, {0xaf, 0x87, 0xaf},
74 // {0xaf, 0x87, 0xd7}, {0xaf, 0x87, 0xff}, {0xaf, 0xaf, 0x00}, {0xaf, 0xaf, 0x5f},
75 // {0xaf, 0xaf, 0x87}, {0xaf, 0xaf, 0xaf}, {0xaf, 0xaf, 0xd7}, {0xaf, 0xaf, 0xff},
76 // {0xaf, 0xd7, 0x00}, {0xaf, 0xd7, 0x5f}, {0xaf, 0xd7, 0x87}, {0xaf, 0xd7, 0xaf},
77 // {0xaf, 0xd7, 0xd7}, {0xaf, 0xd7, 0xff}, {0xaf, 0xff, 0x00}, {0xaf, 0xff, 0x5f},
78 // {0xaf, 0xff, 0x87}, {0xaf, 0xff, 0xaf}, {0xaf, 0xff, 0xd7}, {0xaf, 0xff, 0xff},
79 // {0xd7, 0x00, 0x00}, {0xd7, 0x00, 0x5f}, {0xd7, 0x00, 0x87}, {0xd7, 0x00, 0xaf},
80 // {0xd7, 0x00, 0xd7}, {0xd7, 0x00, 0xff}, {0xd7, 0x5f, 0x00}, {0xd7, 0x5f, 0x5f},
81 // {0xd7, 0x5f, 0x87}, {0xd7, 0x5f, 0xaf}, {0xd7, 0x5f, 0xd7}, {0xd7, 0x5f, 0xff},
82 // {0xd7, 0x87, 0x00}, {0xd7, 0x87, 0x5f}, {0xd7, 0x87, 0x87}, {0xd7, 0x87, 0xaf},
83 // {0xd7, 0x87, 0xd7}, {0xd7, 0x87, 0xff}, {0xd7, 0xaf, 0x00}, {0xd7, 0xaf, 0x5f},
84 // {0xd7, 0xaf, 0x87}, {0xd7, 0xaf, 0xaf}, {0xd7, 0xaf, 0xd7}, {0xd7, 0xaf, 0xff},
85 // {0xd7, 0xd7, 0x00}, {0xd7, 0xd7, 0x5f}, {0xd7, 0xd7, 0x87}, {0xd7, 0xd7, 0xaf},
86 // {0xd7, 0xd7, 0xd7}, {0xd7, 0xd7, 0xff}, {0xd7, 0xff, 0x00}, {0xd7, 0xff, 0x5f},
87 // {0xd7, 0xff, 0x87}, {0xd7, 0xff, 0xaf}, {0xd7, 0xff, 0xd7}, {0xd7, 0xff, 0xff},
88 // {0xff, 0x00, 0x00}, {0xff, 0x00, 0x5f}, {0xff, 0x00, 0x87}, {0xff, 0x00, 0xaf},
89 // {0xff, 0x00, 0xd7}, {0xff, 0x00, 0xff}, {0xff, 0x5f, 0x00}, {0xff, 0x5f, 0x5f},
90 // {0xff, 0x5f, 0x87}, {0xff, 0x5f, 0xaf}, {0xff, 0x5f, 0xd7}, {0xff, 0x5f, 0xff},
91 // {0xff, 0x87, 0x00}, {0xff, 0x87, 0x5f}, {0xff, 0x87, 0x87}, {0xff, 0x87, 0xaf},
92 // {0xff, 0x87, 0xd7}, {0xff, 0x87, 0xff}, {0xff, 0xaf, 0x00}, {0xff, 0xaf, 0x5f},
93 // {0xff, 0xaf, 0x87}, {0xff, 0xaf, 0xaf}, {0xff, 0xaf, 0xd7}, {0xff, 0xaf, 0xff},
94 // {0xff, 0xd7, 0x00}, {0xff, 0xd7, 0x5f}, {0xff, 0xd7, 0x87}, {0xff, 0xd7, 0xaf},
95 // {0xff, 0xd7, 0xd7}, {0xff, 0xd7, 0xff}, {0xff, 0xff, 0x00}, {0xff, 0xff, 0x5f},
96 // {0xff, 0xff, 0x87}, {0xff, 0xff, 0xaf}, {0xff, 0xff, 0xd7}, {0xff, 0xff, 0xff},
97 // {0x08, 0x08, 0x08}, {0x12, 0x12, 0x12}, {0x1c, 0x1c, 0x1c}, {0x26, 0x26, 0x26},
98 // {0x30, 0x30, 0x30}, {0x3a, 0x3a, 0x3a}, {0x44, 0x44, 0x44}, {0x4e, 0x4e, 0x4e},
99 // {0x58, 0x58, 0x58}, {0x62, 0x62, 0x62}, {0x6c, 0x6c, 0x6c}, {0x76, 0x76, 0x76},
100 // {0x80, 0x80, 0x80}, {0x8a, 0x8a, 0x8a}, {0x94, 0x94, 0x94}, {0x9e, 0x9e, 0x9e},
101 // {0xa8, 0xa8, 0xa8}, {0xb2, 0xb2, 0xb2}, {0xbc, 0xbc, 0xbc}, {0xc6, 0xc6, 0xc6},
102 // {0xd0, 0xd0, 0xd0}, {0xda, 0xda, 0xda}, {0xe4, 0xe4, 0xe4}, {0xee, 0xee, 0xee},
103 // };
104
105 // xterm color represented in Oklab
106 // see rgbToOklab()
107 constexpr Lab xterm240_Oklabs[] {
108 // ignore the first 16 colors as they are unpredictable (user configurable)
109 // {0x0p+0, 0x0p+0, 0x0p+0},
110 // {0x1.2d5e6bee2c4f6p+5, 0x1.af99c042ea40cp+3, 0x1.e2f6ba84d2d25p+2},
111 // {0x1.9fcc4f3622914p+5, -0x1.c105bf1d2d218p+3, 0x1.586870aec30a4p+3},
112 // {0x1.d089126c75579p+5, -0x1.12107f2e3119p+2, 0x1.7d020b82d4b9cp+3},
113 // {0x1.b1ce15c4fcb51p+4, -0x1.f203762eb1242p+0, -0x1.2b150ae3c14bep+4},
114 // {0x1.50bc446f31833p+5, 0x1.078a1150b431ap+4, -0x1.44d35b3de7eafp+3},
115 // {0x1.b27d96eb8f471p+5, -0x1.1ee8867b0e065p+3, -0x1.2f2f18261c69ap+1},
116 // {0x1.431e523cc2f4dp+6, -0x1.ad694c777b8p-11, -0x1.c7c3ea0c32ep-8},
117 // {0x1.dfe5855ae1528p+5, -0x1.3ee1ad40618p-11, -0x1.5273b9784a3p-8},
118 // {0x1.f663baac570efp+5, 0x1.67be9f690c994p+4, 0x1.928e76c3750aep+3},
119 // {0x1.5a92b1ff8af32p+6, -0x1.76441609cfb3ap+4, 0x1.1f1186319beaap+4},
120 // {0x1.83323c984ee7ap+6, -0x1.c8df58716d4cbp+2, 0x1.3d9335b5d20f9p+4},
121 // {0x1.69950098864afp+5, -0x1.9f19c42a8c674p+1, -0x1.f293e325bec2ep+4},
122 // {0x1.18ac5c68852cdp+6, 0x1.b753a9bd5dbeep+4, -0x1.0ebf0dff35bfbp+4},
123 // {0x1.6a27499e4d3d6p+6, -0x1.de4892c062f8p+3, -0x1.f96a59bc9de4cp+1},
124 // {0x1.8ffffbb77c76ap+6, -0x1.09cab717214p-10, -0x1.1a1aa7765a7p-7},
125 // 240 colors mode
126 {.L: 0x0p+0, .a: 0x0p+0, .b: 0x0p+0},
127 {.L: 0x1.5f181b2779cap+4, .a: -0x1.930f78e22f09ap+0, .b: -0x1.e41dbddfca08ap+3},
128 {.L: 0x1.c2d3be821f882p+4, .a: -0x1.02c70dd8af008p+1, .b: -0x1.36d1623919ffp+4},
129 {.L: 0x1.10a39beeb2926p+5, .a: -0x1.38fe38b7dab01p+1, .b: -0x1.77efa95d520b4p+4},
130 {.L: 0x1.3de43fe8d92efp+5, .a: -0x1.6cf188320fff2p+1, .b: -0x1.b655790a27192p+4},
131 {.L: 0x1.69950098864afp+5, .a: -0x1.9f19c42a8c674p+1, .b: -0x1.f293e325bec2ep+4},
132 {.L: 0x1.50853f46a9f9ep+5, .a: -0x1.6b6901a80404fp+3, .b: 0x1.16bdec11e60d8p+3},
133 {.L: 0x1.5fa625f2c3fbcp+5, .a: -0x1.d0691ed5aa7ap+2, .b: -0x1.eac16e8cb9241p+0},
134 {.L: 0x1.6f4222fc3f0dbp+5, .a: -0x1.61b0e7ffa5e8ap+2, .b: -0x1.05e8906ee23d7p+3},
135 {.L: 0x1.83e99e6187f46p+5, .a: -0x1.1a61c98b895p+2, .b: -0x1.c3da094bbcf2fp+3},
136 {.L: 0x1.9ca47689a503dp+5, .a: -0x1.e9259e3439104p+1, .b: -0x1.396812c634de3p+4},
137 {.L: 0x1.b872b55db6144p+5, .a: -0x1.ccd50a2fd6662p+1, .b: -0x1.89dec898ec996p+4},
138 {.L: 0x1.b01d15d276ef1p+5, .a: -0x1.d2a4448f6ccacp+3, .b: 0x1.65ec167dcb488p+3},
139 {.L: 0x1.b9760adf5c444p+5, .a: -0x1.6a3760af4b6c4p+3, .b: 0x1.a26bb322495e2p+1},
140 {.L: 0x1.c38a22a31944p+5, .a: -0x1.2a2a93c4b741p+3, .b: -0x1.3b14a376ffbecp+1},
141 {.L: 0x1.d185a36cfcd6ep+5, .a: -0x1.e760f29e8fcb4p+2, .b: -0x1.0bf936f2a6743p+3},
142 {.L: 0x1.e321d754f2b44p+5, .a: -0x1.95a749a95debp+2, .b: -0x1.c3928e31b9cf8p+3},
143 {.L: 0x1.f7eb8d9ad84b9p+5, .a: -0x1.5e28b7cc193ddp+2, .b: -0x1.38bda7259441ep+4},
144 {.L: 0x1.0552717cdb82p+6, .a: -0x1.1a33f75f67b0fp+4, .b: 0x1.b0e8bd24c3fdep+3},
145 {.L: 0x1.08898a5b7805dp+6, .a: -0x1.e29bd3071d97ap+3, .b: 0x1.e0b5994e0238ep+2},
146 {.L: 0x1.0c10ad1b87866p+6, .a: -0x1.a54f2215371a5p+3, .b: 0x1.45758e5457898p+1},
147 {.L: 0x1.1111e924447e9p+6, .a: -0x1.68a24e1d5efe1p+3, .b: -0x1.7d178a838ea32p+1},
148 {.L: 0x1.178bb94d30a5cp+6, .a: -0x1.335d7d75dd017p+3, .b: -0x1.13f78c003ba13p+3},
149 {.L: 0x1.1f6aa49fcff2p+6, .a: -0x1.0830d167759a4p+3, .b: -0x1.c578e33f21d88p+3},
150 {.L: 0x1.30b236b57ac86p+6, .a: -0x1.490afbe3d8e11p+4, .b: 0x1.f8c35fbb689dap+3},
151 {.L: 0x1.331144aae0ad4p+6, .a: -0x1.289bac3eeb83p+4, .b: 0x1.626ea3a63748p+3},
152 {.L: 0x1.35b09f5c62a7ap+6, .a: -0x1.0d33e3db59803p+4, .b: 0x1.b66836d6f7ff6p+2},
153 {.L: 0x1.3973d5b39baa4p+6, .a: -0x1.de87e61f0c86ap+3, .b: 0x1.de8cf8346969cp+0},
154 {.L: 0x1.3e64dbeab2c38p+6, .a: -0x1.a47dea5d85dbbp+3, .b: -0x1.bc586cdcba45p+1},
155 {.L: 0x1.44815d7f73a41p+6, .a: -0x1.706c35b4850ecp+3, .b: -0x1.1d13e731dfc5bp+3},
156 {.L: 0x1.5a92b1ff8af32p+6, .a: -0x1.76441609cfb3ap+4, .b: 0x1.1f1186319beaap+4},
157 {.L: 0x1.5c6885a0ab4cap+6, .a: -0x1.5c0bf4148d03dp+4, .b: 0x1.c5b4a75d0a01ep+3},
158 {.L: 0x1.5e72281eb918cp+6, .a: -0x1.4422b7d2ad44bp+4, .b: 0x1.5305a49da3492p+3},
159 {.L: 0x1.61631d5752788p+6, .a: -0x1.282b31110d79dp+4, .b: 0x1.8b1a60b561753p+2},
160 {.L: 0x1.65488920f4795p+6, .a: -0x1.0b1a0cfacacfbp+4, .b: 0x1.3d72a3bb176fp+0},
161 {.L: 0x1.6a27499e4d3d6p+6, .a: -0x1.de4892c062f8p+3, .b: -0x1.f96a59bc9de4cp+1},
162 {.L: 0x1.e7d1475ebe201p+4, .a: 0x1.5d4f5ebb6cf8ep+3, .b: 0x1.86e150bac0e61p+2},
163 {.L: 0x1.10883ee613f6bp+5, .a: 0x1.aa957aceb4328p+3, .b: -0x1.06e4a6bcf37ep+3},
164 {.L: 0x1.2a507c82ee1e7p+5, .a: 0x1.880d450b132c9p+3, .b: -0x1.c81b5ba73664ap+3},
165 {.L: 0x1.4902475f20191p+5, .a: 0x1.4f4dfeda4e013p+3, .b: -0x1.38d6c960a7255p+4},
166 {.L: 0x1.6aa42ff68fb65p+5, .a: 0x1.1116140836dp+3, .b: -0x1.84f2dbb4bf7b2p+4},
167 {.L: 0x1.8def7d6adc3d5p+5, .a: 0x1.ab6406f65a5b9p+2, .b: -0x1.caee116cbc66ep+4},
168 {.L: 0x1.77f724f99d0c9p+5, .a: -0x1.bb9ee0e906bf2p+1, .b: 0x1.345d15707c9e4p+3},
169 {.L: 0x1.8465d178eda3bp+5, .a: -0x1.021519c6d64p-11, .b: -0x1.11ebea130b3p-8},
170 {.L: 0x1.917d476fba08dp+5, .a: 0x1.84e9b4b9a4816p+0, .b: -0x1.89910d2df7414p+2},
171 {.L: 0x1.a32d016052029p+5, .a: 0x1.352fbc1045df3p+1, .b: -0x1.847eed4ab1e03p+3},
172 {.L: 0x1.b8cfd7baeb72ap+5, .a: 0x1.5d6575ced028ep+1, .b: -0x1.1bff70d82f589p+4},
173 {.L: 0x1.d1a20bfd91dbap+5, .a: 0x1.4de285fe39a4dp+1, .b: -0x1.6f381fca63bcep+4},
174 {.L: 0x1.c99e943a2d79fp+5, .a: -0x1.219ec15c5de22p+3, .b: 0x1.78f2db7cd8205p+3},
175 {.L: 0x1.d20af5c832c06p+5, .a: -0x1.86c31353f2968p+2, .b: 0x1.15f4d958ffb6ep+2},
176 {.L: 0x1.db2d2b76b7db4p+5, .a: -0x1.107aa185fa178p+2, .b: -0x1.3a545f6bb32fep+0},
177 {.L: 0x1.e7ef429658179p+5, .a: -0x1.58a57459e2113p+1, .b: -0x1.c50012145074dp+2},
178 {.L: 0x1.f821bad822a9ep+5, .a: -0x1.8ea06e2a9e1f6p+0, .b: -0x1.9a5cf82ebb612p+3},
179 {.L: 0x1.05b4f2f9696abp+6, .a: -0x1.b3645fe7f9294p-1, .b: -0x1.24f471b4e8b62p+4},
180 {.L: 0x1.0e4a1f1b1ddcep+6, .a: -0x1.b32bbdf7204c1p+3, .b: 0x1.be3e8e0b04a1fp+3},
181 {.L: 0x1.114fcf04f35b3p+6, .a: -0x1.6696d5115c7e4p+3, .b: 0x1.058704ea2708p+3},
182 {.L: 0x1.14a2558b5b6c7p+6, .a: -0x1.2c7e211429d81p+3, .b: 0x1.a9f809f8ee4dfp+1},
183 {.L: 0x1.195c00cf22a51p+6, .a: -0x1.e58de99c40fb6p+2, .b: -0x1.0e4f6479339a5p+1},
184 {.L: 0x1.1f7e4eea78cb7p+6, .a: -0x1.809e6530a25d5p+2, .b: -0x1.ee2cfac349d5ep+2},
185 {.L: 0x1.26f9f5da0f33bp+6, .a: -0x1.2ffe0404fe1e4p+2, .b: -0x1.a87af760f19c2p+3},
186 {.L: 0x1.37635555b270cp+6, .a: -0x1.17deced6d377ep+4, .b: 0x1.0159387931185p+4},
187 {.L: 0x1.39aa8b444a1c7p+6, .a: -0x1.f1b4258b74e35p+3, .b: 0x1.70a1d9ed49333p+3},
188 {.L: 0x1.3c30003dc4bdap+6, .a: -0x1.bcf061cd62971p+3, .b: 0x1.d845b8f464806p+2},
189 {.L: 0x1.3fcf0a7e4fdb8p+6, .a: -0x1.8316f8fb04f1cp+3, .b: 0x1.3be7d797ab219p+1},
190 {.L: 0x1.4492530767fa4p+6, .a: -0x1.4af657f86bc35p+3, .b: -0x1.69ccecfbead6p+1},
191 {.L: 0x1.4a78e7a0304fbp+6, .a: -0x1.18be182ded159p+3, .b: -0x1.07ac09ccdfff4p+3},
192 {.L: 0x1.5fc9ac083946p+6, .a: -0x1.4f840d59cd9aep+4, .b: 0x1.22ef620ec7775p+4},
193 {.L: 0x1.6192b2ae205fp+6, .a: -0x1.361d4509435d5p+4, .b: 0x1.cfe893676e62ep+3},
194 {.L: 0x1.638e487d443e1p+6, .a: -0x1.1edcf418396fcp+4, .b: 0x1.5f105a3f7b33p+3},
195 {.L: 0x1.666b5164f3799p+6, .a: -0x1.03988ade0159ep+4, .b: 0x1.a6a2e5baac692p+2},
196 {.L: 0x1.6a3706fa48d42p+6, .a: -0x1.ce6b98f424c54p+3, .b: 0x1.b67fa4fafc2e8p+0},
197 {.L: 0x1.6ef6b7860b53fp+6, .a: -0x1.97ce09961f218p+3, .b: -0x1.b926ed0fbe897p+1},
198 {.L: 0x1.3931bb83cb32dp+5, .a: 0x1.c0894426a198dp+3, .b: 0x1.f5ea328bf4058p+2},
199 {.L: 0x1.4b9e77eb58ebfp+5, .a: 0x1.108cfd41d7919p+4, .b: -0x1.0eaf04c8d3b35p+2},
200 {.L: 0x1.5df2d7bacd40ap+5, .a: 0x1.11e15f9b225acp+4, .b: -0x1.51924b9c514f3p+3},
201 {.L: 0x1.756bc7d79519bp+5, .a: 0x1.03a63c750b36bp+4, .b: -0x1.052fc194cf52p+4},
202 {.L: 0x1.90b3e1d276c5dp+5, .a: 0x1.d79fd75fb3811p+3, .b: -0x1.58e6a7a1da1fcp+4},
203 {.L: 0x1.aea5654d2d631p+5, .a: 0x1.9f14550c88c57p+3, .b: -0x1.a57e86563dbcap+4},
204 {.L: 0x1.9b6948efcb1e8p+5, .a: 0x1.dddaa142e7b3ap+0, .b: 0x1.4f83ba256abcfp+3},
205 {.L: 0x1.a5f9bfdb796c8p+5, .a: 0x1.3de3647070a5bp+2, .b: 0x1.b416b1e2a3817p+0},
206 {.L: 0x1.b1407c7a80b46p+5, .a: 0x1.9d8674f672f17p+2, .b: -0x1.112d7835e0443p+2},
207 {.L: 0x1.c0b6552db89b1p+5, .a: 0x1.d8c2a5262b398p+2, .b: -0x1.480f10f6d0b5fp+3},
208 {.L: 0x1.d3edc2b5ca1f7p+5, .a: 0x1.edbf49a0778e4p+2, .b: -0x1.fe5ca96caae15p+3},
209 {.L: 0x1.ea519f7b4bf8cp+5, .a: 0x1.e35c8bfe38bf9p+2, .b: -0x1.5483ed4b7e5ebp+4},
210 {.L: 0x1.e2c36ca30962cp+5, .a: -0x1.1cd1878e91233p+2, .b: 0x1.8bf558e4fbde3p+3},
211 {.L: 0x1.ea67769abd5c1p+5, .a: -0x1.c053e5b6c3b66p+0, .b: 0x1.59302161a2865p+2},
212 {.L: 0x1.f2ba2dd022991p+5, .a: -0x1.4b64e809e68p-11, .b: -0x1.5fbb8b338b8p-8},
213 {.L: 0x1.fe6b844bbf20fp+5, .a: 0x1.8040f07e39554p+0, .b: -0x1.7196ffd7de002p+2},
214 {.L: 0x1.06aeef460087dp+6, .a: 0x1.4afa79e390244p+1, .b: -0x1.705a7c8493135p+3},
215 {.L: 0x1.0fa5554a5c955p+6, .a: 0x1.9dea7336ee828p+1, .b: -0x1.1089bb9ca046ep+4},
216 {.L: 0x1.178e9051b853ep+6, .a: -0x1.3b3b8e6c52f1cp+3, .b: 0x1.cc268b9a4e157p+3},
217 {.L: 0x1.1a65db5c4c0d1p+6, .a: -0x1.e697a167b2b86p+2, .b: 0x1.1b4d0d1ca381dp+3},
218 {.L: 0x1.1d86bd983524p+6, .a: -0x1.77ccb1bcef226p+2, .b: 0x1.08e1384d16e75p+2},
219 {.L: 0x1.21fc7ad985402p+6, .a: -0x1.08e3f099fb00fp+2, .b: -0x1.363e6fbac8902p+0},
220 {.L: 0x1.27cb1d6dd9434p+6, .a: -0x1.4f7ef424b1025p+1, .b: -0x1.b1af4c945ce98p+2},
221 {.L: 0x1.2ee710ec206cfp+6, .a: -0x1.6ac7f2787bda1p+0, .b: -0x1.89e8c18db3849p+3},
222 {.L: 0x1.3e7c86695f695p+6, .a: -0x1.ceac064fd3a97p+3, .b: 0x1.06a67a74ff4cp+4},
223 {.L: 0x1.40ac0504d65f2p+6, .a: -0x1.935bda5136fdbp+3, .b: 0x1.7fa8ea5683dffp+3},
224 {.L: 0x1.4317a9d652f14p+6, .a: -0x1.608608dcc2607p+3, .b: 0x1.fc21edd271f8bp+2},
225 {.L: 0x1.46928cf49114ap+6, .a: -0x1.2883a0410f74bp+3, .b: 0x1.8d4b911a8102bp+1},
226 {.L: 0x1.4b27f53f2d1c8p+6, .a: -0x1.e40f8922e26b2p+2, .b: -0x1.11ca0bf0acee9p+1},
227 {.L: 0x1.50d83b6f7788bp+6, .a: -0x1.82a0417f98419p+2, .b: -0x1.e18432a6676edp+2},
228 {.L: 0x1.6567db05a012cp+6, .a: -0x1.27d55f8b64f16p+4, .b: 0x1.271e9400a42e4p+4},
229 {.L: 0x1.6723b3feab73ap+6, .a: -0x1.0f412c5a09d1ap+4, .b: 0x1.dae43f7f7eb6ep+3},
230 {.L: 0x1.6910cf423acf1p+6, .a: -0x1.f152d3d18c3b3p+3, .b: 0x1.6c04c44876eb2p+3},
231 {.L: 0x1.6bd948eca017fp+6, .a: -0x1.bc29d2c53f57ap+3, .b: 0x1.c44a28d107a62p+2},
232 {.L: 0x1.6f8a69cf10b1p+6, .a: -0x1.84b224c6d040cp+3, .b: 0x1.1c910c629db67p+1},
233 {.L: 0x1.7429ebc4e406dp+6, .a: -0x1.4f4a0b735c531p+3, .b: -0x1.73a39097eadefp+1},
234 {.L: 0x1.7acf7694f8c6p+5, .a: 0x1.0f40ef4bed7e8p+4, .b: 0x1.2f88d81b23f2ep+3},
235 {.L: 0x1.87b52573912c8p+5, .a: 0x1.3e9cadbae2c71p+4, .b: -0x1.21c127942ffc2p-1},
236 {.L: 0x1.95308092c646cp+5, .a: 0x1.4bd8ec93cbe28p+4, .b: -0x1.b1ad2fc6cd5ccp+2},
237 {.L: 0x1.a743d71712a69p+5, .a: 0x1.4b428f09e4704p+4, .b: -0x1.984b766ca527bp+3},
238 {.L: 0x1.bd35da1ada54ap+5, .a: 0x1.3ee7b023f1a36p+4, .b: -0x1.252f399063a3p+4},
239 {.L: 0x1.d638af110e2bfp+5, .a: 0x1.2a6369728426p+4, .b: -0x1.777a4238ecc9cp+4},
240 {.L: 0x1.c5db1e678f93bp+5, .a: 0x1.be23ef0d11b9fp+2, .b: 0x1.706e52e6555aep+3},
241 {.L: 0x1.ceab15b1e5e2cp+5, .a: 0x1.376364145a451p+3, .b: 0x1.d7fd2a954eedcp+1},
242 {.L: 0x1.d8302a174d63dp+5, .a: 0x1.67a05469bf7c1p+3, .b: -0x1.0236bbe6ffddap+1},
243 {.L: 0x1.e56c6a282c6fep+5, .a: 0x1.8915eed9a5994p+3, .b: -0x1.fb2d49a257381p+2},
244 {.L: 0x1.f622a3ef65e7fp+5, .a: 0x1.98638aed6156bp+3, .b: -0x1.b57e66e36241ap+3},
245 {.L: 0x1.04f5c4d9c25cfp+6, .a: 0x1.9710ff57a7b7ap+3, .b: -0x1.32004fe7bd5b5p+4},
246 {.L: 0x1.018127d59826cp+6, .a: 0x1.1e619caaa3a8fp-1, .b: 0x1.a49bd0215a96dp+3},
247 {.L: 0x1.04e57659b4cf6p+6, .a: 0x1.8024718692a3cp+1, .b: 0x1.addd1b874609p+2},
248 {.L: 0x1.089bd543a0e63p+6, .a: 0x1.2a7652607e02cp+2, .b: 0x1.8d85013677bap+0},
249 {.L: 0x1.0ddb424d04fadp+6, .a: 0x1.889c9b8223651p+2, .b: -0x1.05aa1bc86a9d4p+2},
250 {.L: 0x1.149da8fbd8909p+6, .a: 0x1.ce4542c130c31p+2, .b: -0x1.3934aac98d184p+3},
251 {.L: 0x1.1cca409e32c6dp+6, .a: 0x1.f855300b25ba5p+2, .b: -0x1.eac45e49f86bap+3},
252 {.L: 0x1.23f3e081f0e6fp+6, .a: -0x1.587d5d644e818p+2, .b: 0x1.deea03a7e9e2p+3},
253 {.L: 0x1.26935d2d5bb58p+6, .a: -0x1.a6ab45ebb79ddp+1, .b: 0x1.38265c4c6d156p+3},
254 {.L: 0x1.29783d4927cb6p+6, .a: -0x1.aa081e388f0b6p+0, .b: 0x1.4de28afb99798p+2},
255 {.L: 0x1.2d9b5abdd0df6p+6, .a: -0x1.90d2c34c4c8p-11, .b: -0x1.a96c3913736p-8},
256 {.L: 0x1.3303704c60227p+6, .a: 0x1.7842d686da2p+0, .b: -0x1.6001098473213p+2},
257 {.L: 0x1.39a911d79bf6cp+6, .a: 0x1.51ae39272f6fp+1, .b: -0x1.604eff9e13224p+3},
258 {.L: 0x1.483b66bb75f41p+6, .a: -0x1.53e4a65e5bd0ep+3, .b: 0x1.0dfa4f4feec89p+4},
259 {.L: 0x1.4a4cab5bdb777p+6, .a: -0x1.1bf0c7950b46ap+3, .b: 0x1.9433415e2e559p+3},
260 {.L: 0x1.4c9756fc325a6p+6, .a: -0x1.d6e499fe26166p+2, .b: 0x1.1693b52228314p+3},
261 {.L: 0x1.4fe3e8502420ap+6, .a: -0x1.6b29463056a7ep+2, .b: 0x1.fcea504279e1bp+1},
262 {.L: 0x1.543e4f84c51c9p+6, .a: -0x1.01c201c3fcfbep+2, .b: -0x1.311b349896504p+0},
263 {.L: 0x1.59a86b698fe17p+6, .a: -0x1.4697ac30fd276p+1, .b: -0x1.a218614573a54p+2},
264 {.L: 0x1.6d3f94c53849cp+6, .a: -0x1.e750c3b6abf94p+3, .b: 0x1.2cfd8e3a3298cp+4},
265 {.L: 0x1.6ee9ffc7c561bp+6, .a: -0x1.b847cb4caca9ap+3, .b: 0x1.ea32a397a5ea3p+3},
266 {.L: 0x1.70c3ef56a2364p+6, .a: -0x1.8cc812a446223p+3, .b: 0x1.7e0dc7e1aa4eep+3},
267 {.L: 0x1.737125604c02dp+6, .a: -0x1.5959ff3792892p+3, .b: 0x1.ed9e6884d6db8p+2},
268 {.L: 0x1.76fef3ccb11e4p+6, .a: -0x1.2379a952d29eap+3, .b: 0x1.77d044e094287p+1},
269 {.L: 0x1.7b739668b58adp+6, .a: -0x1.def62d62ac5d3p+2, .b: -0x1.1242c0559f9b9p+1},
270 {.L: 0x1.b9af6705b3e1ap+5, .a: 0x1.3c46b4e4aa724p+4, .b: 0x1.61ea416f62116p+3},
271 {.L: 0x1.c34652a386648p+5, .a: 0x1.673d5400dac6ap+4, .b: 0x1.575529f864e4ap+1},
272 {.L: 0x1.cd90a5a7155efp+5, .a: 0x1.7aac661c86347p+4, .b: -0x1.97f5d2781d1d2p+1},
273 {.L: 0x1.dbc2183aadc6dp+5, .a: 0x1.83f34257f81ecp+4, .b: -0x1.24c7f27451716p+3},
274 {.L: 0x1.ed84b050fd5fep+5, .a: 0x1.823e0b9a9286fp+4, .b: -0x1.dc10370a896a1p+3},
275 {.L: 0x1.012d3f7585d8bp+6, .a: 0x1.772916c92c8cp+4, .b: -0x1.444d46ad4a3f5p+4},
276 {.L: 0x1.f498a57d50c8dp+5, .a: 0x1.72c50647a1b01p+3, .b: 0x1.9501b7bc92a26p+3},
277 {.L: 0x1.fbec5add8799fp+5, .a: 0x1.c10937ac1ff0cp+3, .b: 0x1.71506b03a0cfcp+2},
278 {.L: 0x1.01f4d75996fc6p+6, .a: 0x1.f168c838b27a5p+3, .b: 0x1.a2871b4170f08p-2},
279 {.L: 0x1.07934868d42f1p+6, .a: 0x1.0ba82dcee8d5fp+4, .b: -0x1.55f50c12fdb69p+2},
280 {.L: 0x1.0ec4c024626fep+6, .a: 0x1.16b4701bb2cep+4, .b: -0x1.627c9a1b01177p+3},
281 {.L: 0x1.1768d8b0aa031p+6, .a: 0x1.199f4e2e9e65fp+4, .b: -0x1.09c1c4e924702p+4},
282 {.L: 0x1.14522a4a9a7e2p+6, .a: 0x1.6020b0a7ecac7p+2, .b: 0x1.c1a8f99df62d9p+3},
283 {.L: 0x1.174b930ef061ap+6, .a: 0x1.ec83068a9839fp+2, .b: 0x1.06f134f7cc07ep+3},
284 {.L: 0x1.1a90921a38fe7p+6, .a: 0x1.28d89eb448a2cp+3, .b: 0x1.ab5fe99a94265p+1},
285 {.L: 0x1.1f36b9520ad75p+6, .a: 0x1.57ace8397fb96p+3, .b: -0x1.0feb2da7758d4p+1},
286 {.L: 0x1.253f2b741784fp+6, .a: 0x1.7c087d08384b1p+3, .b: -0x1.efdb49212f992p+2},
287 {.L: 0x1.2c9a998952cf1p+6, .a: 0x1.9366b5a4edd8ap+3, .b: -0x1.a9848b6099745p+3},
288 {.L: 0x1.330541ae3e385p+6, .a: -0x1.39e238ea7801p-1, .b: 0x1.f5ec9f058d778p+3},
289 {.L: 0x1.3568ff8940412p+6, .a: 0x1.4b2aea6a2b17bp+0, .b: 0x1.5aadb9534e15p+3},
290 {.L: 0x1.380d495b09ffp+6, .a: 0x1.6b2260c162acep+1, .b: 0x1.a0b3e40fdedfp+2},
291 {.L: 0x1.3bd6db55a86abp+6, .a: 0x1.1c0894632c07ep+2, .b: 0x1.73acfbc73a52cp+0},
292 {.L: 0x1.40ced5740b6ecp+6, .a: 0x1.784d8d5bd59bap+2, .b: -0x1.f87f29ae0432ap+1},
293 {.L: 0x1.46f1d56d6a627p+6, .a: 0x1.c2acec311d85p+2, .b: -0x1.2d078d1d0ec93p+3},
294 {.L: 0x1.54692a261ff77p+6, .a: -0x1.91aafde5841e2p+2, .b: 0x1.1733b7c5acf09p+4},
295 {.L: 0x1.5657fb0185f53p+6, .a: -0x1.292fef1c6094ap+2, .b: 0x1.adacb9827ba1ep+3},
296 {.L: 0x1.587cf8c168362p+6, .a: -0x1.9aa045411de21p+1, .b: 0x1.34f47fa15abacp+3},
297 {.L: 0x1.5b946ca219efdp+6, .a: -0x1.98465c0dc7b2ap+0, .b: 0x1.43ded9d06d2f2p+2},
298 {.L: 0x1.5faadb5249c01p+6, .a: -0x1.d35a0bdd6ap-11, .b: -0x1.f008c176226p-8},
299 {.L: 0x1.64c3b31613c93p+6, .a: 0x1.6ffd305ac700dp+0, .b: -0x1.52580b551209ep+2},
300 {.L: 0x1.773c645e32d2dp+6, .a: -0x1.6c001f7fba478p+3, .b: 0x1.3482f283026fp+4},
301 {.L: 0x1.78d215b19332bp+6, .a: -0x1.3f6c1cce8d703p+3, .b: 0x1.fda06ae8dbf53p+3},
302 {.L: 0x1.7a9531dc8addap+6, .a: -0x1.15dd42d16c7e4p+3, .b: 0x1.94e8098e96fccp+3},
303 {.L: 0x1.7d21e2193d5bcp+6, .a: -0x1.c8c718caa9f8ep+2, .b: 0x1.1105d830d79dp+3},
304 {.L: 0x1.808572bc9578ap+6, .a: -0x1.607bf910c57eep+2, .b: 0x1.ebe2b93545493p+1},
305 {.L: 0x1.84c6a7914070fp+6, .a: -0x1.f6c2ba3dfc25ap+1, .b: -0x1.2beab2de5e36ep+0},
306 {.L: 0x1.f663baac570efp+5, .a: 0x1.67be9f690c994p+4, .b: 0x1.928e76c3750aep+3},
307 {.L: 0x1.fdd78bfa0c16bp+5, .a: 0x1.8da11d29326eap+4, .b: 0x1.63128e5e5d16fp+2},
308 {.L: 0x1.02fa8211ca545p+6, .a: 0x1.a3a4c44447321p+4, .b: 0x1.04d0a318f0ebp-3},
309 {.L: 0x1.08ace6a6b1bbcp+6, .a: 0x1.b32f97fdc6c72p+4, .b: -0x1.69ea7f28a916ap+2},
310 {.L: 0x1.0ff3cb8943e81p+6, .a: 0x1.b9bf03db71bbbp+4, .b: -0x1.6cb54bc7e9c6p+3},
311 {.L: 0x1.18ac5c68852cdp+6, .a: 0x1.b753a9bd5dbeep+4, .b: -0x1.0ebf0dff35bfbp+4},
312 {.L: 0x1.12e57190e4906p+6, .a: 0x1.f7243f8456bc9p+3, .b: 0x1.bbbf019d6f15fp+3},
313 {.L: 0x1.15f567ae01685p+6, .a: 0x1.1e238872ebfe1p+4, .b: 0x1.f5db81101607bp+2},
314 {.L: 0x1.1952176f81196p+6, .a: 0x1.35eb43e1b3454p+4, .b: 0x1.6f044118da651p+1},
315 {.L: 0x1.1e17b3f5be1e9p+6, .a: 0x1.4aa233635e996p+4, .b: -0x1.54536023af51fp+1},
316 {.L: 0x1.244540fde90c8p+6, .a: 0x1.590428a4cd957p+4, .b: -0x1.09fd595c81312p+3},
317 {.L: 0x1.2bc8d270727cdp+6, .a: 0x1.6002d76c0fae1p+4, .b: -0x1.bbbb8dd0769e1p+3},
318 {.L: 0x1.2910051d7f4b8p+6, .a: 0x1.4505f3d8c93bep+3, .b: 0x1.e1f0441f64aep+3},
319 {.L: 0x1.2ba848891bba1p+6, .a: 0x1.83d80a13be75dp+3, .b: 0x1.39f759ee811ebp+3},
320 {.L: 0x1.2e852380cafc2p+6, .a: 0x1.b3d439de20201p+3, .b: 0x1.5018eb1694a48p+2},
321 {.L: 0x1.329c951f395aap+6, .a: 0x1.e28db45c54996p+3, .b: 0x1.689e197414bp-7},
322 {.L: 0x1.37f5013da44b7p+6, .a: 0x1.04846845ce376p+4, .b: -0x1.5f9623aa14c25p+2},
323 {.L: 0x1.3e86c9cea44c3p+6, .a: 0x1.11f59319394fdp+4, .b: -0x1.6049926fe33dap+3},
324 {.L: 0x1.443efb939f1d1p+6, .a: 0x1.0a131d35fab88p+2, .b: 0x1.0838884c6c35ap+4},
325 {.L: 0x1.4667729b66dbap+6, .a: 0x1.793a281884d1p+2, .b: 0x1.8155ad5254ba1p+3},
326 {.L: 0x1.48cb51f255527p+6, .a: 0x1.d609d14c020d1p+2, .b: 0x1.fd82e0735fdcep+2},
327 {.L: 0x1.4c3af36c4bb13p+6, .a: 0x1.1c709781a9fb2p+3, .b: 0x1.8ca66dc4e33fdp+1},
328 {.L: 0x1.50c14ed0ba88bp+6, .a: 0x1.4a1d653379591p+3, .b: -0x1.14ee52d3fd0e1p+1},
329 {.L: 0x1.565e87fd89a8p+6, .a: 0x1.6fe5c4c37980fp+3, .b: -0x1.e3e3e3d81f648p+2},
330 {.L: 0x1.62b687ada6a2dp+6, .a: -0x1.b1a7c98c53b9bp+0, .b: 0x1.221d2fc2be6cp+4},
331 {.L: 0x1.6480f342fedfap+6, .a: -0x1.728e49c5a9ba8p-3, .b: 0x1.cb44060a17de8p+3},
332 {.L: 0x1.667e081a8e931p+6, .a: 0x1.2c204353d8acp+0, .b: 0x1.582d07a1ac706p+3},
333 {.L: 0x1.695d03a4b4ea6p+6, .a: 0x1.5b9dd1e5192c9p+1, .b: 0x1.949cc8da7db3dp+2},
334 {.L: 0x1.6d2ad25b49139p+6, .a: 0x1.10c661536b1e3p+2, .b: 0x1.60b9aee667ea2p+0},
335 {.L: 0x1.71ec510363cccp+6, .a: 0x1.6b2af260ca3f1p+2, .b: -0x1.e8e3d6769ed61p+1},
336 {.L: 0x1.83323c984ee7ap+6, .a: -0x1.c8df58716d4cbp+2, .b: 0x1.3d9335b5d20f9p+4},
337 {.L: 0x1.84b109344c92cp+6, .a: -0x1.750134f94569cp+2, .b: 0x1.0a63166d3ca32p+4},
338 {.L: 0x1.865ae5967696dp+6, .a: -0x1.260f129da839ep+2, .b: 0x1.b00f6d0ac1f93p+3},
339 {.L: 0x1.88c3891dbffdp+6, .a: -0x1.8e7bf5d542ea4p+1, .b: 0x1.30323669c3d67p+3},
340 {.L: 0x1.8bf825267af8ep+6, .a: -0x1.89d8c0f2ba1ffp+0, .b: 0x1.3b2af0c9eed76p+2},
341 {.L: 0x1.8ffffbb77c76ap+6, .a: -0x1.09cab717214p-10, .b: -0x1.1a1aa7765a7p-7},
342 {.L: 0x1.ae1c063cf8075p+3, .a: -0x1.1dcc8d6b21p-13, .b: -0x1.2f56d49352fp-10},
343 {.L: 0x1.23869fde6955fp+4, .a: -0x1.836d13c82dp-13, .b: -0x1.9b340cb2926p-10},
344 {.L: 0x1.6a51d9755cb1ep+4, .a: -0x1.e1821bf6d08p-13, .b: -0x1.ff0f3c5806ap-10},
345 {.L: 0x1.adca073d0c9a1p+4, .a: -0x1.1d961152df8p-12, .b: -0x1.2f1d00745cap-9},
346 {.L: 0x1.eeb26a3638306p+4, .a: -0x1.48b751f0a58p-12, .b: -0x1.5ce3e1a3db8p-9},
347 {.L: 0x1.16c4868a9dbc4p+5, .a: -0x1.7278906708p-12, .b: -0x1.89352628678p-9},
348 {.L: 0x1.3552cb4726ed2p+5, .a: -0x1.9b140ac6fbp-12, .b: -0x1.b44e9f2325p-9},
349 {.L: 0x1.53242132979b2p+5, .a: -0x1.c2b46f9c328p-12, .b: -0x1.de5d99b6f9p-9},
350 {.L: 0x1.7051013cf5a69p+5, .a: -0x1.e97a44c10a8p-12, .b: -0x1.03c24d5b897p-8},
351 {.L: 0x1.8ceca3a0569fdp+5, .a: -0x1.07bf8a7e0fcp-11, .b: -0x1.17ef5f1c441p-8},
352 {.L: 0x1.a9067ed63306p+5, .a: -0x1.1a6bb67a6bp-11, .b: -0x1.2bc0e9df0a8p-8},
353 {.L: 0x1.c4ab42f91535bp+5, .a: -0x1.2cca14a233p-11, .b: -0x1.3f3fe0662d2p-8},
354 {.L: 0x1.dfe5855ae1528p+5, .a: -0x1.3ee1ad40618p-11, .b: -0x1.5273b9784a3p-8},
355 {.L: 0x1.fabe397d15c9dp+5, .a: -0x1.50b872fc158p-11, .b: -0x1.6562c531248p-8},
356 {.L: 0x1.0a9e8459f8d9ap+6, .a: -0x1.62537d1958p-11, .b: -0x1.78126ad030ep-8},
357 {.L: 0x1.17b44990f13f5p+6, .a: -0x1.73b732a42fp-11, .b: -0x1.8a87568ccdp-8},
358 {.L: 0x1.24a350705ddf4p+6, .a: -0x1.84e76b1c308p-11, .b: -0x1.9cc59c424aap-8},
359 {.L: 0x1.316e23ed9d96ap+6, .a: -0x1.95e7879947p-11, .b: -0x1.aed0d2216dp-8},
360 {.L: 0x1.3e1704b29c069p+6, .a: -0x1.a6ba8679e08p-11, .b: -0x1.c0ac258f992p-8},
361 {.L: 0x1.4a9ff4d8984e6p+6, .a: -0x1.b76312f3808p-11, .b: -0x1.d25a6bb27acp-8},
362 {.L: 0x1.570ac151c1615p+6, .a: -0x1.c7e3919c138p-11, .b: -0x1.e3de2eb684p-8},
363 {.L: 0x1.6359098c40c61p+6, .a: -0x1.d83e2a89bbp-11, .b: -0x1.f539b894d66p-8},
364 {.L: 0x1.6f8c45b456692p+6, .a: -0x1.e874d1a7f5p-11, .b: -0x1.03378df3804p-7},
365 {.L: 0x1.7ba5cbe12c3fcp+6, .a: -0x1.f8894d9602p-11, .b: -0x1.0bc01d99c93p-7},
366 };
367
368 // Perform the inverse gamma companding for a sRGB color
369 // http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html
370 // double inverseGammaCompanding(int c) {
371 // // [0, 255] to [0, 1]
372 // double v = c / 255.0;
373 // if (v <= 0.04045) {
374 // return v / 12.92;
375 // }
376 // return std::pow((v + 0.055) / 1.055, 2.4);
377 // };
378 constexpr double RGB888_to_sRGB_table[] {
379 0x0p+0, 0x1.3e45677c176f7p-12, 0x1.3e45677c176f7p-11, 0x1.dd681b3a23272p-11,
380 0x1.3e45677c176f7p-10, 0x1.8dd6c15b1d4b4p-10, 0x1.dd681b3a23272p-10, 0x1.167cba8c94818p-9,
381 0x1.3e45677c176f7p-9, 0x1.660e146b9a5d6p-9, 0x1.8dd6c15b1d4b4p-9, 0x1.b6a31b5259c99p-9,
382 0x1.e1e31d70c99ddp-9, 0x1.07c38bf8583a9p-8, 0x1.1fcc2beed6421p-8, 0x1.390ffaf95e279p-8,
383 0x1.53936cc7bc928p-8, 0x1.6f5addb50c915p-8, 0x1.8c6a94031b561p-8, 0x1.aac6c0fb97351p-8,
384 0x1.ca7381f9f602bp-8, 0x1.eb74e160978dp-8, 0x1.06e76bbda92b8p-7, 0x1.18c2a5a8a8044p-7,
385 0x1.2b4e09b3f0ae3p-7, 0x1.3e8b7b3bde965p-7, 0x1.527cd60af8b85p-7, 0x1.6723eea8d3709p-7,
386 0x1.7c8292a3db6b3p-7, 0x1.929a88d67b521p-7, 0x1.a96d91a8016bdp-7, 0x1.c0fd67499fab6p-7,
387 0x1.d94bbdefd740ep-7, 0x1.f25a44089883fp-7, 0x1.061551372c694p-6, 0x1.135f3e4c2cce2p-6,
388 0x1.210bb8642b172p-6, 0x1.2f1b8c1ae46bdp-6, 0x1.3d8f839b79c0bp-6, 0x1.4c6866b3e9fa4p-6,
389 0x1.5ba6fae794313p-6, 0x1.6b4c0380d2deep-6, 0x1.7b5841a1bf3acp-6, 0x1.8bcc74542addbp-6,
390 0x1.9ca95898dc8b5p-6, 0x1.adefa9761c02p-6, 0x1.bfa0200597bd9p-6, 0x1.d1bb7381aec1fp-6,
391 0x1.e442595227bcap-6, 0x1.f73585185e1b5p-6, 0x1.054ad45d76878p-5, 0x1.0f31ba386ff26p-5,
392 0x1.194fcb663747bp-5, 0x1.23a55e62a662ap-5, 0x1.2e32c8e148d11p-5, 0x1.38f85fd21eacfp-5,
393 0x1.43f67766310ffp-5, 0x1.4f2d6313fa8dp-5, 0x1.5a9d759ba5edp-5, 0x1.6647010b254eep-5,
394 0x1.722a56c2239eep-5, 0x1.7e47c775d2427p-5, 0x1.8a9fa33494b07p-5, 0x1.973239698b9ccp-5,
395 0x1.a3ffd8e001389p-5, 0x1.b108cfc6b7fbcp-5, 0x1.be4d6bb31d522p-5, 0x1.cbcdf9a4616f2p-5,
396 0x1.d98ac60675833p-5, 0x1.e7841cb4f16dfp-5, 0x1.f5ba48fde2048p-5, 0x1.0216cad240765p-4,
397 0x1.096f2671eb815p-4, 0x1.10e65c38a5192p-4, 0x1.187c90bf8bce2p-4, 0x1.2031e85f5d6dap-4,
398 0x1.28068731a1952p-4, 0x1.2ffa9111cb94bp-4, 0x1.380e299e53f92p-4, 0x1.40417439ca10fp-4,
399 0x1.4894940bddbfbp-4, 0x1.5107ac0261e59p-4, 0x1.599aded247aacp-4, 0x1.624e4ef892ed4p-4,
400 0x1.6b221ebb4817ep-4, 0x1.7416702a539d1p-4, 0x1.7d2b65206b527p-4, 0x1.86611f43e9e6ap-4,
401 0x1.8fb7c007a4a7p-4, 0x1.992f68abbbc89p-4, 0x1.a2c83a3e6566dp-4, 0x1.ac82559cb3644p-4,
402 0x1.b65ddb7354604p-4, 0x1.c05aec3f4fe5ep-4, 0x1.ca79a84ebe03p-4, 0x1.d4ba2fc17a6a5p-4,
403 0x1.df1ca289d34b8p-4, 0x1.e9a1206d34003p-4, 0x1.f447c904cbb4ep-4, 0x1.ff10bbbe302c2p-4,
404 0x1.04fe0bedfe5f1p-3, 0x1.0a84fe3b36d8fp-3, 0x1.101d443dfc06fp-3, 0x1.15c6ed58eefdfp-3,
405 0x1.1b8208da5fefp-3, 0x1.214ea5fc9514ap-3, 0x1.272cd3e610123p-3, 0x1.2d1ca1a9d1cfbp-3,
406 0x1.331e1e479cdf5p-3, 0x1.393158ac3674ep-3, 0x1.3f565fb1a5fd5p-3, 0x1.458d421f735dfp-3,
407 0x1.4bd60eaae3e73p-3, 0x1.5230d3f736034p-3, 0x1.589da095dbaa1p-3, 0x1.5f1c8306b3a3cp-3,
408 0x1.65ad89b841a2bp-3, 0x1.6c50c307e53bfp-3, 0x1.73063d420fc8p-3, 0x1.79ce06a279303p-3,
409 0x1.80a82d5453b5dp-3, 0x1.8794bf727eb3fp-3, 0x1.8e93cb07b8679p-3, 0x1.95a55e0ecec0bp-3,
410 0x1.9cc98672cf47ep-3, 0x1.a400520f3619cp-3, 0x1.ab49ceb01c003p-3, 0x1.b2a60a1263b0ap-3,
411 0x1.ba1511e3e632dp-3, 0x1.c196f3c39e76fp-3, 0x1.c92bbd41d41fep-3, 0x1.d0d37be045851p-3,
412 0x1.d88e3d1250f68p-3, 0x1.e05c0e3d1d3ep-3, 0x1.e83cfcb7c16fp-3, 0x1.f03115cb6bfd3p-3,
413 0x1.f83866b38924dp-3, 0x1.00297e4ef4553p-2, 0x1.044072557177ap-2, 0x1.086115f6beb3ap-2,
414 0x1.0c8b6fb5c735ep-2, 0x1.10bf860ef039ap-2, 0x1.14fd5f782a5a6p-2, 0x1.1945026102997p-2,
415 0x1.1d967532b31b1p-2, 0x1.21f1be50339e7p-2, 0x1.2656e41649ae3p-2, 0x1.2ac5ecdb988f8p-2,
416 0x1.2f3edef0b0ed8p-2, 0x1.33c1c0a020438p-2, 0x1.384e982e800b1p-2, 0x1.3ce56bda84a81p-2,
417 0x1.418641dd0c1bcp-2, 0x1.463120692c7afp-2, 0x1.4ae60dac4229dp-2, 0x1.4fa50fcdfde15p-2,
418 0x1.546e2cf0727a9p-2, 0x1.59416b3022858p-2, 0x1.5e1ed0a40daabp-2, 0x1.6306635dbdd7bp-2,
419 0x1.67f82969543a2p-2, 0x1.6cf428cd96079p-2, 0x1.71fa678bf915dp-2, 0x1.770aeba0b042ap-2,
420 0x1.7c25bb02b7ac5p-2, 0x1.814adba3e0bd9p-2, 0x1.867a5370de0b1p-2, 0x1.8bb428514f067p-2,
421 0x1.90f86027cb84ep-2, 0x1.964700d1ef1b1p-2, 0x1.9ba0102864521p-2, 0x1.a10393feefafdp-2,
422 0x1.a67192247a9bep-2, 0x1.abea10631e195p-2, 0x1.b16d14802d5cap-2, 0x1.b6faa43c403bbp-2,
423 0x1.bc92c5533d785p-2, 0x1.c2357d7c64e5dp-2, 0x1.c7e2d26a596dep-2, 0x1.cd9ac9cb2aef2p-2,
424 0x1.d35d69485ffc5p-2, 0x1.d92ab686ff782p-2, 0x1.df02b7279a10dp-2, 0x1.e4e570c6539c5p-2,
425 0x1.ead2e8faec526p-2, 0x1.f0cb2558c9ea4p-2, 0x1.f6ce2b6f00983p-2, 0x1.fcdc00c85bec2p-2,
426 0x1.017a5575b3cb2p-1, 0x1.048c17ad3c04bp-1, 0x1.07a349c9d9837p-1, 0x1.0abfee888c05p-1,
427 0x1.0de208a4444c8p-1, 0x1.11099ad5e83ebp-1, 0x1.1436a7d456eefp-1, 0x1.176932546ca12p-1,
428 0x1.1aa13d0906bdap-1, 0x1.1ddecaa307b85p-1, 0x1.2121ddd15aecep-1, 0x1.246a7940f86d1p-1,
429 0x1.27b89f9ce8c4bp-1, 0x1.2b0c538e48b07p-1, 0x1.2e6597bc4ccap-1, 0x1.31c46ecc4528dp-1,
430 0x1.3528db61a0f73p-1, 0x1.3892e01df1fccp-1, 0x1.3c027fa0f01ebp-1, 0x1.3f77bc887cd3bp-1,
431 0x1.42f29970a68f8p-1, 0x1.467318f3ac22dp-1, 0x1.49f93daa00113p-1, 0x1.4d850a2a4bde1p-1,
432 0x1.51168109734e5p-1, 0x1.54ada4da97a1bp-1, 0x1.584a782f1ac23p-1, 0x1.5becfd96a2698p-1,
433 0x1.5f95379f1b3edp-1, 0x1.634328d4bbe97p-1, 0x1.66f6d3c2081cfp-1, 0x1.6ab03aefd39aap-1,
434 0x1.6e6f60e5452b1p-1, 0x1.72344827d98f6p-1, 0x1.75fef33b6669bp-1, 0x1.79cf64a21d1e2p-1,
435 0x1.7da59edc8dabp-1, 0x1.8181a469a9787p-1, 0x1.856377c6c6224p-1, 0x1.894b1b6fa0377p-1,
436 0x1.8d3891de5df49p-1, 0x1.912bdd8b91f45p-1, 0x1.952500ee3dda5p-1, 0x1.9923fe7bd4f67p-1,
437 0x1.9d28d8a83edfcp-1, 0x1.a13391e5da09fp-1, 0x1.a5442ca57e52ep-1, 0x1.a95aab567f88fp-1,
438 0x1.ad771066afec2p-1, 0x1.b1995e4262a69p-1, 0x1.b5c197546e3f8p-1, 0x1.b9efbe062f086p-1,
439 0x1.be23d4bf8981bp-1, 0x1.c25ddde6ecbbbp-1, 0x1.c69ddbe154af1p-1, 0x1.cae3d1124c90bp-1,
440 0x1.cf2fbfdbf11f1p-1, 0x1.d381aa9ef2e82p-1, 0x1.d7d993ba988d4p-1, 0x1.dc377d8cc0fd5p-1,
441 0x1.e09b6a71e5aa6p-1, 0x1.e5055cc51cbb4p-1, 0x1.e97556e01b351p-1, 0x1.edeb5b1b37216p-1,
442 0x1.f2676bcd69adep-1, 0x1.f6e98b4c51466p-1, 0x1.fb71bbec33ab2p-1, 0x1p+0,
443 };
444// clang-format on
445
446// convert a RGB color to Oklab (https://bottosson.github.io/posts/oklab/)
447Lab rgbToOklab(QRgb rgb)
448{
449 const double r = RGB888_to_sRGB_table[qRed(rgb)];
450 const double g = RGB888_to_sRGB_table[qGreen(rgb)];
451 const double b = RGB888_to_sRGB_table[qBlue(rgb)];
452
453 const double l = std::cbrt(x: 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b);
454 const double m = std::cbrt(x: 0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b);
455 const double s = std::cbrt(x: 0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b);
456
457 // M2 * 100 * {l', m', s'}
458 return Lab{
459 .L: (021.04542553 * l + 079.36177850 * m - 000.40720468 * s),
460 .a: (197.79984951 * l - 242.85922050 * m + 045.05937099 * s),
461 .b: (002.59040371 * l + 078.27717662 * m - 080.86757660 * s),
462 };
463}
464
465constexpr double epsilon = 1e-15;
466
467inline double sinDegree(double x)
468{
469 return std::sin(x: x * M_PI / 180.0);
470}
471
472inline double cosDegree(double x)
473{
474 return std::cos(x: x * M_PI / 180.0);
475}
476
477inline double pow2(double x)
478{
479 return x * x;
480}
481
482inline double computeHPrime(double a_prime, double b)
483{
484 if (std::abs(x: a_prime) < epsilon && std::abs(x: b) < epsilon) {
485 return 0.0;
486 }
487
488 const double value = std::atan2(y: b, x: a_prime) * 180.0 / M_PI;
489 return (value < 0.0) ? value + 360.0 : value;
490}
491
492inline double computeDeltaHPrime(double C1_prime, double C2_prime, double h1_prime, double h2_prime)
493{
494 if (C1_prime * C2_prime < epsilon) {
495 return 0.0;
496 }
497
498 const double diff = h2_prime - h1_prime;
499
500 if (std::abs(x: diff) <= 180.0) {
501 return diff;
502 } else if (diff > 180.0) {
503 return diff - 360.0;
504 } else {
505 return diff + 360.0;
506 }
507}
508
509inline double computeHPrimeBar(double C1_prime, double C2_prime, double h1_prime, double h2_prime)
510{
511 const double sum = h1_prime + h2_prime;
512
513 if (C1_prime * C2_prime < epsilon) {
514 return sum;
515 }
516
517 const double dist = std::abs(x: h1_prime - h2_prime);
518
519 if (dist <= 180.0) {
520 return 0.5 * sum;
521 } else if (sum < 360.0) {
522 return 0.5 * (sum + 360.0);
523 } else {
524 return 0.5 * (sum - 360.0);
525 }
526}
527
528/// Calculate the perceptual color difference based on CIEDE2000.
529/// https://en.wikipedia.org/wiki/Color_difference#CIEDE2000
530/// \return The color difference of the two colors.
531double calculate_CIEDE2000(const Lab &color1, const Lab &color2)
532{
533 const double L1 = color1.L;
534 const double a1 = color1.a;
535 const double b1 = color1.b;
536 const double L2 = color2.L;
537 const double a2 = color2.a;
538 const double b2 = color2.b;
539
540 const double _25_pow_7 = /*std::pow(25.0, 7.0) = */ 6103515625.0;
541
542 const double C1_ab = std::sqrt(x: a1 * a1 + b1 * b1);
543 const double C2_ab = std::sqrt(x: a2 * a2 + b2 * b2);
544 const double C_ab_bar = 0.5 * (C1_ab + C2_ab);
545 const double c_ab_bar_pow_7 = std::pow(x: C_ab_bar, y: 7.0);
546 const double G = 0.5 * (1.0 - std::sqrt(x: c_ab_bar_pow_7 / (c_ab_bar_pow_7 + _25_pow_7)));
547 const double a1_prime = (1.0 + G) * a1;
548 const double a2_prime = (1.0 + G) * a2;
549 const double C1_prime = std::sqrt(x: a1_prime * a1_prime + b1 * b1);
550 const double C2_prime = std::sqrt(x: a2_prime * a2_prime + b2 * b2);
551 const double h1_prime = computeHPrime(a_prime: a1_prime, b: b1);
552 const double h2_prime = computeHPrime(a_prime: a2_prime, b: b2);
553
554 const double deltaL_prime = L2 - L1;
555 const double deltaC_prime = C2_prime - C1_prime;
556 const double deltah_prime = computeDeltaHPrime(C1_prime, C2_prime, h1_prime, h2_prime);
557 const double deltaH_prime = 2.0 * std::sqrt(x: C1_prime * C2_prime) * sinDegree(x: 0.5 * deltah_prime);
558
559 const double L_primeBar = 0.5 * (L1 + L2);
560 const double C_primeBar = 0.5 * (C1_prime + C2_prime);
561 const double h_primeBar = computeHPrimeBar(C1_prime, C2_prime, h1_prime, h2_prime);
562
563 const double T = 1.0 - 0.17 * cosDegree(x: h_primeBar - 30.0) + 0.24 * cosDegree(x: 2.0 * h_primeBar) + 0.32 * cosDegree(x: 3.0 * h_primeBar + 6.0)
564 - 0.20 * cosDegree(x: 4.0 * h_primeBar - 63.0);
565
566 const double C_primeBar_pow7 = std::pow(x: C_primeBar, y: 7.0);
567 const double R_C = 2.0 * std::sqrt(x: C_primeBar_pow7 / (C_primeBar_pow7 + _25_pow_7));
568 const double S_L = 1.0 + (0.015 * pow2(x: L_primeBar - 50.0)) / std::sqrt(x: 20.0 + pow2(x: L_primeBar - 50.0));
569 const double S_C = 1.0 + 0.045 * C_primeBar;
570 const double S_H = 1.0 + 0.015 * C_primeBar * T;
571 const double R_T = -R_C * sinDegree(x: 60.0 * std::exp(x: -pow2(x: (h_primeBar - 275) / 25.0)));
572
573 constexpr double kL = 1.0;
574 constexpr double kC = 1.0;
575 constexpr double kH = 1.0;
576
577 const double deltaL = deltaL_prime / (kL * S_L);
578 const double deltaC = deltaC_prime / (kC * S_C);
579 const double deltaH = deltaH_prime / (kH * S_H);
580
581 return /*std::sqrt*/ (deltaL * deltaL + deltaC * deltaC + deltaH * deltaH + R_T * deltaC * deltaH);
582}
583
584struct AnsiBuffer {
585 using ColorCache = QHash<QRgb, int>;
586
587 void append(char c)
588 {
589 Q_ASSERT(remaining() >= 1);
590 m_data[m_size] = c;
591 ++m_size;
592 }
593
594 void append(QLatin1String str)
595 {
596 Q_ASSERT(remaining() >= str.size());
597 memcpy(dest: m_data + m_size, src: str.data(), n: str.size());
598 m_size += str.size();
599 }
600
601 void appendForeground(QRgb rgb, bool is256Colors, ColorCache &colorCache)
602 {
603 append(str: QLatin1String("38;"));
604 append(rgb, is256Colors, colorCache);
605 }
606
607 void appendBackground(QRgb rgb, bool is256Colors, ColorCache &colorCache)
608 {
609 append(str: QLatin1String("48;"));
610 append(rgb, is256Colors, colorCache);
611 }
612
613 void append(QRgb rgb, bool is256Colors, ColorCache &colorCache)
614 {
615 auto appendUInt8 = [&](int x) {
616 Q_ASSERT(x <= 255 && x >= 0);
617 if (x > 99) {
618 if (x >= 200) {
619 append(c: '2');
620 x -= 200;
621 } else {
622 append(c: '1');
623 x -= 100;
624 }
625 } else if (x < 10) {
626 append(c: char('0' + x));
627 return;
628 }
629
630 // clang-format off
631 constexpr char const* tb2digits =
632 "00" "01" "02" "03" "04" "05" "06" "07" "08" "09"
633 "10" "11" "12" "13" "14" "15" "16" "17" "18" "19"
634 "20" "21" "22" "23" "24" "25" "26" "27" "28" "29"
635 "30" "31" "32" "33" "34" "35" "36" "37" "38" "39"
636 "40" "41" "42" "43" "44" "45" "46" "47" "48" "49"
637 "50" "51" "52" "53" "54" "55" "56" "57" "58" "59"
638 "60" "61" "62" "63" "64" "65" "66" "67" "68" "69"
639 "70" "71" "72" "73" "74" "75" "76" "77" "78" "79"
640 "80" "81" "82" "83" "84" "85" "86" "87" "88" "89"
641 "90" "91" "92" "93" "94" "95" "96" "97" "98" "99";
642 // clang-format on
643
644 auto *p = tb2digits + x * 2;
645 append(c: p[0]);
646 append(c: p[1]);
647 };
648
649 if (is256Colors) {
650 double dist = 1e24;
651 int idx = 0;
652 auto it = colorCache.find(key: rgb);
653 if (it == colorCache.end()) {
654 const auto lab = rgbToOklab(rgb);
655 // find the nearest xterm color
656 for (Lab const &xtermLab : xterm240_Oklabs) {
657 auto dist2 = calculate_CIEDE2000(color1: lab, color2: xtermLab);
658 if (dist2 < dist) {
659 dist = dist2;
660 idx = &xtermLab - xterm240_Oklabs;
661 }
662 }
663 // add 16 to convert 240 colors mode to 256 colors mode
664 idx += 16;
665 colorCache.insert(key: rgb, value: idx);
666 } else {
667 idx = it.value();
668 }
669
670 append(c: '5');
671 append(c: ';');
672 appendUInt8(idx);
673 } else {
674 append(c: '2');
675 append(c: ';');
676 appendUInt8(qRed(rgb));
677 append(c: ';');
678 appendUInt8(qGreen(rgb));
679 append(c: ';');
680 appendUInt8(qBlue(rgb));
681 }
682 append(c: ';');
683 }
684
685 // Replace last character with 'm'. Last character must be ';'
686 void setFinalStyle()
687 {
688 Q_ASSERT(m_data[m_size - 1] == ';');
689 m_data[m_size - 1] = 'm';
690 }
691
692 void clear()
693 {
694 m_size = 0;
695 }
696
697 QLatin1String latin1() const
698 {
699 return QLatin1String(m_data, m_size);
700 }
701
702private:
703 char m_data[128];
704 int m_size = 0;
705
706 int remaining() const noexcept
707 {
708 return 128 - m_size;
709 }
710};
711
712void fillString(QString &s, int n, QStringView fill)
713{
714 if (n > 0) {
715 for (; n > fill.size(); n -= fill.size()) {
716 s += fill;
717 }
718 s += fill.sliced(pos: 0, n);
719 }
720}
721
722struct GraphLine {
723 QString graphLine;
724 QString labelLine;
725 int graphLineLength = 0;
726 int labelLineLength = 0;
727 int nextLabelOffset = 0;
728
729 template<class StringBuilder>
730 void pushLabel(int offset, StringBuilder const &s, int numberDisplayableChar)
731 {
732 Q_ASSERT(offset >= labelLineLength);
733 const int n = offset - labelLineLength;
734 labelLineLength += numberDisplayableChar + n;
735 fillLine(s&: labelLine, n);
736 labelLine += s;
737 nextLabelOffset = labelLineLength;
738 }
739
740 template<class StringBuilder>
741 void pushGraph(int offset, StringBuilder const &s, int numberDisplayableChar)
742 {
743 Q_ASSERT(offset >= graphLineLength);
744 const int n = offset - graphLineLength;
745 graphLineLength += numberDisplayableChar + n;
746 fillLine(s&: graphLine, n);
747 const qsizetype ps1 = graphLine.size();
748 graphLine += s;
749 if (offset >= labelLineLength) {
750 const int n2 = offset - labelLineLength;
751 labelLineLength += n2 + 1;
752 fillLine(s&: labelLine, n: n2);
753 labelLine += QStringView(graphLine).sliced(pos: ps1);
754 }
755 }
756
757private:
758 static void fillLine(QString &s, int n)
759 {
760 Q_ASSERT(n >= 0);
761 fillString(s,
762 n,
763 QStringLiteral(" "
764 " "
765 " "));
766 }
767};
768
769/**
770 * Returns the first free line at a given position or create a new one
771 */
772GraphLine &lineAtOffset(std::vector<GraphLine> &graphLines, int offset)
773{
774 const auto last = graphLines.end();
775 auto p = std::find_if(first: graphLines.begin(), last: last, pred: [=](GraphLine const &line) {
776 return line.nextLabelOffset < offset;
777 });
778 if (p == last) {
779 graphLines.emplace_back();
780 return graphLines.back();
781 }
782 return *p;
783}
784
785// disable bold, italic and underline on |
786const QLatin1String graphSymbol("\x1b[22;23;24m|");
787// reverse video
788const QLatin1String nameStyle("\x1b[7m");
789
790/**
791 * ANSI Highlighter dedicated to traces
792 */
793class DebugSyntaxHighlighter : public KSyntaxHighlighting::AbstractHighlighter
794{
795public:
796 using Option = KSyntaxHighlighting::AnsiHighlighter::Option;
797 using Options = KSyntaxHighlighting::AnsiHighlighter::Options;
798
799 void setDefinition(const KSyntaxHighlighting::Definition &def) override
800 {
801 AbstractHighlighter::setDefinition(def);
802 m_contextCapture.setDefinition(def);
803
804 const auto &definitions = def.includedDefinitions();
805 for (const auto &definition : definitions) {
806 const auto *defData = DefinitionData::get(def: definition);
807 for (const auto &context : defData->contexts) {
808 m_defDataBycontexts.insert(key: &context, value: defData);
809 }
810 }
811 }
812
813 void highlightData(QTextStream &in,
814 QTextStream &out,
815 QLatin1String infoStyle,
816 QLatin1String editorBackground,
817 const std::vector<QPair<QString, QString>> &ansiStyles,
818 Options options)
819 {
820 initRegionStyles(ansiStyles);
821
822 m_hasFormatTrace = options.testFlag(flag: Option::TraceFormat);
823 m_hasRegionTrace = options.testFlag(flag: Option::TraceRegion);
824 m_hasStackSizeTrace = options.testFlag(flag: Option::TraceStackSize);
825 m_hasContextTrace = options.testFlag(flag: Option::TraceContext);
826 const bool hasFormatOrContextTrace = m_hasFormatTrace || m_hasContextTrace || m_hasStackSizeTrace;
827
828 const bool hasSeparator = hasFormatOrContextTrace && m_hasRegionTrace;
829 const QString resetBgColor = (editorBackground.isEmpty() ? QStringLiteral("\x1b[0m") : editorBackground);
830
831 bool firstLine = true;
832 State state;
833 QString currentLine;
834 const bool isUnbuffered = options.testFlag(flag: Option::Unbuffered);
835 while (in.readLineInto(line: &currentLine)) {
836 auto oldState = state;
837 state = highlightLine(text: currentLine, state);
838
839 if (hasSeparator) {
840 if (!firstLine) {
841 out << QStringLiteral("\x1b[0m────────────────────────────────────────────────────\x1b[K\n");
842 }
843 firstLine = false;
844 }
845
846 if (!m_regions.empty()) {
847 printRegions(out, regionStyle: infoStyle, lineLength: currentLine.size());
848 out << resetBgColor;
849 }
850
851 for (const auto &fragment : m_highlightedFragments) {
852 auto const &ansiStyle = ansiStyles[fragment.formatId];
853 out << ansiStyle.first << QStringView(currentLine).sliced(pos: fragment.offset, n: fragment.length) << ansiStyle.second;
854 }
855
856 out << QStringLiteral("\x1b[K\n");
857
858 if (hasFormatOrContextTrace && !m_highlightedFragments.empty()) {
859 if (m_hasContextTrace || m_hasStackSizeTrace) {
860 appendContextNames(state&: oldState, currentLine);
861 }
862
863 printFormats(out, regionStyle: infoStyle, ansiStyles);
864 out << resetBgColor;
865 }
866
867 m_highlightedFragments.clear();
868
869 if (isUnbuffered) {
870 out.flush();
871 }
872 }
873 }
874
875 void applyFormat(int offset, int length, const KSyntaxHighlighting::Format &format) override
876 {
877 m_highlightedFragments.push_back(x: {.name: m_hasFormatTrace ? format.name() : QString(), .offset: offset, .length: length, .formatId: format.id()});
878 }
879
880 void applyFolding(int offset, int /*length*/, FoldingRegion region) override
881 {
882 if (!m_hasRegionTrace) {
883 return;
884 }
885
886 const auto id = region.id();
887
888 if (region.type() == FoldingRegion::Begin) {
889 m_regions.push_back(x: Region{.depth: m_regionDepth, .offset: offset, .bindIndex: -1, .regionId: id, .state: Region::State::Open});
890 // swap with previous region if this is a closing region with same offset in order to superimpose labels
891 if (m_regions.size() >= 2) {
892 auto &previousRegion = m_regions[m_regions.size() - 2];
893 if (previousRegion.state == Region::State::Close && previousRegion.offset == offset) {
894 std::swap(a&: previousRegion, b&: m_regions.back());
895 if (previousRegion.bindIndex != -1) {
896 m_regions[previousRegion.bindIndex].bindIndex = m_regions.size() - 1;
897 }
898 }
899 }
900 ++m_regionDepth;
901 } else {
902 // find open region
903 auto it = m_regions.rbegin();
904 auto eit = m_regions.rend();
905 for (int depth = 0; it != eit; ++it) {
906 if (it->regionId == id && it->bindIndex < 0) {
907 if (it->state == Region::State::Close) {
908 ++depth;
909 } else if (--depth < 0) {
910 break;
911 }
912 }
913 }
914
915 if (it != eit) {
916 it->bindIndex = int(m_regions.size());
917 int bindIndex = int(&*it - m_regions.data());
918 m_regions.push_back(x: Region{.depth: it->depth, .offset: offset, .bindIndex: bindIndex, .regionId: id, .state: Region::State::Close});
919 } else {
920 m_regions.push_back(x: Region{.depth: -1, .offset: offset, .bindIndex: -1, .regionId: id, .state: Region::State::Close});
921 }
922
923 m_regionDepth = std::max(a: m_regionDepth - 1, b: 0);
924 }
925 }
926
927 using KSyntaxHighlighting::AbstractHighlighter::highlightLine;
928
929private:
930 /**
931 * Initializes with colors of \p ansiStyle without duplicate.
932 */
933 void initRegionStyles(const std::vector<QPair<QString, QString>> &ansiStyles)
934 {
935 m_regionStyles.resize(new_size: ansiStyles.size());
936 for (std::size_t i = 0; i < m_regionStyles.size(); ++i) {
937 m_regionStyles[i] = ansiStyles[i].first;
938 }
939
940 std::sort(first: m_regionStyles.begin(), last: m_regionStyles.end());
941 m_regionStyles.erase(first: std::unique(first: m_regionStyles.begin(), last: m_regionStyles.end()), last: m_regionStyles.end());
942 }
943
944 /**
945 * Append the context name in front of the format name.
946 */
947 void appendContextNames(State &state, QStringView currentLine)
948 {
949 auto newState = state;
950 for (auto &fragment : m_highlightedFragments) {
951 QString contextName = extractContextName(stateData: StateData::get(state: newState));
952
953 m_contextCapture.offsetNext = 0;
954 m_contextCapture.lengthNext = 0;
955 // truncate the line to deduce the context from the format
956 const auto lineFragment = currentLine.sliced(pos: 0, n: fragment.offset + fragment.length + 1);
957 newState = m_contextCapture.highlightLine(text: lineFragment, state);
958
959 // Deduced context does not start at the position of the format.
960 // This can happen because of lookAhead/fallthrought attribute,
961 // assertion in regex, etc.
962 if (m_contextCapture.offset != fragment.offset && m_contextCapture.length != fragment.length) {
963 contextName.insert(i: 0, c: QLatin1Char('~'));
964 }
965 fragment.name.insert(i: 0, s: contextName);
966 }
967 }
968
969 /**
970 * \return Current context name with definition name if different
971 * from the current definition name
972 */
973 QString extractContextName(StateData *stateData) const
974 {
975 QString label;
976
977 if (m_hasStackSizeTrace) {
978 // first state is empty
979 int stateSize = stateData ? stateData->size() : 0;
980 label = QLatin1Char('(') % QString::number(stateSize) % QLatin1Char(')');
981 }
982
983 if (m_hasContextTrace) {
984 // first state is empty
985 if (!stateData) {
986 return label + QStringLiteral("[???]");
987 }
988
989 const auto context = stateData->topContext();
990 const auto defDataIt = m_defDataBycontexts.find(key: context);
991 const auto contextName = (defDataIt != m_defDataBycontexts.end()) ? QString(QLatin1Char('<') % (*defDataIt)->name % QLatin1Char('>')) : QString();
992 return QString(label % contextName % QLatin1Char('[') % context->name() % QLatin1Char(']'));
993 }
994
995 return label;
996 }
997
998 void printFormats(QTextStream &out, QLatin1String regionStyle, const std::vector<QPair<QString, QString>> &ansiStyles)
999 {
1000 // init graph
1001 m_formatGraph.clear();
1002 for (auto const &fragment : m_highlightedFragments) {
1003 GraphLine &line = lineAtOffset(graphLines&: m_formatGraph, offset: fragment.offset);
1004 auto const &style = ansiStyles[fragment.formatId].first;
1005 line.pushLabel(offset: fragment.offset, s: style % nameStyle % fragment.name % regionStyle, numberDisplayableChar: fragment.name.size());
1006
1007 for (GraphLine *pline = m_formatGraph.data(); pline <= &line; ++pline) {
1008 pline->pushGraph(offset: fragment.offset, s: style % graphSymbol % regionStyle, numberDisplayableChar: 1);
1009 }
1010 }
1011
1012 // display graph
1013 out << regionStyle;
1014 auto first = m_formatGraph.begin();
1015 auto last = m_formatGraph.end();
1016 --last;
1017 for (; first != last; ++first) {
1018 out << first->graphLine << "\x1b[K\n" << first->labelLine << "\x1b[K\n";
1019 }
1020 out << first->graphLine << "\x1b[K\n" << first->labelLine << "\x1b[K\x1b[0m\n";
1021 }
1022
1023 void printRegions(QTextStream &out, QLatin1String regionStyle, int lineLength)
1024 {
1025 const QString continuationLine = QStringLiteral(
1026 "------------------------------"
1027 "------------------------------"
1028 "------------------------------");
1029
1030 bool hasContinuation = false;
1031
1032 m_regionGraph.clear();
1033 QString label;
1034 QString numStr;
1035
1036 // init graph
1037 for (Region &region : m_regions) {
1038 if (region.state == Region::State::Continuation) {
1039 hasContinuation = true;
1040 continue;
1041 }
1042
1043 auto pushGraphs = [&](int offset, const GraphLine *endline, QStringView style) {
1044 for (GraphLine *pline = m_regionGraph.data(); pline <= endline; ++pline) {
1045 // a label can hide a graph
1046 if (pline->graphLineLength <= offset) {
1047 pline->pushGraph(offset, s: style % graphSymbol % regionStyle, numberDisplayableChar: 1);
1048 }
1049 }
1050 };
1051
1052 QChar openChar;
1053 QChar closeChar;
1054 int lpad = 0;
1055 int rpad = 0;
1056
1057 int offsetLabel = region.offset;
1058
1059 numStr.setNum(n: region.regionId);
1060
1061 if (region.state == Region::State::Open) {
1062 openChar = QLatin1Char('(');
1063 if (region.bindIndex == -1) {
1064 rpad = lineLength - region.offset - numStr.size();
1065 } else {
1066 rpad = m_regions[region.bindIndex].offset - region.offset - 2;
1067 closeChar = QLatin1Char(')');
1068 }
1069 // close without open
1070 } else if (region.bindIndex == -1) {
1071 closeChar = QLatin1Char('>');
1072 // label already present, we only display the graph
1073 } else if (m_regions[region.bindIndex].state == Region::State::Open) {
1074 const auto &openRegion = m_regions[region.bindIndex];
1075 // here offset is a graph index
1076 const GraphLine &line = m_regionGraph[openRegion.offset];
1077 const auto &style = m_regionStyles[openRegion.depth % m_regionStyles.size()];
1078 pushGraphs(region.offset, &line, style);
1079 continue;
1080 } else {
1081 closeChar = QLatin1Char(')');
1082 lpad = region.offset - numStr.size();
1083 offsetLabel = 0;
1084 }
1085
1086 const QStringView openS(&openChar, openChar.unicode() ? 1 : 0);
1087 const QStringView closeS(&closeChar, closeChar.unicode() ? 1 : 0);
1088
1089 label.clear();
1090 fillString(s&: label, n: lpad, fill: continuationLine);
1091 label += numStr;
1092 fillString(s&: label, n: rpad, fill: continuationLine);
1093
1094 GraphLine &line = lineAtOffset(graphLines&: m_regionGraph, offset: offsetLabel);
1095 const auto &style = m_regionStyles[region.depth % m_regionStyles.size()];
1096 line.pushLabel(offset: offsetLabel, s: style % nameStyle % openS % label % closeS % regionStyle, numberDisplayableChar: label.size() + openS.size() + closeS.size());
1097 pushGraphs(region.offset, &line, style);
1098
1099 // transforms offset into graph index when region is on 1 line
1100 if (region.state == Region::State::Open && region.bindIndex != -1) {
1101 region.offset = &line - m_regionGraph.data();
1102 }
1103 }
1104
1105 out << regionStyle;
1106
1107 // display regions which are neither closed nor open
1108 if (hasContinuation) {
1109 label.clear();
1110 fillString(s&: label, n: lineLength ? lineLength : 5, fill: continuationLine);
1111 for (const auto &region : m_regions) {
1112 if (region.state == Region::State::Continuation && region.bindIndex == -1) {
1113 const auto &style = m_regionStyles[region.depth % m_regionStyles.size()];
1114 out << style << nameStyle << label << regionStyle << "\x1b[K\n";
1115 }
1116 }
1117 }
1118
1119 // display graph
1120 if (!m_regionGraph.empty()) {
1121 auto first = m_regionGraph.rbegin();
1122 auto last = m_regionGraph.rend();
1123 --last;
1124 for (; first != last; ++first) {
1125 out << first->labelLine << "\x1b[K\n" << first->graphLine << "\x1b[K\n";
1126 }
1127 out << first->labelLine << "\x1b[K\n" << first->graphLine << "\x1b[K\x1b[0m\n";
1128 }
1129
1130 // keep regions that are not closed
1131 m_regions.erase(first: std::remove_if(first: m_regions.begin(),
1132 last: m_regions.end(),
1133 pred: [](Region const &region) {
1134 return region.bindIndex != -1 || region.state == Region::State::Close;
1135 }),
1136 last: m_regions.end());
1137 // all remaining regions become Continuation
1138 for (auto &region : m_regions) {
1139 region.offset = 0;
1140 region.state = Region::State::Continuation;
1141 }
1142 }
1143
1144 struct HighlightFragment {
1145 QString name;
1146 int offset;
1147 int length;
1148 int formatId;
1149 };
1150
1151 struct ContextCaptureHighlighter : KSyntaxHighlighting::AbstractHighlighter {
1152 int offset;
1153 int length;
1154 int offsetNext;
1155 int lengthNext;
1156
1157 void applyFormat(int offset, int length, const KSyntaxHighlighting::Format & /*format*/) override
1158 {
1159 offset = offsetNext;
1160 length = lengthNext;
1161 offsetNext = offset;
1162 lengthNext = length;
1163 }
1164
1165 using KSyntaxHighlighting::AbstractHighlighter::highlightLine;
1166 };
1167
1168 struct Region {
1169 enum class State : int8_t {
1170 Open,
1171 Close,
1172 Continuation,
1173 };
1174
1175 int depth;
1176 int offset;
1177 int bindIndex;
1178 int regionId;
1179 State state;
1180 };
1181
1182 bool m_hasFormatTrace;
1183 bool m_hasRegionTrace;
1184 bool m_hasStackSizeTrace;
1185 bool m_hasContextTrace;
1186
1187 std::vector<HighlightFragment> m_highlightedFragments;
1188 std::vector<GraphLine> m_formatGraph;
1189 ContextCaptureHighlighter m_contextCapture;
1190
1191 int m_regionDepth = 0;
1192 std::vector<Region> m_regions;
1193 std::vector<GraphLine> m_regionGraph;
1194 std::vector<QStringView> m_regionStyles;
1195
1196 QHash<const Context *, const DefinitionData *> m_defDataBycontexts;
1197};
1198} // anonymous namespace
1199
1200class KSyntaxHighlighting::AnsiHighlighterPrivate : public AbstractHighlighterPrivate
1201{
1202public:
1203 QTextStream out;
1204 QFile file;
1205 QStringView currentLine;
1206 // pairs of startColor / resetColor
1207 std::vector<QPair<QString, QString>> ansiStyles;
1208 Theme::EditorColorRole bgRole = Theme::BackgroundColor;
1209};
1210
1211AnsiHighlighter::AnsiHighlighter()
1212 : AbstractHighlighter(new AnsiHighlighterPrivate())
1213{
1214}
1215
1216AnsiHighlighter::~AnsiHighlighter() = default;
1217
1218void KSyntaxHighlighting::AnsiHighlighter::setBackgroundRole(Theme::EditorColorRole bgRole)
1219{
1220 Q_D(AnsiHighlighter);
1221 d->bgRole = bgRole;
1222}
1223
1224void AnsiHighlighter::setOutputFile(const QString &fileName)
1225{
1226 Q_D(AnsiHighlighter);
1227 if (d->file.isOpen()) {
1228 d->file.close();
1229 }
1230 d->file.setFileName(fileName);
1231 if (!d->file.open(flags: QFile::WriteOnly | QFile::Truncate)) {
1232 qCWarning(Log) << "Failed to open output file" << fileName << ":" << d->file.errorString();
1233 return;
1234 }
1235 d->out.setDevice(&d->file);
1236}
1237
1238void AnsiHighlighter::setOutputFile(FILE *fileHandle)
1239{
1240 Q_D(AnsiHighlighter);
1241 if (!d->file.open(f: fileHandle, ioFlags: QIODevice::WriteOnly)) {
1242 qCWarning(Log) << "Failed to open output file" << fileHandle << ":" << d->file.errorString();
1243 return;
1244 }
1245 d->out.setDevice(&d->file);
1246}
1247
1248void AnsiHighlighter::highlightFile(const QString &fileName, AnsiFormat format, Options options)
1249{
1250 QFile f(fileName);
1251 if (!f.open(flags: QFile::ReadOnly)) {
1252 qCWarning(Log) << "Failed to open input file" << fileName << ":" << f.errorString();
1253 return;
1254 }
1255
1256 highlightData(device: &f, format, options);
1257}
1258
1259void AnsiHighlighter::highlightData(QIODevice *dev, AnsiFormat format, Options options)
1260{
1261 Q_D(AnsiHighlighter);
1262
1263 if (!d->out.device()) {
1264 qCWarning(Log) << "No output stream defined!";
1265 return;
1266 }
1267
1268 const auto is256Colors = (format == AnsiFormat::XTerm256Color);
1269 const auto &theme = d->m_theme;
1270 const auto &definition = d->m_definition;
1271
1272 auto definitions = definition.includedDefinitions();
1273 definitions.append(t: definition);
1274
1275 AnsiBuffer::ColorCache colorCache;
1276
1277 AnsiBuffer foregroundColorBuffer;
1278 AnsiBuffer backgroundColorBuffer;
1279 QLatin1String foregroundDefaultColor;
1280 QLatin1String backgroundDefaultColor;
1281
1282 const bool useEditorBackground = options.testFlag(flag: Option::UseEditorBackground);
1283 const bool useSelectedText = useEditorBackground && d->bgRole == Theme::TextSelection;
1284
1285 const auto toRGB = [](QRgb argb) {
1286 return argb & 0xff'ff'ffu;
1287 };
1288
1289 const QRgb foregroundColor = [&] {
1290 if (useSelectedText) {
1291 const auto fg = theme.selectedTextColor(style: Theme::Normal);
1292 if (fg) {
1293 return toRGB(fg);
1294 }
1295 }
1296 return useEditorBackground ? toRGB(theme.textColor(style: Theme::Normal)) : 0;
1297 }();
1298 const QRgb backgroundColor = useEditorBackground ? toRGB(theme.editorColor(role: d->bgRole)) : 0;
1299
1300 // https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters
1301
1302 if (useEditorBackground) {
1303 foregroundColorBuffer.appendForeground(rgb: foregroundColor, is256Colors, colorCache);
1304 backgroundColorBuffer.append(str: QLatin1String("\x1b["));
1305 backgroundColorBuffer.appendBackground(rgb: backgroundColor, is256Colors, colorCache);
1306 foregroundDefaultColor = foregroundColorBuffer.latin1();
1307 backgroundDefaultColor = backgroundColorBuffer.latin1().sliced(pos: 2);
1308 }
1309
1310 int maxId = 0;
1311 for (const auto &definition : std::as_const(t&: definitions)) {
1312 for (const auto &format : std::as_const(t&: DefinitionData::get(def: definition)->formats)) {
1313 maxId = qMax(a: maxId, b: format.id());
1314 }
1315 }
1316 d->ansiStyles.clear();
1317 // ansiStyles must not be empty for applyFormat to work even with a definition without any context
1318 d->ansiStyles.resize(new_size: maxId + 1);
1319
1320 // initialize ansiStyles
1321 for (const auto &definition : std::as_const(t&: definitions)) {
1322 for (const auto &format : std::as_const(t&: DefinitionData::get(def: definition)->formats)) {
1323 AnsiBuffer buffer;
1324
1325 buffer.append(str: QLatin1String("\x1b["));
1326
1327 const auto fg = useSelectedText ? format.selectedTextColor(theme).rgba() : format.textColor(theme).rgba();
1328 const auto bg = useSelectedText ? format.selectedBackgroundColor(theme).rgba() : format.backgroundColor(theme).rgba();
1329 const bool hasFg = fg && (!useEditorBackground || toRGB(fg) != foregroundColor);
1330 const bool hasBg = bg && (!useEditorBackground || toRGB(bg) != backgroundColor);
1331 const bool hasBold = format.isBold(theme);
1332 const bool hasItalic = format.isItalic(theme);
1333 const bool hasUnderline = format.isUnderline(theme);
1334 const bool hasStrikeThrough = format.isStrikeThrough(theme);
1335
1336 if (hasFg) {
1337 buffer.appendForeground(rgb: toRGB(fg), is256Colors, colorCache);
1338 } else {
1339 buffer.append(str: foregroundDefaultColor);
1340 }
1341 if (hasBg) {
1342 buffer.appendBackground(rgb: toRGB(bg), is256Colors, colorCache);
1343 }
1344 if (hasBold) {
1345 buffer.append(str: QLatin1String("1;"));
1346 }
1347 if (hasItalic) {
1348 buffer.append(str: QLatin1String("3;"));
1349 }
1350 if (hasUnderline) {
1351 buffer.append(str: QLatin1String("4;"));
1352 }
1353 if (hasStrikeThrough) {
1354 buffer.append(str: QLatin1String("9;"));
1355 }
1356
1357 // if there is ANSI style
1358 if (buffer.latin1().size() > 2) {
1359 buffer.setFinalStyle();
1360 auto &style = d->ansiStyles[format.id()];
1361 style.first = buffer.latin1();
1362
1363 if (useEditorBackground) {
1364 buffer.clear();
1365 const bool hasEffect = hasBold || hasItalic || hasUnderline || hasStrikeThrough;
1366 if (hasBg) {
1367 buffer.append(str: hasEffect ? QLatin1String("\x1b[0;") : QLatin1String("\x1b["));
1368 buffer.append(str: backgroundDefaultColor);
1369 buffer.setFinalStyle();
1370 style.second = buffer.latin1();
1371 } else if (hasEffect) {
1372 buffer.append(str: QLatin1String("\x1b["));
1373 if (hasBold) {
1374 buffer.append(str: QLatin1String("22;"));
1375 }
1376 if (hasItalic) {
1377 buffer.append(str: QLatin1String("23;"));
1378 }
1379 if (hasUnderline) {
1380 buffer.append(str: QLatin1String("24;"));
1381 }
1382 if (hasStrikeThrough) {
1383 buffer.append(str: QLatin1String("29;"));
1384 }
1385 buffer.setFinalStyle();
1386 style.second = buffer.latin1();
1387 }
1388 } else {
1389 style.second = QStringLiteral("\x1b[0m");
1390 }
1391 }
1392 }
1393 }
1394
1395 if (useEditorBackground) {
1396 backgroundColorBuffer.setFinalStyle();
1397 backgroundDefaultColor = backgroundColorBuffer.latin1();
1398 d->out << backgroundDefaultColor;
1399 }
1400
1401 QTextStream in(dev);
1402
1403 if (!options.testAnyFlag(flag: Option::TraceAll)) {
1404 State state;
1405 QString currentLine;
1406 const bool isUnbuffered = options.testFlag(flag: Option::Unbuffered);
1407 while (in.readLineInto(line: &currentLine)) {
1408 d->currentLine = currentLine;
1409 state = highlightLine(text: d->currentLine, state);
1410
1411 if (useEditorBackground) {
1412 d->out << QStringLiteral("\x1b[K\n");
1413 } else {
1414 d->out << QLatin1Char('\n');
1415 }
1416
1417 if (isUnbuffered) {
1418 d->out.flush();
1419 }
1420 }
1421 } else {
1422 AnsiBuffer buffer;
1423 buffer.append(str: QLatin1String("\x1b[0;"));
1424 buffer.appendBackground(rgb: theme.editorColor(role: useEditorBackground ? Theme::TemplateBackground : Theme::BackgroundColor), is256Colors, colorCache);
1425 buffer.setFinalStyle();
1426 DebugSyntaxHighlighter debugHighlighter;
1427 debugHighlighter.setDefinition(definition);
1428 debugHighlighter.highlightData(in, out&: d->out, infoStyle: buffer.latin1(), editorBackground: backgroundDefaultColor, ansiStyles: d->ansiStyles, options);
1429 }
1430
1431 if (useEditorBackground) {
1432 d->out << QStringLiteral("\x1b[0m");
1433 }
1434
1435 d->out.setDevice(nullptr);
1436 d->file.close();
1437 d->ansiStyles.clear();
1438}
1439
1440void AnsiHighlighter::applyFormat(int offset, int length, const Format &format)
1441{
1442 Q_D(AnsiHighlighter);
1443 auto const &ansiStyle = d->ansiStyles[format.id()];
1444 d->out << ansiStyle.first << d->currentLine.sliced(pos: offset, n: length) << ansiStyle.second;
1445}
1446

source code of syntax-highlighting/src/lib/ansihighlighter.cpp