Skip to content

Commit

Permalink
Fix BufferUnload event notification
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
micbou committed Oct 8, 2016
1 parent b6d5af3 commit 2fabac5
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 35 deletions.
30 changes: 20 additions & 10 deletions autoload/youcompleteme.vim
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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

Expand All @@ -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
Expand Down Expand Up @@ -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


Expand Down
27 changes: 18 additions & 9 deletions python/ycm/client/base_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,20 +154,29 @@ def _ExtraHeaders( method, request_uri, request_body = None ):
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()
filepath = vimsupport.GetCurrentBufferFilepath()
request_data = {

return {
'filepath': current_filepath,
'line_num': line + 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 ):
response = future.result()
Expand Down
11 changes: 7 additions & 4 deletions python/ycm/client/event_notification.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,16 @@


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
Expand Down Expand Up @@ -74,8 +75,10 @@ def Response( self ):
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()


Expand Down
10 changes: 6 additions & 4 deletions python/ycm/tests/vimsupport_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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' ) ) )


Expand Down
9 changes: 6 additions & 3 deletions python/ycm/vimsupport.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 )
Expand Down
12 changes: 7 additions & 5 deletions python/ycm/youcompleteme.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,24 +311,26 @@ def OnFileReadyToParse( self ):
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 ):
if not self.IsServerAlive():
return
extra_data = {}
self._AddUltiSnipsDataIfNeeded( extra_data )
SendEventNotificationAsync( 'BufferVisit', extra_data )
SendEventNotificationAsync( 'BufferVisit', extra_data = extra_data )


def OnInsertLeave( self ):
Expand Down

0 comments on commit 2fabac5

Please sign in to comment.