Event and completion request are now async

This results in a much snappier Vim.
This commit is contained in:
Strahinja Val Markovic 2013-10-01 16:21:17 -07:00
parent e08dd4ab33
commit 9d0a6c96d7
8 changed files with 60 additions and 32 deletions

View File

@ -40,6 +40,8 @@ function! youcompleteme#Enable()
py import sys
py import vim
exe 'python sys.path.insert( 0, "' . s:script_folder_path . '/../python" )'
py from ycm import utils
py utils.AddThirdPartyFoldersToSysPath()
py from ycm import base
py from ycm import vimsupport
py from ycm import user_options_store
@ -497,13 +499,11 @@ python << EOF
def GetCompletions( query ):
request = ycm_state.GetCurrentCompletionRequest()
request.Start( query )
results_ready = False
while not results_ready:
results_ready = request.Done()
while not request.Done():
if bool( int( vim.eval( 'complete_check()' ) ) ):
return { 'words' : [], 'refresh' : 'always'}
results = base.AdjustCandidateInsertionText( request.Results() )
results = base.AdjustCandidateInsertionText( request.Response() )
return { 'words' : results, 'refresh' : 'always' }
EOF

View File

@ -20,6 +20,7 @@
import vim
import json
import requests
from requests_futures.sessions import FuturesSession
from ycm import vimsupport
HEADERS = {'content-type': 'application/json'}
@ -46,22 +47,22 @@ class BaseRequest( object ):
return {}
# This is the blocking version of the method. See below for async.
@staticmethod
def PostDataToHandler( data, handler ):
response = requests.post( _BuildUri( handler ),
data = json.dumps( data ),
headers = HEADERS )
if response.status_code == requests.codes.server_error:
raise ServerError( response.json()[ 'message' ] )
return JsonFromFuture( BaseRequest.PostDataToHandlerAsync( data,
handler ) )
# We let Requests handle the other status types, we only handle the 500
# error code.
response.raise_for_status()
if response.text:
return response.json()
return None
# This returns a future! Use JsonFromFuture to get the value.
@staticmethod
def PostDataToHandlerAsync( data, handler ):
return BaseRequest.session.post( _BuildUri( handler ),
data = json.dumps( data ),
headers = HEADERS )
session = FuturesSession( max_workers = 4 )
server_location = 'http://localhost:6666'
@ -83,6 +84,20 @@ def BuildRequestData( start_column = None, query = None ):
return request_data
def JsonFromFuture( future ):
response = future.result()
if response.status_code == requests.codes.server_error:
raise ServerError( response.json()[ 'message' ] )
# We let Requests handle the other status types, we only handle the 500
# error code.
response.raise_for_status()
if response.text:
return response.json()
return None
def _BuildUri( handler ):
return ''.join( [ BaseRequest.server_location, '/', handler ] )

View File

@ -18,7 +18,6 @@
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
import vim
import time
from ycm.client.base_request import BaseRequest, BuildRequestData, ServerError
from ycm import vimsupport
from ycm.utils import ToUtf8IfNeeded
@ -70,10 +69,8 @@ class CommandRequest( BaseRequest ):
def SendCommandRequest( arguments, completer ):
request = CommandRequest( arguments, completer )
# This is a blocking call.
request.Start()
while not request.Done():
time.sleep( 0.1 )
request.RunPostCommandActionsIfNeeded()
return request.Response()

View File

@ -19,7 +19,8 @@
from ycm import base
from ycm import vimsupport
from ycm.client.base_request import BaseRequest, BuildRequestData
from ycm.client.base_request import ( BaseRequest, BuildRequestData,
JsonFromFuture )
class CompletionRequest( BaseRequest ):
@ -30,27 +31,26 @@ class CompletionRequest( BaseRequest ):
self._request_data = BuildRequestData( self._completion_start_column )
# TODO: Do we need this anymore?
# def ShouldComplete( self ):
# return ( self._do_filetype_completion or
# self._ycm_state.ShouldUseGeneralCompleter( self._request_data ) )
def CompletionStartColumn( self ):
return self._completion_start_column
def Start( self, query ):
self._request_data[ 'query' ] = query
self._response = self.PostDataToHandler( self._request_data,
'get_completions' )
self._response_future = self.PostDataToHandlerAsync( self._request_data,
'get_completions' )
def Results( self ):
if not self._response:
def Done( self ):
return self._response_future.done()
def Response( self ):
if not self._response_future:
return []
try:
return [ _ConvertCompletionDataToVimData( x ) for x in self._response ]
return [ _ConvertCompletionDataToVimData( x )
for x in JsonFromFuture( self._response_future ) ]
except Exception as e:
vimsupport.PostVimMessage( str( e ) )
return []

View File

@ -41,7 +41,7 @@ class EventNotification( BaseRequest ):
# quietly to the Vim message log because nothing bad will happen if the
# server misses some events and we don't want to annoy the user.
try:
self.PostDataToHandler( request_data, 'event_notification' )
self.PostDataToHandlerAsync( request_data, 'event_notification' )
except:
vimsupport.EchoText( traceback.format_exc() )

View File

@ -30,6 +30,9 @@ sys.path.insert( 0, os.path.join(
os.path.dirname( os.path.abspath( __file__ ) ),
'../..' ) )
from ycm import utils
utils.AddThirdPartyFoldersToSysPath()
import logging
import json
import bottle

View File

@ -52,3 +52,15 @@ def TerminateProcess( pid ):
ctypes.windll.kernel32.CloseHandle( handle )
else:
os.kill( pid, signal.SIGTERM )
def AddThirdPartyFoldersToSysPath():
path_to_third_party = os.path.join(
os.path.dirname( os.path.abspath( __file__ ) ),
'../../third_party' )
for folder in os.listdir( path_to_third_party ):
sys.path.insert( 0, os.path.realpath( os.path.join( path_to_third_party,
folder ) ) )

View File

@ -165,6 +165,7 @@ class YouCompleteMe( object ):
def OnVimLeave( self ):
# TODO: There should be a faster way of shutting down the server
self._server_popen.terminate()
os.remove( self._temp_options_filename )