如何创建自己的自动完成功能?


22

如何为某些文件类型创建自己的自动完成列表?

例如,我希望CSS和html从FontAwesome中的CSS类列表中自动完成。

Answers:


22

字典完成将是一种廉价且非侵入式的解决方案:

  1. fontawesome.txt保存在您计算机上的某个位置,

  2. 将其放入after/ftplugin/css.vim(如果不存在则创建文件):

    setlocal complete+=k
    setlocal dictionary+=/path/to/fontawesome.txt
    setlocal iskeyword+=-
    
  3. 开始新的会话或:e在CSS缓冲区中执行操作以强制获取上述文件,

  4. 在插入模式下按<C-n><C-p>

  5. 请享用!

    字典完成

参考:

:help ins-completion
:help 'complete'
:help 'dictionary'
:help 'iskeyword'

17

编辑感谢romainl的评论,我已经编辑了答案以显示如何创建用户定义的完成功能。在以前的版本中,我重写了内置的omni-completion,这不好,因为用户会失去非常强大的默认完成功能。


Vimscript解决方案

一种解决方案是使用vimscript,而vim让我们创建一个自定义的完成函数。

该解决方案的优势在于,您不需要额外的插件,您只需创建用户定义的完成功能并使用内置的完成功能即可。

我将在css文件中使用您的fontAwesome类的示例:

创建文件~/.vim/autoload/csscomplete.vim,并将以下行放入其中:

let s:matches=".fa-lg .fa-2x .fa-3x .fa-4x .fa-5x .fa-fw .fa-ul .fa-ul .fa-li .fa-li.fa-lg .fa-border .fa-pull-left .fa-pull-right .fa.fa-pull-left"

function! csscomplete#CompleteFA(findstart, base)
    if a:findstart
        " locate the start of the word
        let line = getline('.')
        let start = col('.') - 1
        while start > 0 && (line[start - 1] =~ '\a' || line[start - 1] =~ '.' || line[start - 1] =~ '-')
            let start -= 1
        endwhile
        return start
    else
        " find classes matching "a:base"
        let res = []
        for m in split(s:matches)
            if m =~ '^' . a:base
                call add(res, m)
            endif
        endfor
        return res
    endif
endfun

然后创建文件~/.vim/after/ftplugin/css.vim,并将其放入以下行:

setlocal completefunc=csscomplete#CompleteFA

然后,您可以使用触发用户定义的完成功能<C-x><C-u>。它将尝试找到与最后键入的单词匹配的单词。

在屏幕截图中,我输入了以下内容.fa-l,然后使用触发了函数<C-x><C-u>

在此处输入图片说明


它是如何工作的?

首先,这里是一些相关的文档主题:

如果要为特定文件类型创建自定义补全,则必须将其放入文件中$HOME/.vim/autoload/{FILETYPE}complete.vim

然后,在此文件中,您必须创建调用两次的完成函数:

  • 第一次调用时,它的第一个参数是光标的当前位置,函数将确定要完成的单词。在我的函数中,我使用了3个比较来完成单词的生成,因为该类可以由字母组成,.并且- (我认为可以改善这种匹配,但是我对regex真的很不好)

  • 在第二次调用时,第二个参数是要匹配的单词,然后函数将其与可能匹配的类别列表进行比较1。在这里,您看到我返回了一个字典,该字典将填充完成菜单,但是当您阅读文档时,您会发现可以创建一个更为复杂的字典来自定义函数的行为。

您还必须告诉Vim“对于这种文件,请使用我创建的完整功能”。为此,您必须将设置completefunc为创建的函数的名称(在此处csscomplete#CompleteFA),并且必须在文件中完成此设置$HOME/.vim/after/ftplugin/{FILETYPE}.vim

1在我的函数中,变量s:matches包含所有可能的匹配项。在这里,我仅提出一些可读性建议,但您可以放置​​FontAwesome提供的所有类(您可以在romainl创建的此文件中找到完整列表)。


如果我不喜欢Vimscript怎么办?

一种可能性是使用插件YoucompleteMe,该插件提供可与完成菜单一起使用的API。您可以创建python函数来完成匹配工作,并使用API​​填充Vim接口。更多细节在这里


2
CSS和HTML的默认全能补全已经非常有用,一次只能拥有一个,因此我建议使用“用户定义的补全”代替。请参阅:help i_ctrl-x_ctrl-u
romainl

@romainl:绝对正确,我将看看如何调整答案。
statox

5

有时,您希望自定义自动完成功能完全不干扰任何内置或用户定义的自动完成功能。这对于旨在用于各种文件类型的脚本或插件特别有用。

使用complete() 函数和一个简单的包装器就可以很容易地做到这一点 。:help complete-functions 除了需要定义自己的映射并且必须调用complete()自己而不是返回值之外,大多数过程与Statox的答案中所述相同 。

这是一个基本示例,注释应说明其工作原理。

" Map <C-x><C-m> for our custom completion
inoremap <C-x><C-m> <C-r>=MyComplete()<CR>

" Make subsequent <C-m> presses after <C-x><C-m> go to the next entry (just like
" other <C-x>* mappings)
inoremap <expr> <C-m> pumvisible() ?  "\<C-n>" : "\<C-m>"

" Complete function for addresses; we match the name & address
fun! MyComplete()
    " The data. In this example it's static, but you could read it from a file,
    " get it from a command, etc.
    let l:data = [
        \ ["Elmo the Elk", "daring@brave.com"],
        \ ["Eek the Cat", "doesnthurt@help.com"]
    \ ]

    " Locate the start of the word and store the text we want to match in l:base
    let l:line = getline('.')
    let l:start = col('.') - 1
    while l:start > 0 && l:line[l:start - 1] =~ '\a'
        let l:start -= 1
    endwhile
    let l:base = l:line[l:start : col('.')-1]

    " Record what matches − we pass this to complete() later
    let l:res = []

    " Find matches
    for m in l:data
        " Check if it matches what we're trying to complete; in this case we
        " want to match against the start of both the first and second list
        " entries (i.e. the name and email address)
        if l:m[0] !~? '^' . l:base && l:m[1] !~? '^' . l:base
            " Doesn't match
            continue
        endif

        " It matches! See :help complete() for the full docs on the key names
        " for this dict.
        call add(l:res, {
            \ 'icase': 1,
            \ 'word': l:m[0] . ' <' . l:m[1] . '>, ',
            \ 'abbr': l:m[0],
            \ 'menu': l:m[1],
            \ 'info': len(l:m) > 2 ? join(l:m[2:], "\n") : '',
        \ })
    endfor

    " Now call the complete() function
    call complete(l:start + 1, l:res)
    return ''
endfun
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.