How do I implement a custom Text marker?




A: Aha! Custom text markers are one of those cool features lacking adequate documentation on how to implement it. I feel that implementing one is really very straight forward and hope you would be able to use to your heart’s content.


 


Providing custom markers text markers essentially boils down to implementing three components


 


·          A text marker service that proffers the service. Implements IVsTextMarkerTypeProvider


·          The custom text marker. Implements IVsPackageDefinedTextMarkerType and IVsMergeableUIItem.


·          And optionally a call back object so shell can call to notify of some events that implements IVsTextMarkerClient


 


Implementing Text Marker Service


Implementation of IVsTextMarkerTypeProvider essentially allows you to proffer your marker service and returns an instance of your marker when requested. The marker is usuallly identified by a MarkerId(DWORD). This value can be obtained from the text manager. You should do this typically when you create a marker instance returned by  IVsTextMarkerTypeProvider::GetTextMarkerType


 


                       









Obtaining the marker ID


CComPtr<IVsTextManager> pTextMgr = _Module.GetIVsTextManager();


pTextMgr->GetRegisteredMarkerTypeID(&guidCodeMarker, &m_markerID);


pTextMgr.Release();


 


 


 









Implementing the marker service


STDMETHODIMP CAnnotationTextMarkerSvc::GetTextMarkerType(


                                           GUID* pguidMarker,


                                           IVsPackageDefinedTextMarkerType** ppMarkerType)


{


            if(*pguidMarker == guidCodeMarker)


                        return <your_marker_type>


            return E_FAIL;


}


 


 


 









Proffer the Code marker service


CComPtr<IProfferService> srpProffer;


DWORD dwCookie;


if((SUCCEEDED(_Module.QueryService(SID_SProfferService,


                                                         IID_IProfferService,


                                                         (void **)&srpProffer))) && (srpProffer != NULL))


{


       srpProffer->ProfferService(<<guidMyMarkerService>>,


                                               (IServiceProvider *) this, &dwCookie);


}


 


 


 


Implementing the TextMarker


 


IVsPackageDefinedTextMarkerType  and IVsMergeableUIItem  are the two interfaces required to be implemented by a custom Text marker  


 


IVsPackageDefinedTextMarkerType – This is implemented to determine marker information applicable to your custom marker type. This provides information ranging from default fonts and colors to actually doing the drawing of the glyph. This object is what specifies what the marker type should be and where the marker glyph should show up.


 


Couple of rules that you need to comply with when implementing


IVsPackageDefinedMarkerType::DrawGlyphWithColors


 


·          Do not draw anything outside of the passed RECT


·          Because the size of line can change on the fly use GDI primitives rather than bitmaps as they won’t scale well with  size changes


·          Do not draw any text.


 


IVsMergeableUIItem – This is needed to have your marker participate in the Environment->Fonts and Colors Options page so you can take full advantage of all the font and color options. Essentially you get the font and color support for free.


 


The priority value returned by the IVsMergeableUIItem::GetMergingPriority is used to decide what name to show for the marker when two markers are found with the same canonical name. For non- Microsoft component the priority should be between 0 and 0x1000. If you are not doing any localization this value should be negative.


 


 


You create an instance of the marker object and return it to the shell via: IVsTextMarkerTypeProvider::GetTextMarkerType


 


Getting called back:


If you interested in being informed of changes to an individual marker then implement IVsTextMarkerClient. For e.g if you want to send back tooltips or put up special context menu items for your marker. You would hand this object to the shell when you make a call to IVsTextLines::CreateLineMarker.


 


 









Create a text marker client that will be called back


CComObject<CAnnotationTextMarkerClient>* pTMClient;


CComObject<CAnnotationTextMarkerClient>::CreateInstance(&pTMClient);


 


           









Create the marker


IfFailGo(pLines->CreateLineMarker (


            m_markerID,


            line,           // starting line


            0,              // starting character index within the line (must be <= length of line)


            line,           // ending line


            len,           // ending character index within the line (must be <= length of line)


            pTMClient, // client object for call backs


            NULL));


 


Registration:


Here is what you need to register for the text marker and related service.









Registration script


NoRemove ‘Text Editor’


{


            NoRemove ‘External Markers’


            {


                        ForceRemove ‘{<YOUR_MARKER_TYPE_GUID>}’ = s ‘My Sample Text Marker’


                        {


                                    val Service     = s ‘{<YOUR_MARKER_SERVICE_GUID>}’


                                    val DisplayName = s ‘My Sample Text Marker’


                                    val Package     = s ‘{<PACKAGE_GUID>}’


                        }


            }


}


 


NoRemove Services


{


            ForceRemove ‘{<YOUR_MARKER_SERVICE_GUID>}’ = s ‘%<PACKAGE_GUID>e%’


            {


                        val Name = s ‘My Sample Text Marker Service’


            }


}


 


 


Thanks


Dr. eX


 



Comments (5)

  1. Dmitry Shaporenkov says:

    Hi,

    I’ve implemented custom text markers just as you recommend, and all works fine except for one subtle issue:

    my custom text markers seem to have bad influence on debugger markers (such as

    breakpoints &

    current statement pointer). The symptom is that debugger markers often

    retain even after actions

    that should remove them. For example, when I click on the left gutter to

    remove a breakpoint,

    breakpoint is indeed removed (as can be seen from

    Debug->Windows->Breakpoints window) but the corresponding

    text marker still presents in the text. It gets removed after some UI events

    like mouse wheeling, moving the caret etc –

    so far I’ve failed to figure out exactly which events cause deletion of the

    debugger marker. The same situation is observed

    with the current statement pointer. Both issues don’t reproduce always, but

    in a permanent manner, and really disturb

    users.

    If you could provide an insight on this problem, it would be really great.

    Thanks,

    Dmitry

  2. Ed Dore says:

    I’ve been working on a demo editor package that implements custom markers, but haven’t seen this particular behavior.

    The BP tooltip doesn’t get dismissed when you click on the BP to remove it. You can see this behavior with just the generic code editor.

    You see this same behavior if you create a custom marker on top of a BP marker. But I’m not sure if this is the behavior you’re referring to.