Fix BufferUnload event notification

Send the request as the unloaded buffer instead of the current buffer
for the BufferUnload event notification. This fixes the issue where
the filetype of the current buffer is not the same as the unloaded
buffer one, making the ycmd server uses the wrong completer when
handling the request.
This commit is contained in:
micbou 2016-09-05 17:33:30 +02:00
parent b6d5af3424
commit 2fabac5a67
No known key found for this signature in database
GPG Key ID: C7E8FD1F3BDA1E05
6 changed files with 64 additions and 35 deletions

View File

@ -91,7 +91,7 @@ function! youcompleteme#Enable()
autocmd BufReadPre * call s:OnBufferReadPre( expand( '<afile>:p' ) ) autocmd BufReadPre * call s:OnBufferReadPre( expand( '<afile>:p' ) )
autocmd BufRead,FileType * call s:OnBufferRead() autocmd BufRead,FileType * call s:OnBufferRead()
autocmd BufEnter * call s:OnBufferEnter() autocmd BufEnter * call s:OnBufferEnter()
autocmd BufUnload * call s:OnBufferUnload( expand( '<afile>:p' ) ) autocmd BufUnload * call s:OnBufferUnload()
autocmd CursorHold,CursorHoldI * call s:OnCursorHold() autocmd CursorHold,CursorHoldI * call s:OnCursorHold()
autocmd InsertLeave * call s:OnInsertLeave() autocmd InsertLeave * call s:OnInsertLeave()
autocmd InsertEnter * call s:OnInsertEnter() autocmd InsertEnter * call s:OnInsertEnter()
@ -323,10 +323,12 @@ function! s:TurnOffSyntasticForCFamily()
endfunction endfunction
function! s:AllowedToCompleteInCurrentBuffer() function! s:AllowedToCompleteInBuffer( buffer )
if empty( &filetype ) || let buffer_filetype = getbufvar( a:buffer, '&filetype' )
\ getbufvar( winbufnr( winnr() ), "&buftype" ) ==# 'nofile' ||
\ &filetype ==# 'qf' if empty( buffer_filetype ) ||
\ getbufvar( a:buffer, '&buftype' ) ==# 'nofile' ||
\ buffer_filetype ==# 'qf'
return 0 return 0
endif endif
@ -335,13 +337,18 @@ function! s:AllowedToCompleteInCurrentBuffer()
endif endif
let whitelist_allows = has_key( g:ycm_filetype_whitelist, '*' ) || let whitelist_allows = has_key( g:ycm_filetype_whitelist, '*' ) ||
\ has_key( g:ycm_filetype_whitelist, &filetype ) \ has_key( g:ycm_filetype_whitelist, buffer_filetype )
let blacklist_allows = !has_key( g:ycm_filetype_blacklist, &filetype ) let blacklist_allows = !has_key( g:ycm_filetype_blacklist, buffer_filetype )
return whitelist_allows && blacklist_allows return whitelist_allows && blacklist_allows
endfunction endfunction
function! s:AllowedToCompleteInCurrentBuffer()
return s:AllowedToCompleteInBuffer( '%' )
endfunction
function! s:VisitedBufferRequiresReparse() function! s:VisitedBufferRequiresReparse()
if !s:AllowedToCompleteInCurrentBuffer() if !s:AllowedToCompleteInCurrentBuffer()
return 0 return 0
@ -471,13 +478,16 @@ function! s:OnBufferEnter()
endfunction endfunction
function! s:OnBufferUnload( deleted_buffer_file ) function! s:OnBufferUnload()
if !s:AllowedToCompleteInCurrentBuffer() || empty( a:deleted_buffer_file ) " Expanding <abuf> returns the unloaded buffer number as a string but we want
" it as a true number for the getbufvar function.
if !s:AllowedToCompleteInBuffer( str2nr( expand( '<abuf>' ) ) )
return return
endif endif
let deleted_buffer_file = expand( '<afile>:p' )
exec s:python_command "ycm_state.OnBufferUnload(" exec s:python_command "ycm_state.OnBufferUnload("
\ "vim.eval( 'a:deleted_buffer_file' ) )" \ "vim.eval( 'deleted_buffer_file' ) )"
endfunction endfunction

View File

@ -154,20 +154,29 @@ class BaseRequest( object ):
hmac_secret = '' hmac_secret = ''
def BuildRequestData( include_buffer_data = True ): def BuildRequestData( filepath = None ):
"""Build request for the current buffer or the buffer corresponding to
|filepath| if specified."""
current_filepath = vimsupport.GetCurrentBufferFilepath()
if filepath and current_filepath != filepath:
# Cursor position is irrelevant when filepath is not the current buffer.
return {
'filepath': filepath,
'line_num': 1,
'column_num': 1,
'file_data': vimsupport.GetUnsavedAndSpecifiedBufferData( filepath )
}
line, column = vimsupport.CurrentLineAndColumn() line, column = vimsupport.CurrentLineAndColumn()
filepath = vimsupport.GetCurrentBufferFilepath()
request_data = { return {
'filepath': current_filepath,
'line_num': line + 1, 'line_num': line + 1,
'column_num': column + 1, 'column_num': column + 1,
'filepath': filepath 'file_data': vimsupport.GetUnsavedAndSpecifiedBufferData( current_filepath )
} }
if include_buffer_data:
request_data[ 'file_data' ] = vimsupport.GetUnsavedAndCurrentBufferData()
return request_data
def JsonFromFuture( future ): def JsonFromFuture( future ):
response = future.result() response = future.result()

View File

@ -32,15 +32,16 @@ from ycm.client.base_request import ( BaseRequest, BuildRequestData,
class EventNotification( BaseRequest ): class EventNotification( BaseRequest ):
def __init__( self, event_name, extra_data = None ): def __init__( self, event_name, filepath = None, extra_data = None ):
super( EventNotification, self ).__init__() super( EventNotification, self ).__init__()
self._event_name = event_name self._event_name = event_name
self._filepath = filepath
self._extra_data = extra_data self._extra_data = extra_data
self._cached_response = None self._cached_response = None
def Start( self ): def Start( self ):
request_data = BuildRequestData() request_data = BuildRequestData( self._filepath )
if self._extra_data: if self._extra_data:
request_data.update( self._extra_data ) request_data.update( self._extra_data )
request_data[ 'event_name' ] = self._event_name request_data[ 'event_name' ] = self._event_name
@ -74,8 +75,10 @@ class EventNotification( BaseRequest ):
return self._cached_response if self._cached_response else [] return self._cached_response if self._cached_response else []
def SendEventNotificationAsync( event_name, extra_data = None ): def SendEventNotificationAsync( event_name,
event = EventNotification( event_name, extra_data ) filepath = None,
extra_data = None ):
event = EventNotification( event_name, filepath, extra_data )
event.Start() event.Start()

View File

@ -1403,15 +1403,17 @@ def OpenFilename_test( vim_current, vim_command ):
@patch( 'ycm.vimsupport.BufferModified', side_effect = [ True ] ) @patch( 'ycm.vimsupport.BufferModified', side_effect = [ True ] )
@patch( 'ycm.vimsupport.FiletypesForBuffer', side_effect = [ [ 'cpp' ] ] ) @patch( 'ycm.vimsupport.FiletypesForBuffer', side_effect = [ [ 'cpp' ] ] )
def GetUnsavedAndCurrentBufferData_EncodedUnicodeCharsInBuffers_test( *args ): def GetUnsavedAndSpecifiedBufferData_EncodedUnicodeCharsInBuffers_test( *args ):
filepath = os.path.realpath( 'filename' )
mock_buffer = MagicMock() mock_buffer = MagicMock()
mock_buffer.name = os.path.realpath( 'filename' ) mock_buffer.name = filepath
mock_buffer.number = 1 mock_buffer.number = 1
mock_buffer.__iter__.return_value = [ ToBytes ( u'abc' ), ToBytes( u'fДa' ) ] mock_buffer.__iter__.return_value = [ ToBytes ( u'abc' ), ToBytes( u'fДa' ) ]
with patch( 'vim.buffers', [ mock_buffer ] ): with patch( 'vim.buffers', [ mock_buffer ] ):
assert_that( vimsupport.GetUnsavedAndCurrentBufferData(), assert_that( vimsupport.GetUnsavedAndSpecifiedBufferData( filepath ),
has_entry( mock_buffer.name, has_entry( filepath,
has_entry( u'contents', u'abc\nfДa\n' ) ) ) has_entry( u'contents', u'abc\nfДa\n' ) ) )

View File

@ -116,14 +116,17 @@ def BufferModified( buffer_object ):
return bool( int( GetBufferOption( buffer_object, 'mod' ) ) ) return bool( int( GetBufferOption( buffer_object, 'mod' ) ) )
def GetUnsavedAndCurrentBufferData(): def GetUnsavedAndSpecifiedBufferData( including_filepath ):
"""Build part of the request containing the contents and filetypes of all
dirty buffers as well as the buffer with filepath |including_filepath|."""
buffers_data = {} buffers_data = {}
for buffer_object in vim.buffers: for buffer_object in vim.buffers:
buffer_filepath = GetBufferFilepath( buffer_object )
if not ( BufferModified( buffer_object ) or if not ( BufferModified( buffer_object ) or
buffer_object == vim.current.buffer ): buffer_filepath == including_filepath ):
continue continue
buffers_data[ GetBufferFilepath( buffer_object ) ] = { buffers_data[ buffer_filepath ] = {
# Add a newline to match what gets saved to disk. See #1455 for details. # Add a newline to match what gets saved to disk. See #1455 for details.
'contents': JoinLinesAsUnicode( buffer_object ) + '\n', 'contents': JoinLinesAsUnicode( buffer_object ) + '\n',
'filetypes': FiletypesForBuffer( buffer_object ) 'filetypes': FiletypesForBuffer( buffer_object )

View File

@ -311,16 +311,18 @@ class YouCompleteMe( object ):
self._AddSyntaxDataIfNeeded( extra_data ) self._AddSyntaxDataIfNeeded( extra_data )
self._AddExtraConfDataIfNeeded( extra_data ) self._AddExtraConfDataIfNeeded( extra_data )
self._latest_file_parse_request = EventNotification( 'FileReadyToParse', self._latest_file_parse_request = EventNotification(
extra_data ) 'FileReadyToParse', extra_data = extra_data )
self._latest_file_parse_request.Start() self._latest_file_parse_request.Start()
def OnBufferUnload( self, deleted_buffer_file ): def OnBufferUnload( self, deleted_buffer_file ):
if not self.IsServerAlive(): if not self.IsServerAlive():
return return
SendEventNotificationAsync( 'BufferUnload', SendEventNotificationAsync(
{ 'unloaded_buffer': deleted_buffer_file } ) 'BufferUnload',
filepath = deleted_buffer_file,
extra_data = { 'unloaded_buffer': deleted_buffer_file } )
def OnBufferVisit( self ): def OnBufferVisit( self ):
@ -328,7 +330,7 @@ class YouCompleteMe( object ):
return return
extra_data = {} extra_data = {}
self._AddUltiSnipsDataIfNeeded( extra_data ) self._AddUltiSnipsDataIfNeeded( extra_data )
SendEventNotificationAsync( 'BufferVisit', extra_data ) SendEventNotificationAsync( 'BufferVisit', extra_data = extra_data )
def OnInsertLeave( self ): def OnInsertLeave( self ):