/src/mozilla-central/editor/libeditor/InternetCiter.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
3 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
4 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
5 | | |
6 | | #include "InternetCiter.h" |
7 | | |
8 | | #include "nsAString.h" |
9 | | #include "nsCOMPtr.h" |
10 | | #include "nsCRT.h" |
11 | | #include "nsDebug.h" |
12 | | #include "nsDependentSubstring.h" |
13 | | #include "nsError.h" |
14 | | #include "mozilla/intl/LineBreaker.h" |
15 | | #include "nsServiceManagerUtils.h" |
16 | | #include "nsString.h" |
17 | | #include "nsStringIterator.h" |
18 | | |
19 | | namespace mozilla { |
20 | | |
21 | | const char16_t gt ('>'); |
22 | | const char16_t space (' '); |
23 | | const char16_t nl ('\n'); |
24 | | const char16_t cr('\r'); |
25 | | |
26 | | /** |
27 | | * Mail citations using the Internet style: > This is a citation. |
28 | | */ |
29 | | |
30 | | nsresult |
31 | | InternetCiter::GetCiteString(const nsAString& aInString, |
32 | | nsAString& aOutString) |
33 | 0 | { |
34 | 0 | aOutString.Truncate(); |
35 | 0 | char16_t uch = nl; |
36 | 0 |
|
37 | 0 | // Strip trailing new lines which will otherwise turn up |
38 | 0 | // as ugly quoted empty lines. |
39 | 0 | nsReadingIterator <char16_t> beginIter,endIter; |
40 | 0 | aInString.BeginReading(beginIter); |
41 | 0 | aInString.EndReading(endIter); |
42 | 0 | while(beginIter!= endIter && |
43 | 0 | (*endIter == cr || *endIter == nl)) { |
44 | 0 | --endIter; |
45 | 0 | } |
46 | 0 |
|
47 | 0 | // Loop over the string: |
48 | 0 | while (beginIter != endIter) { |
49 | 0 | if (uch == nl) { |
50 | 0 | aOutString.Append(gt); |
51 | 0 | // No space between >: this is ">>> " style quoting, for |
52 | 0 | // compatibility with RFC 2646 and format=flowed. |
53 | 0 | if (*beginIter != gt) { |
54 | 0 | aOutString.Append(space); |
55 | 0 | } |
56 | 0 | } |
57 | 0 |
|
58 | 0 | uch = *beginIter; |
59 | 0 | ++beginIter; |
60 | 0 |
|
61 | 0 | aOutString += uch; |
62 | 0 | } |
63 | 0 |
|
64 | 0 | if (uch != nl) { |
65 | 0 | aOutString += nl; |
66 | 0 | } |
67 | 0 | return NS_OK; |
68 | 0 | } |
69 | | |
70 | | static void AddCite(nsAString& aOutString, int32_t citeLevel) |
71 | 0 | { |
72 | 0 | for (int32_t i = 0; i < citeLevel; ++i) { |
73 | 0 | aOutString.Append(gt); |
74 | 0 | } |
75 | 0 | if (citeLevel > 0) { |
76 | 0 | aOutString.Append(space); |
77 | 0 | } |
78 | 0 | } |
79 | | |
80 | | static inline void |
81 | | BreakLine(nsAString& aOutString, uint32_t& outStringCol, |
82 | | uint32_t citeLevel) |
83 | 0 | { |
84 | 0 | aOutString.Append(nl); |
85 | 0 | if (citeLevel > 0) { |
86 | 0 | AddCite(aOutString, citeLevel); |
87 | 0 | outStringCol = citeLevel + 1; |
88 | 0 | } else { |
89 | 0 | outStringCol = 0; |
90 | 0 | } |
91 | 0 | } |
92 | | |
93 | | static inline bool IsSpace(char16_t c) |
94 | 0 | { |
95 | 0 | const char16_t nbsp (0xa0); |
96 | 0 | return (nsCRT::IsAsciiSpace(c) || (c == nl) || (c == cr) || (c == nbsp)); |
97 | 0 | } |
98 | | |
99 | | nsresult |
100 | | InternetCiter::Rewrap(const nsAString& aInString, |
101 | | uint32_t aWrapCol, |
102 | | uint32_t aFirstLineOffset, |
103 | | bool aRespectNewlines, |
104 | | nsAString& aOutString) |
105 | 0 | { |
106 | 0 | // There shouldn't be returns in this string, only dom newlines. |
107 | 0 | // Check to make sure: |
108 | | #ifdef DEBUG |
109 | | int32_t cr = aInString.FindChar(char16_t('\r')); |
110 | | NS_ASSERTION((cr < 0), "Rewrap: CR in string gotten from DOM!\n"); |
111 | | #endif /* DEBUG */ |
112 | |
|
113 | 0 | aOutString.Truncate(); |
114 | 0 |
|
115 | 0 | nsresult rv; |
116 | 0 |
|
117 | 0 | RefPtr<mozilla::intl::LineBreaker> lineBreaker = |
118 | 0 | mozilla::intl::LineBreaker::Create(); |
119 | 0 | MOZ_ASSERT(lineBreaker); |
120 | 0 |
|
121 | 0 | // Loop over lines in the input string, rewrapping each one. |
122 | 0 | uint32_t length; |
123 | 0 | uint32_t posInString = 0; |
124 | 0 | uint32_t outStringCol = 0; |
125 | 0 | uint32_t citeLevel = 0; |
126 | 0 | const nsPromiseFlatString &tString = PromiseFlatString(aInString); |
127 | 0 | length = tString.Length(); |
128 | 0 | while (posInString < length) { |
129 | 0 | // Get the new cite level here since we're at the beginning of a line |
130 | 0 | uint32_t newCiteLevel = 0; |
131 | 0 | while (posInString < length && tString[posInString] == gt) { |
132 | 0 | ++newCiteLevel; |
133 | 0 | ++posInString; |
134 | 0 | while (posInString < length && tString[posInString] == space) { |
135 | 0 | ++posInString; |
136 | 0 | } |
137 | 0 | } |
138 | 0 | if (posInString >= length) { |
139 | 0 | break; |
140 | 0 | } |
141 | 0 | |
142 | 0 | // Special case: if this is a blank line, maintain a blank line |
143 | 0 | // (retain the original paragraph breaks) |
144 | 0 | if (tString[posInString] == nl && !aOutString.IsEmpty()) { |
145 | 0 | if (aOutString.Last() != nl) { |
146 | 0 | aOutString.Append(nl); |
147 | 0 | } |
148 | 0 | AddCite(aOutString, newCiteLevel); |
149 | 0 | aOutString.Append(nl); |
150 | 0 |
|
151 | 0 | ++posInString; |
152 | 0 | outStringCol = 0; |
153 | 0 | continue; |
154 | 0 | } |
155 | 0 |
|
156 | 0 | // If the cite level has changed, then start a new line with the |
157 | 0 | // new cite level (but if we're at the beginning of the string, |
158 | 0 | // don't bother). |
159 | 0 | if (newCiteLevel != citeLevel && posInString > newCiteLevel+1 && |
160 | 0 | outStringCol) { |
161 | 0 | BreakLine(aOutString, outStringCol, 0); |
162 | 0 | } |
163 | 0 | citeLevel = newCiteLevel; |
164 | 0 |
|
165 | 0 | // Prepend the quote level to the out string if appropriate |
166 | 0 | if (!outStringCol) { |
167 | 0 | AddCite(aOutString, citeLevel); |
168 | 0 | outStringCol = citeLevel + (citeLevel ? 1 : 0); |
169 | 0 | } |
170 | 0 | // If it's not a cite, and we're not at the beginning of a line in |
171 | 0 | // the output string, add a space to separate new text from the |
172 | 0 | // previous text. |
173 | 0 | else if (outStringCol > citeLevel) { |
174 | 0 | aOutString.Append(space); |
175 | 0 | ++outStringCol; |
176 | 0 | } |
177 | 0 |
|
178 | 0 | // find the next newline -- don't want to go farther than that |
179 | 0 | int32_t nextNewline = tString.FindChar(nl, posInString); |
180 | 0 | if (nextNewline < 0) { |
181 | 0 | nextNewline = length; |
182 | 0 | } |
183 | 0 |
|
184 | 0 | // For now, don't wrap unquoted lines at all. |
185 | 0 | // This is because the plaintext edit window has already wrapped them |
186 | 0 | // by the time we get them for rewrap, yet when we call the line |
187 | 0 | // breaker, it will refuse to break backwards, and we'll end up |
188 | 0 | // with a line that's too long and gets displayed as a lone word |
189 | 0 | // on a line by itself. Need special logic to detect this case |
190 | 0 | // and break it ourselves without resorting to the line breaker. |
191 | 0 | if (!citeLevel) { |
192 | 0 | aOutString.Append(Substring(tString, posInString, |
193 | 0 | nextNewline-posInString)); |
194 | 0 | outStringCol += nextNewline - posInString; |
195 | 0 | if (nextNewline != (int32_t)length) { |
196 | 0 | aOutString.Append(nl); |
197 | 0 | outStringCol = 0; |
198 | 0 | } |
199 | 0 | posInString = nextNewline+1; |
200 | 0 | continue; |
201 | 0 | } |
202 | 0 |
|
203 | 0 | // Otherwise we have to use the line breaker and loop |
204 | 0 | // over this line of the input string to get all of it: |
205 | 0 | while ((int32_t)posInString < nextNewline) { |
206 | 0 | // Skip over initial spaces: |
207 | 0 | while ((int32_t)posInString < nextNewline && |
208 | 0 | nsCRT::IsAsciiSpace(tString[posInString])) { |
209 | 0 | ++posInString; |
210 | 0 | } |
211 | 0 |
|
212 | 0 | // If this is a short line, just append it and continue: |
213 | 0 | if (outStringCol + nextNewline - posInString <= aWrapCol-citeLevel-1) { |
214 | 0 | // If this short line is the final one in the in string, |
215 | 0 | // then we need to include the final newline, if any: |
216 | 0 | if (nextNewline+1 == (int32_t)length && tString[nextNewline-1] == nl) { |
217 | 0 | ++nextNewline; |
218 | 0 | } |
219 | 0 | // Trim trailing spaces: |
220 | 0 | int32_t lastRealChar = nextNewline; |
221 | 0 | while ((uint32_t)lastRealChar > posInString && |
222 | 0 | nsCRT::IsAsciiSpace(tString[lastRealChar-1])) { |
223 | 0 | --lastRealChar; |
224 | 0 | } |
225 | 0 |
|
226 | 0 | aOutString += Substring(tString, |
227 | 0 | posInString, lastRealChar - posInString); |
228 | 0 | outStringCol += lastRealChar - posInString; |
229 | 0 | posInString = nextNewline + 1; |
230 | 0 | continue; |
231 | 0 | } |
232 | 0 |
|
233 | 0 | int32_t eol = posInString + aWrapCol - citeLevel - outStringCol; |
234 | 0 | // eol is the prospective end of line. |
235 | 0 | // We'll first look backwards from there for a place to break. |
236 | 0 | // If it's already less than our current position, |
237 | 0 | // then our line is already too long, so break now. |
238 | 0 | if (eol <= (int32_t)posInString) { |
239 | 0 | BreakLine(aOutString, outStringCol, citeLevel); |
240 | 0 | continue; // continue inner loop, with outStringCol now at bol |
241 | 0 | } |
242 | 0 | |
243 | 0 | int32_t breakPt = 0; |
244 | 0 | // XXX Why this uses NS_ERROR_"BASE"? |
245 | 0 | rv = NS_ERROR_BASE; |
246 | 0 | if (lineBreaker) { |
247 | 0 | breakPt = lineBreaker->Prev(tString.get() + posInString, |
248 | 0 | length - posInString, eol + 1 - posInString); |
249 | 0 | if (breakPt == NS_LINEBREAKER_NEED_MORE_TEXT) { |
250 | 0 | // if we couldn't find a breakpoint looking backwards, |
251 | 0 | // and we're not starting a new line, then end this line |
252 | 0 | // and loop around again: |
253 | 0 | if (outStringCol > citeLevel + 1) { |
254 | 0 | BreakLine(aOutString, outStringCol, citeLevel); |
255 | 0 | continue; // continue inner loop, with outStringCol now at bol |
256 | 0 | } |
257 | 0 | |
258 | 0 | // Else try looking forwards: |
259 | 0 | breakPt = lineBreaker->Next(tString.get() + posInString, |
260 | 0 | length - posInString, eol - posInString); |
261 | 0 |
|
262 | 0 | rv = breakPt == NS_LINEBREAKER_NEED_MORE_TEXT ? NS_ERROR_BASE : |
263 | 0 | NS_OK; |
264 | 0 | } else { |
265 | 0 | rv = NS_OK; |
266 | 0 | } |
267 | 0 | } |
268 | 0 | // If rv is okay, then breakPt is the place to break. |
269 | 0 | // If we get out here and rv is set, something went wrong with line |
270 | 0 | // breaker. Just break the line, hard. |
271 | 0 | if (NS_FAILED(rv)) { |
272 | 0 | breakPt = eol; |
273 | 0 | } |
274 | 0 |
|
275 | 0 | // Special case: maybe we should have wrapped last time. |
276 | 0 | // If the first breakpoint here makes the current line too long, |
277 | 0 | // then if we already have text on the current line, |
278 | 0 | // break and loop around again. |
279 | 0 | // If we're at the beginning of the current line, though, |
280 | 0 | // don't force a break since the long word might be a url |
281 | 0 | // and breaking it would make it unclickable on the other end. |
282 | 0 | const int SLOP = 6; |
283 | 0 | if (outStringCol + breakPt > aWrapCol + SLOP && |
284 | 0 | outStringCol > citeLevel+1) { |
285 | 0 | BreakLine(aOutString, outStringCol, citeLevel); |
286 | 0 | continue; |
287 | 0 | } |
288 | 0 | |
289 | 0 | nsAutoString sub (Substring(tString, posInString, breakPt)); |
290 | 0 | // skip newlines or whitespace at the end of the string |
291 | 0 | int32_t subend = sub.Length(); |
292 | 0 | while (subend > 0 && IsSpace(sub[subend-1])) { |
293 | 0 | --subend; |
294 | 0 | } |
295 | 0 | sub.Left(sub, subend); |
296 | 0 | aOutString += sub; |
297 | 0 | outStringCol += sub.Length(); |
298 | 0 | // Advance past the whitespace which caused the wrap: |
299 | 0 | posInString += breakPt; |
300 | 0 | while (posInString < length && IsSpace(tString[posInString])) { |
301 | 0 | ++posInString; |
302 | 0 | } |
303 | 0 |
|
304 | 0 | // Add a newline and the quote level to the out string |
305 | 0 | if (posInString < length) { // not for the last line, though |
306 | 0 | BreakLine(aOutString, outStringCol, citeLevel); |
307 | 0 | } |
308 | 0 | } // end inner loop within one line of aInString |
309 | 0 | } // end outer loop over lines of aInString |
310 | 0 |
|
311 | 0 | return NS_OK; |
312 | 0 | } |
313 | | |
314 | | } // namespace mozilla |