private void ExtractSelectionAndIncludes(EnvDTE.Document document, TrialAndErrorRemovalOptionsPage settings,
                                                 out ITextBuffer textBuffer, out Formatter.IncludeLineInfo[] includeLinesArray)
        {
            // Parsing.
            document.Activate();
            var documentTextView = VSUtils.GetCurrentTextViewHost();

            textBuffer = documentTextView.TextView.TextBuffer;
            string documentText = documentTextView.TextView.TextSnapshot.GetText();
            IEnumerable <Formatter.IncludeLineInfo> includeLines = Formatter.IncludeLineInfo.ParseIncludes(documentText, Formatter.ParseOptions.IgnoreIncludesInPreprocessorConditionals | Formatter.ParseOptions.KeepOnlyValidIncludes);

            // Optionally skip top most include.
            if (settings.IgnoreFirstInclude)
            {
                includeLines = includeLines.Skip(1);
            }

            // Skip everything with preserve flag.
            includeLines = includeLines.Where(x => !x.ShouldBePreserved);

            // Apply filter ignore regex.
            {
                string   documentName    = Path.GetFileNameWithoutExtension(document.FullName);
                string[] ignoreRegexList = RegexUtils.FixupRegexes(settings.IgnoreList, documentName);
                includeLines = includeLines.Where(line => !ignoreRegexList.Any(regexPattern =>
                                                                               new System.Text.RegularExpressions.Regex(regexPattern).Match(line.IncludeContent).Success));
            }
            // Reverse order if necessary.
            if (settings.RemovalOrder == TrialAndErrorRemovalOptionsPage.IncludeRemovalOrder.BottomToTop)
            {
                includeLines = includeLines.Reverse();
            }

            includeLinesArray = includeLines.ToArray();
        }
 public TrialAndErrorRemovalProject(TrialAndErrorRemovalOptionsPage settings)
 {
     this.settings        = settings;
     projectFiles         = new Queue <ProjectItem>();
     impl                 = new TrialAndErrorRemovalWithoutDialog();
     impl.OnFileFinished += OnDocumentIncludeRemovalFinished;
 }
        public bool PerformTrialAndErrorIncludeRemoval(EnvDTE.Document document, TrialAndErrorRemovalOptionsPage settings)
        {
            if (document == null)
            {
                return(false);
            }

            string reasonForFailure;

            if (VSUtils.VCUtils.IsCompilableFile(document, out reasonForFailure) == false)
            {
                Output.Instance.WriteLine("Can't compile file '{1}': {0}", reasonForFailure, document.Name);
                return(false);
            }

            if (WorkInProgress)
            {
                Output.Instance.ErrorMsg("Trial and error include removal already in progress!");
                return(false);
            }
            WorkInProgress = true;

            // Start wait dialog.
            IVsThreadedWaitDialog2 progressDialog = StartProgressDialog(document.Name);

            if (progressDialog == null)
            {
                return(false);
            }

            // Extract all includes.
            ITextBuffer textBuffer;

            Formatter.IncludeLineInfo[] includeLines;
            try
            {
                ExtractSelectionAndIncludes(document, settings, out textBuffer, out includeLines);
            }
            catch (Exception ex)
            {
                Output.Instance.WriteLine("Unexpected error while extracting include selection: {0}", ex);
                progressDialog.EndWaitDialog();
                return(false);
            }

            // Hook into build events.
            SubscribeBuildEvents();

            // The rest runs in a separate thread since the compile function is non blocking and we want to use BuildEvents
            // We are not using Task, since we want to make use of WaitHandles - using this together with Task is a bit more complicated to get right.
            outputWaitEvent.Reset();
            var removalThread = new System.Threading.Thread(() => TrialAndErrorRemovalThreadFunc(document, settings, includeLines, progressDialog, textBuffer));

            removalThread.Start();
            return(true);
        }
        private void TrialAndErrorRemovalThreadFunc(EnvDTE.Document document, TrialAndErrorRemovalOptionsPage settings,
                                                    Formatter.IncludeLineInfo[] includeLines, IVsThreadedWaitDialog2 progressDialog, ITextBuffer textBuffer)
        {
            int  numRemovedIncludes = 0;
            bool canceled           = false;

            try
            {
                int currentProgressStep = 0;

                // For ever include line..
                foreach (Formatter.IncludeLineInfo line in includeLines)
                {
                    // If we are working from top to bottom, the line number may have changed!
                    int currentLine = line.LineNumber;
                    if (settings.RemovalOrder == TrialAndErrorRemovalOptionsPage.IncludeRemovalOrder.TopToBottom)
                    {
                        currentLine -= numRemovedIncludes;
                    }

                    // Update progress.
                    string waitMessage  = $"Removing #includes from '{document.Name}'";
                    string progressText = $"Trying to remove '{line.IncludeContent}' ...";
                    progressDialog.UpdateProgress(
                        szUpdatedWaitMessage: waitMessage,
                        szProgressText: progressText,
                        szStatusBarText: "Running Trial & Error Removal - " + waitMessage + " - " + progressText,
                        iCurrentStep: currentProgressStep + 1,
                        iTotalSteps: includeLines.Length + 1,
                        fDisableCancel: false,
                        pfCanceled: out canceled);
                    if (canceled)
                    {
                        break;
                    }

                    ++currentProgressStep;

                    // Remove include - this needs to be done on the main thread.
                    Application.Current.Dispatcher.Invoke(() =>
                    {
                        using (var edit = textBuffer.CreateEdit())
                        {
                            if (settings.KeepLineBreaks)
                            {
                                edit.Delete(edit.Snapshot.Lines.ElementAt(currentLine).Extent);
                            }
                            else
                            {
                                edit.Delete(edit.Snapshot.Lines.ElementAt(currentLine).ExtentIncludingLineBreak);
                            }
                            edit.Apply();
                        }
                        outputWaitEvent.Set();
                    });
                    outputWaitEvent.WaitOne();

                    // Compile - In rare cases VS tells us that we are still building which should not be possible because we have received OnBuildFinished
                    // As a workaround we just try again a few times.
                    {
                        const int maxNumCompileAttempts = 3;
                        for (int numCompileFails = 0; numCompileFails < maxNumCompileAttempts; ++numCompileFails)
                        {
                            try
                            {
                                VSUtils.VCUtils.CompileSingleFile(document);
                            }
                            catch (Exception e)
                            {
                                Output.Instance.WriteLine("Compile Failed:\n{0}", e);

                                if (numCompileFails == maxNumCompileAttempts - 1)
                                {
                                    document.Undo();
                                    throw e;
                                }
                                else
                                {
                                    // Try again.
                                    System.Threading.Thread.Sleep(100);
                                    continue;
                                }
                            }
                            break;
                        }
                    }

                    // Wait till woken.
                    bool noTimeout = outputWaitEvent.WaitOne(timeoutMS);

                    // Undo removal if compilation failed.
                    if (!noTimeout || !lastBuildSuccessful)
                    {
                        Output.Instance.WriteLine("Could not remove #include: '{0}'", line.IncludeContent);
                        document.Undo();
                        if (!noTimeout)
                        {
                            Output.Instance.ErrorMsg("Compilation of {0} timeouted!", document.Name);
                            break;
                        }
                    }
                    else
                    {
                        Output.Instance.WriteLine("Successfully removed #include: '{0}'", line.IncludeContent);
                        ++numRemovedIncludes;
                    }
                }
            }
            catch (Exception ex)
            {
                Output.Instance.WriteLine("Unexpected error: {0}", ex);
            }
            finally
            {
                Application.Current.Dispatcher.Invoke(() => OnTrialAndErrorRemovalDone(progressDialog, document, numRemovedIncludes, canceled));
            }
        }