using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Drawing;
using System.Text.RegularExpressions;
namespace ScintillaNet
{
[TypeConverterAttribute(typeof(System.ComponentModel.ExpandableObjectConverter))]
public class FindReplace : ScintillaHelperBase
{
internal FindReplace(Scintilla scintilla) : base(scintilla)
{
_marker = scintilla.Markers[10];
_marker.SetSymbolInternal(MarkerSymbol.Arrows);
_indicator = scintilla.Indicators[16];
_indicator.Color = Color.Purple;
_indicator.Style = IndicatorStyle.RoundBox;
_window = new FindReplaceDialog();
_window.Scintilla = scintilla;
_incrementalSearcher = new IncrementalSearcher();
_incrementalSearcher.Scintilla = scintilla;
_incrementalSearcher.Visible = false;
scintilla.Controls.Add(_incrementalSearcher);
}
internal bool ShouldSerialize()
{
return ShouldSerializeFlags() ||
ShouldSerializeIndicator() ||
ShouldSerializeMarker();
}
private string _lastFindString = null;
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public string LastFindString
{
get
{
return _lastFindString;
}
set
{
_lastFindString = value;
_lastFindRegex = null;
}
}
private Regex _lastFindRegex = null;
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Regex LastFindRegex
{
get
{
return _lastFindRegex;
}
set
{
_lastFindRegex = value;
_lastFindString = null;
}
}
private FindReplaceDialog _window;
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public FindReplaceDialog Window
{
get
{
return _window;
}
set
{
_window = value;
}
}
private IncrementalSearcher _incrementalSearcher;
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public IncrementalSearcher IncrementalSearcher
{
get
{
return _incrementalSearcher;
}
set
{
_incrementalSearcher = value;
}
}
#region Flags
private SearchFlags _flags;
[Editor(typeof(ScintillaNet.Design.FlagEnumUIEditor), typeof(System.Drawing.Design.UITypeEditor))]
public SearchFlags Flags
{
get
{
return _flags;
}
set
{
_flags = value;
}
}
private bool ShouldSerializeFlags()
{
return _flags != SearchFlags.Empty;
}
private void ResetFlags()
{
_flags = SearchFlags.Empty;
}
#endregion
#region Marker
private Marker _marker;
public Marker Marker
{
get
{
return _marker;
}
set
{
_marker = value;
}
}
private bool ShouldSerializeMarker()
{
return _marker.Number != 10 || _marker.ForeColor != Color.White || _marker.BackColor != Color.Black || _marker.Symbol != MarkerSymbol.Arrows;
}
private void ResetMarker()
{
_marker.Reset();
_marker.Number = 10;
}
#endregion
#region Indicator
private Indicator _indicator;
public Indicator Indicator
{
get
{
return _indicator;
}
set
{
_indicator = value;
}
}
private bool ShouldSerializeIndicator()
{
return _indicator.Number != 16 || _indicator.Color != Color.Purple || _indicator.IsDrawnUnder;
}
private void ResetIndicator()
{
_indicator.Reset();
}
#endregion
#region Find
public Range Find(string searchString)
{
return Find(0, NativeScintilla.GetTextLength(), searchString, _flags);
}
public Range Find(string searchString, bool searchUp)
{
if (searchUp)
return Find(NativeScintilla.GetTextLength(), 0, searchString, _flags);
else
return Find(0, NativeScintilla.GetTextLength(), searchString, _flags);
}
public Range Find(string searchString, SearchFlags searchflags)
{
return Find(0, NativeScintilla.GetTextLength(), searchString, searchflags);
}
public Range Find(string searchString, SearchFlags searchflags, bool searchUp)
{
if (searchUp)
return Find(NativeScintilla.GetTextLength(), 0, searchString, searchflags);
else
return Find(0, NativeScintilla.GetTextLength(), searchString, searchflags);
}
public Range Find(Range rangeToSearch, string searchString)
{
return Find(rangeToSearch.Start, rangeToSearch.End, searchString, _flags);
}
public Range Find(Range rangeToSearch, string searchString, bool searchUp)
{
if (searchUp)
return Find(rangeToSearch.End, rangeToSearch.Start, searchString, _flags);
else
return Find(rangeToSearch.Start, rangeToSearch.End, searchString, _flags);
}
public Range Find(Range rangeToSearch, string searchString, SearchFlags searchflags)
{
return Find(rangeToSearch.Start, rangeToSearch.End, searchString, searchflags);
}
public Range Find(Range rangeToSearch, string searchString, SearchFlags searchflags, bool searchUp)
{
if (searchUp)
return Find(rangeToSearch.End, rangeToSearch.Start, searchString, searchflags);
else
return Find(rangeToSearch.Start, rangeToSearch.End, searchString, searchflags);
}
public unsafe Range Find(int startPos, int endPos, string searchString, SearchFlags flags)
{
TextToFind ttf = new TextToFind();
ttf.chrg.cpMin = startPos;
ttf.chrg.cpMax = endPos;
fixed (byte* pb = UTF8Encoding.UTF8.GetBytes(searchString))
{
ttf.lpstrText = (IntPtr)pb;
int pos = NativeScintilla.FindText((int)flags, ref ttf);
if (pos >= 0)
{
return new Range(pos, pos + searchString.Length, Scintilla);
}
else
{
return null;
}
}
}
public Range Find(Regex findExpression)
{
return Find(new Range(0, NativeScintilla.GetTextLength(), Scintilla), findExpression, false);
}
public Range Find(Regex findExpression, bool searchUp)
{
return Find(new Range(0, NativeScintilla.GetTextLength(), Scintilla), findExpression, searchUp);
}
public Range Find(int startPos, int endPos, Regex findExpression)
{
return Find(new Range(startPos, endPos, Scintilla), findExpression, false);
}
public Range Find(int startPos, int endPos, Regex findExpression, bool searchUp)
{
return Find(new Range(startPos, endPos, Scintilla), findExpression, searchUp);
}
public Range Find(Range r, Regex findExpression)
{
return Find(r, findExpression, false);
}
public Range Find(Range r, Regex findExpression, bool searchUp)
{
// Single line and Multi Line in RegExp doesn't really effect
// whether or not a match will include newline charaters. This
// means we can't do a line by line search. We have to search
// the entire range becuase it could potentially match the
// entire range.
Match m = findExpression.Match(r.Text);
if (!m.Success)
return null;
if (searchUp)
{
// Since we can't search backwards with RegExp we
// have to search the entire string and return the
// last match. Not the most efficient way of doing
// things but it works.
Range range = null;
while (m.Success)
{
range = new Range(r.Start + m.Index, r.Start + m.Index + m.Length, Scintilla);
m = m.NextMatch();
}
return range;
}
return new Range(r.Start + m.Index, r.Start + m.Index + m.Length, Scintilla);
}
#endregion
#region FindAll
public List<Range> FindAll(string searchString)
{
return FindAll(searchString, _flags);
}
public List<Range> FindAll(string searchString, SearchFlags flags)
{
return FindAll(0, NativeScintilla.GetTextLength(), searchString, flags);
}
public List<Range> FindAll(Range rangeToSearch, string searchString)
{
return FindAll(rangeToSearch.Start, rangeToSearch.End, searchString, _flags);
}
public List<Range> FindAll(Range rangeToSearch, string searchString, SearchFlags flags)
{
return FindAll(rangeToSearch.Start, rangeToSearch.End, searchString, _flags);
}
public List<Range> FindAll(int startPos, int endPos, string searchString, SearchFlags flags)
{
List<Range> res = new List<Range>();
while (true)
{
Range r = Find(startPos, endPos, searchString, flags);
if (r == null)
{
break;
}
else
{
res.Add(r);
startPos = r.End;
}
}
return res;
}
public List<Range> FindAll(Regex findExpression)
{
return FindAll(0, NativeScintilla.GetTextLength(), findExpression);
}
public List<Range> FindAll(int startPos, int endPos, Regex findExpression)
{
return FindAll(new Range(startPos, endPos, Scintilla), findExpression);
}
public List<Range> FindAll(Range rangeToSearch, Regex findExpression)
{
List<Range> res = new List<Range>();
while (true)
{
Range r = Find(rangeToSearch, findExpression);
if (r == null)
{
break;
}
else
{
res.Add(r);
rangeToSearch = new Range(r.End, rangeToSearch.End, Scintilla);
}
}
return res;
}
#endregion
#region FindNext
public Range FindNext(string searchString)
{
return FindNext(searchString, true, _flags);
}
public Range FindNext(string searchString, bool wrap)
{
return FindNext(searchString, wrap, _flags);
}
public Range FindNext(string searchString, SearchFlags flags)
{
return FindNext(searchString, true, flags);
}
public Range FindNext(string searchString, bool wrap, SearchFlags flags)
{
Range r = Find(NativeScintilla.GetCurrentPos(), NativeScintilla.GetTextLength(), searchString, flags);
if (r != null)
return r;
else if (wrap)
return Find(0, NativeScintilla.GetCurrentPos(), searchString, flags);
else
return null;
}
public Range FindNext(string searchString, bool wrap, SearchFlags flags, Range searchRange)
{
int caret = Scintilla.Caret.Position;
if (!searchRange.PositionInRange(caret))
return Find(searchRange.Start, searchRange.End, searchString, flags);
Range r = Find(caret, searchRange.End, searchString, flags);
if (r != null)
return r;
else if (wrap)
return Find(searchRange.Start, caret, searchString, flags);
else
return null;
}
public Range FindNext(Regex findExpression)
{
return FindNext(findExpression, false);
}
public Range FindNext(Regex findExpression, bool wrap)
{
Range r = Find(NativeScintilla.GetCurrentPos(), NativeScintilla.GetTextLength(), findExpression);
if (r != null)
return r;
else if (wrap)
return Find(0, NativeScintilla.GetCurrentPos(), findExpression);
else
return null;
}
public Range FindNext(Regex findExpression, bool wrap, Range searchRange)
{
int caret = Scintilla.Caret.Position;
if (!searchRange.PositionInRange(caret))
return Find(searchRange.Start, searchRange.End, findExpression, false);
Range r = Find(caret, searchRange.End, findExpression);
if (r != null)
return r;
else if (wrap)
return Find(searchRange.Start, caret, findExpression);
else
return null;
}
#endregion
#region ReplaceNext
public Range ReplaceNext(string searchString, string replaceString)
{
return ReplaceNext(searchString, replaceString, true, _flags);
}
public Range ReplaceNext(string searchString, string replaceString, bool wrap)
{
return ReplaceNext(searchString, replaceString, wrap, _flags);
}
public Range ReplaceNext(string searchString, string replaceString, SearchFlags flags)
{
return ReplaceNext(searchString, replaceString, true, flags);
}
public Range ReplaceNext(string searchString, string replaceString, bool wrap, SearchFlags flags)
{
Range r = FindNext(searchString, wrap, flags);
if (r != null)
{
r.Text = replaceString;
r.End = r.Start + replaceString.Length;
}
return r;
}
#endregion
#region FindPrevious
public Range FindPrevious(string searchString)
{
return FindPrevious(searchString, true, _flags);
}
public Range FindPrevious(string searchString, bool wrap)
{
return FindPrevious(searchString, wrap, _flags);
}
public Range FindPrevious(string searchString, SearchFlags flags)
{
return FindPrevious(searchString, true, flags);
}
public Range FindPrevious(string searchString, bool wrap, SearchFlags flags)
{
Range r = Find(NativeScintilla.GetAnchor(), 0, searchString, flags);
if (r != null)
return r;
else if (wrap)
return Find(NativeScintilla.GetTextLength(), NativeScintilla.GetCurrentPos(), searchString, flags);
else
return null;
}
public Range FindPrevious(string searchString, bool wrap, SearchFlags flags, Range searchRange)
{
int caret = Scintilla.Caret.Position;
if (!searchRange.PositionInRange(caret))
return Find(searchRange.End, searchRange.Start, searchString, flags);
int anchor = Scintilla.Caret.Anchor;
if (!searchRange.PositionInRange(anchor))
anchor = caret;
Range r = Find(anchor, searchRange.Start, searchString, flags);
if (r != null)
return r;
else if (wrap)
return Find(searchRange.End, anchor, searchString, flags);
else
return null;
}
public Range FindPrevious(Regex findExpression)
{
return FindPrevious(findExpression, false);
}
public Range FindPrevious(Regex findExpression, bool wrap)
{
Range r = Find(0, NativeScintilla.GetAnchor(), findExpression, true);
if (r != null)
return r;
else if (wrap)
return Find(NativeScintilla.GetCurrentPos(), NativeScintilla.GetTextLength(), findExpression, true);
else
return null;
}
public Range FindPrevious(Regex findExpression, bool wrap, Range searchRange)
{
int caret = Scintilla.Caret.Position;
if (!searchRange.PositionInRange(caret))
return Find(searchRange.Start, searchRange.End, findExpression, true);
int anchor = Scintilla.Caret.Anchor;
if (!searchRange.PositionInRange(anchor))
anchor = caret;
Range r = Find(searchRange.Start, anchor, findExpression, true);
if (r != null)
return r;
else if (wrap)
return Find(anchor, searchRange.End, findExpression, true);
else
return null;
}
#endregion
#region ReplacePrevious
public Range ReplacePrevious(string searchString, string replaceString)
{
return ReplacePrevious(searchString, replaceString, true, _flags);
}
public Range ReplacePrevious(string searchString, string replaceString, bool wrap)
{
return ReplacePrevious(searchString, replaceString, wrap, _flags);
}
public Range ReplacePrevious(string searchString, string replaceString, SearchFlags flags)
{
return ReplacePrevious(searchString, replaceString, true, flags);
}
public Range ReplacePrevious(string searchString, string replaceString, bool wrap, SearchFlags flags)
{
Range r = FindPrevious(searchString, wrap, flags);
if (r != null)
{
r.Text = replaceString;
r.End = r.Start + replaceString.Length;
}
return r;
}
#endregion
#region ReplaceAll
public List<Range> ReplaceAll(string searchString, string replaceString)
{
return ReplaceAll(searchString, replaceString, _flags);
}
public List<Range> ReplaceAll(string searchString, string replaceString, SearchFlags flags)
{
return ReplaceAll(0, NativeScintilla.GetTextLength(), searchString, replaceString, flags);
}
public List<Range> ReplaceAll(Range rangeToSearch, string searchString, string replaceString)
{
return ReplaceAll(rangeToSearch.Start, rangeToSearch.End, searchString, replaceString, _flags);
}
public List<Range> ReplaceAll(Range rangeToSearch, string searchString, string replaceString, SearchFlags flags)
{
return ReplaceAll(rangeToSearch.Start, rangeToSearch.End, searchString, replaceString, _flags);
}
public List<Range> ReplaceAll(int startPos, int endPos, string searchString, string replaceString, SearchFlags flags)
{
List<Range> ret = new List<Range>();
Scintilla.UndoRedo.BeginUndoAction();
int diff = replaceString.Length - searchString.Length;
while (true)
{
Range r = Find(startPos, endPos, searchString, flags);
if (r == null)
{
break;
}
else
{
r.Text = replaceString;
r.End = startPos = r.Start + replaceString.Length;
endPos += diff;
ret.Add(r);
}
}
Scintilla.UndoRedo.EndUndoAction();
return ret;
}
public List<Range> ReplaceAll(Regex findExpression, string replaceString)
{
return ReplaceAll(0, NativeScintilla.GetTextLength(), findExpression, replaceString);
}
public List<Range> ReplaceAll(int startPos, int endPos, Regex findExpression, string replaceString)
{
return ReplaceAll(new Range(startPos, endPos, Scintilla), findExpression, replaceString);
}
private List<Range> _lastReplaceAllMatches = new List<Range>();
private string _lastReplaceAllReplaceString = "";
private Range _lastReplaceAllRangeToSearch = null;
private int _lastReplaceAllOffset = 0;
public List<Range> ReplaceAll(Range rangeToSearch, Regex findExpression, string replaceString)
{
Scintilla.UndoRedo.BeginUndoAction();
// I tried using an anonymous delegate for this but it didn't work too well.
// It's too bad because it was a lot cleaner than using member variables as
// psuedo globals.
_lastReplaceAllMatches = new List<Range>();
_lastReplaceAllReplaceString = replaceString;
_lastReplaceAllRangeToSearch = rangeToSearch;
_lastReplaceAllOffset = 0;
findExpression.Replace(rangeToSearch.Text, new MatchEvaluator(replaceAllEvaluator));
Scintilla.UndoRedo.EndUndoAction();
// No use having these values hanging around wasting memory :)
List<Range> ret = _lastReplaceAllMatches;
_lastReplaceAllMatches = null;
_lastReplaceAllReplaceString = null;
_lastReplaceAllRangeToSearch = null;
return ret;
}
private string replaceAllEvaluator(Match m)
{
// So this method is called for every match
// We make a replacement in the range based upon
// the match range.
string replacement = m.Result(_lastReplaceAllReplaceString);
int start = _lastReplaceAllRangeToSearch.Start + m.Index + _lastReplaceAllOffset;
int end = start + m.Length;
Range r = new Range(start, end, Scintilla);
_lastReplaceAllMatches.Add(r);
r.Text = replacement;
// But because we've modified the document, the RegEx
// match ranges are going to be different from the
// document ranges. We need to compensate
_lastReplaceAllOffset += replacement.Length - m.Value.Length;
return replacement;
}
#endregion
public void ShowFind()
{
if (!_window.Visible)
_window.Show(Scintilla.FindForm());
_window.tabAll.SelectedTab = _window.tabAll.TabPages["tpgFind"];
Range selRange = Scintilla.Selection.Range;
if (selRange.IsMultiLine)
{
_window.chkSearchSelectionF.Checked = true;
}
else if (selRange.Length > 0)
{
_window.cboFindF.Text = selRange.Text;
}
_window.cboFindF.Select();
_window.cboFindF.SelectAll();
}
public void ShowReplace()
{
if (!_window.Visible)
_window.Show(Scintilla.FindForm());
_window.tabAll.SelectedTab = _window.tabAll.TabPages["tpgReplace"];
Range selRange = Scintilla.Selection.Range;
if (selRange.IsMultiLine)
{
_window.chkSearchSelectionR.Checked = true;
}
else if (selRange.Length > 0)
{
_window.cboFindR.Text = selRange.Text;
}
_window.cboFindR.Select();
_window.cboFindR.SelectAll();
}
public List<MarkerInstance> MarkAll(IList<Range> foundRanges)
{
List<MarkerInstance> ret = new List<MarkerInstance>();
Line lastLine = new Line(Scintilla, -1);
foreach (Range r in foundRanges)
{
// We can of course have multiple instances of a find on a single
// line. We don't want to mark this line more than once.
Line line = r.StartingLine;
if (line.Number > lastLine.Number)
ret.Add(Marker.AddInstanceTo(r.StartingLine));
lastLine = line;
}
return ret;
}
public void HighlightAll(IList<Range> foundRanges)
{
foreach (Range r in foundRanges)
{
r.SetIndicator(Indicator.Number);
}
}
public void ClearAllHighlights()
{
Indicator i = Scintilla.FindReplace.Indicator;
foreach (Range r in i.SearchAll())
{
r.ClearIndicator(i);
}
}
public void IncrementalSearch()
{
_incrementalSearcher.Show();
}
}
}