Optimize request building
Reduce the time spent to build the request when there are a lot of buffers by: - using the options property on the buffer object to get the mod variable instead of evaluating getbufvar; - not computing the buffer filepath if the buffer is not modified; - passing the number of the unloaded buffer instead of its filepath on the BufferUnload event. Getting the Python buffer object from its number is easier than from its filepath.
This commit is contained in:
parent
ad41dfb8af
commit
43ebd5252d
@ -1,4 +1,4 @@
|
||||
" Copyright (C) 2011, 2012 Google Inc.
|
||||
" Copyright (C) 2011-2018 YouCompleteMe contributors
|
||||
"
|
||||
" This file is part of YouCompleteMe.
|
||||
"
|
||||
@ -475,13 +475,12 @@ endfunction
|
||||
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>' ) ) )
|
||||
let buffer_number = str2nr( expand( '<abuf>' ) )
|
||||
if !s:AllowedToCompleteInBuffer( buffer_number )
|
||||
return
|
||||
endif
|
||||
|
||||
let deleted_buffer_file = expand( '<afile>:p' )
|
||||
exec s:python_command "ycm_state.OnBufferUnload(" .
|
||||
\ "vim.eval( 'deleted_buffer_file' ) )"
|
||||
exec s:python_command "ycm_state.OnBufferUnload( " . buffer_number . " )"
|
||||
endfunction
|
||||
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2013 Google Inc.
|
||||
# Copyright (C) 2013-2018 YouCompleteMe contributors
|
||||
#
|
||||
# This file is part of YouCompleteMe.
|
||||
#
|
||||
@ -25,6 +25,7 @@ from builtins import * # noqa
|
||||
import contextlib
|
||||
import logging
|
||||
import json
|
||||
import vim
|
||||
from future.utils import native
|
||||
from base64 import b64decode, b64encode
|
||||
from ycm import vimsupport
|
||||
@ -152,22 +153,26 @@ class BaseRequest( object ):
|
||||
hmac_secret = ''
|
||||
|
||||
|
||||
def BuildRequestData( filepath = None ):
|
||||
"""Build request for the current buffer or the buffer corresponding to
|
||||
|filepath| if specified."""
|
||||
current_filepath = vimsupport.GetCurrentBufferFilepath()
|
||||
def BuildRequestData( buffer_number = None ):
|
||||
"""Build request for the current buffer or the buffer with number
|
||||
|buffer_number| if specified."""
|
||||
working_dir = GetCurrentDirectory()
|
||||
current_buffer = vim.current.buffer
|
||||
|
||||
if filepath and current_filepath != filepath:
|
||||
if buffer_number and current_buffer.number != buffer_number:
|
||||
# Cursor position is irrelevant when filepath is not the current buffer.
|
||||
buffer_object = vim.buffers[ buffer_number ]
|
||||
filepath = vimsupport.GetBufferFilepath( buffer_object )
|
||||
return {
|
||||
'filepath': filepath,
|
||||
'line_num': 1,
|
||||
'column_num': 1,
|
||||
'working_dir': working_dir,
|
||||
'file_data': vimsupport.GetUnsavedAndSpecifiedBufferData( filepath )
|
||||
'file_data': vimsupport.GetUnsavedAndSpecifiedBufferData( buffer_object,
|
||||
filepath )
|
||||
}
|
||||
|
||||
current_filepath = vimsupport.GetBufferFilepath( current_buffer )
|
||||
line, column = vimsupport.CurrentLineAndColumn()
|
||||
|
||||
return {
|
||||
@ -175,7 +180,8 @@ def BuildRequestData( filepath = None ):
|
||||
'line_num': line + 1,
|
||||
'column_num': column + 1,
|
||||
'working_dir': working_dir,
|
||||
'file_data': vimsupport.GetUnsavedAndSpecifiedBufferData( current_filepath )
|
||||
'file_data': vimsupport.GetUnsavedAndSpecifiedBufferData( current_buffer,
|
||||
current_filepath )
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2013 Google Inc.
|
||||
# Copyright (C) 2013-2018 YouCompleteMe contributors
|
||||
#
|
||||
# This file is part of YouCompleteMe.
|
||||
#
|
||||
@ -27,17 +27,17 @@ from ycm.client.base_request import ( BaseRequest, BuildRequestData,
|
||||
|
||||
|
||||
class EventNotification( BaseRequest ):
|
||||
def __init__( self, event_name, filepath = None, extra_data = None ):
|
||||
def __init__( self, event_name, buffer_number = None, extra_data = None ):
|
||||
super( EventNotification, self ).__init__()
|
||||
self._event_name = event_name
|
||||
self._filepath = filepath
|
||||
self._buffer_number = buffer_number
|
||||
self._extra_data = extra_data
|
||||
self._response_future = None
|
||||
self._cached_response = None
|
||||
|
||||
|
||||
def Start( self ):
|
||||
request_data = BuildRequestData( self._filepath )
|
||||
request_data = BuildRequestData( self._buffer_number )
|
||||
if self._extra_data:
|
||||
request_data.update( self._extra_data )
|
||||
request_data[ 'event_name' ] = self._event_name
|
||||
@ -64,7 +64,7 @@ class EventNotification( BaseRequest ):
|
||||
|
||||
|
||||
def SendEventNotificationAsync( event_name,
|
||||
filepath = None,
|
||||
buffer_number = None,
|
||||
extra_data = None ):
|
||||
event = EventNotification( event_name, filepath, extra_data )
|
||||
event = EventNotification( event_name, buffer_number, extra_data )
|
||||
event.Start()
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2017 YouCompleteMe Contributors
|
||||
# Copyright (C) 2017-2018 YouCompleteMe Contributors
|
||||
#
|
||||
# This file is part of YouCompleteMe.
|
||||
#
|
||||
@ -22,7 +22,7 @@ from __future__ import absolute_import
|
||||
# Not installing aliases from python-future; it's unreliable and slow.
|
||||
from builtins import * # noqa
|
||||
|
||||
from ycm.tests.test_utils import MockVimModule
|
||||
from ycm.tests.test_utils import MockVimBuffers, MockVimModule, VimBuffer
|
||||
MockVimModule()
|
||||
|
||||
from hamcrest import assert_that, has_entry
|
||||
@ -32,14 +32,16 @@ from ycm.client.base_request import BuildRequestData
|
||||
|
||||
@patch( 'ycm.client.base_request.GetCurrentDirectory',
|
||||
return_value = '/some/dir' )
|
||||
@patch( 'ycm.vimsupport.CurrentLineAndColumn', return_value = ( 1, 1 ) )
|
||||
def BuildRequestData_AddWorkingDir_test( *args ):
|
||||
current_buffer = VimBuffer( 'foo' )
|
||||
with MockVimBuffers( [ current_buffer ], current_buffer, ( 1, 1 ) ):
|
||||
assert_that( BuildRequestData(), has_entry( 'working_dir', '/some/dir' ) )
|
||||
|
||||
|
||||
@patch( 'ycm.client.base_request.GetCurrentDirectory',
|
||||
return_value = '/some/dir' )
|
||||
@patch( 'ycm.vimsupport.CurrentLineAndColumn', return_value = ( 1, 1 ) )
|
||||
def BuildRequestData_AddWorkingDirWithFileName_test( *args ):
|
||||
assert_that( BuildRequestData( 'foo' ),
|
||||
current_buffer = VimBuffer( 'foo' )
|
||||
with MockVimBuffers( [ current_buffer ], current_buffer, ( 1, 1 ) ):
|
||||
assert_that( BuildRequestData( current_buffer.number ),
|
||||
has_entry( 'working_dir', '/some/dir' ) )
|
||||
|
@ -1,6 +1,6 @@
|
||||
# coding: utf-8
|
||||
#
|
||||
# Copyright (C) 2015-2016 YouCompleteMe contributors
|
||||
# Copyright (C) 2015-2018 YouCompleteMe contributors
|
||||
#
|
||||
# This file is part of YouCompleteMe.
|
||||
#
|
||||
@ -25,8 +25,7 @@ from __future__ import absolute_import
|
||||
from builtins import * # noqa
|
||||
|
||||
from ycm.tests.test_utils import ( CurrentWorkingDirectory, ExtendedMock,
|
||||
MockVimBuffers, MockVimModule, VimBuffer,
|
||||
ToBytesOnPY2 )
|
||||
MockVimBuffers, MockVimModule, VimBuffer )
|
||||
MockVimModule()
|
||||
|
||||
import contextlib
|
||||
@ -398,7 +397,6 @@ def EventNotification_BufferVisit_BuildRequestForCurrentAndUnsavedBuffers_test(
|
||||
contents = [ 'current_buffer_contents' ],
|
||||
filetype = 'some_filetype',
|
||||
modified = False )
|
||||
|
||||
modified_buffer_file = os.path.realpath( 'modified_buffer' )
|
||||
modified_buffer = VimBuffer( name = modified_buffer_file,
|
||||
number = 2,
|
||||
@ -465,7 +463,7 @@ def EventNotification_BufferUnload_BuildRequestForDeletedAndUnsavedBuffers_test(
|
||||
with patch( 'ycm.client.event_notification.EventNotification.'
|
||||
'PostDataToHandlerAsync' ) as post_data_to_handler_async:
|
||||
with MockVimBuffers( [ current_buffer, deleted_buffer ], current_buffer ):
|
||||
ycm.OnBufferUnload( ToBytesOnPY2( deleted_buffer_file ) )
|
||||
ycm.OnBufferUnload( deleted_buffer.number )
|
||||
|
||||
assert_that(
|
||||
# Positional arguments passed to PostDataToHandlerAsync.
|
||||
|
@ -1,5 +1,4 @@
|
||||
# Copyright (C) 2011-2012 Google Inc.
|
||||
# 2016 YouCompleteMe contributors
|
||||
# Copyright (C) 2011-2018 YouCompleteMe contributors
|
||||
#
|
||||
# This file is part of YouCompleteMe.
|
||||
#
|
||||
@ -272,6 +271,10 @@ class VimBuffer( object ):
|
||||
self.omnifunc = omnifunc
|
||||
self.omnifunc_name = omnifunc.__name__ if omnifunc else ''
|
||||
self.changedtick = 1
|
||||
self.options = {
|
||||
'mod': modified,
|
||||
'bh': bufhidden
|
||||
}
|
||||
|
||||
|
||||
def __getitem__( self, index ):
|
||||
@ -292,6 +295,27 @@ class VimBuffer( object ):
|
||||
return [ ToUnicode( x ) for x in self.contents ]
|
||||
|
||||
|
||||
class VimBuffers( object ):
|
||||
"""An object that looks like a vim.buffers object."""
|
||||
|
||||
def __init__( self, *buffers ):
|
||||
"""Arguments are VimBuffer objects."""
|
||||
self._buffers = buffers
|
||||
|
||||
|
||||
def __getitem__( self, number ):
|
||||
"""Emulates vim.buffers[ number ]"""
|
||||
for buffer_object in self._buffers:
|
||||
if number == buffer_object.number:
|
||||
return buffer_object
|
||||
raise KeyError( number )
|
||||
|
||||
|
||||
def __iter__( self ):
|
||||
"""Emulates for loop on vim.buffers"""
|
||||
return iter( self._buffers )
|
||||
|
||||
|
||||
class VimMatch( object ):
|
||||
|
||||
def __init__( self, group, pattern ):
|
||||
@ -326,7 +350,7 @@ def MockVimBuffers( buffers, current_buffer, cursor_position = ( 1, 1 ) ):
|
||||
|
||||
line = current_buffer.contents[ cursor_position[ 0 ] - 1 ]
|
||||
|
||||
with patch( 'vim.buffers', buffers ):
|
||||
with patch( 'vim.buffers', VimBuffers( *buffers ) ):
|
||||
with patch( 'vim.current.buffer', current_buffer ):
|
||||
with patch( 'vim.current.window.cursor', cursor_position ):
|
||||
with patch( 'vim.current.line', line ):
|
||||
|
@ -1,6 +1,6 @@
|
||||
# coding: utf-8
|
||||
#
|
||||
# Copyright (C) 2015-2016 YouCompleteMe contributors
|
||||
# Copyright (C) 2015-2018 YouCompleteMe contributors
|
||||
#
|
||||
# This file is part of YouCompleteMe.
|
||||
#
|
||||
@ -1434,7 +1434,8 @@ def GetUnsavedAndSpecifiedBufferData_EncodedUnicodeCharsInBuffers_test():
|
||||
vim_buffer = VimBuffer( filepath, contents = contents )
|
||||
|
||||
with patch( 'vim.buffers', [ vim_buffer ] ):
|
||||
assert_that( vimsupport.GetUnsavedAndSpecifiedBufferData( filepath ),
|
||||
assert_that( vimsupport.GetUnsavedAndSpecifiedBufferData( vim_buffer,
|
||||
filepath ),
|
||||
has_entry( filepath,
|
||||
has_entry( u'contents', u'abc\nfДa\n' ) ) )
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
# Copyright (C) 2011-2012 Google Inc.
|
||||
# 2016 YouCompleteMe contributors
|
||||
# Copyright (C) 2011-2018 YouCompleteMe contributors
|
||||
#
|
||||
# This file is part of YouCompleteMe.
|
||||
#
|
||||
@ -100,42 +99,34 @@ def TextBeforeCursor():
|
||||
return ToUnicode( vim.current.line[ :CurrentColumn() ] )
|
||||
|
||||
|
||||
# Note the difference between buffer OPTIONS and VARIABLES; the two are not
|
||||
# the same.
|
||||
def GetBufferOption( buffer_object, option ):
|
||||
# NOTE: We used to check for the 'options' property on the buffer_object which
|
||||
# is available in recent versions of Vim and would then use:
|
||||
#
|
||||
# buffer_object.options[ option ]
|
||||
#
|
||||
# to read the value, BUT this caused annoying flickering when the
|
||||
# buffer_object was a hidden buffer (with option = 'ft'). This was all due to
|
||||
# a Vim bug. Until this is fixed, we won't use it.
|
||||
|
||||
to_eval = 'getbufvar({0}, "&{1}")'.format( buffer_object.number, option )
|
||||
return GetVariableValue( to_eval )
|
||||
|
||||
|
||||
def BufferModified( buffer_object ):
|
||||
return bool( int( GetBufferOption( buffer_object, 'mod' ) ) )
|
||||
return buffer_object.options[ 'mod' ]
|
||||
|
||||
|
||||
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_filepath == including_filepath ):
|
||||
continue
|
||||
|
||||
buffers_data[ buffer_filepath ] = {
|
||||
def GetBufferData( buffer_object ):
|
||||
return {
|
||||
# Add a newline to match what gets saved to disk. See #1455 for details.
|
||||
'contents': JoinLinesAsUnicode( buffer_object ) + '\n',
|
||||
'filetypes': FiletypesForBuffer( buffer_object )
|
||||
}
|
||||
|
||||
|
||||
def GetUnsavedAndSpecifiedBufferData( included_buffer, included_filepath ):
|
||||
"""Build part of the request containing the contents and filetypes of all
|
||||
dirty buffers as well as the buffer |included_buffer| with its filepath
|
||||
|included_filepath|."""
|
||||
buffers_data = { included_filepath: GetBufferData( included_buffer ) }
|
||||
|
||||
for buffer_object in vim.buffers:
|
||||
if not BufferModified( buffer_object ):
|
||||
continue
|
||||
|
||||
filepath = GetBufferFilepath( buffer_object )
|
||||
if filepath in buffers_data:
|
||||
continue
|
||||
|
||||
buffers_data[ filepath ] = GetBufferData( buffer_object )
|
||||
|
||||
return buffers_data
|
||||
|
||||
|
||||
@ -362,7 +353,7 @@ def VimExpressionToPythonType( vim_expression ):
|
||||
|
||||
|
||||
def HiddenEnabled( buffer_object ):
|
||||
if GetBufferOption( buffer_object, 'bh' ) == "hide":
|
||||
if buffer_object.options[ 'bh' ] == "hide":
|
||||
return True
|
||||
return GetBoolValue( '&hidden' )
|
||||
|
||||
@ -601,7 +592,14 @@ def GetBufferFiletypes( bufnr ):
|
||||
def FiletypesForBuffer( buffer_object ):
|
||||
# NOTE: Getting &ft for other buffers only works when the buffer has been
|
||||
# visited by the user at least once, which is true for modified buffers
|
||||
return ToUnicode( GetBufferOption( buffer_object, 'ft' ) ).split( '.' )
|
||||
|
||||
# We don't use
|
||||
#
|
||||
# buffer_object.options[ 'ft' ]
|
||||
#
|
||||
# to get the filetypes because this causes annoying flickering when the buffer
|
||||
# is hidden.
|
||||
return GetBufferFiletypes( buffer_object.number )
|
||||
|
||||
|
||||
def VariableExists( variable ):
|
||||
|
@ -1,5 +1,4 @@
|
||||
# Copyright (C) 2011-2012 Google Inc.
|
||||
# 2016-2017 YouCompleteMe contributors
|
||||
# Copyright (C) 2011-2018 YouCompleteMe contributors
|
||||
#
|
||||
# This file is part of YouCompleteMe.
|
||||
#
|
||||
@ -381,10 +380,8 @@ class YouCompleteMe( object ):
|
||||
self.CurrentBuffer().SendParseRequest( extra_data )
|
||||
|
||||
|
||||
def OnBufferUnload( self, deleted_buffer_file ):
|
||||
SendEventNotificationAsync(
|
||||
'BufferUnload',
|
||||
filepath = utils.ToUnicode( deleted_buffer_file ) )
|
||||
def OnBufferUnload( self, deleted_buffer_number ):
|
||||
SendEventNotificationAsync( 'BufferUnload', deleted_buffer_number )
|
||||
|
||||
|
||||
def OnBufferVisit( self ):
|
||||
|
Loading…
Reference in New Issue
Block a user