Common API

Parnassus Plugins Common API

Introduction

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’.

Unit PnPluginAPICommon

Enumerable types

These two types are used by all collections (lists, etc) to make sure they are enumerable using for..in syntax.  They are specified here instead of reusing Delphi’s IEnumerator<T> because IEnumerator<T> inherits from IEnumerator, a design decision making it troublesome to implement.

Read-only list

This defines a read-only list of T.  It is used to expose items, without allowing items to be added or removed.  (For example, a list of bookmarks is a read-only list; bookmarks are added or deleted through other methods.)

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.

Multicast events

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.

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.