Yggdroot / LeaderF

An efficient fuzzy finder that helps to locate files, buffers, mrus, gtags, etc. on the fly for both vim and neovim.
Apache License 2.0
2.15k stars 179 forks source link

使用vimL来写扩展 #144

Open skywind3000 opened 6 years ago

skywind3000 commented 6 years ago

新命令:

:LeaderfAny[!] 

可以提供一个 list 列表,和一个 callback 函数,用 Leaderf 的搜索方式提供搜索,关闭 leaderf 窗口后,调用一下用户提供的回调,告知选择结果,null 代表用户没做选择。

如果还能提供个额外的语法文件支持就太好了啊。

Yggdroot commented 6 years ago

哎,有点小忙,我对这个插件也只是fix一些bug,好久没有增加新的feature了。

我也一直想实现这个功能,这个功能就类似于fzf了,可以自己随便自定义要搜索的内容。 要不然还是你帮忙来实现,有问题我们一起讨论?

skywind3000 commented 6 years ago

好吧,我先研究下 denite 去,先把命令接口讨论清楚了,我再照着 LeaderfFunction 改改。 让用户提供 source,preview function 和一个 callback 即可。

skywind3000 commented 6 years ago

写好了 ??

Yggdroot commented 6 years ago

以下内容已经过时,请参考 https://github.com/Yggdroot/LeaderF/issues/144#issuecomment-540008950

功能大致实现了,还有些细节问题。 在any branch。

let g:Lf_Extensions = {
    \ "apple": {
    \       "source": [], "grep -r '%s' *", funcref (...) 
    \       "format": funcref ([], ...),
    \       "accept": funcref (line, ...),
    \       "options": [],
    \       "preview": funcref,
    \       "supports_name_only": 0,
    \       "get_digest": funcref,
    \       "before_enter": funcref (...),
    \       "after_enter": funcref (orig_buf_nr, orig_cursor, ...),
    \       "bang_enter": funcref (orig_buf_nr, orig_cursor, ...),
    \       "before_exit": funcref (orig_buf_nr, orig_cursor, ...),
    \       "after_exit": funcref (...),
    \       "highlights_def": {
    \               "Lf_hl_apple": '^\s*\zs\d\+',
    \               "Lf_hl_appleId": '\d\+$',
    \       },
    \       "highlights_cmd": [
    \               "hi Lf_hl_apple guifg=red",
    \               "hi Lf_hl_appleId guifg=green",
    \       ],
    \       "supports_multi": 0,
    \       "supports_refine": 0,
    \ },
    \ "orange": {}
\}

字典中只有sourceaccept是mandatory的, 其他都是optional的。

  1. source 可以是个list,也可以是个字符串,如果是字符串就是个命令,还可以是个Funcref。如果是Funcref,函数签名是不定参数(...),会接受Leaderf test --a --b --c命令中的“--a”, "--b", "--c", 可以根据参数自来决定不同的返回值,返回类型是个list
  2. format是个Funcref,作用是对source的结果进行格式化,比如source是个ctags命令, 但是想对ctags的输出进行调整,就用format函数修改一下,返回值是个list。format函数的签名是([], ...), list是source的返回值,...跟source中一样。
  3. accept是个Funcref,签名是 (line, ...),是响应回调,...同上,也许会根据某个参数做不同的响应。
  4. options指定可以有哪些选项,补全时可以用到,是个list。
  5. preview是个Funcref,还没实现,具体没想好。
  6. supports_name_only是bool类型,如果搜索内容是类似文件路径这样的,就跟LeaderfFile一样。
  7. get_digest,是个Funcref,签名是(line, mode),返回值类型是个list,[不同模式下要匹配的字符串,对应的起始位置(in bytes)]
        specify what part in the line to be processed and highlighted
        Args:
            mode:
                  0, return the full path和full path的起始位置
                  1, return the name only和name only的起始位置
                  2, return the directory name和directory name的起始位置
  8. before_enter, after_enter, before_exit, after_exit 分别是进入LeaderF窗口前,进入后,退出前,退出后的回调,签名在上面。
  9. bang_enter是在bang模式下刚进入时的回调,签名是(orig_buf_nr, orig_cursor, ...),orig_buf_nr是原来buffer的number,orig_cursor是原来buffer光标的位置[行,列],行列都是从1开始。...同上。它的作用就像你上次实现的_relocateCursor
  10. highlights_defhighlights_cmd可以定义高亮。Note, pattern部分最好用单引号引起来,不然反斜杠要用两个。
  11. supports_multi是bool类型,指定是否支持多选,缺省不支持。比如当前LeaderfFile就支持多选,shift+鼠标可以选择多行。
  12. supports_refine是bool类型,指定是否支持refine,缺省不支持。refine就是LeaderfFile在nameOnly模式下,有多个同名的,输入分号,然后再匹配路径。

目前主要功能已经差不多了,已经支持选项"--top", "--bottom", "--left", "--right", "--belowright", "--aboveleft", "--fullScreen"--cword(当前光标下的单词直接作为模式字符串进行匹配), 例如: Leaderf test --right --cword 这是我的测试例子:

function! Source(...)
    return ["aaabcaabcabc 23333333333", "bbaabbccdbb 345555533333", "caaaabbbbcccc 453333333"]
endfunction
function! Get_digest(line, mode)
    if a:mode == 0
        return [a:line, 0]
    elseif a:mode == 1
        return [split(a:line)[0], 0]
    else
        return [split(a:line)[1], len(a:line) - len(split(a:line)[1])]
    endif
endfunction
function! Accept(line, ...)
    exec "edit ".split(a:line)[1]
endfunction
function! Bang(orig_buf_nr, orig_cursor, ...)
    exec "norm! 2G"
endfunction
let g:Lf_Extensions = {
            \ "apple": {
            \       "source": function("Source"),
            \       "supports_name_only": 1,
            \       "get_digest": function("Get_digest"),
            \       "accept": function("Accept"),
            \       "highlights_def": {
            \               "Lf_hl_apple": '\w\+',
            \               "Lf_hl_appleId": '\d\+',
            \       },
            \       "highlights_cmd": [
            \               "hi Lf_hl_apple guifg=red",
            \               "hi Lf_hl_appleId guifg=green",
            \       ],
            \       "bang_enter": function("Bang"),
            \       "supports_multi": 1,
            \       "supports_refine": 0,
            \ },
            \ "orange": {
            \       "source": "git ls-files",
            \       "supports_name_only": 0,
            \       "highlights_def": {
            \               "Lf_hl_bufNumber": "^\s*\zs\d\+",
            \               "Lf_hl_buf": "^\s*\zs\d\+",
            \       },
            \       "highlights_cmd": [
            \               "hi Lf_hl_bufNumber guifg=red",
            \               "hi Lf_hl_buf guifg=green",
            \       ],
            \       "supports_multi": 0,
            \       "supports_refine": 0,
            \ },
            \}

可以使用Leaderf apple --right

你看看还有什么需要注意和改进的地方。

skywind3000 commented 6 years ago

我擦,帅啊,我明天看看,初看感觉可定制度快赶上 denite 了。

skywind3000 commented 6 years ago

看了一下,功能上我好像没什么补充了,补充点式样方面,如果每行内容采用 tab 分隔的话,可以被识别成不同的语法高亮,定义成:

LfColorColumn1
LfColorColumn2
LfColorColumn3
LfColorColumn4

之类,可以修改的,然后能否有一个对齐选项,就是列表里所有 tab 分隔的记录可以统计长度,进行对齐,比如 LeaderfBufTag 搜索窗口中,如果对齐的话,会好看很多,也更容易识别。

Yggdroot commented 6 years ago

这个好像不太好做吧,因为source的内容不确定,不一定是分成几块的。而且直接采用tab分隔,不一定能对齐。像LeaderfBuffer,LeaderfBufTag, 我都是做了计算的,跟名字最长的那个对齐。 语法高亮可以在highlights_def里自己定义,也很方便。

skywind3000 commented 6 years ago

BufTag 经过对齐了?为啥我经常发现没对齐,比如:

align

对齐 bug ?

Yggdroot commented 6 years ago

这是做了点妥协,并不是跟最长的对齐,而是平均长度的1.5倍,防止因为某一个特别的长,导致中间有很大空白,这样保证大部分是对齐的。当然还可以是平均长度的2倍或更多。

skywind3000 commented 6 years ago

其实如果宽度够,能不能不管这个妥协?现在都是 16:9的LCD了,你看我右边空那么多。

skywind3000 commented 6 years ago

你有四列,如果每列最长没超过窗口宽度四分之一就不用1.5了,直接最长对齐吧。

Yggdroot commented 6 years ago

这是因为是当前buffer,就没有显示文件名。如果是所有buffer,会显示文件名,有时候文件名加上路径会很长。

skywind3000 commented 6 years ago

还可以2/5,1/5,1/5,1/5 比例分配

Yggdroot commented 6 years ago

或者最长长度大于平均长度的2倍就用平均长度的2倍,小于就用最长长度。我还是不打算简单的使用最长长度,因为我们公司的代码,有时候就有某个函数名特别的长。 貌似有些跑题了。

skywind3000 commented 6 years ago

对,这个方法也很好。这样就看起来比较大气了,恩回过头来吧,我貌似没什么补充了,可能要实际用一下才知道。

Yggdroot commented 6 years ago

现在功能基本实现了,我在考虑怎么支持使用Python写。

Yggdroot commented 6 years ago

改好了(https://github.com/Yggdroot/LeaderF/commit/66fbab1b39a79bdc5e6f06d17072e5c661a3b233 ),你更新下。

skywind3000 commented 6 years ago

帅啊,thanks

Yggdroot commented 6 years ago

我已经merge到master了,有空补补文档。

skywind3000 commented 6 years ago

帅啊,怎么用它写个

:LeaderfAny grep printf

Yggdroot commented 6 years ago

你一下子就问到点子上,现在还不支持参数,本来想实现source可以是这样的"grep -r '%s' *",还没实现,还需要加点代码。 我在想怎么来支持呢?

Yggdroot commented 6 years ago

以下内容已经过时,请参考 https://github.com/Yggdroot/LeaderF/issues/144#issuecomment-540008950

skywind3000 commented 6 years ago

辛苦了,周末我研究一下。

tracyone commented 6 years ago

你好反馈一个问题:

macvim的python2使能

vim的python3使能

这两个使用同一个配置以及插件目录,leaderf的其它命令都正常

但是我添加了一个扩展,在macvim下是正常的,在vim下面却出现了如下错误信息:

 searching ...
Error detected while processing function te#tools#vim_get_message[3]..leaderf#Any#start[4]..leaderf#LfPy:
line    1:
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/Users/tracyone/.vim/bundle/LeaderF/autoload/leaderf/python/leaderf/anyExpl.py", line 499, in start
    the_args.start(arguments, *args, **kwargs)
  File "/Users/tracyone/.vim/bundle/LeaderF/autoload/leaderf/python/leaderf/anyExpl.py", line 466, in _default_action
    manager.startExplorer(win_pos[2:], *args, **kwargs)
  File "/Users/tracyone/.vim/bundle/LeaderF/autoload/leaderf/python/leaderf/anyExpl.py", line 312, in startExplorer
    super(AnyExplManager, self).startExplorer(win_pos, *args, **kwargs)
  File "/Users/tracyone/.vim/bundle/LeaderF/autoload/leaderf/python/leaderf/manager.py", line 787, in startExplorer
    if len(content[0]) == len(content[0].rstrip("\r\n")):
TypeError: a bytes-like object is required, not 'str'

Error detected while processing function youcompleteme#Enable[7]..<SNR>142_SetUpOptions[4]..<SNR>142_SetUpKeyMappings:
line   59:
E227: mapping already exists for 
let g:Lf_Extensions = {
            \ "dir": {
            \       "source": function("te#leaderf#dir#source"),
            \       "accept": function("te#leaderf#dir#accept"),
            \       "supports_name_only": 1,
            \       "supports_multi": 0,
            \ },
            \}
Yggdroot commented 6 years ago

@tracyone 你试一下,在dev branch,看看有没有fix。另外那个map冲突的问题应该跟LeaderF无关。

tracyone commented 6 years ago

@Yggdroot 第一次打开的时候没错误了,可是筛选的时候出现了以下错误,同样配置macvim不会出现。

screen shot 2018-09-03 at 20 00 18
Yggdroot commented 6 years ago

@tracyone 这是python3上的问题,单独开一个issue讨论吧。 你能给我你的扩展所有代码吗?这样我可以方便调试。只根据你图上的错误信息不太容易查。

tracyone commented 6 years ago

hi,我想问下,accept的时候如何得知当前用户的动作呢?是按回车还是准备标签页打开呢?

可能,我描述的不清楚:

就是通常在浏览文件的时候,按回车是直接当前buffer打开,而按ctrl-t的时候标签页打开。

Yggdroot commented 6 years ago

目前没法知道。除非修改代码。

wsdjeg commented 6 years ago

我最近也再给leaderf定义一些自定义source,遇到一个问题,比如我需要指定输入,目前仅支持 --cword ,我希望可以更加智能一点比如 --input=hello 起到一个类似于打开leaderf后并默认输入hello的效果,并且这个hello在leaderf打开后是可以修改的。比如启动后按下 <BS> 他就变成 hell

Yggdroot commented 6 years ago

@wsdjeg 已经支持了,readme里有说。

Yggdroot commented 6 years ago

neovim中不支持这样来写扩展,因为neovim不再支持vim.bindeval,我不知道neovim怎么来使用Python获取vimscript写的Funcref,谁如果知道,望告知。

hanxi commented 5 years ago

补个需求,搜出来的结果不自动换行要如何处理?另外感觉速度比 vim-grepper 慢好多。

Yggdroot commented 5 years ago

补个需求,搜出来的结果不自动换行要如何处理?另外感觉速度比 vim-grepper 慢好多。

不自动换行是什么意思? 上面只是个demo,离实用还有点距离。你可以使用:Leaderf rg, 然后跟vim-grepper 比比。

hanxi commented 5 years ago

@Yggdroot 上面的是用 :Leader grep Build搜出来的,搜索结果自动换行了。下面的是用 vim-grepper 搜出来的,结果一行超出屏幕宽度没有自动换行。 image

image

Yggdroot commented 5 years ago

@hanxi 这是因为set wrap了,就是为了看整行的,如果不这样就要拉滚动条了。

hanxi commented 5 years ago

@Yggdroot 不喜欢 set wrap ,因为我这工程目录下有一个自动生成的协议文件一行代码很长很长,一搜到它就占用了四五行。

你这个支持 fuzzy 再次搜索匹配的结果很好用,如果以后这个 grep 功能完善了,我就可以把 vim-grepper 扔了。

Yggdroot commented 5 years ago

@Yggdroot 不喜欢 set wrap ,因为我这工程目录下有一个自动生成的协议文件一行代码很长很长,一搜到它就占用了四五行。

增加了一个--nowrap选项,在命令里加上它就可以了。

你这个支持 fuzzy 再次搜索匹配的结果很好用,如果以后这个 grep 功能完善了,我就可以把 vim-grepper 扔了。

已经有Leaderf rg了呀。

hanxi commented 5 years ago

@Yggdroot 抱歉,昨天没看明白 Leaderf rg 是啥意思,今天看了 README 才发现有这么个命令。赞一个!

hanxi commented 5 years ago

Leaderf rg 太好用了,按 tab 或 i 对搜索结果进行二次搜索。

yongwangd commented 5 years ago

neovim 还是不支持这个功能么?我刚刚在mac里的neovim试了试 得到下面这个错误

function! Data()
    return ['a', 'b', 'c', 'd']
endfunction

function! AcceptEdit(line, ...)
    echom "start"
    echom line
endfunction

let g:Lf_Extensions = {
    \ "apple": {
    \       "source": function("Data")(),
    \       "accept": function("AcceptEdit")
    \ }
\}

去掉function("Data")后面的括号也不行

错误如下 screenshot_2019-01-22 16 21 38_bocpi5

目前主要的搜索都已经转移到Leaderf 不能加扩展是我还保留Denite的唯一原因了。。。惆怅啊

Yggdroot commented 5 years ago

@yongwangd 写扩展可以用Python写呀,可以参考 https://github.com/Yggdroot/LeaderF-marks

thlineric commented 5 years ago

@Yggdroot 請問Leaderf rg 可以加入--heading的功能嗎?

Yggdroot commented 5 years ago

@thlineric 加了就没法模糊匹配了,模糊匹配后跳转需要知道文件信息才能跳过去。

thlineric commented 5 years ago

@thlineric 加了就没法模糊匹配了,模糊匹配后跳转需要知道文件信息才能跳过去。

@Yggdroot 我之前有嘗試把python的--no-heading拿掉...結果就是無法跳過去Orz

liubaohai commented 5 years ago

@Yggdroot 前面提到的bufTag对齐问题。我希望添加个配置来控制是否要尽量对齐。我调试了下,下面是我修改的接口。如果可以的话,希望能合到master。 我个人是希望全部对齐的,可能有些人不喜欢这样(前面提到的有些函数名过长)。因此加个配置比较友好。

    def _formatResult(self, buffer, result):
        if not buffer.name or lfEval("bufloaded(%d)" % buffer.number) == '0':
            return []

        # a list of [tag, file, line, kind, scope]
        output = [line.split('\t') for line in result if line is not None]
        if not output:
            return []

        if len(output[0]) < 4:
            lfCmd("echoerr '%s'" % escQuote(str(output[0])))
            return []

        tag_total_len = 0
        max_kind_len = 0
        max_tag_len = 0
        max_scope_len = 0
        for _, item  in enumerate(output):
            tag_len = len(item[0])
            tag_total_len += tag_len
            if tag_len > max_tag_len:
                max_tag_len = tag_len
            kind_len = len(item[3])
            if kind_len > max_kind_len:
                max_kind_len = kind_len

            scope_len = len(item[4] if len(item) > 4 else "Global")
            if max_scope_len < scope_len:
                max_scope_len = scope_len

        ave_taglen = tag_total_len // len(output)
        tag_len = min(max_tag_len, ave_taglen * 2)

        tab_len = buffer.options["shiftwidth"]
        std_tag_kind_len = tag_len // tab_len * tab_len + tab_len + max_kind_len

        tag_list = []
        almost_align = int(lfEval("g:Lf_AlmostAlign"))
        for _, item  in enumerate(output):
            scope = item[4] if len(item) > 4 else "Global"
            bufname = buffer.name if vim.options["autochdir"] else lfRelpath(buffer.name)
            if almost_align == 0:
                line = "{:<{l1}s}\t{:<{l2}s}\t{:<{l3}s}\t{}:{}\t{}".format(item[0],
                                                                           item[3],
                                                                           scope,
                                                                           bufname,        # file
                                                                           item[2][:-2],   # line
                                                                           buffer.number,
                                                                           l1=max_tag_len,
                                                                           l2=max_kind_len,
                                                                           l3=max_scope_len)
            else:
                tag_kind = "{:{taglen}s}\t{}".format(item[0],   # tag
                                                     item[3],   # kind
                                                     taglen=tag_len
                                                     )
                tag_kind_len = int(lfEval("strdisplaywidth('%s')" % escQuote(tag_kind)))
                num = std_tag_kind_len - tag_kind_len
                space_num = num if num > 0 else 0
                line = "{}{}\t{}\t{:2s}{}:{}\t{}".format(tag_kind,
                                                         ' ' * space_num,
                                                         scope,          # scope
                                                         ' ',
                                                         bufname,        # file
                                                         item[2][:-2],   # line
                                                         buffer.number
                                                         )

            tag_list.append(line)
            if self._supports_preview:
                # code = "{:{taglen}s}\t{}".format(' ' * len(item[0]),
                #                                  buffer[int(item[2][:-2]) - 1].lstrip(),
                #                                  taglen=tag_len
                #                                  )
                code = "\t\t{}".format(buffer[int(item[2][:-2]) - 1].lstrip())
                tag_list.append(code)

        self._tag_list[buffer.number] = tag_list

        return tag_list
Yggdroot commented 5 years ago

又做了一下更新,可以同时给vim和neovim写扩展了。@wsdjeg

现在应该还没有人用vimL给LeaderF写扩展,所以做了些改变,跟前面不兼容了:原来每个回调函数是个Funcref对象,现在改为函数名了。

let g:Lf_Extensions = {
    \ "apple": {
    \       "source": [], "grep -r '%s' *", funcref (arguments), {"command": "ls" or funcref(arguments)}
    \       "arguments": [
    \           { "name": ["--foo", "-f"], "nargs": n or "?" or "*" or "+", help: "hehe"},
    \           { "name": ["bar"], "nargs": n or "?" or "*" or "+" }
    \       ],
    \       "format_line": funcref (line, arguments),
    \       "format_list": funcref ([], arguments),
    \       "accept": funcref (line, arguments),
    \       "preview": funcref (orig_buf_nr, orig_cursor, line, arguments),
    \       "supports_name_only": 0,
    \       "get_digest": funcref (line, mode),
    \       "before_enter": funcref (arguments),
    \       "after_enter": funcref (orig_buf_nr, orig_cursor, arguments),
    \       "bang_enter": funcref (orig_buf_nr, orig_cursor, arguments),
    \       "before_exit": funcref (orig_buf_nr, orig_cursor, arguments),
    \       "after_exit": funcref (arguments),
    \       "highlights_def": {
    \               "Lf_hl_apple": '^\s*\zs\d\+',
    \               "Lf_hl_appleId": '\d\+$',
    \       },
    \       "highlights_cmd": [
    \               "hi Lf_hl_apple guifg=red",
    \               "hi Lf_hl_appleId guifg=green",
    \       ],
    \       "highlight": funcref (arguments),
    \       "need_exit": funcref (line, arguments),
    \       "supports_multi": 0,
    \       "supports_refine": 0,
    \ },
    \ "orange": {}
\}

字典中只有source和accept是mandatory的, 其他都是optional的。

  1. source 可以是以下几种类型

    • list 例如:["aaa", "bbbb", "cccc"]
    • string 是函数名,签名是(arguments)arguments是个字典,会在下面详细说明。 返回值是个list
    • dict({"command": string or Funcref}) dict只有一个元素, "command"的值可以是个string,例如:git ls-files;如果想传参数给这个命令,在命令里使用"%s", 例如:grep -r '%s' %s,如果命令中有“%s", 下面说的位置参数必须与"%s"相对应(个数,顺序);也可以是个Funcref,签名是(arguments),返回值是个命令字符串。
  2. arguments用来指定命令的参数,参数分为两种类型:

    • 位置参数 前面不带--或者-的是位置参数,位置参数在命令行中一般是必填项(当然也可以是可选),如果缺失会报错。而且位置参数之间的顺序跟定义时要一致,顺序乱了也会出错。
    • 可选参数 前面带--或者-的是可选参数。可选参数,顾名思义就是optional的。 arguments的格式如下:
    "arguments": [
      { "name": ["--foo", "-f"], "nargs": n or "?" or "*" or "+", "help": "hehe"},
      { "name": ["bar"], "nargs": n or "?" or "*" or "+" },
      [ 
        { "name": ["--big"], "nargs": n or "?" or "*" or "+" },
        { "name": ["--small"], "nargs": n or "?" or "*" or "+" }
      ]
    ]

    "name"是个list,可选参数一般可以有长名和短名。"nargs"指定参数的个数,值可以是个整数N(0,1,2 ...),也可以是"?"(0个或1个),"*"(0个或多个),"+"(1个或多个)。不管"nargs"指定的个数是0、1,还是多个,命令行都会把参数放到一个list中,例如:

    "arguments": [
      { "name": ["--foo", "-f"], "nargs": 0, "help": "hehe"},
      { "name": ["--bar"], "nargs": 1, "help": "hehe"},
      { "name": ["--name"], "nargs": "+", "help": "hehe"},
    ]

    则在解析命令Leaderf grep --name aaa bbb ccc --foo --bar hello时,会形成一个这样的dict作为上面提到的函数签名中的arguments

    {
        "--foo":  [],
        "--bar":  ["hello"],
        "--name": ["aaa", "bbb", "ccc"]
    }

    "help"是可选的,主要是使用打印帮助时用的,例如使用:Leaderf file -h就可以查看Leaderf file的帮助。 对于一些参数是互斥的,需要再放到一层list中去,例如上面的"--big"和"--small"。

  3. format_line类型是string, 指向一个函数名,函数作用是对source结果中的行进行格式化,签名是(line, arguments),返回格式化后的行。

  4. format_list类型是string, 指向一个函数名,函数作用是对source的结果进行格式化,返回值是个list。函数的签名是([], arguments), list是source的返回值,返回格式化后的list。

  5. accept类型是string, 指向一个函数名,函数作用是对当前选中的行做操作,当按下回车时调用这个函数。函数签名是 (line, arguments),line是当前选中的行,arguments同上,也许会根据某个参数做不同的响应。

  6. preview类型是string, 指向一个函数名,函数签名是(orig_buf_nr, orig_cursor, line, arguments)。orig_buf_nr是原来buffer的number,orig_cursor是原来buffer光标的位置[行,列],行列都是从1开始,line是当前选中的行, arguments同上。返回值可以是[], 表示preview什么都不做,或者返回[filename, line_num, jump_cmd], filename 是预览的文件名,line_num是预览buffer里的行号,jump_cmd 是个vim里的一个命令,用来跳转到具体某一行,比如当获取不到行号时可以用,它可以是空的,如果不空,就用它跳转,否则用行号跳转。

  7. supports_name_only是bool类型,如果搜索内容是类似文件路径这样的,就跟LeaderfFile一样。

  8. get_digest,类型是string, 指向一个函数名,函数签名是(line, mode),返回值类型是个list,[不同模式下要匹配的字符串,对应的起始位置(in bytes)]

    specify what part in the line to be processed and highlighted
        Args:
            mode:
                  0, return the full path和full path的起始位置
                  1, return the name only和name only的起始位置
                  2, return the directory name和directory name的起始位置
  9. before_enter, after_enter, before_exit, after_exit 类型是string, 分别是进入LeaderF窗口前,进入后,退出前,退出后的回调,函数签名(orig_buf_nr, orig_cursor, arguments)。

  10. bang_enter类型是string,是在bang模式下刚进入时的回调,签名是(orig_buf_nr, orig_cursor, arguments)。

  11. highlights_defhighlights_cmd可以定义高亮。Note, pattern部分最好用单引号引起来,不然反斜杠要用两个。 highlights_def类型是个dict,highlights_cmd类型是哦list highlight类型是string, 指向一个函数名,函数签名是(arguments),有时需要根据arguments来设置高亮,可以用它。返回一个list,list内容是matchadd()的返回id

  12. need_exit类型是string, 指向一个函数名,函数签名是(line, arguments)。用来指定按回车后,LeaderF窗口是否需要退出,例如,如果当前行是路径,就不退出,return 0,是文件名,就退出 return 1

  13. supports_multi是bool类型,指定是否支持多选,缺省不支持。比如当前LeaderfFile就支持多选,shift+鼠标可以选择多行。

  14. supports_refine是bool类型,指定是否支持refine,缺省不支持。refine就是LeaderfFile在nameOnly模式下,有多个同名的,输入分号,然后再匹配路径。

这是我写的一个demo:

function! Grep(args)
    let ignorecase = ""
    if has_key(a:args, "--ignore-case")
        let ignorecase = "-i"
    endif

    let whole_word = ""
    if has_key(a:args, "-w")
        let whole_word = "-w"
    endif

    return printf("grep -n -I -r %s %s '%s' .", ignorecase, whole_word, get(a:args, "pattern", [""])[0])
endfunction

function! FormatLine(line, args)
    let line = substitute(a:line, '^[^:]*\zs:', '\t|', '')
    let line = substitute(line, '|\d\+\zs:', '|\t', '')
    return line
endfunction

function! Accept(line, args)
    let items = split(a:line, '\t')
    let file = items[0]
    let line = items[1][1:-2]
    exec "edit +".line." ".file
    norm! zz
    setlocal cursorline!
    redraw
    sleep 100m
    setlocal cursorline!
endfunction

function! Preview(orig_buf_nr, orig_cursor, line, args)
    let items = split(a:line, '\t')
    let file = items[0]
    let line_num = items[1][1:-2]
    return [file, line_num, ""]
endfunction

function! Highlight(args)
    highlight Grep_Pattern guifg=Black guibg=lightgreen ctermfg=16 ctermbg=120

    " suppose that pattern is not a regex
    if has_key(a:args, "-w")
        let pattern = '\<' . get(a:args, "pattern", [""])[0] . '\>'
    else
        let pattern = get(a:args, "pattern", [""])[0]
    endif

    if has_key(a:args, "--ignore-case")
        let pattern = '\c' . pattern
    endif

    let ids = []
    call add(ids, matchadd("Grep_Pattern", pattern, 20))
    return ids
endfunction

function! Get_digest(line, mode)
    " full path, i.e, the whole line
    if a:mode == 0
        return [a:line, 0]
    " name only, i.e, the part of file name
    elseif a:mode == 1
        return [split(a:line)[0], 0]
    " directory, i.e, the part of greped line
    else
        let items = split(a:line, '\t')
        return [items[2], len(a:line) - len(items[2])]
    endif
endfunction

function! Do_nothing(orig_buf_nr, orig_cursor, args)
endfunction

let g:Lf_Extensions = {
            \ "grep": {
            \       "source": {"command": function("Grep")},
            \       "arguments": [
            \           { "name": ["pattern"], "nargs": 1 },
            \           { "name": ["--ignore-case", "-i"], "nargs": 0 },
            \           { "name": ["-w"], "nargs": 0, "help": "Select  only  those lines containing matches that form whole words." },
            \       ],
            \       "format_line": "FormatLine",
            \       "accept": "Accept",
            \       "preview": "Preview",
            \       "supports_name_only": 1,
            \       "get_digest": "Get_digest",
            \       "highlights_def": {
            \               "Lf_hl_grep_file": '^.\{-}\ze\t',
            \               "Lf_hl_grep_line": '\t|\zs\d\+\ze|\t',
            \       },
            \       "highlights_cmd": [
            \               "hi Lf_hl_grep_file guifg=red ctermfg=196",
            \               "hi Lf_hl_grep_line guifg=green ctermfg=120",
            \       ],
            \       "highlight": "Highlight",
            \       "bang_enter": "Do_nothing",
            \       "after_enter": "",
            \       "before_exit": "",
            \       "supports_multi": 0,
            \ },
            \}

用法可以: :Leaderf grep -h

NOTE: 上面的函数都是全局函数,如果想用脚本局部函数s:FunctionName,需要这样用: 例如, "source": string(function("s:FunctionName"))[10:-3]

hilarryxu commented 5 years ago

neovim下不能使用

Error invoking 'python_execute' on channel 4 (python3-script-host):
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "plugged/LeaderF/autoload/leaderf/python/leaderf/anyExpl.py", line
715, in start
    the_args.start(arguments, *args, **kwargs)
  File "plugged/LeaderF/autoload/leaderf/python/leaderf/anyExpl.py", line
600, in _default_action
    config[k] = vim.bindeval("g:Lf_Extensions['%s']['%s']" % (category, k))
AttributeError: 'LegacyVim' object has no attribute 'bindeval'
Yggdroot commented 5 years ago

@hilarryxu 你用的不是最新的代码,请更新一下插件。

hilarryxu commented 5 years ago

更新后可以了 谢谢 @Yggdroot