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