Plugins - SwensenSoftware/fseye GitHub Wiki

Introduction

Plugins are dlls that contain types that implement two interfaces: IPlugin and IWatchViewer. IPlugins produce IWatchViewers from traditional FsEye tree view watches to provide alternative views of a watch value. Plugins are deployed either along side FsEye.dll or in the plugins sub-folder (preferred) (FsEye automatically loads these upon start-up, though plugins may also be programmatically registered or removed).

FsEye includes two plugins out-of-the-box: FsEye.TreeView.Plugin.dll and FsEye.PropertyGrid.Plugin.dll. The former is simply the traditional FsEye TreeView-based watch viewer wrapped as a plugin. The latter wraps the WinForms PropertyGrid control.

For GUI Users

Plugin watch viewers are displayed in tabs on the right-hand-side of a split panel (the "plugin panel") with the traditional FsEye watch viewer kept on the left-hand-side (the "main panel"). When no plugin watch tabs are open, the plugin panel is hidden. When a watch in the main panel is right-clicked, you will be presented with a context menu containing a Send To menu item (which may be hidden or disable based on the context, such as whether the selected watch has its value loaded) which has the following organization

Send To -> PluginA -> New
                      -
		      PluginA 1
		      PluginA 2
	-> PluginB -> New
		      -
     		      PluginB 1

If we chose Send To -> PluginA -> New we would send the selected watch to a new watch viewer instance for the given plugin.

If we chose Send To -> PluginA -> PluginA 1 we would send the selected watch to the existing watch viewer instance for PluginA (replacing or updating it according to the rules implemented by the plugin author).

The created or updated plugin watch viewer tab will receive receive focus.

Various options for closing plugin tabs are available by right-clicking a given tab. These are Close, Close Others, and Close All. Close Others will be disabled if there is only one tab. When all tabs are closed, the plugin panel will become hidden.

For API Users

The following public classes have been renamed in FsEye 2.0.0 to reflect the growing breadth of functionality they now handle:

  • WatchPanel -> EyePanel
  • WatchForm -> EyeForm

The following types now expose a public property PluginManager which is used to programmatically manipulate plugins: Eye (the primary surface for manipulating FsEye within FSI), EyeForm, and EyePanel.

The PluginManager object is responsible for managing plugins. As mentioned previously, plugin authors implement two interfaces, IPlugin and IWatchViewer. These interfaces are wrapped by two record types, ManagedPlugin and ManagedWatchViewer. The following are the definitions for these types (with some members removed for simplicity):

///Represents a plugin watch viewer being managed by the PluginManager
type ManagedWatchViewer =
  {///The unique ID of the watch viewer instance 
   ID: string;
   ///The watch viewer instance which is being managed
   WatchViewer: IWatchViewer;
   ///The owning ManagedPlugin
   ManagedPlugin: ManagedPlugin;}
///Represents a plugin being managed by the PluginManager
and ManagedPlugin =
  {///The plugin being managed
   Plugin: IPlugin;
   ///The owning plugin manager
   PluginManager: PluginManager;}
  with
    member ManagedWatchViewers : seq<ManagedWatchViewer>
  end
///Manages FsEye watch viewer plugins
and PluginManager =
  class
	///Initialize and load any plugins in the "plugins" folder relative to the executing assembly
    new : unit -> PluginManager
	///Register the given plugin and return the managed plugin wrapping it. If a managed plugin wrapping a plugin of the same name exists, 
    ///removes it (and all of its associated managed watch viewers).
    member RegisterPlugin : plugin:IPlugin -> ManagedPlugin
    ///Remove the given managed plugin (and all of its managed watch viewers).
    member RemoveManagedPlugin : mp:ManagedPlugin -> unit
    ///Remove the managed plugin (and all of its managed watch viewers) by name.
    member RemoveManagedPlugin : name:string -> unit
    ///Remove the given managed watch viewer, disposing the watch viewer's Control.
    member RemoveManagedWatchViewer : mwv:ManagedWatchViewer -> unit
    ///Remove the managed watch viewer by id, disposing the watch viewer's Control.
    member RemoveManagedWatchViewer : id:string -> unit
    ///Create a new watch viewer for the given managed plugin, sending the given label, value and type.
    ///Returns the ManagedWatchViewer which wraps the created watch viewer.
    member
      SendTo : managedPlugin:ManagedPlugin * label:string * value:obj *
               valueTy:System.Type -> ManagedWatchViewer
    ///Send the given label, value and type to the given, existing managed watch viewer.
    member
      SendTo : mwv:ManagedWatchViewer * label:string * value:obj *
               valueTy:System.Type -> unit
    member ManagedPlugins : seq<ManagedPlugin>
    member ManagedWatchViewers : seq<ManagedWatchViewer>
    member WatchAdded : IEvent<ManagedWatchViewer>
    member WatchRemoved : IEvent<ManagedWatchViewer>
    member WatchUpdated : IEvent<ManagedWatchViewer>
  end

For API users interested in the IPlugin and IWatchViewer interfaces, see the section for plugin authors.

Note that adding, removing, and updating watches triggers the WatchAdded, WatchRemoved, and WatchUpdated events of the plugin manager. The GUI plugin panel listens to these events to add, update (select), and remove the tabs it controls.

For Plugin Authors

Plugin authors should be well versed in the documentation provided for all other types of users. The following are the definitions for the interfaces plugin authors are required to implement, they are found in the Swensen.FsEye namespace:

///Specifies a watch viewer interface, an instance which can add or update one or more watches with 
///a custom watch viewer control
type IWatchViewer =
  interface
    ///Add or update a watch with the given label, value, and type. Note: you can choose to 
    ///disregard the label and type if desired, but will almost certainly need the value.
    abstract member Watch : string * obj * System.Type -> unit
    ///The underlying watch viewer control. Exists as a property of IWatchViewer 
    ///since you may or may not own the control (i.e. you cannot directly implement IWatchViewer on the control).
    abstract member Control : System.Windows.Forms.Control
  end
  
///Specificies a watch view plugin, capable of creating watch viewer instances
type IPlugin =
  interface
    ///Create an instance of this plugin's watch viewer
    abstract member CreateWatchViewer : unit -> IWatchViewer
    ///Returns true or false depending on whether the given instance and its type (which we may need if 
    ///the instance is null) are watchable: if false, then FsEye will not allow creating a watch for a value 
    ///of the given type. Plugin authors should be mindful of the performance impact this method may have.
    abstract member IsWatchable : obj * System.Type -> bool
    //The name of the plugin
    abstract member Name : string
  end

these should be implemented as class libraries (.dll assembly files) targeting .NET 2.0 or greater with processor AnyCPU or x86 platforms (FsEye.dll, FsEye.PropertyGrid.Plugin.dll, and FsEye.TreeView.Plugin.dll all target .NET 4.0 with processor AnyCPU platform).

PluginManager.RegisterPlugin is a nice way to interactively develop and test plugins through FSI.

The out-of-the-box PropertyGrid (FsEye.PropertyGrid.Plugin/PropertyGridPlugin.fs) and TreeView (FsEye.TreeView.Plugin/TreeViewPlugin.fs) plugins may service as an implementation reference.

If you are developing a plugin, we'd be interested to hear about it! Eventually, we plan to have a "PluginGallery" wiki page to feature links to 3rd party FsEye plugins.

Screenshots

send to screen shot close screen shot