From eba717553fd52b7a7d872338ef1b1fee93e9e0ed Mon Sep 17 00:00:00 2001 From: micbou Date: Fri, 19 Oct 2018 15:59:41 +0200 Subject: [PATCH] Recompute starting column on auto-wrapping When auto-wrapping is enabled, Vim wraps the current line after the completion request is sent but before calling the completefunc. The starting column returned by the server is invalid in that case and must be recomputed. --- autoload/youcompleteme.vim | 50 ++++++++++++------- python/ycm/client/completion_request.py | 7 ++- python/ycm/client/omni_completion_request.py | 12 +++-- .../client/omni_completion_request_tests.py | 8 ++- python/ycm/vimsupport.py | 7 +++ 5 files changed, 58 insertions(+), 26 deletions(-) diff --git a/autoload/youcompleteme.vim b/autoload/youcompleteme.vim index 5f56f40e..7f0b09f7 100644 --- a/autoload/youcompleteme.vim +++ b/autoload/youcompleteme.vim @@ -23,11 +23,9 @@ set cpo&vim let s:script_folder_path = escape( expand( ':p:h' ), '\' ) let s:force_semantic = 0 let s:completion_stopped = 0 -let s:default_completion = { - \ 'start_column': -1, - \ 'candidates': [] - \ } -let s:completion = s:default_completion +" These two variables are initialized in youcompleteme#Enable. +let s:default_completion = {} +let s:completion = {} let s:previous_allowed_buffer_number = 0 let s:pollers = { \ 'completion': { @@ -147,6 +145,9 @@ function! youcompleteme#Enable() let s:pollers.server_ready.id = timer_start( \ s:pollers.server_ready.wait_milliseconds, \ function( 's:PollServerReady' ) ) + + let s:default_completion = s:Pyeval( 'vimsupport.NO_COMPLETIONS' ) + let s:completion = s:default_completion endfunction @@ -811,11 +812,7 @@ function! s:PollCompletion( ... ) return endif - let response = s:Pyeval( 'ycm_state.GetCompletionResponse()' ) - let s:completion = { - \ 'start_column': response.completion_start_column, - \ 'candidates': response.completions - \ } + let s:completion = s:Pyeval( 'ycm_state.GetCompletionResponse()' ) call s:Complete() endfunction @@ -824,16 +821,17 @@ function! s:Complete() " Do not call user's completion function if the start column is after the " current column or if there are no candidates. Close the completion menu " instead. This avoids keeping the user in completion mode. - if s:completion.start_column > col( '.' ) || empty( s:completion.candidates ) + if s:completion.completion_start_column > s:completion.column || + \ empty( s:completion.completions ) call s:CloseCompletionMenu() else " invokes the user's completion function (which we have set to " youcompleteme#CompleteFunc), and tells Vim to select the previous - " completion candidate. This is necessary because by default, Vim selects the - " first candidate when completion is invoked, and selecting a candidate - " automatically replaces the current text with it. Calling forces Vim to - " deselect the first candidate and in turn preserve the user's current text - " until he explicitly chooses to replace it with a completion. + " completion candidate. This is necessary because by default, Vim selects + " the first candidate when completion is invoked, and selecting a candidate + " automatically replaces the current text with it. Calling forces Vim + " to deselect the first candidate and in turn preserve the user's current + " text until he explicitly chooses to replace it with a completion. call s:SendKeys( "\\\" ) endif endfunction @@ -841,9 +839,25 @@ endfunction function! youcompleteme#CompleteFunc( findstart, base ) if a:findstart - return s:completion.start_column - 1 + " When auto-wrapping is enabled, Vim wraps the current line after the + " completion request is sent but before calling this function. The starting + " column returned by the server is invalid in that case and must be + " recomputed. + if s:completion.line != line( '.' ) + " Given + " scb: column where the completion starts before auto-wrapping + " cb: cursor column before auto-wrapping + " sca: column where the completion starts after auto-wrapping + " ca: cursor column after auto-wrapping + " we have + " ca - sca = cb - scb + " sca = scb + ca - cb + let s:completion.completion_start_column += + \ col( '.' ) - s:completion.column + endif + return s:completion.completion_start_column - 1 endif - return s:completion.candidates + return s:completion.completions endfunction diff --git a/python/ycm/client/completion_request.py b/python/ycm/client/completion_request.py index 5acefc24..4729571d 100644 --- a/python/ycm/client/completion_request.py +++ b/python/ycm/client/completion_request.py @@ -28,6 +28,7 @@ from ycmd.utils import ToUnicode from ycm.client.base_request import ( BaseRequest, DisplayServerException, MakeServerException ) from ycm import vimsupport +from ycm.vimsupport import NO_COMPLETIONS _logger = logging.getLogger( __name__ ) @@ -56,12 +57,12 @@ class CompletionRequest( BaseRequest ): def RawResponse( self ): if not self._response_future: - return { 'completions': [], 'completion_start_column': -1 } + return NO_COMPLETIONS response = self.HandleFuture( self._response_future, truncate_message = True ) if not response: - return { 'completions': [], 'completion_start_column': -1 } + return NO_COMPLETIONS # Vim may not be able to convert the 'errors' entry to its internal format # so we remove it from the response. @@ -71,6 +72,8 @@ class CompletionRequest( BaseRequest ): _logger.error( exception ) DisplayServerException( exception, truncate_message = True ) + response[ 'line' ] = self.request_data[ 'line_num' ] + response[ 'column' ] = self.request_data[ 'column_num' ] return response diff --git a/python/ycm/client/omni_completion_request.py b/python/ycm/client/omni_completion_request.py index 35b639ca..d27243cc 100644 --- a/python/ycm/client/omni_completion_request.py +++ b/python/ycm/client/omni_completion_request.py @@ -41,15 +41,19 @@ class OmniCompletionRequest( CompletionRequest ): def RawResponse( self ): return { - 'completions': _ConvertVimDatasToCompletionDatas( self._results ), - 'completion_start_column': self.request_data[ 'start_column' ] + 'line': self.request_data[ 'line_num' ], + 'column': self.request_data[ 'column_num' ], + 'completion_start_column': self.request_data[ 'start_column' ], + 'completions': _ConvertVimDatasToCompletionDatas( self._results ) } def Response( self ): return { - 'completions': self._results, - 'completion_start_column': self.request_data[ 'start_column' ] + 'line': self.request_data[ 'line_num' ], + 'column': self.request_data[ 'column_num' ], + 'completion_start_column': self.request_data[ 'start_column' ], + 'completions': self._results } diff --git a/python/ycm/tests/client/omni_completion_request_tests.py b/python/ycm/tests/client/omni_completion_request_tests.py index aba42412..d4ead04d 100644 --- a/python/ycm/tests/client/omni_completion_request_tests.py +++ b/python/ycm/tests/client/omni_completion_request_tests.py @@ -34,6 +34,8 @@ def BuildOmnicompletionRequest( results, start_column = 1 ): omni_completer.ComputeCandidates = MagicMock( return_value = results ) request_data = { + 'line_num': 1, + 'column_num': 1, 'start_column': start_column } request = OmniCompletionRequest( omni_completer, request_data ) @@ -53,8 +55,10 @@ def Response_FromOmniCompleter_test(): request = BuildOmnicompletionRequest( results ) eq_( request.Response(), { - 'completions': results, - 'completion_start_column': 1 + 'line': 1, + 'column': 1, + 'completion_start_column': 1, + 'completions': results } ) diff --git a/python/ycm/vimsupport.py b/python/ycm/vimsupport.py index 03c0f7f4..70ab6c3d 100644 --- a/python/ycm/vimsupport.py +++ b/python/ycm/vimsupport.py @@ -59,6 +59,13 @@ SIGN_ID_FOR_BUFFER = defaultdict( lambda: SIGN_BUFFER_ID_INITIAL_VALUE ) SIGN_PLACE_REGEX = re.compile( r"^.*=(?P\d+).*=(?P\d+).*=(?PYcm\w+)$" ) +NO_COMPLETIONS = { + 'line': -1, + 'column': -1, + 'completion_start_column': -1, + 'completions': [] +} + def CurrentLineAndColumn(): """Returns the 0-based current line and 0-based current column."""