Auto merge of #2657 - micbou:completion-system-rewrite, r=bstaletic
[READY] Rewrite completion system There is a number of issues with the current completion system: - UI is blocked until the completions are returned by the server, the request timed out, or a key is pressed by the user. This leads to a rough experience when completions take too much time: cursor disappearing and timeout errors (see https://github.com/Valloric/YouCompleteMe/issues/2192 and https://github.com/Valloric/YouCompleteMe/issues/2574). Users even [increase the timeout by manually editing the `completion_request.py` file](https://github.com/Valloric/YouCompleteMe/blob/master/python/ycm/client/completion_request.py#L30) to avoid these errors, which exacerbate the issue and, in some cases, make the plugin unusable (see https://github.com/Valloric/YouCompleteMe/issues/2574). - no fuzzy matching from omnifunc when forcing semantic completion. See https://github.com/Valloric/YouCompleteMe/issues/961; - no fuzzy matching when deleting characters. Vim filtering is used instead: ![completion-bug-deleting-characters](https://cloud.githubusercontent.com/assets/10026824/26276156/f298c6de-3d71-11e7-92da-d22186239c27.gif) Neovim and MacVim are not affected. - completion menu disappears after deleting a character and inserting one: ![completion-bug-reinserting-character](https://cloud.githubusercontent.com/assets/10026824/26276192/b3ed0f7a-3d72-11e7-8c64-523a0a59cbdc.gif) Neovim and MacVim are not affected. - ignore the start column returned by the server. See PR https://github.com/Valloric/YouCompleteMe/pull/2489. - subject to flickers. This one depends a lot on the version of Vim. Completion is almost flicker-free in Neovim and MacVim. Not too bad in console Vim (except in fast terminal like [alacritty](https://github.com/jwilm/alacritty)). Awful in gVim GTK2 (a bit better on GTK3) and gVim on Windows. This PR is an attempt at fixing all of these issues while reducing flickers as best as possible (due to how completion works in Vim, a flicker-free experience is impossible to achieve). Here's how: - poll for completions using a timer and call `completefunc` once the completions are ready. Use the start column returned by the server in `completefunc`; - immediately display the last completions on the `TextChangedI` event to prevent the popup menu disappearing while waiting for the completions. This reduces flickering; - use the `InsertCharPre` event to close the completion menu just before inserting a character. This way the `TextChangedI` event is triggered when the character is inserted (this event is not fired when the completion menu is visible). This replaces the `refresh` option set to `always` in `completefunc` and the `s:cursor_moved` hack; - remap the backspace key to close the completion menu when deleting a character and thus triggering the `TextChangedI` event; - send a request with `force_semantic` set to `True` when forcing semantic completion instead of calling the omnifunc. This fixes the issue where there is no fuzzy matching for custom omnifuncs. Here's a demo where I added a spin animation on the command line while loading the completions to show that it doesn't block the Vim UI: ![async-completion-for-real](https://cloud.githubusercontent.com/assets/10026824/26277295/0f16a718-3d86-11e7-90f3-8a56bbf53f9f.gif) Fixes #961. Fixes #1282. Fixes #1881. Fixes #2192. Fixes #2500. Fixes #2574. <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/valloric/youcompleteme/2657) <!-- Reviewable:end -->
This commit is contained in:
commit
6ba6b56fb7
13
README.md
13
README.md
@ -2313,6 +2313,19 @@ Default: `['<S-TAB>', '<Up>']`
|
|||||||
let g:ycm_key_list_previous_completion = ['<S-TAB>', '<Up>']
|
let g:ycm_key_list_previous_completion = ['<S-TAB>', '<Up>']
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### The `g:ycm_key_list_stop_completion` option
|
||||||
|
|
||||||
|
This option controls the key mappings used to close the completion menu. This is
|
||||||
|
useful when the menu is blocking the view, when you need to insert the `<TAB>`
|
||||||
|
character, or when you want to expand a snippet from [UltiSnips][] and navigate
|
||||||
|
through it.
|
||||||
|
|
||||||
|
Default: `['<C-y>']`
|
||||||
|
|
||||||
|
```viml
|
||||||
|
let g:ycm_key_list_stop_completion = ['<C-y>']
|
||||||
|
```
|
||||||
|
|
||||||
### The `g:ycm_key_invoke_completion` option
|
### The `g:ycm_key_invoke_completion` option
|
||||||
|
|
||||||
This option controls the key mapping used to invoke the completion menu for
|
This option controls the key mapping used to invoke the completion menu for
|
||||||
|
@ -21,12 +21,19 @@ set cpo&vim
|
|||||||
|
|
||||||
" This needs to be called outside of a function
|
" This needs to be called outside of a function
|
||||||
let s:script_folder_path = escape( expand( '<sfile>:p:h' ), '\' )
|
let s:script_folder_path = escape( expand( '<sfile>:p:h' ), '\' )
|
||||||
let s:omnifunc_mode = 0
|
let s:force_semantic = 0
|
||||||
|
let s:completion_stopped = 0
|
||||||
let s:old_cursor_position = []
|
let s:default_completion = {
|
||||||
let s:cursor_moved = 0
|
\ 'start_column': -1,
|
||||||
|
\ 'candidates': []
|
||||||
|
\ }
|
||||||
|
let s:completion = s:default_completion
|
||||||
let s:previous_allowed_buffer_number = 0
|
let s:previous_allowed_buffer_number = 0
|
||||||
let s:pollers = {
|
let s:pollers = {
|
||||||
|
\ 'completion': {
|
||||||
|
\ 'id': -1,
|
||||||
|
\ 'wait_milliseconds': 10
|
||||||
|
\ },
|
||||||
\ 'file_parse_response': {
|
\ 'file_parse_response': {
|
||||||
\ 'id': -1,
|
\ 'id': -1,
|
||||||
\ 'wait_milliseconds': 100
|
\ 'wait_milliseconds': 100
|
||||||
@ -100,7 +107,6 @@ function! youcompleteme#Enable()
|
|||||||
autocmd BufEnter * call s:OnBufferEnter()
|
autocmd BufEnter * call s:OnBufferEnter()
|
||||||
autocmd BufUnload * call s:OnBufferUnload()
|
autocmd BufUnload * call s:OnBufferUnload()
|
||||||
autocmd InsertLeave * call s:OnInsertLeave()
|
autocmd InsertLeave * call s:OnInsertLeave()
|
||||||
autocmd InsertEnter * call s:OnInsertEnter()
|
|
||||||
autocmd VimLeave * call s:OnVimLeave()
|
autocmd VimLeave * call s:OnVimLeave()
|
||||||
autocmd CompleteDone * call s:OnCompleteDone()
|
autocmd CompleteDone * call s:OnCompleteDone()
|
||||||
augroup END
|
augroup END
|
||||||
@ -119,6 +125,10 @@ function! youcompleteme#EnableCursorMovedAutocommands()
|
|||||||
autocmd CursorMoved * call s:OnCursorMovedNormalMode()
|
autocmd CursorMoved * call s:OnCursorMovedNormalMode()
|
||||||
autocmd TextChanged * call s:OnTextChangedNormalMode()
|
autocmd TextChanged * call s:OnTextChangedNormalMode()
|
||||||
autocmd TextChangedI * call s:OnTextChangedInsertMode()
|
autocmd TextChangedI * call s:OnTextChangedInsertMode()
|
||||||
|
" The TextChangedI event is not triggered when inserting a character while
|
||||||
|
" the completion menu is open. We handle this by closing the completion menu
|
||||||
|
" just before inserting a character.
|
||||||
|
autocmd InsertCharPre * call s:OnInsertChar()
|
||||||
augroup END
|
augroup END
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
@ -206,13 +216,19 @@ function! s:SetUpKeyMappings()
|
|||||||
\ ' pumvisible() ? "\<C-n>" : "\' . key .'"'
|
\ ' pumvisible() ? "\<C-n>" : "\' . key .'"'
|
||||||
endfor
|
endfor
|
||||||
|
|
||||||
|
|
||||||
for key in g:ycm_key_list_previous_completion
|
for key in g:ycm_key_list_previous_completion
|
||||||
" This selects the previous candidate for shift-tab (default)
|
" This selects the previous candidate for shift-tab (default)
|
||||||
exe 'inoremap <expr>' . key .
|
exe 'inoremap <expr>' . key .
|
||||||
\ ' pumvisible() ? "\<C-p>" : "\' . key .'"'
|
\ ' pumvisible() ? "\<C-p>" : "\' . key .'"'
|
||||||
endfor
|
endfor
|
||||||
|
|
||||||
|
for key in g:ycm_key_list_stop_completion
|
||||||
|
" When selecting a candidate and closing the completion menu with the <C-y>
|
||||||
|
" key, the menu will automatically be reopened because of the TextChangedI
|
||||||
|
" event. We define a command to prevent that.
|
||||||
|
exe 'inoremap <expr>' . key . ' <SID>StopCompletion( "\' . key . '" )'
|
||||||
|
endfor
|
||||||
|
|
||||||
if !empty( g:ycm_key_invoke_completion )
|
if !empty( g:ycm_key_invoke_completion )
|
||||||
let invoke_key = g:ycm_key_invoke_completion
|
let invoke_key = g:ycm_key_invoke_completion
|
||||||
|
|
||||||
@ -221,15 +237,22 @@ function! s:SetUpKeyMappings()
|
|||||||
imap <Nul> <C-Space>
|
imap <Nul> <C-Space>
|
||||||
endif
|
endif
|
||||||
|
|
||||||
" <c-x><c-o> trigger omni completion, <c-p> deselects the first completion
|
silent! exe 'inoremap <unique> <silent> ' . invoke_key .
|
||||||
" candidate that vim selects by default
|
\ ' <C-R>=<SID>InvokeSemanticCompletion()<CR>'
|
||||||
silent! exe 'inoremap <unique> ' . invoke_key . ' <C-X><C-O><C-P>'
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if !empty( g:ycm_key_detailed_diagnostics )
|
if !empty( g:ycm_key_detailed_diagnostics )
|
||||||
silent! exe 'nnoremap <unique> ' . g:ycm_key_detailed_diagnostics .
|
silent! exe 'nnoremap <unique> ' . g:ycm_key_detailed_diagnostics .
|
||||||
\ ' :YcmShowDetailedDiagnostic<cr>'
|
\ ' :YcmShowDetailedDiagnostic<CR>'
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
" The TextChangedI event is not triggered when deleting a character while the
|
||||||
|
" completion menu is open. We handle this by closing the completion menu on
|
||||||
|
" the keys that delete a character in insert mode.
|
||||||
|
for key in [ "<BS>", "<C-h>" ]
|
||||||
|
silent! exe 'inoremap <unique> <expr> ' . key .
|
||||||
|
\ ' <SID>OnDeleteChar( "\' . key . '" )'
|
||||||
|
endfor
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
@ -406,6 +429,11 @@ function! s:SetUpCompleteopt()
|
|||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
|
function! s:SetCompleteFunc()
|
||||||
|
let &completefunc = 'youcompleteme#CompleteFunc'
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
function! s:OnVimLeave()
|
function! s:OnVimLeave()
|
||||||
exec s:python_command "ycm_state.OnVimLeave()"
|
exec s:python_command "ycm_state.OnVimLeave()"
|
||||||
endfunction
|
endfunction
|
||||||
@ -423,7 +451,6 @@ function! s:OnFileTypeSet()
|
|||||||
|
|
||||||
call s:SetUpCompleteopt()
|
call s:SetUpCompleteopt()
|
||||||
call s:SetCompleteFunc()
|
call s:SetCompleteFunc()
|
||||||
call s:SetOmnicompleteFunc()
|
|
||||||
|
|
||||||
exec s:python_command "ycm_state.OnBufferVisit()"
|
exec s:python_command "ycm_state.OnBufferVisit()"
|
||||||
call s:OnFileReadyToParse( 1 )
|
call s:OnFileReadyToParse( 1 )
|
||||||
@ -437,7 +464,6 @@ function! s:OnBufferEnter()
|
|||||||
|
|
||||||
call s:SetUpCompleteopt()
|
call s:SetUpCompleteopt()
|
||||||
call s:SetCompleteFunc()
|
call s:SetCompleteFunc()
|
||||||
call s:SetOmnicompleteFunc()
|
|
||||||
|
|
||||||
exec s:python_command "ycm_state.OnBufferVisit()"
|
exec s:python_command "ycm_state.OnBufferVisit()"
|
||||||
" Last parse may be outdated because of changes from other buffers. Force a
|
" Last parse may be outdated because of changes from other buffers. Force a
|
||||||
@ -502,24 +528,30 @@ function! s:PollFileParseResponse( ... )
|
|||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
function! s:SetCompleteFunc()
|
function! s:OnInsertChar()
|
||||||
let &completefunc = 'youcompleteme#Complete'
|
call timer_stop( s:pollers.completion.id )
|
||||||
let &l:completefunc = 'youcompleteme#Complete'
|
if pumvisible()
|
||||||
|
call feedkeys( "\<C-e>", 'n' )
|
||||||
|
endif
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
function! s:SetOmnicompleteFunc()
|
function! s:OnDeleteChar( key )
|
||||||
if s:Pyeval( 'ycm_state.NativeFiletypeCompletionUsable()' )
|
call timer_stop( s:pollers.completion.id )
|
||||||
let &omnifunc = 'youcompleteme#OmniComplete'
|
if pumvisible()
|
||||||
let &l:omnifunc = 'youcompleteme#OmniComplete'
|
return "\<C-y>" . a:key
|
||||||
|
|
||||||
" If we don't have native filetype support but the omnifunc is set to YCM's
|
|
||||||
" omnifunc because the previous file the user was editing DID have native
|
|
||||||
" support, we remove our omnifunc.
|
|
||||||
elseif &omnifunc == 'youcompleteme#OmniComplete'
|
|
||||||
let &omnifunc = ''
|
|
||||||
let &l:omnifunc = ''
|
|
||||||
endif
|
endif
|
||||||
|
return a:key
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
|
function! s:StopCompletion( key )
|
||||||
|
call timer_stop( s:pollers.completion.id )
|
||||||
|
if pumvisible()
|
||||||
|
let s:completion_stopped = 1
|
||||||
|
return "\<C-y>"
|
||||||
|
endif
|
||||||
|
return a:key
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
@ -546,23 +578,34 @@ function! s:OnTextChangedInsertMode()
|
|||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
exec s:python_command "ycm_state.OnCursorMoved()"
|
if s:completion_stopped
|
||||||
call s:UpdateCursorMoved()
|
let s:completion_stopped = 0
|
||||||
|
let s:completion = s:default_completion
|
||||||
call s:IdentifierFinishedOperations()
|
return
|
||||||
if g:ycm_autoclose_preview_window_after_completion
|
|
||||||
call s:ClosePreviewWindowIfNeeded()
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if g:ycm_auto_trigger || s:omnifunc_mode
|
call s:IdentifierFinishedOperations()
|
||||||
|
|
||||||
|
" We have to make sure we correctly leave semantic mode even when the user
|
||||||
|
" inserts something like a "operator[]" candidate string which fails
|
||||||
|
" CurrentIdentifierFinished check.
|
||||||
|
if s:force_semantic && !s:Pyeval( 'base.LastEnteredCharIsIdentifierChar()' )
|
||||||
|
let s:force_semantic = 0
|
||||||
|
endif
|
||||||
|
|
||||||
|
if &completefunc == "youcompleteme#CompleteFunc" &&
|
||||||
|
\ ( g:ycm_auto_trigger || s:force_semantic ) &&
|
||||||
|
\ !s:InsideCommentOrStringAndShouldStop() &&
|
||||||
|
\ !s:OnBlankLine()
|
||||||
|
" Immediately call previous completion to avoid flickers.
|
||||||
|
call s:Complete()
|
||||||
call s:InvokeCompletion()
|
call s:InvokeCompletion()
|
||||||
endif
|
endif
|
||||||
|
|
||||||
" We have to make sure we correctly leave omnifunc mode even when the user
|
exec s:python_command "ycm_state.OnCursorMoved()"
|
||||||
" inserts something like a "operator[]" candidate string which fails
|
|
||||||
" CurrentIdentifierFinished check.
|
if g:ycm_autoclose_preview_window_after_completion
|
||||||
if s:omnifunc_mode && !s:Pyeval( 'base.LastEnteredCharIsIdentifierChar()')
|
call s:ClosePreviewWindowIfNeeded()
|
||||||
let s:omnifunc_mode = 0
|
|
||||||
endif
|
endif
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
@ -572,7 +615,8 @@ function! s:OnInsertLeave()
|
|||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let s:omnifunc_mode = 0
|
let s:force_semantic = 0
|
||||||
|
let s:completion = s:default_completion
|
||||||
call s:OnFileReadyToParse()
|
call s:OnFileReadyToParse()
|
||||||
exec s:python_command "ycm_state.OnInsertLeave()"
|
exec s:python_command "ycm_state.OnInsertLeave()"
|
||||||
if g:ycm_autoclose_preview_window_after_completion ||
|
if g:ycm_autoclose_preview_window_after_completion ||
|
||||||
@ -582,22 +626,6 @@ function! s:OnInsertLeave()
|
|||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
function! s:OnInsertEnter()
|
|
||||||
if !s:AllowedToCompleteInCurrentBuffer()
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
|
|
||||||
let s:old_cursor_position = []
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
|
|
||||||
function! s:UpdateCursorMoved()
|
|
||||||
let current_position = getpos('.')
|
|
||||||
let s:cursor_moved = current_position != s:old_cursor_position
|
|
||||||
let s:old_cursor_position = current_position
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
|
|
||||||
function! s:ClosePreviewWindowIfNeeded()
|
function! s:ClosePreviewWindowIfNeeded()
|
||||||
let current_buffer_name = bufname('')
|
let current_buffer_name = bufname('')
|
||||||
|
|
||||||
@ -619,7 +647,8 @@ function! s:IdentifierFinishedOperations()
|
|||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
exec s:python_command "ycm_state.OnCurrentIdentifierFinished()"
|
exec s:python_command "ycm_state.OnCurrentIdentifierFinished()"
|
||||||
let s:omnifunc_mode = 0
|
let s:force_semantic = 0
|
||||||
|
let s:completion = s:default_completion
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
@ -662,28 +691,45 @@ endfunction
|
|||||||
|
|
||||||
|
|
||||||
function! s:InvokeCompletion()
|
function! s:InvokeCompletion()
|
||||||
if &completefunc != "youcompleteme#Complete"
|
exec s:python_command "ycm_state.SendCompletionRequest(" .
|
||||||
|
\ "vimsupport.GetBoolValue( 's:force_semantic' ) )"
|
||||||
|
|
||||||
|
call s:PollCompletion()
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
|
function! s:InvokeSemanticCompletion()
|
||||||
|
let s:force_semantic = 1
|
||||||
|
exec s:python_command "ycm_state.SendCompletionRequest( True )"
|
||||||
|
|
||||||
|
call s:PollCompletion()
|
||||||
|
" Since this function is called in a mapping through the expression register
|
||||||
|
" <C-R>=, its return value is inserted (see :h c_CTRL-R_=). We don't want to
|
||||||
|
" insert anything so we return an empty string.
|
||||||
|
return ''
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
|
function! s:PollCompletion( ... )
|
||||||
|
if !s:Pyeval( 'ycm_state.CompletionRequestReady()' )
|
||||||
|
let s:pollers.completion.id = timer_start(
|
||||||
|
\ s:pollers.completion.wait_milliseconds,
|
||||||
|
\ function( 's:PollCompletion' ) )
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if s:InsideCommentOrStringAndShouldStop() || s:OnBlankLine()
|
let response = s:Pyeval( 'ycm_state.GetCompletionResponse()' )
|
||||||
return
|
let s:completion = {
|
||||||
endif
|
\ 'start_column': response.completion_start_column,
|
||||||
|
\ 'candidates': response.completions
|
||||||
|
\ }
|
||||||
|
call s:Complete()
|
||||||
|
endfunction
|
||||||
|
|
||||||
" This is tricky. First, having 'refresh' set to 'always' in the dictionary
|
|
||||||
" that our completion function returns makes sure that our completion function
|
|
||||||
" is called on every keystroke. Second, when the sequence of characters the
|
|
||||||
" user typed produces no results in our search an infinite loop can occur. The
|
|
||||||
" problem is that our feedkeys call triggers the OnCursorMovedI event which we
|
|
||||||
" are tied to. We prevent this infinite loop from starting by making sure that
|
|
||||||
" the user has moved the cursor since the last time we provided completion
|
|
||||||
" results.
|
|
||||||
if !s:cursor_moved
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
|
|
||||||
|
function! s:Complete()
|
||||||
" <c-x><c-u> invokes the user's completion function (which we have set to
|
" <c-x><c-u> invokes the user's completion function (which we have set to
|
||||||
" youcompleteme#Complete), and <c-p> tells Vim to select the previous
|
" youcompleteme#CompleteFunc), and <c-p> tells Vim to select the previous
|
||||||
" completion candidate. This is necessary because by default, Vim selects the
|
" completion candidate. This is necessary because by default, Vim selects the
|
||||||
" first candidate when completion is invoked, and selecting a candidate
|
" first candidate when completion is invoked, and selecting a candidate
|
||||||
" automatically replaces the current text with it. Calling <c-p> forces Vim to
|
" automatically replaces the current text with it. Calling <c-p> forces Vim to
|
||||||
@ -693,43 +739,17 @@ function! s:InvokeCompletion()
|
|||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
" This is our main entry point. This is what vim calls to get completions.
|
function! youcompleteme#CompleteFunc( findstart, base )
|
||||||
function! youcompleteme#Complete( findstart, base )
|
|
||||||
" After the user types one character after the call to the omnifunc, the
|
|
||||||
" completefunc will be called because of our mapping that calls the
|
|
||||||
" completefunc on every keystroke. Therefore we need to delegate the call we
|
|
||||||
" 'stole' back to the omnifunc
|
|
||||||
if s:omnifunc_mode
|
|
||||||
return youcompleteme#OmniComplete( a:findstart, a:base )
|
|
||||||
endif
|
|
||||||
|
|
||||||
if a:findstart
|
if a:findstart
|
||||||
" InvokeCompletion has this check but we also need it here because of random
|
if s:completion.start_column > col( '.' ) ||
|
||||||
" Vim bugs and unfortunate interactions with the autocommands of other
|
\ empty( s:completion.candidates )
|
||||||
" plugins
|
" For vim, -2 means not found but don't trigger an error message.
|
||||||
if !s:cursor_moved
|
" See :h complete-functions.
|
||||||
" for vim, -2 means not found but don't trigger an error message
|
|
||||||
" see :h complete-functions
|
|
||||||
return -2
|
return -2
|
||||||
endif
|
endif
|
||||||
|
return s:completion.start_column - 1
|
||||||
exec s:python_command "ycm_state.CreateCompletionRequest()"
|
|
||||||
return s:Pyeval( 'base.CompletionStartColumn()' )
|
|
||||||
else
|
|
||||||
return s:Pyeval( 'ycm_state.GetCompletions()' )
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
|
|
||||||
function! youcompleteme#OmniComplete( findstart, base )
|
|
||||||
if a:findstart
|
|
||||||
let s:omnifunc_mode = 1
|
|
||||||
exec s:python_command "ycm_state.CreateCompletionRequest(" .
|
|
||||||
\ "force_semantic = True )"
|
|
||||||
return s:Pyeval( 'base.CompletionStartColumn()' )
|
|
||||||
else
|
|
||||||
return s:Pyeval( 'ycm_state.GetCompletions()' )
|
|
||||||
endif
|
endif
|
||||||
|
return s:completion.candidates
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
|
@ -115,18 +115,19 @@ Contents ~
|
|||||||
32. The |g:ycm_max_diagnostics_to_display| option
|
32. The |g:ycm_max_diagnostics_to_display| option
|
||||||
33. The |g:ycm_key_list_select_completion| option
|
33. The |g:ycm_key_list_select_completion| option
|
||||||
34. The |g:ycm_key_list_previous_completion| option
|
34. The |g:ycm_key_list_previous_completion| option
|
||||||
35. The |g:ycm_key_invoke_completion| option
|
35. The |g:ycm_key_list_stop_completion| option
|
||||||
36. The |g:ycm_key_detailed_diagnostics| option
|
36. The |g:ycm_key_invoke_completion| option
|
||||||
37. The |g:ycm_global_ycm_extra_conf| option
|
37. The |g:ycm_key_detailed_diagnostics| option
|
||||||
38. The |g:ycm_confirm_extra_conf| option
|
38. The |g:ycm_global_ycm_extra_conf| option
|
||||||
39. The |g:ycm_extra_conf_globlist| option
|
39. The |g:ycm_confirm_extra_conf| option
|
||||||
40. The |g:ycm_filepath_completion_use_working_dir| option
|
40. The |g:ycm_extra_conf_globlist| option
|
||||||
41. The |g:ycm_semantic_triggers| option
|
41. The |g:ycm_filepath_completion_use_working_dir| option
|
||||||
42. The |g:ycm_cache_omnifunc| option
|
42. The |g:ycm_semantic_triggers| option
|
||||||
43. The |g:ycm_use_ultisnips_completer| option
|
43. The |g:ycm_cache_omnifunc| option
|
||||||
44. The |g:ycm_goto_buffer_command| option
|
44. The |g:ycm_use_ultisnips_completer| option
|
||||||
45. The |g:ycm_disable_for_files_larger_than_kb| option
|
45. The |g:ycm_goto_buffer_command| option
|
||||||
46. The |g:ycm_python_binary_path| option
|
46. The |g:ycm_disable_for_files_larger_than_kb| option
|
||||||
|
47. The |g:ycm_python_binary_path| option
|
||||||
11. FAQ |youcompleteme-faq|
|
11. FAQ |youcompleteme-faq|
|
||||||
1. I used to be able to 'import vim' in '.ycm_extra_conf.py', but now can't |youcompleteme-i-used-to-be-able-to-import-vim-in-.ycm_extra_conf.py-but-now-cant|
|
1. I used to be able to 'import vim' in '.ycm_extra_conf.py', but now can't |youcompleteme-i-used-to-be-able-to-import-vim-in-.ycm_extra_conf.py-but-now-cant|
|
||||||
2. I get 'ImportError' exceptions that mention 'PyInit_ycm_core' or 'initycm_core' |youcompleteme-i-get-importerror-exceptions-that-mention-pyinit_ycm_core-or-initycm_core|
|
2. I get 'ImportError' exceptions that mention 'PyInit_ycm_core' or 'initycm_core' |youcompleteme-i-get-importerror-exceptions-that-mention-pyinit_ycm_core-or-initycm_core|
|
||||||
@ -2565,6 +2566,18 @@ Default: "['<S-TAB>', '<Up>']"
|
|||||||
let g:ycm_key_list_previous_completion = ['<S-TAB>', '<Up>']
|
let g:ycm_key_list_previous_completion = ['<S-TAB>', '<Up>']
|
||||||
<
|
<
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
The *g:ycm_key_list_stop_completion* option
|
||||||
|
|
||||||
|
This option controls the key mappings used to close the completion menu. This
|
||||||
|
is useful when the menu is blocking the view, when you need to insert the
|
||||||
|
'<TAB>' character, or when you want to expand a snippet from UltiSnips [21] and
|
||||||
|
navigate through it.
|
||||||
|
|
||||||
|
Default: "['<C-y>']"
|
||||||
|
>
|
||||||
|
let g:ycm_key_list_stop_completion = ['<C-y>']
|
||||||
|
<
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
The *g:ycm_key_invoke_completion* option
|
The *g:ycm_key_invoke_completion* option
|
||||||
|
|
||||||
This option controls the key mapping used to invoke the completion menu for
|
This option controls the key mapping used to invoke the completion menu for
|
||||||
|
@ -82,6 +82,9 @@ let g:ycm_key_list_select_completion =
|
|||||||
let g:ycm_key_list_previous_completion =
|
let g:ycm_key_list_previous_completion =
|
||||||
\ get( g:, 'ycm_key_list_previous_completion', ['<S-TAB>', '<Up>'] )
|
\ get( g:, 'ycm_key_list_previous_completion', ['<S-TAB>', '<Up>'] )
|
||||||
|
|
||||||
|
let g:ycm_key_list_stop_completion =
|
||||||
|
\ get( g:, 'ycm_key_list_stop_completion', ['<C-y>'] )
|
||||||
|
|
||||||
let g:ycm_key_invoke_completion =
|
let g:ycm_key_invoke_completion =
|
||||||
\ get( g:, 'ycm_key_invoke_completion', '<C-Space>' )
|
\ get( g:, 'ycm_key_invoke_completion', '<C-Space>' )
|
||||||
|
|
||||||
|
@ -25,7 +25,6 @@ from builtins import * # noqa
|
|||||||
from future.utils import iteritems
|
from future.utils import iteritems
|
||||||
from ycm import vimsupport
|
from ycm import vimsupport
|
||||||
from ycmd import user_options_store
|
from ycmd import user_options_store
|
||||||
from ycmd import request_wrap
|
|
||||||
from ycmd import identifier_utils
|
from ycmd import identifier_utils
|
||||||
|
|
||||||
YCM_VAR_PREFIX = 'ycm_'
|
YCM_VAR_PREFIX = 'ycm_'
|
||||||
@ -57,13 +56,6 @@ def LoadJsonDefaultsIntoVim():
|
|||||||
vimsupport.SetVariableValue( new_key, value )
|
vimsupport.SetVariableValue( new_key, value )
|
||||||
|
|
||||||
|
|
||||||
def CompletionStartColumn():
|
|
||||||
return ( request_wrap.CompletionStartColumn(
|
|
||||||
vimsupport.CurrentLineContents(),
|
|
||||||
vimsupport.CurrentColumn() + 1,
|
|
||||||
vimsupport.CurrentFiletypes()[ 0 ] ) - 1 )
|
|
||||||
|
|
||||||
|
|
||||||
def CurrentIdentifierFinished():
|
def CurrentIdentifierFinished():
|
||||||
line, current_column = vimsupport.CurrentLineContentsAndCodepointColumn()
|
line, current_column = vimsupport.CurrentLineContentsAndCodepointColumn()
|
||||||
previous_char_index = current_column - 1
|
previous_char_index = current_column - 1
|
||||||
|
@ -27,20 +27,18 @@ from ycm.client.base_request import ( BaseRequest, JsonFromFuture,
|
|||||||
HandleServerException,
|
HandleServerException,
|
||||||
MakeServerException )
|
MakeServerException )
|
||||||
|
|
||||||
TIMEOUT_SECONDS = 0.5
|
|
||||||
|
|
||||||
|
|
||||||
class CompletionRequest( BaseRequest ):
|
class CompletionRequest( BaseRequest ):
|
||||||
def __init__( self, request_data ):
|
def __init__( self, request_data ):
|
||||||
super( CompletionRequest, self ).__init__()
|
super( CompletionRequest, self ).__init__()
|
||||||
self.request_data = request_data
|
self.request_data = request_data
|
||||||
self._response_future = None
|
self._response_future = None
|
||||||
|
self._response = { 'completions': [], 'completion_start_column': -1 }
|
||||||
|
|
||||||
|
|
||||||
def Start( self ):
|
def Start( self ):
|
||||||
self._response_future = self.PostDataToHandlerAsync( self.request_data,
|
self._response_future = self.PostDataToHandlerAsync( self.request_data,
|
||||||
'completions',
|
'completions' )
|
||||||
TIMEOUT_SECONDS )
|
|
||||||
|
|
||||||
|
|
||||||
def Done( self ):
|
def Done( self ):
|
||||||
@ -49,21 +47,26 @@ class CompletionRequest( BaseRequest ):
|
|||||||
|
|
||||||
def RawResponse( self ):
|
def RawResponse( self ):
|
||||||
if not self._response_future:
|
if not self._response_future:
|
||||||
return []
|
return self._response
|
||||||
with HandleServerException( truncate = True ):
|
|
||||||
response = JsonFromFuture( self._response_future )
|
|
||||||
|
|
||||||
errors = response[ 'errors' ] if 'errors' in response else []
|
with HandleServerException( truncate = True ):
|
||||||
|
self._response = JsonFromFuture( self._response_future )
|
||||||
|
|
||||||
|
# Vim may not be able to convert the 'errors' entry to its internal format
|
||||||
|
# so we remove it from the response.
|
||||||
|
errors = self._response.pop( 'errors', [] )
|
||||||
for e in errors:
|
for e in errors:
|
||||||
with HandleServerException( truncate = True ):
|
with HandleServerException( truncate = True ):
|
||||||
raise MakeServerException( e )
|
raise MakeServerException( e )
|
||||||
|
|
||||||
return response[ 'completions' ]
|
return self._response
|
||||||
return []
|
|
||||||
|
|
||||||
|
|
||||||
def Response( self ):
|
def Response( self ):
|
||||||
return _ConvertCompletionDatasToVimDatas( self.RawResponse() )
|
response = self.RawResponse()
|
||||||
|
response[ 'completions' ] = _ConvertCompletionDatasToVimDatas(
|
||||||
|
response[ 'completions' ] )
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
def ConvertCompletionDataToVimData( completion_data ):
|
def ConvertCompletionDataToVimData( completion_data ):
|
||||||
|
@ -40,11 +40,17 @@ class OmniCompletionRequest( CompletionRequest ):
|
|||||||
|
|
||||||
|
|
||||||
def RawResponse( self ):
|
def RawResponse( self ):
|
||||||
return _ConvertVimDatasToCompletionDatas( self._results )
|
return {
|
||||||
|
'completions': _ConvertVimDatasToCompletionDatas( self._results ),
|
||||||
|
'completion_start_column': self.request_data[ 'start_column' ]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def Response( self ):
|
def Response( self ):
|
||||||
return self._results
|
return {
|
||||||
|
'completions': self._results,
|
||||||
|
'completion_start_column': self.request_data[ 'start_column' ]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def ConvertVimDataToCompletionData( vim_data ):
|
def ConvertVimDataToCompletionData( vim_data ):
|
||||||
|
@ -59,6 +59,8 @@ class OmniCompleter( Completer ):
|
|||||||
def ShouldUseNowInner( self, request_data ):
|
def ShouldUseNowInner( self, request_data ):
|
||||||
if not self._omnifunc:
|
if not self._omnifunc:
|
||||||
return False
|
return False
|
||||||
|
if request_data.get( 'force_semantic', False ):
|
||||||
|
return True
|
||||||
return super( OmniCompleter, self ).ShouldUseNowInner( request_data )
|
return super( OmniCompleter, self ).ShouldUseNowInner( request_data )
|
||||||
|
|
||||||
|
|
||||||
|
@ -29,11 +29,14 @@ from hamcrest import assert_that, has_entries
|
|||||||
from ycm.client.omni_completion_request import OmniCompletionRequest
|
from ycm.client.omni_completion_request import OmniCompletionRequest
|
||||||
|
|
||||||
|
|
||||||
def BuildOmnicompletionRequest( results ):
|
def BuildOmnicompletionRequest( results, start_column = 1 ):
|
||||||
omni_completer = MagicMock()
|
omni_completer = MagicMock()
|
||||||
omni_completer.ComputeCandidates = MagicMock( return_value = results )
|
omni_completer.ComputeCandidates = MagicMock( return_value = results )
|
||||||
|
|
||||||
request = OmniCompletionRequest( omni_completer, None )
|
request_data = {
|
||||||
|
'start_column': start_column
|
||||||
|
}
|
||||||
|
request = OmniCompletionRequest( omni_completer, request_data )
|
||||||
request.Start()
|
request.Start()
|
||||||
|
|
||||||
return request
|
return request
|
||||||
@ -49,7 +52,10 @@ def Response_FromOmniCompleter_test():
|
|||||||
results = [ { "word": "test" } ]
|
results = [ { "word": "test" } ]
|
||||||
request = BuildOmnicompletionRequest( results )
|
request = BuildOmnicompletionRequest( results )
|
||||||
|
|
||||||
eq_( request.Response(), results )
|
eq_( request.Response(), {
|
||||||
|
'completions': results,
|
||||||
|
'completion_start_column': 1
|
||||||
|
} )
|
||||||
|
|
||||||
|
|
||||||
def RawResponse_ConvertedFromOmniCompleter_test():
|
def RawResponse_ConvertedFromOmniCompleter_test():
|
||||||
@ -73,7 +79,7 @@ def RawResponse_ConvertedFromOmniCompleter_test():
|
|||||||
]
|
]
|
||||||
request = BuildOmnicompletionRequest( vim_results )
|
request = BuildOmnicompletionRequest( vim_results )
|
||||||
|
|
||||||
results = request.RawResponse()
|
results = request.RawResponse()[ 'completions' ]
|
||||||
|
|
||||||
eq_( len( results ), len( expected_results ) )
|
eq_( len( results ), len( expected_results ) )
|
||||||
for result, expected_result in zip( results, expected_results ):
|
for result, expected_result in zip( results, expected_results ):
|
||||||
|
@ -28,29 +28,56 @@ from ycm.tests.test_utils import ( CurrentWorkingDirectory, ExtendedMock,
|
|||||||
MockVimModule, MockVimBuffers, VimBuffer )
|
MockVimModule, MockVimBuffers, VimBuffer )
|
||||||
MockVimModule()
|
MockVimModule()
|
||||||
|
|
||||||
|
import contextlib
|
||||||
from hamcrest import assert_that, contains, empty, has_entries
|
from hamcrest import assert_that, contains, empty, has_entries
|
||||||
from mock import call, patch
|
from mock import call, MagicMock, patch
|
||||||
|
from nose.tools import ok_
|
||||||
|
|
||||||
from ycm.tests import PathToTestFile, YouCompleteMeInstance
|
from ycm.tests import PathToTestFile, YouCompleteMeInstance
|
||||||
from ycmd.responses import ServerError
|
from ycmd.responses import ServerError
|
||||||
|
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def MockCompletionRequest( response_method ):
|
||||||
|
"""Mock out the CompletionRequest, replacing the response handler
|
||||||
|
JsonFromFuture with the |response_method| parameter."""
|
||||||
|
|
||||||
|
# We don't want the event to actually be sent to the server, just have it
|
||||||
|
# return success.
|
||||||
|
with patch( 'ycm.client.completion_request.CompletionRequest.'
|
||||||
|
'PostDataToHandlerAsync',
|
||||||
|
return_value = MagicMock( return_value=True ) ):
|
||||||
|
|
||||||
|
# We set up a fake response (as called by CompletionRequest.RawResponse)
|
||||||
|
# which calls the supplied callback method.
|
||||||
|
#
|
||||||
|
# Note: JsonFromFuture is actually part of ycm.client.base_request, but we
|
||||||
|
# must patch where an object is looked up, not where it is defined.
|
||||||
|
# See https://docs.python.org/dev/library/unittest.mock.html#where-to-patch
|
||||||
|
# for details.
|
||||||
|
with patch( 'ycm.client.completion_request.JsonFromFuture',
|
||||||
|
side_effect = response_method ):
|
||||||
|
yield
|
||||||
|
|
||||||
|
|
||||||
@YouCompleteMeInstance()
|
@YouCompleteMeInstance()
|
||||||
def CreateCompletionRequest_UnicodeWorkingDirectory_test( ycm ):
|
def SendCompletionRequest_UnicodeWorkingDirectory_test( ycm ):
|
||||||
unicode_dir = PathToTestFile( 'uni¢𐍈d€' )
|
unicode_dir = PathToTestFile( 'uni¢𐍈d€' )
|
||||||
current_buffer = VimBuffer( PathToTestFile( 'uni¢𐍈d€', 'current_buffer' ) )
|
current_buffer = VimBuffer( PathToTestFile( 'uni¢𐍈d€', 'current_buffer' ) )
|
||||||
|
|
||||||
|
def ServerResponse( *args ):
|
||||||
|
return { 'completions': [], 'completion_start_column': 1 }
|
||||||
|
|
||||||
with CurrentWorkingDirectory( unicode_dir ):
|
with CurrentWorkingDirectory( unicode_dir ):
|
||||||
with MockVimBuffers( [ current_buffer ], current_buffer ):
|
with MockVimBuffers( [ current_buffer ], current_buffer ):
|
||||||
ycm.CreateCompletionRequest(),
|
with MockCompletionRequest( ServerResponse ):
|
||||||
|
ycm.SendCompletionRequest()
|
||||||
results = ycm.GetCompletions()
|
ok_( ycm.CompletionRequestReady() )
|
||||||
|
|
||||||
assert_that(
|
assert_that(
|
||||||
results,
|
ycm.GetCompletionResponse(),
|
||||||
has_entries( {
|
has_entries( {
|
||||||
'words': empty(),
|
'completions': empty(),
|
||||||
'refresh': 'always'
|
'completion_start_column': 1
|
||||||
} )
|
} )
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -58,14 +85,13 @@ def CreateCompletionRequest_UnicodeWorkingDirectory_test( ycm ):
|
|||||||
@YouCompleteMeInstance()
|
@YouCompleteMeInstance()
|
||||||
@patch( 'ycm.client.base_request._logger', autospec = True )
|
@patch( 'ycm.client.base_request._logger', autospec = True )
|
||||||
@patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock )
|
@patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock )
|
||||||
def CreateCompletionRequest_ResponseContainingError_test( ycm,
|
def SendCompletionRequest_ResponseContainingError_test( ycm,
|
||||||
post_vim_message,
|
post_vim_message,
|
||||||
logger ):
|
logger ):
|
||||||
current_buffer = VimBuffer( 'buffer' )
|
current_buffer = VimBuffer( 'buffer' )
|
||||||
with MockVimBuffers( [ current_buffer ], current_buffer ):
|
|
||||||
ycm.CreateCompletionRequest(),
|
|
||||||
|
|
||||||
response = {
|
def ServerResponse( *args ):
|
||||||
|
return {
|
||||||
'completions': [ {
|
'completions': [ {
|
||||||
'insertion_text': 'insertion_text',
|
'insertion_text': 'insertion_text',
|
||||||
'menu_text': 'menu_text',
|
'menu_text': 'menu_text',
|
||||||
@ -86,18 +112,20 @@ def CreateCompletionRequest_ResponseContainingError_test( ycm,
|
|||||||
} ]
|
} ]
|
||||||
}
|
}
|
||||||
|
|
||||||
with patch( 'ycm.client.completion_request.JsonFromFuture',
|
with MockVimBuffers( [ current_buffer ], current_buffer ):
|
||||||
return_value = response ):
|
with MockCompletionRequest( ServerResponse ):
|
||||||
results = ycm.GetCompletions()
|
ycm.SendCompletionRequest()
|
||||||
|
ok_( ycm.CompletionRequestReady() )
|
||||||
logger.exception.assert_called_with( 'Error while handling server response' )
|
response = ycm.GetCompletionResponse()
|
||||||
|
logger.exception.assert_called_with( 'Error while handling server '
|
||||||
|
'response' )
|
||||||
post_vim_message.assert_has_exact_calls( [
|
post_vim_message.assert_has_exact_calls( [
|
||||||
call( 'Exception: message', truncate = True )
|
call( 'Exception: message', truncate = True )
|
||||||
] )
|
] )
|
||||||
assert_that(
|
assert_that(
|
||||||
results,
|
response,
|
||||||
has_entries( {
|
has_entries( {
|
||||||
'words': contains( has_entries( {
|
'completions': contains( has_entries( {
|
||||||
'word': 'insertion_text',
|
'word': 'insertion_text',
|
||||||
'abbr': 'menu_text',
|
'abbr': 'menu_text',
|
||||||
'menu': 'extra_menu_info',
|
'menu': 'extra_menu_info',
|
||||||
@ -106,7 +134,7 @@ def CreateCompletionRequest_ResponseContainingError_test( ycm,
|
|||||||
'dup': 1,
|
'dup': 1,
|
||||||
'empty': 1
|
'empty': 1
|
||||||
} ) ),
|
} ) ),
|
||||||
'refresh': 'always'
|
'completion_start_column': 3
|
||||||
} )
|
} )
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -114,25 +142,24 @@ def CreateCompletionRequest_ResponseContainingError_test( ycm,
|
|||||||
@YouCompleteMeInstance()
|
@YouCompleteMeInstance()
|
||||||
@patch( 'ycm.client.base_request._logger', autospec = True )
|
@patch( 'ycm.client.base_request._logger', autospec = True )
|
||||||
@patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock )
|
@patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock )
|
||||||
def CreateCompletionRequest_ErrorFromServer_test( ycm,
|
def SendCompletionRequest_ErrorFromServer_test( ycm,
|
||||||
post_vim_message,
|
post_vim_message,
|
||||||
logger ):
|
logger ):
|
||||||
current_buffer = VimBuffer( 'buffer' )
|
current_buffer = VimBuffer( 'buffer' )
|
||||||
with MockVimBuffers( [ current_buffer ], current_buffer ):
|
with MockVimBuffers( [ current_buffer ], current_buffer ):
|
||||||
ycm.CreateCompletionRequest(),
|
with MockCompletionRequest( ServerError( 'Server error' ) ):
|
||||||
|
ycm.SendCompletionRequest()
|
||||||
with patch( 'ycm.client.completion_request.JsonFromFuture',
|
ok_( ycm.CompletionRequestReady() )
|
||||||
side_effect = ServerError( 'Server error' ) ):
|
response = ycm.GetCompletionResponse()
|
||||||
results = ycm.GetCompletions()
|
logger.exception.assert_called_with( 'Error while handling server '
|
||||||
|
'response' )
|
||||||
logger.exception.assert_called_with( 'Error while handling server response' )
|
|
||||||
post_vim_message.assert_has_exact_calls( [
|
post_vim_message.assert_has_exact_calls( [
|
||||||
call( 'Server error', truncate = True )
|
call( 'Server error', truncate = True )
|
||||||
] )
|
] )
|
||||||
assert_that(
|
assert_that(
|
||||||
results,
|
response,
|
||||||
has_entries( {
|
has_entries( {
|
||||||
'words': empty(),
|
'completions': empty(),
|
||||||
'refresh': 'always'
|
'completion_start_column': -1
|
||||||
} )
|
} )
|
||||||
)
|
)
|
||||||
|
@ -86,15 +86,15 @@ def MockEventNotification( response_method, native_filetype_completer = True ):
|
|||||||
'PostDataToHandlerAsync',
|
'PostDataToHandlerAsync',
|
||||||
return_value = MagicMock( return_value=True ) ):
|
return_value = MagicMock( return_value=True ) ):
|
||||||
|
|
||||||
# We set up a fake a Response (as called by EventNotification.Response)
|
# We set up a fake response (as called by EventNotification.Response) which
|
||||||
# which calls the supplied callback method. Generally this callback just
|
# calls the supplied callback method. Generally this callback just raises an
|
||||||
# raises an apropriate exception, otherwise it would have to return a mock
|
# apropriate exception, otherwise it would have to return a mock future
|
||||||
# future object.
|
# object.
|
||||||
#
|
#
|
||||||
# Note: JsonFromFuture is actually part of ycm.client.base_request, but we
|
# Note: JsonFromFuture is actually part of ycm.client.base_request, but we
|
||||||
# must patch where an object is looked up, not where it is defined.
|
# must patch where an object is looked up, not where it is defined. See
|
||||||
# See https://docs.python.org/dev/library/unittest.mock.html#where-to-patch
|
# https://docs.python.org/dev/library/unittest.mock.html#where-to-patch for
|
||||||
# for details.
|
# details.
|
||||||
with patch( 'ycm.client.event_notification.JsonFromFuture',
|
with patch( 'ycm.client.event_notification.JsonFromFuture',
|
||||||
side_effect = response_method ):
|
side_effect = response_method ):
|
||||||
|
|
||||||
|
@ -173,9 +173,6 @@ def _MockVimEval( value ):
|
|||||||
if value == 'tempname()':
|
if value == 'tempname()':
|
||||||
return '_TEMP_FILE_'
|
return '_TEMP_FILE_'
|
||||||
|
|
||||||
if value == 'complete_check()':
|
|
||||||
return 0
|
|
||||||
|
|
||||||
if value == 'tagfiles()':
|
if value == 'tagfiles()':
|
||||||
return [ 'tags' ]
|
return [ 'tags' ]
|
||||||
|
|
||||||
|
@ -277,37 +277,35 @@ class YouCompleteMe( object ):
|
|||||||
self._SetupServer()
|
self._SetupServer()
|
||||||
|
|
||||||
|
|
||||||
def CreateCompletionRequest( self, force_semantic = False ):
|
def SendCompletionRequest( self, force_semantic = False ):
|
||||||
request_data = BuildRequestData()
|
request_data = BuildRequestData()
|
||||||
|
request_data[ 'force_semantic' ] = force_semantic
|
||||||
if ( not self.NativeFiletypeCompletionAvailable() and
|
if ( not self.NativeFiletypeCompletionAvailable() and
|
||||||
self.CurrentFiletypeCompletionEnabled() ):
|
self.CurrentFiletypeCompletionEnabled() ):
|
||||||
wrapped_request_data = RequestWrap( request_data )
|
wrapped_request_data = RequestWrap( request_data )
|
||||||
if self._omnicomp.ShouldUseNow( wrapped_request_data ):
|
if self._omnicomp.ShouldUseNow( wrapped_request_data ):
|
||||||
self._latest_completion_request = OmniCompletionRequest(
|
self._latest_completion_request = OmniCompletionRequest(
|
||||||
self._omnicomp, wrapped_request_data )
|
self._omnicomp, wrapped_request_data )
|
||||||
return self._latest_completion_request
|
self._latest_completion_request.Start()
|
||||||
|
return
|
||||||
|
|
||||||
request_data[ 'working_dir' ] = utils.GetCurrentDirectory()
|
request_data[ 'working_dir' ] = utils.GetCurrentDirectory()
|
||||||
|
|
||||||
self._AddExtraConfDataIfNeeded( request_data )
|
self._AddExtraConfDataIfNeeded( request_data )
|
||||||
if force_semantic:
|
|
||||||
request_data[ 'force_semantic' ] = True
|
|
||||||
self._latest_completion_request = CompletionRequest( request_data )
|
self._latest_completion_request = CompletionRequest( request_data )
|
||||||
return self._latest_completion_request
|
self._latest_completion_request.Start()
|
||||||
|
|
||||||
|
|
||||||
def GetCompletions( self ):
|
def CompletionRequestReady( self ):
|
||||||
request = self.GetCurrentCompletionRequest()
|
return bool( self._latest_completion_request and
|
||||||
request.Start()
|
self._latest_completion_request.Done() )
|
||||||
while not request.Done():
|
|
||||||
try:
|
|
||||||
if vimsupport.GetBoolValue( 'complete_check()' ):
|
|
||||||
return { 'words' : [], 'refresh' : 'always' }
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
return { 'words' : [], 'refresh' : 'always' }
|
|
||||||
|
|
||||||
results = base.AdjustCandidateInsertionText( request.Response() )
|
|
||||||
return { 'words' : results, 'refresh' : 'always' }
|
def GetCompletionResponse( self ):
|
||||||
|
response = self._latest_completion_request.Response()
|
||||||
|
response[ 'completions' ] = base.AdjustCandidateInsertionText(
|
||||||
|
response[ 'completions' ] )
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
def SendCommandRequest( self, arguments, completer ):
|
def SendCommandRequest( self, arguments, completer ):
|
||||||
|
Loading…
Reference in New Issue
Block a user