Auto merge of #3192 - micbou:auto-wrap, r=micbou

[READY] Recompute starting column on auto-wrapping

When auto-wrapping is enabled (`t` or `c` are present in `formatoptions`), Vim wraps the current line after the completion request is sent but before calling the completefunc. The column where the completion starts is invalid in that case and must be recomputed.

Fixes #2789.

<!-- 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/3192)
<!-- Reviewable:end -->
This commit is contained in:
zzbot 2018-11-16 16:39:33 -08:00 committed by GitHub
commit b668ed6405
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 58 additions and 26 deletions

View File

@ -23,11 +23,9 @@ set cpo&vim
let s:script_folder_path = escape( expand( '<sfile>: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
" <c-x><c-u> invokes the user's completion function (which we have set to
" youcompleteme#CompleteFunc), and <c-p> 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 <c-p> 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 <c-p> 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( "\<C-X>\<C-U>\<C-P>" )
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.candidates
return s:completion.completion_start_column - 1
endif
return s:completion.completions
endfunction

View File

@ -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

View File

@ -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
}

View File

@ -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
} )

View File

@ -59,6 +59,13 @@ SIGN_ID_FOR_BUFFER = defaultdict( lambda: SIGN_BUFFER_ID_INITIAL_VALUE )
SIGN_PLACE_REGEX = re.compile(
r"^.*=(?P<line>\d+).*=(?P<id>\d+).*=(?P<name>Ycm\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."""