The DLTK interpreter and console
The most important feature of a DLTK application isn't the text editor or preferences but the ability to launch an interpreter with a button click and view its output in the IDE console. The DLTK interpreter and console are closely entwined, and managing them in code is a complicated process. Before delving into all the classes and interfaces, here's a high-level look at the events that take place when the user clicks Run:
- The IDE responds to the button click by creating a launch object, collecting launch information, and adding a process to the launch.
- The creation of the launch notifies all launch listeners, including the DLTK console manager.
- The console manager acquires the launch information and uses it to create three objects: a console server, an interpreter object, and a console factory.
- The console server creates a socket (by default, port 25000) and waits for incoming connections.
- The console factory starts the interpreter and creates a console.
- The console manager accesses the Eclipse Console plug-in and adds the new console.
DLTK classes handle much of this processing, but you still have to create many of your own. The Octave IDE needs to support Octave-related launches, then it needs Octave-specific classes to manage the console and interpreter. This discussion presents each of these subjects in turn.
In Eclipse, the process of starting an application is called launching the application in Run mode. Because Eclipse supports many launching executables, you must make sure that the Octave interpreter launches when the user executes an Octave script. This association is made through launch configuration types, and you can see the available types in the Run window by clicking Run > Run in Eclipse. Figure 11 shows what launch configuration types looks like.
Figure 11. The Octave launch configuration type
To create a launch configuration type for Octave, the first step is to
add an extension of the
to plugin.xml. In Figure 11, you can see the Octave configuration type
selected in the window. Listing 13 presents the
extension that defines this new type.
Listing 13. Defining the Octave launch configuration type
<extension point="org.eclipse.debug.core.launchConfigurationTypes"> <launchConfigurationType id="org.dworks.octaveide.launch.OctaveLaunchConfigurationType" delegate="org.dworks.octaveide.launch.OctaveLaunchConfigurationDelegate" modes="run" public="true" name="%OctaveLaunchConfigurationType.name" </launchConfigurationType> </extension>
The example project executes Octave scripts but doesn't debug them.
modes attribute is set to
run but not
run,debug. The most important attribute is
delegate, which identifies a class that
This interface defines a single method,
launch, which is called when the user
creates a launch configuration and clicks Run.
DLTK provides its own implementation of
When this class's
launch method is invoked,
it calls the methods listed in Table 9.
Table 9. The methods called during AbstractScriptLaunchConfigurationDelegate.launch()
|This method constructs an
|This method accesses the
|This method creates a process for the interpreter and adds it to the launch.|
This process may sound complicated, but the first two methods simply
organize information about the launch; the
InterpreterConfig holds information taken
from the launch configuration, and the
IInterpreterInstall stores information
taken from the preferences. It's important to understand that the
third method does not actually start the interpreter. It
IProcess for the interpreter and
adds it to the
When the DLTK plug-in
org.eclipse.dltk.console.ui starts, it
accesses the Eclipse Debug UI plug-in and adds an instance of
ScriptConsoleManager as a launch listener.
These listeners are notified whenever a launch is added, changed, or
responds to added launches, and its first task is to check whether the
launch has a recognizable script nature. If so, the manager creates
three important objects: a console server, an interpreter object, and
a console factory.
The manager creates a
manage communication between the interpreter and the console. During
its initialization, the server binds a
ServerSocket to port 25000 and tells it to
wait for incoming connection requests. These requests are embodied by
ConsoleRequest objects, and as the server
receives new requests, it calls the
consoleConnected method of each.
After creating the server, the
ScriptConsoleManager accesses a
ConsoleRequest representing the
interpreter. It does this by searching for an extension of the point
in plugin.xml. This extension point identifies a nature designation
and a class that implements
IScriptInterpreter, a subinterface of
ConsoleRequest. After it obtains the server
and interpreter object, the console manager can start creating the
console. To do this, it calls upon a console factory.
Ordinarily, if you want to access an Eclipse console from a plug-in,
you need only extend one extension point:
This extension must identify a class that implements
IConsoleFactory. Then, when the user clicks
Open Console in the Console view, Eclipse calls
openConsole method to create a
DLTK makes things slightly more complicated. The DLTK
ScriptConsoleManager also needs to access a
console factory, but it requires a class that implements
IScriptConsoleFactory both require the same
openConsole method. However, you need to
create two extension points: one for the Eclipse console factory and
one for the DLTK console factory. Listing 14
shows what these two points look like for the Octave IDE.
Listing 14. Identifying the Octave console factory
<extension point="org.eclipse.dltk.console.ui.scriptConsole"> <scriptConsole class="org.dworks.octaveide.launch.OctaveConsoleFactory" natureID="org.dworks.octaveide.nature" /> </extension> <extension point="org.eclipse.ui.console.consoleFactories"> <consoleFactory class="org.dworks.octaveide.launch.OctaveConsoleFactory" icon="icons/oct.gif" label="%OctaveConsole.Console" /> </extension>
ScriptConsoleManager accesses a
script console factory, it calls the factory's
openConsole method, which creates the
ScriptConsole object. Like
the Eclipse text editor, this console displays text using a
document-based methodology; it has a
IConsoleDocumentPartitioner, and arrays of
ScriptConsole is created, it calls
setInterpreter with a reference to the
IScriptInterpreter object that the console
manager creates. This method creates a connection between the console
and the interpreter in the form of an
InitialStreamReader. This stream reader
attaches itself to the interpreter by calling
InitialStreamReader reads each
line of the interpreter's output and sends it to the console's
display. When it's done displaying text, the console prints a prompt
and places itself in editable mode.
After creating the
ScriptConsoleManager accesses the Eclipse
ConsoleManager and adds the new object to
the list of available consoles. Now the new console will be visible to
the user and accessible by the interpreter.
Let's take a closer look at the
IScriptInterpreter object created by the
ScriptConsoleManager. This interface
ConsoleRequest interface, which
means that it can be delivered to the
ScriptConsoleServer for processing. The
console manager handles this delivery, and when the server receives
the interpreter's request, it calls
This method serves two important roles: It constructs the
InitialStreamReader that enables data
transfer between the console and the interpreter, and the method's
argument identifies the protocol to be used for communication. The
IScriptConsoleIO interface defines two
important methods that receive data from the interpreter:
InterpreterResponse execInterpreter(String command). Sends a script command to the interpreter, then receives a response
ShellResponse execShell(String command, String args). Sends a shell command to the interpreter, then receives a response
These two functions direct commands to the interpreter and receive its
object is initialized with an
execInterpreter is called, the protocol
sends the command to the interpreter to its
OutputStream. When the interpreter
responds, the protocol receives the
InterpreterResponse (essentially, a
String) and directs it to the console
All communication to the interpreter relies on the protocol's methods.
For example, the
interface contains the
that sends commands to the interpreter. This command calls on
IScriptConsoleIO.execInterpreter to deliver
the command. Similarly, the interpreter's
close method relies on