Click here to Skip to main content
15,893,814 members
Articles / Programming Languages / XML

.NET Regular Expressions Find and Replace Add-In for Visual Studio 2008

Rate me:
Please Sign up or sign in to vote.
4.91/5 (36 votes)
12 Oct 2009CPOL3 min read 186.1K   1.6K   117  
A .NET Regular Expressions Find and Replace add-in for Visual Studio 2008
using System;
using System.Collections.Generic;
using System.Text;
using EnvDTE80;
using System.Text.RegularExpressions;
using System.IO;
using System.Collections.Specialized;
using System.Collections;
using EnvDTE;
using System.Windows.Forms;

namespace RegexFindAndReplace
{
    class StaticFinderAndReplacer
    {
        private bool _isReplaceNext = false;
        private FinderAndReplacer finderAndReplacer = null;
        private DTE2 applicationObject;
        private int fileCount;

        public StaticFinderAndReplacer( DTE2 applicationObject )
        {
            this.applicationObject = applicationObject;
        }

        private string pattern = string.Empty;

        public string Pattern
        {
            get
            {
                return this.pattern;
            }
            set
            {
                try
                {
                    Regex testPatternRegex = new Regex( value );
                }
                catch ( ArgumentException ex )
                {
                    MessageBox.Show( ex.Message );
                    throw;
                }

                this.pattern = value;
            }
        }

        private RegexOptions regexOptions = RegexOptions.None;

        public RegexOptions RegexOptions
        {
            set
            {
                this.regexOptions = value;
            }
            get
            {
                return this.regexOptions;
            }
        }

        private string searchDirectory;

        public string SearchDirectory
        {
            get
            {
                return this.searchDirectory;
            }
            set
            {
                this.searchDirectory = value;
            }
        }

        private int matchContextLineCount;

        public int MatchContextLineCount
        {
            get
            {
                return matchContextLineCount;
            }
            set
            {
                matchContextLineCount = value;
            }
        }

        public bool IsReplaceNext
        {
            set
            {
                _isReplaceNext = value;
            }
        }
        
        private StringCollection _replacedFileList = new StringCollection();
        
        private OutputWindowPane outputWindowPane = null;
        
        private ArrayList _matchInfoList = new ArrayList();

        public ArrayList MatchInfoList
        {
            get
            {
                return _matchInfoList;
            }
        }

        private string fileTypes = string.Empty;
        public string FileTypes
        {
            set
            {
                this.fileTypes = value;
            }
        }

        private bool includeSubDirectories;

        public bool IncludeSubDirectories
        {
            set
            {
                this.includeSubDirectories = value;
            }
        }

        private int _backLines;
        public int BackLines
        {
            set
            {
                _backLines = value;
            }
        }

        private int _frontLines;
        public int FrontLines
        {
            set
            {
                _frontLines = value;
            }
        }

        public void FindAll()
        {
            ValidateOutputWindowPane();

            this.outputWindowPane.OutputString( string.Format( @"Find all ""{0}"", {1}""{2}"", ""{3}""{4}", this.Pattern, GetRegexOptionsString(), this.searchDirectory, this.fileTypes, Environment.NewLine ) );

            FindAllInFiles( this.searchDirectory );
        }

        private void FindAllInFiles( string directory )
        {
            string[] fileTypeArray = this.fileTypes.Split( ';' );
            foreach ( string fileType in fileTypeArray )
            {
                foreach ( string fullFilename in Directory.GetFiles( directory, fileType ) )
                {
                    FindInFile( fullFilename );
                }
            }

            if ( this.includeSubDirectories )
            {
                foreach ( string fileType in fileTypeArray )
                {
                    foreach ( string directoryName in Directory.GetDirectories( directory, fileType ) )
                    {
                        FindAllInFiles( directoryName );
                    }
                }
            }
        }

        private void FindInFile( string filename )
        {
            string source = string.Empty;

            using ( StreamReader streamReader = new StreamReader( filename ) )
            {
                source = streamReader.ReadToEnd();
                fileCount++;
            }
            
            Regex regex = new Regex( this.Pattern, this.RegexOptions );
            
            foreach ( Match match in regex.Matches( source ) )
            {
                int lineNumber = MatchContextUtils.GetLineCount( source, match.Index );

                MatchContextInfo matchContextInfo = new MatchContextInfo( filename, lineNumber, 0, match.Value, MatchContextUtils.GetMatchContextString( source, match.Index, match.Index + match.Length, this.matchContextLineCount ) );

                int lineCount = MatchContextUtils.GetLineCount( matchContextInfo.MatchContext, matchContextInfo.MatchContext.Length );

                string fileNameAndLineNumbers = string.Empty;

                if ( lineCount != 1 )
                {
                    fileNameAndLineNumbers = string.Format( "  {0}({1}-{2}):\n", filename, ( lineNumber - this.matchContextLineCount ) > 0 ? ( lineNumber - this.matchContextLineCount ) : 0, lineNumber + lineCount - 1 );
                }
                else
                {
                    fileNameAndLineNumbers = string.Format( "  {0}({1}):  ", filename, lineNumber );
                }

                this.outputWindowPane.OutputString( string.Format( "{0}{1}{2}", fileNameAndLineNumbers, matchContextInfo.MatchContext, Environment.NewLine ) );
            }
        }

        public void FindNext()
        {
            this.finderAndReplacer.Pattern = this.Pattern;
            this.finderAndReplacer.RegexOptions = this.RegexOptions;

            if ( !finderAndReplacer.FindNextForMultipleFiles() )
            {
                ReplaceNextInFiles( this.searchDirectory, this.fileTypes, string.Empty, true );
            }
        }

        private void showResults( bool isReplace )
        {
            ValidateOutputWindowPane();

            string title1 = string.Empty;
            string title2 = string.Empty;
            outputWindowPane.Activate();

            if ( isReplace )
            {
                title1 = "Replace";
                title2 = "Replaced";
            }
            else
            {
                title1 = "Search";
                title2 = "Found";
            }

            outputWindowPane.OutputString( string.Format( "{0} \"{1}\", in {2}{3}", title1, this.Pattern, this.searchDirectory, Environment.NewLine ) );

            StringCollection matchedFiles = new StringCollection();
            foreach ( MatchContextInfo matchContextInfo in MatchInfoList )
            {
                string showInfo = string.Format( "{0}({1}):   {2}{3}", matchContextInfo.FullFilename, matchContextInfo.StartLine, MatchContextUtils.HasMultipleLines( matchContextInfo.MatchContext ) ? Environment.NewLine : string.Empty, matchContextInfo.MatchContext );
                if ( showInfo.LastIndexOf( '\r' ) == showInfo.Length - 1 || showInfo.LastIndexOf( '\n' ) == showInfo.Length - 1 )
                    outputWindowPane.OutputString( string.Format( "{0}{1}", showInfo, Environment.NewLine ) );
                else
                    outputWindowPane.OutputString( string.Format( "{0}{1}", showInfo, Environment.NewLine ) );
                if ( !matchedFiles.Contains( matchContextInfo.FullFilename ) )
                    matchedFiles.Add( matchContextInfo.FullFilename );
            }
            outputWindowPane.OutputString( string.Format( "{0} {1}, matched files {2}, Searching files {3}{4}", title2, MatchInfoList.Count, matchedFiles.Count, fileCount, Environment.NewLine ) );
        }

        public void ReplaceInFiles( string dirPath, string fileSearchPattern, string replacePattern )
        {
            string[] fileSearchPatternList = fileSearchPattern.Split( ';' );
            foreach ( string fileSearchPatternItem in fileSearchPatternList )
            {
                foreach ( string fullFilename in Directory.GetFiles( dirPath, fileSearchPatternItem ) )
                {
                    ReplaceInOneFile( fullFilename, replacePattern );
                }
                if ( includeSubDirectories )
                {
                    foreach ( string dirFullName in Directory.GetDirectories( dirPath ) )
                    {
                        ReplaceInFiles( dirFullName, fileSearchPattern, replacePattern );
                    }
                }
            }
        }

        public void ReplaceAll( string replacePattern )
        {
            _matchInfoList.Clear();
            ReplaceInFiles( this.searchDirectory, fileTypes, replacePattern );
            showResults( true );
        }

        private void recordReplacedInfomation( string source, string replacedAllSource, string replacePattern, string fullFilename )
        {
            string replacingSource = source;
            int startPos = 0;
            while ( true )
            {
                Regex rg = new Regex( this.Pattern, this.RegexOptions );
                Match match = rg.Match( replacingSource, startPos );
                if ( match.Success )
                {
                    string changedSource = rg.Replace( replacingSource, replacePattern, 1, startPos );
                    startPos = match.Index + match.Length;
                    int replaceTextStartPos = match.Index;//position in changedSource
                    int replaceTextEndPos = changedSource.Length - ( replacingSource.Length - startPos );//position in changedSource
                    string replaceText = changedSource.Substring( replaceTextStartPos, replaceTextEndPos - replaceTextStartPos );

                    int lineNum = MatchContextUtils.GetLineCount( replacedAllSource, replaceTextStartPos ) + 1;
                    MatchContextInfo matchContextInfo = new MatchContextInfo( fullFilename, lineNum, 0, replaceText, MatchContextUtils.GetMatchContextString( replacedAllSource, replaceTextStartPos, replaceTextEndPos, this.matchContextLineCount ) );
                    _matchInfoList.Add( matchContextInfo );
                    replacingSource = changedSource;
                }
                else
                {
                    break;
                }
            }
        }

        private bool ReplaceInOneFile( string fullFilename, string replacePattern )
        {
            string source = string.Empty;
            using ( StreamReader sr = new StreamReader( fullFilename, System.Text.Encoding.GetEncoding( "GB2312" ) ) )
            {
                source = sr.ReadToEnd();
            }

            Regex rg = new Regex( this.Pattern, this.RegexOptions );
            int replaceCount;

            string replacedAllSource = this.finderAndReplacer.ReplaceAll( source, replacePattern, out replaceCount );

            if ( replacedAllSource.Length > 0 )
            {
                if ( _isReplaceNext )
                {
                    this.applicationObject.ItemOperations.OpenFile( fullFilename, Constants.vsViewKindTextView );
                    return true;
                }
                else
                {
                    recordReplacedInfomation( source, replacedAllSource, replacePattern, fullFilename );

                    using ( StreamWriter sw = new StreamWriter( fullFilename, false, System.Text.Encoding.Default ) )
                    {
                        sw.Write( replacedAllSource );
                    }
                }
            }
            return false;
        }

        public void ReplaceNext( string replacePattern )
        {
            if ( false )
            {
                finderAndReplacer.Pattern = this.Pattern;
                finderAndReplacer.RegexOptions = this.RegexOptions;
                if ( !finderAndReplacer.ReplaceNextForMultipleFiles( replacePattern ) )
                {
                    ReplaceNextInFiles( this.searchDirectory, fileTypes, replacePattern, true );
                }
            }
            else
            {
                ReplaceNextInFiles( this.searchDirectory, fileTypes, replacePattern, true );
            }
        }

        public bool ReplaceNextInFiles( string dirPath, string fileSearchPattern, string replacePattern, bool isRoot )
        {
            string[] fileSearchPatternList = fileSearchPattern.Split( ';' );
            foreach ( string fileSearchPatternItem in fileSearchPatternList )
            {
                foreach ( string fullFilename in Directory.GetFiles( dirPath, fileSearchPatternItem ) )
                {
                    if ( !_replacedFileList.Contains( fullFilename ) )
                    {
                        _replacedFileList.Add( fullFilename );
                        if ( ReplaceInOneFile( fullFilename, replacePattern ) )
                        {
                            ValidateFinderAndReplacer();
                            return true;
                        }
                    }
                }
            }
            if ( includeSubDirectories )
            {
                foreach ( string fileSearchPatternItem in fileSearchPatternList )
                {
                    foreach ( string dirFullName in Directory.GetDirectories( dirPath, fileSearchPatternItem ) )
                    {
                        if ( ReplaceNextInFiles( dirFullName, fileSearchPattern, replacePattern, false ) )
                            return true;
                    }
                }
            }
            if ( isRoot )
            {
                //UIHelper.ShowInfo( Strings.UI_PROMPT_FIND_TO_END );
            }
            return false;
        }

        private void ValidateFinderAndReplacer()
        {
            if ( finderAndReplacer == null || !( finderAndReplacer.Pattern == this.pattern && finderAndReplacer.RegexOptions == this.regexOptions ) )
            {
                InitializeFinderAndReplacer();
            }
        }

        private void InitializeFinderAndReplacer()
        {
            if ( finderAndReplacer == null )
            {
                finderAndReplacer = new FinderAndReplacer( this.applicationObject );
            }

            finderAndReplacer.Pattern = this.pattern;
            finderAndReplacer.RegexOptions = this.regexOptions;
        }

        private void ValidateOutputWindowPane()
        {
            if ( this.outputWindowPane == null )
            {
                this.outputWindowPane = this.applicationObject.ToolWindows.OutputWindow.OutputWindowPanes.Add( "Regex Find and Replace" );
            }
        }

        private string GetRegexOptionsString()
        {
            StringBuilder regexOptionsStringBuilder = new StringBuilder();

            if ( ( this.regexOptions & RegexOptions.IgnoreCase ) == RegexOptions.IgnoreCase )
            {
                regexOptionsStringBuilder.Append( "RegexOptions.IgnoreCase" );
            }
            if ( ( this.regexOptions & RegexOptions.Multiline ) == RegexOptions.Multiline )
            {
                if ( regexOptionsStringBuilder.Length > 0 )
                {
                    regexOptionsStringBuilder.Append( " | " );
                }

                regexOptionsStringBuilder.Append( "RegexOptions.Multiline" );
            }
            if ( ( this.regexOptions & RegexOptions.Singleline ) == RegexOptions.Singleline )
            {
                if ( regexOptionsStringBuilder.Length > 0 )
                {
                    regexOptionsStringBuilder.Append( " | " );
                }

                regexOptionsStringBuilder.Append( "RegexOptions.Singleline" );
            }
            if ( ( this.regexOptions & RegexOptions.ExplicitCapture ) == RegexOptions.ExplicitCapture )
            {
                if ( regexOptionsStringBuilder.Length > 0 )
                {
                    regexOptionsStringBuilder.Append( " | " );
                }

                regexOptionsStringBuilder.Append( "RegexOptions.ExplicitCapture" );
            }
            if ( ( this.regexOptions & RegexOptions.IgnorePatternWhitespace ) == RegexOptions.IgnorePatternWhitespace )
            {
                if ( regexOptionsStringBuilder.Length > 0 )
                {
                    regexOptionsStringBuilder.Append( " | " );
                }

                regexOptionsStringBuilder.Append( "RegexOptions.IgnorePatternWhitespace" );
            }
            if ( ( this.regexOptions & RegexOptions.ECMAScript ) == RegexOptions.ECMAScript )
            {
                if ( regexOptionsStringBuilder.Length > 0 )
                {
                    regexOptionsStringBuilder.Append( " | " );
                }

                regexOptionsStringBuilder.Append( "RegexOptions.ECMAScript" );
            }
            if ( ( this.regexOptions & RegexOptions.RightToLeft ) == RegexOptions.RightToLeft )
            {
                if ( regexOptionsStringBuilder.Length > 0 )
                {
                    regexOptionsStringBuilder.Append( " | " );
                }

                regexOptionsStringBuilder.Append( "RegexOptions.RightToLeft" );
            }
            if ( ( this.regexOptions & RegexOptions.CultureInvariant ) == RegexOptions.CultureInvariant )
            {
                if ( regexOptionsStringBuilder.Length > 0 )
                {
                    regexOptionsStringBuilder.Append( " | " );
                }

                regexOptionsStringBuilder.Append( "RegexOptions.CultureInvariant" );
            }

            if ( regexOptionsStringBuilder.Length > 0 )
            {
                regexOptionsStringBuilder.Insert( 0, "\"" );
                regexOptionsStringBuilder.Append( "\", " );
            }

            return regexOptionsStringBuilder.ToString();
        }
    }
}

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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
United States United States
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.

Comments and Discussions