1
0
mirror of https://github.com/SpaceVim/SpaceVim.git synced 2025-01-23 17:30:04 +08:00

pref(bundle): update bundle deoplete

This commit is contained in:
wsdjeg 2022-04-23 12:09:55 +08:00
parent 023c48c740
commit b730079c88
50 changed files with 928 additions and 574 deletions

View File

@ -22,3 +22,4 @@ In `bundle/` directory, there are two kinds of plugins: forked plugins without c
- [deoplete-lsp](https://github.com/deoplete-plugins/deoplete-lsp/tree/6299a22bedfb4f814d95cb0010291501472f8fd0) - [deoplete-lsp](https://github.com/deoplete-plugins/deoplete-lsp/tree/6299a22bedfb4f814d95cb0010291501472f8fd0)
- [nvim-cmp](https://github.com/hrsh7th/nvim-cmp/tree/3192a0c57837c1ec5bf298e4f3ec984c7d2d60c0) - [nvim-cmp](https://github.com/hrsh7th/nvim-cmp/tree/3192a0c57837c1ec5bf298e4f3ec984c7d2d60c0)
- [coc-neosnippet](https://github.com/notomo/cmp-neosnippet/tree/2d14526af3f02dcea738b4cea520e6ce55c09979) - [coc-neosnippet](https://github.com/notomo/cmp-neosnippet/tree/2d14526af3f02dcea738b4cea520e6ce55c09979)
- [deoplete](https://github.com/Shougo/deoplete.nvim/tree/1c40f648d2b00e70beb4c473b7c0e32b633bd9ae)

View File

@ -0,0 +1,12 @@
# These are supported funding model platforms
github: Shougo # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

View File

@ -0,0 +1,64 @@
name: Lint, Test & Coverage
on: [push, pull_request]
jobs:
test:
name: Python ${{ matrix.python-version }} - Neovim (${{ matrix.build }})
runs-on: ubuntu-latest
strategy:
matrix:
python-version:
- 3.7
- 3.8
- 3.9
build:
- nightly
- stable
steps:
- uses: actions/checkout@v2
- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
if [ -f test/requirements.txt ]; then pip install -r test/requirements.txt; fi
- name: Initialize Neovim
uses: rhysd/action-setup-vim@v1
id: vim
with:
neovim: true
version: ${{ matrix.build }}
- name: Clone vim-themis
uses: actions/checkout@v2
with:
repository: thinca/vim-themis
path: vim-themis
- name: Run Lint & Test
run: make --keep-going THEMIS_VIM=${{ steps.vim.outputs.executable }} test lint
coverage:
name: Generate Codecav Report
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup Python
uses: actions/setup-python@v2
- name: Install dependencies
run: |
python -m pip install --upgrade pip
if [ -f test/requirements.txt ]; then pip install -r test/requirements.txt; fi
- name: Generate Report
run: pytest --cov=./rplugin/python3/deoplete --cov=./test --cov-report=xml
- uses: codecov/codecov-action@v1
with:
files: ./coverage.xml
functionalities: coveragepy, fix, gcov, search, xcode

View File

@ -1,22 +0,0 @@
dist: xenial
language: python
python:
- 3.6
- 3.7
install:
- eval "$(curl -Ss https://raw.githubusercontent.com/neovim/bot-ci/master/scripts/travis-setup.sh) nightly-x64"
- make install
env:
global:
- PATH=$HOME/neovim/bin:$PATH
- PYTEST_ADDOPTS=--cov rplugin/python3/deoplete
script:
- make --keep-going test lint
- coverage report -m --skip-covered
- coverage xml
- bash <(curl -s https://codecov.io/bash) -X gcov -X coveragepy -X fix -X search -X xcode -f coverage.xml

View File

@ -2,10 +2,19 @@
> Dark powered asynchronous completion framework for neovim/Vim8 > Dark powered asynchronous completion framework for neovim/Vim8
[![Build Status](https://travis-ci.org/Shougo/deoplete.nvim.svg?branch=master)](https://travis-ci.org/Shougo/deoplete.nvim) **Note**: The development of this plugin is finished. Accepts minor patches and
issues but no new features.
[ddc.vim](https://github.com/Shougo/ddc.vim) is the next generation auto
completion plugin. Consider migrating to it.
[![Join the chat at https://gitter.im/Shougo/deoplete.nvim](https://badges.gitter.im/Shougo/deoplete.nvim.svg)](https://gitter.im/Shougo/deoplete.nvim?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Join the chat at https://gitter.im/Shougo/deoplete.nvim](https://badges.gitter.im/Shougo/deoplete.nvim.svg)](https://gitter.im/Shougo/deoplete.nvim?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Doc](https://img.shields.io/badge/doc-%3Ah%20deoplete-orange.svg)](doc/deoplete.txt) [![Doc](https://img.shields.io/badge/doc-%3Ah%20deoplete-orange.svg)](doc/deoplete.txt)
Please read [help](doc/deoplete.txt) for details.
Note: If you need to understand what's different between deoplete and other
similar plugins, please read "deoplete-faq" section in the documentation.
Deoplete is the abbreviation of "dark powered neo-completion". It Deoplete is the abbreviation of "dark powered neo-completion". It
provides an extensible and asynchronous completion framework for provides an extensible and asynchronous completion framework for
neovim/Vim8. neovim/Vim8.
@ -26,7 +35,7 @@ Here are some [completion sources](https://github.com/Shougo/deoplete.nvim/wiki/
## Install ## Install
**Note:** deoplete requires Neovim (0.3.0+ and of course, **latest** is **Note:** deoplete requires Neovim (0.3.0+ and of course, **latest** is
recommended) or Vim8 with Python 3.6.1+ and timers enabled. See recommended) or Vim8.1 with Python 3.6.1+ and timers enabled. See
[requirements](#requirements) if you aren't sure whether you have this. [requirements](#requirements) if you aren't sure whether you have this.
Note: deoplete requires msgpack package 1.0.0+. Note: deoplete requires msgpack package 1.0.0+.
@ -75,7 +84,8 @@ For manual installation(not recommended)
deoplete requires Neovim or Vim8 with `if_python3`. deoplete requires Neovim or Vim8 with `if_python3`.
If `:echo has("python3")` returns `1`, then you have python 3 support; otherwise, see below. If `:echo has("python3")` returns `1`, then you have python 3 support;
otherwise, see below.
You can enable Python3 interface with pip: You can enable Python3 interface with pip:
@ -157,3 +167,5 @@ Deoplete for JavaScript
![Rust using rls](https://user-images.githubusercontent.com/1750795/38780764-8524b0b8-40a9-11e8-91bc-6e4148c398a3.png) ![Rust using rls](https://user-images.githubusercontent.com/1750795/38780764-8524b0b8-40a9-11e8-91bc-6e4148c398a3.png)
![Ruby dictionary completion](https://user-images.githubusercontent.com/1314340/44786516-5bb57a00-abcf-11e8-8687-492fa5f9f905.gif) ![Ruby dictionary completion](https://user-images.githubusercontent.com/1314340/44786516-5bb57a00-abcf-11e8-8687-492fa5f9f905.gif)
![LanguageClient-neovim integration](https://user-images.githubusercontent.com/4245199/87716288-efd25f80-c7ae-11ea-8080-334d155b3155.png)

View File

@ -72,7 +72,6 @@ function! deoplete#close_popup() abort
return pumvisible() ? "\<C-y>" : '' return pumvisible() ? "\<C-y>" : ''
endfunction endfunction
function! deoplete#smart_close_popup() abort function! deoplete#smart_close_popup() abort
call deoplete#handler#_skip_next_completion()
return pumvisible() ? "\<C-e>" : '' return pumvisible() ? "\<C-e>" : ''
endfunction endfunction
function! deoplete#cancel_popup() abort function! deoplete#cancel_popup() abort
@ -88,3 +87,7 @@ endfunction
function! deoplete#complete_common_string() abort function! deoplete#complete_common_string() abort
return deoplete#mapping#_complete_common_string() return deoplete#mapping#_complete_common_string()
endfunction endfunction
function! deoplete#can_complete() abort
return !empty(get(get(g:, 'deoplete#_context', {}), 'candidates', []))
\ && deoplete#mapping#_can_complete()
endfunction

View File

@ -18,10 +18,10 @@ function! deoplete#custom#_init() abort
let s:cached.source_vars = {} let s:cached.source_vars = {}
endfunction endfunction
function! deoplete#custom#_init_buffer() abort function! deoplete#custom#_init_buffer() abort
let b:custom = {} let b:deoplete_custom = {}
let b:custom.option = {} let b:deoplete_custom.option = {}
let b:custom.source_vars = {} let b:deoplete_custom.source_vars = {}
let b:custom.filter = {} let b:deoplete_custom.filter = {}
endfunction endfunction
function! deoplete#custom#_update_cache() abort function! deoplete#custom#_update_cache() abort
@ -65,11 +65,11 @@ function! deoplete#custom#_get() abort
return s:custom return s:custom
endfunction endfunction
function! deoplete#custom#_get_buffer() abort function! deoplete#custom#_get_buffer() abort
if !exists('b:custom') if !exists('b:deoplete_custom')
call deoplete#custom#_init_buffer() call deoplete#custom#_init_buffer()
endif endif
return b:custom return b:deoplete_custom
endfunction endfunction
function! deoplete#custom#_get_source(source_name) abort function! deoplete#custom#_get_source(source_name) abort
@ -92,8 +92,14 @@ function! deoplete#custom#_get_filetype_option(name, filetype, default) abort
endif endif
let option = s:cached.option[a:name] let option = s:cached.option[a:name]
let filetype = has_key(option, a:filetype) ? a:filetype : '_' " Check filetype -> a.b filetype -> '_'
return get(option, filetype, a:default) for filetype in [a:filetype] + split(a:filetype, '\.') + ['_']
if has_key(option, filetype)
return option[filetype]
endif
endfor
return a:default
endfunction endfunction
function! deoplete#custom#_get_source_vars(name) abort function! deoplete#custom#_get_source_vars(name) abort
return get(s:cached.source_vars, a:name, {}) return get(s:cached.source_vars, a:name, {})

View File

@ -14,7 +14,7 @@ function! deoplete#handler#_init() abort
for event in [ for event in [
\ 'InsertEnter', 'InsertLeave', \ 'InsertEnter', 'InsertLeave',
\ 'BufReadPost', 'BufWritePost', \ 'BufReadPost', 'BufWritePost',
\ 'VimLeavePre', \ 'VimLeavePre', 'FileType',
\ ] \ ]
call s:define_on_event(event) call s:define_on_event(event)
endfor endfor
@ -26,11 +26,7 @@ function! deoplete#handler#_init() abort
call s:define_completion_via_timer('InsertEnter') call s:define_completion_via_timer('InsertEnter')
endif endif
if deoplete#custom#_get_option('refresh_always') if deoplete#custom#_get_option('refresh_always')
if exists('##TextChangedP')
call s:define_completion_via_timer('TextChangedP') call s:define_completion_via_timer('TextChangedP')
else
call s:define_completion_via_timer('InsertCharPre')
endif
endif endif
" Note: Vim 8 GUI(MacVim and Win32) is broken " Note: Vim 8 GUI(MacVim and Win32) is broken
@ -51,11 +47,7 @@ function! deoplete#handler#_do_complete() abort
let context = g:deoplete#_context let context = g:deoplete#_context
let event = get(context, 'event', '') let event = get(context, 'event', '')
if s:is_exiting() || v:insertmode !=# 'i' || s:check_input_method() if s:is_exiting() || v:insertmode !=# 'i' || s:check_input_method()
return \ || !has_key(context, 'candidates')
endif
if !has_key(context, 'candidates')
\ || deoplete#util#get_input(context.event) !=# context.input
return return
endif endif
@ -65,6 +57,11 @@ function! deoplete#handler#_do_complete() abort
let prev.candidates = context.candidates let prev.candidates = context.candidates
let prev.complete_position = context.complete_position let prev.complete_position = context.complete_position
let prev.linenr = line('.') let prev.linenr = line('.')
let prev.time = context.time
if context.event ==# 'Manual'
let context.event = ''
endif
let auto_popup = deoplete#custom#_get_option( let auto_popup = deoplete#custom#_get_option(
\ 'auto_complete_popup') !=# 'manual' \ 'auto_complete_popup') !=# 'manual'
@ -74,13 +71,10 @@ function! deoplete#handler#_do_complete() abort
let auto_popup = v:true let auto_popup = v:true
endif endif
if context.event ==# 'Manual'
let context.event = ''
elseif !exists('g:deoplete#_saved_completeopt') && auto_popup
call deoplete#mapping#_set_completeopt()
endif
if auto_popup if auto_popup
" Note: completeopt must be changed before complete() and feedkeys()
call deoplete#mapping#_set_completeopt(g:deoplete#_context.is_async)
call feedkeys("\<Plug>_", 'i') call feedkeys("\<Plug>_", 'i')
endif endif
endfunction endfunction
@ -108,16 +102,8 @@ function! deoplete#handler#_check_omnifunc(context) abort
let prev.input = a:context.input let prev.input = a:context.input
let prev.candidates = [] let prev.candidates = []
if &completeopt =~# 'noselect' call deoplete#mapping#_set_completeopt(v:true)
call deoplete#mapping#_set_completeopt()
call feedkeys("\<C-x>\<C-o>", 'in') call feedkeys("\<C-x>\<C-o>", 'in')
else
call deoplete#util#print_error(
\ 'omni_patterns feature is disabled.')
call deoplete#util#print_error(
\ 'You need to set "noselect" in completeopt option.')
endif
return 1
endif endif
endfor endfor
endfor endfor
@ -145,7 +131,7 @@ function! s:completion_timer_stop() abort
unlet s:completion_timer unlet s:completion_timer
endfunction endfunction
function! s:check_prev_completion(event) abort function! deoplete#handler#_check_prev_completion(event) abort
let prev = g:deoplete#_prev_completion let prev = g:deoplete#_prev_completion
if a:event ==# 'Async' || a:event ==# 'Update' || mode() !=# 'i' if a:event ==# 'Async' || a:event ==# 'Update' || mode() !=# 'i'
\ || empty(get(prev, 'candidates', [])) \ || empty(get(prev, 'candidates', []))
@ -160,8 +146,6 @@ function! s:check_prev_completion(event) abort
return return
endif endif
call deoplete#mapping#_set_completeopt()
let mode = deoplete#custom#_get_option('prev_completion_mode') let mode = deoplete#custom#_get_option('prev_completion_mode')
let candidates = copy(prev.candidates) let candidates = copy(prev.candidates)
@ -169,9 +153,9 @@ function! s:check_prev_completion(event) abort
let input = input[prev.complete_position :] let input = input[prev.complete_position :]
let escaped_input = escape(input, '~\.^$[]*') let escaped_input = escape(input, '~\.^$[]*')
let pattern = substitute(escaped_input, '\w', '\\w*\0', 'g') let pattern = substitute(escaped_input, '\w', '\\w*\0', 'g')
call filter(candidates, 'v:val.word =~? pattern') call filter(candidates, { _, val -> val.word =~? pattern })
if mode ==# 'length' if mode ==# 'length'
call filter(candidates, 'len(v:val.word) > len(input)') call filter(candidates, { _, val -> len(val.word) > len(input) })
endif endif
elseif mode ==# 'mirror' elseif mode ==# 'mirror'
" pass " pass
@ -183,7 +167,7 @@ function! s:check_prev_completion(event) abort
\ 'complete_position': prev.complete_position, \ 'complete_position': prev.complete_position,
\ 'candidates': candidates, \ 'candidates': candidates,
\ } \ }
call feedkeys("\<Plug>+", 'i') return 1
endfunction endfunction
function! deoplete#handler#_async_timer_start() abort function! deoplete#handler#_async_timer_start() abort
@ -198,12 +182,26 @@ endfunction
function! deoplete#handler#_completion_begin(event) abort function! deoplete#handler#_completion_begin(event) abort
call deoplete#custom#_update_cache() call deoplete#custom#_update_cache()
if s:is_skip(a:event) let auto_popup = deoplete#custom#_get_option(
\ 'auto_complete_popup') !=# 'manual'
let prev_input = get(g:deoplete#_context, 'input', '')
let cur_input = deoplete#util#get_input(a:event)
let check_back_space = auto_popup
\ && cur_input !=# prev_input
\ && len(cur_input) + 1 ==# len(prev_input)
\ && stridx(prev_input, cur_input) == 0
let refresh_backspace = deoplete#custom#_get_option('refresh_backspace')
if s:is_skip(a:event) || (check_back_space && !refresh_backspace)
let g:deoplete#_context.candidates = [] let g:deoplete#_context.candidates = []
let g:deoplete#_context.input = cur_input
return return
endif endif
call s:check_prev_completion(a:event) if auto_popup && deoplete#handler#_check_prev_completion(a:event)
call feedkeys("\<Plug>+", 'i')
endif
if a:event !=# 'Update' && a:event !=# 'Async' if a:event !=# 'Update' && a:event !=# 'Async'
call deoplete#init#_prev_completion() call deoplete#init#_prev_completion()
@ -211,6 +209,11 @@ function! deoplete#handler#_completion_begin(event) abort
call deoplete#util#rpcnotify( call deoplete#util#rpcnotify(
\ 'deoplete_auto_completion_begin', {'event': a:event}) \ 'deoplete_auto_completion_begin', {'event': a:event})
" For <BS> popup flicker
if check_back_space && empty(v:completed_item)
call feedkeys("\<Plug>_", 'i')
endif
endfunction endfunction
function! s:is_skip(event) abort function! s:is_skip(event) abort
if a:event ==# 'TextChangedP' && !empty(v:completed_item) if a:event ==# 'TextChangedP' && !empty(v:completed_item)
@ -231,11 +234,19 @@ function! s:is_skip(event) abort
return 1 return 1
endif endif
" Check nofile buffers
if &l:buftype =~# 'nofile' && bufname('%') !=# '[Command Line]'
let nofile_complete_filetypes = deoplete#custom#_get_option(
\ 'nofile_complete_filetypes')
if index(nofile_complete_filetypes, &l:filetype) < 0
return 1
endif
endif
let auto_complete = deoplete#custom#_get_option('auto_complete') let auto_complete = deoplete#custom#_get_option('auto_complete')
if &paste if &paste
\ || (a:event !=# 'Manual' && a:event !=# 'Update' && !auto_complete) \ || (a:event !=# 'Manual' && a:event !=# 'Update' && !auto_complete)
\ || (&l:completefunc !=# '' && &l:buftype =~# 'nofile')
\ || v:insertmode !=# 'i' \ || v:insertmode !=# 'i'
return 1 return 1
endif endif
@ -266,6 +277,11 @@ function! s:is_skip_prev_text(event) abort
endfunction endfunction
function! s:is_skip_text(event) abort function! s:is_skip_text(event) abort
let input = deoplete#util#get_input(a:event) let input = deoplete#util#get_input(a:event)
if !has('nvim') && iconv(iconv(input, 'utf-8', 'utf-16'),
\ 'utf-16', 'utf-8') !=# input
" In Vim8, invalid bytes brokes nvim-yarp.
return 1
endif
let lastchar = matchstr(input, '.$') let lastchar = matchstr(input, '.$')
let skip_multibyte = deoplete#custom#_get_option('skip_multibyte') let skip_multibyte = deoplete#custom#_get_option('skip_multibyte')
@ -280,12 +296,17 @@ function! s:is_skip_text(event) abort
\ && displaywidth >= &l:textwidth \ && displaywidth >= &l:textwidth
if &l:formatoptions =~# '[ta]' if &l:formatoptions =~# '[ta]'
\ || !empty(filter(deoplete#util#get_syn_names(), \ || !empty(filter(deoplete#util#get_syn_names(),
\ "v:val ==# 'Comment'")) \ { _, val -> val ==# 'Comment' }))
\ || is_virtual \ || is_virtual
return 1 return 1
endif endif
endif endif
if a:event =~# '^TextChanged' && s:matched_indentkeys(input) !=# ''
call deoplete#util#indent_current_line()
return 1
endif
let skip_chars = deoplete#custom#_get_option('skip_chars') let skip_chars = deoplete#custom#_get_option('skip_chars')
return (a:event !=# 'Manual' && input !=# '' return (a:event !=# 'Manual' && input !=# ''
@ -294,6 +315,32 @@ endfunction
function! s:check_input_method() abort function! s:check_input_method() abort
return exists('*getimstatus') && getimstatus() return exists('*getimstatus') && getimstatus()
endfunction endfunction
function! s:matched_indentkeys(input) abort
if &l:indentexpr ==# ''
" Disable auto indent
return ''
endif
" Note: check the last word
let checkstr = matchstr(a:input, '\w\+$')
for word in filter(map(split(&l:indentkeys, ','),
\ { _, val -> matchstr(val, 'e\|=\zs.*') }),
\ { _, val -> val !=# '' && val =~# '\h\w*' })
if word ==# 'e'
let word = 'else'
endif
let lastpos = len(a:input) - len(word)
if checkstr ==# word || (word =~# '^\W\+$' &&
\ lastpos >= 0 && strridx(a:input, word) == lastpos)
return word
endif
endfor
return ''
endfunction
function! s:define_on_event(event) abort function! s:define_on_event(event) abort
if !exists('##' . a:event) if !exists('##' . a:event)
@ -325,11 +372,27 @@ endfunction
function! s:on_complete_done() abort function! s:on_complete_done() abort
if get(v:completed_item, 'word', '') ==# '' if get(v:completed_item, 'word', '') ==# ''
\ || !has_key(g:deoplete#_context, 'complete_str')
return return
endif endif
call deoplete#handler#_skip_next_completion() call deoplete#handler#_skip_next_completion()
let max_used = 100
let g:deoplete#_recently_used = insert(
\ g:deoplete#_recently_used,
\ tolower(v:completed_item.word),
\ )
let min_pattern_length = deoplete#custom#_get_option('min_pattern_length')
if len(g:deoplete#_context['complete_str']) > min_pattern_length
let g:deoplete#_recently_used = insert(
\ g:deoplete#_recently_used,
\ tolower(g:deoplete#_context['complete_str']),
\ )
endif
let g:deoplete#_recently_used = deoplete#util#uniq(
\ g:deoplete#_recently_used)[: max_used]
let user_data = get(v:completed_item, 'user_data', '') let user_data = get(v:completed_item, 'user_data', '')
if type(user_data) !=# v:t_string || user_data ==# '' if type(user_data) !=# v:t_string || user_data ==# ''
return return

View File

@ -38,7 +38,7 @@ function! deoplete#init#_channel() abort
return 1 return 1
endif endif
let python3 = get(g:, 'python3_host_prog', 'python3') let python3 = expand(get(g:, 'python3_host_prog', 'python3'), 1)
if !executable(python3) if !executable(python3)
call deoplete#util#print_error( call deoplete#util#print_error(
\ string(python3) . ' is not executable.') \ string(python3) . ' is not executable.')
@ -49,8 +49,8 @@ function! deoplete#init#_channel() abort
call deoplete#util#print_error('deoplete requires nvim 0.3.0+.') call deoplete#util#print_error('deoplete requires nvim 0.3.0+.')
return 1 return 1
endif endif
if !has('nvim') && v:version < 800 if !has('nvim') && v:version < 801
call deoplete#util#print_error('deoplete requires Vim 8.0+.') call deoplete#util#print_error('deoplete requires Vim 8.1+.')
return 1 return 1
endif endif
@ -112,6 +112,7 @@ function! s:init_internal_variables() abort
call deoplete#init#_prev_completion() call deoplete#init#_prev_completion()
let g:deoplete#_context = {} let g:deoplete#_context = {}
let g:deoplete#_recently_used = []
if !exists('g:deoplete#_logging') if !exists('g:deoplete#_logging')
let g:deoplete#_logging = {} let g:deoplete#_logging = {}
@ -136,7 +137,7 @@ function! s:init_internal_variables() abort
if deoplete#util#has_yarp() if deoplete#util#has_yarp()
" Dummy call is needed to check exists() " Dummy call is needed to check exists()
call neovim_rpc#serveraddr() silent! call neovim_rpc#serveraddr()
if !exists('*neovim_rpc#serveraddr') if !exists('*neovim_rpc#serveraddr')
call deoplete#util#print_error( call deoplete#util#print_error(
\ 'deoplete requires vim-hug-neovim-rpc plugin in Vim.') \ 'deoplete requires vim-hug-neovim-rpc plugin in Vim.')
@ -220,7 +221,8 @@ function! s:check_custom_var(source_name, old_var, new_var) abort
call deoplete#util#print_error( call deoplete#util#print_error(
\ printf('%s is deprecated variable. '. \ printf('%s is deprecated variable. '.
\ 'Please use deoplete#custom#var() instead.', a:old_var)) \ 'Please use deoplete#custom#var("%s", "%s", {value}) instead.',
\ a:old_var, a:source_name, a:new_var))
call deoplete#custom#var(a:source_name, a:new_var, eval(a:old_var)) call deoplete#custom#var(a:source_name, a:new_var, eval(a:old_var))
endfunction endfunction
function! s:check_custom_option(old_var, new_var) abort function! s:check_custom_option(old_var, new_var) abort
@ -230,36 +232,36 @@ function! s:check_custom_option(old_var, new_var) abort
call deoplete#util#print_error( call deoplete#util#print_error(
\ printf('%s is deprecated variable. '. \ printf('%s is deprecated variable. '.
\ 'Please use deoplete#custom#option() instead.', a:old_var)) \ 'Please use deoplete#custom#option("%s", {value}) instead.',
\ a:old_var, a:new_var))
call deoplete#custom#option(a:new_var, eval(a:old_var)) call deoplete#custom#option(a:new_var, eval(a:old_var))
endfunction endfunction
function! deoplete#init#_option() abort function! deoplete#init#_option() abort
" Note: HTML omni func use search().
return { return {
\ 'auto_complete': v:true, \ 'auto_complete': v:true,
\ 'auto_complete_delay': 0, \ 'auto_complete_delay': 0,
\ 'auto_complete_popup': 'auto', \ 'auto_complete_popup': 'auto',
\ 'auto_refresh_delay': 100, \ 'auto_refresh_delay': 20,
\ 'camel_case': v:false,
\ 'candidate_marks': [], \ 'candidate_marks': [],
\ 'overwrite_completeopt': v:true,
\ 'check_stderr': v:true, \ 'check_stderr': v:true,
\ 'complete_suffix': v:true, \ 'complete_suffix': v:true,
\ 'ignore_case': &ignorecase,
\ 'ignore_sources': {}, \ 'ignore_sources': {},
\ 'keyword_patterns': {'_': '[a-zA-Z_]\k*'}, \ 'keyword_patterns': {'_': '[a-zA-Z_]\k*'},
\ 'max_list': 500, \ 'max_list': 500,
\ 'min_pattern_length': 2, \ 'min_pattern_length': 2,
\ 'num_processes': 4, \ 'num_processes': 1,
\ 'nofile_complete_filetypes': ['denite-filter'],
\ 'omni_patterns': {}, \ 'omni_patterns': {},
\ 'on_insert_enter': v:true, \ 'on_insert_enter': v:true,
\ 'on_text_changed_i': v:true, \ 'on_text_changed_i': v:true,
\ 'prev_completion_mode': '', \ 'prev_completion_mode': 'filter',
\ 'profile': v:false, \ 'profile': v:false,
\ 'refresh_always': v:true, \ 'refresh_always': v:true,
\ 'refresh_backspace': v:true,
\ 'skip_chars': ['(', ')'], \ 'skip_chars': ['(', ')'],
\ 'skip_multibyte': v:false, \ 'skip_multibyte': v:false,
\ 'smart_case': &smartcase,
\ 'sources': {}, \ 'sources': {},
\ 'trigger_key': v:char, \ 'trigger_key': v:char,
\ 'yarp': v:false, \ 'yarp': v:false,
@ -272,6 +274,7 @@ function! deoplete#init#_prev_completion() abort
\ 'linenr': -1, \ 'linenr': -1,
\ 'candidates': [], \ 'candidates': [],
\ 'complete_position': -1, \ 'complete_position': -1,
\ 'time': reltime(),
\ } \ }
endfunction endfunction

View File

@ -10,6 +10,12 @@ function! deoplete#mapping#_init() abort
\ deoplete#mapping#_dummy('deoplete#mapping#_complete') \ deoplete#mapping#_dummy('deoplete#mapping#_complete')
inoremap <expr><silent> <Plug>+ inoremap <expr><silent> <Plug>+
\ deoplete#mapping#_dummy('deoplete#mapping#_prev_complete') \ deoplete#mapping#_dummy('deoplete#mapping#_prev_complete')
" Note: The dummy mappings may be inserted on other modes.
cnoremap <silent> <Plug>_ <Nop>
cnoremap <silent> <Plug>+ <Nop>
noremap <silent> <Plug>_ <Nop>
noremap <silent> <Plug>+ <Nop>
endfunction endfunction
function! deoplete#mapping#_dummy(func) abort function! deoplete#mapping#_dummy(func) abort
return "\<C-r>=".a:func."()\<CR>" return "\<C-r>=".a:func."()\<CR>"
@ -35,16 +41,41 @@ function! s:check_completion_info(candidates) abort
endif endif
return 0 return 0
let old_candidates = sort(map(copy(info.items), 'v:val.word')) let old_candidates = sort(map(copy(info.items), { _, val -> val.word }))
return sort(map(copy(a:candidates), 'v:val.word')) ==# old_candidates return sort(map(copy(a:candidates),
\ { _, val -> val.word })) ==# old_candidates
endfunction
function! deoplete#mapping#_can_complete() abort
let context = get(g:, 'deoplete#_context', {})
return has_key(context, 'candidates') && has_key(context, 'event')
\ && has_key(context, 'input')
\ && !s:check_completion_info(context.candidates)
\ && &modifiable
endfunction endfunction
function! deoplete#mapping#_complete() abort function! deoplete#mapping#_complete() abort
if !has_key(g:deoplete#_context, 'candidates') if !deoplete#mapping#_can_complete()
\ || s:check_completion_info(g:deoplete#_context.candidates) let g:deoplete#_context.candidates = []
\ || !&modifiable
return '' return ''
endif endif
if deoplete#util#get_input(g:deoplete#_context.event)
\ !=# g:deoplete#_context.input
" Use prev completion instead
if deoplete#handler#_check_prev_completion(g:deoplete#_context.event)
call feedkeys("\<Plug>+", 'i')
endif
return ''
endif
let auto_popup = deoplete#custom#_get_option(
\ 'auto_complete_popup') !=# 'manual'
if auto_popup
" Note: completeopt must be changed before complete()
call deoplete#mapping#_set_completeopt(g:deoplete#_context.is_async)
endif
" echomsg string(g:deoplete#_context) " echomsg string(g:deoplete#_context)
if empty(g:deoplete#_context.candidates) && deoplete#util#check_popup() if empty(g:deoplete#_context.candidates) && deoplete#util#check_popup()
" Note: call complete() to close the popup " Note: call complete() to close the popup
@ -62,20 +93,34 @@ function! deoplete#mapping#_prev_complete() abort
return '' return ''
endif endif
let auto_popup = deoplete#custom#_get_option(
\ 'auto_complete_popup') !=# 'manual'
if auto_popup
" Note: completeopt must be changed before complete()
call deoplete#mapping#_set_completeopt(v:false)
endif
call complete(g:deoplete#_filtered_prev.complete_position + 1, call complete(g:deoplete#_filtered_prev.complete_position + 1,
\ g:deoplete#_filtered_prev.candidates) \ g:deoplete#_filtered_prev.candidates)
return '' return ''
endfunction endfunction
function! deoplete#mapping#_set_completeopt() abort function! deoplete#mapping#_set_completeopt(is_async) abort
if exists('g:deoplete#_saved_completeopt') if !deoplete#custom#_get_option('overwrite_completeopt')
return return
endif endif
if !exists('g:deoplete#_saved_completeopt')
let g:deoplete#_saved_completeopt = &completeopt let g:deoplete#_saved_completeopt = &completeopt
endif
set completeopt-=longest set completeopt-=longest
set completeopt+=menuone set completeopt+=menuone
set completeopt-=menu set completeopt-=menu
if &completeopt !~# 'noinsert\|noselect' if &completeopt !~# 'noinsert\|noselect' || a:is_async
" Note: If is_async, noselect is needed to prevent without confirmation
" problem
set completeopt-=noinsert
set completeopt+=noselect set completeopt+=noselect
endif endif
endfunction endfunction
@ -117,16 +162,9 @@ function! deoplete#mapping#_complete_common_string() abort
return '' return ''
endif endif
let complete_str = prev.input[prev.complete_position :] let complete_str = deoplete#util#get_input('')[prev.complete_position :]
let candidates = filter(copy(prev.candidates), let common_str = prev.candidates[0].word
\ 'stridx(tolower(v:val.word), tolower(complete_str)) == 0') for candidate in prev.candidates[1:]
if empty(candidates) || complete_str ==# ''
return ''
endif
let common_str = candidates[0].word
for candidate in candidates[1:]
while stridx(tolower(candidate.word), tolower(common_str)) != 0 while stridx(tolower(candidate.word), tolower(common_str)) != 0
let common_str = common_str[: -2] let common_str = common_str[: -2]
endwhile endwhile

View File

@ -35,10 +35,6 @@ function! deoplete#util#get_input(event) abort
\ '^.*\%' . (mode ==# 'i' ? col('.') : col('.') - 1) \ '^.*\%' . (mode ==# 'i' ? col('.') : col('.') - 1)
\ . 'c' . (mode ==# 'i' ? '' : '.')) \ . 'c' . (mode ==# 'i' ? '' : '.'))
if a:event ==# 'InsertCharPre'
let input .= v:char
endif
return input return input
endfunction endfunction
function! deoplete#util#get_next_input(event) abort function! deoplete#util#get_next_input(event) abort
@ -88,7 +84,7 @@ function! s:vimoption2python(option) abort
endfunction endfunction
function! deoplete#util#uniq(list) abort function! deoplete#util#uniq(list) abort
let list = map(copy(a:list), '[v:val, v:val]') let list = map(copy(a:list), { _, val -> [val, val] })
let i = 0 let i = 0
let seen = {} let seen = {}
while i < len(list) while i < len(list)
@ -100,7 +96,7 @@ function! deoplete#util#uniq(list) abort
let i += 1 let i += 1
endif endif
endwhile endwhile
return map(list, 'v:val[0]') return map(list, { _, val -> val[0] })
endfunction endfunction
function! deoplete#util#get_syn_names() abort function! deoplete#util#get_syn_names() abort
@ -183,8 +179,8 @@ endfunction
" >0 if a > b " >0 if a > b
" 0 if versions are equal. " 0 if versions are equal.
function! deoplete#util#versioncmp(a, b) abort function! deoplete#util#versioncmp(a, b) abort
let a = map(split(a:a, '\.'), 'str2nr(v:val)') let a = map(split(a:a, '\.'), { _, val -> str2nr(val) })
let b = map(split(a:b, '\.'), 'str2nr(v:val)') let b = map(split(a:b, '\.'), { _, val -> str2nr(val) })
let l = min([len(a), len(b)]) let l = min([len(a), len(b)])
let d = 0 let d = 0
@ -213,3 +209,17 @@ endfunction
function! deoplete#util#check_popup() abort function! deoplete#util#check_popup() abort
return exists('*complete_info') && complete_info().mode ==# 'eval' return exists('*complete_info') && complete_info().mode ==# 'eval'
endfunction endfunction
function! deoplete#util#indent_current_line() abort
let pos = getpos('.')
let len = len(getline('.'))
let equalprg = &l:equalprg
try
setlocal equalprg=
silent normal! ==
finally
let &l:equalprg = equalprg
let pos[2] += len(getline('.')) - len
call setpos('.', pos)
endtry
endfunction

View File

@ -59,11 +59,11 @@ function! s:still_have_issues() abort
let indentation = ' ' let indentation = ' '
call health#report_info("If you're still having problems, " . call health#report_info("If you're still having problems, " .
\ "try the following commands:\n" . \ "try the following commands:\n" .
\ indentation . "$ export NVIM_PYTHON_LOG_FILE=/tmp/log\n" . \ indentation . "- $ export NVIM_PYTHON_LOG_FILE=/tmp/log\n" .
\ indentation . "$ export NVIM_PYTHON_LOG_LEVEL=DEBUG\n" . \ indentation . "- $ export NVIM_PYTHON_LOG_LEVEL=DEBUG\n" .
\ indentation . "$ nvim\n" . \ indentation . "- $ nvim\n" .
\ indentation . "$ cat /tmp/log_{PID}\n" . \ indentation . "- $ cat /tmp/log_{PID}\n" .
\ indentation . ' and then create an issue on github' \ indentation . '- and then create an issue on github'
\ ) \ )
endfunction endfunction

View File

@ -1,8 +0,0 @@
coverage:
status:
project: false
patch: true
changes: true
comment:
layout: "diff"

View File

@ -1,6 +1,6 @@
*deoplete.txt* Dark powered asynchronous completion framework for Neovim/Vim8 *deoplete.txt* Dark powered asynchronous completion framework for Neovim/Vim8
Version: 6.0 Version: 6.2
Author: Shougo <Shougo.Matsu at gmail.com> Author: Shougo <Shougo.Matsu at gmail.com>
License: MIT license License: MIT license
@ -30,14 +30,14 @@ Compatibility |deoplete-compatibility|
INTRODUCTION *deoplete-introduction* INTRODUCTION *deoplete-introduction*
*deoplete* is the abbreviation of "dark powered neo-completion". It *deoplete* is the abbreviation of "dark powered neo-completion". It
provides asynchronous keyword completion system in the provides an asynchronous keyword completion system in the
current buffer. current buffer.
Note: deoplete may consume more memory than other plugins do. Note: deoplete may consume more memory than other plugins do.
Improvements in deoplete in comparison to |neocomplete|: Improvements in deoplete in comparison to neocomplete:
1. Real asynchronous completion behavior like |YouCompleteMe| by default. 1. Real asynchronous completion behavior like YouCompleteMe by default.
2. Uses Python3 to implement sources. 2. Uses Python3 to implement sources.
3. Removes legacy interface. 3. Removes legacy interface.
4. Requires |+python3|. 4. Requires |+python3|.
@ -45,8 +45,8 @@ Improvements in deoplete in comparison to |neocomplete|:
============================================================================== ==============================================================================
INSTALL *deoplete-install* INSTALL *deoplete-install*
Note: deoplete requires Neovim (0.3.0+) or Vim8 (latest is recommended) with Note: deoplete requires Neovim (0.3.0+) or Vim8.1+ (latest is recommended)
Python 3.6.1+ and |+timers| enabled. with Python 3.6.1+ and |+timers| enabled.
Please install/upgrade msgpack package (1.0.0+). Please install/upgrade msgpack package (1.0.0+).
https://github.com/msgpack/msgpack-python https://github.com/msgpack/msgpack-python
@ -113,7 +113,7 @@ auto_complete
auto_complete_delay auto_complete_delay
Delay the completion after input in milliseconds. Delay the completion after input in milliseconds.
Default value: 0 (milliseconds) Default value: 20 (milliseconds)
*deoplete-options-auto_complete_popup* *deoplete-options-auto_complete_popup*
auto_complete_popup auto_complete_popup
@ -131,21 +131,19 @@ auto_refresh_delay
Default value: 100 (milliseconds) Default value: 100 (milliseconds)
*deoplete-options-camel_case*
camel_case
If it is True, lowercase letters are also matched with the
corresponding uppercase ones.
Ex: "foB" is matched with "FooBar" but not with "foobar".
Note: This feature is only available in
|deoplete-filter-matcher_fuzzy| or
|deoplete-filter-matcher_full_fuzzy|.
Default value: v:false
*deoplete-options-candidate_marks* *deoplete-options-candidate_marks*
candidate_marks candidate_marks
The candidate additional marks. The candidate additional marks.
If set, this will be used to display an additional mark next
to the the first {N} candidates. The first mark is used for
the top ranked candidate, the second for the second candidate
and so forth.
In the example below, the first 5 candidates are marked A..G,
which serve as mnemonics for commands to insert that
candidate.
Default value: [] Default value: []
> >
call deoplete#custom#option('candidate_marks', call deoplete#custom#option('candidate_marks',
@ -174,16 +172,11 @@ complete_suffix
Default value: v:true Default value: v:true
*deoplete-options-ignore_case*
ignore_case
If it is True, deoplete ignores case.
Default value: same with your 'ignorecase' value
*deoplete-options-ignore_sources* *deoplete-options-ignore_sources*
ignore_sources ignore_sources
It is a dictionary to decide ignore source names. It is a dictionary to decide ignore source names.
The key is filetype and the value is source names list. The key is filetype and the value is source names list.
Note: It is disabled in |deoplete#manual_complete()|.
Default value: {} Default value: {}
@ -209,15 +202,25 @@ max_list
Default value: 500 Default value: 500
*deoplete-options-nofile_complete_filetypes*
nofile_complete_filetypes
If 'buftype' is "nofile", deoplete completion is disabled
automatically except the filetype list.
Default value: ["denite-filter"]
*deoplete-options-num_processes* *deoplete-options-num_processes*
num_processes num_processes
The number of processes used for the deoplete parallel The number of processes used for the deoplete parallel
feature. completion feature.
The parallel completion increases the completion speed, but it
increases the screen flicker.
If it is 1, this feature is disabled. If it is 1, this feature is disabled.
If it is less than or equal to 0, the number of processes is If it is less than or equal to 0, the number of processes is
equal to that of sources. equal to that of sources.
Default value: 4 Default value: 1
*deoplete-options-omni_patterns* *deoplete-options-omni_patterns*
omni_patterns omni_patterns
@ -260,6 +263,14 @@ on_text_changed_i
Deoplete enables the auto completion on |TextChangedI| autocmd Deoplete enables the auto completion on |TextChangedI| autocmd
if this value is True. if this value is True.
Default value: v:true
*deoplete-options-overwrite_completeopt*
overwrite_completeopt
Deoplete overwrites 'completeopt' option.
You can disable the feature but if you change it, deoplete may
not work.
Default value: v:true Default value: v:true
*deoplete-options-profile* *deoplete-options-profile*
@ -289,13 +300,22 @@ prev_completion_mode
refresh_always refresh_always
Deoplete refreshes the candidates automatically if this value Deoplete refreshes the candidates automatically if this value
is True. is True.
Note: It increases the screen flicker.
Note: It increases the screen flicker when
|deoplete-options-num_processes| != 0.
Default value: v:true
*deoplete-options-refresh_backspace*
refresh_backspace
Deoplete refreshes the candidates automatically when you
press <BS> or <C-h> key.
Default value: v:true Default value: v:true
*deoplete-options-skip_multibyte* *deoplete-options-skip_multibyte*
skip_multibyte skip_multibyte
Deoplete skip multibyte text completion automatically if this Deoplete skips multibyte text completion automatically if this
value is True. value is True.
Default value: v:false Default value: v:false
@ -306,20 +326,13 @@ skip_chars
Default value: ['(', ')'] Default value: ['(', ')']
*deoplete-options-smart_case*
smart_case
When a capital letter is included in input, deoplete does
not ignore the upper- and lowercase.
Default value: same with your 'smartcase' value
*deoplete-options-sources* *deoplete-options-sources*
sources sources
It is a dictionary to specify source names. The key is It is a dictionary to specify source names. The key is
filetype and the value is source names list. If the key is filetype and the value is a list of source names. If the key
"_", the value will be used for default filetypes. For is "_", the value will be used for default filetypes. For
example, you can load some sources in C++ filetype. example, you can load some sources in C++ filetype. If the
If the value is [], it will load all sources. value is [], it will load all sources.
Default value: {} Default value: {}
> >
@ -470,7 +483,7 @@ deoplete#custom#source({source-name}, {dict})
" Disable the candidates in Comment/String syntaxes. " Disable the candidates in Comment/String syntaxes.
call deoplete#custom#source('_', call deoplete#custom#source('_',
\ 'disabled_syntaxes', ['Comment', 'String']) \ 'disabled_syntaxes', ['Comment', 'String', 'Constant'])
" Change the truncate width. " Change the truncate width.
call deoplete#custom#source('javacomplete2', call deoplete#custom#source('javacomplete2',
@ -492,7 +505,8 @@ deoplete#custom#source({source-name}, {dict})
" Enable jedi source debug messages " Enable jedi source debug messages
" call deoplete#custom#option('profile', v:true) " call deoplete#custom#option('profile', v:true)
" call deoplete#enable_logging('DEBUG', 'deoplete.log') " call deoplete#enable_logging('DEBUG', 'deoplete.log')
" call deoplete#custom#source('jedi', 'is_debug_enabled', 1) " call deoplete#custom#source('jedi',
\ 'is_debug_enabled', v:true)
< <
*deoplete#custom#var()* *deoplete#custom#var()*
deoplete#custom#var({source-name}, {var-name}, {value}) deoplete#custom#var({source-name}, {var-name}, {value})
@ -510,9 +524,14 @@ KEY MAPPINGS *deoplete-key-mappings*
deoplete#auto_complete([{event}]) deoplete#auto_complete([{event}])
It calls the auto completion of deoplete. You can use it to It calls the auto completion of deoplete. You can use it to
call auto completion again. call auto completion again.
{event} is autocmd event name. If it is omit, "Async" is {event} is autocmd event name. If it is omitted, "Async" is
used. used.
*deoplete#can_complete()*
deoplete#can_complete()
Return v:true if current word completion is available.
Note: |deoplete-options-auto_complete_popup| must be "manual".
*deoplete#close_popup()* *deoplete#close_popup()*
deoplete#close_popup() deoplete#close_popup()
Insert candidate and close popup menu for deoplete. Insert candidate and close popup menu for deoplete.
@ -525,18 +544,18 @@ deoplete#complete()
*deoplete#complete_common_string()* *deoplete#complete_common_string()*
deoplete#complete_common_string() deoplete#complete_common_string()
complete common string in candidates. It will be convenient complete common string in candidates.
when candidates have long common string. This can be useful when candidates have a long common prefix.
Note: It must be in |:map-<expr>|. Note: It must be in |:map-<expr>|.
*deoplete#insert_candidate()* *deoplete#insert_candidate()*
deoplete#insert_candidate({number}) deoplete#insert_candidate({index})
Insert {number}th candidate. Insert the candidate at index {index}. Indices start at 0.
Note: It must be in |:map-<expr>|. Note: It must be in |:map-<expr>|.
*deoplete#manual_complete()* *deoplete#manual_complete()*
deoplete#manual_complete([{sources}]) deoplete#manual_complete([{sources}])
It calls the completion of deoplete. You can use it with Trigger deoplete completion. You can use it with
custom completion setups. custom completion setups.
You can provide a list of {sources}: It can be the name of a You can provide a list of {sources}: It can be the name of a
source or a list of sources name. source or a list of sources name.
@ -544,8 +563,8 @@ deoplete#manual_complete([{sources}])
Note: It must be in |:map-<expr>|. Note: It must be in |:map-<expr>|.
If you want to trigger deoplete manually, see also If you want to trigger deoplete manually, see also
|deoplete-options-auto_complete|, which should be 1 then |deoplete-options-auto_complete|, which should typically then
typically. be 1.
> >
inoremap <silent><expr> <TAB> inoremap <silent><expr> <TAB>
\ pumvisible() ? "\<C-n>" : \ pumvisible() ? "\<C-n>" :
@ -566,13 +585,12 @@ deoplete#smart_close_popup()
inoremap <expr><BS> inoremap <expr><BS>
\ deoplete#smart_close_popup()."\<C-h>" \ deoplete#smart_close_popup()."\<C-h>"
< <
Note: This mapping conflicts with |SuperTab| or |endwise| Note: This mapping conflicts with SuperTab or endwise plugins.
plugins.
Note: This key mapping is for <C-h> or <BS> keymappings. Note: This key mapping is for <C-h> or <BS> keymappings.
*deoplete#undo_completion()* *deoplete#undo_completion()*
deoplete#undo_completion() deoplete#undo_completion()
Undo inputted candidate. Undo inserted candidate.
Note: It must be in |:map-<expr>|. Note: It must be in |:map-<expr>|.
> >
inoremap <expr><C-g> deoplete#undo_completion() inoremap <expr><C-g> deoplete#undo_completion()
@ -583,7 +601,7 @@ EXAMPLES *deoplete-examples*
" Use deoplete. " Use deoplete.
let g:deoplete#enable_at_startup = 1 let g:deoplete#enable_at_startup = 1
" Use smartcase. " Use smartcase.
call deoplete#custom#option('smart_case', v:true) call deoplete#custom#source('_', 'smart_case', v:true)
" <CR>: close popup and save indent. " <CR>: close popup and save indent.
inoremap <silent> <CR> <C-r>=<SID>my_cr_function()<CR> inoremap <silent> <CR> <C-r>=<SID>my_cr_function()<CR>
@ -656,15 +674,21 @@ file *deoplete-source-file*
Source custom variables: Source custom variables:
enable_buffer_path enable_buffer_path
If it is True, file source completes the files If it is True, file source completes the files
from the buffer path instead of the current from the buffer directory instead of the
directory. current directory.
(default: v:true) (default: v:true)
enable_slash_completion
If it is True, file source completes the files
when user input "/".
(default: v:false)
force_completion_length force_completion_length
The completion length if the input does not The completion length if the input does not
contain "/". contain "/".
If it is less than 0, it is disabled. If it is less than 0, it is disabled.
(default: -1) (default: -1)
member *deoplete-source-member* member *deoplete-source-member*
This source collects members from current buffer. This source collects members from current buffer.
@ -706,7 +730,7 @@ omni *deoplete-source-omni*
setting. setting.
(default: {}) (default: {})
> >
call deoplete#custom#source('omni', 'functions', { call deoplete#custom#var('omni', 'functions', {
\ 'javascript': ['tern#Complete', 'jspc#omni'] \ 'javascript': ['tern#Complete', 'jspc#omni']
\}) \})
< <
@ -730,6 +754,10 @@ omni *deoplete-source-omni*
============================================================================== ==============================================================================
FILTERS *deoplete-filters* FILTERS *deoplete-filters*
Once candidates have been supplied by one or more sources, they are passed
through the filters, which are matchers, converters or sorters. Sources can
have specific filters.
*deoplete-filter-matcher_default* *deoplete-filter-matcher_default*
Default matchers: ['matcher_fuzzy'] Default matchers: ['matcher_fuzzy']
@ -778,9 +806,13 @@ matcher_length
Length matching matcher. Length matching matcher.
It removes candidates shorter than or equal to the user input. It removes candidates shorter than or equal to the user input.
*deoplete-filter-matcher_matchfuzzy*
matcher_matchfuzzy
|matchfuzzy()| matcher.
*deoplete-filter-sorter_rank* *deoplete-filter-sorter_rank*
sorter_rank Matched rank order sorter. The higher the head matched word sorter_rank Matched rank order sorter. The higher the head matched word
or already typed word. or already typed or inserted word.
The locality bonus feature is implemented like VSCode. The locality bonus feature is implemented like VSCode.
*deoplete-filter-sorter_word* *deoplete-filter-sorter_word*
@ -889,8 +921,7 @@ converter_reorder_attr
*deoplete-filter-converter_auto_paren* *deoplete-filter-converter_auto_paren*
converter_auto_paren converter_auto_paren
It adds parentheses character in a candidate's word. It adds parentheses character in a candidate's word.
It is useful if you use |neopairs| or |neosnippet| It is useful if you use neopairs or neosnippet plugins.
plugins.
*deoplete-filter-converter_case* *deoplete-filter-converter_case*
converter_case converter_case
@ -920,7 +951,10 @@ converter_truncate_info
converter_truncate_menu converter_truncate_menu
It truncates a candidate's menu by the current window width. It truncates a candidate's menu by the current window width.
============================================================================== *deoplete-filter-converter_word_abbr*
converter_word_abbr
Convert candidate word to abbr.
CREATE SOURCE *deoplete-create-source* CREATE SOURCE *deoplete-create-source*
To create source, you should read default sources implementation in To create source, you should read default sources implementation in
@ -928,7 +962,7 @@ rplugin/python3/deoplete/source/*.py.
The files are automatically loaded and deoplete creates new Source class The files are automatically loaded and deoplete creates new Source class
object. object.
Source class must extend Base class in ".base". Source class must extend the Base class in ".base".
Note: The sources must be written in Python3 language. Note: The sources must be written in Python3 language.
@ -946,9 +980,20 @@ __init__ (Function)
*deoplete-source-attribute-__* *deoplete-source-attribute-__*
__{name} (Unspecified) (Optional) __{name} (Unspecified) (Optional)
Additional source information. Additional source information.
Note: Recommend sources save variables instead of Note: Sources should save variables instead of
global variables. global variables.
*deoplete-source-attribute-camel_case*
camel_case
If it is True, lowercase letters are also matched with the
corresponding uppercase ones.
Ex: "foB" is matched with "FooBar" but not with "foobar".
Note: This feature is only available in
|deoplete-filter-matcher_fuzzy| or
|deoplete-filter-matcher_full_fuzzy|.
Default value: v:false
*deoplete-source-attribute-converters* *deoplete-source-attribute-converters*
converters (List[str]) (Optional) converters (List[str]) (Optional)
Source default converters list. Source default converters list.
@ -989,40 +1034,58 @@ filetypes (List[str]) (Optional)
Available filetype list. Available filetype list.
Default: [] Default: []
Note: It means this source available in all filetypes. Note: It means this source is available for all filetypes.
*deoplete-source-attribute-gather_candidates* *deoplete-source-attribute-gather_candidates*
gather_candidates gather_candidates
(Function) (Required) (Function) (Required)
It is called to gather candidates. It is called to gather candidates.
It takes {self} and {context} as its parameter and returns a It takes {self} and {context} as parameters and returns a
list of {candidate}. list of {candidate}.
If the error is occurred, it must return None. If an error occurrs, it must return None.
{candidate} must be String or Dictionary contains {candidate} must be String or Dictionary contains
|deoplete-candidate-attributes|. |deoplete-candidate-attributes|.
Here, {context} is the context information when the source is Here, {context} is the context information when the source is
called (|deoplete-notation-{context}|). called (|deoplete-notation-{context}|).
Note: The source must not filter the candidates by user input. Note: The source must not filter the candidates by user input.
It is |deoplete-filters| work. If the source filter the Instead, let the |deoplete-filters| match and sort the
candidates, user cannot filter the candidates by fuzzy match. results. If the source filters the candidates, the user
cannot then filter the candidates by fuzzy match.
Instead, let the |deoplete-filters| match and sort the
results. If the source filters the candidates, the user
cannot then filter the candidates by fuzzy match.
candidates, the user cannot then filter the candidates by
fuzzy match.
*deoplete-source-attribute-get_complete_position* *deoplete-source-attribute-get_complete_position*
get_complete_position get_complete_position
(Function) (Optional) (Function) (Optional)
It is called to get complete position. It is called to get the position of the current completion.
It takes {self} and {context} as its parameter and returns It takes {self} and {context} as its parameter and returns
complete position in current line. a number representing the starting position of the completion
Here, {context} is the context information when the source is in the current line.
Here, {context} is the context information from when the
source is called (|deoplete-notation-{context}|).
called (|deoplete-notation-{context}|). called (|deoplete-notation-{context}|).
Default: position using |deoplete-options-keyword_patterns|. Default: position using |deoplete-options-keyword_patterns|.
Note: If |deoplete-source-attribute-is_bytepos| is True, it Note: If |deoplete-source-attribute-is_bytepos| is True, it
must return byte position. must return byte position.
This is useful if you want to complete terms that are more.
*deoplete-source-attribute-ignore_case*
ignore_case
If it is True, deoplete ignores case.
Default value: v:false
*deoplete-source-attribute-input_pattern* *deoplete-source-attribute-input_pattern*
input_pattern input_pattern
(String) (Optional) (String) (Optional)
If it is matched with input, deoplete ignores If it matches the input, deoplete ignores
|deoplete-source-attribute-min_pattern_length|. |deoplete-source-attribute-min_pattern_length|.
It is useful for omni function sources. It is useful for omni function sources.
Note: It is Python3 regexp. Note: It is Python3 regexp.
@ -1036,7 +1099,7 @@ input_patterns
The dictionary version of The dictionary version of
|deoplete-source-attribute-input_pattern|. |deoplete-source-attribute-input_pattern|.
A key is filetype and a value is input pattern. A key is filetype and a value is input pattern.
Note: It is Python3 regexp. Note: Values are Python3 regexps.
*deoplete-source-attribute-is_bytepos* *deoplete-source-attribute-is_bytepos*
is_bytepos is_bytepos
@ -1045,7 +1108,7 @@ is_bytepos
|deoplete-source-attribute-get_complete_position| |deoplete-source-attribute-get_complete_position|
returns byteposition instead of character position. returns byteposition instead of character position.
It is useful for Vim script to create sources because Vim It is useful for Vim script to create sources because Vim
script string uses byte position. Python string uses script string uses byte position. Python strings use
character position. character position.
Default: False Default: False
@ -1067,7 +1130,7 @@ is_silent
*deoplete-source-attribute-is_skip_langmap* *deoplete-source-attribute-is_skip_langmap*
is_skip_langmap is_skip_langmap
(Bool) (Optional) (Bool) (Optional)
If it is True, the source skips the |langmap| completion. If it is True, the source skips the 'langmap' completion.
Default: True Default: True
@ -1087,6 +1150,11 @@ is_volatile
*deoplete-source-attribute-mark* *deoplete-source-attribute-mark*
mark (String) (Optional) mark (String) (Optional)
The mark of a source. The mark of a source.
Note: If the source set candidate menu, the source must set
it. If the attribute is empty string, the candidate menu will
be disabled.
Default: ''
*deoplete-source-attribute-matchers* *deoplete-source-attribute-matchers*
matchers (List[str]) (Optional) matchers (List[str]) (Optional)
@ -1104,44 +1172,43 @@ matcher_key (String) (Optional)
*deoplete-source-attribute-max_abbr_width* *deoplete-source-attribute-max_abbr_width*
max_abbr_width max_abbr_width
(Integer) (Optional) (Integer) (Optional)
If the candidate abbr length exceeds the length it will be cut If the candidate abbr length exceeds this value, it will be
cut down.
down. down.
If it is less than or equal to 0, it will be disabled. If it is less than or equal to 0, it will be disabled.
Default: 80 Default: 80
*deoplete-source-attribute-max_candidates* *deoplete-source-attribute-max_candidates*
max_candidates max_candidates
(Integer) (Optional) (Integer) (Optional)
If the candidates are more than it, deoplete will ignore the If there are more candidates than this value, deoplete will
filtering. ignore the filtering.
Default: 500 Default: 500
*deoplete-source-attribute-max_kind_width* *deoplete-source-attribute-max_kind_width*
max_kind_width max_kind_width
(Integer) (Optional) (Integer) (Optional)
If the candidate kind length exceeds the length it will be cut If the candidate kind length exceeds this value it will be
down. trimmed. If this value is less than or equal to 0, it will be
If it is less than or equal to 0, it will be disabled. disabled.
Default: 40 Default: 40
*deoplete-source-attribute-max_info_width* *deoplete-source-attribute-max_info_width*
max_info_width max_info_width
(Integer) (Optional) (Integer) (Optional)
If the candidate info length exceeds the length it will be cut If the candidate info length exceeds this value it is trimmed.
down. If this value is less than or equal to 0, it will be disabled.
If it is less than or equal to 0, it will be disabled.
Default: 200 Default: 200
*deoplete-source-attribute-max_menu_width* *deoplete-source-attribute-max_menu_width*
max_menu_width max_menu_width
(Integer) (Optional) (Integer) (Optional)
If the candidate menu length exceeds the length it will be cut If the candidate menu length exceeds this value it is trimmed.
down. down.
If it is less than or equal to 0, it will be disabled. If this value is less than or equal to 0, it will be disabled.
Default: 40 Default: 40
@ -1199,6 +1266,13 @@ rank (Integer) (Optional)
Default: 100 Default: 100
*deoplete-source-attribute-smart_case*
smart_case
When a capital letter is included in input, deoplete does
not ignore the upper- and lowercase.
Default value: v:false
*deoplete-source-attribute-sorters* *deoplete-source-attribute-sorters*
sorters (List[str]) (Optional) sorters (List[str]) (Optional)
Source default sorters list. Source default sorters list.
@ -1249,6 +1323,9 @@ vars (Dictionary) (Optional)
The input string of the current line, namely the part The input string of the current line, namely the part
before the cursor. before the cursor.
is_refresh (Bool)
If the input is changed, it will be "True".
is_async (Bool) is_async (Bool)
If the gather is asynchronous, the source must set If the gather is asynchronous, the source must set
it to "True". A typical strategy for an asynchronous it to "True". A typical strategy for an asynchronous
@ -1287,10 +1364,6 @@ vars (Dictionary) (Optional)
context['is_async'] = self._count < 10 context['is_async'] = self._count < 10
return [context['input'].split()[-1] + str(self._count)] return [context['input'].split()[-1] + str(self._count)]
< <
is_refresh (Bool)
If the input is changed, it will be "True".
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
CANDIDATE ATTRIBUTES *deoplete-candidate-attributes* CANDIDATE ATTRIBUTES *deoplete-candidate-attributes*
@ -1333,6 +1406,9 @@ The files are automatically loaded and deoplete creates new Filter class
object. object.
Filter class must extend Base class in ".base". Filter class must extend Base class in ".base".
Matchers, sorters and converters are all kinds of filters, and should all
be placed in the same filter directory.
Note: The filters must be written in Python3 language. Note: The filters must be written in Python3 language.
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
@ -1362,6 +1438,8 @@ filter
============================================================================== ==============================================================================
EXTERNAL SOURCES *deoplete-external-sources* EXTERNAL SOURCES *deoplete-external-sources*
Please see https://github.com/Shougo/deoplete.nvim/wiki/Completion-Sources
neco-vim: "vim" source for Vim script neco-vim: "vim" source for Vim script
https://github.com/Shougo/neco-vim https://github.com/Shougo/neco-vim
@ -1374,124 +1452,11 @@ https://github.com/Shougo/neoinclude.vim
neco-syntax: "syntax" source neco-syntax: "syntax" source
https://github.com/Shougo/neco-syntax https://github.com/Shougo/neco-syntax
vimshell: "vimshell" source for vimshell
https://github.com/Shougo/vimshell.vim
neco-ghc: "ghc" source for Haskell
https://github.com/eagletmt/neco-ghc
neco-look: "look" source to suggest words from sorted dictionary
https://github.com/ujihisa/neco-look
vim-racer: "racer" source for Rust
https://github.com/racer-rust/vim-racer
UltiSnips source: "ultisnips" source for UltiSnips
https://github.com/SirVer/ultisnips
clang-complete: "clang_complete" source for C/C++/Objective-C
https://github.com/Rip-Rip/clang_complete
deoplete-go: "go" source for Go
https://github.com/deoplete-plugins/deoplete-go
elixir.nvim: "elixir" source for Elixir
https://github.com/awetzel/elixir.nvim
deoplete-jedi: "jedi" source for Python
https://github.com/deoplete-plugins/deoplete-jedi
perlomni.vim: "PerlOmni" source for Perl
https://github.com/c9s/perlomni.vim
nvim-typescript: "typescript" source for typescript
https://github.com/mhartington/nvim-typescript
async-clj-omni: "async_clj" source for Clojure
https://github.com/SevereOverfl0w/async-clj-omni
deoplete-ternjs: "ternjs" source for JavaScript
https://github.com/carlitux/deoplete-ternjs
deoplete-swift: "swift" source for Swift
https://github.com/landaire/deoplete-swift
neovim-intellij-complete-deoplete: "intellij" source for Intellij IDE
https://github.com/vhakulinen/neovim-intellij-complete-deoplete
tmux-complete: "tmuxcomplete" source for tmux panes
https://github.com/wellle/tmux-complete.vim
deoplete-github: "github" source for "gitcommit" filetype
https://github.com/SevereOverfl0w/deoplete-github
deoplete-flow: "flow" source for JavaScript
https://github.com/steelsojka/deoplete-flow
deoplete-d: "d" source for D language
https://github.com/landaire/deoplete-d
deoplete-rtags: "rtags" source for "c", "cpp", "objc" and "objcpp" filetypes
https://github.com/LuXuryPro/deoplete-rtags
deoplete-solargraph "solargraph" source for Ruby language
https://github.com/uplus/deoplete-solargraph
deoplete-padawan: "padawan" source for padawan.php
https://github.com/pbogut/deoplete-padawan
webcomplete.vim: "webcomplete" source for browser opened pages
https://github.com/thalesmello/webcomplete.vim
deoplete-julia: "julia" source for Julia
https://github.com/JuliaEditorSupport/deoplete-julia
acid.nvim: "acid" source for Clojure
https://github.com/hkupty/acid.nvim
deoplete-omnisharp: "cs" source for C#
https://github.com/Robzz/deoplete-omnisharp/
deoplete-omnisharp: Improved version of deoplete-omnisharp
https://github.com/dimixar/deoplete-omnisharp/
deoplete-omnisharp: Embedded omnisharp server version of deoplete-omnisharp
https://github.com/cyansprite/deoplete-omnisharp
deoplete-hack: "hack" source for Hack and PHP
https://github.com/zefei/deoplete-hack
deoplete-laravel-plugin: "laravel-plugin" source for "php" and "blade"
filetypes
https://github.com/rafaelndev/deoplete-laravel-plugin
deoplete-zsh: "zsh" source for Zsh deoplete-zsh: "zsh" source for Zsh
https://github.com/deoplete-plugins/deoplete-zsh https://github.com/deoplete-plugins/deoplete-zsh
deoplete-fish: "fish" source for fish shell deoplete-lsp source for neovim builtin LSP features
https://github.com/ponko2/deoplete-fish https://github.com/deoplete-plugins/deoplete-lsp
deoplete-fsharp: "fsharp" source for F#
https://github.com/callmekohei/deoplete-fsharp
autocomplete-flow: "flow" source for JavaScript
https://github.com/wokalski/autocomplete-flow
deoplete-asm: "asm" source for Assembly Language
https://github.com/deoplete-plugins/deoplete-asm
deoplete-abook: "abook" source for abook contacts
https://github.com/fszymanski/deoplete-abook
deoplete-emoji: "emoji" source for emoji codes
https://github.com/fszymanski/deoplete-emoji
LanguageClient-neovim: "LanguageClient" source for Language Server
Protocol(LSP)
https://github.com/autozimu/LanguageClient-neovim
deoplete-vim-lsp: "lsp" source for vim-lsp
https://github.com/lighttiger2505/deoplete-vim-lsp
deoplete-tags: "tag" source for tag files deoplete-tags: "tag" source for tag files
https://github.com/deoplete-plugins/deoplete-tag https://github.com/deoplete-plugins/deoplete-tag
@ -1617,7 +1582,7 @@ A: Please enable logging feature like this. >
call deoplete#custom#option('profile', v:true) call deoplete#custom#option('profile', v:true)
call deoplete#enable_logging('DEBUG', 'deoplete.log') call deoplete#enable_logging('DEBUG', 'deoplete.log')
call deoplete#custom#source('jedi', 'is_debug_enabled', 1) call deoplete#custom#source('jedi', 'is_debug_enabled', v:true)
Q: "Channel id must be a positive integer" error. Q: "Channel id must be a positive integer" error.
https://github.com/Shougo/deoplete.nvim/issues/406 https://github.com/Shougo/deoplete.nvim/issues/406
@ -1666,6 +1631,13 @@ Q: deoplete conflicts with lexima.vim
A: > A: >
https://github.com/cohama/lexima.vim/issues/65#issuecomment-339338677 https://github.com/cohama/lexima.vim/issues/65#issuecomment-339338677
Q: Vim's build-in completion behavior(|i_CTRL-P| etc) is changed after
deoplete is enabled. The first entry is not inserted.
A: It is the feature of deoplete. The default insertion is disabled to enable
completion asynchronously. If you don't like the behavior, you should use
other auto completion plugin.
*deoplete-faq-config* *deoplete-faq-config*
2. Configuration~ 2. Configuration~
@ -1675,11 +1647,9 @@ Q: I want to silence the |ins-completion-menu| messages in the command line
A: You can disable the messages through the 'shortmess' option. > A: You can disable the messages through the 'shortmess' option. >
if has("patch-7.4.314")
set shortmess+=c set shortmess+=c
endif
Q: I want to use the auto select feature like |neocomplete|. Q: I want to use the auto select feature like neocomplete.
A: You can use it by the 'completeopt' option. > A: You can use it by the 'completeopt' option. >
@ -1747,6 +1717,11 @@ Q: How can I sort all entries alphabetically?
A: > A: >
call deoplete#custom#source('_', 'sorters', ['sorter_word']) call deoplete#custom#source('_', 'sorters', ['sorter_word'])
Q: I want to disable all sources marks.
A: >
call deoplete#custom#source('_', 'mark', '')
Q: I want to use head matcher instead of fuzzy matcher. Q: I want to use head matcher instead of fuzzy matcher.
A: You can achieve this by following > A: You can achieve this by following >
@ -1848,7 +1823,7 @@ A: >
inoremap <silent><expr> <TAB> inoremap <silent><expr> <TAB>
\ pumvisible() ? "\<C-n>" : \ pumvisible() ? "\<C-n>" :
\ <SID>check_back_space() ? "\<Tab>" : \ <SID>check_back_space() ? "\<Tab>" :
\ deoplete#complete() \ deoplete#can_complete() ? deoplete#complete() : ''
function! s:check_back_space() abort function! s:check_back_space() abort
let col = col('.') - 1 let col = col('.') - 1
return !col || getline('.')[col - 1] =~# '\s' return !col || getline('.')[col - 1] =~# '\s'
@ -1876,6 +1851,15 @@ If you really need the feature, it works for me. >
inoremap <expr> <C-h> pumvisible() ? inoremap <expr> <C-h> pumvisible() ?
\ "\<C-h>" . deoplete#complete() : "\<C-h>" \ "\<C-h>" . deoplete#complete() : "\<C-h>"
< <
Q: deoplete has the flicker issue when parallel completion feature is enabled.
A: I recommend for you to disable |deoplete-options-refresh_always|option when
you enable deoplete parallel completion. >
call deoplete#custom#option('num_processes', 4)
call deoplete#custom#option('refresh_always', v:false)
*deoplete-faq-ft-specific* *deoplete-faq-ft-specific*
3. Filetype Specific Questions~ 3. Filetype Specific Questions~
@ -1888,7 +1872,7 @@ A: Please use |deoplete-options-auto_complete|. >
Q: I want to use C/C++ omni completion with deoplete. Q: I want to use C/C++ omni completion with deoplete.
A: You should use |deoplete-clangx|. A: You should use deoplete-clangx.
https://github.com/Shougo/deoplete-clangx https://github.com/Shougo/deoplete-clangx
@ -1963,10 +1947,10 @@ A: >
Q: How to donate money to you? Q: How to donate money to you?
A: I don't get the donation, but if you want to donate, please support neovim A: I have started github sponsorship to spend more time for Vim/neovim
project. My plugins depends on neovim development. plugins. You can donate money to help me!
https://salt.bountysource.com/teams/neovim https://github.com/sponsors/Shougo
Q: What means "dark powered"? Q: What means "dark powered"?
@ -2005,6 +1989,11 @@ A: deoplete is:
If you don't like node.js based plugin or huge plugin base system, you If you don't like node.js based plugin or huge plugin base system, you
should not choose coc.nvim. should not choose coc.nvim.
completion-nvim is Lua based plugin for neovim.
You cannot use it in Vim8.
If you like Lua, you don't use Vim8, and you don't like external
dependency, it is a better choice.
And the important view is the author. And the important view is the author.
If you like the author or the author created plugins, you should choose the If you like the author or the author created plugins, you should choose the
auto completion plugin. auto completion plugin.
@ -2021,9 +2010,26 @@ A: deoplete is:
I don't think deoplete is the best for everyone. Please choose auto I don't think deoplete is the best for everyone. Please choose auto
completion plugin. completion plugin.
Q: deoplete with coc.nvim does not work.
https://github.com/Shougo/deoplete.nvim/issues/1192
A: Unfortunately, both deoplete and coc.nvim provide auto completion feature.
So deoplete conflicts with coc.nvim.
You cannot use both. You need to choose coc.nvim or deoplete.
Note: But to change |deoplete-options-overwrite_completeopt| may work for
you.
============================================================================== ==============================================================================
COMPATIBILITY *deoplete-compatibility* COMPATIBILITY *deoplete-compatibility*
2021.05.30
* "smart_case" and "ignore_case" and "camel_case" are source specific options
instead.
2020.11.07
* Disable nofile buffers completion except
deoplete-options-nofile_complete_filetypes.
2020.04.26 2020.04.26
* Add deprecated variables warnings. * Add deprecated variables warnings.

View File

@ -4,19 +4,19 @@
# License: MIT license # License: MIT license
# ============================================================================ # ============================================================================
from importlib.util import find_spec
from pynvim import Nvim
import typing import typing
from importlib.util import find_spec
from deoplete.deoplete import Deoplete from deoplete.deoplete import Deoplete
from deoplete.util import Nvim
try:
if find_spec('yarp'): # For Vim8
import vim import vim
elif find_spec('pynvim'): except ModuleNotFoundError:
# For neovim
# Note: neovim cannot import vim module
import pynvim as vim import pynvim as vim
else:
import neovim as vim
Context = typing.Dict[str, typing.Any] Context = typing.Dict[str, typing.Any]
@ -26,8 +26,8 @@ if hasattr(vim, 'plugin'):
@vim.plugin @vim.plugin
class DeopleteHandlers(object): class DeopleteHandlers(object):
def __init__(self, vim: Nvim): def __init__(self, _vim: Nvim):
self._vim = vim self._vim = _vim
@vim.function('_deoplete_init', sync=False) # type: ignore @vim.function('_deoplete_init', sync=False) # type: ignore
def init_channel(self, def init_channel(self,

View File

@ -4,11 +4,12 @@
# License: MIT license # License: MIT license
# ============================================================================ # ============================================================================
from pynvim import Nvim
import typing import typing
from abc import abstractmethod from abc import abstractmethod
from deoplete.logger import LoggingMixin from deoplete.logger import LoggingMixin
from deoplete.util import error_vim, Nvim, UserContext, Candidates from deoplete.util import error_vim, UserContext, Candidates
class Base(LoggingMixin): class Base(LoggingMixin):
@ -22,14 +23,14 @@ class Base(LoggingMixin):
def on_event(self, context: UserContext) -> None: def on_event(self, context: UserContext) -> None:
pass pass
def get_var(self, var_name: str) -> typing.Optional[typing.Any]: def get_var(self, var_name: str) -> typing.Any:
custom_vars = self.vim.call( custom_vars = self.vim.call(
'deoplete#custom#_get_filter', self.name) 'deoplete#custom#_get_filter', self.name)
if var_name in custom_vars: if var_name in custom_vars:
return custom_vars[var_name] return custom_vars[var_name]
if var_name in self.vars: if var_name in self.vars:
return self.vars[var_name] return self.vars[var_name]
return None return ''
@abstractmethod @abstractmethod
def filter(self, context: UserContext) -> Candidates: def filter(self, context: UserContext) -> Candidates:

View File

@ -4,12 +4,13 @@
# License: MIT license # License: MIT license
# ============================================================================ # ============================================================================
from abc import abstractmethod
from pynvim import Nvim
import re import re
import typing import typing
from abc import abstractmethod
from deoplete.logger import LoggingMixin from deoplete.logger import LoggingMixin
from deoplete.util import debug, error_vim, Nvim, UserContext, Candidates from deoplete.util import debug, error_vim, UserContext, Candidates
class Base(LoggingMixin): class Base(LoggingMixin):
@ -51,6 +52,9 @@ class Base(LoggingMixin):
self.max_candidates = 500 self.max_candidates = 500
self.matcher_key = '' self.matcher_key = ''
self.dup = False self.dup = False
self.ignore_case = False
self.smart_case = False
self.camel_case = False
def get_complete_position(self, context: UserContext) -> int: def get_complete_position(self, context: UserContext) -> int:
m = re.search('(?:' + context['keyword_pattern'] + ')$|$', m = re.search('(?:' + context['keyword_pattern'] + ')$|$',
@ -72,14 +76,14 @@ class Base(LoggingMixin):
def on_event(self, context: UserContext) -> None: def on_event(self, context: UserContext) -> None:
pass pass
def get_var(self, var_name: str) -> typing.Optional[typing.Any]: def get_var(self, var_name: str) -> typing.Any:
custom_vars = self.vim.call( custom_vars = self.vim.call(
'deoplete#custom#_get_source_vars', self.name) 'deoplete#custom#_get_source_vars', self.name)
if var_name in custom_vars: if var_name in custom_vars:
return custom_vars[var_name] return custom_vars[var_name]
if var_name in self.vars: if var_name in self.vars:
return self.vars[var_name] return self.vars[var_name]
return None return ''
def get_filetype_var(self, filetype: str, def get_filetype_var(self, filetype: str,
var_name: str) -> typing.Optional[typing.Any]: var_name: str) -> typing.Optional[typing.Any]:

View File

@ -4,24 +4,24 @@
# License: MIT license # License: MIT license
# ============================================================================ # ============================================================================
from collections import defaultdict
from pathlib import Path
from pynvim import Nvim
import copy import copy
import os.path import msgpack
import re import re
import sys import sys
import time import time
import msgpack
import typing import typing
from collections import defaultdict
from deoplete import logger from deoplete import logger
from deoplete.exceptions import SourceInitError from deoplete.exceptions import SourceInitError
from deoplete.util import (bytepos2charpos, charpos2bytepos, error, error_tb, from deoplete.util import (bytepos2charpos, charpos2bytepos, error, error_tb,
import_plugin, get_custom, get_syn_names, import_plugin, get_custom, get_syn_names,
convert2candidates, uniq_list_dict, Nvim) convert2candidates, uniq_list_dict)
UserContext = typing.Dict[str, typing.Any] UserContext = typing.Dict[str, typing.Any]
Candidates = typing.Dict[str, typing.Any] Candidates = typing.List[typing.Dict[str, typing.Any]]
Result = typing.Dict[str, typing.Any] Result = typing.Dict[str, typing.Any]
@ -75,7 +75,8 @@ class Child(logger.LoggingMixin):
self._vim.call('deoplete#auto_complete', 'Update') self._vim.call('deoplete#auto_complete', 'Update')
def main(self, name: str, args: typing.List[typing.Any], def main(self, name: str, args: typing.List[typing.Any],
queue_id: typing.Optional[int]) -> typing.Optional[Candidates]: queue_id: typing.Optional[int]) -> typing.Optional[
typing.Dict[str, typing.Any]]:
ret = None ret = None
if name == 'enable_logging': if name == 'enable_logging':
self._enable_logging() self._enable_logging()
@ -103,6 +104,9 @@ class Child(logger.LoggingMixin):
self.is_debug_enabled = True self.is_debug_enabled = True
def _add_source(self, path: str) -> None: def _add_source(self, path: str) -> None:
# Resolve symbolic link
path = str(Path(path).resolve())
source = None source = None
try: try:
Source = import_plugin(path, 'source', 'Source') Source = import_plugin(path, 'source', 'Source')
@ -110,14 +114,15 @@ class Child(logger.LoggingMixin):
return return
source = Source(self._vim) source = Source(self._vim)
name = os.path.splitext(os.path.basename(path))[0] name = Path(path).stem
source.name = getattr(source, 'name', name) source.name = getattr(source, 'name', name)
source.path = path source.path = path
if source.name in self._loaded_sources: loaded_path = self._loaded_sources.get(source.name, '')
if source.name in self._loaded_sources and path != loaded_path:
# Duplicated name # Duplicated name
error_tb(self._vim, 'Duplicated source: %s' % source.name) error_tb(self._vim, 'Duplicated source: %s' % source.name)
error_tb(self._vim, 'path: "%s" "%s"' % error_tb(self._vim, 'path: "%s" "%s"' %
(path, self._loaded_sources[source.name])) (path, loaded_path))
source = None source = None
except Exception: except Exception:
error_tb(self._vim, 'Could not load source: %s' % path) error_tb(self._vim, 'Could not load source: %s' % path)
@ -129,6 +134,9 @@ class Child(logger.LoggingMixin):
f'Loaded Source: {source.name} ({path})') f'Loaded Source: {source.name} ({path})')
def _add_filter(self, path: str) -> None: def _add_filter(self, path: str) -> None:
# Resolve symbolic link
path = str(Path(path).resolve())
f = None f = None
try: try:
Filter = import_plugin(path, 'filter', 'Filter') Filter = import_plugin(path, 'filter', 'Filter')
@ -136,14 +144,15 @@ class Child(logger.LoggingMixin):
return return
f = Filter(self._vim) f = Filter(self._vim)
name = os.path.splitext(os.path.basename(path))[0] name = Path(path).stem
f.name = getattr(f, 'name', name) f.name = getattr(f, 'name', name)
f.path = path f.path = path
if f.name in self._loaded_filters: loaded_path = self._loaded_filters.get(f.name, '')
if f.name in self._loaded_filters and path != loaded_path:
# Duplicated name # Duplicated name
error_tb(self._vim, 'Duplicated filter: %s' % f.name) error_tb(self._vim, 'Duplicated filter: %s' % f.name)
error_tb(self._vim, 'path: "%s" "%s"' % error_tb(self._vim, 'path: "%s" "%s"' %
(path, self._loaded_filters[f.name])) (path, loaded_path))
f = None f = None
except Exception: except Exception:
# Exception occurred when loading a filter. Log stack trace. # Exception occurred when loading a filter. Log stack trace.
@ -230,6 +239,8 @@ class Child(logger.LoggingMixin):
source.is_volatile, source.is_async)): source.is_volatile, source.is_async)):
return self._prev_results[source.name] return self._prev_results[source.name]
ctx['bufpath'] = context['bufpath']
ctx['cwd'] = context['cwd']
ctx['is_async'] = False ctx['is_async'] = False
ctx['is_refresh'] = True ctx['is_refresh'] = True
ctx['max_abbr_width'] = min(source.max_abbr_width, ctx['max_abbr_width'] = min(source.max_abbr_width,
@ -248,6 +259,8 @@ class Child(logger.LoggingMixin):
if ctx['max_menu_width'] > 0: if ctx['max_menu_width'] > 0:
ctx['max_menu_width'] = max(10, ctx['max_menu_width']) ctx['max_menu_width'] = max(10, ctx['max_menu_width'])
self._set_context_case(source, ctx)
# Gathering # Gathering
self._profile_start(ctx, source.name) self._profile_start(ctx, source.name)
ctx['vars'] = self._vim.vars ctx['vars'] = self._vim.vars
@ -289,6 +302,19 @@ class Child(logger.LoggingMixin):
except Exception as exc: except Exception as exc:
self._handle_source_exception(source, exc) self._handle_source_exception(source, exc)
def _set_context_case(self, source: typing.Any,
context: UserContext) -> None:
case = source.smart_case or source.camel_case
ignorecase = source.ignore_case
if case:
if re.search(r'[A-Z]', context['complete_str']):
ignorecase = False
else:
ignorecase = True
context['camelcase'] = source.camel_case
context['ignorecase'] = ignorecase
context['smartcase'] = source.smart_case
def _handle_source_exception(self, def _handle_source_exception(self,
source: typing.Any, exc: Exception) -> None: source: typing.Any, exc: Exception) -> None:
if isinstance(exc, SourceInitError): if isinstance(exc, SourceInitError):
@ -330,8 +356,7 @@ class Child(logger.LoggingMixin):
error_tb(self._vim, 'Errors from: %s' % f) error_tb(self._vim, 'Errors from: %s' % f)
def _get_candidates(self, result: Result, def _get_candidates(self, result: Result,
context_input: str, next_input: str context_input: str, next_input: str) -> Candidates:
) -> typing.Optional[Candidates]:
source = result['source'] source = result['source']
# Gather async results # Gather async results
@ -339,7 +364,7 @@ class Child(logger.LoggingMixin):
self._gather_async_results(result, source) self._gather_async_results(result, source)
if not result['candidates']: if not result['candidates']:
return None return []
# Source context # Source context
ctx = copy.copy(result['context']) ctx = copy.copy(result['context'])
@ -349,14 +374,7 @@ class Child(logger.LoggingMixin):
ctx['complete_str'] = context_input[ctx['char_position']:] ctx['complete_str'] = context_input[ctx['char_position']:]
ctx['is_sorted'] = False ctx['is_sorted'] = False
# Set ignorecase self._set_context_case(source, ctx)
case = ctx['smartcase'] or ctx['camelcase']
if case:
if re.search(r'[A-Z]', ctx['complete_str']):
ctx['ignorecase'] = False
else:
ctx['ignorecase'] = True
ignorecase = ctx['ignorecase']
# Match # Match
matchers = [self._filters[x] for x matchers = [self._filters[x] for x
@ -397,14 +415,19 @@ class Child(logger.LoggingMixin):
for candidates in sorted_candidates: for candidates in sorted_candidates:
ctx['candidates'] += candidates ctx['candidates'] += candidates
ctx['ignorecase'] = ignorecase
# On post filter # On post filter
if hasattr(source, 'on_post_filter'): if hasattr(source, 'on_post_filter'):
ctx['candidates'] = source.on_post_filter(ctx) ctx['candidates'] = source.on_post_filter(ctx)
mark = source.mark + ' ' mark = source.mark + ' '
# Check user mark set
user_mark = self._vim.call(
'deoplete#custom#_get_source', source.name).get('mark', '')
if user_mark == '':
user_mark = self._vim.call(
'deoplete#custom#_get_source', '_').get('mark', mark)
refresh = False refresh = False
refresh_always = self._vim.call( refresh_always = self._vim.call(
'deoplete#custom#_get_option', 'refresh_always') 'deoplete#custom#_get_option', 'refresh_always')
@ -418,10 +441,13 @@ class Child(logger.LoggingMixin):
for candidate in ctx['candidates']: for candidate in ctx['candidates']:
candidate['icase'] = 1 candidate['icase'] = 1
candidate['equal'] = refresh candidate['equal'] = refresh
candidate['source'] = source.name
# Set default menu # Set default menu
if (mark != ' ' and if user_mark == '':
candidate.get('menu', '').find(mark) != 0): # Disable menu
candidate['menu'] = ''
elif mark != ' ' and candidate.get('menu', '').find(mark) != 0:
candidate['menu'] = mark + candidate.get('menu', '') candidate['menu'] = mark + candidate.get('menu', '')
if source.dup: if source.dup:
@ -431,7 +457,7 @@ class Child(logger.LoggingMixin):
# Remove duplicates # Remove duplicates
ctx['candidates'] = uniq_list_dict(ctx['candidates']) ctx['candidates'] = uniq_list_dict(ctx['candidates'])
return ctx['candidates'] # type: ignore return list(ctx['candidates'])
def _itersource(self, context: UserContext def _itersource(self, context: UserContext
) -> typing.Generator[typing.Any, None, None]: ) -> typing.Generator[typing.Any, None, None]:
@ -443,7 +469,9 @@ class Child(logger.LoggingMixin):
'ignore_sources', ft, [])) 'ignore_sources', ft, []))
for source_name, source in self._get_sources().items(): for source_name, source in self._get_sources().items():
if source.filetypes is None or source_name in ignore_sources: if source.filetypes is None or (
source_name in ignore_sources and
context['event'] != 'Manual'):
continue continue
if context['sources'] and source_name not in context['sources']: if context['sources'] and source_name not in context['sources']:
continue continue
@ -509,9 +537,9 @@ class Child(logger.LoggingMixin):
context['input'].find(result['prev_input']) == 0) context['input'].find(result['prev_input']) == 0)
def _is_skip(self, context: UserContext, source: typing.Any) -> bool: def _is_skip(self, context: UserContext, source: typing.Any) -> bool:
if 'syntax_names' in context and source.disabled_syntaxes: if (context.get('syntax_names', []) and source.disabled_syntaxes
p = re.compile('(' + '|'.join(source.disabled_syntaxes) + ')$') and len(set(context['syntax_names']) &
if next(filter(p.search, context['syntax_names']), None): set(source.disabled_syntaxes)) > 0):
return True return True
iminsert = self._vim.call('getbufvar', '%', '&iminsert') iminsert = self._vim.call('getbufvar', '%', '&iminsert')
@ -536,11 +564,14 @@ class Child(logger.LoggingMixin):
Each item in `attrs` is the attribute name. Each item in `attrs` is the attribute name.
""" """
attrs = ( attrs = (
'camel_case',
'converters', 'converters',
'disabled_syntaxes', 'disabled_syntaxes',
'dup', 'dup',
'filetypes', 'filetypes',
'ignore_case',
'input_pattern', 'input_pattern',
'input_patterns',
'is_debug_enabled', 'is_debug_enabled',
'is_silent', 'is_silent',
'is_volatile', 'is_volatile',
@ -553,6 +584,7 @@ class Child(logger.LoggingMixin):
'max_menu_width', 'max_menu_width',
'max_pattern_length', 'max_pattern_length',
'min_pattern_length', 'min_pattern_length',
'smart_case',
'sorters', 'sorters',
) )

View File

@ -4,11 +4,12 @@
# License: MIT license # License: MIT license
# ============================================================================ # ============================================================================
import os from pathlib import Path
from pynvim import Nvim
import re import re
import typing import typing
from deoplete.util import Nvim from deoplete.util import exists_path
UserContext = typing.Dict[str, typing.Any] UserContext = typing.Dict[str, typing.Any]
@ -48,6 +49,7 @@ class Context(object):
'deoplete#util#get_next_input', event), 'deoplete#util#get_next_input', event),
'position': self._vim.call('getpos', '.'), 'position': self._vim.call('getpos', '.'),
'same_filetypes': same_filetypes, 'same_filetypes': same_filetypes,
'time': self._vim.call('reltime'),
} }
context.update(self._cached) # type: ignore context.update(self._cached) # type: ignore
@ -79,26 +81,20 @@ class Context(object):
bufname = self._vim.call('bufname', bufnr) bufname = self._vim.call('bufname', bufnr)
cwd = self._vim.call('getcwd') cwd = self._vim.call('getcwd')
buftype = self._vim.call('getbufvar', '%', '&buftype') buftype = self._vim.call('getbufvar', '%', '&buftype')
bufpath = (bufname if os.path.isabs(bufname) bufpath = (bufname if Path(bufname).is_absolute()
else os.path.join(cwd, bufname)) else str(Path(cwd).joinpath(bufname)))
if not os.path.exists(bufpath) or 'nofile' in buftype: if not exists_path(bufpath) or 'nofile' in buftype:
bufpath = '' bufpath = ''
self._cached = { self._cached = {
'bufnr': bufnr, 'bufnr': bufnr,
'bufname': bufname, 'bufname': bufname,
'bufpath': bufpath, 'bufpath': bufpath,
'camelcase': self._vim.call(
'deoplete#custom#_get_option', 'camel_case'),
'complete_str': '', 'complete_str': '',
'custom': self._vim.call('deoplete#custom#_get'), 'custom': self._vim.call('deoplete#custom#_get'),
'cwd': cwd, 'cwd': cwd,
'encoding': self._vim.options['encoding'], 'encoding': self._vim.options['encoding'],
'ignorecase': self._vim.call(
'deoplete#custom#_get_option', 'ignore_case'),
'is_windows': self._vim.call('has', 'win32'), 'is_windows': self._vim.call('has', 'win32'),
'smartcase': self._vim.call(
'deoplete#custom#_get_option', 'smart_case'),
} }
def _get_context_filetype(self, def _get_context_filetype(self,

View File

@ -4,15 +4,15 @@
# License: MIT license # License: MIT license
# ============================================================================ # ============================================================================
from pathlib import Path
from pynvim import Nvim
import copy import copy
import glob
import os
import typing import typing
import deoplete.parent import deoplete.parent
from deoplete import logger from deoplete import logger
from deoplete.context import Context from deoplete.context import Context
from deoplete.util import error, error_tb, Nvim from deoplete.util import error, error_tb
UserContext = typing.Dict[str, typing.Any] UserContext = typing.Dict[str, typing.Any]
Candidates = typing.Dict[str, typing.Any] Candidates = typing.Dict[str, typing.Any]
@ -26,7 +26,6 @@ class Deoplete(logger.LoggingMixin):
self._vim = vim self._vim = vim
self._runtimepath = '' self._runtimepath = ''
self._runtimepath_list: typing.List[str] = []
self._custom: typing.Dict[str, typing.Dict[str, typing.Any]] = {} self._custom: typing.Dict[str, typing.Dict[str, typing.Any]] = {}
self._loaded_paths: typing.Set[str] = set() self._loaded_paths: typing.Set[str] = set()
self._prev_results: typing.Dict[int, Candidates] = {} self._prev_results: typing.Dict[int, Candidates] = {}
@ -70,6 +69,8 @@ class Deoplete(logger.LoggingMixin):
def completion_begin(self, user_context: UserContext) -> None: def completion_begin(self, user_context: UserContext) -> None:
if not self._context: if not self._context:
self.init_context() self.init_context()
else:
self._context._init_cached()
context = self._context.get(user_context['event']) # type: ignore context = self._context.get(user_context['event']) # type: ignore
context.update(user_context) context.update(user_context)
@ -96,24 +97,30 @@ class Deoplete(logger.LoggingMixin):
if needs_poll: if needs_poll:
self._vim.call('deoplete#handler#_async_timer_start') self._vim.call('deoplete#handler#_async_timer_start')
if not candidates:
self._vim.call('deoplete#mapping#_restore_completeopt')
# Async update is skipped if same.
prev_completion = self._vim.vars['deoplete#_prev_completion'] prev_completion = self._vim.vars['deoplete#_prev_completion']
# Skip if async update is same.
# Note: If needs_poll, it cannot be skipped.
prev_candidates = prev_completion['candidates'] prev_candidates = prev_completion['candidates']
event = context['event'] event = context['event']
if (event == 'Async' or event == 'Update' and same_candidates = prev_candidates and candidates == prev_candidates
prev_candidates and candidates == prev_candidates): if not needs_poll and same_candidates and (
event == 'Async' or event == 'Update'):
return
# Skip if old completion.
if context['time'] < prev_completion['time']:
return return
# error(self._vim, candidates) # error(self._vim, candidates)
self._vim.vars['deoplete#_context'] = { self._vim.vars['deoplete#_context'] = {
'complete_position': position, 'complete_position': position,
'complete_str': context['input'][position:],
'candidates': candidates, 'candidates': candidates,
'event': context['event'], 'event': context['event'],
'input': context['input'], 'input': context['input'],
'is_async': is_async, 'time': context['time'],
'is_async': needs_poll,
} }
if candidates or self._vim.call('deoplete#util#check_popup'): if candidates or self._vim.call('deoplete#util#check_popup'):
@ -165,9 +172,15 @@ class Deoplete(logger.LoggingMixin):
def _merge_results(self, context: UserContext) -> typing.Tuple[ def _merge_results(self, context: UserContext) -> typing.Tuple[
bool, bool, int, typing.List[typing.Any]]: bool, bool, int, typing.List[typing.Any]]:
# If parallel feature is enabled, it is updated frequently.
# But if it is single process, it cannot be updated.
# So it must be updated.
async_check = len(self._parents) > 1 or (
context['event'] != 'Async' and context['event'] != 'Update')
use_prev = (context['input'] == self._prev_input use_prev = (context['input'] == self._prev_input
and context['next_input'] == self._prev_next_input and context['next_input'] == self._prev_next_input
and context['event'] != 'Manual') and context['event'] != 'Manual'
and async_check)
if not use_prev: if not use_prev:
self._prev_results = {} self._prev_results = {}
@ -182,7 +195,8 @@ class Deoplete(logger.LoggingMixin):
complete_position = min(x['complete_position'] for x in results) complete_position = min(x['complete_position'] for x in results)
all_candidates: typing.List[Candidates] = [] all_candidates: typing.List[Candidates] = []
for result in sorted(results, key=lambda x: x['rank'], reverse=True): for result in sorted(results,
key=lambda x: int(x['rank']), reverse=True):
candidates = result['candidates'] candidates = result['candidates']
prefix = context['input'][ prefix = context['input'][
complete_position:result['complete_position']] complete_position:result['complete_position']]
@ -218,43 +232,38 @@ class Deoplete(logger.LoggingMixin):
parent.enable_logging() parent.enable_logging()
self._parents.append(parent) self._parents.append(parent)
def _find_rplugins(self, def _find_rplugins(self, source: str) -> typing.List[Path]:
source: str) -> typing.Generator[str, None, None]:
"""Search for base.py or *.py """Search for base.py or *.py
Searches $VIMRUNTIME/*/rplugin/python3/deoplete/$source[s]/ Searches $VIMRUNTIME/*/rplugin/python3/deoplete/$source[s]/
""" """
if not self._runtimepath_list:
return
sources = ( result = []
os.path.join('rplugin', 'python3', 'deoplete', result += self._vim.call(
source, '*.py'), 'globpath', self._vim.options['runtimepath'],
os.path.join('rplugin', 'python3', 'deoplete', f'rplugin/python3/deoplete/{source}/*.py', 1, 1)
source + 's', '*.py'), result += self._vim.call(
os.path.join('rplugin', 'python3', 'deoplete', 'globpath', self._vim.options['runtimepath'],
source, '*', '*.py'), f'rplugin/python3/deoplete/{source}s/*.py', 1, 1)
) result += self._vim.call(
'globpath', self._vim.options['runtimepath'],
for src in sources: f'rplugin/python3/deoplete/{source}/*/*.py', 1, 1)
for path in self._runtimepath_list: return [Path(x) for x in result]
yield from glob.iglob(os.path.join(path, src))
def _load_sources(self, context: UserContext) -> None: def _load_sources(self, context: UserContext) -> None:
if not self._parents and self._max_parents == 1: if not self._parents and self._max_parents == 1:
self._add_parent(deoplete.parent.SyncParent) self._add_parent(deoplete.parent.SyncParent)
for path in self._find_rplugins('source'): for path in self._find_rplugins('source'):
if (path in self._loaded_paths if str(path) in self._loaded_paths or path.name == 'base.py':
or os.path.basename(path) == 'base.py'):
continue continue
self._loaded_paths.add(path) self._loaded_paths.add(str(path))
if len(self._parents) <= self._parent_count: if len(self._parents) <= self._parent_count:
# Add parent automatically # Add parent automatically
self._add_parent(deoplete.parent.AsyncParent) self._add_parent(deoplete.parent.AsyncParent)
self._parents[self._parent_count].add_source(path) self._parents[self._parent_count].add_source(str(path))
self.debug( # type: ignore self.debug( # type: ignore
f'Process {self._parent_count}: {path}') f'Process {self._parent_count}: {path}')
@ -267,7 +276,7 @@ class Deoplete(logger.LoggingMixin):
def _load_filters(self, context: UserContext) -> None: def _load_filters(self, context: UserContext) -> None:
for path in self._find_rplugins('filter'): for path in self._find_rplugins('filter'):
for parent in self._parents: for parent in self._parents:
parent.add_filter(path) parent.add_filter(str(path))
def _set_source_attributes(self, context: UserContext) -> None: def _set_source_attributes(self, context: UserContext) -> None:
for parent in self._parents: for parent in self._parents:
@ -277,7 +286,6 @@ class Deoplete(logger.LoggingMixin):
runtimepath = self._vim.options['runtimepath'] runtimepath = self._vim.options['runtimepath']
if runtimepath != self._runtimepath: if runtimepath != self._runtimepath:
self._runtimepath = runtimepath self._runtimepath = runtimepath
self._runtimepath_list = runtimepath.split(',')
self._load_sources(context) self._load_sources(context)
self._load_filters(context) self._load_filters(context)

View File

@ -4,9 +4,9 @@
# License: MIT license # License: MIT license
# ============================================================================ # ============================================================================
# For backward compatibility from pynvim import Nvim
from deoplete.base.filter import Base as _Base from deoplete.base.filter import Base as _Base
from deoplete.util import Nvim
class Base(_Base): class Base(_Base):

View File

@ -4,10 +4,11 @@
# License: MIT license # License: MIT license
# ============================================================================ # ============================================================================
from pynvim import Nvim
import typing import typing
from deoplete.base.filter import Base from deoplete.base.filter import Base
from deoplete.util import Nvim, UserContext, Candidates from deoplete.util import UserContext, Candidates
class Filter(Base): class Filter(Base):
@ -21,8 +22,7 @@ class Filter(Base):
} }
def filter(self, context: UserContext) -> Candidates: def filter(self, context: UserContext) -> Candidates:
delimiters: typing.List[str] = self.get_var( # type: ignore delimiters: typing.List[str] = self.get_var('delimiters')
'delimiters')
for candidate, delimiter in [ for candidate, delimiter in [
[x, last_find(x['abbr'], delimiters)] [x, last_find(x['abbr'], delimiters)]
for x in context['candidates'] for x in context['candidates']
@ -30,7 +30,7 @@ class Filter(Base):
not last_find(x['word'], delimiters) and not last_find(x['word'], delimiters) and
last_find(x['abbr'], delimiters)]: last_find(x['abbr'], delimiters)]:
candidate['word'] += delimiter candidate['word'] += delimiter
return context['candidates'] # type: ignore return list(context['candidates'])
def last_find(s: str, needles: typing.List[str]) -> typing.Optional[str]: def last_find(s: str, needles: typing.List[str]) -> typing.Optional[str]:

View File

@ -4,10 +4,11 @@
# License: MIT license # License: MIT license
# ============================================================================ # ============================================================================
from pynvim import Nvim
import re import re
from deoplete.base.filter import Base from deoplete.base.filter import Base
from deoplete.util import Nvim, UserContext, Candidates from deoplete.util import UserContext, Candidates
class Filter(Base): class Filter(Base):
@ -26,4 +27,4 @@ class Filter(Base):
(('abbr' in x and p2.search(x['abbr'])) or (('abbr' in x and p2.search(x['abbr'])) or
('info' in x and p2.search(x['info'])))]: ('info' in x and p2.search(x['info'])))]:
candidate['word'] += '(' candidate['word'] += '('
return context['candidates'] # type: ignore return list(context['candidates'])

View File

@ -4,10 +4,11 @@
# License: MIT license # License: MIT license
# ============================================================================ # ============================================================================
from pynvim import Nvim
import re import re
from deoplete.base.filter import Base from deoplete.base.filter import Base
from deoplete.util import Nvim, UserContext, Candidates from deoplete.util import UserContext, Candidates
class Filter(Base): class Filter(Base):
@ -20,7 +21,7 @@ class Filter(Base):
def filter(self, context: UserContext) -> Candidates: def filter(self, context: UserContext) -> Candidates:
complete_str = context['complete_str'] complete_str = context['complete_str']
if not re.search(r'[A-Z]', complete_str): if not re.search(r'[A-Z]', complete_str):
return context['candidates'] # type: ignore return list(context['candidates'])
complete_lower = complete_str.lower() complete_lower = complete_str.lower()
complete_len = len(complete_str) complete_len = len(complete_str)
@ -29,4 +30,4 @@ class Filter(Base):
if x['word'].lower().startswith(complete_lower)]: if x['word'].lower().startswith(complete_lower)]:
candidate['word'] = complete_str + candidate[ candidate['word'] = complete_str + candidate[
'word'][complete_len:] 'word'][complete_len:]
return context['candidates'] # type: ignore return list(context['candidates'])

View File

@ -4,11 +4,12 @@
# License: MIT license # License: MIT license
# ============================================================================ # ============================================================================
from pynvim import Nvim
import re import re
import typing import typing
from deoplete.base.filter import Base from deoplete.base.filter import Base
from deoplete.util import Nvim, UserContext, Candidates from deoplete.util import UserContext, Candidates
class Filter(Base): class Filter(Base):
@ -20,26 +21,36 @@ class Filter(Base):
def filter(self, context: UserContext) -> Candidates: def filter(self, context: UserContext) -> Candidates:
if not context['next_input']: if not context['next_input']:
return context['candidates'] # type: ignore return list(context['candidates'])
next_input_words = [x for x in re.split( next_input_words = [x for x in re.split(
r'([a-zA-Z_]+|\W)', context['next_input']) if x] r'([a-zA-Z_]+|\W)', context['next_input']) if x]
# Skip parentheses if close parentheses is found after cursor
cur_pos = self.vim.call('getcurpos')[1:3]
check_pairs = [] check_pairs = []
if self.vim.call('searchpair', '(', '', ')', 'bnw'): pair_pos = self.vim.call('searchpairpos', '(', '', ')', 'nW')
check_pairs.append(['(', ')']) if ('(' in context['input'] and cur_pos < pair_pos and
if self.vim.call('searchpair', '[', '', ']', 'bnw'): cur_pos[0] == pair_pos[0]):
check_pairs.append(['[', ']']) check_pairs.append(['(', ')', pair_pos])
pair_pos = self.vim.call('searchpairpos', '[', '', ']', 'nW')
if ('[' in context['input'] and cur_pos < pair_pos and
cur_pos[0] == pair_pos[0]):
check_pairs.append(['[', ']', pair_pos])
for [overlap, candidate, word] in [ for [overlap, candidate, word] in [
[x, y, y['word']] for x, y [x, y, y['word']] for x, y
in [[overlap_length(x['word'], next_input_words), x] in [[overlap_length(x['word'], next_input_words), x]
for x in context['candidates']] if x > 0]: for x in context['candidates']] if x > 0]:
if [x for x in check_pairs if x[0] in word and x[1] in word]: word_end_pos = (context['complete_position'] +
self.vim.call('len', word))
if [x for x in check_pairs
if x[0] in word and x[1] in word[-overlap:] and
word_end_pos >= x[2][1]]:
continue continue
if 'abbr' not in candidate: if 'abbr' not in candidate:
candidate['abbr'] = word candidate['abbr'] = word
candidate['word'] = word[: -overlap] candidate['word'] = word[: -overlap]
return context['candidates'] # type: ignore return list(context['candidates'])
def overlap_length(left: str, next_input_words: typing.List[str]) -> int: def overlap_length(left: str, next_input_words: typing.List[str]) -> int:

View File

@ -4,10 +4,11 @@
# License: MIT license # License: MIT license
# ============================================================================ # ============================================================================
from pynvim import Nvim
import re import re
from deoplete.base.filter import Base from deoplete.base.filter import Base
from deoplete.util import Nvim, UserContext, Candidates from deoplete.util import UserContext, Candidates
class Filter(Base): class Filter(Base):
@ -22,4 +23,4 @@ class Filter(Base):
if '(' in x['word']]: if '(' in x['word']]:
candidate['word'] = re.sub(r'\(.*\)(\$\d+)?', '', candidate['word'] = re.sub(r'\(.*\)(\$\d+)?', '',
candidate['word']) candidate['word'])
return context['candidates'] # type: ignore return list(context['candidates'])

View File

@ -4,11 +4,12 @@
# License: MIT license # License: MIT license
# ============================================================================ # ============================================================================
from pynvim import Nvim
import re import re
import typing import typing
from deoplete.base.filter import Base from deoplete.base.filter import Base
from deoplete.util import Nvim, UserContext, Candidates from deoplete.util import UserContext, Candidates
class Filter(Base): class Filter(Base):
@ -63,10 +64,10 @@ class Filter(Base):
return new_candidates return new_candidates
def filter(self, context: UserContext) -> Candidates: def filter(self, context: UserContext) -> Candidates:
preferred_order_attrs = self.get_var( # type: ignore preferred_order_attrs = self.get_var(
'attrs_order').get(context['filetype'], []) 'attrs_order').get(context['filetype'], [])
if not context['candidates'] or not preferred_order_attrs: if not context['candidates'] or not preferred_order_attrs:
return context['candidates'] # type: ignore return list(context['candidates'])
max_list_size = self.vim.call( max_list_size = self.vim.call(
'deoplete#custom#_get_option', 'max_list' 'deoplete#custom#_get_option', 'max_list'

View File

@ -4,8 +4,10 @@
# License: MIT license # License: MIT license
# ============================================================================ # ============================================================================
from pynvim import Nvim
from deoplete.base.filter import Base from deoplete.base.filter import Base
from deoplete.util import truncate_skipping, Nvim, UserContext, Candidates from deoplete.util import truncate_skipping, UserContext, Candidates
class Filter(Base): class Filter(Base):
@ -18,11 +20,11 @@ class Filter(Base):
def filter(self, context: UserContext) -> Candidates: def filter(self, context: UserContext) -> Candidates:
max_width = context['max_abbr_width'] max_width = context['max_abbr_width']
if max_width <= 0: if max_width <= 0:
return context['candidates'] # type: ignore return list(context['candidates'])
footer_width = max_width / 3 footer_width = max_width / 3
for candidate in context['candidates']: for candidate in context['candidates']:
candidate['abbr'] = truncate_skipping( candidate['abbr'] = truncate_skipping(
candidate.get('abbr', candidate['word']), candidate.get('abbr', candidate['word']),
max_width, '..', footer_width) max_width, '..', footer_width)
return context['candidates'] # type: ignore return list(context['candidates'])

View File

@ -4,8 +4,10 @@
# License: MIT license # License: MIT license
# ============================================================================ # ============================================================================
from pynvim import Nvim
from deoplete.base.filter import Base from deoplete.base.filter import Base
from deoplete.util import truncate_skipping, Nvim, UserContext, Candidates from deoplete.util import truncate_skipping, UserContext, Candidates
class Filter(Base): class Filter(Base):
@ -18,11 +20,11 @@ class Filter(Base):
def filter(self, context: UserContext) -> Candidates: def filter(self, context: UserContext) -> Candidates:
max_width = context['max_info_width'] max_width = context['max_info_width']
if not context['candidates'] or max_width <= 0: if not context['candidates'] or max_width <= 0:
return context['candidates'] # type: ignore return list(context['candidates'])
footer_width = 1 footer_width = 1
for candidate in context['candidates']: for candidate in context['candidates']:
candidate['info'] = truncate_skipping( candidate['info'] = truncate_skipping(
candidate.get('info', ''), candidate.get('info', ''),
max_width, '..', footer_width) max_width, '..', footer_width)
return context['candidates'] # type: ignore return list(context['candidates'])

View File

@ -4,8 +4,10 @@
# License: MIT license # License: MIT license
# ============================================================================ # ============================================================================
from pynvim import Nvim
from deoplete.base.filter import Base from deoplete.base.filter import Base
from deoplete.util import truncate_skipping, Nvim, UserContext, Candidates from deoplete.util import truncate_skipping, UserContext, Candidates
class Filter(Base): class Filter(Base):
@ -19,11 +21,11 @@ class Filter(Base):
max_width = context['max_kind_width'] max_width = context['max_kind_width']
if not context['candidates'] or 'kind' not in context[ if not context['candidates'] or 'kind' not in context[
'candidates'][0] or max_width <= 0: 'candidates'][0] or max_width <= 0:
return context['candidates'] # type: ignore return list(context['candidates'])
footer_width = max_width / 3 footer_width = max_width / 3
for candidate in context['candidates']: for candidate in context['candidates']:
candidate['kind'] = truncate_skipping( candidate['kind'] = truncate_skipping(
candidate.get('kind', ''), candidate.get('kind', ''),
max_width, '..', footer_width) max_width, '..', footer_width)
return context['candidates'] # type: ignore return list(context['candidates'])

View File

@ -4,8 +4,10 @@
# License: MIT license # License: MIT license
# ============================================================================ # ============================================================================
from pynvim import Nvim
from deoplete.base.filter import Base from deoplete.base.filter import Base
from deoplete.util import truncate_skipping, Nvim, UserContext, Candidates from deoplete.util import truncate_skipping, UserContext, Candidates
class Filter(Base): class Filter(Base):
@ -19,11 +21,11 @@ class Filter(Base):
max_width = context['max_menu_width'] max_width = context['max_menu_width']
if not context['candidates'] or 'menu' not in context[ if not context['candidates'] or 'menu' not in context[
'candidates'][0] or max_width <= 0: 'candidates'][0] or max_width <= 0:
return context['candidates'] # type: ignore return list(context['candidates'])
footer_width = max_width / 3 footer_width = max_width / 3
for candidate in context['candidates']: for candidate in context['candidates']:
candidate['menu'] = truncate_skipping( candidate['menu'] = truncate_skipping(
candidate.get('menu', ''), candidate.get('menu', ''),
max_width, '..', footer_width) max_width, '..', footer_width)
return context['candidates'] # type: ignore return list(context['candidates'])

View File

@ -0,0 +1,23 @@
# ============================================================================
# FILE: converter_word_abbr.py
# AUTHOR: Shougo Matsushita <Shougo.Matsu at gmail.com>
# License: MIT license
# ============================================================================
from pynvim import Nvim
from deoplete.base.filter import Base
from deoplete.util import UserContext, Candidates
class Filter(Base):
def __init__(self, vim: Nvim) -> None:
super().__init__(vim)
self.name = 'converter_word_abbr'
self.description = 'word abbr converter'
def filter(self, context: UserContext) -> Candidates:
for candidate in context['candidates']:
candidate['abbr'] = candidate['word']
return list(context['candidates'])

View File

@ -4,13 +4,14 @@
# License: MIT license # License: MIT license
# ============================================================================ # ============================================================================
import os from pathlib import Path
from pynvim import Nvim
import sys import sys
import typing import typing
from deoplete.base.filter import Base from deoplete.base.filter import Base
from deoplete.util import error, globruntime from deoplete.util import error, globruntime
from deoplete.util import Nvim, UserContext, Candidates from deoplete.util import UserContext, Candidates
class Filter(Base): class Filter(Base):
@ -26,7 +27,7 @@ class Filter(Base):
def filter(self, context: UserContext) -> Candidates: def filter(self, context: UserContext) -> Candidates:
if (not context['candidates'] or not context['input'] if (not context['candidates'] or not context['input']
or self._cpsm is False): or self._cpsm is False):
return context['candidates'] # type: ignore return list(context['candidates'])
if self._cpsm is None: if self._cpsm is None:
errmsg = self._init_cpsm(context) errmsg = self._init_cpsm(context)
@ -49,7 +50,7 @@ class Filter(Base):
found = globruntime(self.vim.options['runtimepath'], fname) found = globruntime(self.vim.options['runtimepath'], fname)
errmsg = '' errmsg = ''
if found: if found:
sys.path.insert(0, os.path.dirname(found[0])) sys.path.insert(0, str(Path(found[0]).parent))
try: try:
import cpsm_py import cpsm_py
except ImportError as exc: except ImportError as exc:

View File

@ -4,9 +4,11 @@
# License: MIT license # License: MIT license
# ============================================================================ # ============================================================================
from pynvim import Nvim
import re import re
from deoplete.base.filter import Base from deoplete.base.filter import Base
from deoplete.util import fuzzy_escape, Nvim, UserContext, Candidates from deoplete.util import fuzzy_escape, UserContext, Candidates
class Filter(Base): class Filter(Base):

View File

@ -4,12 +4,12 @@
# License: MIT license # License: MIT license
# ============================================================================ # ============================================================================
from pynvim import Nvim
import re import re
from deoplete.base.filter import Base from deoplete.base.filter import Base
from deoplete.util import ( from deoplete.util import binary_search_begin, binary_search_end
fuzzy_escape, binary_search_begin, binary_search_end) from deoplete.util import fuzzy_escape, UserContext, Candidates
from deoplete.util import Nvim, UserContext, Candidates
class Filter(Base): class Filter(Base):
@ -25,7 +25,7 @@ class Filter(Base):
if context['ignorecase']: if context['ignorecase']:
complete_str = complete_str.lower() complete_str = complete_str.lower()
if not complete_str: if not complete_str:
return context['candidates'] # type: ignore return list(context['candidates'])
if context['is_sorted']: if context['is_sorted']:
begin = binary_search_begin( begin = binary_search_begin(

View File

@ -4,9 +4,11 @@
# License: MIT license # License: MIT license
# ============================================================================ # ============================================================================
from pynvim import Nvim
from deoplete.base.filter import Base from deoplete.base.filter import Base
from deoplete.util import binary_search_begin, binary_search_end from deoplete.util import binary_search_begin, binary_search_end
from deoplete.util import Nvim, UserContext, Candidates from deoplete.util import UserContext, Candidates
class Filter(Base): class Filter(Base):
@ -32,7 +34,7 @@ class Filter(Base):
candidates = context['candidates'][begin:end+1] candidates = context['candidates'][begin:end+1]
if context['ignorecase']: if context['ignorecase']:
return candidates # type: ignore return list(candidates)
else: else:
candidates = context['candidates'] candidates = context['candidates']

View File

@ -4,8 +4,10 @@
# License: MIT license # License: MIT license
# ============================================================================ # ============================================================================
from pynvim import Nvim
from deoplete.base.filter import Base from deoplete.base.filter import Base
from deoplete.util import Nvim, UserContext, Candidates from deoplete.util import UserContext, Candidates
class Filter(Base): class Filter(Base):

View File

@ -0,0 +1,28 @@
# ============================================================================
# FILE: matcher_matchfuzzy.py
# AUTHOR: Shougo Matsushita <Shougo.Matsu at gmail.com>
# License: MIT license
# ============================================================================
from pynvim import Nvim
from deoplete.base.filter import Base
from deoplete.util import UserContext, Candidates
class Filter(Base):
def __init__(self, vim: Nvim) -> None:
super().__init__(vim)
self.name = 'matcher_matchfuzzy'
self.description = 'matchfuzzy() matcher'
def filter(self, context: UserContext) -> Candidates:
if not self.vim.call('exists', '*matchfuzzy'):
return []
return list(self.vim.call(
'matchfuzzy', context['candidates'],
context['complete_str'], {'key': 'word'}
))

View File

@ -4,12 +4,13 @@
# License: MIT license # License: MIT license
# ============================================================================ # ============================================================================
from pynvim import Nvim
import re import re
import typing import typing
from deoplete.base.filter import Base from deoplete.base.filter import Base
from deoplete.util import getlines from deoplete.util import getlines
from deoplete.util import Nvim, UserContext, Candidates, Candidate from deoplete.util import UserContext, Candidates, Candidate
LINES_MAX = 150 LINES_MAX = 150
@ -39,11 +40,15 @@ class Filter(Base):
def filter(self, context: UserContext) -> Candidates: def filter(self, context: UserContext) -> Candidates:
complete_str = context['complete_str'].lower() complete_str = context['complete_str'].lower()
linenr = context['position'][1] linenr = context['position'][1]
recently_used = self.vim.vars['deoplete#_recently_used']
def compare(x: Candidate) -> int: def compare(x: Candidate) -> int:
word = x['word'] word = x['word']
matched = int(complete_str in word.lower()) lower = x['word'].lower()
matched = int(complete_str in lower)
score = -matched * 40 score = -matched * 40
if [x for x in recently_used if lower.startswith(x)]:
score -= 1000
if word in self._cache: if word in self._cache:
mru = min([abs(x - linenr) for x in self._cache[word]]) mru = min([abs(x - linenr) for x in self._cache[word]])
mru -= LINES_MAX mru -= LINES_MAX

View File

@ -4,8 +4,10 @@
# License: MIT license # License: MIT license
# ============================================================================ # ============================================================================
from pynvim import Nvim
from deoplete.base.filter import Base from deoplete.base.filter import Base
from deoplete.util import Nvim, UserContext, Candidates from deoplete.util import UserContext, Candidates
class Filter(Base): class Filter(Base):
@ -18,4 +20,4 @@ class Filter(Base):
def filter(self, context: UserContext) -> Candidates: def filter(self, context: UserContext) -> Candidates:
return sorted(context['candidates'], return sorted(context['candidates'],
key=lambda x: x['word'].swapcase()) key=lambda x: str(x['word'].swapcase()))

View File

@ -8,10 +8,9 @@ import time
import logging import logging
import typing import typing
from deoplete.util import Nvim
from functools import wraps
from collections import defaultdict from collections import defaultdict
from functools import wraps
from pynvim import Nvim
log_format = '%(asctime)s %(levelname)-8s [%(process)d] (%(name)s) %(message)s' log_format = '%(asctime)s %(levelname)-8s [%(process)d] (%(name)s) %(message)s'
log_message_cooldown = 0.5 log_message_cooldown = 0.5
@ -50,19 +49,11 @@ def setup(vim: Nvim, level: str, output_file: str = '') -> None:
level = 'DEBUG' level = 'DEBUG'
root.setLevel(getattr(logging, level)) root.setLevel(getattr(logging, level))
try:
import pkg_resources
pynvim_version = pkg_resources.get_distribution('pynvim').version
except Exception:
pynvim_version = 'unknown'
log = getLogger('logging') log = getLogger('logging')
log.info('--- Deoplete Log Start ---') log.info('--- Deoplete Log Start ---')
log.info('%s, Python %s, pynvim %s', log.info('%s, Python %s',
vim.call('deoplete#util#neovim_version'), vim.call('deoplete#util#neovim_version'),
'.'.join(map(str, sys.version_info[:3])), '.'.join(map(str, sys.version_info[:3])))
pynvim_version)
if 'deoplete#_logging_notified' not in vim.vars: if 'deoplete#_logging_notified' not in vim.vars:
vim.vars['deoplete#_logging_notified'] = 1 vim.vars['deoplete#_logging_notified'] = 1

View File

@ -4,20 +4,20 @@
# License: MIT license # License: MIT license
# ============================================================================ # ============================================================================
import time
import os
import msgpack
import subprocess
import sys
import typing
from abc import abstractmethod from abc import abstractmethod
from functools import partial from functools import partial
from pathlib import Path from pathlib import Path
from pynvim import Nvim
from queue import Queue from queue import Queue
import msgpack
import subprocess
import sys
import time
import typing
from deoplete import logger from deoplete import logger
from deoplete.process import Process from deoplete.process import Process
from deoplete.util import error_tb, error, Nvim from deoplete.util import error_tb, error
UserContext = typing.Dict[str, typing.Any] UserContext = typing.Dict[str, typing.Any]
@ -91,7 +91,7 @@ class AsyncParent(_Parent):
Taken from jedi.api.environment._try_get_same_env. Taken from jedi.api.environment._try_get_same_env.
""" """
exe = sys.executable exe = sys.executable
if not os.path.basename(exe).lower().startswith('python'): if not Path(exe).name.lower().startswith('python'):
checks: typing.Tuple[typing.Any, ...] checks: typing.Tuple[typing.Any, ...]
if sys.platform == 'win32': if sys.platform == 'win32':
checks = (r'Scripts\python.exe', 'python.exe') checks = (r'Scripts\python.exe', 'python.exe')
@ -103,8 +103,8 @@ class AsyncParent(_Parent):
'bin/python', 'bin/python',
) )
for check in checks: for check in checks:
guess = os.path.join(sys.exec_prefix, check) guess = Path(sys.exec_prefix).joinpath(check)
if os.path.isfile(str(guess)): if guess.is_file():
return str(guess) return str(guess)
if 'python3_host_prog' not in self._vim.vars: if 'python3_host_prog' not in self._vim.vars:
return 'python3' return 'python3'

View File

@ -4,11 +4,12 @@
# License: MIT license # License: MIT license
# ============================================================================ # ============================================================================
from pynvim import Nvim
import re import re
from deoplete.base.source import Base from deoplete.base.source import Base
from deoplete.util import parse_buffer_pattern, getlines from deoplete.util import parse_buffer_pattern, getlines
from deoplete.util import Nvim, UserContext, Candidates from deoplete.util import UserContext, Candidates
class Source(Base): class Source(Base):

View File

@ -4,9 +4,9 @@
# License: MIT license # License: MIT license
# ============================================================================ # ============================================================================
# For backward compatibility from pynvim import Nvim
from deoplete.base.source import Base as _Base from deoplete.base.source import Base as _Base
from deoplete.util import Nvim
class Base(_Base): class Base(_Base):

View File

@ -4,11 +4,12 @@
# License: MIT license # License: MIT license
# ============================================================================ # ============================================================================
from pynvim import Nvim
import typing import typing
from deoplete.base.source import Base from deoplete.base.source import Base
from deoplete.util import parse_buffer_pattern, getlines from deoplete.util import parse_buffer_pattern, getlines
from deoplete.util import Nvim, UserContext, Candidates from deoplete.util import UserContext, Candidates
class Source(Base): class Source(Base):

View File

@ -5,13 +5,13 @@
# License: MIT license # License: MIT license
# ============================================================================ # ============================================================================
import os from pathlib import Path
from pynvim import Nvim
import re import re
import typing import typing
from os.path import exists, dirname
from deoplete.base.source import Base from deoplete.base.source import Base
from deoplete.util import expand, Nvim, UserContext, Candidates from deoplete.util import expand, exists_path, UserContext, Candidates
class Source(Base): class Source(Base):
@ -26,6 +26,7 @@ class Source(Base):
self.events: typing.List[str] = ['InsertEnter'] self.events: typing.List[str] = ['InsertEnter']
self.vars = { self.vars = {
'enable_buffer_path': True, 'enable_buffer_path': True,
'enable_slash_completion': False,
'force_completion_length': -1, 'force_completion_length': -1,
} }
@ -39,7 +40,7 @@ class Source(Base):
def get_complete_position(self, context: UserContext) -> int: def get_complete_position(self, context: UserContext) -> int:
pos = int(context['input'].rfind('/')) pos = int(context['input'].rfind('/'))
force_completion_length = int( force_completion_length = int(
self.get_var('force_completion_length')) # type: ignore self.get_var('force_completion_length'))
if pos < 0 and force_completion_length >= 0: if pos < 0 and force_completion_length >= 0:
fmt = '[a-zA-Z0-9.-]{{{}}}$'.format(force_completion_length) fmt = '[a-zA-Z0-9.-]{{{}}}$'.format(force_completion_length)
m = re.search(fmt, context['input']) m = re.search(fmt, context['input'])
@ -55,20 +56,38 @@ class Source(Base):
if context['input'].rfind('/') >= 0 if context['input'].rfind('/') >= 0
else './') else './')
p = self._longest_path_that_exists(context, input_str) # Note: context['bufpath'] will be empty if not exists file
if not p or p == '/' or re.search('//+$', p): bufname = context['bufname']
bufpath = (bufname if Path(bufname).is_absolute()
else str(Path(context['cwd']).joinpath(bufname)))
buftype = self.vim.call('getbufvar', '%', '&buftype')
if not bufname or 'nofile' in buftype or not self.get_var(
'enable_buffer_path'):
bufpath = ''
p = self._longest_path_that_exists(context, input_str, bufpath)
slash_completion = bool(self.get_var('enable_slash_completion'))
if not p or re.search('//+$', p) or (
p == '/' and not slash_completion):
return [] return []
complete_str = self._substitute_path(context, dirname(p) + '/')
if not os.path.isdir(complete_str): p = expand(p)
if p[-1] != '/':
p += '/'
complete_str = self._substitute_path(context, p, bufpath)
if not Path(complete_str).is_dir():
return [] return []
hidden = context['complete_str'].find('.') == 0 hidden = context['complete_str'].find('.') == 0
contents: typing.List[typing.Any] = [[], []] contents: typing.List[typing.Any] = [[], []]
try: try:
for item in sorted(os.listdir(complete_str), key=str.lower): for item in sorted([str(x.name) for x
in Path(complete_str).iterdir()],
key=str.lower):
if not hidden and item[0] == '.': if not hidden and item[0] == '.':
continue continue
contents[not os.path.isdir(complete_str + item)].append(item) is_dir = not Path(complete_str + '/' + item).is_dir()
except PermissionError: contents[is_dir].append(item)
except (PermissionError, FileNotFoundError):
pass pass
dirs, files = contents dirs, files = contents
@ -76,25 +95,32 @@ class Source(Base):
] + [{'word': x} for x in files] ] + [{'word': x} for x in files]
def _longest_path_that_exists(self, context: UserContext, def _longest_path_that_exists(self, context: UserContext,
input_str: str) -> str: input_str: str, bufpath: str) -> str:
input_str = re.sub(r'[^/]*$', '', input_str) input_str = re.sub(r'[^/]*$', '', input_str)
data = re.split(r'((?:%s+|(?:(?<![\w\s/\.])(?:~|\.{1,2})?/)+))' % data = [x for x in re.split(
self._isfname, input_str) r'((?:%s+|(?:(?<![\w\s/\.])(?:~|\.{1,2})?/)+))' %
self._isfname, input_str)]
data = [''.join(data[i:]) for i in range(len(data))] data = [''.join(data[i:]) for i in range(len(data))]
existing_paths = sorted(filter(lambda x: exists( existing_paths = sorted(filter(
dirname(self._substitute_path(context, x))), data)) lambda x: exists_path(self._substitute_path(
context, x, bufpath)), data))
return existing_paths[-1] if existing_paths else '' return existing_paths[-1] if existing_paths else ''
def _substitute_path(self, context: UserContext, path: str) -> str: def _substitute_path(self, context: UserContext,
path: str, bufpath: str) -> str:
m = re.match(r'(\.{1,2})/+', path) m = re.match(r'(\.{1,2})/+', path)
if m: if not m:
if self.get_var('enable_buffer_path') and context['bufpath']:
base = context['bufpath']
else:
base = os.path.join(context['cwd'], 'x')
for _ in m.group(1):
base = dirname(base)
return os.path.abspath(os.path.join(
base, path[len(m.group(0)):])) + '/'
return expand(path) return expand(path)
if bufpath:
base = str(Path(bufpath).parent)
else:
base = context['cwd']
if m.group(1) == '..':
base = str(Path(base).parent)
rest = path[len(m.group(0)):]
if rest:
return str(Path(base).joinpath(rest)) + '/'
else:
return base

View File

@ -4,13 +4,14 @@
# License: MIT license # License: MIT license
# ============================================================================ # ============================================================================
from pynvim import Nvim
import re import re
import typing import typing
from deoplete.base.source import Base from deoplete.base.source import Base
from deoplete.util import ( from deoplete.util import (
convert2list, parse_buffer_pattern, set_pattern, getlines) convert2list, parse_buffer_pattern, set_pattern, getlines)
from deoplete.util import Nvim, UserContext, Candidates from deoplete.util import UserContext, Candidates
class Source(Base): class Source(Base):

View File

@ -4,13 +4,14 @@
# License: MIT license # License: MIT license
# ============================================================================ # ============================================================================
from pynvim import Nvim
import re import re
import typing import typing
from deoplete.base.source import Base from deoplete.base.source import Base
from deoplete.util import ( from deoplete.util import (
convert2list, set_pattern, convert2candidates) convert2list, set_pattern, convert2candidates)
from deoplete.util import Nvim, UserContext, Candidates from deoplete.util import UserContext, Candidates
class Source(Base): class Source(Base):
@ -44,6 +45,8 @@ class Source(Base):
def _get_complete_position(self, context: UserContext, def _get_complete_position(self, context: UserContext,
current_ft: str, filetype: str) -> int: current_ft: str, filetype: str) -> int:
complete_pos = -1
for omnifunc in convert2list( for omnifunc in convert2list(
self.get_filetype_var(filetype, 'functions')): self.get_filetype_var(filetype, 'functions')):
if omnifunc == '' and (filetype == current_ft or if omnifunc == '' and (filetype == current_ft or
@ -69,15 +72,16 @@ class Source(Base):
'rubycomplete#Complete', 'rubycomplete#Complete',
'phpcomplete#CompletePHP']: 'phpcomplete#CompletePHP']:
# In the blacklist # In the blacklist
return -1 continue
try: try:
complete_pos = int(self.vim.call(self._omnifunc, 1, '')) complete_pos = int(self.vim.call(self._omnifunc, 1, ''))
except Exception: except Exception:
self.print_error('Error occurred calling omnifunction: ' + self.print_error('Error occurred calling omnifunction: ' +
self._omnifunc) self._omnifunc)
return -1 return -1
if complete_pos >= 0:
break
return complete_pos return complete_pos
return -1
def gather_candidates(self, context: UserContext) -> Candidates: def gather_candidates(self, context: UserContext) -> Candidates:
try: try:
@ -95,4 +99,4 @@ class Source(Base):
candidate['dup'] = 1 candidate['dup'] = 1
candidate['equal'] = 1 candidate['equal'] = 1
return candidates # type: ignore return list(candidates)

View File

@ -4,22 +4,18 @@
# License: MIT license # License: MIT license
# ============================================================================ # ============================================================================
import os from os.path import expandvars
import re from pathlib import Path
import sys from pynvim import Nvim
from pynvim.api import Buffer
import glob import glob
import importlib.util import importlib.util
import re
import sys
import traceback import traceback
import typing import typing
import unicodedata import unicodedata
if importlib.util.find_spec('pynvim'):
from pynvim import Nvim
from pynvim.api import Buffer
else:
from neovim import Nvim
from neovim.api import Buffer
UserContext = typing.Dict[str, typing.Any] UserContext = typing.Dict[str, typing.Any]
Candidate = typing.Dict[str, typing.Any] Candidate = typing.Dict[str, typing.Any]
Candidates = typing.List[Candidate] Candidates = typing.List[Candidate]
@ -61,17 +57,19 @@ def import_plugin(path: str, source: str,
If the class exists, add its directory to sys.path. If the class exists, add its directory to sys.path.
""" """
name = os.path.splitext(os.path.basename(path))[0] name = str(Path(path).name)[: -3]
module_name = 'deoplete.%s.%s' % (source, name) module_name = 'deoplete.%s.%s' % (source, name)
spec = importlib.util.spec_from_file_location(module_name, path) spec = importlib.util.spec_from_file_location(module_name, path)
if not spec:
return None
module = importlib.util.module_from_spec(spec) module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module) # type: ignore spec.loader.exec_module(module) # type: ignore
cls = getattr(module, classname, None) cls = getattr(module, classname, None)
if not cls: if not cls:
return None return None
dirname = os.path.dirname(path) dirname = str(Path(path).parent)
if dirname not in sys.path: if dirname not in sys.path:
sys.path.insert(0, dirname) sys.path.insert(0, dirname)
return cls return cls
@ -96,7 +94,7 @@ def error_tb(vim: Nvim, msg: str) -> None:
t, v, tb = sys.exc_info() t, v, tb = sys.exc_info()
if t and v and tb: if t and v and tb:
lines += traceback.format_exc().splitlines() lines += traceback.format_exc().splitlines()
lines += ['%s. Use :messages / see above for error details.' % msg] lines += ['%s Use :messages / see above for error details.' % msg]
if hasattr(vim, 'err_write'): if hasattr(vim, 'err_write'):
vim.err_write('[deoplete] %s\n' % '\n'.join(lines)) vim.err_write('[deoplete] %s\n' % '\n'.join(lines))
else: else:
@ -141,8 +139,8 @@ def get_custom(custom: typing.Dict[str, typing.Any],
return default return default
def get_syn_names(vim: Nvim) -> str: def get_syn_names(vim: Nvim) -> typing.List[str]:
return str(vim.call('deoplete#util#get_syn_names')) return list(vim.call('deoplete#util#get_syn_names'))
def parse_file_pattern(f: typing.Iterable[str], def parse_file_pattern(f: typing.Iterable[str],
@ -169,8 +167,8 @@ def fuzzy_escape(string: str, camelcase: bool) -> str:
def load_external_module(base: str, module: str) -> None: def load_external_module(base: str, module: str) -> None:
current = os.path.dirname(os.path.abspath(base)) current = Path(base).parent.resolve()
module_dir = os.path.join(os.path.dirname(current), module) module_dir = str(current.parent.joinpath(module))
if module_dir not in sys.path: if module_dir not in sys.path:
sys.path.insert(0, module_dir) sys.path.insert(0, module_dir)
@ -219,7 +217,20 @@ def charwidth(c: str) -> int:
def expand(path: str) -> str: def expand(path: str) -> str:
return os.path.expanduser(os.path.expandvars(path)) if path.startswith('~'):
try:
path = str(Path(path).expanduser())
except Exception:
pass
return expandvars(path)
def exists_path(path: str) -> bool:
try:
return Path(path).exists()
except Exception:
pass
return False
def getlines(vim: Nvim, start: int = 1, def getlines(vim: Nvim, start: int = 1,
@ -230,7 +241,9 @@ def getlines(vim: Nvim, start: int = 1,
lines: typing.List[str] = [] lines: typing.List[str] = []
current = start current = start
while current <= int(end): while current <= int(end):
lines += vim.call('getline', current, current + max_len) # Skip very long lines
lines += [x for x in vim.call('getline', current, current + max_len)
if len(x) < 300]
current += max_len + 1 current += max_len + 1
return lines return lines

View File

@ -1,5 +1,5 @@
import os from pathlib import Path
import sys import sys
BASE_DIR = os.path.dirname(os.path.dirname(__file__)) BASE_DIR = Path(__file__).parent.parent
sys.path.insert(0, os.path.join(BASE_DIR, 'rplugin/python3')) sys.path.insert(0, str(Path(BASE_DIR).joinpath('rplugin/python3')))