Configuring and using jEdit for Haxe

jEdit is an open source extendable programmer's text editor suited to Haxe development without an IDE. It performs syntax highlighting, code completion, automatic imports, and more. It recognizes haxe and hxml files out of the box.

A new haxe plugin for jEdit has been released, and version 0.3 of the plugin has the following features:

- Code completion (using the haxe compiler).
- Automatic import inserts (either by scanning the whole source file or via a single import of the class at the cursor.
- Import with the "using" keyword of a class at the cursor.
- Build integration: compiler errors are listed and are linked to the source, so you can traverse build errors by just clicking the error, or via keyboard shortcut.
- Code tree displayed (via ctags parsing).
- Advanced: project specific build files, launch commands, haxe compiler, and std library.

Other plugins provide the following relevant features: (jEdit plugin name listed at the end)

- Code completion of any word in the current class file. (TextAutocomplete)
- Code completion of any Class in any source folder. (CtagsInterface)
- Code completion of haxe keywords (e.g. Dynamic, class etc). - Advanced
below).
- Navigate to a class member by starting to type its name Navigate to any class by starting to type its name. (CtagsInterface + macro below)
- Navigate to any class by starting to type its name. (CtagsInterface + macro below)
- Code snippets, with the tab key skipping through the sections. (SuperAbbrevs)

Plugins

jEdit can be extended using plugins and macros.

- HaxeSidekick (depends on ProjectViewer and CtagsInterface)
- CtagsInterface
- SuperAbbrevs
- TextAutocomplete

To install a plugin use jEdit's plugin manager by selecting Plugin Manager from the Plugins menu. From the dialog that appears switch to the Install tab. Check the plugins you want to install and press the Install button at the bottom left side of the window. The plugins will be downloaded and installed automatically. While it isn't really required, i recommend to restart jEdit to make sure everything is properly loaded.

Note that most plugins do not add any UI element to jEdit unless they are explicitly said to do so. Usually a plugins adds a new panel which can be shown using the plugin's submenu in the Plugins menu. By default a panel is in floating state, which means that the panel is inside its own window. To dock it, click on the small triangle at the top left corner of the panel and select one of the Dock at .... commands. This will make the panel part of the jEdit window. Personally i prefer to put the project management specific parts at the left side of the window and the building, errors, etc at the bottom side, thus having an IDE-like layout.

Additionally you may want to use the alternative toolbar placement so that the jEdit toolbar occupies the full top area of the jEdit's window instead of being above the editing area by clicking on the Alternate tool bar placement button in Global options' View option group.

CtagsInterface

Two extremely useful shortcuts I use are "Select current class field or function" and "Find class". Before you use these, you have to add the source files to the project, or one of its dependencies.

Go to Plugin Options->CtagsInterface->Projects and add your haxe projects to the list. It's useful to use dependencies, so that e.g. by creating a haxestd project you simply need to add that as a dependency to your other projects.

Once parsing and database are finished, you should be able to create and use the following actions:

Go to Plugin Options->CtagsInterface->Actions and define the following actions:

Action Name: List class members
Lucene Query: `_path:"{file}" AND (kind:function OR kind:variable)`
Query Type: "Search Prefix"
Call query immediately: true

Action Name: Class search
Lucene Query: `kind:class`
Query Type: "Search Prefix"
Call query immediately: false

Bind these actions to a shortcut, and you are able to very quickly navigate to any source location rapidly.

ProjectViewer

This plugin is a dependency of HaxeSidekick, so will be installed when you install the haxe plugin. This adds some simple project management features in jEdit by grouping files in projects and grouping projects in a hierarchy of project groups. This way you can keep your projects nicely organized - personally i have a specific project group called Flash games and inside i have all my projects related to games in Flash.

By default, the haxe plugin looks for a file ending with .hxml in the project root folder, and uses the first one it comes across as the project build folder. To change this, right click on the top level icon in the project viewer and select properties. Then select Haxe Paths and change the path.

The plugin provides a panel named Project Viewer which can be shown using the PluginsProject ViewerShow Project Viewer command. The panel is initially floating so you should dock it if you want. Inside the panel there are three tabs. All of them display the projects and project groups in a tree format. They differ in the way they display the project contents:

  • Folders will display the files as a tree hierarchy, like a file manager's tree panel.
  • Files will display the files in a flat format with all files under the project's node.
  • Working Files like the above, but will only display the files which are currently opened in jEdit.

The panel also has a combo box (drop down listbox) from which you can select the project or project group (which can be used to limit what the project tabs are displaying) and create new groups and projects.

When creating new projects keep these in mind:

  • If you use the macros below, you should specify a project root directory so that the macros can find the building and running scripts (i'll explain them below).
  • By default you need to add files in the projects manually. This can keep the projects clean and show only what you want, but it is a tedious process. I recommend you to enable the Enable automatic re-import feature in the project's Auto Reimport options.

You may also want to enable the Use system icons for files option from PluginsPlugin Options...Project ViewerGeneral Options.

A bit on building and running

With the configuration discussed so far you can do a lot from within jEdit and have a good development environment. Using a console window (command-line) and a haxe hxml file will be enough for many projects. However sometimes you need a more complex building process and the haxe compiler commands are not enough for this. At this point you will need a builder script, which is a script that prepares your source code and a hxml file to be used with the haxe compiler.

Personally i use Python to write a build.py script that does all the preparation job. The simplest build.py script simply creates a compile.hxml file and calls the haxe compiler. The more complicated scripts can gather binary files, create a swfmill XML file and dummy Haxe classes for using these binary files from Flash 9. For my RayFaster 2 engine i use GNU M4 to preprocess the source code files and RayFaster 1 provides a Python class for the games that uses it, for building the files, adding binary and bitmap resources and other stuff.

Using a building script will give you much more power on what you can do with your code. You don't have to use Python of course, but personally i find Python to be a good choice because it is available almost everywhere and it is a very simple scripting language (note: of course one could use Haxe for this too, but i find Haxe too rigid and verbose for such uses - also the Haxe script should be compiled to neko and executed using a neko vm, something that complicates matters a bit).

Below is a simple build.py Python script that gathers binary resources:

#!/usr/bin/env python

import os
import sys
import glob

# the resources map
resources = {}

# fills the resources map
def gather_resources():
    # gather some data files
    for file in glob.glob('*.dat'):
        nameonly = os.path.splitext(file)[0]
        resources[file] = nameonly

# creates dummy Haxe class for a resource
def create_dummy_resource_class(resname):
    print 'Creating dummy Haxe class for ' +resname
    f = open('Res_' + resname + '.hx', 'w')
    f.write('class Res_' + resname + ' extends flash.utils.ByteArray { ')
    f.write('public function new(){ super(); } }\n')
    f.close()

# generates swfmill XML code for the resources and dummy Haxe classes
def generate_resources_xml_code():
    code = ""
    for res in resources:
        resname = resources[res]
        filename = res
        code = code + '<binary id="Res_' + resname + '" import="' + filename + '"/>'
        create_dummy_resource_class(resname)
    return code

# create the resources XML file for swfmill
def create_swfmill_xml_file():
    code = '<?xml version="1.0" encoding="iso-8859-1" ?>'
    code = code + '<movie width="640" height="480" framerate="60">'
    code = code + '<frame><library>' + generate_resources_xml_code() + '</library></frame>'
    code = code + '</movie>\n'
    f = open('resources.xml', 'w')
    f.write(code)
    f.close()

# create a helper Haxe class for getting resources by name
def create_resource_getter():
    f = open('ResSource.hx', 'wt')
    f.write('import flash.utils.ByteArray;\n')
    f.write('class ResSource {\n')
    for res in resources:
        f.write('    private static var In_' + resources[res] + ':Res_' + resources[res] + ';\n')
    f.write('    public static function get(name:String):ByteArray {\n')
    for res in resources:
        f.write('        if (name == "' + resources[res] + '") {\n')
        f.write('            if (In_' + resources[res] + ' == null) In_' + resources[res] + ' = new Res_' + resources[res] + '();\n')
        f.write('            In_' + resources[res] + '.position = 0;\n')
        f.write('            In_' + resources[res] + '.endian = flash.utils.Endian.BIG_ENDIAN;\n') # you can also use little
        f.write('            return In_' + resources[res] + ';\n')
        f.write('        } else\n')
    f.write('        return null;\n')
    f.write('    }\n')
    f.write('}\n\n')
    f.close()

# process resources
gather_resources()
create_swfmill_xml_file()
create_resource_getter()

# create resources.swf file
os.system('swfmill simple resources.xml resources.swf')

# create the hxml file
f = open('compile.hxml', 'w')
f.write('-swf dungeon.swf\n')
f.write('-swf-header 640:480:30:000000\n')
f.write('-swf-version 9\n')
f.write('-swf-lib resources.swf\n')
f.write('-main Dungeon\n')
f.close()

# create args
args = ""
for arg in sys.argv[1:]:
    args += arg + " "
print args

# compile the code
os.system("haxe compile.hxml " + args)

(note: this is a slightly modified version of the building script used for the Dungeon Knight flash game)

This building script gathers resource files (all the .dat files in the directory) and creates dummy Haxe classes for them and an utility class names ResSource which can be used to "load" resources like "ResSource.get('foo')". Then it creates a resources swf file using a generated swfmill XML file and a hxml file that uses it and compiles the code. Finally it gathers arguments passed to the script itself to the haxe compiler so one can use haxe compiler arguments such as "-debug" with the script.

Another script that can be used when developing Haxe programs and games is a running script. This is an extension to the building script like:

#!/usr/bin/env python

import os
import os.path

execfile("build.py")

if os.path.exists("dungeon.swf"):
    os.system("flashplayer dungeon.swf")

This code assumes that there is an executable called flashplayer in your system path that executes the standalone Flash Player. Personally i have renamed Adobe's standalone Debug Flash Player to flashplayer.exe and copied it to a directory that is in my path (C:\Apps\Bin). Of course you can modify the flashplayer command to point to your executable if you don't want to do all this.

This is a very simple running script that simply runs the SWF file if the compilation was successful (depending on how you create the swf file you might want to add a call to os.unlink in your building script to delete the original SWF file before trying to gather/generate/compile/obfuscate). Simply running this script is the same as using a "run" command in some IDE - and this is exactly what we will do with jEdit below.

However since this is also a script, it can be enhanced to do some interesting stuff. For example in my Rombo game my running script can take arguments that specify in which level the game will start, to skip the background music or to not include code for MochiGames' leaderboards. This modifies a Configuration.hx file which the code compiles against. The game's level editor calls this script to launch the game inside the level i'm editing so i can test levels quickly.

Using these two build.py and run.py scripts, one can make jEdit perform these tasks from within itself. To do this, a new plugin is required. This is the Console plugin which adds a system (and BeanShell) console to jEdit.

Console

To install the Console plugin follow the same steps as for installing the ProjectViewer plugin. Like PV, Console adds a new panel named Console. This panel (activated from PluginsConsoleConsole) contains (at least) two consoles: the System console and the BeanShell console. The System console provides a shell to your system's native command interpreter (this is cmd.exe under Windows and sh under UNIX).

With this plugin you can run the build.py and run.py scripts directly from jEdit: just go to the directory where they are located and run them (make sure your system is configured to open .py files using the Python interpreter). Since this is a full console you can pass extra arguments to the scripts without leaving jEdit.

The error list

Console provides an extra plugin called ErrorList which can parse the error messages from several tools. The haxe compiler error output is compatible with this plugin's regular expressions and so it can parse its errors and add them to its list. The plugin provides a panel called Error List which can be shown using PluginsErrorListError List. Personally i have this panel docked near the Console panel.

You can configure the error list to appear when an error message is outputted in the Console using the plugin's Automatically show on error option in PluginsPlugin OptionsErrorList.

The BeanShell script macros

With the above you are ready to go and use jEdit as a full featured development environment, but if typing "build.py" and "run.py" in the console is a bit tedious. I prefer to just click on a toolbar icon or press a shortcut key to do that. Fortunately with jEdit it is possible to extend the interface to add this feature.

The following BeanShell scripts can be installed in your .jedit directory's macros subdirectory (it is in the same place where you found the modes subdirectory to install the syntax file). You can download the script files from this site (note: the site is temporary down, here is an alternative direct link to the macros until i bring up the site again). Extract the .bsh and .inc files inside the macros subdirectory and select Rescan Macros from the Macros menu.

Now you will have two additional macros in the Macros menu:

  • Call Build Python Script this will call your build.py script, and
  • Call Run Python Script this will call your run.py script

These macros will try to locate the build.py and run.py files using the following criteria:

  • If the ProjectViewer plugin is installed and the currently active file is part of a project, the project's root directory is used as the place where the macro will look for the script.
  • If the ProjectViewer plugin is not installed or the currently active file is not part of a project, the macro will scan all the parent folders of the currently active file to see if the script is there.

If the script is not found an error message will be shown. Otherwise the macros will run the script inside the Console (in System console). Note that the script will change the current directory of the console to the directory where the building script is located before running the script.

Toolbar buttons

To add two toolbar buttons for launching these macros open Global Options and from the Tool Bar group select the item near where the buttons will be added and press the + button. Select the Command or macro: radio button and from the drop down list select Macros. Find and select the Call Build Python Script. Use a built-in icon for this (i chose 22x22/actions/application-run.png - yes the name doesn't fit but with the Tango theme that jEdit uses by default this is a gear which in my mind equals to "compile"). Repeat the same steps for the Call Run Python Script (for which i chose the 22x22/actions/media-playback-start.png icon because it has a "play" image, which in my mind equals to "run" :-P).

Use the up and down arrow buttons to move the toolbar buttons and maybe add one or two separators to make it fit in the toolbar.

Shortcuts

A similar process is needed for assigning shortcut keys. Using the Shortcuts group from Global Options select Macros from the Edit Shortcuts: drop down list. Find the two macros and double-click on the cell with the Primary shortcut header. This will make a dialog appear. Press the key you want to assign (i chose F11 for building and F5 for running - note that F11 is assigned to Toggle fullscreen but since i never use that, i decided to override it) and click Ok.

Now you can write, build and run projects from within jEdit!

Additional plugins

Some additional plugins you might want to install to jEdit to make your life easier are:

  • Archive - adds support for several archive file formats (zip, tar, etc) to jEdit's VFS so it can edit them directly.
  • BufferTabs - adds tabs above the editing area.
  • Calculator - an advanced calculator with the ability to define custom functions.
  • Code2HTML - exports your colored code as HTML for sharing it with others.
  • FTP - adds FTP and SFTP support for jEdit's VFS to edit files over FTP and SSH.
  • HexEdit - a hex editor. If you're working with custom data formats you will need this.
  • InfoViewer - much improved help viewer (some plugins even require this) and embedded web browser.
  • Navigator - you can use it to go back and forward to several places in your source code. It is recommended to add special toolbar buttons for this.
  • TaskList - extracts ToDo comments from your source code.
version #9394, modified 2010-11-23 01:32:59 by dionjwa