imenu 为 js2-mode 定制 imenu--generic-function

如何在imenu--generic-function定义function regex的时候ignore一些case。

目前的正则表达式是这样的。但是这样会匹配到if语句,希望能够将if, while之类的ignore掉。

 ("Function" "^[ \t]*\\([A-Za-z_$][A-Za-z0-9_$]+\\)[ \t]*([a-zA-Z0-9, ]*) *\{ *$" 1) ;; xxx (e) { }

有试过这个,但是并不行。

("Function" "^[ \t]*\\([^if|while|for][A-Za-z_$][A-Za-z0-9_$]+\\)[ \t]*([a-zA-Z0-9, ]*) *\{ *$" 1) ;; xxx (e) { }

参考的陈斌大神的博客。http://blog.binchen.org/posts/why-emacs-is-better-editor-part-two.html

Emacs 的正则怕是做不到。

这个不行是因爲 就算 [^if|while|for] 排除了这三個词,[A-Za-z_$][A-Za-z0-9_$]+\ 也会把这三個词匹配进去。

如果这么写能排除 if/while/for,那函数名当中是不是也不能有这三个词?

这是山人配置里的一个正则,看上去区别只是while|for后面多了个空格。我也不知道这个正则的规则到底怎么样的。感觉imenu又加了一些规则,像是\\( \\)中间的,好像就是能显示在imenu里面的string。

 ("Function" "^[ \t]*\\([^while|for ][a-zA-Z0-9_$]*\\)[ \t]*([a-zA-Z0-9_$,/\\* ]*)[ \t]*" 1)

这个表达式有问题吧,[^a-z] 表示不匹配 a 到 z 范围内的字符,[^az] 表示不匹配 ‘a’ 和 ‘z’ 两个字符。所以整个表达式相当于把所有 ‘w’ ‘h’ ‘i’ ‘l’ ‘e’ ‘f’ ‘o’ ‘r’ 开头的函数都过滤掉了:

#+BEGIN_SRC emacs-lisp :results output
(with-temp-buffer
  (insert "\
if () {}
while () {}
for () {}
ixxx () {}
fxxx () {}
wxxx () {}
foo () {}
bar () {}
quux () {}")
  (goto-char (point-min))
  (while (re-search-forward
          "^[ \t]*\\([^while|for ][a-zA-Z0-9_$]*\\)[ \t]*([a-zA-Z0-9_$,/\\* ]*)[ \t]*"
          nil t)
    (princ (format "(match-string 1)    => [%s]\n" (match-string 1)))
    ))
#+END_SRC

#+RESULTS:
: (match-string 1)    => [bar]
: (match-string 1)    => [quux]

for 后面那个空格应该是不起作用的,因为被前面的规则吃了。for 前面的 ‘|’ 应该也是没有起到“或”的作用,而是作为普通字符。

1赞

:+1:所以很好奇emacs里面到底是怎么写的 :joy:

imenu-generic-expression 的正则表达式参数也可以是一个函数:

(MENU-TITLE REGEXP INDEX [FUNCTION] [ARGUMENTS...])
            ^^^^^^

REGEXP is a regular expression matching a definition construct which is to be displayed in the menu. REGEXP may also be a function, called without arguments. It is expected to search backwards. It must return true and set ‘match-data’ if it finds another element.

所以你可以在函数里面排除掉不想要的结果。我自己举了一个简单的例子,试了下是可以的:

假设文件内容为:

# foo
# bar
# baz
# auz

再设我想得到 # xxx 这样的 Imenu,但是排除掉 bar,可以通过:

(defun foo-imenu-generic-expression-regexp ()
  (if (re-search-backward "^# \\(.*\\)" nil t)
      (if (string= (match-string 1) "bar")
          (foo-imenu-generic-expression-regexp)
        t)
    nil))

(setq imenu-generic-expression '((nil foo-imenu-generic-expression-regexp 1)))

得到了预期的结果:

2赞

排除需要 (?!pattern),但是 emacs 目前不支持,看样子也没打算要支持:

所以你只能像楼上说的,再过滤一遍。或者再找找其他特征,你这是什么语言,函数定义会跟 if/while 混淆?


Update

如果是 elisp,并且过滤的是函数调用(不是定义),那你几乎把所有源代码都列出来了,意义何在?

况且 if/while… 这些在 lisp 中也是函数。

多谢多谢,可以了!

是js的,react里面会有,像

ComponentDidMount() {

}
render(){

}

就没有任何函数定义的keyword。

if / while 不会出现在顶层吧,我看 js2-imenu-extra-generic-expression 根本就不考虑这个问题:

("Function" "^[ 	]*\\([A-Za-z_$][A-Za-z0-9_$]+\\)[ 	]*([a-zA-Z0-9, ]*) *{ *$" 1)

顶层是什么意思?感觉默认给的问题都多多少少有点,像你给的这个,小括号里都不允许_.'",但写参数经常会用啊。

这个正则不能处理 default parameter, object destruct 和换行。

比如下面这个函数就匹配不到。

    ShareNormal ({picName = "",
                          title = "",
                          reportIDLaunch = 0,
                          getGroupMsgTicket = false,
                          reportID = 0,
                          cbSuccess = undefined,
                          cbFailed = undefined} = {}){

我改了一下可以支持了:

^[ \t]*\\(async\\)?[ \t]*\\([A-Za-z_$][A-Za-z0-9_$]+\\)[ \t]*([\{a-zA-Z0-9, \\\)\('\"=\t\n\}]*) *\{ *
1赞
ShareNormal ({picName = ")",
                          title = " \") {",
                          reportIDLaunch = 0,
                          getGroupMsgTicket = false,
                          reportID = 0,
                          cbSuccess = undefined,
                          cbFailed = undefined} = {}){

这个呢?

现在可以了,你试试,添加了 ‘()\ 在默认参数里面的支持

其实我发现像tidelsp-javascript-typescript现在都支持imenu了,根据lang server的相对比较准确。