gyn

Win32下的Perl,无用的select,停滞的Tk,结束吧....

Cucumber2的语法高亮代码分析

在写代码的时候,不应该仅仅追求完成某件功能,而是需去寻找一个尽量优雅的方法解决。我不单是这么想,而且也的的确确这么做的。在处理 cucumber 的语法高亮模块时,我思考了很久。最后选择了 rename 办法,即是通过 rename 和重载原命令来得到命令参数,实现代码注入。举个例子:

1.package require Tk

2.text .t

3.pack .t

4.rename .t t

5.proc .t {args} {

6.    # injected code

7.    uplevel #1 t $args

8.}

对于复杂的对象,处于简化使用或者其他的目的,往往会隐藏一些底层的方法,而仅仅提供高级的抽象功能。比如 text 组件的剪切功能,将删除、赋值和标记设定等命令以一定的方式顺序组合在一起,对程序员提供一个指令来完成这一系列的事件。以上的办法为在执行这些底层命令前提供了捕获控制的机会。在高亮的解决方案中,通过这种机制,介入所有的 text 组件指令,筛选出 insert delete mark set see insert 这四个指令,完成当发生包括输入,黏贴、删除,剪切、撤销和重做等事件时的文本高亮渲染。

在这个基本思想的基础上, cucumber1.1.1 版本实现了关键词、变量、注解和引号语句的高亮,但如在实现顺序上有错疏忽,虽然可能不会对破坏的渲染结果,但是会极大影响大文本渲染的效率。为什么单指大文本,因为对于文本输入一般包括键盘输入和大段文本的载入如打开文件或者黏贴。与电脑的处理相比无论打字速度多快,其中的间歇都是足够渲染处理打入的单个符号的上下文环境;而大文本的载入首先是一次操作完成的,与下一个操作之间只有一次间隔,第二需要渲染的内容很多,这么一来渲染效率的瓶颈边出现在大文本处理上了。所以,我所说的效率可以理解为特指大文本渲染效率。在第二个版本里我是使用了以下的一系列优化代码。在这里我首先通过一下函数完成捕获

1.proc _tool_core_fake {cucumber fake} {

2.    set ::$fake [regsub {(.*)\..+$} $cucumber {\1.linumColumn}]

3.    rename $cucumber $fake

4.    eval "proc $cucumber {args} {

5.        after idle \[_tool_core_sortMap $fake \$args\]

6.        uplevel 1 $fake \$args

7.    }"

8.}

_tool_core_fake

2 行是设置了一个与当前 text 文本相对应的行号显示组件变量,这里就不再多说了。真正的高亮渲染注入发生在第 5 行,通过以下的函数返回需要执行的指令集。

1.proc _tool_core_sortMap {fake args} {

2.    eval "set args $args"

3.    switch -glob -- $args {

4.        insert* {

5.            set from [$fake index [lindex $args 1]]

6.            set to "$from+[string length [lindex $args 2]]c"

7.            return "

8.            _tool_core_hiContent $fake $from $to;

9.            _tool_incrLinum $fake;

10.            _tool_core_hiLine $fake

11.            "

12.        }

13.        delete* {

14.            set from [$fake index [lindex $args 1]]

15.            return "

16.            _tool_core_hiContent $fake $from $from;

17.            _tool_decrLinum $fake

18.            _tool_core_hiLine $fake

19.            "

20.        }       

21.        {mark set insert*} { return "_tool_core_hiLine $fake" }

22.        {see insert} { return "_tool_core_hiLine $fake" }

23.    }

24.}

_tool_core_sortMap

第二行的作用是消除字符串外的大括号,将如“ {insert end nihao} ”变化为“ insert end nihao ”。之后使用 glob 匹配的 switch 来筛选命令参数。在 1.1.1 版本的筛选函数中,首先我没有加入用于筛选高亮当前行显示的 insert 标记判断,而是在单独的模块中通过绑定事件来完成,这显然是累赘的;第二,最初使用 none 函数来处理非匹配的指令,这个自以为保证代码一致性的手法在事后证明是一个画蛇添足的举动,因为即使是一个空函数,也存在执行消耗,而大量累加执行的结果就是拖慢渲染的速度,这在 1.1.1 中已经被删除了;另外,在处理 insert delete 时,存在值得商榷的渲染级别。在目前代码中可以看见,事实上渲染发生在以上代码的第 8 行和第 16 行里的 _tool_core_hiContent 函数里。顺便提一下,在该函数以下的两个命令,跟行号和当前行指示有关,这里不做详解。

1.proc _tool_core_hiContent {fake from to} {

2.    set temp $from

3.    set temp [_tool_core_hiWord $fake $temp]

4.    while {[$fake compare $temp <= $to]} {

5.        set temp [_tool_core_hiWord $fake $temp]

6.    }

7.    _tool_core_hiQuoteContext $fake

8.    _tool_core_hiCommentContext $fake $from $to

9.}

_tool_core_hiContent

_tool_core_hiConetnt 的文本渲染函数中,包含了三个同层次的渲染,分别是处于 index 位于 from to 之间的单词循环渲染和各行注解渲染,以及一次全文引号渲染。这么安排的道理在于,一般情况系关键词和变量都是以单词的形式出现的,可以统一在 _tool_core_hiWord 中处理;注解可能占据一行或者后半行,与其在每次单词渲染时判断是否存在注解起始符,不如在处理完所有的单词之后统一检索当前文本段,前者是单词数量级的运算,而后者是行数量级的运算,显然后者更为高效;引号数量的单双数变化,会造成全文渲染范围的颠倒,但考虑到引号数量与单词相比可能相差两三个数量级,因此这种全文渲染变化是很快速的,尤其是我更引入了延迟渲染的机制,消耗更是可以忽略不计。以下是这三个函数。

1.proc _tool_core_hiWord {fake id} {

2.    if [_tool_ifInWord $fake $id] {

 3.       set head [_tool_indexWordHead $fake $id]

 4.       set end  [_tool_indexWordEnd $fake $id]

 5.       foreach hi {

 6.           _tool_core_hiTextContext

 7.           _tool_core_hiVariableContext

 8.       } { $hi $fake $head $end}

 9.       return "$end +1c"

10.    }

11.    return "$id +1c"

12.}

_tool_core_hiWord

1 proc _tool_core_hiCommentContext {fake from to} {

 2.   set f [_tool_linum $fake $from]

 3.   set t [_tool_linum $fake $to]

 4.   while {$f <= $t} {

 5.       $fake tag remove Com "$f.0" "$f.0 lineend"

 6.       if {[$fake search -regexp $::hi_comment_regexp "$f.0" "$f.0 lineend"] != {}} {

 7.           $fake tag add Com [$fake search -regexp $::hi_comment_symbol "$f.0" "$f.0 lineend"] "$f.0 lineend"

 8.       }

 9.       incr f

10.    }

11.}

_tool_core_hiCommentContext

1.proc _tool_core_hiQuoteContext {fake} {

2.    after cancel [list _hiQuoteContext $fake]

3.    after 1000 [list _hiQuoteContext $fake]

4.}

5.proc _hiQuoteContext {fake} {

6.    if {[$fake get 1.0] == "\""} { set rid [list 1.0] } else { set rid {} }

7.    set qid [$fake search -nolinestop -overlap -all -regexp {[^\\]\"} 1.0 end]

8.    foreach id $qid { lappend rid [$fake index [$fake index "$id + 1c"]] }

9.   

10.    if ![set e [llength $rid]] {return}

11.    set now [lindex $rid 0]

12.    $fake tag remove Quo 1.0 $now

13   

14.    set i 1

15.    while {$i < $e} {

16.        set old $now

17.        set now [lindex $rid $i]

18.        if [expr $i % 2] {

19.            $fake tag add Quo $old "$now +1c"

20.        } else {

21.            $fake tag remove Quo "$old +1c" $now

22.        }

23.        incr i

24.    }

25.   

26.    if [expr $e % 2] {

27.        $fake tag add Quo "$now +1c" end

28.    } else {

29.        $fake tag remove Quo "$now +1c" end

30.    }

31.}

_tool_core_hiQuoteContext

posted on 2009-09-24 20:45 gyn_tadao 阅读(501) 评论(0)  编辑 收藏 引用 所属分类: TclTk

只有注册用户登录后才能发表评论。
<2009年9月>
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910

导航

统计

常用链接

留言簿(14)

随笔分类(126)

随笔档案(108)

相册

搜索

最新评论

阅读排行榜

评论排行榜