Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version
Go to top

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

, 12 Oct 2009
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 EnvDTE;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using System.Collections;
using System.IO;

namespace RegexFindAndReplace
{
    /// <summary>
    /// The MultipleFinderAndReplacer class finds and replaces using .NET regular expressions
    /// in multiple text files.
    /// </summary>
    class MultipleFinderAndReplacer
    {
        private FinderAndReplacer finderAndReplacer = null;
        private DTE2 applicationObject;
        private OutputWindowPane outputWindowPane = null;
        private string currentFilePath = null;
        private int matchCount;
        private int matchedFileCount;
        private int fileCount;
        private Regex replaceAllRegex;
        private string replaceAllPattern;
        private string fileSource;
        private int replacementCountForCurrentFile;
        private bool showReplaceAllOutput;

        /// <summary>
        /// Initializes a new instance of the <see cref="T:MultipleFinderAndReplacer"/> class.
        /// </summary>
        /// <param name="applicationObject">The application object.</param>
        public MultipleFinderAndReplacer( DTE2 applicationObject )
        {
            this.applicationObject = applicationObject;
        }

        private string pattern = string.Empty;

        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;
            }
        }

        private RegexOptions regexOptions = RegexOptions.None;

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

        private Stack itemsToProcess = new Stack();

        public Stack ItemsToProcess
        {
            get
            {
                return itemsToProcess;
            }
            set
            {
                itemsToProcess = value;
            }
        }

        private int matchContextBeforeLineCount;

        public int MatchContextBeforeLineCount
        {
            get
            {
                return matchContextBeforeLineCount;
            }
            set
            {
                matchContextBeforeLineCount = value;
            }
        }

        private int matchContextAfterLineCount;

        public int MatchContextAfterLineCount
        {
            get
            {
                return matchContextAfterLineCount;
            }
            set
            {
                matchContextAfterLineCount = value;
            }
        }

        private string fileTypes = string.Empty;

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

        private bool includeSubDirectories;

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

        private bool displayOnlyFileNames;

        public bool DisplayOnlyFileNames
        {
            get
            {
                return displayOnlyFileNames;
            }
            set
            {
                displayOnlyFileNames = value;
            }
        }

        private bool keepModifedFilesOpen;

        public bool KeepModifedFilesOpen
        {
            get
            {
                return keepModifedFilesOpen;
            }
            set
            {
                keepModifedFilesOpen = value;
            }
        }

        private bool continueProcessing;

        public bool ContinueProcessing
        {
            get
            {
                return continueProcessing;
            }
            set
            {
                continueProcessing = value;
            }
        }

        /// <summary>
        /// Finds the next match
        /// </summary>
        public void FindNext()
        {
            bool noMatchInFile;

            while ( UpdateCurrentFile() && !finderAndReplacer.AttemptMatchInCurrentFile( out noMatchInFile ) )
            {
                this.itemsToProcess.Pop();

                if ( this.itemsToProcess.Count == 0 )
                {
                    this.finderAndReplacer.ShowMessage( "Passed the end of the document.", true );
                }
            }
        }

        /// <summary>
        /// Performs a static find all operation starting in the specifed directory.  Results are written to an output window pane.
        /// </summary>
        /// <param name="findAllDirectory">The starting directory.</param>
        public void FindAll( object findAllScope )
        {
            ValidateOutputWindowPane();
            ValidateFinderAndReplacer();

            string stringScope;

            if ( findAllScope is Project )
            {
                stringScope = ( (Project)findAllScope ).Name;
            }
            else
            {
                stringScope = findAllScope.ToString();
            }

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

            this.matchCount = 0;
            this.fileCount = 0;
            this.matchedFileCount = 0;

            ProcessAll( findAllScope, false );

            if ( this.displayOnlyFileNames )
            {
                this.outputWindowPane.OutputString( string.Format( @"  Matching files: {0,4}   Total files searched: {1,4}", this.matchedFileCount, this.fileCount ) );
            }
            else
            {
                this.outputWindowPane.OutputString( string.Format( @"  Matches: {0,4}   Matching files: {1,4}   Total files searched: {2,4}", this.matchCount, this.matchedFileCount, this.fileCount ) );
            }
        }

        /// <summary>
        /// Performs a static find all or replace all operation in the specifed directory.  Recursively processes sub-directories if
        /// this.includeSubDirectories is true.
        /// </summary>
        /// <param name="directory">The directory.</param>
        /// <param name="replace">Indicates if this is a replace all operation.</param>
        private void FindAndReplaceAllInFiles( string directory, bool replace )
        {
            string[] fileTypeArray = this.fileTypes.Split( ';' );

            foreach ( string fileType in fileTypeArray )
            {
                // search each file
                foreach ( string fullFilename in Directory.GetFiles( directory, fileType ) )
                {
                    if ( this.continueProcessing )
                    {
                        if ( replace )
                        {
                            ReplaceAllInFile( fullFilename );
                        }
                        else
                        {
                            FindAllInFile( fullFilename );
                        }
                    }
                }
            }

            if ( this.includeSubDirectories )
            {
                // search sub-directories
                foreach ( string directoryName in Directory.GetDirectories( directory ) )
                {
                    FindAndReplaceAllInFiles( directoryName, replace );
                }
            }
        }

        /// <summary>
        /// Process all files in the provided scope.
        /// </summary>
        /// <param name="scope">The scope of the operation.</param>
        /// <param name="replace">Indicates if this is a replace all operation.</param>
        private void ProcessAll( object scope, bool replace )
        {
            if ( scope is Project )
            {
                ProcessAllInProject( (Project)scope, replace );
            }
            else
            {
                if ( File.Exists( scope.ToString() ) )
                {
                    if ( replace )
                    {
                        ReplaceAllInFile( scope.ToString() );
                    }
                    else
                    {
                        FindAllInFile( scope.ToString() );
                    }
                }
                else if ( scope.ToString() == Strings.ENTIRE_SOLUTION )
                {
                    List<Project> projects = new List<Project>();

                    foreach ( Project project in this.applicationObject.Solution.Projects )
                    {
                        projects.Add( project );
                    }

                    projects.Sort( new ProjectComparer() );

                    if ( ( (Array)this.applicationObject.ActiveSolutionProjects ).Length > 0 )
                    {
                        Project activeProject = (Project)( (Array)this.applicationObject.ActiveSolutionProjects ).GetValue( 0 );

                        // if the currently active project is in the list of projects, adjust the list so it will be 
                        // processed first
                        if ( activeProject != null && projects.Contains( activeProject ) )
                        {
                            int currentProjectIndex = projects.IndexOf( activeProject );

                            List<Project> currentProjectAndAfters = projects.GetRange( currentProjectIndex, projects.Count - currentProjectIndex );

                            projects.RemoveRange( currentProjectIndex, projects.Count - currentProjectIndex );

                            projects.InsertRange( 0, currentProjectAndAfters );
                        }
                    }

                    projects.Reverse();

                    foreach ( Project project in projects )
                    {
                        if ( this.continueProcessing )
                        {
                            ProcessAllInProject( project, replace );
                        }
                    }
                }
                else if ( scope.ToString() == Strings.ALL_OPEN_DOCUMENTS )
                {
                    List<string> filenames = new List<string>();

                    foreach ( Document document in this.applicationObject.Documents )
                    {
                        if ( document.Object( string.Empty ) is TextDocument )
                        {
                            filenames.Add( document.FullName );
                        }
                    }

                    filenames.Sort();

                    foreach ( string filename in filenames )
                    {
                        if ( this.continueProcessing )
                        {
                            if ( replace )
                            {
                                ReplaceAllInFile( filename );
                            }
                            else
                            {
                                FindAllInFile( filename );
                            }
                        }
                    }
                }
                else if ( Directory.Exists( scope.ToString() ) )
                {
                    FindAndReplaceAllInFiles( scope.ToString(), replace );
                }
            }
        }

        /// <summary>
        /// Performs a static find all operation on the specified project.
        /// </summary>
        /// <param name="project">The project.</param>
        /// <param name="replace">Indicates if this is a replace all operation.</param>
        private void ProcessAllInProject( Project project, bool replace )
        {
            if ( project.ProjectItems != null )
            {
                foreach ( ProjectItem projectItem in project.ProjectItems )
                {
                    if ( this.continueProcessing )
                    {
                        if ( projectItem.Kind == Constants.vsProjectItemKindPhysicalFolder || projectItem.Kind == Constants.vsProjectItemKindVirtualFolder )
                        {
                            ProcessAllInProjectDirectory( projectItem, replace );
                        }
                        else if ( projectItem.Kind == Constants.vsProjectItemKindPhysicalFile )
                        {
                            string projectFilePath = projectItem.get_FileNames( 0 );

                            if ( File.Exists( projectFilePath ) )
                            {
                                if ( replace )
                                {
                                    ReplaceAllInFile( projectFilePath );
                                }
                                else
                                {
                                    FindAllInFile( projectFilePath );
                                }
                            }
                        }
                        else if ( projectItem.Kind == Constants.vsProjectItemKindSolutionItems )
                        {
                            if ( projectItem.SubProject != null )
                            {
                                ProcessAllInProject( projectItem.SubProject, replace );
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Performs a static find all operation on the specified project directory.
        /// </summary>
        /// <param name="projectDirectory">The project directory.</param>
        /// <param name="replace">Indicates if this is a replace all operation.</param>
        private void ProcessAllInProjectDirectory( ProjectItem projectDirectory, bool replace )
        {
            foreach ( ProjectItem projectDirectoryItem in projectDirectory.ProjectItems )
            {
                if ( this.continueProcessing )
                {
                    if ( projectDirectoryItem.Kind == Constants.vsProjectItemKindPhysicalFile )
                    {
                        string projectFilePath = projectDirectoryItem.get_FileNames( 0 );

                        if ( File.Exists( projectFilePath ) )
                        {
                            if ( replace )
                            {
                                ReplaceAllInFile( projectFilePath );
                            }
                            else
                            {
                                FindAllInFile( projectFilePath );
                            }
                        }
                    }
                    else if ( projectDirectoryItem.Kind == Constants.vsProjectItemKindPhysicalFolder || projectDirectoryItem.Kind == Constants.vsProjectItemKindVirtualFolder )
                    {
                        ProcessAllInProjectDirectory( projectDirectoryItem, replace );
                    }
                }
            }
        }

        /// <summary>
        /// Finds all matches in the specified file and prints the results to an output window pane.
        /// </summary>
        /// <param name="filename">The filename.</param>
        private void FindAllInFile( string filename )
        {
            string source = string.Empty;

            Application.DoEvents();

            this.finderAndReplacer.ShowMessage( string.Format( "Searching '{0}'...", filename ), false );

            try
            {
                using ( StreamReader streamReader = new StreamReader( filename ) )
                {
                    source = streamReader.ReadToEnd();
                    this.fileCount++;
                }

                Regex regex = new Regex( this.pattern, this.regexOptions );
                MatchCollection matches = regex.Matches( source );

                if ( matches.Count > 0 )
                {
                    this.matchCount += matches.Count;
                    this.matchedFileCount++;
                }

                if ( this.displayOnlyFileNames )
                {
                    if ( matches.Count > 0 )
                    {
                        this.outputWindowPane.OutputString( string.Format( "  {0}{1}", filename, Environment.NewLine ) );
                    }
                }
                else
                {
                    foreach ( Match match in matches )
                    {
                        int lineNumber = MatchContextUtils.GetLineCount( source, match.Index );

                        string matchContext = MatchContextUtils.GetMatchContextString( source, match.Index, match.Index + match.Length, this.matchContextBeforeLineCount, this.matchContextAfterLineCount );

                        string lineEnd = "";

                        if ( matchContext.IndexOf( '\n' ) < matchContext.LastIndexOf( '\n' ) )
                        {
                            lineEnd = Environment.NewLine;
                        }

                        this.outputWindowPane.OutputString( string.Format( "  {0}({1}):{2}{3}", filename, lineNumber, lineEnd, matchContext ) );
                    }
                }
            }
            catch ( Exception ex )
            {
                MessageBox.Show( ex.Message, "RegexFindAndReplace Error!" );
            }

            this.finderAndReplacer.ShowMessage( "", false );
        }

        /// <summary>
        /// Performs and find and replace on the specified file and prints the results to an output window pane, via a MatchEvaluator delegate.
        /// </summary>
        private void ReplaceAllInFile( string filename )
        {
            string source = string.Empty;

            Application.DoEvents();

            ValidateFinderAndReplacer();

            if ( this.showReplaceAllOutput )
            {
                this.finderAndReplacer.ShowMessage( string.Format( "Searching '{0}'...", filename ), false );
            }

            try
            {
                Encoding encoding = Encoding.Default;

                using ( StreamReader streamReader = new StreamReader( filename ) )
                {
                    source = streamReader.ReadToEnd();
                    encoding = streamReader.CurrentEncoding;
                    this.fileCount++;
                }

                this.fileSource = source;

                this.matchedFileCount++;

                this.replaceAllRegex = new Regex( this.pattern, this.regexOptions );
                this.replacementCountForCurrentFile = 0;

                string replacementSource = this.replaceAllRegex.Replace( source, new MatchEvaluator( ReplaceAllMatchEvaluator ) );

                if ( this.replacementCountForCurrentFile > 0 )
                {
                    // if the current file is open, replace the text in that file
                    if ( this.applicationObject.ItemOperations.IsFileOpen( filename, Constants.vsViewKindAny ) || this.keepModifedFilesOpen )
                    {
                        try
                        {
                            Window currentFileWindow = this.applicationObject.ItemOperations.OpenFile( filename, Constants.vsViewKindTextView );

                            if ( currentFileWindow != null )
                            {
                                if ( currentFileWindow.Document != null )
                                {
                                    TextSelection textSelection = (TextSelection)currentFileWindow.Document.Selection;

                                    int selectionStartOffset = textSelection.AnchorPoint.AbsoluteCharOffset;
                                    int selectionEndOffset = textSelection.ActivePoint.AbsoluteCharOffset;

                                    string previouslySelectedText = textSelection.Text;

                                    textSelection.SelectAll();

                                    textSelection.Insert( replacementSource, (int)( vsInsertFlags.vsInsertFlagsContainNewText | vsInsertFlags.vsInsertFlagsCollapseToEnd ) );

                                    if ( selectionStartOffset < replacementSource.Length - 1 )
                                    {
                                        try
                                        {
                                            textSelection.MoveToAbsoluteOffset( selectionStartOffset, false );

                                            if ( selectionEndOffset < replacementSource.Length - 1 )
                                            {
                                                textSelection.MoveToAbsoluteOffset( selectionEndOffset, true );
                                            }

                                            if ( textSelection.Text != previouslySelectedText )
                                            {
                                                textSelection.MoveToAbsoluteOffset( selectionStartOffset, false );
                                            }
                                        }
                                        catch
                                        {
                                            textSelection.Cancel();
                                        }
                                    }
                                    else
                                    {
                                        textSelection.Cancel();
                                    }
                                }
                            }
                        }
                        catch ( Exception ex )
                        {
                            MessageBox.Show( ex.Message, "RegexFindAndReplace Error!" );
                        }
                    }
                    else
                    {
                        using ( StreamWriter streamWriter = new StreamWriter( filename, false, encoding ) )
                        {
                            streamWriter.Write( replacementSource );
                        }
                    }
                }
            }
            catch ( Exception ex )
            {
                MessageBox.Show( ex.Message, "RegexFindAndReplace Error!" );
            }

            if ( this.showReplaceAllOutput )
            {
                this.finderAndReplacer.ShowMessage( "", false );
            }
        }

        /// <summary>
        /// MatchEvaluator used to update the output pane during a replace all operation
        /// </summary>
        private string ReplaceAllMatchEvaluator( Match match )
        {
            string replacement = match.Result( this.replaceAllPattern );

            this.replacementCountForCurrentFile++;
            this.matchCount++;

            if ( this.showReplaceAllOutput )
            {
                try
                {
                    if ( this.displayOnlyFileNames )
                    {
                        this.outputWindowPane.OutputString( string.Format( "  {0}{1}", this.currentFilePath, Environment.NewLine ) );
                    }
                    else
                    {
                        int lineNumber = MatchContextUtils.GetLineCount( this.fileSource, match.Index );

                        string matchContext = MatchContextUtils.GetReplacementContextString( this.fileSource, match.Index, match.Index + match.Length, replacement, this.matchContextBeforeLineCount, this.matchContextAfterLineCount );

                        string lineEnd = "";

                        if ( matchContext.IndexOf( '\n' ) < matchContext.LastIndexOf( '\n' ) )
                        {
                            lineEnd = Environment.NewLine;
                        }

                        this.outputWindowPane.OutputString( string.Format( "  {0}({1}):{2}{3}", this.currentFilePath, lineNumber, lineEnd, matchContext ) );
                    }
                }
                catch ( Exception ex )
                {
                    MessageBox.Show( ex.Message, "RegexFindAndReplace Error!" );
                }
            }

            return replacement;
        }

        /// <summary>
        /// Replaces the current match and finds the next one.
        /// </summary>
        /// <param name="replacePattern">The replace pattern.</param>
        public void ReplaceNext( string replacePattern )
        {
            bool noMatchInFile;

            while ( UpdateCurrentFile() && !finderAndReplacer.AttemptReplaceInCurrentFile( replacePattern, out noMatchInFile ) )
            {
                this.itemsToProcess.Pop();
            }
        }

        /// <summary>
        /// Performs a replace all operation on each item in this.itemsToProcess.
        /// </summary>
        /// <param name="replacePattern">The replace pattern.</param>
        /// <param name="replaceScope">The scope of the replacement.</param>
        /// <param name="showOutput">Indicates if the replacment results are to be written to the output pane.</param>
        public void ReplaceAll( object replaceScope, string replacePattern, bool showOutput )
        {
            this.replaceAllPattern = replacePattern;
            this.showReplaceAllOutput = showOutput;

            if ( showOutput )
            {
                ValidateOutputWindowPane();

                this.outputWindowPane.Activate();
                this.outputWindowPane.Clear();
                this.outputWindowPane.OutputString( string.Format( @"Replace all ""{0}"", ""{1}"", {2}""{3}"", ""{4}""{5}", this.Pattern, replacePattern, GetRegexOptionsString(), replaceScope, this.fileTypes, Environment.NewLine ) );

                this.matchCount = 0;
                this.fileCount = 0;
                this.matchedFileCount = 0;
            }

            ProcessAll( replaceScope, true );

            if ( showOutput )
            {
                if ( this.displayOnlyFileNames )
                {
                    this.outputWindowPane.OutputString( string.Format( @"  Matching files: {0,4}   Total files searched: {1,4}", this.matchedFileCount, this.fileCount ) );
                }
                else
                {
                    this.outputWindowPane.OutputString( string.Format( @"  Total replaced: {0,4}   Matching files: {1,4}   Total files searched: {2,4}", this.matchCount, this.matchedFileCount, this.fileCount ) );
                }
            }
        }

        /// <summary>
        /// Finds the next valid file for processing.
        /// </summary>
        /// <param name="attemptOpen">Indicates if the file should be opened in the editor for interactive finding and replacing.</param>
        /// <returns>true if a valid file is available; false otherwise</returns>
        private bool UpdateCurrentFile()
        {
            bool foundValidFile = false;

            if ( this.itemsToProcess.Count > 0 )
            {
                ValidateFinderAndReplacer();

                object itemToProcess = this.itemsToProcess.Peek();

                bool processNextItem = false;

                do
                {
                    List<string> filenames = new List<string>();
                    List<string> directories = new List<string>();

                    processNextItem = false;

                    bool fileIsNew = false;

                    // if the current item is a string, it is a file or a directory
                    if ( itemToProcess is string )
                    {
                        if ( File.Exists( itemToProcess.ToString() ) )
                        {
                            if ( this.currentFilePath != itemToProcess.ToString() )
                            {
                                this.currentFilePath = itemToProcess.ToString();

                                fileIsNew = true;
                            }
                            else
                            {
                                foundValidFile = true;
                            }
                        }
                        else if ( Directory.Exists( itemToProcess.ToString() ) )
                        {
                            this.itemsToProcess.Pop();

                            if ( this.includeSubDirectories )
                            {
                                // put sub-directories on the stack to be processed later
                                foreach ( string directory in Directory.GetDirectories( itemToProcess.ToString() ) )
                                {
                                    directories.Add( directory );
                                }

                                directories.Sort();
                                directories.Reverse();

                                foreach ( string directory in directories )
                                {
                                    this.itemsToProcess.Push( directory );
                                }
                            }

                            string[] fileTypeArray = this.fileTypes.Split( ';' );
                            
                            // get the valid files to process
                            foreach ( string fileType in fileTypeArray )
                            {
                                foreach ( string filename in Directory.GetFiles( itemToProcess.ToString(), fileType ) )
                                {
                                    filenames.Add( filename );
                                }
                            }

                            AddFileNamesToItemsToProcess( filenames );

                            if ( this.itemsToProcess.Count > 0 )
                            {
                                itemToProcess = this.itemsToProcess.Peek();

                                if ( File.Exists( itemToProcess.ToString() ) )
                                {
                                    // go around again, so the File condition does the work
                                    processNextItem = true;
                                }
                            }
                        }
                    }
                    else if ( itemToProcess is Project )
                    {
                        this.itemsToProcess.Pop();

                        Project project = (Project)itemToProcess;

                        // look at each ProjectItem
                        foreach ( ProjectItem projectItem in project.ProjectItems )
                        {
                            this.itemsToProcess.Push( projectItem );
                        }

                        if ( this.itemsToProcess.Count > 0 )
                        {
                            itemToProcess = this.itemsToProcess.Peek();
                            processNextItem = true;
                        }
                    }
                    else if ( itemToProcess is ProjectItem )
                    {
                        this.itemsToProcess.Pop();

                        ProjectItem projectItem = (ProjectItem)itemToProcess;

                        // if the ProjectItem is a folder, add all of the files in the folder
                        if ( projectItem.Kind == Constants.vsProjectItemKindPhysicalFolder || projectItem.Kind == Constants.vsProjectItemKindVirtualFolder )
                        {
                            foreach ( ProjectItem projectDirectoryItem in projectItem.ProjectItems )
                            {
                                this.itemsToProcess.Push( projectDirectoryItem );
                            }
                        }
                        else if ( projectItem.Kind == Constants.vsProjectItemKindPhysicalFile )
                        {
                            string projectFilePath = projectItem.get_FileNames( 0 );

                            if ( File.Exists( projectFilePath ) )
                            {
                                this.itemsToProcess.Push( projectFilePath );

                                foreach ( ProjectItem childProjectItem in projectItem.ProjectItems )
                                {
                                    this.itemsToProcess.Push( childProjectItem );
                                }
                            }
                        }

                        if ( this.itemsToProcess.Count > 0 )//&& ( File.Exists( this.itemsToProcess.Peek().ToString() ) || Directory.Exists( this.itemsToProcess.Peek().ToString() ) ) )
                        {
                            itemToProcess = this.itemsToProcess.Peek();
                            processNextItem = true;
                        }
                    }

                    // if we are now working with a new file, check to see if it contains a match before an other work is done with it
                    if ( fileIsNew )
                    {
                        bool fileContainsMatch = false;

                        using ( StreamReader reader = new StreamReader( this.currentFilePath ) )
                        {
                            string source = reader.ReadToEnd();

                            fileContainsMatch = Regex.Match( source, this.pattern, this.regexOptions ).Success;
                        }

                        if ( fileContainsMatch )
                        {
                            try
                            {
                                Window currentFileWindow = this.applicationObject.ItemOperations.OpenFile( this.currentFilePath, Constants.vsViewKindTextView );

                                if ( currentFileWindow != null )
                                {
                                    this.finderAndReplacer.MoveToStartInTextWindow();
                                }
                            }
                            catch ( Exception ex )
                            {
                                MessageBox.Show( ex.Message, "RegexFindAndReplace Error!" );
                            }

                            foundValidFile = true;
                        }
                        else
                        {
                            this.itemsToProcess.Pop();

                            if ( this.itemsToProcess.Count > 0 )
                            {
                                itemToProcess = this.itemsToProcess.Peek();

                                this.currentFilePath = null;

                                // go around again to process the next item
                                processNextItem = true;
                            }
                        }
                    }
                    else if ( foundValidFile && !processNextItem ) // if we the current file is still in play, make sure it hasn't been closed
                    {
                        if ( !this.applicationObject.ItemOperations.IsFileOpen( this.currentFilePath, Constants.vsViewKindTextView ) )
                        {
                            try
                            {
                                Window currentFileWindow = this.applicationObject.ItemOperations.OpenFile( this.currentFilePath, Constants.vsViewKindTextView );

                                if ( currentFileWindow != null )
                                {
                                    this.finderAndReplacer.MoveToStartInTextWindow();
                                }
                            }
                            catch ( Exception ex )
                            {
                                MessageBox.Show( ex.Message, "RegexFindAndReplace Error!" );
                            }
                        }
                    }
                }
                while ( processNextItem || ( this.itemsToProcess.Count > 0 && !File.Exists( itemToProcess.ToString() ) ) );
            }

            return foundValidFile;
        }

        /// <summary>
        /// Sorts and adjusts a list of file names before adding them to the itemsToProcess stack
        /// </summary>
        private void AddFileNamesToItemsToProcess( List<string> filenames )
        {
            filenames.Sort();

            // if the currently open file is in the list of file names, adjust the list so it will be 
            // processed first
            if ( this.applicationObject.ActiveDocument != null && filenames.Contains( this.applicationObject.ActiveDocument.FullName ) )
            {
                int currentFileIndex = filenames.IndexOf( this.applicationObject.ActiveDocument.FullName );

                List<string> currentFileAndAfters = filenames.GetRange( currentFileIndex, filenames.Count - currentFileIndex );

                filenames.RemoveRange( currentFileIndex, filenames.Count - currentFileIndex );

                filenames.InsertRange( 0, currentFileAndAfters );
            }

            // reverse the list
            filenames.Reverse();

            foreach ( string filename in filenames )
            {
                this.itemsToProcess.Push( filename );
            }
        }

        /// <summary>
        /// Validates the FinderAndReplacer.
        /// </summary>
        private void ValidateFinderAndReplacer()
        {
            if ( finderAndReplacer == null || !( finderAndReplacer.Pattern == this.pattern && finderAndReplacer.RegexOptions == this.regexOptions ) )
            {
                InitializeFinderAndReplacer();
            }
        }

        /// <summary>
        /// Initializes the FinderAndReplacer.
        /// </summary>
        private void InitializeFinderAndReplacer()
        {
            if ( finderAndReplacer == null )
            {
                finderAndReplacer = new FinderAndReplacer( this.applicationObject );
            }

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

        /// <summary>
        /// Validates the output window pane.
        /// </summary>
        private void ValidateOutputWindowPane()
        {
            if ( this.outputWindowPane == null )
            {
                this.outputWindowPane = this.applicationObject.ToolWindows.OutputWindow.OutputWindowPanes.Add( "Regex Find and Replace" );
            }
        }

        /// <summary>
        /// Gets the RegexOptions as a string.
        /// </summary>
        /// <returns>RegexOptions as a string</returns>
        private string GetRegexOptionsString()
        {
            StringBuilder regexOptionsStringBuilder = new StringBuilder();

            foreach ( RegexOptions regexOption in new RegexOptions[] { 
                RegexOptions.IgnoreCase, 
                RegexOptions.Multiline, 
                RegexOptions.Singleline, 
                RegexOptions.ExplicitCapture, 
                RegexOptions.IgnorePatternWhitespace, 
                RegexOptions.ECMAScript, 
                RegexOptions.RightToLeft, 
                RegexOptions.CultureInvariant } )
            {
                if ( ( this.regexOptions & regexOption ) == regexOption )
                {
                    if ( regexOptionsStringBuilder.Length > 0 )
                    {
                        regexOptionsStringBuilder.Append( " | " );
                    }

                    regexOptionsStringBuilder.Append( string.Format( "RegexOptions.{0}", regexOption.ToString() ) );
                }
            }

            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)

Share

About the Author

jhillman
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.

| Advertise | Privacy | Mobile
Web03 | 2.8.140905.1 | Last Updated 12 Oct 2009
Article Copyright 2008 by jhillman
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid