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

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

#1 楼

字典补全将是一种便宜且非侵入式的解决方案:


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

将其放在after/ftplugin/css.vim中(如果它不存在):

setlocal complete+=k
setlocal dictionary+=/path/to/fontawesome.txt
setlocal iskeyword+=-


开始新的会话或在CSS缓冲区中执行:e以强制源于上面的文件,
<C-n><C-p>在插入模式下,

享受!




参考:

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


#2 楼

编辑感谢romainl的评论,我编辑了答案以显示如何创建用户定义的完成功能。在以前的版本中,我重写了内置的omni-completion,这不好,因为用户会失去非常强大的默认完成功能。 />
一种解决方案是使用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>触发了该函数: />
如何工作?

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


:h完整功能
:h 'completefunc'
:h i_CTRL-X_CTRL-U
关于文件组织的好答案。

如果要为特定文件类型创建自定义补全,则必须将其放在文件$HOME/.vim/autoload/{FILETYPE}complete.vim中。然后在该文件中创建您的完成函数,该函数将被调用两次:


第一次调用时,其第一个参数是光标的当前位置,函数将确定要完成的单词。在我的函数中,我使用了3个比较来完成单词的生成,因为该类可以由字母.-组成(我认为可以改善这种匹配性,但是使用regex真的很不好)
第二次调用时,第二个参数是要匹配的单词,然后函数将其与可能匹配的类列表进行比较。在这里,您看到我返回了一个字典,该字典将填充完成菜单,但是当您阅读文档时, '您会发现您可以创建一个更加复杂的字典来自定义函数的行为。 ”。为此,必须将completefunc设置为所创建函数的名称(此处为csscomplete#CompleteFA),并且必须在文件$HOME/.vim/after/ftplugin/{FILETYPE}.vim中进行此设置。

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

一种可能性是使用插件YoucompleteMe,该插件提供可与完成菜单一起播放的API。您可以创建python函数来完成匹配工作,并使用API​​填充Vim接口。此处有更多详细信息。

评论


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

– romainl
15年8月30日在8:53

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

–statox♦
15年8月30日在18:29

#3 楼

有时,您希望自定义自动完成功能完全不会干扰任何内置或用户定义的自动完成功能。这对于要用于各种文件类型的脚本或插件特别有用。和一个简单的包装;大部分过程与
complete()
和Statox的答案中所述相同,除了您需要定义自己的映射并具有
自己调用:help complete-functions而不是返回值。 />
这是一个基本示例,注释应说明其工作原理。

" 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