创建一个具有商业品质的 Eclipse IDE

创建具有商业品质且可插入 Eclipse 的专业 IDE

Prashant Deva ( pdeva@placidsystems.com), 创始人, Placid Systems

简介:  “创建一个具有商业品质的 Eclipse IDE” 系列教程主要研究如何创建具有商业品质且可插入 Eclipse 的专业 IDE,本期教程主要学习如何创建 IDE 内核。

查看本系列更多内容

发布日期:  2006 年 11 月 23 日 
级别: 高级 

访问情况 : 8400 次浏览

1 star 2 stars 3 stars 4 stars 5 stars  平均分 (共 3 个评分 )

开始之前

关于本教程

创建具有商业品质的 Eclipse IDE” 系列教程介绍了如何创建一个作为 Eclipse 插件的集成开发环境(IDE),且使其可用于现有任何编程语言或您自己使用的语言。本教程简述了 IDE 的两个重要的部分 —— 内核以及用户界面(UI),并详细讲解了与设计、实现内核和用户界面相关的问题。

本系列以 ANTLR Studio IDE 作为案例分析,研究了其内幕细节,以使您了解如何创建一个极为专业的商业级 IDE。教程中的示例代码能帮助您理解概念,并弄清楚如何在自己的 IDE 中利用这些概念。

第 1 部分介绍如何创建 IDE 的基础部分,也就是 内核,IDE 的其他所有组件都构建于内核之上。这篇教程还探讨了商用 ANTLR Studio IDE 中使用的一些技术,在您设计内核时,可利用这些技术来解决可能遇到的某些问题。

先决条件

本教程假设您具有关于 ANTLR、解析器和词法分析器(概念和用途)的基本知识,并使用过 Eclipse Java™ 开发工具(JDT)。

系统要求

为了试验本教程中的示例代码,您需要具有运行 Java 虚拟机(JVM) V1.4 或更高版本的 Eclipse 软件开发包(SDK)。除此之外,为了生成解析器以及词法分析器示例,还需要 ANTLR(参见 参考资料)。

架构

当前可用的 IDE 外观各有不同、特性多种多样,但几乎都共用相同的基本架构。IDE 通常根据其不同部分的功能性划分为多个层或者模块。

有两个最通用的模块是任何一个合格的 IDE 都具备的:

  1. 内核 —— 任何 IDE 的基础 层,包括能识别语言元素的解析器或词法分析器。
  2. UI —— IDE 中与 UI 相关的代码位于此处,包括语法突出显示、内容提要等等。请注意 UI 层构建在内核层之上。

随着本教程的深入,您会注意到在 IDE 中,几乎所有的东西都可以划分到这两层内。可以创建其他一些模块,但几乎所有 IDE 中都有这两个模块,本教程将详细讲解这两个模块。

一个 IDE 通常还包括下面这些模块:

  • 调试 —— 与调试器相关的代码
  • 文档 —— HTML 格式的文档
  • 构建 —— 构建系统(build system),这个系统有可能复杂到需要一个单独模块的程度

如果您的 IDE 规模庞大、错综复杂,通常的做法是将内核和 UI 模块作为彼此独立的项目,分别由一个团队来完成。这种方式在内核与 UI 之间设置了一个定义良好的边界,但修改和调试代码非常困难。因为 UI 层位于内核层之上,UI 团队成员会时常发现他们不得不停滞在某个阶段,等待内核团队完成工作以后,才能继续他们的工作。还可能会遇到因内核中存在 bug 而造成的 UI 的奇特行为。因此,内核需要非常健壮。这类大型 IDE 的一个绝佳示例就是 JDT,JDT 包含大量模块,各模块都由一个单独的团队编码。

另一方面,如果您的 IDE 将用于一种较小型的语言,那么可以将内核和 UI 综合到一个 Java Archive(JAR)中。尽管采用一个团队完成这些工作略有困难,但采用这种方式时,更改更便捷、代码更易于调试。此类 IDE 的一个例子就是 ANTLR Studio —— 流行的 ANTLR 解析器生成器的 IDE。因为 ANTLR Studio 的代码由同一个人编写,且 ANTLR 的语法并不像 Java 语言的语法那么复杂,因此更易于将所有的层放在一个 JAR 中,使之更易于管理和调试。

图 1 显示了 ANTLR Studio 的架构。


图 1. ANTLR Studio 架构
ANTLR Studio 架构  

如您所见,ANTLR Studio 中几乎每个模块都可分为内核和 UI 类。由于构建系统对任何一层都没有过多的依赖性,因此在图中显示为一个独立的垂直条。

内核

本教程详细分析了 IDE 内核层的实现。内核构成了整个 IDE 的基础:内核部分 了解 IDE 中的所有代码。如果将一个 IDE 比作人的身体,内核就是人的大脑。有关 IDE 的任何信息 —— 速度、稳定性以及 IDE 能提供的任何特性,最终取决于内核设计的成功与否。

注意内核位于 IDE 的最底层, 因此 IDE 中除内核以外的 所有东西 都直接或者间接地依赖于内核。因此,在编写 IDE 中其他 UI 部分的代码时,内核中的 bug 将导致非常大的麻烦。

图2 显示了 ANTLR Studio 的内核图。


图2. ANTLR Studio 内核
ANTLR Studio 内核  

内核包括如下部分:

  • 解析器
  • 词法分析器
  • 符号表

几乎所有的内核都包括这几个基本元素。解析器 以及 词法分析器 用于读取用户键入的源代码,而符号表将这些信息以树的形式保存起来。IDE 的其他部分查询符号表,只是为了找到相应的代码,并根据代码完成相应的动作。解析器和词法分析器通常运行于后台线程,以便与符号表保持更新。

符号表 是 IDE 中提供给其他组件的部分。符号表通常以一个解析树的形式出现,其中语言语法中的每个元素就是一个节点。在内核解析树中,一个节点可能包括如下一些常用的属性:

  • 开始/结束偏移范围 —— 表示元素在编辑器中的位置
  • 值 —— 与节点相关的实际文本(例如 int a;
  • 父元素
  • 子元素
  • 特定于元素类型的其他方法(例如, “ method declaration ”节点可能包含用于获得参数并返回某个数据类型的方法)

下一节将说明如何实现词法分析器和解析器。

创建一个解析器以及词法分析器

您需要为 IDE 创建一个解析器和一个词法分析器,这样 IDE 才能识别键入文档中的代码。为了达到这个目标,需要使用一个解析器和词法分析器生成器。目前有很多可用的解析器和生成器,如 FLex/Yacc、 JavaCC、 Anagram 以及 ANTLR。本系列使用 ANTLR,这是一个免费的开放源码解析器生成器。

在本教程中,将创建一个解析器或者词法分析器,用于一个称为 Tiny C 的小的语言,该语言类似 C 语言。该语言允许声明变量,并允许进行函数声明。该语言目前只能识别两种原始类型( native type ):char 以及 int。 这些声明包含了一些简单的语句,类似 while 和 if 循环以及赋值表达式。该语言还允许进行单行注释。

清单 1 显示了解析器的 ANTLR 语法。


清单 1. 解析器的 ANTLR 语法
                    
class TinyCParser extends Parser;

program
	:	( declaration )* EOF
	;

declaration
	:	(variable) => variable
	|	function
	;

declarator
	:	id:ID
	|	STAR id2:ID
	;

variable
	:	type declarator SEMI
	;

function
	:	type id:ID LPAREN
		(formalParameter (COMMA formalParameter)*)?
		RPAREN
		block
	;

formalParameter
	:	type declarator
	;

type:	
	(
		"int"
	|	"char"
	|	ID
	)
	;

block
	:	LCURLY ( statement )* RCURLY
	;

statement
	:	(declaration) => declaration
	|	expr SEMI
	|	"if" LPAREN expr RPAREN statement
		( TK_else statement )?
	|	"while" LPAREN expr RPAREN statement
	|	block
	;

expr:	assignExpr
	;

assignExpr
	:	aexpr (ASSIGN assignExpr)?
	;

aexpr
	:	atom (PLUS atom)*
	;

atom:	ID
	|	INT
	|	CHAR_LITERAL
	|	STRING_LITERAL
	;

清单 2 显示了相应的词法分析器代码。


清单 2. 词法分析器
                    
				class TinyCLexer extends Lexer;
options {
	k=2;
	charVocabulary = '\3'..'\377';
}

tokens {
	"int"; "char"; "if"; "else"; "while";
}

WS	:	(' '
	|	'\t'
	|	'\n'	{newline();}
	|	'\r')
		{ _ttype = Token.SKIP; }
	;


SL_COMMENT : 
	"//" 
	(~'\n')* '\n'
	{ _ttype = Token.SKIP; newline(); }
	;


LPAREN
options {
	paraphrase="'('";
}
	:	'('
	;

RPAREN
options {
	paraphrase="')'";
}
	:	')'
	;

LCURLY:	'{'
	;

RCURLY:	'}'
	;

STAR:	'*'
	;

PLUS:	'+'
	;

ASSIGN
	:	'='
	;

SEMI:	';'
	;

COMMA
	:	','
	;

CHAR_LITERAL
	:	'\'' (ESC|~'\'') '\''
	;

STRING_LITERAL
	:	'"' (ESC|~'"')* '"'
	;

protected
ESC	:	'\\'
		(	'n'
		|	'r'
		|	't'
		|	'b'
		|	'f'
		|	'"'
		|	'\''
		|	'\\'
		|	'0'..'3'
			(
				options {warnWhenFollowAmbig = false;}
			:	DIGIT
				(
					options {warnWhenFollowAmbig = false;}
				:	DIGIT
				)?
			)?
		|	'4'..'7'
			(
				options {warnWhenFollowAmbig = false;}
			:	DIGIT
			)?
		)
	;

protected
DIGIT
	:	'0'..'9'
	;

INT	:	(DIGIT)+
	;

ID
options {
	testLiterals = true;
	paraphrase = "an identifier";
}
	:	('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'_'|'0'..'9')*
	;

以下是符合该语法的一个有效代码块:


清单 3. 有效代码块
                    
int a;

char myFunc()
{
   int b =a;
}

速度需求:深入解析内核功能

要确定内核的设计如何成功,主要取决于一个关键因素:速度。在设计内核时速度是最大的挑战,整个 IDE 的性能最终是由内核的性能来决定的。如果内核不够快,您就会发现无法实现某些设计特性,因为 IDE 的速度太慢了,根本无法使用。

在内核中速度如此重要的原因何在?对于目前可用的 3-GHz (或者更快)的多内核处理器来说,是否需要重新考虑呢?

IDE (在这种情况下,尤其是 IDE 的编辑器)有一个特殊的需求,这个需求也许在您编写的其他大部分应用程序中不存在。如果您已经使用过任何一个现代的 IDE,那您一定已经注意到当您在编辑器中键入代码时,会有一些曲线标注,以提示出现了错误。另外,在窗口旁边的视图中会出现代码的大纲,此时如果使用 Ctrl+Space,就可以获得即时代码完成特性。这些行为几乎在您键入代码的时候同时发生,不需要按下按钮来更新 IDE。同时您感觉不到任何延迟,也无需考虑您已经编写了多少行代码。

这就是速度在起作用。IDE 必须跟上您的键入速度,并在极短的时间内更新其所有的数据结构。但 IDE 不能占用太多的处理能力,或者用户在键入时会感觉到一点点迟滞 —— 字符在已经输入之后才显示在屏幕上,这样的 IDE 就很难使用。

IDE 设计的艺术包括在保持 IDE 敏感度的同时,使数据结构保持为最新状态。要做到这一点非常困难,特别是人们的键入速度都很快,而且都希望字符能立即出现在屏幕上。

大部分 IDE (包括 Eclipse JDT ) 使用以下方法解决这个问题:

  1. 在一个后台线程中运行解析器或者词法分析器。
  2. 当线程运行了一个固定的短时间间隔后(例如 200 毫秒),运行解析器或者词法分析器以更新符号表。
  3. 其他视图,如大纲视图,在其自身的线程中运行,并通过查找符号表来更新自身,但它们进行更新的间隔时间比后台线程的间隔时间长 (也许是 1,200 毫秒)。

使用这些方法,即使您无法 总是 保持为最新状态,但能在 大部分时间 内都保持更新,此时 IDE 的速度足够快,用户不会注意到键入的延迟。同时,因为每个视图都运行在后台线程中,用于处理用户键入的用户线程几乎总能不受影响,因此用户在键入时不会感觉到任何迟滞。

ANTLR Studio 在处理速度时有些小小的不同。ANTLR Studio 总是保持为最新状态,这意味着在每次击键时,整个符号表都将被更新。当用户在编辑器中键入后,字符表会立即更新,这之间不存在任何迟滞,因此当字符显示在屏幕上时,用户不会感觉到任何的迟滞现象。因此,ANTLR Studio 能提供一些独特的特性,如果使用其他方式,这些特性是无法实现的 —— 这是一个非常好的案例学习。下一节我们将深入研究 ANTLR Studio。

增量解析器和词法分析器

在 ANTLR Studio 中,解析器或者词法分析器与编辑器运行在同一个线程中。简而言之,在 ANTLR Studio 编辑器中的任何一次击键都会导致以下结果:

  1. 调用解析器和词法分析器。解析器和词法分析器能扫描整个文档,为编辑器中的所有代码生成一颗代码树。
  2. 语法高亮器被调用,以扫描用户键入的部分,并用彩色显示该部分。
  3. 检查用户键入的上下文。如果可以使用任何的自动完成特性,将这些特性显示给用户。

所有这些都是在毫无迟滞的情况下发生的,无论编辑器有 10 行或者 10,000 行都是如此。要达到这种性能,可能要归功于一个技巧。

图 2 显示了 ANTLR Studio 的内核,包括 增量解析器 以及 增量词法分析器 的代码。这里有什么不同呢?

为了不需要每次都检查整个文档,增量解析器或者词法分析器会表示出文档的哪个部分从上一次修改之后被修改了,并且只处理被修改的那部分文档,而不处理其余的部分。如果键入一个字符,增量解析器和文法分析器只扫描文档中的这个字符。这节约了大量的时间,并使得在同一个线程中运行解析器以及词法分析器成为可能。

增量解析器和词法分析器的缺点

具有讽刺意义的是,虽然增量解析器或者词法分析器能为最终用户生成更好的 IDE,但它也有能影响开发者的缺点:

  • 开发一个增量解析器和词法分析器是一个艰难而复杂的过程。
  • 要开发增量解析器和词法分析器将给底层代码带来巨大的复杂性。
  • 您必须以从本质上修改所有的组件设计, 反过来,这会使这些组件非常复杂。

为了更全面地利用增量解析器和词法分析器的增量本质,需要让 IDE 中所有其他组件以增量的方式进行更新。这又回到了初始的问题:在击键的一瞬间更新 IDE 的所有 数据结构,必须让数据结构以增量的方式进行更新,而不是全部同时进行更新,才能解决这个问题。拥有一个增量解析器和词法分析器只是让 IDE 统一使用增量方式进行更新的基础。

delta

作为一个示例,考虑一下内容大纲视图。在通常情况下,它会有以下行为:

  1. 在某个时间间隔后,它向内核请求一个代码树(例如 1,200 毫秒)。
  2. 它使用这个新的代码树来更新整个视图。
  3. 视图会出现一些闪烁,因为整个视图都被更新且被重画了。如果每隔 1.2 秒都发生这种情况,这会严重影响用户的注意力。

作为一个演示的例子,我们可以打开 Eclipse,创建一个新的 Java 项目(或者使用现存的项目)。此时要确保大纲视图是打开的,然后创建一个 Java 文件,在其中键入一些代码。大纲视图进行更新时并不会闪烁,这是一个类似 JDT 的高品质 IDE 所期望的现象。

与对修改的部分进行比较,并只更新所需的修改部分相比,获得一个代码树并告诉视图进行刷新是非常容易的。但是,只更新需要更新的部分会给最终用户带来更好的体验。这会导致另一个问题,一旦从解析器中获得代码树,需要检查与上一次的代码树相比,进行了哪些修改,这样才能将这些修改传递给需要它们的组件。保存修改信息的数据结构称为 delta。这样,您需要计算在每一次解析之后,对解析树的 delta 进行计算。

delta 必须在解析之后立即计算,并且必须运行在同一个线程中。与 ANTLR Studio 类似,如果您计划将解析器与编辑器运行在同一个线程中,您需要确保 delta 计算的算法足够快,用户不会在键入的时候感觉到任何迟滞。

下面是用于 Tiny C 语言的简单数据结构:


清单 4. 用于 Tiny C 的简单数据结构
                    
interface  Delta {
 
Declaration[] getAddedDeclarations();
Declaration[] getRemovedDeclarations();
Declaration[] getModifiedDeclarations();

}

如果大纲视图需要自己进行更新,它需要做以下工作:

  1. 调用getRemovedDeclarations() ,该语句将获得被删除的所有声明,将对应于这些声明的节点删除。
  2. 调用 getAddedDeclarations() ,该语句将获得已添加的所有声明,将对应于这些声明的节点添加到代码树中。
  3. 调用 getModifiedDeclarations() ,获得已经修改的所有节点,修改这些节点的文本。

虽然这个过程增加了复杂性,但它比更新整个树要快 —— 而且用户不会看到任何闪烁。

delta 合并

如果在同一个线程中运行解析器和编辑器,且同时打开了三个其他的视图,它们都要使用由解析器生成的解析树。为了在每次击键时重新生成解析树,还需要在每次击键后更新所有三个视图。这会产生迟滞,因为每个视图都会在解析树上进行自己的处理。 这样就需要在每次击键后更新所有数据结构 并且 重画所有视图。这种情况正是需要您采取措施来 阻止 的!

如果将视图运行于不同的线程并在某个时间间隔后更新它们,但始终让解析器运行于 UI 所在的线程,会出现什么情况?以下就是这种情况的结果:

  1. 在每次击键后,内核生成一个解析树,并 根据上一个解析树 为该解析树生成一个 delta。
  2. 内容大纲视图在 1,200 毫秒后调用一个线程,该线程将向内核请求一个 delta。
  3. 但内容大纲视图将保有一个根据先前五次击键而生成的解析树,因此当前的 delta 不会应用于该解析树。
  4. 内容大纲视图无法保持同步。

如何解决这个问题?(在查看解决方案之前先考虑一分钟。)

以下就是解决方案:

  1. 每个视图都有一个与之相关的队列。
  2. 每次内核生成 delta 时,它都在每个视图的队列中保存一个拷贝。
  3. 当视图需要更新自身时,它 连续地 在它保存的解析树上应用每个 delta。 瞧,现在它已经更新了!您可以首先查看所有 delta,对于那些最初加入解析树中,但在随后的任何一次队列 delta 中被删除的节点,可以删除它们,以此来优化这个过程。
  4. 一旦视图已经完成,它将清空队列,整个过程重新开始。

这种技术称为 delta 合并 (参见图 3),因为您需要收集所有的 delta 并将它们组合成一个 delta,然后在解析树上运行这个单独的 delta。注意您必须使队列保持同步,这样内核以及 UI 组件才不会同时写队列。


图 3. delta 合并
delta 合并  

以下的代码演示了这种技术的工作方式。您可能需要添加一些额外的同步代码,这取决于 IDE 的设计。


清单 5. delta 合并
                    
// interface for components that want to collect deltas
interface DeltaListener{
	void newDeltaCreated(Delta delta);
}

class DeltaUpdatingView extends MyView implements DeltaListener
{
	private BlockingQueue<Delta> queue;
	

	public void newDeltaCreated(Delta delta)
	{
		queue.put(delta);
	}

	public void onUpdate()
	{
		for(Delta delta:queue.toArray(new Delta[queue.size()]))
		{
			//apply  delta to the UI component
		}

		//clear the queue
		queue.clear();
	}

}

错误恢复

解析器的错误恢复是在设计内核时必须密切注意的。在大部分时间内,当用户输入代码时,都会出现错误。解析器必须很好地从这些错误中恢复,继续进行解析,直到到达文档结尾。

不幸的是这种情况没有任何捷径。虽然 ANTLR 提供了一些基本的错误处理,您仍然需要手动在解析器中加入一些检查代码,使之从错误中恢复。这是所有 IDE 的工作方式,包括 JDT 以及 ANTLR Studio。

考虑到速度要求,您必须非常小心:ANTLR 在每次遇到错误时都会抛出一个异常,这些异常将会降低速度。在发现一个有效的输入之前,可以使用标志来尽量充分地利用每一个异常抛出。

作为一个例子,我们在 Tiny C 语言中添加一些简单的错误恢复代码。在解析过程中如果遇到一个错误,您应当做以下工作:

  1. 如果一个 语句出现了错误,跳过该语句,直到发现一个分号 (;)。
  2. 如果一个代码 出现了错误,跳过该块,直到发现一个右花括号 (})。

下面是对应的语法代码,添加了错误处理:


清单 6. 添加了错误处理的语法代码
                    
statement
	:	(declaration) => declaration
	|	expr SEMI
	|	"if" LPAREN expr RPAREN statement
		( TK_else statement )?
	|	"while" LPAREN expr RPAREN statement
	|	block
	;

exception catch [RecognitionException ex] 
    {
       consume();
       while (LA(1) != Token.EOF_TYPE && LA(1)!=SEMI ) {
            consume();
        }
        
        if(LA(1)==SEMI)
        	consume();
       
     return;
    }

block
	:	LCURLY ( statement )* RCURLY
	;

exception catch [RecognitionException ex] 
    {
       consume();
       while (LA(1) != Token.EOF_TYPE  && LA(1)!=RCURLY ) {
            consume();
        }
        
        if(LA(1)==RCURLY)
        	consume();
       
     return;
    }

注意错误处理是内核一个很重要的部分。如果错误处理机制不够好,整个 IDE 都会受到影响,用户有可能无法获得任何代码完成特性或者大纲。考虑这样一种情况:当在一个 Java 文件中,除了键入 int的地方, 其余的代码行下面都出现了红色的下划曲线,因为解析器无法从错误中恢复(参见图 4 )。您不会愿意使用这样的 IDE,不是吗?


图 4. 如果解析器的错误恢复不够好,用户将会看到红色的小曲线
如果解析器得错误恢复不够好,用户将会看到红色的小曲线  

要确保在设计内核时赋予错误处理代码一个高优先级,还可以使用多个测试用例来检查 IDE 如何能很好地从错误中恢复。相对于构建 IDE 的语言的复杂性而言,错误恢复更加复杂。例如,因为 Java 的语法比 ANTLR 的语法 更加复杂,因此相对于一个 ANTLR IDE 而言,一个 Java IDE 需要更加复杂的错误处理代码。

在设计内核时的设计决策

既然您已经了解了与设计内核相关的问题,现在就需要研究一些在创建内核架构时需要确定的设计决策:

增量或者非增量
如上所述,选择增量或者非增量内核将从本质上影响 IDE。您需要从一开始就决定是否要设计一个增量内核。
UI 或者非 UI 线程
如果您决定使用一个增量内核,必须确定是否在一个后台线程或者在编辑器线程中运行解析器或者词法分析器。在 UI 线程中运行将会避免  大量 与同步相关的问题,但内核的速度将会成为麻烦(如果在每次击键后运行于 UI 线程,必须在文档分析上设置一个时间限制)。即使是毫秒级的延迟都将在用户键入字符和屏幕字符显示之间导致迟滞,这会使 IDE的使用 变得无法忍受。 

如果您实现了增量内核,就可以在 IDE 中提供一些非常酷的特性,这些特性对于运行于后台线程的 IDE 来说是不可能拥有的。例如,在 UI 线程中运行分析过程所产生的结果是 ANTLR Studio 能提供 TypeOnce 功能,该功能无需用户按下  Ctrl+Space 来显示自动完成特性,甚至当键入未定义的规则名称时,这项功能也能正常工作。
部分增量
可以不使用增量内核,让 IDE 以增量的方式更新任何东西,一个替代方法是使用部分增量内核。这意味着不使用增量解析器以及增量词法分析器,而只使用增量词法分析器。在分析的部分,词法分析器占用了 80% 的时间,因此使用增量词法分析器将会在很大程度上加快分析过程。采用这种方式,IDE 在速度足够快的同时,就不需要设计得如此复杂。NetBeans IDE 采用了这种方式。
错误恢复策略
当用户键入代码时,您需要识别解析器或者词法分析器可能会遇到的错误的最常用形式。您必须添加定制的代码来确保解析器能很好地从这些错误中恢复。否则,用户在使用 IDE 的过程中,将会有不愉快的经历,因为当用户需要执行一个操作时,如代码完成操作,IDE 中没有任何关于关键点代码的信息。

结束语

 创建一个具有商业品质的 Eclipse IDE” 系列的第 1 部分研究了 IDE 的通用架构,并详细分析了与实现 IDE 内核相关的问题。现在,您应该了解了一个健壮的高性能内核的重要性,以及与在 UI 或者非 UI 线程中运行解析器或者词法分析器相关的设计问题。本教程还分析了与内核进行通信的 UI 部分的相关问题,以及内核中健壮错误处理的重要性。

本系列的第 2 部分将分析 UI 以及 Eclipse 提供的 API 框架,这会使 IDE 的编写更加容易。

参考资料

学习

获得产品和技术

讨论


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值