Browse Source

SD2-857. Fixed an edge case where the search would incorrectly find no match when the cursor was located after the very last character in the text.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/branches/2.1@2364 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Matt Ward 19 years ago
parent
commit
4a6209467f
  1. 2
      src/Main/Base/Project/Src/TextEditor/SearchAndReplace/Engine/Search.cs
  2. 18
      src/Main/Base/Project/Src/TextEditor/SearchAndReplace/Engine/TextIterator/ForwardTextIterator.cs
  3. 91
      src/Main/Base/Test/FindNextWithCursorAtEndTestFixture.cs
  4. 117
      src/Main/Base/Test/ForwardTextIteratorPositionIsEndOffsetTestFixture.cs
  5. 4
      src/Main/Base/Test/ICSharpCode.SharpDevelop.Tests.csproj
  6. 60
      src/Main/Base/Test/Utils/MockCurrentDocumentIterator.cs
  7. 254
      src/Main/Base/Test/Utils/MockDocument.cs

2
src/Main/Base/Project/Src/TextEditor/SearchAndReplace/Engine/Search.cs

@ -107,7 +107,7 @@ namespace SearchAndReplace @@ -107,7 +107,7 @@ namespace SearchAndReplace
if (documentIterator.MoveForward()) {
info = documentIterator.Current;
// document is valid for searching -> set iterator & fileName
if (info != null && info.TextBuffer != null && info.EndOffset >= 0 && info.EndOffset < info.TextBuffer.Length) {
if (info != null && info.TextBuffer != null && info.EndOffset >= 0 && info.EndOffset <= info.TextBuffer.Length) {
textIterator = textIteratorBuilder.BuildTextIterator(info);
} else {
textIterator = null;

18
src/Main/Base/Project/Src/TextEditor/SearchAndReplace/Engine/TextIterator/ForwardTextIterator.cs

@ -91,9 +91,27 @@ namespace SearchAndReplace @@ -91,9 +91,27 @@ namespace SearchAndReplace
case TextIteratorState.Done:
return false;
case TextIteratorState.Iterating:
if (oldOffset == -1 && textBuffer.Length == endOffset) {
// HACK: Take off one if the iterator start
// position is at the end of the text.
Position--;
}
if (oldOffset != -1 && Position == endOffset - 1 && textBuffer.Length == endOffset) {
state = TextIteratorState.Done;
return false;
}
Position = (Position + numChars) % textBuffer.Length;
bool finish = oldOffset != -1 && (oldOffset > Position || oldOffset < endOffset) && Position >= endOffset;
// HACK: Iterating is complete if Position == endOffset - 1
// when the iterator start position was initially at the
// end of the text.
if (oldOffset != -1 && oldOffset == endOffset - 1 && textBuffer.Length == endOffset) {
finish = true;
}
oldOffset = Position;
if (finish) {
state = TextIteratorState.Done;

91
src/Main/Base/Test/FindNextWithCursorAtEndTestFixture.cs

@ -0,0 +1,91 @@ @@ -0,0 +1,91 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Matthew Ward" email="mrward@users.sourceforge.net"/>
// <version>$Revision$</version>
// </file>
using System;
using ICSharpCode.Core;
using ICSharpCode.TextEditor.Document;
using ICSharpCode.SharpDevelop.Tests.Utils;
using NUnit.Framework;
using SearchAndReplace;
namespace ICSharpCode.SharpDevelop.Tests
{
/// <summary>
/// SD2-857 - The search fails to find a string if the cursor is
/// positioned at the end of the last line. For example, if the
/// text editor has the text:
/// "foo"
/// without the quotes and no newline after this text, then
/// putting the cursor at the end after the last 'o' and
/// opening the search dialog and doing a find next for 'foo'
/// the string is not found.
/// </summary>
[TestFixture]
public class FindNextWithCursorAtEndTestFixture
{
Search search;
SearchResult result;
[TestFixtureSetUp]
public void SetUpFixture()
{
if (!PropertyService.Initialized) {
PropertyService.InitializeService(String.Empty, String.Empty, String.Empty);
}
}
[SetUp]
public void SetUp()
{
// Set up SearchOptions required by the BruteForceSearchStrategy.
SearchOptions.CurrentFindPattern = "foo";
SearchOptions.MatchCase = false;
SearchOptions.MatchWholeWord = false;
// Create search.
search = new Search();
search.TextIteratorBuilder = new ForwardTextIteratorBuilder();
search.SearchStrategy = new BruteForceSearchStrategy();
MockCurrentDocumentIterator currentDocIterator = new MockCurrentDocumentIterator();
search.DocumentIterator = currentDocIterator;
// Create the document to be searched.
MockDocument doc = new MockDocument();
StringTextBufferStrategy textBufferStrategy = new StringTextBufferStrategy();
textBufferStrategy.SetContent("foo");
doc.TextBufferStrategy = textBufferStrategy;
// Create a doc info with an initial end offset right
// at the end of the text.
ProvidedDocumentInformation docInfo = new ProvidedDocumentInformation(doc, @"C:\Temp\test.txt", doc.TextLength);
currentDocIterator.Current = docInfo;
currentDocIterator.CurrentFileName = docInfo.FileName;
// Search the document.
search.SearchStrategy.CompilePattern(null);
result = search.FindNext();
}
[Test]
public void FoundMatch()
{
Assert.IsNotNull(result);
}
[Test]
public void FoundTextOffset()
{
Assert.AreEqual(0, result.Offset);
}
[Test]
public void FoundTextLength()
{
Assert.AreEqual(3, result.Length);
}
}
}

117
src/Main/Base/Test/ForwardTextIteratorPositionIsEndOffsetTestFixture.cs

@ -0,0 +1,117 @@ @@ -0,0 +1,117 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Matthew Ward" email="mrward@users.sourceforge.net"/>
// <version>$Revision$</version>
// </file>
using System;
using ICSharpCode.SharpDevelop.Tests.Utils;
using ICSharpCode.TextEditor.Document;
using SearchAndReplace;
using NUnit.Framework;
namespace ICSharpCode.SharpDevelop.Tests
{
/// <summary>
/// The forward text iterator never finishes if the initial
/// offset is right at the end of the text.
/// </summary>
[TestFixture]
public class ForwardTextIteratorPositionIsEndOffsetTestFixture
{
ForwardTextIterator forwardTextIterator;
[SetUp]
public void SetUp()
{
// Create the document to be iterated through.
MockDocument doc = new MockDocument();
StringTextBufferStrategy textBufferStrategy = new StringTextBufferStrategy();
textBufferStrategy.SetContent("bar");
doc.TextBufferStrategy = textBufferStrategy;
// Create a doc info with an initial end offset right
// at the end of the text.
ProvidedDocumentInformation docInfo = new ProvidedDocumentInformation(doc,
@"C:\Temp\test.txt",
doc.TextLength);
// Create the forward iterator.
forwardTextIterator = new ForwardTextIterator(docInfo);
}
/// <summary>
/// Note that we cannot move 4 chars in one go with the first
/// call to MoveAhead due to another bug in the ForwardTextIterator.
/// If the iterator has been reset then the first call to MoveAhead
/// always just moves to the DocInfo's EndOffset ignoring any
/// number of chars passed in as an argument to MoveAhead.
/// </summary>
[Test]
public void MoveAheadFourChars()
{
// First move ahead does nothing if the forward text
// iterator was reset. I consider this a bug. All this
// call does is put the iterator at the current position to
// start the iteration. In this case it is after the last
// character in the string, offset 3.
forwardTextIterator.MoveAhead(1);
bool firstMove = forwardTextIterator.MoveAhead(1);
bool secondMove = forwardTextIterator.MoveAhead(1);
bool thirdMove = forwardTextIterator.MoveAhead(1);
bool fourthMove = forwardTextIterator.MoveAhead(1);
Assert.IsTrue(firstMove);
Assert.IsTrue(secondMove);
Assert.IsTrue(thirdMove);
Assert.IsFalse(fourthMove);
}
/// <summary>
/// Check that after moving ahead one character the first
/// char in the string is selected.
/// </summary>
[Test]
public void MoveAheadOneChar()
{
// First move ahead does nothing see comment in MoveAheadThreeChars
// test.
forwardTextIterator.MoveAhead(1);
// Move one char.
forwardTextIterator.MoveAhead(1);
Assert.AreEqual(0, forwardTextIterator.Position);
}
/// <summary>
/// Tests the unusual scenario when a find all is done for a
/// single character when the starting iteration position is after the
/// last character in a string and the last character is a match.
/// </summary>
[Test]
public void ChangePositionAfterThreeMoves()
{
// First move does nothing.
forwardTextIterator.MoveAhead(1);
bool firstMove = forwardTextIterator.MoveAhead(1);
bool secondMove = forwardTextIterator.MoveAhead(1);
bool thirdMove = forwardTextIterator.MoveAhead(1);
// Change position to simulate the search when it finds a
// match at the very last offset. Here the search will
// set the position to be the end offset.
forwardTextIterator.Position = 3;
bool fourthMove = forwardTextIterator.MoveAhead(1);
Assert.IsTrue(firstMove);
Assert.IsTrue(secondMove);
Assert.IsTrue(thirdMove);
Assert.IsFalse(fourthMove);
}
}
}

4
src/Main/Base/Test/ICSharpCode.SharpDevelop.Tests.csproj

@ -52,6 +52,8 @@ @@ -52,6 +52,8 @@
<Compile Include="AssemblyInfo.cs" />
<Compile Include="CSharpExpressionFinderTests.cs" />
<Compile Include="ExceptionClassOverridesTestFixture.cs" />
<Compile Include="FindNextWithCursorAtEndTestFixture.cs" />
<Compile Include="ForwardTextIteratorPositionIsEndOffsetTestFixture.cs" />
<Compile Include="NRefactoryResolverTests.cs" />
<Compile Include="CollectionClassOverridesTestFixture.cs" />
<Compile Include="OverridableMethodsTestFixture.cs" />
@ -66,8 +68,10 @@ @@ -66,8 +68,10 @@
<Compile Include="MemberLookupHelperTests.cs" />
<Compile Include="Utils\MockAmbience.cs" />
<Compile Include="Utils\MockClass.cs" />
<Compile Include="Utils\MockCurrentDocumentIterator.cs" />
<Compile Include="Utils\MockDecoration.cs" />
<Compile Include="Utils\MockDefaultReturnType.cs" />
<Compile Include="Utils\MockDocument.cs" />
<Compile Include="Utils\MockMethod.cs" />
<Compile Include="Utils\MockProject.cs" />
<Compile Include="Utils\MockProperty.cs" />

60
src/Main/Base/Test/Utils/MockCurrentDocumentIterator.cs

@ -0,0 +1,60 @@ @@ -0,0 +1,60 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Matthew Ward" email="mrward@users.sourceforge.net"/>
// <version>$Revision$</version>
// </file>
using System;
using SearchAndReplace;
namespace ICSharpCode.SharpDevelop.Tests.Utils
{
public class MockCurrentDocumentIterator : IDocumentIterator
{
ProvidedDocumentInformation current;
string currentFileName = String.Empty;
bool moved;
public MockCurrentDocumentIterator()
{
}
public ProvidedDocumentInformation Current {
get {
return current;
}
set {
current = value;
}
}
public string CurrentFileName {
get {
return currentFileName;
}
set {
currentFileName = value;
}
}
public bool MoveForward()
{
if (moved) {
return false;
} else {
moved = true;
}
return true;
}
public bool MoveBackward()
{
return false;
}
public void Reset()
{
}
}
}

254
src/Main/Base/Test/Utils/MockDocument.cs

@ -0,0 +1,254 @@ @@ -0,0 +1,254 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="Matthew Ward" email="mrward@users.sourceforge.net"/>
// <version>$Revision$</version>
// </file>
using System;
using ICSharpCode.TextEditor.Document;
using ICSharpCode.TextEditor.Undo;
namespace ICSharpCode.SharpDevelop.Tests.Utils
{
public class MockDocument : IDocument
{
ITextBufferStrategy textBufferStrategy;
public MockDocument()
{
}
public event EventHandler UpdateCommited;
public event DocumentEventHandler DocumentAboutToBeChanged;
public event DocumentEventHandler DocumentChanged;
public event EventHandler TextContentChanged;
public ITextEditorProperties TextEditorProperties {
get {
throw new NotImplementedException();
}
set {
throw new NotImplementedException();
}
}
public UndoStack UndoStack {
get {
throw new NotImplementedException();
}
}
public bool ReadOnly {
get {
throw new NotImplementedException();
}
set {
throw new NotImplementedException();
}
}
public IFormattingStrategy FormattingStrategy {
get {
throw new NotImplementedException();
}
set {
throw new NotImplementedException();
}
}
public ITextBufferStrategy TextBufferStrategy {
get {
return textBufferStrategy;
}
set {
textBufferStrategy = value;
}
}
public FoldingManager FoldingManager {
get {
throw new NotImplementedException();
}
}
public IHighlightingStrategy HighlightingStrategy {
get {
throw new NotImplementedException();
}
set {
throw new NotImplementedException();
}
}
public BookmarkManager BookmarkManager {
get {
throw new NotImplementedException();
}
}
public ICustomLineManager CustomLineManager {
get {
throw new NotImplementedException();
}
}
public MarkerStrategy MarkerStrategy {
get {
throw new NotImplementedException();
}
}
public System.Collections.Generic.List<LineSegment> LineSegmentCollection {
get {
throw new NotImplementedException();
}
}
public int TotalNumberOfLines {
get {
throw new NotImplementedException();
}
}
public string TextContent {
get {
throw new NotImplementedException();
}
set {
throw new NotImplementedException();
}
}
public int TextLength {
get {
return textBufferStrategy.Length;
}
}
public System.Collections.Generic.List<ICSharpCode.TextEditor.TextAreaUpdate> UpdateQueue {
get {
throw new NotImplementedException();
}
}
public int GetLineNumberForOffset(int offset)
{
throw new NotImplementedException();
}
public LineSegment GetLineSegmentForOffset(int offset)
{
throw new NotImplementedException();
}
public LineSegment GetLineSegment(int lineNumber)
{
throw new NotImplementedException();
}
public int GetFirstLogicalLine(int lineNumber)
{
throw new NotImplementedException();
}
public int GetLastLogicalLine(int lineNumber)
{
throw new NotImplementedException();
}
public int GetVisibleLine(int lineNumber)
{
throw new NotImplementedException();
}
public int GetNextVisibleLineAbove(int lineNumber, int lineCount)
{
throw new NotImplementedException();
}
public int GetNextVisibleLineBelow(int lineNumber, int lineCount)
{
throw new NotImplementedException();
}
public void Insert(int offset, string text)
{
throw new NotImplementedException();
}
public void Remove(int offset, int length)
{
throw new NotImplementedException();
}
public void Replace(int offset, int length, string text)
{
throw new NotImplementedException();
}
public char GetCharAt(int offset)
{
throw new NotImplementedException();
}
public string GetText(int offset, int length)
{
throw new NotImplementedException();
}
public string GetText(ISegment segment)
{
throw new NotImplementedException();
}
public System.Drawing.Point OffsetToPosition(int offset)
{
throw new NotImplementedException();
}
public int PositionToOffset(System.Drawing.Point p)
{
throw new NotImplementedException();
}
public void RequestUpdate(ICSharpCode.TextEditor.TextAreaUpdate update)
{
throw new NotImplementedException();
}
public void CommitUpdate()
{
throw new NotImplementedException();
}
public void UpdateSegmentListOnDocumentChange<T>(System.Collections.Generic.List<T> list, DocumentEventArgs e) where T : ISegment
{
throw new NotImplementedException();
}
void OnUpdateCommited()
{
if (UpdateCommited != null) {
}
}
void OnDocumentAboutToBeChanged()
{
if (DocumentAboutToBeChanged != null) {
}
}
void OnDocumentChanged()
{
if (DocumentChanged != null) {
}
}
void OnTextContentChanged()
{
if (TextContentChanged != null) {
}
}
}
}
Loading…
Cancel
Save