Auto merge of #2707 - micbou:fix-omnifunc-cursor-move, r=bstaletic

[READY] Restore cursor position after omnifunc call

When compiled without C-family support, YCM will use the default omnifunc from Vim (`ccomplete#Complete`) to provide semantic completion. This omnifunc calls [`searchdecl`](http://vimdoc.sourceforge.net/htmldoc/eval.html#searchdecl()) to find a declaration, which is supposed to move the cursor to that declaration. However, the cursor is not moved when called through the omni completion mapping (`CTRL-X CTRL-O`). Since PR https://github.com/Valloric/YouCompleteMe/pull/2657, YCM calls the omnifunc outside completion mode and thus the cursor is moved to the found declaration after typing `.` or `->`.

Considering this `searchdecl` trick may be used by other omnifuncs, we fix the issue by always restoring the cursor position after calling the omnifunc.

Fixes #2698.

<!-- 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/2707)
<!-- Reviewable:end -->
This commit is contained in:
zzbot 2017-07-07 06:29:34 -07:00 committed by GitHub
commit d299f9eb70
3 changed files with 53 additions and 1 deletions

View File

@ -90,13 +90,22 @@ class OmniCompleter( Completer ):
# 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' ] = return_value + 1
# Calling directly the omnifunc may move the cursor position. This is the
# case with the default Vim omnifunc for C-family languages
# (ccomplete#Complete) which calls searchdecl to find a declaration. This
# function is supposed to move the cursor to the found declaration but it
# doesn't when called through the omni completion mapping (CTRL-X CTRL-O).
# So, we restore the cursor position after calling the omnifunc.
line, column = vimsupport.CurrentLineAndColumn()
omnifunc_call = [ self._omnifunc, omnifunc_call = [ self._omnifunc,
"(0,'", "(0,'",
vimsupport.EscapeForVim( request_data[ 'query' ] ), vimsupport.EscapeForVim( request_data[ 'query' ] ),
"')" ] "')" ]
items = vim.eval( ''.join( omnifunc_call ) ) items = vim.eval( ''.join( omnifunc_call ) )
vimsupport.SetCurrentLineAndColumn( line, column )
if isinstance( items, dict ) and 'words' in items: if isinstance( items, dict ) and 'words' in items:
items = items[ 'words' ] items = items[ 'words' ]

View File

@ -30,6 +30,7 @@ from ycm.tests.test_utils import ( ExpectedFailure, MockVimBuffers,
MockVimModule, ToBytesOnPY2, VimBuffer ) MockVimModule, ToBytesOnPY2, VimBuffer )
MockVimModule() MockVimModule()
from ycm import vimsupport
from ycm.tests import YouCompleteMeInstance from ycm.tests import YouCompleteMeInstance
@ -621,3 +622,39 @@ def OmniCompleter_GetCompletions_Cache_ObjectListObject_Unicode_test( ycm ):
'completion_start_column': 13 'completion_start_column': 13
} ) } )
) )
@YouCompleteMeInstance( { 'cache_omnifunc': 1 } )
def OmniCompleter_GetCompletions_RestoreCursorPositionAfterOmnifuncCall_test(
ycm ):
# This omnifunc moves the cursor to the test definition like
# ccomplete#Complete would.
def Omnifunc( findstart, base ):
if findstart:
return 5
vimsupport.SetCurrentLineAndColumn( 0, 0 )
return [ 'length' ]
current_buffer = VimBuffer( 'buffer',
contents = [ 'String test',
'',
'test.' ],
filetype = 'java',
omnifunc = Omnifunc )
with MockVimBuffers( [ current_buffer ], current_buffer, ( 3, 5 ) ):
# Make sure there is an omnifunc set up.
ycm.OnFileReadyToParse()
ycm.SendCompletionRequest()
assert_that(
vimsupport.CurrentLineAndColumn(),
contains( 2, 5 )
)
assert_that(
ycm.GetCompletionResponse(),
has_entries( {
'completions': ToBytesOnPY2( [ 'length' ] ),
'completion_start_column': 6
} )
)

View File

@ -56,6 +56,12 @@ def CurrentLineAndColumn():
return line, column return line, column
def SetCurrentLineAndColumn( line, column ):
"""Sets the cursor position to the 0-based line and 0-based column."""
# Line from vim.current.window.cursor is 1-based.
vim.current.window.cursor = ( line + 1, column )
def CurrentColumn(): def CurrentColumn():
"""Returns the 0-based current column. Do NOT access the CurrentColumn in """Returns the 0-based current column. Do NOT access the CurrentColumn in
vim.current.line. It doesn't exist yet when the cursor is at the end of the vim.current.line. It doesn't exist yet when the cursor is at the end of the