DLTK 解释器和控制台
DLTK 应用程序的最重要特性不是文本编辑器或首选项,而是实现通过单击按钮启动解释器以及在 IDE 控制台中查看它的输出。DLTK 解释器和控制台是紧密关联的,通过代码管理它们非常复杂。在深入了解所有类和接口之前,现在先大致了解一下当用户单击 Run 时触发的事件:
- IDE 通过创建一个启动对象来响应单击按钮事件,从而收集启动信息并为启动添加一个进程。
- 启动的创建将通知所有启动监听器,包括 DLTK 控制台管理器。
- 控制台管理器获得启动信息,并使用该信息创建 3 个对象:一个控制台服务器,一个解释器对象和一个控制台工厂。
- 控制台服务器创建一个套接字(默认端口是 25000),然后等待连接。
- 控制台工厂启动解释器并创建一个控制台。
- 控制台管理器访问 Eclipse 控制台插件并添加新的控制台。
DLTK 类处理大部分事情,但您仍然需要完成一些事情。Octave IDE 必须支持与 Octave 相关的启动,然后需要特定于 Octave 的类来管理控制台和解释器。这里将逐一讨论这些主题。
配置 DLTK 启动
在 Eclipse 中,启动应用程序的过程称为在 Run 模型下启动应用程序。由于 Eclipse 支持许多启动执行文件,所以您要确保当用户执行 Octave 脚本时启动 Octave 解释器。这种关联通过启动配置类型 来实现,并且通过在 Eclipse 中单击 Run > Run 可以看到所有可用的类型。图 8 显示了启动配置类型。
图 8. Octave 启动配置类型
要为 Octave 创建启动配置类型,第一步就是将 org.eclipse.debug.core.launchConfigurationTypes 的一个扩展添加到 plugin.xml。在图 8 中,可以看到在窗口中选中的 Octave 配置类型。清单 13 给出了定义这个新类型的扩展。
启动 13. 定义 Octave 启动配置类型
<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>
|
示例项目会执行 Octave 脚本,但不调试它们。因此,需要将 modes 属性设置为 run 而不是 run,debug。最重要的属性是 delegate,它标识实现 ILaunchConfigurationDelegate 的类。这个接口定义一个方法 launch,当用户创建启动配置并单击 Run 时会调用该方法。
DLTK 提供它自己的 ILaunchConfigurationDelegate 实现,称为 AbstractScriptLaunchConfigurationDelegate。当这个类的 launch 方法被调用时,它会调用 3 个重要的方法:
createInterpreterConfig
- 这个方法构造一个
InterpreterConfig 对象,它接收来自启动配置的所有信息。
getInterpreterRunner
- 这个方法访问由解释器安装类型创建的
IInterpreterInstall 对象,并使用它构造一个 IInterpreterRunner。
runRunner
- 这个方法为解释器创建一个进程,并将其添加到启动中。
这个过程看起来有些复杂,但前两个方法仅组织了关于启动的信息;InterpreterConfig 存储来自启动配置的信息,而 IInterpreterInstall 存储来自首选项的信息。第 3 个方法并没有 实际启动解释器,理解这点很重要。它为解释器创建一个 IProcess,并将其添加到 Launch 对象。
DLTK 控制台管理器
当 DLTK 插件 org.eclipse.dltk.console.ui 启动时,它将访问 Eclipse Debug UI 插件,并添加一个 ScriptConsoleManager 实例作为启动监听器。当添加、更改或删除启动时,都会通知这些监听器。ScriptConsoleManager 仅响应添加的启动,它的第一个任务是检查启动是否具有可识别的特征。如果是的话,该管理器将创建 3 个重要的对象:一个控制台服务器、一个解释器对象和一个控制台工厂。
管理器创建一个 ScriptConsoleServer 来管理解释器和控制台之间的通信。在初始化期间,服务器将一个 ServerSocket 绑定到端口 25000,并告诉它等待进来的连接请求。这些请求以 ConsoleRequest 对象的形式出现,并且当服务器接受到新的请求时,它将调用每个新请求的 consoleConnected 方法。
创建服务器之后,ScriptConsoleManager 访问一个表示解释器的 ConsoleRequest。它通过在 plugin.xml 中搜索 org.eclipse.dltk.console.scriptInterpreter 的扩展点来实现这个目的。这个扩展点标识一个特征目标和一个实现 IScriptInterpreter(ConsoleRequest 的子接口)的类。控制台管理器在获取解释器对象之后,就可以开始创建控制台了。为此,需要调用工作台工厂。
控制台工厂和控制台
 |
Octclipse 和 Ryan Rusaw 我对 Ryan Rusaw 表示感谢,因为他创建了 Octclipse 工具。他的开发为本教程的示例项目提供了灵感。该工具不仅提供语法着色、首选项和解释器集成,而且还提供其他许多特性,包括解析、调试、语义高亮显示和项目创建向导。如果您对使用 Octave 解释器感兴趣,我强烈建议您下载根据 Eclipse Public License 授权的 Octclipse 工具集(见 参考资料)。 |
|
通常,如果您想从插件访问 Eclipse 控制台,仅需扩展?个扩展点:org.eclipse.ui.console.consoleFactories。这个扩展必须标识一个实现 IConsoleFactory 的类。然后,当在 Console 视图中单击 Open Console 时,Eclipse 将调用这个类的 openConsole 方法创建一个新的控制台。
DLTK 让事情变得复杂一些。DLTK ScriptConsoleManager 也需要访问控制台工厂,但它需要一个实现 IScriptConsoleFactory 的类。IConsoleFactory 和 IScriptConsoleFactory 都需要相同的 openConsole 方法,这很方便。不过,您需要创建两个扩展点:一个用于 Eclipse 控制台工厂,一个用于 DLTK 控制台工厂。清单 14 展示了这两个 Octave IDE 扩展点。
清单 14. 标识 Octave 控制台工厂
<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 访问一个脚本控制台工厂时,它调用该工厂的 openConsole 方法,并通过它创建最重要的 ScriptConsole 对象。就像 Eclipse 文本编辑器一样,这个控制台使用基于文档的方法显示文本;它有一个 ConsoleDocument、一个 IConsoleDocumentPartitioner 以及一些 Position 和 Region 数组。
创建 ScriptConsole 之后,它将调用引用由控制台创建的 IScriptInterpreter 对象的 setInterpreter。这个方法以 InitialStreamReader 的形式在控制台和解释器之间创建一个连接。这个流阅读器通过调用 IScriptInterpreter.getInitialOutputStream 将自身连接到解释器。接下来,InitialStreamReader 读取解释器输出的每行内容,并发送到控制台进行显示。显示文本之后,控制台会输出一个提示,并进入可编辑模式。
在创建 ScriptConsole 之后,ScriptConsoleManager 将访问 Eclipse ConsoleManager,并向可用控制台列表添加新的对象。现在,新的控制台对用户是可见的,并且解释器也可以访问它。
运行解释器
我们仔细看看由 ScriptConsoleManager 创建的 IScriptInterpreter 对象。这个接口扩展 ConsoleRequest 接口,这意味着可以将它提交给 ScriptConsoleServer 以进行处理。控制台管理器处理这个提交,并且当服务器收到解释器的请求时,它将调用 IScriptInterpreter.consoleConnected(IScriptConsoleIO protocol)。
这个方法担任两个重要的角色:它构造实现在控制台和解释器之间传输数据的 InitialStreamReader,并且这个方法的参数标识用于通信的协议。IScriptConsoleIO 定义两个从解释器接收数据的重要方法:
InterpreterResponse execInterpreter(String command)
- 向解释器发送一个脚本命令,然后接收一个响应
ShellResponse execShell(String command, String[] args)
- 向解释器发送一个 shell 命令,然后接收一个响应
这两个函数将命令指向解释器并接收解释器的输出。每个 IScriptConsoleIO 协议对象都使用 InputStream 和 OutputStream 进行初始化。当 execInterpreter 被调用时,协议将命令发送给解释器的 OutputStream。当解释器进行响应时,协议接收 InterpreterResponse(实际上是一个 String)并通过 InputStream 将其发送到控制台。
所有与解释器进行的通信都依赖于协议的方法。例如,IScriptInterpreter 接口包含将命令发送到解释器的 exec(String command) 方法。这个命令通过调用 IScriptConsoleIO.execInterpreter 来提交命令。同样,解释器的 close 方法也依赖于 IScriptConsoleIO.execShell。
|