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