Parnassus Plugins Common API
Several Parnassus plugins provide a publicly accessible API, in order that third-party plugins can interoperate: access their data, invoke specific functionality, etc.
While each plugin specifies its own API, they rely on a common core set of interfaces:
- Enumerable types
- Read-only list
- Multicast events (or list of event sinks)
Types, methods names, etc are prefixed ‘PnAPI’.
IPnAPIEnumerator<T> = interface
function GetCurrent: T;
function MoveNext: Boolean;
property Current: T read GetCurrent;
IPnAPIEnumerable<T> = interface
function GetEnumerator: IPnEnumerator<T>;
IPnAPIReadOnlyList<T> = interface(IPnEnumerable<T>)
function GetItem(Index: Integer): T;
function GetCount : Integer;
property Items[Index: Integer]: T read GetItem; default;
property Count : Integer read GetCount;
It is enumerable using for..in syntax, and can otherwise have its elements accessed through the default property Items. There are 0..Count-1 elements in the list.
Many plugins or parts of a plugin can expose events, and those events often need to support multiple event handlers – in other words, rather than having On* event handlers where only a single event handler can be set, a list of event handlers is required. In addition, events are often logically related: if there is an event for a bookmark (say) being added, there is also one for it being removed.
The Parnassus API design is that instead of implementing individual event handlers, to implement an interface specifying an event sink for a defined family of events. That interface – the event sink object – is registered by adding it to the list of event sinks.
IPnAPIMulticastInterfaceList<T : IUnknown> = interface
procedure Add(const Item : T);
procedure AddAtStart(const Item : T);
procedure Remove(const Item : T);
A multicast interface list is a list of interfaces of a specific type. One example is IPnAPIBookmarkEvents. Items can be added or removed. Each addition should be matched by a removal.
Usually you only want to add or remove your event sink, but occasionally you want to ensure you get events before other event handlers. To do so, call AddAtStart. If multiple sinks are added via AddAtStart, the order of which is the very first is undefined. All items added via AddAtStart will be called before items added via Add.
There is no provision for preventing other event handlers also being notified of an event after your handler has received the notification. There is also no support for enumeration or accessing individual elements, since a consumer of the interface should only ever add or remove items it knows about.
It is essential that you call Remove for every Add. Not doing so will result in access violations on IDE shutdown or when the plugin is unloaded. If you get an access violation on IDE or plugin shutdown, this is the most likely cause.