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 BufRead,FileType * call s:OnBufferRead()
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 InsertLeave * call s:OnInsertLeave()
autocmd InsertEnter * call s:OnInsertEnter()
@ -323,10 +323,12 @@ function! s:TurnOffSyntasticForCFamily()
endfunction
function! s:AllowedToCompleteInCurrentBuffer()
if empty( &filetype ) ||
\ getbufvar( winbufnr( winnr() ), "&buftype" ) ==# 'nofile' ||
\ &filetype ==# 'qf'
function! s:AllowedToCompleteInBuffer( buffer )
let buffer_filetype = getbufvar( a:buffer, '&filetype' )
if empty( buffer_filetype ) ||
\ getbufvar( a:buffer, '&buftype' ) ==# 'nofile' ||
\ buffer_filetype ==# 'qf'
return 0
endif
@ -335,13 +337,18 @@ function! s:AllowedToCompleteInCurrentBuffer()
endif
let whitelist_allows = has_key( g:ycm_filetype_whitelist, '*' ) ||
\ has_key( g:ycm_filetype_whitelist, &filetype )
let blacklist_allows = !has_key( g:ycm_filetype_blacklist, &filetype )
\ has_key( g:ycm_filetype_whitelist, buffer_filetype )
let blacklist_allows = !has_key( g:ycm_filetype_blacklist, buffer_filetype )
return whitelist_allows && blacklist_allows
endfunction
function! s:AllowedToCompleteInCurrentBuffer()
return s:AllowedToCompleteInBuffer( '%' )
endfunction
function! s:VisitedBufferRequiresReparse()
if !s:AllowedToCompleteInCurrentBuffer()
return 0
@ -471,13 +478,16 @@ function! s:OnBufferEnter()
endfunction
function! s:OnBufferUnload( deleted_buffer_file )
if !s:AllowedToCompleteInCurrentBuffer() || empty( a:deleted_buffer_file )
function! s:OnBufferUnload()
" 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
endif
let deleted_buffer_file = expand( '<afile>:p' )
exec s:python_command "ycm_state.OnBufferUnload("
\ "vim.eval( 'a:deleted_buffer_file' ) )"
\ "vim.eval( 'deleted_buffer_file' ) )"
endfunction

View File

@ -154,19 +154,28 @@ class BaseRequest( object ):
hmac_secret = ''
def BuildRequestData( include_buffer_data = True ):
line, column = vimsupport.CurrentLineAndColumn()
filepath = vimsupport.GetCurrentBufferFilepath()
request_data = {
'line_num': line + 1,
'column_num': column + 1,
'filepath': filepath
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 )
}
if include_buffer_data:
request_data[ 'file_data' ] = vimsupport.GetUnsavedAndCurrentBufferData()
line, column = vimsupport.CurrentLineAndColumn()
return request_data
return {
'filepath': current_filepath,
'line_num': line + 1,
'column_num': column + 1,
'file_data': vimsupport.GetUnsavedAndSpecifiedBufferData( current_filepath )
}
def JsonFromFuture( future ):

View File

@ -32,15 +32,16 @@ from ycm.client.base_request import ( BaseRequest, BuildRequestData,
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__()
self._event_name = event_name
self._filepath = filepath
self._extra_data = extra_data
self._cached_response = None
def Start( self ):
request_data = BuildRequestData()
request_data = BuildRequestData( self._filepath )
if self._extra_data:
request_data.update( self._extra_data )
request_data[ 'event_name' ] = self._event_name
@ -74,8 +75,10 @@ class EventNotification( BaseRequest ):
return self._cached_response if self._cached_response else []
def SendEventNotificationAsync( event_name, extra_data = None ):
event = EventNotification( event_name, extra_data )
def SendEventNotificationAsync( event_name,
filepath = None,
extra_data = None ):
event = EventNotification( event_name, filepath, extra_data )
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.FiletypesForBuffer', side_effect = [ [ 'cpp' ] ] )
def GetUnsavedAndCurrentBufferData_EncodedUnicodeCharsInBuffers_test( *args ):
def GetUnsavedAndSpecifiedBufferData_EncodedUnicodeCharsInBuffers_test( *args ):
filepath = os.path.realpath( 'filename' )
mock_buffer = MagicMock()
mock_buffer.name = os.path.realpath( 'filename' )
mock_buffer.name = filepath
mock_buffer.number = 1
mock_buffer.__iter__.return_value = [ ToBytes ( u'abc' ), ToBytes( u'fДa' ) ]
with patch( 'vim.buffers', [ mock_buffer ] ):
assert_that( vimsupport.GetUnsavedAndCurrentBufferData(),
has_entry( mock_buffer.name,
assert_that( vimsupport.GetUnsavedAndSpecifiedBufferData( filepath ),
has_entry( filepath,
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' ) ) )
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 = {}
for buffer_object in vim.buffers:
buffer_filepath = GetBufferFilepath( buffer_object )
if not ( BufferModified( buffer_object ) or
buffer_object == vim.current.buffer ):
buffer_filepath == including_filepath ):
continue
buffers_data[ GetBufferFilepath( buffer_object ) ] = {
buffers_data[ buffer_filepath ] = {
# Add a newline to match what gets saved to disk. See #1455 for details.
'contents': JoinLinesAsUnicode( buffer_object ) + '\n',
'filetypes': FiletypesForBuffer( buffer_object )

View File

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