diff --git a/autoload/youcompleteme.vim b/autoload/youcompleteme.vim index 9290a0b7..550151f9 100644 --- a/autoload/youcompleteme.vim +++ b/autoload/youcompleteme.vim @@ -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 diff --git a/python/ycm/client/base_request.py b/python/ycm/client/base_request.py index 84b95e81..4c46a5d9 100644 --- a/python/ycm/client/base_request.py +++ b/python/ycm/client/base_request.py @@ -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 ] ) diff --git a/python/ycm/client/command_request.py b/python/ycm/client/command_request.py index d3fe5ad3..0efbbae7 100644 --- a/python/ycm/client/command_request.py +++ b/python/ycm/client/command_request.py @@ -18,7 +18,6 @@ # along with YouCompleteMe. If not, see . 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() diff --git a/python/ycm/client/completion_request.py b/python/ycm/client/completion_request.py index 076f26ac..a7c8541e 100644 --- a/python/ycm/client/completion_request.py +++ b/python/ycm/client/completion_request.py @@ -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 [] diff --git a/python/ycm/client/event_notification.py b/python/ycm/client/event_notification.py index 4aa7825a..62c0fa65 100644 --- a/python/ycm/client/event_notification.py +++ b/python/ycm/client/event_notification.py @@ -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() ) diff --git a/python/ycm/server/ycmd.py b/python/ycm/server/ycmd.py index 098e95d1..fb290c8b 100755 --- a/python/ycm/server/ycmd.py +++ b/python/ycm/server/ycmd.py @@ -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 diff --git a/python/ycm/utils.py b/python/ycm/utils.py index 9f1ae775..f861947b 100644 --- a/python/ycm/utils.py +++ b/python/ycm/utils.py @@ -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 ) ) ) + + diff --git a/python/ycm/youcompleteme.py b/python/ycm/youcompleteme.py index 78ee83cf..7777f390 100644 --- a/python/ycm/youcompleteme.py +++ b/python/ycm/youcompleteme.py @@ -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 )