Creating a Simple Add-in

From $1
    Table of contents

    This walkthrough will guide you through the process of creating a simple add-in, introducing the fundamentals of the add-in system and some of MonoDevelop's core APIs.

    Introduction

    The MonoDevelop addin architecture is designed to allow you to extend any part of MonoDevelop, for example supporting a new language or version control system, or custom source editing commands. This walkthrough will take you through the basics of writing addins, by describing the creating of a simple addin that adds a command to insert the current date into the source editor.

    MonoDevelop is built around the concept of an extension tree. An addin is a set of extensions that plug into extension points defined in other addins, and can also define new extension points for other addins to extend. The whole of MonoDevelop is built this way, so there are many extension points available, and the core ones are described in the Extension Point Reference. MonoDevelop uses the Mono.Addins addin engine, so for advanced addin questions, refer to the Mono.Addins Reference Manual. You can also use Mono.Addins in your own applications.

    Defining the Addin

    An addin requires a manifest, which describes the addin, and defines the extensions and extension points in the addin. The manifest is an XML file with the extension .addin.xml.

    We start by defining the skeleton of the addin, the MonoDevelop.Samples.DateInserter.addin.xml file:

    <Addin namespace   = "MonoDevelop.Samples"
           id          = "DateInserter"
           name        = "Date Inserter"
           author      = "Michael Hutchinson"
           copyright   = "MIT/X11"
           url         = "http://www.monodevelop.com"
           description = "Provides a command to insert the date into the curent document."
           category    = "Source Editor Extensions"
           version     = "1.0">
    </Addin>

    The combined namespace and id should be unique among all MonoDevelop addins. The other attributes are self-explanatory, and many of them are optional, particularly if the addin does not define extension points, but it is good practice to fill them all out.

    Any nontrivial addin will include an assembly containing the classes required for implementing most extension points. The addin manifest can be embedded into this assembly as an embedded resource. Some extension points accept resources as arguments, so these can be embedded into the assembly too, and hence many addins consist of a single assembly. However, if the addin requires additional files or assemblies, these should be referenced in the Runtime element, so that the addin engine can ensure that they exist, and can package all of the addin's files into an mpack file for distribution. This addin requires no extra files, so we can omit this element.

    Next we must declare dependencies on the addins with the extension points we wish to use. We want to use the MonoDevelop.Ide addin, which contains many of the extension points and APIs for the general development environment. Ensure that the dependencies' versions match the targeted MonoDevelop version.

    <Addin ...>
    
      <Dependencies>
          <Addin id="::MonoDevelop.Ide" version="4.0"/>
      </Dependencies>
    
    </Addin>

    Note that the addin engine considers dependencies' full names to be relative to the addin's own namespace. If we had referenced the dependency as "MonoDevelop.Ide", the addin engine would have tried to find an addin called "MonoDevelop.Samples.MonoDevelop.Ide", and failed. To prevent this, we used the "::" qualifier to make it a global reference.

    The core addins and their extension points and APIs are described in the API Overview document, though this is not always kept up to date.

    Adding Extensions

    Now that the addin is defined, we can add some extensions.

    Each extension point has a unique path, so the extension is contained in and Extension element with a path attribute. This element may contain multiple extension nodes to plug into the extension point. The nodes' names and attributes depend are defined by the extension point. Some extension nodes can have child nodes, and they then become extension points in their own right. For example, the /MonoDevelop/Ide/Commands extension point accepts Command or Category nodes, and category nodes may contain commands or categories. Since MonoDevelop.Ide already has a category with the id "Edit" registered at the /MonoDevelop/Ide/Commands extension point, we can add a command directly to the resulting /MonoDevelop/Ide/Commands/Edit Extension point.

    <Addin ...>
    ...
    
    <Extension path = "/MonoDevelop/Ide/Commands/Edit">
        <Command id = "MonoDevelop.Samples.DateInserter.DateInserterCommands.InsertDate"
                 _label = "Insert Date"
                 _description = "Insert the current date" />
    </Extension>
    
    </Addin>

    This extension defines a command for the command system. The command ID should correspond to an enum value. The underscore prefixing the label and description attributes indicates that they're translatable using Gettext, but adding a Gettext translation catalog to the addin is beyond the scope
    of this introduction.

    MonoDevelop's command system provides ways to control the availability, visibility and handling of commands depending on context. Commands can also be assigned a keyboard shortcut and an icon. For more details, read the guide to the Command System. We will implement the command later in the walkthrough.

    Commands can be bound to keyboard shortcuts and can be inserted into menus. We're going to insert this command into the main Edit menu with another extension.

    <Addin ...>
    ...
    
    <Extension path = "/MonoDevelop/Ide/MainMenu/Edit">
        <CommandItem id="MonoDevelop.Samples.DateInserter.DateInserterCommands.InsertDate" />
    </Extension>
    
    </Addin>

    The add-in xml manifest is now complete. This is the whole file:

    <Addin namespace   = "MonoDevelop.Samples"
           id          = "DateInserter"
           name        = "Date Inserter"
           author      = "Michael Hutchinson"
           copyright   = "MIT/X11"
           url         = "http://www.monodevelop.com"
           description = "Provides a command to insert the date into the curent document."
           category    = "Source Editor Extensions"
           version     = "1.0">
    
        <Dependencies>
            <Addin id="::MonoDevelop.Ide" version="4.0"/>
        </Dependencies>
    
        <Extension path = "/MonoDevelop/Ide/Commands/Edit">
            <Command id = "MonoDevelop.Samples.DateInserter.DateInserterCommands.InsertDate"
                     _label = "Insert Date"
                     _description = "Insert the current date" />
        </Extension>
    
        <Extension path = "/MonoDevelop/Ide/MainMenu/Edit">
            <CommandItem id="MonoDevelop.Samples.DateInserter.DateInserterCommands.InsertDate" />
        </Extension>
    </Addin>

    Implementing the Command

    The command is now registered, but it doesn't do anything. For that, we need a command handler.  The simplest way to use it is with a default handler, which is a class that implements MonoDevelop.Components.Commands.CommandHandler.

    Add the following attribute to the Command element in the addin manifest:

    defaultHandler = "MonoDevelop.Samples.DateInserter.InsertDateHandler"

    So the Command element now looks like this: 

    <Extension path = "/MonoDevelop/Ide/Commands/Edit">
            <Command id = "MonoDevelop.Samples.DateInserter.DateInserterCommands.InsertDate"
                     _label = "Insert Date"                 
                     _description = "Insert the current date" 
                     defaultHandler = "MonoDevelop.Samples.DateInserter.InsertDateHandler"/>
        </Extension>

    Now create a new C# Library project called MonoDevelop.Samples.DateInserter, and add the MonoDevelop.Samples.DateInserter.addin.xml file to the project as an embedded resource. Set the project's target framework to .NET 4.0.

    In order to use the types from the MonoDevelop.Ide addin, edit the project's references, and references to MonoDevelop.Ide.dll and Mono.TextEditor.dll. The "assembly version" of these assemblies may be a little older than the actual addins - this is simply to enable newer MonoDevelop versions to load assemblies of addins that were built against older versions of MonoDevelop, if permitted by the addin manifest's dependencies. When you add more functionality to your addin, you may need to reference additional libraries such as MonoDevelop.Core.dll and gtk-sharp.dll.

    Add a new class to the project with the name we assigned as the command's default handler. It doesn't need to be public.

    using MonoDevelop.Components.Commands;
    
    namespace MonoDevelop.Samples.DateInserter
    {
        class InsertDateHandler : CommandHandler
        {
            protected override void Run ()
            {
            }
           
            protected override void Update (CommandInfo info)
            {
            }   
        }
    }

    We also need to define the enum for the command id.

    namespace MonoDevelop.Samples.DateInserter
    {
            public enum DateInserterCommands
            {
                    InsertDate,
            }
    }

    Now build and run the project. The resulting MonoDevelop.Samples.DateInserter.dll is your addin. To run it, find the addins directory. The location is dependent on the OS:

    Mac ~/Library/Application Support/XamarinStudio-4.0/LocalInstall/Addins
    Windows Vista/7 ~/AppData/Local/XamarinStudio-4.0/LocalInstall/Addins
    Linux ~/.local/share/MonoDevelop-4.0/LocalInstall/Addins

    Create a directory called MonoDevelop.Samples.DateInserter inside the Addins directory, and copy MonoDevelop.Samples.DateInserter.dll into it. Now run MonoDevelop, and you will see the new command in the edit menu.

    The command still doesn't do anything, as the command handler is a stub. First, we want to make sure that the command is only enabled when the active document is an editable text document.

    Many important services can be accessed via the MonoDevelop.Ide.Gui.IdeApp static class. The workbench represents the IDE window, including the Documents, which may contain multiple views. We query the active document to see whether any of the views offer the Mono.TextEditor.ITextEditorDataProvider interface.

    First, import the namespaces:

    using MonoDevelop.Ide.Gui;
    using MonoDevelop.Ide.Gui.Content;
    using Mono.TextEditor;

    Then alter the command handler's Update method. This method is queried whenever a command is shown in a menu or executed via keybindings. By changing the info object, you can disable the command or make it invisible, populate array commands, update the state of check or radio commands, and many other things. We are just going to disable it if we can't find an ITextEditorDataProvider in the active document.

    protected override void Update (CommandInfo info)
    {
        MonoDevelop.Ide.Gui.Document doc = IdeApp.Workbench.ActiveDocument;
        info.Enabled = doc != null && doc.GetContent<ITextEditorDataProvider> () != null;
    }

    Now we can implement the command's Run method, which, unsurprisingly, is called when the command is run.

    protected override void Run ()
    {
        MonoDevelop.Ide.Gui.Document doc = IdeApp.Workbench.ActiveDocument;
        var textEditorData = doc.GetContent<ITextEditorDataProvider> ().GetTextEditorData ();
        string date = DateTime.Now.ToString ();
        textEditorData.InsertAtCaret (date);
    }

    This uses the ITextEditorDataProvider directly - we know it's not null, since the Run method could not be called unless our Update method had enabled the command. It simply inserts the current date at the cursor position using one of the convenience methods of the text editor.

    Wrapping Up

    It's done! Rebuild the project, and copy MonoDevelop.Samples.DateInserter.dll to the addin directory again, overwriting the incomplete version from earlier.

    Now that you've built your first addin, try adding more features to it. For example, if the text editor has a selection, the addin could use that as a format string for the date. The reference version of the project that's attached to this document implements this feature.

    When you're ready to try more adventurous addin development, there are more articles that describe more advanced addin development, and a lot of the API is readily discoverable via code completion, once you know the general areas to look. If you can't work out how best to do something, take a look at
    an existing addin that does something similar, or email your question to the monodevelop-devel mailing list.

    Tag page
    • No tags

    Files (1)

    FileSizeDateAttached by 
    MonoDevelop.Samples.DateInserter.tar.bz2
    Completed Sample Addin
    3.46 kB17:02, 2 Apr 2009M.j.hutchinsonActions
    Viewing 7 of 7 comments: view all
    Worth to note: in project's Options/Compilation/Output, it is possible to specify directly the path /home/$USER/.config/MonoDevelop/addins/, avoiding to copy the file.
    Posted 03:21, 18 Jun 2009
    @Sylvain Nahas: thanks for your comments. If fixed all issues you reported.
    Posted 00:03, 19 Jun 2009
    I'm trying to play with this sample under MD from an last svn trunk and finally get this error at startup :
    monodevelop
    WARNING: The add-in 'MonoDevelop.DateInserter.Samples,2.0' is trying to extend '/MonoDevelop/Ide/Commands/Edit', but there isn't any compatible add-in defining this extension point
    WARNING: The add-in 'MonoDevelop.DateInserter.Samples,2.0' is trying to extend '/MonoDevelop/Ide/Commands/Edit/MonoDevelop.Samples.DateInserter.DateInserterCommands.InsertDate', but there isn't any compatible add-in defining this extension point
    WARNING: The add-in 'MonoDevelop.DateInserter.Samples,2.0' is trying to extend '/MonoDevelop/Ide/MainMenu/Edit', but there isn't any compatible add-in defining this extension point
    WARNING: The add-in 'MonoDevelop.DateInserter.Samples,2.0' is trying to extend '/MonoDevelop/Ide/MainMenu/Edit/MonoDevelop.Samples.DateInserter.DateInserterCommands.InsertDate', but there isn't any compatible add-in defining this extension point
    ATTENTION [2009-12-22 11:19:21Z]: Error creating composed icon gtk-execute___asm2__debug-overlay-22.png at size Button. Icon __asm2__debug-overlay-22.png is 20x20, expected 22x22.
    Posted 17:21, 22 Dec 2009
    Sencillo pero muy informativo :)

    Saludos!
    Posted 06:57, 18 Feb 2010
    Write very well!This foreigner to understand。
    Posted 16:54, 20 Oct 2010
    <Runtime>
    <Import assembly="MonoDevelop.Samples.DateInserte.dll"/>
    </Runtime>
    Posted 21:06, 20 Oct 2010
    The code for the simple addin can be found on Github: https://github.com/mhutch/MonoDevelop.Samples.DateInserter

    It seems that the addin manifest file *has* to be embedded as resource file otherwise the types won't be found in the assembly.
    Posted 17:33, 4 Sep 2013
    Viewing 7 of 7 comments: view all
    You must login to post a comment.
    Page last modified 20:16, 8 Mar 2013 by M.j.hutchinson