内容


构建基于 CDT 的编辑器,第 1 部分

C/C++ 开发工具模型

探讨包含 CDT 信息的类

Comments

系列内容:

此内容是该系列 # 部分中的第 # 部分: 构建基于 CDT 的编辑器,第 1 部分

敬请期待该系列的后续内容。

此内容是该系列的一部分:构建基于 CDT 的编辑器,第 1 部分

敬请期待该系列的后续内容。

在 CDT 这个问题上,我想我是被我的老板骗了,他说其中只不过是包含了 太多的功能。结果是,他只想要其中的源码编辑器功能 —— 语法着色功能、自动解析功能,特别是代码完成功能。他问我是否能够定制这个编辑器并将其添加到我们的富客户平台(RCP)应用程序中。“没问题”,我回答。“这是 Eclipse!又能有多难呢?”

结果却是非常难。目前,CDT V3.1 由 20 个插件组成,这些插件包含了超过 100 个包以及数千个接口和类。要找出究竟用哪种方法需要长久的努力。但最终我用一个工作编辑器让我的老板满意,我很高兴和大家分享我学到的东西。

精简版 CDT

本系列文章的目标是使您能够理解 CDT 编辑,从而构建可定制的 C/C++ 工具。为简化这一过程,我创建了一个完整 CDT 的精简版,我把它称作 Bare Bones CDT(BBCDT)(参见 下载 获得它的代码 )。我移除了绝大多数的 CDT 类,但保留了相同的命名约定 —— 部分原因是为了简化将 CDT 代码添加到 BBCDT 中的过程,另一部分是由于我很懒。

BBCDT 只包含两个插件:org.dworks.bbcdt.core(核心插件)和 org.dworks.bbcdt.ui(用户界面(UI)插件)。第一个插件提供了构成 CDT 模块的类和接口。第二个插件创建了 UI,其中包含了创建新源码文件和项目所需的 CEditor 及相关的类、向导和页面。

在第一篇文章中,BBCDT 并未完成任何值得关注的任务,只展示了基本 CDT 类是如何进行交互的。接下来的文章将解释 CDT 如何执行分块、语法着色、解析及代码完成功能。这些文章也将会详述有关 BBCDT 的内容,并确保这些功能被添加到了代码中。

CDT 模块的元素

通常的 Java™ 代码是通过 File 对象来访问文件和目录的。要在工作空间中对文件执行特定于 Eclipse 的操作,您却需要借助于资源应用编程接口(API)中的适配器:

  • IFile
  • IProject
  • IFolder
  • IWorkspaceRoot

除了提供对文件的访问外,这些 IResource 还提供了工作空间的结构。最顶层的资源是 IWorkspaceRoot,其子为 IProject。每个 IProject 都包含了一些 IFolderIFile。到目前这止,一切都正常。

CDT 模块代表了一个适用于工作空间资源的不同却相似的适配器集合。图 1 显示了它们的继承层次结构。

图 1. CDT 模块元素的层次结构
CDT 模块元素的层次结构

图 2 显示了 CDT 是如何使用这些元素来组成其工作空间的。在顶端,ICModel 包含了一些 ICContainerICProject。这些 ICProject 包含了一些 ISourceRoot,这些 ISourceRoot 像 JDT 包一样运行,但却能够包含处于文件系统中不同位置的代码。为管理这些位置,ISourceRoot 包含了 ISourceEntry 实例,它们中的每个都持有一个到不同源码的 IPath。每个 ICElement 都包含一个用于访问其底层 IResource 的特定名称及方法。

图 2. CDT 工作空间结构
CDT 工作空间结构
CDT 工作空间结构

ITranslationUnit

ITranslationUnit 代表了单个 C/C++ 代码文件,是 CDT 模块中最重要的元素。每一个单元都包含了代表源代码文件不同方面的子文件,如 IIncludeIUsingINamespace。(我将在后面讨论 CDT 解析时对 ITranslationUnit 结构进行更为详细的探讨。)

另外,每个 ITranslationUnit 持有单个的 IWorkingCopy,来管理未保存的代码修改。ITranslationUnitIWorkingCopy 都将其内容存储到单独的 IBufferCache 实例中,该实例由一个 BufferManager 来分配,并像最近最少使用(LRU)算法缓存一样运行。文章包含了这个主题,但如果您对此感兴趣,可以浏览 org.dworks.bbcdt.internal.core.util 包。

信息对象

每个 ICModelICContainerICProjectITranslationUnit 都有一个相应的信息对象(CModelInfoCContainerInfo 等等)。这些信息对象包含了对其子元素的引用并提供对其子元素的访问。如果您想要一份 CModelICProject 的列表,需要调用 CModelInfo.getCProjects() 方法,此方法调用了 CModelInfo.getChildren() 方法。CDT 在元素首次打开时创建了一个信息对象,并在其关闭时对其进行资源清理。

CDT 模型的三个阶段

既然您已经了解了该模型的基本元素,那么,您还需要了解这些元素在 CDT 操作时是如何进行交互的。CDT 模型生命周期由三个重要步骤组成:初始创建、创建 CProject 以及在 CDT 编辑器中创建 TranslationUnit

第 1 步:创建 CDT 模型

当工作台初始化了 CDT 插件,该核心插件就会创建一个单独的 CoreModel(不是 CModel)。此过程会依次创建两个重要的类: PathEntryManagerCModelManagerPathEntryManager 跟踪构建过程所需的目录,如包含目录或宏目录。在 CDT 模块中,每个目录的路径都保存在一个 SourceEntry 对象中。

CModelManager 类对于此次探讨来说至关重要。它在 CDT 中完成了诸多重要任务:

  • 创建作为元素层次结构中顶层元素的 CModel
  • 随着其资源被创建或删除,添加和移除新的模型元素
  • 跟踪模型元素及其信息对象
  • 持有一份有关 TranslationUnit 和作为其子元素的 WorkingCopy 的映射表。
  • 存储一份新近打开元素的缓存
  • 对资源内容类型的改变作出响应
  • 对资源描述符的改变作出响应

最重要的功能是前两个。CModelManager 作为 CDT 的模型工厂,通过创建 CModel 对象开始运转。随后,它监听工作空间中资源的改变并在 CDT 元素受到影响时更新该模型。

一旦有资源被创建、删除或修改,Eclipse 工作空间就会创建一个存储有新旧层次结构的 IResourceDelta。管理器用 DeltaProcessor 对此进行分析,DeltaProcessor 决定了受影响的资源是否是 CElement。如果是,它会创建一个与此资源相应的新元素,并将其添加到父亲的子列表中。

BBCDT 的 CModelManager 功能执行除最后两项的所有上述功能。资源的内容类型是由在 plugin.xml 文件中列出的 <content-type> 所设置,并创建一个描述符文件用以描述新项目。

步骤 2:创建新的 CProject

CDT 为创建 CDT 资源提供了一个完整的可扩展向导结构。新项目向导 (New Project Wizard) 功能尤其强大,它允许您配置项目构建过程的每一个方面,从环境变量到源码索引。当您完成该向导时,该向导及其核心插件将执行四项主要任务:

  1. 在给定路径中创建并打开一个 IProject
  2. 构建一个 IProjectDescription 来保存 IProject 的通用信息。
  3. 构造一个 CDescriptor 来保存特定于 CDT 的信息。
  4. 根据内容类型,为 IProject 给出一个 CNatureCCNature

第一步和第二步对于创建任何 IProject 来说都是通用的。在第二项任务中,IProjectDescription 存储了工作台用于定义项目的信息。此数据在项目顶层目录的 .project 文件中以 XML 格式存在。两个重要的元素分别是,列出项目构建命令的 <buildSpec></buildSpec> 和标志着该项目具有不同于常规 IProject 特征的 <natures></natures>

第三步中的 CDescriptorIProjectDescription 类似。主要的区别在于,它包含了特定于 CDT 的数据,并将此信息插入到一个独立的 .cdtproject 文件中,该文件包含了用于构建过程的不同工具的配置文件,并为每个工具指定了配置参数。此文件使用与 IProjectDescription 类似的 XML 格式。清单 1 是一个 .cdtproject 配置文件声明的例子。

清单 1. .cdtproject 配置文件中的信息
<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile">
    <buildOutputProvider>
        <openAction enabled="false" filePath=""/>
        <parser enabled="true"/>
    </buildOutputProvider>
    <scannerInfoProvider id="makefileGenerator">
        <runAction arguments="-f ${project_name}_scd.mk" command="make"   
            useDefault="true"/>
        <parser enabled="false"/>
    </scannerInfoProvider>
</profile>

最后一步涉及将该项目标记为一个 C 或 C++ 项目。核心插件通过将 CNatureCCNature 添加到 IProjectDescription 中来完成这一任务。这些类自己并未完成什么值得关注的任务,但当 DeltaProcessor 意识到新项目的性质时,它会让CModelManager 创建一个与项目资源相对应的 CProject,并将其添加至 CDT 模型层次结构中。

步骤 3:创建新 TranslationUnit 和 WorkingCopy

不同于 CProjectTranslationUnit 并不会在其内含的 IFile 出现后立刻被构建。相反,它会在该文件被激活且编辑器打开后被构建。为解释这项任务是如何运行的,我首先要介绍 CDT 编辑过程的核心类:CEditor

本质上,CEditor 是一个跟工作台适配,并从 IEditorInput 实例中获取内容的 StyledText 小部件。为了与模型-视图-控制器架构保持一致,Eclipse 文本编辑器 API 只使用此部件来提供视图外观。该编辑器的信息被封装到 IDocument 实例中。SourceViewer 扮演控制器的角色,CSourceViewer 管理对 CDT 文档的访问。

在核心插件中,plugin.xml 保存着不同类型的 C/C++ 文件的扩展名,并将每一个 contentType 和一个文件后缀联合起来。在 UI 插件中,plugin.xml 将 CEditor 和这些 contentType 关联起来。BBCDT 识别具有 .bbc、.bbcpp、.bbh 和 .bbhpp 后缀的文件。当您创建或双击一个这样的文件时,工作台会将 IFile 转换为 IEditorInput 并使用它来初始化 CEditor

编辑器被初始化后,CDocumentProvider 会执行三项重要任务:

  1. 使用输入的 IFile 来创建 TranslationUnitTranslationUnitInfo 对象。
  2. IEditorInput 中的信息为编辑器创建一个 IDocument
  3. TranslationUnit 及其缓冲构造 WorkingCopy

要创建新的 CDT 元素,提供者调用 CoreModelCoreModel 调用 CModelManager。这些工作空间操作(如CreateWorkingCopyOperationDestroyWorkingCopyOperation)都实现了 IWorkspaceRunnable 接口。它们异步运行,并使工作空间阻止了其他操作对资源修改的妨碍。

CProject 一样,每个 TranslationUnit 都具有自己的配置信息,比如注释。但提供者并不会创建一个独立的 CDescriptor。相反,它使数据保持在 IEditorInputFileInfo 对象中。这样,下一次激活输入时,提供者不用从头开始来访问该单元信息。

运行 BBCDT

我将 BBCDT 作为一个插件或插件项目来提供。因为这个工具的主要目的是为以后的工作提供一个基础,所以我建议您引入这个项目,而不是将其功能添加到您的 Eclipse 安装中。

为使 BBCDT 尽可能简单,我省去了 PathEntryManager,这意味着该工具不使用 SourceEntry 对象,甚至是 SourceRoot 对象。因而,您必须将源文件(.bbc、.bbh、.bbcc 和 .bbhh 文件)直接添加到项目中。为创建 BBCDT 资源,我在 org.dworks.bbcdt.ui.wizards 包中创建了一组新的向导/页面类。要创建项目,请单击 File > New > Project 并选择 CC++ 选项。要创建文件,请单击 New > Other 并选择 CC++ 选项。图 3 显示了 BBCDT 项目及编辑器的外观。

图 3. BBCDT
BBCDT
BBCDT

结束语

如果您访问过 Java 代码中的 IFileIProject,CDT 模型中的元素就不是什么问题了。CModel 包含了一些 CProject,这些 CProject 中包含了一些 CSourceRoot。每个 TranslationUnit 对应于单个的 C/C++ 源文件,因而编辑器通过访问并修改这些元素来与 CDT 模型进行交互。

功能越强大,其内容越复杂。这个编辑器也许太详细了,但您会在开始构建自己的 C/C++ 编辑器时发现它的用处。您可以通过研究 BBCDT 源代码来更好地理解 CDT 的运行机制。我建议您用添加、移除及修改来对其操作进行实验。

CDT 模型会在接下来的文章中变得更为清晰,因为我讨论了 TranslationUnitWorkingCopyCEditor 及其 UI 类进行交互的多种方式。第 2 部分会对 Document 如何管理事件、分块如何发生及这些分块如何提供语法着色进行深入介绍。


下载资源


相关主题

  • 您可以参阅本文在 developerWorks 全球站点上的 英文原文
  • 在 Eclipse.org 上查阅 Eclipse CDT
  • 阅读 CDT 开发团队的领袖人物的出色博客 Doug Schaefer
  • 从 Eclipse.org 下载 Eclipse CDT
  • 了解更多有关 Eclipse 基金会 的消息和它的众多项目。
  • 参见 “Eclipse 平台入门” 一文,这篇文章对 Eclipse 平台进行了很好的介绍。
  • 从 IBM 的 alphaWorks 查找最新的 Eclipse 技术下载
  • 访问 IBM developerWorks 的 Eclipse 项目资源,来扩展您的 Eclipse 使用技巧。
  • 访问 developerWorks Open source 专区,获取广泛的 how-to 信息、工具及项目更新,从而帮助您用开放源码技术进行开发,并将它们与 IBM 的产品结合使用。
  • IBM 试用版 改进您的下一个开放源码开发项目,这些软件可以通过下载或 DVD 获取。

评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Open source
ArticleID=176478
ArticleTitle=构建基于 CDT 的编辑器,第 1 部分: C/C++ 开发工具模型
publish-date=11232006