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