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

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