Komodo 4.0 Extensions

Posted by shanec on 2006-10-26 14:09
OS: All / Any | Product: Komodo | tags: extension komodo xpi
Question: 

How do I create an extension for Komodo 4.0?

Answer: 

This FAQ is aimed towards advanced users who have some prior knowledge of XUL, XBL and Firefox extensions. It may also be useful for first time extension developers.

Komodo is based on the Mozilla code base just like Firefox. This enables us to use the extension mechanism that Firefox uses. There are some slight differences in what needs to be done, but you can otherwise follow instructions for building Firefox extensions. The following websites are great references:

http://developer.mozilla.org
http://www.xulplanet.com

The following will guide you through creating a very simple "Hello World" extension for Komodo, and list a few extra Komodo specific features of extensions.

You first need to create three files.

.../myproject/install.rdf
.../myproject/chrome.manifest
.../myproject/content/overlay.xul

install.rdf is used by Komodo to collect basic information about the extension.

chrome.manifest is used by Komodo to know how to overlay the extension UI into the Komodo UI.

overlay.xul is required to add new UI elements to Komodo.

There will be more details on those files below, but first we'll show how to wrap these up into an XPI file that can be installed into Komodo.

The steps are simple (may require some modification for different platforms)

cd myproject
zip -Dr myproject.jar content
zip -Dr myproject-1.0.xpi myproject.jar install.rdf chrome.manifest

You now have an XPI file that can be installed into Komodo. If you have added other directories to your extension, the first zip command should include those directories.

example: zip -Dr myproject.jar content skin locale components

Now to the dirty details of those files. I've attached files to this FAQ that you can look at.

The important differences between an extension for Firefox and for Komodo is the targetApplication information for install.rdf, and the overlay elements in overlay.xul.

In install.rdf, the targetApplication element should use the id of {36E66FA0-F259-11D9-850E-000D935D3368}. The minVersion should be 4.0, and I would suggest 4.0.* for the maxVersion.

The chrome.manifest file is required if you are overlaying any new UI elements, such as a menu, into the Komodo UI. Here is an example:

content myproject jar:myproject.jar!/content/ xpcnativewrappers=yes
overlay chrome://komodo/content/komodo.xul chrome://myproject/content/overlay.xul

The overlay file for a simple hello world is best looked at in the attached file. The important part is the element id attributes, which need to match id attributes in komodo.xul. You can open komodo.xul from your Komodo installation to see what is available, but if you just need to attach a menu to the tools menu, use a menuitem element with the id "menu_ToolsPopup"

There are a few customizations of the extension directory structure for Komodo.

1. you can have a components directory (as you can in Firefox), but with Komodo you can use Python components through PyXPCOM (JavaScript components are also supported). See the PyXPCOM documentation on developer.mozilla.org for details. You can dig through the components directory in Komodo for examples, or use the Python PyXPCOM file template in Komodo (menu: File->New File...) to create a simple skeleton component.

2. you can have a "templates" directory where you can store new file templates that appear in the New File dialog.

3. you can have a "project-templates" directory where you can store new project kpz files that appear in the New Project dialog.

4. a "lexers" directory is used for adding the UDL lexres files, used for defining custom syntax coloring for languages in Komodo. There are utilities for creating UDL extensions.

We will be adding more special directories in the future on an as need basis.

AttachmentSize
chrome.manifest.txt153 bytes
install.rdf.txt689 bytes
overlay.xul.txt788 bytes

barrygently | Fri, 2007-03-09 07:58

Hi,

I've tried this with the files you indicated (replacing as necessary) and I get a message (when I try and install):

"My First Extension 1.0 could not be installed because it is not compatible with Komodo"

I'm using: Komodo Edit, version 4.0.2, build 275451

Does this mean that you can't have "custom" extensions in Komodo Edit?

Thanks,

Gary

shanec
ActiveState Staff
Fri, 2007-03-09 13:57

Komodo Edit has a different em? than Komodo IDE. So for an extension to support both Edit and IDE, you need to modify the install.rdf file with:
<em>
<Description>
    <!-- Komodo IDE's uuid -->
    <em>{36E66FA0-F259-11D9-850E-000D935D3368}</em>
    <em>4.0</em>
    <em>4.0.*</em>
</Description>
</em>
<em>
<Description>
    <!-- Komodo Edit's uuid -->
    <em>{b1042fb5-9e9c-11db-b107-000d935d3368}</em>
    <em>4.0</em>
    <em>4.0.*</em>
</Description>
</em>
Also, if you want to support Komodo 4.1, the maxVersion needs to be 4.1.*. Be aware, things are changing in 4.1 so don't do that unless you plan on testing with 4.1 releases.

barrygently | Fri, 2007-03-09 17:35

Thanks that did it ;)

Although (for everyone else) I also got a "not a valid GUID" error which only went away went when I made the id value look like an email address, this seems to be the same as what FireFox 1.5 does.

See: http://developer.mozilla.org/en/docs/Install_Manifests#id for more details.

barrygently | Sun, 2007-03-11 08:12

Just some information (that I've discovered) for those wanting to create a komodo extension.

1. I tried using a local file path for my javascript files (hoping that komodo will use that version rather than having to repackage and reinstall the extension each time) but to no avail. Even if you use a local path the extension will be installed with a COPY of your javascript file(s).

2. Use the internal komodo error log and logging mechanism. To do this use try/catch blocks in each of your functions, i.e.

MyObject =
{

   myFunction : function (arg)
   {

       try
       {

          do_some_stuff ();

       } catch (ex) {

         log.exception (ex);

       }

   }

};

komodo will then log all the properties of the exception (providing everything you need to know, but not quite in the nicest format) to the komodo error log file.

3. Do a watch file on the komodo error log. This log file can be found (on windoze) in the Documents and Settings/[Username]/Application Data/ActiveState/KomodoEdit/4.0/host-main directory, it's called: pystderr.log.

I created a macro that will open the viewer in a single click. Note: to create the macro you can't just use macro record function, you need to provide a bit of javascript instead. I use:

var pathToLogFile = 'path_to_pystderr.log_file';

openWindowMultipleInstance("chrome://komodo/content/tail/tail.xul",
                                            "komodo_tail",
                                            defaultOpenOptions + ",resizable,scrollbars",
                                            pathToLogFile,
                                            window);

Also, the path parts has to be separated be / NOT \.

I'll post more as I discover it.

shanec
ActiveState Staff
Sun, 2007-03-11 10:23

Throughout the 4.x series, we'll be cleaning up much or our api's. There will be wrappers to provide backward compatibility to 4.0. As we move towards this, a "ko" namespace will start to emerge that will be the public APIs in Komodo.

I mention this because, openWindowMultipleInstance is one that will be deprecated. In our current development code, we have:

var openWindowMultipleInstance = window.openDialog;

For your example, you should use

var pathToLogFile = 'path_to_pystderr.log_file';
window.openDialog("chrome://komodo/content/tail/tail.xul",
      "_blank",
      "chrome,all,close=yes,resizable,scrollbars",
      pathToLogFile,
      window);

shanec
ActiveState Staff
Sun, 2007-03-11 10:24

I'm not sure what you mean by:

1. I tried using a local file path for my javascript files (hoping that komodo will use that version rather than having to repackage and reinstall the extension each time) but to no avail. Even if you use a local path the extension will be installed with a COPY of your javascript file(s).

barrygently | Sun, 2007-03-11 16:51

Thanks for that, I'll change the call ;)

For the javascript file, basically in my overlay file I wanted to have the javascript I'm using stored on the local disk, rather than in the .jar file. I was hoping then that instead of having to go through the rather painful uninstall/repackage/install cycle which I'm currently having to do, I could restart and the latest version of the javascript file would be loaded when the overlay is loaded.

So I wanted to do:

<overlay id="qopenOverlay"
         xmlns?="http://www.w3.org/1999/xhtml"
         xmlns?="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

  <script src="file:///d:/qopen/content/js/QOpen.js" type="application/x-javascript"/>

</overlay>

However the extension manager seems to take a copy of the file and then install it. I think it's saving it to the host-main/XRE/Cache directory, but I can't be sure (if I remove the files in that directory it seems to make no difference). I know that when I reload komodo the changes aren't picked up.

I'm gonna try and find out if there is some option that tells the extension manager to not cache the file, i.e. to load it from the local file system each. That would speed up development no end.

shanec
ActiveState Staff
Sun, 2007-03-11 17:15

ok, it's pretty easy to make a non-jarred extension. that allows you to do a few things, such as avoid uninstall/reinstall to update and test your changes.

The trick is the "yourextension.manifest" file. It defines what the paths are for Mozilla to find the chrome files in the extension.

Lets take the Komodo chrome as an example. As of 4.0 we started JARing our chrome (helps performance and eases maintenance of the windows installer). For Komodo, we have the following "directories" for our chrome.

* chrome/komodo/content
* chrome/komodo/skin
* chrome/komodo/locale

Our komodo.manifest file looks like this:

content komodo jar:komodo.jar!/content/ xpcnativewrappers=yes
locale komodo en-US jar:komodo.jar!/locale/en-US/
skin komodo classic/1.0 jar:komodo.jar!/skin/

overlay chrome://mozapps/content/extensions/extensions.xul?type=extensions chrome://komodo/content/extmgr/em-overlay.xul

But when we're actually doing our "dev" builds of Komodo, we do not want to have to deal with the JAR file, so we use the file system directly. When we do that, we have to update our manifest to look like:

content komodo komodo/content/ xpcnativewrappers=yes
locale komodo en-US komodo/locale/en-US/
skin komodo classic/1.0 komodo/skin/

overlay chrome://mozapps/content/extensions/extensions.xul?type=extensions chrome://komodo/content/extmgr/em-overlay.xul

The difference is the paths used in "content", "locale" and "skin". When we jar, we have to point into the jar file:

jar:komodo.jar!/content/

When we do not jar, we have to provide a path relative to the chrome directory:

komodo/content/

Now, this is important, no other changes to your files are necessary. You do not use file:/// uri's anywhere, just keep using the chrome:/// uri's that you use when you jar your extension.

You can find detailed documentation on the manifest files at http://developer.mozilla.org/en/docs/Chrome_Registration

As a side-note, if you want to run Komodo with an unjarred chrome, you can unzip the jar file into a komodo directory:

cd /path/to/Komodo/lib/mozilla/chrome
mkdir komodo
cd komodo
unzip ../komodo
cd ..
mv komodo.jar komodo.jar.unused

Then change the komodo.manifest file to match what I provided above. This might make it easier to explore the Komodo chrome when you are writing extensions.

barrygently | Sun, 2007-03-11 22:49

Thanks. I'll try it out later.

Just one further question is there any documentation on the komodo DOM?

For instance I'd like to (will ;) write an extension that will display all the currently open files in a dropdown, grouped by file type/extension (this is another useful thing I've used in XEmacs but again it wasn't quite what I wanted). But of course I've no idea (without doing some extensive crawling around the komodo code) how to get hold of this list.

shanec
ActiveState Staff
Mon, 2007-03-12 08:24

You can look in uilayout.js for an example. See uilayout_updateWindowList which is used to update the Window menu with the list of files.

barrygently | Tue, 2007-03-13 06:44

I just have some further questions (if you'll indulge me ;)

My extension is now "basically" working, however there are a few tweaks that I'd like to make to it:

1. Do you know of any way to popup a popup at the caret position? For example I'd like to be able to popup it up at the location where the user is typing. I can't find anything that allows me to do it (i.e. I can't get the x,y position of the caret). The only thing I can think of doing is to have an item in the same location as the textbox and set the text and then measure it's width.

2. When I make the popup visible it then won't allow me to type in the textbox, i.e. I'd like it to behave "like" an autocomplete popup but without the strange behavior that comes along with it. I tried using ignorekeys on the popup (which makes it possible to type in the textbox) but then of course you can't use the arrow keys to navigate amongst the items which is behavior that I'd like to keep. Any ideas?

3. How can I make the key bindings work in the textbox? For example, I'd like to be able to the kill line command, but it doesn't seem to work.

4. How would I bind to a key sequence to make the "search bar" visible?

Thanks.

shanec
ActiveState Staff
Tue, 2007-03-13 13:41

1. komodo.editor is a scintilla instance. You can find the documentation at http://scintilla.sourceforge.net/ScintillaDoc.html

You want to look at doing something like:

pos = komodo.editor.currentPos
x = komodo.editor.pointXFromPosition(pos)
y = komodo.editor.pointYFromPosition(pos)

The problem is that those coordinates will be based on the top-left of the editor widget, and not the top left of the XUL window. So you have to figure out the offset as well.

Fortunately, you have access to komodo.view, which is the xul element that you need to get the top-left of. Every element has a boxObject (http://lxr.mozilla.org/mozilla1.8/source/layout/xul/base/public/nsIBoxOb...) that you can get coordinates from. I think what you will want is:

x += komodo.view.boxObject.screenX
y += komodo.view.boxObject.screenY

though, it may be boxObject.x and boxObject.y, you'll have to play a bit there.

2. I haven't tried a textbox in a popup, you might try something like:

<popup onpopupshowing="this.firstChild.focus();">
<textbox/>
</popup>

3. I dont think the kill line bindings wont work in a textbox, they are written against the scintilla widget. Is it an actual popup, or a dialog? If it's a dialog, you have to include some overlays to get keybindings at all.

4. Hmm, I'm not certain you can add a default binding easily. To make the keybinding system aware of your command, you need to do something like:

  <overlay>
  <commandset id="allcommands">
    <command id="Tasks?" oncommand="command_doCommand('cmd_helloworld')"/>
  </commandset>

  <menupopup id="popup_tools">
    <menuitem id="menu_${extension_name}"
              label="${extension_nice_name}"
              observes="Tasks?"
              class="menu-iconic-wide"/>
  </menupopup>
  </overlay>

The keybinding system will automatically find all commands in the commandset "allcommands". You need a command handler attached to the window also. See the perlapp extension for an example of using controllers and commands.

To get a default binding, you can *try* to add a default the following way. We used to do this, but changed, so I am not certain it will work.

<overlay>
<keyset id="widekeyset">
<key id="Meta+Ctrl+Alt+Shift+?" name="Meta+Ctrl+Alt+Shift+?" key="/" modifiers="meta,control,alt,shift" command="cmd_helloworld"/>
</keyset>
</overlay>

barrygently | Thu, 2007-03-15 06:02

Hi again,

Sorry, I don't think I made stuff clear ;)

In essence my extension behaves more like the find bar in Firefox. That is I'd like it to show up in the same place as the find bar in Firefox (I've currently got it showing there all the time) when a key sequence is pressed, for example: Ctrl+X, Ctrl+F.

It consists of a textbox and will have a couple of buttons, enclosed in a hbox. When the user presses the TAB key it will then display a popup with some dynamically added "menuitem"s of the files/directories that match for the current path in the textbox (which is inited to the cwd of the currently viewed file), they scroll through and select the one they want.

I am overlaying the komodo.xul.

Also, how would I get a reference to komodo.editor? I tried just using komodo.editor and that didn't work.

Do you know of anyway to change the background color of a textbox? I'd like to, when no matches are found, "flash" the background color of the textbox to tell the user. The javascript is easy but I can't find anything that tells me how to set the background color of the textbox (I can easily change font/font size etc). I've tried styling it to no avail.

Thanks (in advance!)

Gary

toddw
ActiveState Staff
Fri, 2007-03-30 12:03

komodo.editor is only a temporary namespace that is created and used by the macro system.

Outside of macros the way to access the current scimoz editor is:

var view = gViewMgr.currentView;
var document = gViewMgr.currentView.document;
var editor = gViewMgr.currentView.scintilla.scimoz;

To change the background color of the textbox, use css. Examples:

  • Through xul
    <textbox style="background-color: red;" />
  • Through dom manipulation
    texbox.setAttribute("style", "background-color: red;");

Cheers,
Todd

craigmarvelley | Mon, 2007-08-13 06:31

Hi,

I've tried to run the hello world example in Edit 4.1 without any joy - it installs fine but i can't see anything in the IDE. Where should I be seeing the menu item? I've updated the install.rdf file to include the appropriate Edit info/version - are the files supplied valid for the latest version?

Cheers,
Craig

craigmarvelley | Mon, 2007-08-13 07:07

I got it eventually. Looked at the komodo.xul file - in my version of Edit I had to update the overlay.xul file to use the latest references - 'allcommands' as the commandset id and 'popup_tools' as the menupopup id.

Cheers!

mblais | Sun, 2007-12-23 14:20

After I make a change to my extension I have to restart Firefox in order to get it to load the new version. Apparently FF caches all the extensions when it runs.

Is there a way to turn off FF's extension caching, to avoid having to restart FF every time I make a change to my extension?

johnm | Sun, 2007-12-23 15:19

You can use the extension developers extension: http://ted.mielczarek.org/code/mozilla/extensiondev/
There's a Reload Chrome entry in the Tools menu and there is also a toolbar button you can add. You need to toggle the extension debugging prefs before it will work though.
You can also set them manually here: http://developer.mozilla.org/en/docs/Setting_up_extension_development_en...

Make sure you also used a file pointing to your extension when you installed it so you don't need to repackage it every time.

mblais | Sun, 2007-12-23 16:03

Thanks - the developer link had exactly the info I needed (prefs) :-)