Fix for bug 103539: Find does not always find strings containing spaces.

and bug 98730: Find/Replace not finding a string with more than one space

  - Reworked the whitespace ignoring code in FindInString() so that it actually
    ignores trailing whitespace and made that code conditional so that it can be
    toggled on and off in the future when the context of the text we are looking
    at is available.

  - We now treat   as a space.

r=jfrancis@netscape.com  sr=sfraser@netscape.com
This commit is contained in:
kin%netscape.com 2001-10-31 15:19:47 +00:00
Родитель 9c301478f8
Коммит 9ddf7a0214
1 изменённых файлов: 85 добавлений и 91 удалений

Просмотреть файл

@ -323,10 +323,13 @@ nsFindAndReplace::Replace(const PRUnichar *aFindText, const PRUnichar *aReplaceT
// non whitespace and same char. // non whitespace and same char.
// ---------------------------------------------------------------- // ----------------------------------------------------------------
#define NBSP_CHARCODE ((PRUnichar)160)
#define IS_SPACE_CHAR(c) (nsCRT::IsAsciiSpace(c) || (c) == NBSP_CHARCODE)
inline static PRBool inline static PRBool
CharsMatch(PRUnichar c1, PRUnichar c2) CharsMatch(PRUnichar c1, PRUnichar c2)
{ {
return (nsCRT::IsAsciiSpace(c1) && nsCRT::IsAsciiSpace(c2)) || (c1 == c2); return (c1 == c2) || (IS_SPACE_CHAR(c1) && IS_SPACE_CHAR(c2));
} }
// ---------------------------------------------------------------- // ----------------------------------------------------------------
@ -338,136 +341,126 @@ CharsMatch(PRUnichar c1, PRUnichar c2)
// This uses a brute-force algorithm, which should be sufficient // This uses a brute-force algorithm, which should be sufficient
// for our purposes (text searching) // for our purposes (text searching)
// //
// searchStr contains the text from a content node, which can contain // aSearchStr contains the text from a content node, which can contain
// extra white space between words, which we have to deal with. // extra white space between words, which we have to deal with.
// The offsets passed in and back are offsets into searchStr, // The offsets passed in and back are offsets into aSearchStr,
// and thus include extra white space. // and thus include extra white space.
// //
// If we are ignoring case, the strings have already been lowercased // If we are ignoring case, the strings have already been lowercased
// at this point. // at this point.
// //
// startOffset is the offset in the search string to start searching // aStartOffset is the offset in the search string to start searching
// at. If -1, it means search from the start (forwards) or end (backwards). // at. If -1, it means search from the start (forwards) or end (backwards).
// //
// aSkipTrailingSpaces adjusts the matching algorithm when dealing with
// adjacent white space. If true the algorithm matches the exact number
// of spaces in each consecutive run of spaces in the aPatternStr, and if
// the number of consecutive spaces for a given run in aSearchStr is longer,
// the trailing extra spaces are ignored/skipped, but included in the length
// of the string matched. If false, the matching algorithm will only match
// the number of spaces in each run contained in aPatternStr.
//
// aFoundStrLen will contain the length of the string matching aPatternStr.
// It should be noted that aFoundStrLen can be greater than the length of
// aPatternStr since the matched string could contain extra whitespace.
//
// Returns -1 if the string is not found, or if the pattern is an // Returns -1 if the string is not found, or if the pattern is an
// empty string, or if startOffset is off the end of the string. // empty string, or if aStartOffset is off the end of the string.
// ---------------------------------------------------------------- // ----------------------------------------------------------------
static PRInt32 FindInString(const nsString &searchStr, const nsString &patternStr, static PRInt32 FindInString(const nsString &aSearchStr, const nsString &aPatternStr,
PRInt32 startOffset, PRBool searchBackwards) PRInt32 aStartOffset, PRBool aSearchBackwards,
PRBool aSkipTrailingSpaces, PRInt32 *aFoundStrLen)
{ {
PRInt32 foundOffset = -1; PRInt32 patternLen = aPatternStr.Length();
PRInt32 patternLen = patternStr.Length(); PRInt32 searchStrLen = aSearchStr.Length();
PRInt32 searchStrLen = searchStr.Length();
if (aFoundStrLen)
*aFoundStrLen = 0;
if (patternLen == 0) // pattern is empty if (patternLen == 0) // pattern is empty
return -1; return -1;
if (startOffset < 0) if (aStartOffset < 0)
startOffset = (searchBackwards) ? searchStrLen : 0; aStartOffset = (aSearchBackwards) ? searchStrLen : 0;
if (startOffset > searchStrLen) // bad start offset if (aStartOffset > searchStrLen) // bad start offset
return -1; return -1;
if (patternLen > searchStrLen) // pattern is longer than string to search if (patternLen > searchStrLen) // pattern is longer than string to search
return -1; return -1;
const PRUnichar *searchBuf = searchStr.get(); const PRUnichar *searchBuf = aSearchStr.get();
const PRUnichar *patternBuf = patternStr.get(); const PRUnichar *patternBuf = aPatternStr.get();
const PRUnichar *searchEnd = searchBuf + searchStrLen; const PRUnichar *searchEnd = searchBuf + searchStrLen;
const PRUnichar *patEnd = patternBuf + patternLen; const PRUnichar *patEnd = patternBuf + patternLen;
if (searchBackwards) const PRUnichar *s = searchBuf + aStartOffset;
{
// searching backwards
const PRUnichar *s = searchBuf + startOffset - patternLen;
while (s >= searchBuf) if (aSearchBackwards)
s -= patternLen;
while ((!aSearchBackwards && s < searchEnd) ||
( aSearchBackwards && s >= searchBuf))
{ {
if (CharsMatch(*patternBuf, *s)) // start potential match if (CharsMatch(*patternBuf, *s)) // start potential match
{ {
const PRUnichar *t = s; const PRUnichar *t = s;
const PRUnichar *p = patternBuf; const PRUnichar *p = patternBuf;
PRInt32 curMatchOffset = t - searchBuf; PRInt32 curMatchOffset = t - searchBuf;
PRBool inWhitespace = nsCRT::IsAsciiSpace(*p); PRInt32 curMatchLen = 0;
while (p < patEnd && CharsMatch(*p, *t)) while (p < patEnd && t < searchEnd && CharsMatch(*p, *t))
{ {
if (inWhitespace && !nsCRT::IsAsciiSpace(*p)) PRBool didProcessWhitespace = PR_FALSE;
{
// leaving p whitespace. Eat up addition whitespace in s
while (t < searchEnd - 1 && nsCRT::IsAsciiSpace(*(t + 1)))
t ++;
inWhitespace = PR_FALSE; while (t != searchEnd && p != patEnd && IS_SPACE_CHAR(*p))
{
didProcessWhitespace = PR_TRUE;
if (!IS_SPACE_CHAR(*t))
break;
p++;
t++;
curMatchLen++;
if (aSkipTrailingSpaces && (p == patEnd || !IS_SPACE_CHAR(*p)))
{
// leaving p whitespace. Eat up additional whitespace in t
while (t < searchEnd && IS_SPACE_CHAR(*t))
{
t++;
curMatchLen++;
}
}
} }
else
inWhitespace = nsCRT::IsAsciiSpace(*p);
t ++; if (!didProcessWhitespace)
p ++; {
t++;
p++;
curMatchLen++;
}
} }
if (p == patEnd) if (p == patEnd)
{ {
foundOffset = curMatchOffset; if (aFoundStrLen)
goto done; *aFoundStrLen = curMatchLen;
}
// could be smart about decrementing s here return curMatchOffset;
}
s --;
}
}
else
{
// searching forwards
const PRUnichar *s = &searchBuf[startOffset];
while (s < searchEnd)
{
if (CharsMatch(*patternBuf, *s)) // start potential match
{
const PRUnichar *t = s;
const PRUnichar *p = patternBuf;
PRInt32 curMatchOffset = t - searchBuf;
PRBool inWhitespace = nsCRT::IsAsciiSpace(*p);
while (p < patEnd && CharsMatch(*p, *t))
{
if (inWhitespace && !nsCRT::IsAsciiSpace(*p))
{
// leaving p whitespace. Eat up addition whitespace in s
while (t < searchEnd - 1 && nsCRT::IsAsciiSpace(*t))
t ++;
inWhitespace = PR_FALSE;
}
else
inWhitespace = nsCRT::IsAsciiSpace(*p);
t ++;
p ++;
}
if (p == patEnd)
{
foundOffset = curMatchOffset;
goto done;
}
// could be smart about incrementing s here
}
s ++;
} }
} }
done: if (aSearchBackwards)
return foundOffset; s--;
else // searching forwards
s++;
}
return -1;
} }
// utility method to discover which block we're in. The TSDoc interface doesn't give // utility method to discover which block we're in. The TSDoc interface doesn't give
@ -725,11 +718,12 @@ nsFindAndReplace::DoFind(nsITextServicesDocument *aTxtDoc, const nsString &aFind
} }
} }
PRInt32 foundOffset = FindInString(str, aFindString, mCurrentSelOffset, mFindBackwards); PRInt32 foundLength = 0;
PRInt32 foundOffset = FindInString(str, aFindString, mCurrentSelOffset, mFindBackwards, PR_TRUE, &foundLength);
if (mWrapFind && mWrappedOnce && (mCurrentBlockIndex == mStartBlockIndex)) if (mWrapFind && mWrappedOnce && (mCurrentBlockIndex == mStartBlockIndex))
{ {
if ((mFindBackwards && ((foundOffset + aFindString.Length()) <= mStartSelOffset)) || if ((mFindBackwards && ((foundOffset + (PRInt32)aFindString.Length()) <= mStartSelOffset)) ||
(!mFindBackwards && (foundOffset >= mStartSelOffset))) (!mFindBackwards && (foundOffset >= mStartSelOffset)))
{ {
done = PR_TRUE; done = PR_TRUE;
@ -742,7 +736,7 @@ nsFindAndReplace::DoFind(nsITextServicesDocument *aTxtDoc, const nsString &aFind
mCurrentSelOffset = foundOffset; // reset for next call to DoFind(). mCurrentSelOffset = foundOffset; // reset for next call to DoFind().
// Match found. Select it, remember where it was, and quit. // Match found. Select it, remember where it was, and quit.
aTxtDoc->SetSelection(foundOffset, aFindString.Length()); aTxtDoc->SetSelection(foundOffset, foundLength);
aTxtDoc->ScrollSelectionIntoView(); aTxtDoc->ScrollSelectionIntoView();
done = PR_TRUE; done = PR_TRUE;
*aDidFind = PR_TRUE; *aDidFind = PR_TRUE;