@@ -88,7 +88,7 @@ void applyAnsiCodeToFormat(QTextCharFormat &fmt, const QString &codeStr) {
8888 break ;
8989 case 2 :
9090 fmt.setFontWeight (QFont::Normal);
91- break ; // Faint fallback
91+ break ;
9292 case 3 :
9393 fmt.setFontItalic (true );
9494 break ;
@@ -185,15 +185,12 @@ void insertLinkifiedText(QTextCursor &cursor, const QString &text,
185185 }
186186}
187187
188- void appendAnsiHtml (QTextEdit *edit, const QString &ansiText) {
188+ void appendAnsiText (QTextEdit *edit, const QString &ansiText) {
189189 auto cursor = edit->textCursor ();
190190 cursor.movePosition (QTextCursor::End);
191191
192192 static QRegularExpression ansiRegex (" \x1b\\ [([0-9;]*)m" );
193193 static QRegularExpression removeRegex (" \x1b\\ [K" );
194- static QRegularExpression urlRegex (R"( (https?|file)://[^\s<>()]+)" );
195- static QRegularExpression pathRegex (
196- R"( (([A-Za-z]:[\\/][\w\-.+\\/ ]+|~?/?[\w\-.+/]+)\.[a-zA-Z0-9+_-]{1,8})(:\d+)?(:\d+)?(:)?)" );
197194
198195 auto cleanedText = ansiText;
199196 cleanedText.remove (removeRegex);
@@ -235,170 +232,6 @@ void appendAnsiHtml(QTextEdit *edit, const QString &ansiText) {
235232 edit->ensureCursorVisible ();
236233}
237234
238- auto appendAnsiHtml2 (QTextEdit *edit, const QString &ansiText) -> void {
239- edit->moveCursor (QTextCursor::End);
240-
241- #if 1
242- auto html = ansiToHtml (ansiText, true );
243- edit->insertHtml (ansiText);
244- #else
245- edit->insertPlainText(ansiText);
246- #endif
247- edit->moveCursor (QTextCursor::End);
248- edit->ensureCursorVisible ();
249- }
250-
251- auto static isInsideAnchor (const QString &html, int pos) -> bool {
252- auto open = html.lastIndexOf (" <a " , pos, Qt::CaseInsensitive);
253- auto close = html.lastIndexOf (" </a>" , pos, Qt::CaseInsensitive);
254- return open != -1 && (close == -1 || open > close);
255- };
256-
257- auto linkifyFileNames (const QString &html) -> QString {
258- auto pathRegex = QRegularExpression (
259- R"( (([A-Za-z]:[\\/][\w\-.+\\/ ]+|~?/?[\w\-.+/]+)\.[a-zA-Z0-9+_-]{1,8})(:\d+)?(:\d+)?(:)?)" );
260-
261- auto result = QString{};
262- auto lastPos = 0 ;
263- auto it = pathRegex.globalMatch (html);
264-
265- while (it.hasNext ()) {
266- auto match = it.next ();
267- auto start = match.capturedStart ();
268- auto end = match.capturedEnd ();
269- auto fullMatch = match.captured (0 );
270- auto filePart = match.captured (1 );
271- auto line = match.captured (3 ).mid (1 );
272- auto column = match.captured (4 ).mid (1 );
273- auto insideAnchor = isInsideAnchor (html, start);
274-
275- result += html.mid (lastPos, start - lastPos);
276- if (insideAnchor) {
277- result += fullMatch;
278- } else {
279- auto fragment = QString{};
280- auto urlPath = filePart;
281- // Convert backslashes to slashes for file:// URLs
282- urlPath.replace (" \\ " , " /" );
283- auto fileUrl = QUrl::fromLocalFile (urlPath);
284-
285- if (!line.isEmpty ()) {
286- fragment += line;
287- }
288- if (!column.isEmpty ()) {
289- if (!fragment.isEmpty ()) {
290- fragment += " ," ;
291- }
292- fragment += column;
293- }
294- if (!fragment.isEmpty ()) {
295- fileUrl.setFragment (fragment);
296- }
297- auto link =
298- QString (" <a href=\" %1\" >%2</a>" ).arg (fileUrl.toString (), fullMatch.toHtmlEscaped ());
299- result += link;
300- }
301- lastPos = end;
302- }
303- result += html.mid (lastPos);
304- return result;
305- }
306-
307- QString linkifyUrls (const QString &html) {
308- static const QRegularExpression urlRegex (
309- R"( (([a-z][a-z0-9+\-.]*://[^\s<>"']+)|www\.[^\s<>"']+))" ,
310- QRegularExpression::CaseInsensitiveOption);
311-
312- auto output = QString{};
313- auto lastPos = 0 ;
314- auto matchIter = urlRegex.globalMatch (html);
315- while (matchIter.hasNext ()) {
316- auto match = matchIter.next ();
317- auto start = match.capturedStart ();
318- auto end = match.capturedEnd ();
319-
320- if (isInsideAnchor (html, start)) {
321- continue ;
322- }
323- output += html.mid (lastPos, start - lastPos);
324- auto url = match.captured (0 );
325- // Only prepend http:// if the url starts with www.
326- auto href = url.startsWith (" www." , Qt::CaseInsensitive) ? " http://" + url : url;
327- output += " <a href=\" " + href.toHtmlEscaped () + " \" >" + url.toHtmlEscaped () + " </a>" ;
328- lastPos = end;
329- }
330- output += html.mid (lastPos);
331- return output;
332- }
333-
334- auto ansiToHtml (const QString &ansiText, bool linkifyFiles) -> QString {
335- static auto ansiRegex = QRegularExpression (" \x1b\\ [([0-9;]*)m" );
336- static auto removeRegex = QRegularExpression (" \x1b\\ [K" );
337-
338- auto htmlText = ansiText;
339- htmlText.replace (" &" , " &" );
340- htmlText.replace (" <" , " <" );
341- htmlText.replace (" >" , " >" );
342- htmlText.replace (" " , " " );
343- htmlText.replace (" \n " , " <br>\n " );
344-
345- htmlText.remove (removeRegex);
346-
347- auto ansiToCss = QMap<QString, QString>{
348- {" 30" , " black" }, {" 31" , " red" }, {" 32" , " green" }, {" 33" , " yellow" },
349- {" 34" , " blue" }, {" 35" , " magenta" }, {" 36" , " cyan" }, {" 37" , " white" },
350- {" 90" , " gray" }, {" 91" , " lightcoral" }, {" 92" , " lightgreen" }, {" 93" , " khaki" },
351- {" 94" , " lightskyblue" }, {" 95" , " violet" }, {" 96" , " lightcyan" }, {" 97" , " whitesmoke" },
352- };
353-
354- auto lastPos = 0 ;
355- auto it = ansiRegex.globalMatch (htmlText);
356- QStringList openTags;
357- QString result;
358-
359- while (it.hasNext ()) {
360- auto match = it.next ();
361- auto codes = match.captured (1 ).split (' ;' );
362- auto isReset = (codes.isEmpty () || codes.contains (" 0" ));
363-
364- result += htmlText.mid (lastPos, match.capturedStart () - lastPos);
365- if (isReset) {
366- while (!openTags.isEmpty ()) {
367- result += openTags.takeLast ();
368- }
369- } else {
370- auto style = QString{};
371- for (auto const &code : std::as_const (codes)) {
372- if (ansiToCss.contains (code)) {
373- style += QString (" color:%1;" ).arg (ansiToCss.value (code));
374- } else if (code == " 1" ) {
375- style += " font-weight:bold;" ;
376- }
377- // Add more style handlers here if needed
378- }
379- if (!style.isEmpty ()) {
380- result += QString (" <span style=\" %1\" >" ).arg (style);
381- openTags.append (" </span>" );
382- }
383- }
384-
385- lastPos = match.capturedEnd ();
386- }
387-
388- result += htmlText.mid (lastPos);
389- while (!openTags.isEmpty ()) {
390- result += openTags.takeLast ();
391- }
392-
393- result = linkifyUrls (result);
394-
395- if (linkifyFiles) {
396- result = linkifyFileNames (result);
397- }
398-
399- return result + " </br>" ;
400- }
401-
402235auto removeAnsiEscapeCodes (const QString &input) -> QString {
403236 static const QRegularExpression ansiRegex (
404237 R"( (\x1B\[[0-9;?]*[ -/]*[@-~])|(\x1B\]8;[^\x07\x1B]*[\x07\x1B\\]))" );
0 commit comments