/// <summary> /// This is the delegate for BuildFinishedHandler events. /// </summary> /// <param name="sender"></param> /// <param name="buildEvent"></param> private void BuildFinishedHandler(object sender, BuildFinishedEventArgs buildEvent) { try { if (LogAtImportance(buildEvent.Succeeded ? MessageImportance.Low : MessageImportance.High)) { if (this.outputWindowPane != null) { Output(Environment.NewLine); } LogEvent(sender, buildEvent); } // BRIANMCN: // There are two reasons to call UIThread.Run. // The obvious one is when you have to call IVsBlahBlah that must be accessed on the UI thread. // That’s not the case here, here it’s for the less obvious reason that, whereas all the events // happening in this class (that happen on the MSBuild logger thread) have a chronological // ordering, most of those events transfer to the UIThread via UIThread.Run, and so we need to // preserve the ordering. UIThread.Run(delegate() { taskReporter.OutputTaskList(); }); } catch (Exception e) { Debug.Assert(false, "Problem logging buildfinished event: " + e.Message + " at " + e.TargetSite); // swallow the exception } }
private void Output(string s) { // Various events can call SetOutputLogger(null), which will null out "this.OutputWindowPane". // So we capture a reference to it. At some point in the future, after this build finishes, // the pane reference we have will no longer accept input from us. // But here there is no race, because // - we only log user-invoked builds to the Output window // - user-invoked buils always run MSBuild ASYNC // - in an ASYNC build, the BuildCoda uses UIThread.Run() to schedule itself to be run on the UI thread // - UIThread.Run() protects against re-entrancy and thus always preserves the queuing order of its actions // - the pane is good until at least the point when BuildCoda runs and we declare to MSBuild that we are finished with this build var pane = this.OutputWindowPane; // copy to capture in delegate UIThread.Run(delegate() { try { pane.OutputStringThreadSafe(s); } catch (Exception e) { Debug.Assert(false, "We would like to know if this happens; exception in IDEBuildLogger.Output(): " + e.ToString()); // Don't crash process due to random exception, just swallow it } }); }
/// <summary> /// Add the error/warning to the error list and potentially to the output window. /// </summary> private void AddToErrorList( BuildEventArgs errorEvent, string subcategory, string errorCode, string file, int startLine, int startColumn, int endLine, int endColumn) { if (file == null) { file = String.Empty; } bool isWarning = errorEvent is BuildWarningEventArgs; Shell.TaskPriority priority = isWarning ? Shell.TaskPriority.Normal : Shell.TaskPriority.High; TextSpan span; span.iStartLine = startLine; span.iStartIndex = startColumn; span.iEndLine = endLine < startLine ? span.iStartLine : endLine; span.iEndIndex = (endColumn < startColumn) && (span.iStartLine == span.iEndLine) ? span.iStartIndex : endColumn; if (OutputWindowPane != null && (this.Verbosity != LoggerVerbosity.Quiet || errorEvent is BuildErrorEventArgs)) { // Format error and output it to the output window string message = this.FormatMessage(errorEvent.Message); DefaultCompilerError e = new DefaultCompilerError(file, span.iStartLine, span.iStartIndex, span.iEndLine, span.iEndIndex, errorCode, message); e.IsWarning = isWarning; Output(GetFormattedErrorMessage(e)); } UIThread.Run(delegate() { IVsUIShellOpenDocument openDoc = serviceProvider.GetService(typeof(IVsUIShellOpenDocument)) as IVsUIShellOpenDocument; if (openDoc == null) { return; } IVsWindowFrame frame; IOleServiceProvider sp; IVsUIHierarchy hier; uint itemid; Guid logicalView = VSConstants.LOGVIEWID_Code; IVsTextLines buffer = null; // JAF // Notes about acquiring the buffer: // If the file physically exists then this will open the document in the current project. It doesn't matter if the file is a member of the project. // Also, it doesn't matter if this is an F# file. For example, an error in Microsoft.Common.targets will cause a file to be opened here. // However, opening the document does not mean it will be shown in VS. if (!Microsoft.VisualStudio.ErrorHandler.Failed(openDoc.OpenDocumentViaProject(file, ref logicalView, out sp, out hier, out itemid, out frame)) && frame != null) { object docData; frame.GetProperty((int)__VSFPROPID.VSFPROPID_DocData, out docData); // Get the text lines buffer = docData as IVsTextLines; if (buffer == null) { IVsTextBufferProvider bufferProvider = docData as IVsTextBufferProvider; if (bufferProvider != null) { bufferProvider.GetTextBuffer(out buffer); } } } // Need to adjust line and column indexing for the task window, which assumes zero-based values if (span.iStartLine > 0 && span.iStartIndex > 0) { span.iStartLine -= 1; span.iEndLine -= 1; span.iStartIndex -= 1; span.iEndIndex -= 1; } // Add error to task list var taskText = global::FSharp.Compiler.ErrorLogger.NewlineifyErrorString(errorEvent.Message); if (errorReporter != null) { errorReporter.ReportError2(taskText, errorCode, (VSTASKPRIORITY)priority, span.iStartLine, span.iStartIndex, span.iEndLine, span.iEndIndex, file); } }); }
/// <summary> /// Add the error/warning to the error list and potentially to the output window. /// </summary> private void AddToErrorList( BuildEventArgs errorEvent, string subcategory, string errorCode, string file, int startLine, int startColumn, int endLine, int endColumn) { if (file == null) { file = String.Empty; } bool isWarning = errorEvent is BuildWarningEventArgs; TaskPriority priority = isWarning ? TaskPriority.Normal : TaskPriority.High; TextSpan span; span.iStartLine = startLine; span.iStartIndex = startColumn; span.iEndLine = endLine < startLine ? span.iStartLine : endLine; span.iEndIndex = (endColumn < startColumn) && (span.iStartLine == span.iEndLine) ? span.iStartIndex : endColumn; if (OutputWindowPane != null && (this.Verbosity != LoggerVerbosity.Quiet || errorEvent is BuildErrorEventArgs)) { // Format error and output it to the output window string message = this.FormatMessage(errorEvent.Message); DefaultCompilerError e = new DefaultCompilerError(file, span.iStartLine, span.iStartIndex, span.iEndLine, span.iEndIndex, errorCode, message); e.IsWarning = isWarning; Output(GetFormattedErrorMessage(e)); } UIThread.Run(delegate() { IVsUIShellOpenDocument openDoc = serviceProvider.GetService(typeof(IVsUIShellOpenDocument)) as IVsUIShellOpenDocument; if (openDoc == null) { return; } IVsWindowFrame frame; IOleServiceProvider sp; IVsUIHierarchy hier; uint itemid; Guid logicalView = VSConstants.LOGVIEWID_Code; IVsTextLines buffer = null; // JAF // Notes about acquiring the buffer: // If the file physically exists then this will open the document in the current project. It doesn't matter if the file is a member of the project. // Also, it doesn't matter if this is an F# file. For example, an error in Microsoft.Common.targets will cause a file to be opened here. // However, opening the document does not mean it will be shown in VS. if (!Microsoft.VisualStudio.ErrorHandler.Failed(openDoc.OpenDocumentViaProject(file, ref logicalView, out sp, out hier, out itemid, out frame)) && frame != null) { object docData; frame.GetProperty((int)__VSFPROPID.VSFPROPID_DocData, out docData); // Get the text lines buffer = docData as IVsTextLines; if (buffer == null) { IVsTextBufferProvider bufferProvider = docData as IVsTextBufferProvider; if (bufferProvider != null) { bufferProvider.GetTextBuffer(out buffer); } } } // Need to adjust line and column indexing for the task window, which assumes zero-based values if (span.iStartLine > 0 && span.iStartIndex > 0) { span.iStartLine -= 1; span.iEndLine -= 1; span.iStartIndex -= 1; span.iEndIndex -= 1; } // Add new document task to task list DocumentTask task = new DocumentTask(serviceProvider, buffer, // May be null // This seems weird. Why would warning status make this a 'compile error'? // The “code sense” errors produce red squiggles, whereas the “compile” errors produce blue squiggles. (This is in line with C#’s pre-VS2008-SP1 behavior.) Swapping these two gives us a look consistent with that of the language service. isWarning ? MARKERTYPE.MARKER_COMPILE_ERROR : MARKERTYPE.MARKER_CODESENSE_ERROR, span, file, subcategory); // Add error to task list task.Text = Microsoft.FSharp.Compiler.ErrorLogger.NewlineifyErrorString(errorEvent.Message); task.Priority = priority; task.ErrorCategory = isWarning ? TaskErrorCategory.Warning : TaskErrorCategory.Error; task.Category = TaskCategory.BuildCompile; task.HierarchyItem = hierarchy; task.Navigate += new EventHandler(NavigateTo); if (null != this.TaskReporter) { this.taskReporter.AddTask(task); } else { this.taskProvider.Tasks.Add(task); } }); }