Home > VSX > Writing to the VS ErrorList

Writing to the VS ErrorList

The Shell provides a class to gain access to the ErrorList pane : ErrorListProvider. It implements the Add/Remove of ErrorList items in the ErrorList pane. To gain access to this provider it expects a ServiceProvider in the constructor. This provider can be the GlobalService of the Package class.

An ErrorList item is a class (called ErrorTask) where you can set all needed properties to navigate to the location in the document, categorize it and show the message.

So, it’s a quite simple and natural way of showing items in the ErrorList. I’ll show you the details now.

The simpliest approach is to create a helper class that implements the IServiceProvider interface; the implementation body of this interface (GetService(Type)) is a call to the Package.GetGlobalService(serviceType). Now we enabled the construction of the ErrorListProvider with this helper class.

public class ErrorListHelper : IServiceProvider
{
   public object GetService(Type serviceType)
    {
        return Package.GetGlobalService(serviceType);
    }
}

Now we can instantiate the ErrorListProvider: it is important here to give the provider a name and a new guid. Try to avoid making a new (= new guid and name) ErrorListProvider for each call. Consider making a singleton to access the helper class, if so make it scope the package only (for example by making the class generic).

public class ErrorListHelper : IServiceProvider
{
    public ErrorListProvider GetErrorListProvider()
    {
        ErrorListProvider provider = new ErrorListProvider(this);//this implementing IServiceProvider
        provider.ProviderName = {name scoping package};
        provider.ProviderGuid = {guid scoping package};
        return provider;
    }
}

Now that we have the ErrorListProvider in place, we have to make the helper class to add and remove items from the list: this includes, creating a ErrorTask object and handle the event to navigate to the context document’s position.

Lets start taking a look on how to create a ErrorTask object:

public void Write(
            TaskCategory category,
            TaskErrorCategory errorCategory,
            string context, //used as an indicator when removing
            string text,
            string document,
            int line,
            int column)
{
    ErrorTask task = new ErrorTask();
    task.Text = text;
    task.ErrorCategory = errorCategory;
    //The task list does +1 before showing this numbers
    task.Line = line - 1;
    task.Column = column - 1;
    task.Document = document;
    task.Category = category;

    if(! string.IsNullOrEmpty(document))
    {
        //attach to the navigate event
        task.Navigate += NavigateDocument;
    }   
    GetErrorListProvider().Tasks.Add(task);//add it to the errorlistprovider

}

Now that we have the ErrorTask in place, we can take a look at the handling of the Navigate event. This is a bit of code that I have centralized in another helper class to be reusable.

void NavigateDocument(object sender, EventArgs e)
{
    Task task = sender as Task;
    if (task == null)
    {
        throw new ArgumentException("sender");
    }
    //use the helper class to handle the navigation
    ShellContext.OpenDocumentAndNavigateTo(task.Document, task.Line, task.Column);
}

What it must cover:

  • get the VS service to open the document (IVsShellOpenDocument)
  • open the document via the OpenDocumentViaProject; this gives you a pointer to the frame (IVsWindowFrame) hosting the document
  • use the frame to get the VsTextBuffer
  • get the VS service that manages the textbuffer (VsTextManager) and use it to navigate to the position in the document

Here is the peace of code that does this:

    public static void OpenDocumentAndNavigateTo(
                                    string path, int line, int column)
    {
        IVsUIShellOpenDocument openDoc =
            Package.GetGlobalService(typeof(IVsUIShellOpenDocument))
                    as IVsUIShellOpenDocument;
        if (openDoc == null)
        {
            return ;
        }
        IVsWindowFrame frame;
        Microsoft.VisualStudio.OLE.Interop.IServiceProvider sp;
        IVsUIHierarchy hier;
        uint itemid;
        Guid logicalView = VSConstants.LOGVIEWID_Code;
        if (ErrorHandler.Failed(
            openDoc.OpenDocumentViaProject(path, ref logicalView, out sp, out hier, out itemid, out frame))
            || frame == null)
        {
            return ;
        }  
        object docData;  
        frame.GetProperty((int)__VSFPROPID.VSFPROPID_DocData, out docData);  

        // Get the VsTextBuffer  
        VsTextBuffer buffer = docData as VsTextBuffer;  
        if (buffer == null )  
        {  
            IVsTextBufferProvider bufferProvider = docData as IVsTextBufferProvider;  
            if (bufferProvider != null )  
            {  
                IVsTextLines lines;  
                ErrorHandler.ThrowOnFailure(bufferProvider.GetTextBuffer(out lines));  
                buffer = lines as VsTextBuffer;  
                Debug.Assert(buffer != null, "IVsTextLines does not implement IVsTextBuffer");  
                if (buffer == null)  
                {  
                   return ;  
                }  
            }  
        } 
        // Finally, perform the navigation.  
        IVsTextManager mgr = Package.GetGlobalService(typeof(VsTextManagerClass))
             as IVsTextManager;  
        if (mgr == null)  
        {  
            return;  
        }  
        mgr.NavigateToLineAndColumn(buffer, ref logicalView, line, column, line, column);  
    }

To remove the ErrorTask from the list, make use of the same Guid/Name to re-instantiate the ErrorListProvider.  The context argument in the write method can be used to remove all ErrorTasks for this context. The handling with the context is not covered here, because this post is focussed on the element to make this work.

When removing you should use the SuspendRefresh() before removing and ResumeRefresh() after the items are removed to avoid flickering.

 With all this parts paste together it should do the job, for me it did ;-)

Categories: VSX Tags: ,
  1. Luke
    September 15, 2010 at 08:46 | #1

    Hello,

    Thanks for the post, this is very useful information that I haven’t been able to find anywhere.. But I am a little lost as to how the ErrorTask is added to the ErrorList – The Write function provided just creates the object, (which I haven’t been able to get to appear on the error list).

    I would be extremely greatful if you could provide a full code example to download or give me a pointer? Thankyou very much.

    • September 15, 2010 at 09:02 | #2

      Hey Luke,

      thanks for the comment. There was a line of code missing in the Write method (last line). I have update the post and include it.
      Have fun !

  2. Martin Lemburg
    February 24, 2011 at 16:19 | #3

    Hi,

    first … thanks for your code example!
    I searched a lot and found only a few!

    But I don’t get it to work (in Visual Basic 2010).

    The problem is, that accessing the errorListProvider to add a task fails with the error message, that the IVsErrorListProvide is NOT installed.

    Do you have an idea, what to do?

    Thanks,

    Martin

    • February 24, 2011 at 22:10 | #4

      Try to make use of a singleton to keep the instance alive. Something like:
      This forces you to write to the same errorlist (package scope).

      private ErrorListProvider _errorListProvider = null;

      public static ErrorListProvider GetErrorListProvider()
      {
      if(_errorListProvider == null)
      {
      _errorListProvider = new ErrorListProvider(this);
      _errorListProvider.ProviderName = {name scoping package}; <- keep const
      _errorListProvider.ProviderGuid = {guid scoping package};
      return provider;
      }
      return _errorListProvider;
      }

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.