Fork me on GitHub

Commands

For each eclipse feature that is exposed in eclim, there is a corresponding command on the daemon which handles calling the appropriate eclipse APIs and returning a result back to the client. This page will walk you through creating a simple command to familiarize you with the process.

Creating a Command

Commands are simple classes which extend AbstractCommand and are registered using the @Command annotation. They then define an execute method which can return any object that can be serialized appropriately using gson.

Here is an example of a trivial command which returns a map of the arguments it was supplied, with the supplied project and file paths converted to absolute paths and the file byte offset converted to a character offset (eclim’s vim function eclim#util#GetOffset() returns the offset in bytes since getting a character offset in vim with multi byte characters is less reliable, but most eclipse APIs expect character offsets):

Note

Eclim’s source code is grouped by bundles (org.eclim, org.eclim.core, etc), each of which has java directory containing the java source code for that bundle.

package org.eclim.plugin.core.command.sample;

import java.util.HashMap;

import org.eclim.annotation.Command;

import org.eclim.command.CommandLine;
import org.eclim.command.Options;

import org.eclim.plugin.core.command.AbstractCommand;

import org.eclim.plugin.core.util.ProjectUtils;

import org.eclipse.core.resources.IProject;

@Command(
  name = "echo",
  options =
    "REQUIRED p project ARG," +
    "REQUIRED f file ARG," +
    "REQUIRED o offset ARG," +
    "OPTIONAL e encoding ARG"
)
public class EchoCommand
  extends AbstractCommand
{
  @Override
  public Object execute(CommandLine commandLine)
    throws Exception
  {
    String projectName = commandLine.getValue(Options.PROJECT_OPTION);
    String file = commandLine.getValue(Options.FILE_OPTION);

    IProject project = ProjectUtils.getProject(projectName);

    // translates client supplied byte offset to a character offset using the
    // 'project', 'file', 'offset', and 'encoding' command line args.
    int offset = getOffset(commandLine);

    HashMap<String,Object> result = new HashMap<String,Object>();
    result.put("project", ProjectUtils.getPath(project));
    result.put("file", ProjectUtils.getFilePath(project, file));
    result.put("offset", offset);
    if (commandLine.hasOption(Options.ENCODING_OPTION)){
      result.put("encoding", commandLine.getValue(Options.ENCODING_OPTION));
    }

    return result;
  }
}

When registering the command with the @Command annotation, you give it a name and a comma separated list of options. Each option consists of 4 parts in the form of:

REQUIRED|OPTIONAL s longname ARG|NOARG|ANY

Where each part is defined as:

  1. REQUIRED or OPTIONAL
  2. a single letter short name for the option
  3. a long name for the option
  4. whether the option requires an argument, no argument, or can have any number of additional arguments. In the case of ANY, you should only have one option with that value and when running the command from the command line, that option should be supplied last.

That should give you the basics on what’s involved with creating a new command, but the biggest hurdle for creating most commands is locating and deciphering the eclipse API calls that are necessary to implement the feature you want. Unfortunately most of the eclipse code that you’ll need to hook into will most likely have little to no documentation so you’re going to have to dig through the eclipse code. Eclim does provide a couple ant tasks to at least help you to quickly extract any docs or source code found in your eclipse install:

  • eclipse.doc: This target will extract any doc jars from your eclipse install to a ‘doc’ directory in your eclipse home (or user local eclipse home).

  • eclipse.src: This target will extract any src jars from your eclipse install to a ‘src’ directory in your eclipse home (or user local eclipse home). If you download the sdk version of eclipse then the jdt and all the core eclipse source will be available. Some other plugins provide sdk versions which include the source code and this target can extract those as well, but some plugins don’t seem to have this option when installing via eclipse’s update manager (and may not include the source when installed from a system package manager). For those you can often download a zip version of their update site which should include source bundles. Once you’ve extracted that file, you can tell this target to extract source bundles from a specified directory. Here is an example of extracting the source from an unpacked dltk update site:

    $ ant -Dsrc.dir=/home/ervandew/downloads/dltk-core-5.0.0/plugins eclipse.src
    

Running a Command

Once you’ve created your command you then need to compile the code using eclim’s ant build file. After you’ve done that you can then start eclimd and execute your command from the command line to test it:

$ eclim -pretty -command echo -p eclim -f org.eclim.core/plugin.properties -o 42 -e utf-8

Note

As you are developing your commands, you can avoid restarting eclimd after every change by using eclim’s reload command which will reload all of eclim’s plugin bundles with the exception of org.eclim.core (so unfortunately it won’t help with our example above if we put that command in the org.eclim.core bundle):

$ eclim -command reload

Adding to Vim

Continuing with our echo command example, we can add the command to vim by first defining a new vim command in org.eclim.core/vim/eclim/plugin/eclim.vim:

Note

If the command should only be available for a specific file type, then you’d put it in a vim/eclim/ftplugin/somefiltetype.vim file instead.

command EclimEcho :call eclim#echo#Echo()

Now that we’ve created the command, we then need to define our eclim#echo#Echo() function accordingly in org.eclim.core/vim/eclim/autoload/eclim/echo.vim:

" Script Variables {{{
  let s:echo_command =
    \ '-command echo -p "<project>" -f "<file>" ' .
    \ '-o <offset> -e <encoding>'
" }}}

function! eclim#echo#Echo() " {{{
  if !eclim#project#util#IsCurrentFileInProject(0)
    return
  endif

  let project = eclim#project#util#GetCurrentProjectName()
  let file = eclim#project#util#GetProjectRelativeFilePath()

  let command = s:echo_command
  let command = substitute(command, '<project>', project, '')
  let command = substitute(command, '<file>', file, '')
  let command = substitute(command, '<offset>', eclim#util#GetOffset(), '')
  let command = substitute(command, '<encoding>', eclim#util#GetEncoding(), '')

  let response = eclim#Execute(command)

  " if we didn't get back a dict as expected, then there was probably a
  " failure in the command, which eclim#Execute will handle alerting the user
  " to.
  if type(response) != g:DICT_TYPE
    return
  endif

  " simply print the response for the user.
  call eclim#util#Echo(string(response))
endfunction " }}}

And that’s all there is to it. After re-building eclim, restarting eclimd, and restarting vim, you can now execute the command :EclimEcho to see the response printed in vim.

Now that you know the basics, you can explore the many existing eclim commands found in the eclim source code to see detailed examples of how to access various eclipse features to expose them for use in vim or the editor of your choice.

You should also take a look at the eclim Plugins documentation which documents how to create a new eclim plugin, including information on adding new eclim settings, managing the plugin’s dependencies through its META-INF/MANIFEST.MF, etc.