make mark all like this scope sensitive in javascript

sublime text 2的多cursor功能很强大,在做某些操作的时候很方便。但是标记
多cursor也很麻烦。emacs的multiple-cursors可以用来模仿这个特性,而且
更好的是它完全可以融入emacs的环境里,配合其他mode来使用会更加强大。但是
有个小问题是它的标记是基于正则的,这在有些时候不太方便,比如在js里,我
想标记某个变量的所有实例,就很难。于是自己写了一个函数来解决这个问题。

sycao/js2-mark-token-at-point
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

(defun sycao/js2-mark-token-at-point ()
"mark all semantic instances of the variable under cursor within the defining scope"
(interactive)
(mc/remove-fake-cursors)
(let ((ref-node (js2-node-at-point))
(ref-node-beg nil)
name
scope)

(setq ref-node-beg (js2-node-abs-pos ref-node))
(unless (region-active-p)
(goto-char ref-node-beg)
(push-mark (+ ref-node-beg (js2-node-len ref-node)))
(activate-mark))

(save-excursion
(when (and ref-node (js2-name-node-p ref-node))
(setq scope (js2-node-get-enclosing-scope ref-node)
name (js2-name-node-name ref-node))
(setq scope (js2-get-defining-scope scope name))
(js2-visit-ast
scope
(lambda (node end-p)
(when (and (not end-p)
(js2-name-node-p node)
(string= name (js2-name-node-name node)))
(let* ((beg (js2-node-abs-pos node))
(end (+ beg (js2-node-len node)))
(new-scope (js2-node-get-enclosing-scope node))
(new-scope (js2-get-defining-scope new-scope name)))
(if (and (eq new-scope scope)
(not (= beg end)))
(progn
(goto-char end)
(push-mark beg)
(exchange-point-and-mark)
(if (not (= ref-node-beg beg))
(mc/create-fake-cursor-at-point))
(exchange-point-and-mark)
))))

t)))))
(if (> (mc/num-cursors) 1)
(multiple-cursors-mode 1)
(multiple-cursors-mode 0)))

我参考了js2-highlight-vars.el和mc/mark-all-like-this的代码。基本思
路就是利用js2-mode提供的api遍历ast,找到所有目标变量的引用,并进行标记。
需要注意一点的是,要把函数自身加入mc--default-cmds-to-run-once里,否
则会产生循环调用。

1
(add-to-list 'mc--default-cmds-to-run-once 'sycao/js2-mark-token-at-point)

例如用下面的代码进行测试,如果光标在第二行的localVar上,执行
sycao/js2-mark-token-at-point就会标记第2、9、11行的localVar。

var localVar = 12;
1
2
3
4
5
6
7
8
9
10
11
12
13

function f() {
var localVar = 10;

function f2() {
var localVar = 20;
localVar++;
}

localVar += 1;
f2 ();
localVar += 9;
}