8
8
9
9
#include < QTextDocument> // for escape()
10
10
11
- // With Qt4 this func was more complex. Now we don't need it
12
11
QString TextUtil::escape (const QString &plain) { return plain.toHtmlEscaped (); }
13
12
13
+ /* *
14
+ * Converts a given QString containing escaped XML/HTML entities into their unescaped equivalents.
15
+ *
16
+ * This function replaces specific XML/HTML escape sequences within the input string
17
+ * with their corresponding special characters. The replacements include:
18
+ * - "<" with "<"
19
+ * - ">" with ">"
20
+ * - """ with "\""
21
+ * - "&" with "&"
22
+ *
23
+ * @param escaped The QString containing escaped XML/HTML entities to be unescaped.
24
+ * @return A QString where the escape sequences are replaced with their corresponding characters.
25
+ */
14
26
QString TextUtil::unescape (const QString &escaped)
15
27
{
16
28
QString plain = escaped;
@@ -21,65 +33,103 @@ QString TextUtil::unescape(const QString &escaped)
21
33
return plain;
22
34
}
23
35
36
+ /* *
37
+ * Quote text for a response with "> " prefix.
38
+ * Nested quotes and word wrapping are supported.
39
+ * assert(TextUtil::quote("Hello world", 80, true) == "> Hello world\n\n")
40
+ * // Line wrapping
41
+ * assert(TextUtil::quote("This is a very long line that should be wrapped", 20, true) == "> This is a very\n> long line that should be\n> wrapped\n\n")
42
+ * // Empty lines
43
+ * assert(TextUtil::quote("Line1\n\nLine2", 80, false) == "> Line1\n\n> Line2\n\n")
44
+ * // Empty lines: quoteEmpty
45
+ * assert(TextUtil::quote("Line1\n\nLine2", 80, true) == "> Line1\n>\n> Line2\n\n")
46
+ * @param toquote text to quote
47
+ * @param width width where to wrap text
48
+ * @param quoteEmpty add > to empty lines
49
+ * @return
50
+ */
24
51
QString TextUtil::quote (const QString &toquote, int width, bool quoteEmpty)
25
52
{
26
- int ql = 0 , col = 0 , atstart = 1 , ls = 0 ;
27
-
28
- QString quoted = " > " + toquote; // quote first line
29
- QString rxs = quoteEmpty ? " \n " : " \n (?!\\ s*\n )" ;
30
- QRegularExpression rx (rxs); // quote following lines
31
- quoted.replace (rx, " \n > " );
32
- rx.setPattern (" > +>" ); // compress > > > > quotes to >>>>
33
- quoted.replace (rx, " >>" );
34
- quoted.replace (rx, " >>" );
35
- quoted.replace (QRegularExpression (" +\n " ), " \n " ); // remove trailing spaces
53
+ int quoteLevel = 0 ; // amount of leading '>' in the current line
54
+ int column = 0 ; // current column
55
+ bool atLineStart = true ; // at beginning of line
56
+ int lastSpaceIndex = 0 ; // index of last whitespace to break line
57
+
58
+ static const QRegularExpression rxTrimTrailingSpaces (QStringLiteral (" +\n " ));
59
+ static const QRegularExpression rxUnquote1 (QStringLiteral (" ^>+\n " ));
60
+ static const QRegularExpression rxUnquote2 (QStringLiteral (" \n >+\n " ));
61
+ static const QRegularExpression rxFollowLineEmpty (QStringLiteral (" \n " ));
62
+ static const QRegularExpression rxFollowLinePattern (QStringLiteral (" \n (?!\\ s*\n )" ));
63
+ static const QRegularExpression rxCompress (QStringLiteral (" > +>" ));
64
+
65
+ // quote first line
66
+ QString quoted = QStringLiteral (" > " ) + toquote;
67
+ QRegularExpression rx = quoteEmpty ? rxFollowLineEmpty : rxFollowLinePattern;
68
+ // quote the following lines
69
+ quoted.replace (rx, QStringLiteral (" \n > " ));
70
+ // compress > > > > quotes to >>>>
71
+ quoted.replace (rxCompress, QStringLiteral (" >>" ));
72
+ quoted.replace (rxCompress, QStringLiteral (" >>" ));
73
+ // remove trailing spaces before a newline
74
+ quoted.replace (rxTrimTrailingSpaces, QStringLiteral (" \n " ));
36
75
37
76
if (!quoteEmpty) {
38
- quoted.replace (QRegularExpression (" ^>+\n " ), " \n\n " ); // unquote empty lines
39
- quoted.replace (QRegularExpression (" \n >+\n " ), " \n\n " );
77
+ // unquote empty lines
78
+ quoted.replace (rxUnquote1, QStringLiteral (" \n\n " ));
79
+ quoted.replace (rxUnquote2, QStringLiteral (" \n\n " ));
40
80
}
41
81
42
82
for (int i = 0 ; i < int (quoted.length ()); i++) {
43
- col++;
44
- if (atstart && quoted[i] == ' >' )
45
- ql++;
46
- else
47
- atstart = 0 ;
83
+ column++;
84
+ if (atLineStart && quoted[i] == ' >' ) {
85
+ quoteLevel++;
86
+ } else {
87
+ atLineStart = false ;
88
+ }
48
89
49
90
switch (quoted[i].toLatin1 ()) {
50
91
case ' \n ' :
51
- ql = col = 0 ;
52
- atstart = 1 ;
92
+ // Reset state at a newline
93
+ quoteLevel = 0 ;
94
+ column = 0 ;
95
+ atLineStart = true ;
53
96
break ;
54
97
case ' ' :
55
98
case ' \t ' :
56
- ls = i;
99
+ lastSpaceIndex = i;
57
100
break ;
58
101
}
59
102
if (quoted[i] == ' \n ' ) {
60
- ql = 0 ;
61
- atstart = 1 ;
103
+ quoteLevel = 0 ;
104
+ atLineStart = true ;
62
105
}
63
106
64
- if (col > width) {
65
- if ((ls + width) < i) {
66
- ls = i;
107
+ if (column > width) {
108
+ // If we have no breakable space in range, advance to the next whitespace
109
+ if ((lastSpaceIndex + width) < i) {
110
+ // advance to the next whitespace from the position
111
+ lastSpaceIndex = i;
67
112
i = quoted.length ();
68
- while ((ls < i) && !quoted[ls].isSpace ())
69
- ls++;
70
- i = ls;
113
+ while ((lastSpaceIndex < i) && !quoted[lastSpaceIndex].isSpace ()) {
114
+ lastSpaceIndex++;
115
+ }
116
+ i = lastSpaceIndex;
71
117
}
72
- if ((i < int (quoted.length ())) && (quoted[ls] != ' \n ' )) {
73
- quoted.insert (ls, ' \n ' );
74
- ++ls;
75
- quoted.insert (ls, QString ().fill (' >' , ql));
76
- i += ql + 1 ;
77
- col = 0 ;
118
+ if ((i < int (quoted.length ())) && (quoted[lastSpaceIndex] != ' \n ' )) {
119
+ // Insert newline at lastSpaceIndex
120
+ quoted.insert (lastSpaceIndex, QLatin1Char (' \n ' ));
121
+ ++lastSpaceIndex;
122
+ // Re-insert the current quote prefix to the next line
123
+ quoted.insert (lastSpaceIndex, QString ().fill (QLatin1Char (' >' ), quoteLevel));
124
+ // count of inserted '>' chars
125
+ i += (quoteLevel + 1 );
126
+ // Reset wrapping state for the new line
127
+ column = 0 ;
78
128
}
79
129
}
80
130
}
81
- quoted += " \n\n " ; // add two empty lines to quoted text - the cursor
82
- // will be positioned at the end of those.
131
+ // add two empty lines to the quoted text - the cursor will be positioned at the end of those.
132
+ quoted += QStringLiteral ( " \n\n " );
83
133
return quoted;
84
134
}
85
135
0 commit comments