editor/libeditor/text/nsInternetCiter.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/editor/libeditor/text/nsInternetCiter.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,397 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +
    1.10 +#include "nsAString.h"
    1.11 +#include "nsCOMPtr.h"
    1.12 +#include "nsCRT.h"
    1.13 +#include "nsDebug.h"
    1.14 +#include "nsDependentSubstring.h"
    1.15 +#include "nsError.h"
    1.16 +#include "nsILineBreaker.h"
    1.17 +#include "nsInternetCiter.h"
    1.18 +#include "nsLWBrkCIID.h"
    1.19 +#include "nsServiceManagerUtils.h"
    1.20 +#include "nsString.h"
    1.21 +#include "nsStringIterator.h"
    1.22 +
    1.23 +const char16_t gt ('>');
    1.24 +const char16_t space (' ');
    1.25 +const char16_t nbsp (0xa0);
    1.26 +const char16_t nl ('\n');
    1.27 +const char16_t cr('\r');
    1.28 +
    1.29 +/** Mail citations using the Internet style: > This is a citation
    1.30 +  */
    1.31 +
    1.32 +nsresult
    1.33 +nsInternetCiter::GetCiteString(const nsAString& aInString, nsAString& aOutString)
    1.34 +{
    1.35 +  aOutString.Truncate();
    1.36 +  char16_t uch = nl;
    1.37 +
    1.38 +  // Strip trailing new lines which will otherwise turn up
    1.39 +  // as ugly quoted empty lines.
    1.40 +  nsReadingIterator <char16_t> beginIter,endIter;
    1.41 +  aInString.BeginReading(beginIter);
    1.42 +  aInString.EndReading(endIter);
    1.43 +  while(beginIter!= endIter &&
    1.44 +        (*endIter == cr ||
    1.45 +         *endIter == nl))
    1.46 +  {
    1.47 +    --endIter;
    1.48 +  }
    1.49 +
    1.50 +  // Loop over the string:
    1.51 +  while (beginIter != endIter)
    1.52 +  {
    1.53 +    if (uch == nl)
    1.54 +    {
    1.55 +      aOutString.Append(gt);
    1.56 +      // No space between >: this is ">>> " style quoting, for
    1.57 +      // compatibility with RFC 2646 and format=flowed.
    1.58 +      if (*beginIter != gt)
    1.59 +        aOutString.Append(space);
    1.60 +    }
    1.61 +
    1.62 +    uch = *beginIter;
    1.63 +    ++beginIter;
    1.64 +
    1.65 +    aOutString += uch;
    1.66 +  }
    1.67 +
    1.68 +  if (uch != nl)
    1.69 +    aOutString += nl;
    1.70 +
    1.71 +  return NS_OK;
    1.72 +}
    1.73 +
    1.74 +nsresult
    1.75 +nsInternetCiter::StripCitesAndLinebreaks(const nsAString& aInString,
    1.76 +                                         nsAString& aOutString,
    1.77 +                                         bool aLinebreaksToo,
    1.78 +                                         int32_t* aCiteLevel)
    1.79 +{
    1.80 +  if (aCiteLevel)
    1.81 +    *aCiteLevel = 0;
    1.82 +
    1.83 +  aOutString.Truncate();
    1.84 +  nsReadingIterator <char16_t> beginIter,endIter;
    1.85 +  aInString.BeginReading(beginIter);
    1.86 +  aInString.EndReading(endIter);
    1.87 +  while (beginIter!= endIter)  // loop over lines
    1.88 +  {
    1.89 +    // Clear out cites first, at the beginning of the line:
    1.90 +    int32_t thisLineCiteLevel = 0;
    1.91 +    while (beginIter!= endIter && (*beginIter == gt || nsCRT::IsAsciiSpace(*beginIter)))
    1.92 +    {
    1.93 +      if (*beginIter == gt) ++thisLineCiteLevel;
    1.94 +      ++beginIter;
    1.95 +    }
    1.96 +
    1.97 +    // Now copy characters until line end:
    1.98 +    while (beginIter != endIter && (*beginIter != '\r' && *beginIter != '\n'))
    1.99 +    {
   1.100 +      aOutString.Append(*beginIter);
   1.101 +      ++beginIter;
   1.102 +    }
   1.103 +    if (aLinebreaksToo)
   1.104 +      aOutString.Append(char16_t(' '));
   1.105 +    else
   1.106 +      aOutString.Append(char16_t('\n'));    // DOM linebreaks, not NS_LINEBREAK
   1.107 +      // Skip over any more consecutive linebreak-like characters:
   1.108 +    while (beginIter != endIter && (*beginIter == '\r' || *beginIter == '\n'))
   1.109 +      ++beginIter;
   1.110 +
   1.111 +    // Done with this line -- update cite level
   1.112 +    if (aCiteLevel && (thisLineCiteLevel > *aCiteLevel))
   1.113 +      *aCiteLevel = thisLineCiteLevel;
   1.114 +  }
   1.115 +  return NS_OK;
   1.116 +}
   1.117 +
   1.118 +nsresult
   1.119 +nsInternetCiter::StripCites(const nsAString& aInString, nsAString& aOutString)
   1.120 +{
   1.121 +  return StripCitesAndLinebreaks(aInString, aOutString, false, 0);
   1.122 +}
   1.123 +
   1.124 +static void AddCite(nsAString& aOutString, int32_t citeLevel)
   1.125 +{
   1.126 +  for (int32_t i = 0; i < citeLevel; ++i)
   1.127 +    aOutString.Append(gt);
   1.128 +  if (citeLevel > 0)
   1.129 +    aOutString.Append(space);
   1.130 +}
   1.131 +
   1.132 +static inline void
   1.133 +BreakLine(nsAString& aOutString, uint32_t& outStringCol,
   1.134 +          uint32_t citeLevel)
   1.135 +{
   1.136 +  aOutString.Append(nl);
   1.137 +  if (citeLevel > 0)
   1.138 +  {
   1.139 +    AddCite(aOutString, citeLevel);
   1.140 +    outStringCol = citeLevel + 1;
   1.141 +  }
   1.142 +  else
   1.143 +    outStringCol = 0;
   1.144 +}
   1.145 +
   1.146 +static inline bool IsSpace(char16_t c)
   1.147 +{
   1.148 +  return (nsCRT::IsAsciiSpace(c) || (c == nl) || (c == cr) || (c == nbsp));
   1.149 +}
   1.150 +
   1.151 +nsresult
   1.152 +nsInternetCiter::Rewrap(const nsAString& aInString,
   1.153 +                        uint32_t aWrapCol, uint32_t aFirstLineOffset,
   1.154 +                        bool aRespectNewlines,
   1.155 +                        nsAString& aOutString)
   1.156 +{
   1.157 +  // There shouldn't be returns in this string, only dom newlines.
   1.158 +  // Check to make sure:
   1.159 +#ifdef DEBUG
   1.160 +  int32_t cr = aInString.FindChar(char16_t('\r'));
   1.161 +  NS_ASSERTION((cr < 0), "Rewrap: CR in string gotten from DOM!\n");
   1.162 +#endif /* DEBUG */
   1.163 +
   1.164 +  aOutString.Truncate();
   1.165 +
   1.166 +  nsresult rv;
   1.167 +  nsCOMPtr<nsILineBreaker> lineBreaker = do_GetService(NS_LBRK_CONTRACTID, &rv);
   1.168 +  NS_ENSURE_SUCCESS(rv, rv);
   1.169 +
   1.170 +  // Loop over lines in the input string, rewrapping each one.
   1.171 +  uint32_t length;
   1.172 +  uint32_t posInString = 0;
   1.173 +  uint32_t outStringCol = 0;
   1.174 +  uint32_t citeLevel = 0;
   1.175 +  const nsPromiseFlatString &tString = PromiseFlatString(aInString);
   1.176 +  length = tString.Length();
   1.177 +#ifdef DEBUG_wrapping
   1.178 +  int loopcount = 0;
   1.179 +#endif
   1.180 +  while (posInString < length)
   1.181 +  {
   1.182 +#ifdef DEBUG_wrapping
   1.183 +    printf("Outer loop: '%s'\n",
   1.184 +           NS_LossyConvertUTF16toASCII(Substring(tString, posInString,
   1.185 +                                                length-posInString)).get());
   1.186 +    printf("out string is now: '%s'\n",
   1.187 +           NS_LossyConvertUTF16toASCII(aOutString).get());
   1.188 +
   1.189 +#endif
   1.190 +
   1.191 +    // Get the new cite level here since we're at the beginning of a line
   1.192 +    uint32_t newCiteLevel = 0;
   1.193 +    while (posInString < length && tString[posInString] == gt)
   1.194 +    {
   1.195 +      ++newCiteLevel;
   1.196 +      ++posInString;
   1.197 +      while (posInString < length && tString[posInString] == space)
   1.198 +        ++posInString;
   1.199 +    }
   1.200 +    if (posInString >= length)
   1.201 +      break;
   1.202 +
   1.203 +    // Special case: if this is a blank line, maintain a blank line
   1.204 +    // (retain the original paragraph breaks)
   1.205 +    if (tString[posInString] == nl && !aOutString.IsEmpty())
   1.206 +    {
   1.207 +      if (aOutString.Last() != nl)
   1.208 +        aOutString.Append(nl);
   1.209 +      AddCite(aOutString, newCiteLevel);
   1.210 +      aOutString.Append(nl);
   1.211 +
   1.212 +      ++posInString;
   1.213 +      outStringCol = 0;
   1.214 +      continue;
   1.215 +    }
   1.216 +
   1.217 +    // If the cite level has changed, then start a new line with the
   1.218 +    // new cite level (but if we're at the beginning of the string,
   1.219 +    // don't bother).
   1.220 +    if (newCiteLevel != citeLevel && posInString > newCiteLevel+1
   1.221 +        && outStringCol != 0)
   1.222 +    {
   1.223 +      BreakLine(aOutString, outStringCol, 0);
   1.224 +    }
   1.225 +    citeLevel = newCiteLevel;
   1.226 +
   1.227 +    // Prepend the quote level to the out string if appropriate
   1.228 +    if (outStringCol == 0)
   1.229 +    {
   1.230 +      AddCite(aOutString, citeLevel);
   1.231 +      outStringCol = citeLevel + (citeLevel ? 1 : 0);
   1.232 +    }
   1.233 +    // If it's not a cite, and we're not at the beginning of a line in
   1.234 +    // the output string, add a space to separate new text from the
   1.235 +    // previous text.
   1.236 +    else if (outStringCol > citeLevel)
   1.237 +    {
   1.238 +      aOutString.Append(space);
   1.239 +      ++outStringCol;
   1.240 +    }
   1.241 +
   1.242 +    // find the next newline -- don't want to go farther than that
   1.243 +    int32_t nextNewline = tString.FindChar(nl, posInString);
   1.244 +    if (nextNewline < 0) nextNewline = length;
   1.245 +
   1.246 +    // For now, don't wrap unquoted lines at all.
   1.247 +    // This is because the plaintext edit window has already wrapped them
   1.248 +    // by the time we get them for rewrap, yet when we call the line
   1.249 +    // breaker, it will refuse to break backwards, and we'll end up
   1.250 +    // with a line that's too long and gets displayed as a lone word
   1.251 +    // on a line by itself.  Need special logic to detect this case
   1.252 +    // and break it ourselves without resorting to the line breaker.
   1.253 +    if (citeLevel == 0)
   1.254 +    {
   1.255 +      aOutString.Append(Substring(tString, posInString,
   1.256 +                                  nextNewline-posInString));
   1.257 +      outStringCol += nextNewline - posInString;
   1.258 +      if (nextNewline != (int32_t)length)
   1.259 +      {
   1.260 +        aOutString.Append(nl);
   1.261 +        outStringCol = 0;
   1.262 +      }
   1.263 +      posInString = nextNewline+1;
   1.264 +      continue;
   1.265 +    }
   1.266 +
   1.267 +    // Otherwise we have to use the line breaker and loop
   1.268 +    // over this line of the input string to get all of it:
   1.269 +    while ((int32_t)posInString < nextNewline)
   1.270 +    {
   1.271 +#ifdef DEBUG_wrapping
   1.272 +      if (++loopcount > 1000)
   1.273 +        NS_ASSERTION(false, "possible infinite loop in nsInternetCiter\n");
   1.274 +
   1.275 +      printf("Inner loop: '%s'\n",
   1.276 +             NS_LossyConvertUTF16toASCII(Substring(tString, posInString,
   1.277 +                                              nextNewline-posInString)).get());
   1.278 +#endif
   1.279 +
   1.280 +      // Skip over initial spaces:
   1.281 +      while ((int32_t)posInString < nextNewline
   1.282 +             && nsCRT::IsAsciiSpace(tString[posInString]))
   1.283 +        ++posInString;
   1.284 +
   1.285 +      // If this is a short line, just append it and continue:
   1.286 +      if (outStringCol + nextNewline - posInString <= aWrapCol-citeLevel-1)
   1.287 +      {
   1.288 +        // If this short line is the final one in the in string,
   1.289 +        // then we need to include the final newline, if any:
   1.290 +        if (nextNewline+1 == (int32_t)length && tString[nextNewline-1] == nl)
   1.291 +          ++nextNewline;
   1.292 +
   1.293 +        // Trim trailing spaces:
   1.294 +        int32_t lastRealChar = nextNewline;
   1.295 +        while ((uint32_t)lastRealChar > posInString
   1.296 +               && nsCRT::IsAsciiSpace(tString[lastRealChar-1]))
   1.297 +          --lastRealChar;
   1.298 +
   1.299 +        aOutString += Substring(tString,
   1.300 +                                posInString, lastRealChar - posInString);
   1.301 +        outStringCol += lastRealChar - posInString;
   1.302 +        posInString = nextNewline + 1;
   1.303 +        continue;
   1.304 +      }
   1.305 +
   1.306 +      int32_t eol = posInString + aWrapCol - citeLevel - outStringCol;
   1.307 +      // eol is the prospective end of line.
   1.308 +      // We'll first look backwards from there for a place to break.
   1.309 +      // If it's already less than our current position,
   1.310 +      // then our line is already too long, so break now.
   1.311 +      if (eol <= (int32_t)posInString)
   1.312 +      {
   1.313 +        BreakLine(aOutString, outStringCol, citeLevel);
   1.314 +        continue;    // continue inner loop, with outStringCol now at bol
   1.315 +      }
   1.316 +
   1.317 +      int32_t breakPt = 0;
   1.318 +      rv = NS_ERROR_BASE;
   1.319 +      if (lineBreaker)
   1.320 +      {
   1.321 +        breakPt = lineBreaker->Prev(tString.get() + posInString,
   1.322 +                                 length - posInString, eol + 1 - posInString);
   1.323 +        if (breakPt == NS_LINEBREAKER_NEED_MORE_TEXT)
   1.324 +        {
   1.325 +          // if we couldn't find a breakpoint looking backwards,
   1.326 +          // and we're not starting a new line, then end this line
   1.327 +          // and loop around again:
   1.328 +          if (outStringCol > citeLevel + 1)
   1.329 +          {
   1.330 +            BreakLine(aOutString, outStringCol, citeLevel);
   1.331 +            continue;    // continue inner loop, with outStringCol now at bol
   1.332 +          }
   1.333 +
   1.334 +          // Else try looking forwards:
   1.335 +          breakPt = lineBreaker->Next(tString.get() + posInString,
   1.336 +                                      length - posInString, eol - posInString);
   1.337 +          if (breakPt == NS_LINEBREAKER_NEED_MORE_TEXT) rv = NS_ERROR_BASE;
   1.338 +          else rv = NS_OK;
   1.339 +        }
   1.340 +        else rv = NS_OK;
   1.341 +      }
   1.342 +      // If rv is okay, then breakPt is the place to break.
   1.343 +      // If we get out here and rv is set, something went wrong with line
   1.344 +      // breaker.  Just break the line, hard.
   1.345 +      if (NS_FAILED(rv))
   1.346 +      {
   1.347 +#ifdef DEBUG_akkana
   1.348 +        printf("nsInternetCiter: LineBreaker not working -- breaking hard\n");
   1.349 +#endif
   1.350 +        breakPt = eol;
   1.351 +      }
   1.352 +
   1.353 +      // Special case: maybe we should have wrapped last time.
   1.354 +      // If the first breakpoint here makes the current line too long,
   1.355 +      // then if we already have text on the current line,
   1.356 +      // break and loop around again.
   1.357 +      // If we're at the beginning of the current line, though,
   1.358 +      // don't force a break since the long word might be a url
   1.359 +      // and breaking it would make it unclickable on the other end.
   1.360 +      const int SLOP = 6;
   1.361 +      if (outStringCol + breakPt > aWrapCol + SLOP
   1.362 +          && outStringCol > citeLevel+1)
   1.363 +      {
   1.364 +        BreakLine(aOutString, outStringCol, citeLevel);
   1.365 +        continue;
   1.366 +      }
   1.367 +
   1.368 +      nsAutoString sub (Substring(tString, posInString, breakPt));
   1.369 +      // skip newlines or whitespace at the end of the string
   1.370 +      int32_t subend = sub.Length();
   1.371 +      while (subend > 0 && IsSpace(sub[subend-1]))
   1.372 +        --subend;
   1.373 +      sub.Left(sub, subend);
   1.374 +      aOutString += sub;
   1.375 +      outStringCol += sub.Length();
   1.376 +      // Advance past the whitespace which caused the wrap:
   1.377 +      posInString += breakPt;
   1.378 +      while (posInString < length && IsSpace(tString[posInString]))
   1.379 +        ++posInString;
   1.380 +
   1.381 +      // Add a newline and the quote level to the out string
   1.382 +      if (posInString < length)    // not for the last line, though
   1.383 +        BreakLine(aOutString, outStringCol, citeLevel);
   1.384 +
   1.385 +    } // end inner loop within one line of aInString
   1.386 +#ifdef DEBUG_wrapping
   1.387 +    printf("---------\nEnd inner loop: out string is now '%s'\n-----------\n",
   1.388 +           NS_LossyConvertUTF16toASCII(aOutString).get());
   1.389 +#endif
   1.390 +  } // end outer loop over lines of aInString
   1.391 +
   1.392 +#ifdef DEBUG_wrapping
   1.393 +  printf("Final out string is now: '%s'\n",
   1.394 +         NS_LossyConvertUTF16toASCII(aOutString).get());
   1.395 +
   1.396 +#endif
   1.397 +  return NS_OK;
   1.398 +}
   1.399 +
   1.400 +

mercurial