From 2fabac5a67be0ffc57180a65dbbd3fc49ed63af6 Mon Sep 17 00:00:00 2001 From: micbou Date: Mon, 5 Sep 2016 17:33:30 +0200 Subject: [PATCH] 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. --- autoload/youcompleteme.vim | 30 ++++++++++++++++--------- python/ycm/client/base_request.py | 27 ++++++++++++++-------- python/ycm/client/event_notification.py | 11 +++++---- python/ycm/tests/vimsupport_test.py | 10 +++++---- python/ycm/vimsupport.py | 9 +++++--- python/ycm/youcompleteme.py | 12 +++++----- 6 files changed, 64 insertions(+), 35 deletions(-) diff --git a/autoload/youcompleteme.vim b/autoload/youcompleteme.vim index 52d9f5af..5a69051d 100644 --- a/autoload/youcompleteme.vim +++ b/autoload/youcompleteme.vim @@ -91,7 +91,7 @@ function! youcompleteme#Enable() autocmd BufReadPre * call s:OnBufferReadPre( expand( ':p' ) ) autocmd BufRead,FileType * call s:OnBufferRead() autocmd BufEnter * call s:OnBufferEnter() - autocmd BufUnload * call s:OnBufferUnload( expand( ':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 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( '' ) ) ) return endif + let deleted_buffer_file = expand( ':p' ) exec s:python_command "ycm_state.OnBufferUnload(" - \ "vim.eval( 'a:deleted_buffer_file' ) )" + \ "vim.eval( 'deleted_buffer_file' ) )" endfunction diff --git a/python/ycm/client/base_request.py b/python/ycm/client/base_request.py index 97f4bf31..1fae1ea8 100644 --- a/python/ycm/client/base_request.py +++ b/python/ycm/client/base_request.py @@ -154,20 +154,29 @@ class BaseRequest( object ): 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() diff --git a/python/ycm/client/event_notification.py b/python/ycm/client/event_notification.py index b9a60d18..bd73f31f 100644 --- a/python/ycm/client/event_notification.py +++ b/python/ycm/client/event_notification.py @@ -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() diff --git a/python/ycm/tests/vimsupport_test.py b/python/ycm/tests/vimsupport_test.py index 785a948a..74af6bb0 100644 --- a/python/ycm/tests/vimsupport_test.py +++ b/python/ycm/tests/vimsupport_test.py @@ -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' ) ) ) diff --git a/python/ycm/vimsupport.py b/python/ycm/vimsupport.py index 068922d4..c50641e0 100644 --- a/python/ycm/vimsupport.py +++ b/python/ycm/vimsupport.py @@ -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 ) diff --git a/python/ycm/youcompleteme.py b/python/ycm/youcompleteme.py index 33a324ff..461fcb3a 100644 --- a/python/ycm/youcompleteme.py +++ b/python/ycm/youcompleteme.py @@ -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 ):