Auto merge of #3112 - micbou:omnifunc-start-column, r=micbou

[READY] Move cursor to start column before calling again omnifunc

Vim internally moves the cursor to the start column before calling the omnifunc a second time. This can be confirmed by starting Vim with the following vimrc:
```vim
set nocompatible

function! Omnifunc(findstart, base)
        echom col('.')
	if a:findstart
		if getline('.') == 'foo.ba'
			return 4
		endif
	endif
	return [ 'bar', 'baz']
endfunction

set omnifunc=Omnifunc
```
typing `foo.ba`, and hitting `<C-X><C-O>`. This outputs `7` and `5` on the command line which means that the cursor at column 7 on the first call of the omnifunc is moved to column 5 on the second call.

Since some omnifuncs depend on this to compute the list of candidates (e.g. the one defined by [the LanguageClient-neovim plugin](https://github.com/autozimu/LanguageClient-neovim)), we should reproduce that behavior in YCM.

Fixes https://github.com/Valloric/YouCompleteMe/issues/3099.

<!-- 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/3112)
<!-- Reviewable:end -->
This commit is contained in:
zzbot 2018-08-19 08:49:46 -07:00 committed by GitHub
commit 96e80c8de2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 47 additions and 5 deletions

View File

@ -79,17 +79,18 @@ class OmniCompleter( Completer ):
return [] return []
try: try:
return_value = vimsupport.GetIntValue( self._omnifunc + '(1,"")' ) start_column = vimsupport.GetIntValue( self._omnifunc + '(1,"")' )
if return_value < 0: if start_column < 0:
# FIXME: Technically, if the return is -1 we should raise an error # FIXME: Technically, if the returned value is -1 we should raise an
# error.
return [] return []
# Use the start column calculated by the omnifunc, rather than our own # Use the start column calculated by the omnifunc, rather than our own
# interpretation. This is important for certain languages where our # interpretation. This is important for certain languages where our
# identifier detection is either incorrect or not compatible with the # identifier detection is either incorrect or not compatible with the
# behaviour of the omnifunc. Note: do this before calling the omnifunc # behaviour of the omnifunc. Note: do this before calling the omnifunc
# because it affects the value returned by 'query' # because it affects the value returned by 'query'.
request_data[ 'start_column' ] = return_value + 1 request_data[ 'start_column' ] = start_column + 1
# Calling directly the omnifunc may move the cursor position. This is the # Calling directly the omnifunc may move the cursor position. This is the
# case with the default Vim omnifunc for C-family languages # case with the default Vim omnifunc for C-family languages
@ -99,6 +100,12 @@ class OmniCompleter( Completer ):
# So, we restore the cursor position after calling the omnifunc. # So, we restore the cursor position after calling the omnifunc.
line, column = vimsupport.CurrentLineAndColumn() line, column = vimsupport.CurrentLineAndColumn()
# Vim internally moves the cursor to the start column before calling again
# the omnifunc. Some omnifuncs like the one defined by the
# LanguageClient-neovim plugin depend on this behavior to compute the list
# of candidates.
vimsupport.SetCurrentLineAndColumn( line, start_column )
omnifunc_call = [ self._omnifunc, omnifunc_call = [ self._omnifunc,
"(0,'", "(0,'",
vimsupport.EscapeForVim( request_data[ 'query' ] ), vimsupport.EscapeForVim( request_data[ 'query' ] ),

View File

@ -649,6 +649,41 @@ def OmniCompleter_GetCompletions_RestoreCursorPositionAfterOmnifuncCall_test(
) )
@YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 1,
'g:ycm_semantic_triggers': TRIGGERS } )
def OmniCompleter_GetCompletions_MoveCursorPositionAtStartColumn_test( ycm ):
# This omnifunc relies on the cursor being moved at the start column when
# called the second time like LanguageClient#complete from the
# LanguageClient-neovim plugin.
def Omnifunc( findstart, base ):
if findstart:
return 5
if vimsupport.CurrentColumn() == 5:
return [ 'length' ]
return []
current_buffer = VimBuffer( 'buffer',
contents = [ 'String test',
'',
'test.le' ],
filetype = FILETYPE,
omnifunc = Omnifunc )
with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 3, 7 ) ):
ycm.SendCompletionRequest()
assert_that(
vimsupport.CurrentLineAndColumn(),
contains( 2, 7 )
)
assert_that(
ycm.GetCompletionResponse(),
has_entries( {
'completions': ToBytesOnPY2( [ 'length' ] ),
'completion_start_column': 6
} )
)
@YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 0, @YouCompleteMeInstance( { 'g:ycm_cache_omnifunc': 0,
'g:ycm_semantic_triggers': TRIGGERS } ) 'g:ycm_semantic_triggers': TRIGGERS } )
def OmniCompleter_GetCompletions_NoCache_NoSemanticTrigger_test( ycm ): def OmniCompleter_GetCompletions_NoCache_NoSemanticTrigger_test( ycm ):