Làm thế nào để tạo chức năng tự động hoàn thành của riêng tôi?


22

Làm cách nào để tôi tạo danh sách tự động hoàn thành cho một số loại tệp nhất định?

Ví dụ, tôi muốn css và html tự động hoàn thành từ danh sách các lớp css trong FontAwgie .

Câu trả lời:


22

Hoàn thành từ điển sẽ là một giải pháp rẻ tiền và không xâm phạm:

  1. lưu fontawclaw.txt ở đâu đó trên máy của bạn,

  2. đặt cái này vào after/ftplugin/css.vim(tạo tập tin nếu nó không tồn tại):

    setlocal complete+=k
    setlocal dictionary+=/path/to/fontawesome.txt
    setlocal iskeyword+=-
    
  3. bắt đầu một phiên mới hoặc thực hiện :etrong bộ đệm CSS để lấy nguồn mạnh ở trên,

  4. nhấn <C-n>hoặc <C-p>trong chế độ chèn,

  5. thưởng thức!

    hoàn thành từ điển

Tài liệu tham khảo:

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

17

EDIT Nhờ nhận xét của romainl Tôi đã chỉnh sửa câu trả lời của mình để hiển thị cách tạo chức năng hoàn thành do người dùng xác định. Trong phiên bản trước, tôi đã ghi đè hoàn thành omni-in, điều này không tốt vì người dùng sẽ mất hoàn thành mặc định, điều này khá mạnh mẽ.


Một giải pháp vimscript

Một giải pháp là sử dụng vimscript và thực tế là vim cho phép bạn tạo một chức năng hoàn thành tùy chỉnh.

Ưu điểm của giải pháp này là bạn không cần một plugin bổ sung, bạn chỉ cần tạo một hàm hoàn thành do người dùng xác định và sử dụng tính năng hoàn thành tích hợp.

Tôi sẽ sử dụng ví dụ của bạn về các lớp fontAwgie trong csscác tệp:

Tạo tập tin ~/.vim/autoload/csscomplete.vimvà đặt các dòng sau vào nó:

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

Sau đó tạo tập tin ~/.vim/after/ftplugin/css.vimvà đặt nó vào dòng sau:

setlocal completefunc=csscomplete#CompleteFA

Sau đó, bạn có thể kích hoạt chức năng hoàn thành do người dùng xác định với <C-x><C-u>. Nó sẽ cố gắng tìm một từ khớp với từ được gõ cuối cùng.

Trong ảnh chụp màn hình tôi đã gõ .fa-lvà sau đó kích hoạt chức năng với <C-x><C-u>:

nhập mô tả hình ảnh ở đây


Làm thế nào nó hoạt động?

Đầu tiên ở đây là một số chủ đề tài liệu có liên quan:

Nếu bạn muốn tạo một hoàn thành tùy chỉnh cho một kiểu tệp cụ thể, bạn phải đặt nó vào tệp $HOME/.vim/autoload/{FILETYPE}complete.vim.

Sau đó, trong tệp này, bạn phải tạo chức năng hoàn thành của mình được gọi là hai lần:

  • Trong lần gọi đầu tiên, đối số đầu tiên của nó là vị trí hiện tại của con trỏ và hàm sẽ xác định từ cần hoàn thành. Trong chức năng của mình, tôi đã sử dụng 3 so sánh để hoàn thành từ vì lớp có thể bao gồm các chữ cái .- (tôi nghĩ có thể cải thiện kết hợp này nhưng tôi thực sự xấu với regex)

  • Trong cuộc gọi thứ hai, đối số thứ hai là từ phù hợp và sau đó hàm so sánh nó với danh sách các lớp có thể khớp với 1 . Ở đây bạn thấy rằng tôi trả lại một từ điển sẽ điền vào menu hoàn thành nhưng khi bạn đọc tài liệu bạn sẽ thấy rằng bạn có thể tạo một từ điển phức tạp hơn nhiều để tùy chỉnh nhiều hơn hành vi của chức năng của bạn.

Bạn cũng phải nói với Vim "đối với loại tệp này, hãy sử dụng chức năng hoàn chỉnh này mà tôi đã tạo". Để làm như vậy, bạn phải đặt thành completefunctên của chức năng bạn đã tạo (ở đây csscomplete#CompleteFA) và cài đặt này phải được thực hiện trong tệp $HOME/.vim/after/ftplugin/{FILETYPE}.vim.

1 Trong hàm của tôi, biến s:matcheschứa tất cả các kết quả khớp có thể. Ở đây tôi chỉ đưa ra một số gợi ý về tính dễ đọc nhưng bạn có thể đặt tất cả các lớp được cung cấp bởi FontAwgie (Bạn có thể tìm thấy danh sách đầy đủ trong tệp này được tạo bởi romainl).


Nếu tôi không thích Vimscript thì sao?

Một khả năng là sử dụng plugin YoucompleteMe cung cấp API để chơi với menu hoàn thành. Bạn có thể tạo chức năng python sẽ thực hiện công việc phù hợp và sẽ sử dụng API để điền giao diện Vim. Thêm chi tiết tại đây .


2
Hoàn thành omni mặc định cho CSS và HTML đã khá hữu ích và bạn chỉ có thể có một lần một lần nên tôi sẽ đề nghị sử dụng "hoàn thành do người dùng xác định". Xem :help i_ctrl-x_ctrl-u.
romainl

@romainl: Bạn hoàn toàn đúng, tôi sẽ xem cách điều chỉnh câu trả lời của tôi.
statox

5

Đôi khi bạn muốn tự động hoàn thành tùy chỉnh mà không can thiệp vào bất kỳ tự động hoàn thành do người dùng xác định hoặc tích hợp nào. Điều này đặc biệt hữu ích cho các tập lệnh hoặc plugin được dự định hoạt động cho một loạt các kiểu tệp.

Điều này có thể được thực hiện khá dễ dàng với complete() chức năng và một trình bao bọc đơn giản; hầu hết các thủ tục giống như được mô tả trong :help complete-functions và câu trả lời của Statox, ngoại trừ việc bạn cần xác định ánh xạ của riêng mình và phải tự gọi complete()mình thay vì trả về một giá trị.

Dưới đây là một ví dụ cơ bản, các ý kiến ​​nên giải thích cách nó hoạt động.

" 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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.