|
using System;
using EnvDTE;
using System.Text.RegularExpressions;
using RegexFindAndReplace;
using System.Diagnostics;
using EnvDTE80;
using System.Windows.Forms;
using System.Collections.Generic;
namespace RegexFindAndReplace
{
/// <summary>
/// The FinderAndReplacer class finds and replaces using .NET regular expressions
/// in the file that is currently open.
/// </summary>
public class FinderAndReplacer
{
private DTE2 applicationObject;
private EditPoint searchStartEditPoint = null;
private EditPoint searchLatestEditPoint = null;
/// <summary>
/// The MatchInfo struct holds information about the position of the current match.
/// </summary>
struct MatchPositionInfo
{
public int StartLine;
public int StartLineCharOffset;
public int EndLine;
public int EndLineCharOffset;
}
private MatchPositionInfo matchPositionInfo;
private int matchStartPoint;
private int matchEndPoint;
private IFindState findState;
private string sourceText;
/// <summary>
/// Initializes a new instance of the <see cref="T:FinderAndReplacer"/> class.
/// </summary>
/// <param name="applicationObject">The application object.</param>
public FinderAndReplacer( DTE2 applicationObject )
{
this.applicationObject = applicationObject;
}
private string pattern = string.Empty;
/// <summary>
/// Gets or sets the pattern used in regex finds.
/// </summary>
/// <value>The pattern.</value>
public string Pattern
{
get
{
return this.pattern;
}
set
{
try
{
Regex testRegex = new Regex( value );
}
catch ( ArgumentException ex )
{
MessageBox.Show( ex.Message );
throw;
}
this.pattern = value;
this.findState = new FirstFindState();
}
}
private RegexOptions regexOptions = RegexOptions.None;
/// <summary>
/// Gets or sets the RegexOptions.
/// </summary>
/// <value>The RegexOptions.</value>
public RegexOptions RegexOptions
{
set
{
this.regexOptions = value;
}
get
{
return this.regexOptions;
}
}
/// <summary>
/// Sets the IFindState.
/// </summary>
/// <param name="newFindState">The new state</param>
public void SetState( IFindState newFindState )
{
this.findState = newFindState;
}
/// <summary>
/// Finds the next match
/// </summary>
internal void FindNext()
{
SetLatestFindStartPosition();
this.findState.FindNext( this );
}
/// <summary>
/// Attempts to find a match in the current file.
/// </summary>
/// <param name="noMatchInFile">if set to <c>true</c> [no match in file].</param>
/// <returns>true if a match was found</returns>
internal bool AttemptMatchInCurrentFile( out bool noMatchInFile )
{
bool matchSuccessful = false;
noMatchInFile = false;
SetLatestFindStartPosition();
// see if this is the first match attempt in this file
bool matchFromStartOfFile = this.searchLatestEditPoint.AbsoluteCharOffset == 1;
if ( AttemptMatch() )
{
// select the match and report success
SelectMatchedText();
SaveMatchedPostion();
matchSuccessful = true;
}
else
{
if ( matchFromStartOfFile )
{
noMatchInFile = true;
}
ResetLatestFindStartPosition();
}
return matchSuccessful;
}
/// <summary>
/// Performs a replace operation on the source string and returns the result
/// </summary>
/// <param name="source">The source.</param>
/// <param name="replacePattern">The replacement pattern.</param>
/// <returns></returns>
public string Replace( string source, string replacePattern )
{
Regex regex = new Regex( this.pattern, this.regexOptions );
return regex.Replace( source, replacePattern );
}
/// <summary>
/// Replaces the selected text using the specified replacement pattern and finds the next match.
/// </summary>
/// <param name="replacePattern">The replace pattern.</param>
public void ReplaceNext( string replacePattern )
{
if ( this.searchLatestEditPoint == null )
{
SetLatestFindStartPosition();
}
else
{
ReplaceSelectedText( replacePattern );
}
FindNext();
SaveMatchedPostion();
}
/// <summary>
/// Attempts to replace the selected text in the current file
/// </summary>
/// <param name="replacePattern">The replace pattern.</param>
/// <param name="noMatchInFile">if set to <c>true</c> [no match in file].</param>
/// <returns>true if the replacement was successful</returns>
public bool AttemptReplaceInCurrentFile( string replacePattern, out bool noMatchInFile )
{
if ( this.searchLatestEditPoint == null )
{
SetLatestFindStartPositionToStartOfDocument();
}
else
{
ReplaceSelectedText( replacePattern );
}
return AttemptMatchInCurrentFile( out noMatchInFile );
}
/// <summary>
/// Performs a replace operation on the source string and returns the result.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="replacePattern">The replacement pattern.</param>
/// <param name="repeatCount">out parameter for the number of replacements made</param>
/// <returns></returns>
public string ReplaceAll( string source, string replacePattern, out int replacementCount )
{
Regex regex = new Regex( this.pattern, this.regexOptions );
string result = string.Empty;
replacementCount = regex.Matches( source ).Count;
if ( replacementCount > 0 )
{
result = regex.Replace( source, replacePattern );
}
return result;
}
/// <summary>
/// Performs a replace all operation on the current text file.
/// </summary>
/// <param name="replacePattern">The replace pattern.</param>
/// <param name="replaceInMultipleFiles">if set to <c>true</c> [replace in multiple files].</param>
/// <returns>The number of replacements</returns>
public int ReplaceAll( string replacePattern, bool replaceInMultipleFiles )
{
SetSearchStartEditPoint();
MoveToStartInTextWindow();
TextSelection textSelection = GetTextSelection();
TextDocument textDocument = GetCurrentTextDocument();
string windowText = textSelection.ActivePoint.CreateEditPoint().GetText( textDocument.EndPoint );
EditPoint editPoint = textSelection.ActivePoint.CreateEditPoint();
TextPoint endPoint = textDocument.EndPoint.CreateEditPoint();
int replacementCount;
string replaceWindowText = ReplaceAll( windowText, replacePattern, out replacementCount );
if ( replacementCount > 0 )
{
editPoint.ReplaceText( endPoint, Replace( windowText, replacePattern ), (int)vsEPReplaceTextOptions.vsEPReplaceTextAutoformat );
if ( !replaceInMultipleFiles )
{
// report the number of replacements if working on just the current file
ShowMessage( string.Format( "{0} occurance(s) replaced.", replacementCount ), false );
}
}
textSelection.MoveToPoint( this.searchStartEditPoint, false );
return replacementCount;
}
/// <summary>
/// Performs a replace all operation on the currently selected text in the current text file.
/// </summary>
/// <param name="replacePattern">The replace pattern.</param>
public void ReplaceAllInSelection( string replacePattern )
{
TextSelection textSelection = GetTextSelection();
int topCharOffset = textSelection.AnchorPoint.AbsoluteCharOffset;
Regex regex = new Regex( this.pattern, this.regexOptions );
string replacement = textSelection.Text = regex.Replace( textSelection.Text, replacePattern );
textSelection.MoveToAbsoluteOffset( topCharOffset, false );
// adjust the end of the selection to account for changes in the length of replacement text
textSelection.MoveToAbsoluteOffset( topCharOffset + Regex.Replace( replacement, "\r", "" ).Length, true );
}
/// <summary>
/// Displays a message in the Visual Studio status bar
/// </summary>
/// <param name="message">The message.</param>
/// <param name="highlight">if set to <c>true</c> [highlight the text in the status bar].</param>
public void ShowMessage( string message, bool highlight )
{
this.applicationObject.StatusBar.Text = message;
this.applicationObject.StatusBar.Highlight( highlight );
}
/// <summary>
/// Sets the search start edit point.
/// </summary>
public void SetSearchStartEditPoint()
{
TextSelection textSelection = (TextSelection)this.applicationObject.ActiveDocument.Selection;
this.searchStartEditPoint = textSelection.ActivePoint.CreateEditPoint();
}
/// <summary>
/// Moves to the start of the text window.
/// </summary>
public void MoveToStartInTextWindow()
{
TextSelection textSelection = GetTextSelection();
textSelection.MoveToPoint( GetCurrentTextDocument().StartPoint, false );
}
/// <summary>
/// Gets the MatchContextInfo for the current match.
/// </summary>
/// <returns>The MatchContextInfo</returns>
private MatchContextInfo GetMatchContextInfo()
{
MatchContextInfo matchContextInfo = null;
Regex regex = new Regex( this.pattern, this.regexOptions );
Match match = regex.Match( this.sourceText );
if ( match.Success && match.Length > 0 )
{
int startLine = MatchContextUtils.GetLineCount( this.sourceText, match.Index );
int startLineOffset = MatchContextUtils.GetPositionInLine( this.sourceText, match.Index ) - 1;
int endLine = MatchContextUtils.GetLineCount( this.sourceText, match.Index + match.Length );
int endLineOffset = MatchContextUtils.GetPositionInLine( this.sourceText, match.Index + match.Length - 1 );
matchContextInfo = new MatchContextInfo( startLine, endLine, match.Value, startLineOffset, match.Index, match.Length, endLineOffset );
}
return matchContextInfo;
}
/// <summary>
/// Attempts a match from the current position in the file.
/// </summary>
/// <returns>true if a match was found</returns>
public bool AttemptMatch()
{
bool matchFound = false;
TextSelection textSelection = GetTextSelection();
int activePointLine = textSelection.ActivePoint.Line;
int activePointLineCharOffset = textSelection.ActivePoint.LineCharOffset;
this.sourceText = textSelection.ActivePoint.CreateEditPoint().GetText( GetCurrentTextDocument().EndPoint );
MatchContextInfo match = GetMatchContextInfo();
if ( match != null )
{
if ( match.MatchContent.Length > 0 )
{
matchFound = true;
// if the match only includes one line
if ( match.StartLine == 1 && match.EndLine == match.StartLine )
{
this.matchPositionInfo.StartLine = activePointLine;
this.matchPositionInfo.StartLineCharOffset = activePointLineCharOffset + match.StartLineOffset;
this.matchPositionInfo.EndLine = activePointLine + match.EndLine - 1;
this.matchPositionInfo.EndLineCharOffset = activePointLineCharOffset + match.EndLineOffset;
}
else
{
this.matchPositionInfo.StartLine = activePointLine + match.StartLine - 1;
this.matchPositionInfo.StartLineCharOffset = match.StartLineOffset + 1;
this.matchPositionInfo.EndLine = activePointLine + match.EndLine - 1;
this.matchPositionInfo.EndLineCharOffset = match.EndLineOffset + 1;
}
}
}
return matchFound;
}
/// <summary>
/// Selects the matched text.
/// </summary>
public void SelectMatchedText()
{
TextSelection textSelection = GetTextSelection();
textSelection.MoveToLineAndOffset( this.matchPositionInfo.StartLine, this.matchPositionInfo.StartLineCharOffset, false );
textSelection.MoveToLineAndOffset( this.matchPositionInfo.EndLine, this.matchPositionInfo.EndLineCharOffset, true );
}
/// <summary>
/// Determines if the search in the current file has wrapped to where the search begain
/// </summary>
/// <returns>true if the search has return to the start</returns>
public bool WrappedToStartPoint()
{
return ( ( this.matchPositionInfo.StartLine > this.searchStartEditPoint.Line ) ||
( this.matchPositionInfo.StartLine == this.searchStartEditPoint.Line && this.matchPositionInfo.StartLineCharOffset >= this.searchStartEditPoint.LineCharOffset ) );
}
/// <summary>
/// Saves the matched postion.
/// </summary>
public void SaveMatchedPostion()
{
TextSelection textSelection = GetTextSelection();
this.matchStartPoint = textSelection.AnchorPoint.AbsoluteCharOffset;
this.matchEndPoint = textSelection.ActivePoint.AbsoluteCharOffset;
}
/// <summary>
/// Gets the text selection in the current text file.
/// </summary>
/// <returns></returns>
public TextSelection GetTextSelection()
{
TextSelection textSelection = null;
if ( this.applicationObject.ActiveDocument != null )
{
textSelection = (TextSelection)this.applicationObject.ActiveDocument.Selection;
}
return textSelection;
}
/// <summary>
/// Gets the current text document.
/// </summary>
/// <returns></returns>
private TextDocument GetCurrentTextDocument()
{
return (TextDocument)this.applicationObject.ActiveDocument.Object( string.Empty );
}
/// <summary>
/// Replaces the selected text using the specified replacement pattern.
/// </summary>
/// <param name="replacePattern">The replace pattern.</param>
private void ReplaceSelectedText( string replacePattern )
{
TextSelection textSelection = GetTextSelection();
if ( this.matchStartPoint == textSelection.AnchorPoint.AbsoluteCharOffset && this.matchEndPoint == textSelection.ActivePoint.AbsoluteCharOffset )
{
textSelection.Text = GetReplacementText( this.searchLatestEditPoint.GetText( GetCurrentTextDocument().EndPoint ), replacePattern );
}
SetLatestFindStartPosition();
}
/// <summary>
/// Sets the latest find start position.
/// </summary>
private void SetLatestFindStartPosition()
{
TextSelection textSelection = GetTextSelection();
this.searchLatestEditPoint = textSelection.ActivePoint.CreateEditPoint();
}
/// <summary>
/// Resets the latest find start position.
/// </summary>
public void ResetLatestFindStartPosition()
{
this.searchLatestEditPoint = null;
}
/// <summary>
/// Sets the latest find start position to the start of the current text file.
/// </summary>
public void SetLatestFindStartPositionToStartOfDocument()
{
GetTextSelection().StartOfDocument( false );
this.searchLatestEditPoint = GetTextSelection().ActivePoint.CreateEditPoint();
}
/// <summary>
/// Gets the replacement text to use.
/// </summary>
/// <param name="source">The source string.</param>
/// <param name="replacePattern">The replace pattern.</param>
/// <returns></returns>
private string GetReplacementText( string source, string replacePattern )
{
string replacementText = string.Empty;
string replacingSource = source;
Regex regex = new Regex( this.pattern, this.regexOptions );
Match match = regex.Match( replacingSource );
if ( match.Success )
{
string changedSource = regex.Replace( replacingSource, replacePattern, 1 );
int replaceTextStartPosition = match.Index;
int replaceTextEndPosition = changedSource.Length - ( replacingSource.Length - match.Index - match.Length );
replacementText = changedSource.Substring( replaceTextStartPosition, replaceTextEndPosition - replaceTextStartPosition );
}
return replacementText;
}
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
I am a software developer currently working in Salt Lake City, Utah. I work primarily with C# for my job, but I mess around with C, Perl, and Windows PowerShell for fun.