diff options
| author | Karel Kočí <cynerd@email.cz> | 2016-06-30 16:03:25 +0200 | 
|---|---|---|
| committer | Karel Kočí <cynerd@email.cz> | 2016-06-30 16:03:25 +0200 | 
| commit | e573b3020c032400eed60b649a2cbf55266e6bb0 (patch) | |
| tree | 8f572394ac8433529c7a8e70d160a2fbe8268b4e /vim/bundle/YouCompleteMe/python/ycm/client | |
| parent | b8c667bd64b3edd38d56c63c5bd1db53a23b4499 (diff) | |
| download | myconfigs-e573b3020c032400eed60b649a2cbf55266e6bb0.tar.gz myconfigs-e573b3020c032400eed60b649a2cbf55266e6bb0.tar.bz2 myconfigs-e573b3020c032400eed60b649a2cbf55266e6bb0.zip | |
Add current configurations from old repository
Diffstat (limited to 'vim/bundle/YouCompleteMe/python/ycm/client')
12 files changed, 1328 insertions, 0 deletions
| diff --git a/vim/bundle/YouCompleteMe/python/ycm/client/__init__.py b/vim/bundle/YouCompleteMe/python/ycm/client/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/vim/bundle/YouCompleteMe/python/ycm/client/__init__.py diff --git a/vim/bundle/YouCompleteMe/python/ycm/client/base_request.py b/vim/bundle/YouCompleteMe/python/ycm/client/base_request.py new file mode 100644 index 0000000..ffc3fac --- /dev/null +++ b/vim/bundle/YouCompleteMe/python/ycm/client/base_request.py @@ -0,0 +1,244 @@ +# Copyright (C) 2013  Google Inc. +# +# This file is part of YouCompleteMe. +# +# YouCompleteMe is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# YouCompleteMe is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with YouCompleteMe.  If not, see <http://www.gnu.org/licenses/>. + +from __future__ import unicode_literals +from __future__ import print_function +from __future__ import division +from __future__ import absolute_import +from future import standard_library +standard_library.install_aliases() +from builtins import *  # noqa + +import requests +import urllib.parse +import json +from future.utils import native +from base64 import b64decode, b64encode +from retries import retries +from requests_futures.sessions import FuturesSession +from ycm.unsafe_thread_pool_executor import UnsafeThreadPoolExecutor +from ycm import vimsupport +from ycmd.utils import ToBytes +from ycmd.hmac_utils import CreateRequestHmac, CreateHmac, SecureBytesEqual +from ycmd.responses import ServerError, UnknownExtraConf + +_HEADERS = {'content-type': 'application/json'} +_EXECUTOR = UnsafeThreadPoolExecutor( max_workers = 30 ) +# Setting this to None seems to screw up the Requests/urllib3 libs. +_DEFAULT_TIMEOUT_SEC = 30 +_HMAC_HEADER = 'x-ycm-hmac' + + +class BaseRequest( object ): + +  def __init__( self ): +    pass + + +  def Start( self ): +    pass + + +  def Done( self ): +    return True + + +  def Response( self ): +    return {} + +  # This method blocks +  # |timeout| is num seconds to tolerate no response from server before giving +  # up; see Requests docs for details (we just pass the param along). +  @staticmethod +  def GetDataFromHandler( handler, timeout = _DEFAULT_TIMEOUT_SEC ): +    return JsonFromFuture( BaseRequest._TalkToHandlerAsync( '', +                                                            handler, +                                                            'GET', +                                                            timeout ) ) + + +  # This is the blocking version of the method. See below for async. +  # |timeout| is num seconds to tolerate no response from server before giving +  # up; see Requests docs for details (we just pass the param along). +  @staticmethod +  def PostDataToHandler( data, handler, timeout = _DEFAULT_TIMEOUT_SEC ): +    return JsonFromFuture( BaseRequest.PostDataToHandlerAsync( data, +                                                               handler, +                                                               timeout ) ) + + +  # This returns a future! Use JsonFromFuture to get the value. +  # |timeout| is num seconds to tolerate no response from server before giving +  # up; see Requests docs for details (we just pass the param along). +  @staticmethod +  def PostDataToHandlerAsync( data, handler, timeout = _DEFAULT_TIMEOUT_SEC ): +    return BaseRequest._TalkToHandlerAsync( data, handler, 'POST', timeout ) + + +  # This returns a future! Use JsonFromFuture to get the value. +  # |method| is either 'POST' or 'GET'. +  # |timeout| is num seconds to tolerate no response from server before giving +  # up; see Requests docs for details (we just pass the param along). +  @staticmethod +  def _TalkToHandlerAsync( data, +                           handler, +                           method, +                           timeout = _DEFAULT_TIMEOUT_SEC ): +    def SendRequest( data, handler, method, timeout ): +      request_uri = _BuildUri( handler ) +      if method == 'POST': +        sent_data = _ToUtf8Json( data ) +        return BaseRequest.session.post( +            request_uri, +            data = sent_data, +            headers = BaseRequest._ExtraHeaders( method, +                                                 request_uri, +                                                 sent_data ), +            timeout = timeout ) +      if method == 'GET': +        return BaseRequest.session.get( +            request_uri, +            headers = BaseRequest._ExtraHeaders( method, request_uri ), +            timeout = timeout ) + +    @retries( 5, delay = 0.5, backoff = 1.5 ) +    def DelayedSendRequest( data, handler, method ): +      request_uri = _BuildUri( handler ) +      if method == 'POST': +        sent_data = _ToUtf8Json( data ) +        return requests.post( +            request_uri, +            data = sent_data, +            headers = BaseRequest._ExtraHeaders( method, +                                                 request_uri, +                                                 sent_data ) ) +      if method == 'GET': +        return requests.get( +            request_uri, +            headers = BaseRequest._ExtraHeaders( method, request_uri ) ) + +    if not _CheckServerIsHealthyWithCache(): +      return _EXECUTOR.submit( DelayedSendRequest, data, handler, method ) + +    return SendRequest( data, handler, method, timeout ) + + +  @staticmethod +  def _ExtraHeaders( method, request_uri, request_body = None ): +    if not request_body: +      request_body = bytes( b'' ) +    headers = dict( _HEADERS ) +    headers[ _HMAC_HEADER ] = b64encode( +        CreateRequestHmac( ToBytes( method ), +                           ToBytes( urllib.parse.urlparse( request_uri ).path ), +                           request_body, +                           BaseRequest.hmac_secret ) ) +    return headers + +  session = FuturesSession( executor = _EXECUTOR ) +  server_location = '' +  hmac_secret = '' + + +def BuildRequestData( include_buffer_data = True ): +  line, column = vimsupport.CurrentLineAndColumn() +  filepath = vimsupport.GetCurrentBufferFilepath() +  request_data = { +    'line_num': line + 1, +    'column_num': column + 1, +    'filepath': filepath +  } + +  if include_buffer_data: +    request_data[ 'file_data' ] = vimsupport.GetUnsavedAndCurrentBufferData() + +  return request_data + + +def JsonFromFuture( future ): +  response = future.result() +  _ValidateResponseObject( response ) +  if response.status_code == requests.codes.server_error: +    raise MakeServerException( response.json() ) + +  # 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 HandleServerException( exception ): +  serialized_exception = str( exception ) + +  # We ignore the exception about the file already being parsed since it comes +  # up often and isn't something that's actionable by the user. +  if 'already being parsed' in serialized_exception: +    return +  vimsupport.PostMultiLineNotice( serialized_exception ) + + +def _ToUtf8Json( data ): +  return ToBytes( json.dumps( data ) if data else None ) + + +def _ValidateResponseObject( response ): +  our_hmac = CreateHmac( response.content, BaseRequest.hmac_secret ) +  their_hmac = ToBytes( b64decode( response.headers[ _HMAC_HEADER ] ) ) +  if not SecureBytesEqual( our_hmac, their_hmac ): +    raise RuntimeError( 'Received invalid HMAC for response!' ) +  return True + + +def _BuildUri( handler ): +  return native( ToBytes( urllib.parse.urljoin( BaseRequest.server_location, +                                                handler ) ) ) + + +SERVER_HEALTHY = False + + +def _CheckServerIsHealthyWithCache(): +  global SERVER_HEALTHY + +  def _ServerIsHealthy(): +    request_uri = _BuildUri( 'healthy' ) +    response = requests.get( request_uri, +                             headers = BaseRequest._ExtraHeaders( +                                 'GET', request_uri, bytes( b'' ) ) ) +    _ValidateResponseObject( response ) +    response.raise_for_status() +    return response.json() + +  if SERVER_HEALTHY: +    return True + +  try: +    SERVER_HEALTHY = _ServerIsHealthy() +    return SERVER_HEALTHY +  except: +    return False + + +def MakeServerException( data ): +  if data[ 'exception' ][ 'TYPE' ] == UnknownExtraConf.__name__: +    return UnknownExtraConf( data[ 'exception' ][ 'extra_conf_file' ] ) + +  return ServerError( '{0}: {1}'.format( data[ 'exception' ][ 'TYPE' ], +                                         data[ 'message' ] ) ) diff --git a/vim/bundle/YouCompleteMe/python/ycm/client/command_request.py b/vim/bundle/YouCompleteMe/python/ycm/client/command_request.py new file mode 100644 index 0000000..6c5bc66 --- /dev/null +++ b/vim/bundle/YouCompleteMe/python/ycm/client/command_request.py @@ -0,0 +1,153 @@ +# Copyright (C) 2013  Google Inc. +# +# This file is part of YouCompleteMe. +# +# YouCompleteMe is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# YouCompleteMe is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with YouCompleteMe.  If not, see <http://www.gnu.org/licenses/>. + +from __future__ import unicode_literals +from __future__ import print_function +from __future__ import division +from __future__ import absolute_import +from future import standard_library +standard_library.install_aliases() +from builtins import *  # noqa + +from requests.exceptions import ReadTimeout + +from ycmd.responses import ServerError +from ycm.client.base_request import ( BaseRequest, BuildRequestData, +                                      HandleServerException ) +from ycm import vimsupport +from ycmd.utils import ToUnicode + + +def _EnsureBackwardsCompatibility( arguments ): +  if arguments and arguments[ 0 ] == 'GoToDefinitionElseDeclaration': +    arguments[ 0 ] = 'GoTo' +  return arguments + + +class CommandRequest( BaseRequest ): +  def __init__( self, arguments, completer_target = None ): +    super( CommandRequest, self ).__init__() +    self._arguments = _EnsureBackwardsCompatibility( arguments ) +    self._completer_target = ( completer_target if completer_target +                               else 'filetype_default' ) +    self._response = None + + +  def Start( self ): +    request_data = BuildRequestData() +    request_data.update( { +      'completer_target': self._completer_target, +      'command_arguments': self._arguments +    } ) +    try: +      self._response = self.PostDataToHandler( request_data, +                                               'run_completer_command' ) +    except ( ServerError, ReadTimeout ) as e: +      HandleServerException( e ) + + +  def Response( self ): +    return self._response + + +  def RunPostCommandActionsIfNeeded( self ): +    if not self.Done() or self._response is None: +      return + +    # If not a dictionary or a list, the response is necessarily a +    # scalar: boolean, number, string, etc. In this case, we print +    # it to the user. +    if not isinstance( self._response, ( dict, list ) ): +      return self._HandleBasicResponse() + +    if 'fixits' in self._response: +      return self._HandleFixitResponse() + +    if 'message' in self._response: +      return self._HandleMessageResponse() + +    if 'detailed_info' in self._response: +      return self._HandleDetailedInfoResponse() + +    # The only other type of response we understand is GoTo, and that is the +    # only one that we can't detect just by inspecting the response (it should +    # either be a single location or a list) +    return self._HandleGotoResponse() + + +  def _HandleGotoResponse( self ): +    if isinstance( self._response, list ): +      vimsupport.SetQuickFixList( +        [ _BuildQfListItem( x ) for x in self._response ], +        focus = True, +        autoclose = True ) +    else: +      vimsupport.JumpToLocation( self._response[ 'filepath' ], +                                 self._response[ 'line_num' ], +                                 self._response[ 'column_num' ] ) + + +  def _HandleFixitResponse( self ): +    if not len( self._response[ 'fixits' ] ): +      vimsupport.EchoText( "No fixits found for current line" ) +    else: +      chunks = self._response[ 'fixits' ][ 0 ][ 'chunks' ] +      try: +        vimsupport.ReplaceChunks( chunks ) +      except RuntimeError as e: +        vimsupport.PostMultiLineNotice( str( e ) ) + + +  def _HandleBasicResponse( self ): +    vimsupport.EchoText( self._response ) + + +  def _HandleMessageResponse( self ): +    vimsupport.EchoText( self._response[ 'message' ] ) + + +  def _HandleDetailedInfoResponse( self ): +    vimsupport.WriteToPreviewWindow( self._response[ 'detailed_info' ] ) + + +def SendCommandRequest( arguments, completer ): +  request = CommandRequest( arguments, completer ) +  # This is a blocking call. +  request.Start() +  request.RunPostCommandActionsIfNeeded() +  return request.Response() + + +def _BuildQfListItem( goto_data_item ): +  qf_item = {} +  if 'filepath' in goto_data_item: +    qf_item[ 'filename' ] = ToUnicode( goto_data_item[ 'filepath' ] ) +  if 'description' in goto_data_item: +    qf_item[ 'text' ] = ToUnicode( goto_data_item[ 'description' ] ) +  if 'line_num' in goto_data_item: +    qf_item[ 'lnum' ] = goto_data_item[ 'line_num' ] +  if 'column_num' in goto_data_item: +    # ycmd returns columns 1-based, and QuickFix lists require "byte offsets". +    # See :help getqflist and equivalent comment in +    # vimsupport.ConvertDiagnosticsToQfList. +    # +    # When the Vim help says "byte index", it really means "1-based column +    # number" (which is somewhat confusing). :help getqflist states "first +    # column is 1". +    qf_item[ 'col' ] = goto_data_item[ 'column_num' ] + +  return qf_item diff --git a/vim/bundle/YouCompleteMe/python/ycm/client/completer_available_request.py b/vim/bundle/YouCompleteMe/python/ycm/client/completer_available_request.py new file mode 100644 index 0000000..3a480ca --- /dev/null +++ b/vim/bundle/YouCompleteMe/python/ycm/client/completer_available_request.py @@ -0,0 +1,58 @@ +# Copyright (C) 2013  Google Inc. +# +# This file is part of YouCompleteMe. +# +# YouCompleteMe is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# YouCompleteMe is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with YouCompleteMe.  If not, see <http://www.gnu.org/licenses/>. + +from __future__ import unicode_literals +from __future__ import print_function +from __future__ import division +from __future__ import absolute_import +from future import standard_library +standard_library.install_aliases() +from builtins import *  # noqa + +from requests.exceptions import ReadTimeout + +from ycm.client.base_request import ( BaseRequest, BuildRequestData, +                                      HandleServerException ) +from ycmd.responses import ServerError + + +class CompleterAvailableRequest( BaseRequest ): +  def __init__( self, filetypes ): +    super( CompleterAvailableRequest, self ).__init__() +    self.filetypes = filetypes +    self._response = None + + +  def Start( self ): +    request_data = BuildRequestData() +    request_data.update( { 'filetypes': self.filetypes } ) +    try: +      self._response = self.PostDataToHandler( request_data, +                                               'semantic_completion_available' ) +    except ( ServerError, ReadTimeout ) as e: +      HandleServerException( e ) + + +  def Response( self ): +    return self._response + + +def SendCompleterAvailableRequest( filetypes ): +  request = CompleterAvailableRequest( filetypes ) +  # This is a blocking call. +  request.Start() +  return request.Response() diff --git a/vim/bundle/YouCompleteMe/python/ycm/client/completion_request.py b/vim/bundle/YouCompleteMe/python/ycm/client/completion_request.py new file mode 100644 index 0000000..bf16912 --- /dev/null +++ b/vim/bundle/YouCompleteMe/python/ycm/client/completion_request.py @@ -0,0 +1,109 @@ +# Copyright (C) 2013  Google Inc. +# +# This file is part of YouCompleteMe. +# +# YouCompleteMe is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# YouCompleteMe is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with YouCompleteMe.  If not, see <http://www.gnu.org/licenses/>. + +from __future__ import unicode_literals +from __future__ import print_function +from __future__ import division +from __future__ import absolute_import +from future import standard_library +standard_library.install_aliases() +from builtins import *  # noqa + +from requests.exceptions import ReadTimeout + +from ycmd.utils import ToUnicode +from ycm.client.base_request import ( BaseRequest, JsonFromFuture, +                                      HandleServerException, +                                      MakeServerException ) +from ycmd.responses import ServerError + +TIMEOUT_SECONDS = 0.5 + + +class CompletionRequest( BaseRequest ): +  def __init__( self, request_data ): +    super( CompletionRequest, self ).__init__() +    self.request_data = request_data + + +  def Start( self ): +    self._response_future = self.PostDataToHandlerAsync( self.request_data, +                                                         'completions', +                                                         TIMEOUT_SECONDS ) + + +  def Done( self ): +    return self._response_future.done() + + +  def RawResponse( self ): +    if not self._response_future: +      return [] +    try: +      response = JsonFromFuture( self._response_future ) + +      errors = response[ 'errors' ] if 'errors' in response else [] +      for e in errors: +        HandleServerException( MakeServerException( e ) ) + +      return JsonFromFuture( self._response_future )[ 'completions' ] +    except ( ServerError, ReadTimeout ) as e: +      HandleServerException( e ) +    return [] + + +  def Response( self ): +    return _ConvertCompletionDatasToVimDatas( self.RawResponse() ) + + +def ConvertCompletionDataToVimData( completion_data ): +  # see :h complete-items for a description of the dictionary fields +  vim_data = { +    'word'  : '', +    'dup'   : 1, +    'empty' : 1, +  } + +  if ( 'extra_data' in completion_data and +       'doc_string' in completion_data[ 'extra_data' ] ): +    doc_string = completion_data[ 'extra_data' ][ 'doc_string' ] +  else: +    doc_string = "" + +  if 'insertion_text' in completion_data: +    vim_data[ 'word' ] = completion_data[ 'insertion_text' ] +  if 'menu_text' in completion_data: +    vim_data[ 'abbr' ] = completion_data[ 'menu_text' ] +  if 'extra_menu_info' in completion_data: +    vim_data[ 'menu' ] = completion_data[ 'extra_menu_info' ] +  if 'kind' in completion_data: +    kind = ToUnicode( completion_data[ 'kind' ] ) +    if kind: +      vim_data[ 'kind' ] = kind[ 0 ].lower() +  if 'detailed_info' in completion_data: +    vim_data[ 'info' ] = completion_data[ 'detailed_info' ] +    if doc_string: +      vim_data[ 'info' ] += '\n' + doc_string +  elif doc_string: +    vim_data[ 'info' ] = doc_string + +  return vim_data + + +def _ConvertCompletionDatasToVimDatas( response_data ): +  return [ ConvertCompletionDataToVimData( x ) +           for x in response_data ] diff --git a/vim/bundle/YouCompleteMe/python/ycm/client/event_notification.py b/vim/bundle/YouCompleteMe/python/ycm/client/event_notification.py new file mode 100644 index 0000000..b9a60d1 --- /dev/null +++ b/vim/bundle/YouCompleteMe/python/ycm/client/event_notification.py @@ -0,0 +1,89 @@ +# Copyright (C) 2013  Google Inc. +# +# This file is part of YouCompleteMe. +# +# YouCompleteMe is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# YouCompleteMe is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with YouCompleteMe.  If not, see <http://www.gnu.org/licenses/>. + +from __future__ import unicode_literals +from __future__ import print_function +from __future__ import division +from __future__ import absolute_import +from future import standard_library +standard_library.install_aliases() +from builtins import *  # noqa + +from requests.exceptions import ReadTimeout + +from ycm import vimsupport +from ycmd.responses import UnknownExtraConf, ServerError +from ycm.client.base_request import ( BaseRequest, BuildRequestData, +                                      JsonFromFuture, HandleServerException ) + + +class EventNotification( BaseRequest ): +  def __init__( self, event_name, extra_data = None ): +    super( EventNotification, self ).__init__() +    self._event_name = event_name +    self._extra_data = extra_data +    self._cached_response = None + + +  def Start( self ): +    request_data = BuildRequestData() +    if self._extra_data: +      request_data.update( self._extra_data ) +    request_data[ 'event_name' ] = self._event_name + +    self._response_future = self.PostDataToHandlerAsync( request_data, +                                                         'event_notification' ) + + +  def Done( self ): +    return self._response_future.done() + + +  def Response( self ): +    if self._cached_response: +      return self._cached_response + +    if not self._response_future or self._event_name != 'FileReadyToParse': +      return [] + +    try: +      try: +        self._cached_response = JsonFromFuture( self._response_future ) +      except UnknownExtraConf as e: +          if vimsupport.Confirm( str( e ) ): +            _LoadExtraConfFile( e.extra_conf_file ) +          else: +            _IgnoreExtraConfFile( e.extra_conf_file ) +    except ( ServerError, ReadTimeout ) as e: +      HandleServerException( e ) + +    return self._cached_response if self._cached_response else [] + + +def SendEventNotificationAsync( event_name, extra_data = None ): +  event = EventNotification( event_name, extra_data ) +  event.Start() + + +def _LoadExtraConfFile( filepath ): +  BaseRequest.PostDataToHandler( { 'filepath': filepath }, +                                 'load_extra_conf_file' ) + + +def _IgnoreExtraConfFile( filepath ): +  BaseRequest.PostDataToHandler( { 'filepath': filepath }, +                                 'ignore_extra_conf_file' ) diff --git a/vim/bundle/YouCompleteMe/python/ycm/client/omni_completion_request.py b/vim/bundle/YouCompleteMe/python/ycm/client/omni_completion_request.py new file mode 100644 index 0000000..2bcb291 --- /dev/null +++ b/vim/bundle/YouCompleteMe/python/ycm/client/omni_completion_request.py @@ -0,0 +1,71 @@ +# Copyright (C) 2013  Google Inc. +# +# This file is part of YouCompleteMe. +# +# YouCompleteMe is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# YouCompleteMe is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with YouCompleteMe.  If not, see <http://www.gnu.org/licenses/>. + +from __future__ import unicode_literals +from __future__ import print_function +from __future__ import division +from __future__ import absolute_import +from future import standard_library +standard_library.install_aliases() +from builtins import *  # noqa + +from ycm.client.completion_request import CompletionRequest + + +class OmniCompletionRequest( CompletionRequest ): +  def __init__( self, omni_completer, request_data ): +    super( OmniCompletionRequest, self ).__init__( request_data ) +    self._omni_completer = omni_completer + + +  def Start( self ): +    self._results = self._omni_completer.ComputeCandidates( self.request_data ) + + +  def Done( self ): +    return True + + +  def RawResponse( self ): +    return _ConvertVimDatasToCompletionDatas( self._results ) + + +  def Response( self ): +    return self._results + + +def ConvertVimDataToCompletionData( vim_data ): +  # see :h complete-items for a description of the dictionary fields +  completion_data = {} + +  if 'word' in vim_data: +    completion_data[ 'insertion_text' ] = vim_data[ 'word' ] +  if 'abbr' in vim_data: +    completion_data[ 'menu_text' ] = vim_data[ 'abbr' ] +  if 'menu' in vim_data: +    completion_data[ 'extra_menu_info' ] = vim_data[ 'menu' ] +  if 'kind' in vim_data: +    completion_data[ 'kind' ] = [ vim_data[ 'kind' ] ] +  if 'info' in vim_data: +    completion_data[ 'detailed_info' ] = vim_data[ 'info' ] + +  return completion_data + + +def _ConvertVimDatasToCompletionDatas( response_data ): +  return [ ConvertVimDataToCompletionData( x ) +           for x in response_data ] diff --git a/vim/bundle/YouCompleteMe/python/ycm/client/tests/__init__.py b/vim/bundle/YouCompleteMe/python/ycm/client/tests/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/vim/bundle/YouCompleteMe/python/ycm/client/tests/__init__.py diff --git a/vim/bundle/YouCompleteMe/python/ycm/client/tests/command_request_test.py b/vim/bundle/YouCompleteMe/python/ycm/client/tests/command_request_test.py new file mode 100644 index 0000000..dfc55e0 --- /dev/null +++ b/vim/bundle/YouCompleteMe/python/ycm/client/tests/command_request_test.py @@ -0,0 +1,283 @@ +# Copyright (C) 2016 YouCompleteMe Contributors +# +# This file is part of YouCompleteMe. +# +# YouCompleteMe is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# YouCompleteMe is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with YouCompleteMe.  If not, see <http://www.gnu.org/licenses/>. + +from __future__ import unicode_literals +from __future__ import print_function +from __future__ import division +from __future__ import absolute_import +from future import standard_library +standard_library.install_aliases() +from builtins import *  # noqa + +from ycm.test_utils import ExtendedMock, MockVimModule +MockVimModule() + +import json +from mock import patch, call +from nose.tools import ok_ +from ycm.client.command_request import CommandRequest + + +class GoToResponse_QuickFix_test( object ): +  """This class tests the generation of QuickFix lists for GoTo responses which +  return multiple locations, such as the Python completer and JavaScript +  completer. It mostly proves that we use 1-based indexing for the column +  number.""" + +  def setUp( self ): +    self._request = CommandRequest( [ 'GoToTest' ] ) + + +  def tearDown( self ): +    self._request = None + + +  def GoTo_EmptyList_test( self ): +    self._CheckGoToList( [], [] ) + + +  def GoTo_SingleItem_List_test( self ): +    self._CheckGoToList( [ { +      'filepath':     'dummy_file', +      'line_num':     10, +      'column_num':   1, +      'description': 'this is some text', +    } ], [ { +      'filename':    'dummy_file', +      'text':        'this is some text', +      'lnum':        10, +      'col':         1 +    } ] ) + + +  def GoTo_MultiItem_List_test( self ): +    self._CheckGoToList( [ { +      'filepath':     'dummy_file', +      'line_num':     10, +      'column_num':   1, +      'description': 'this is some other text', +    }, { +      'filepath':     'dummy_file2', +      'line_num':     1, +      'column_num':   21, +      'description': 'this is some text', +    } ], [ { +      'filename':    'dummy_file', +      'text':        'this is some other text', +      'lnum':        10, +      'col':         1 +    }, { +      'filename':    'dummy_file2', +      'text':        'this is some text', +      'lnum':        1, +      'col':         21 +    } ] ) + + +  @patch( 'ycm.vimsupport.VariableExists', return_value = True ) +  @patch( 'ycm.vimsupport.SetFittingHeightForCurrentWindow' ) +  @patch( 'vim.command', new_callable = ExtendedMock ) +  @patch( 'vim.eval', new_callable = ExtendedMock ) +  def _CheckGoToList( self, +                      completer_response, +                      expected_qf_list, +                      vim_eval, +                      vim_command, +                      set_fitting_height, +                      variable_exists ): +    self._request._response = completer_response + +    self._request.RunPostCommandActionsIfNeeded() + +    vim_eval.assert_has_exact_calls( [ +      call( 'setqflist( {0} )'.format( json.dumps( expected_qf_list ) ) ) +    ] ) +    vim_command.assert_has_exact_calls( [ +      call( 'botright copen' ), +      call( 'au WinLeave <buffer> q' ), +      call( 'doautocmd User YcmQuickFixOpened' ) +    ] ) +    set_fitting_height.assert_called_once_with() + + +class Response_Detection_test( object ): + +  def BasicResponse_test( self ): +    def _BasicResponseTest( command, response ): +      with patch( 'vim.command' ) as vim_command: +        request = CommandRequest( [ command ] ) +        request._response = response +        request.RunPostCommandActionsIfNeeded() +        vim_command.assert_called_with( "echom '{0}'".format( response ) ) + +    tests = [ +      [ 'AnythingYouLike',        True ], +      [ 'GoToEvenWorks',          10 ], +      [ 'FixItWorks',             'String!' ], +      [ 'and8434fd andy garbag!', 10.3 ], +    ] + +    for test in tests: +      yield _BasicResponseTest, test[ 0 ], test[ 1 ] + + +  def FixIt_Response_Empty_test( self ): +    # Ensures we recognise and handle fixit responses which indicate that there +    # are no fixits available +    def EmptyFixItTest( command ): +      with patch( 'ycm.vimsupport.ReplaceChunks' ) as replace_chunks: +        with patch( 'ycm.vimsupport.EchoText' ) as echo_text: +          request = CommandRequest( [ command ] ) +          request._response = { +            'fixits': [] +          } +          request.RunPostCommandActionsIfNeeded() + +          echo_text.assert_called_with( 'No fixits found for current line' ) +          replace_chunks.assert_not_called() + +    for test in [ 'FixIt', 'Refactor', 'GoToHell', 'any_old_garbade!!!21' ]: +      yield EmptyFixItTest, test + + +  def FixIt_Response_test( self ): +    # Ensures we recognise and handle fixit responses with some dummy chunk data +    def FixItTest( command, response, chunks ): +      with patch( 'ycm.vimsupport.ReplaceChunks' ) as replace_chunks: +        with patch( 'ycm.vimsupport.EchoText' ) as echo_text: +          request = CommandRequest( [ command ] ) +          request._response = response +          request.RunPostCommandActionsIfNeeded() + +          replace_chunks.assert_called_with( chunks ) +          echo_text.assert_not_called() + +    basic_fixit = { +      'fixits': [ { +        'chunks': [ { +          'dummy chunk contents': True +        } ] +      } ] +    } +    basic_fixit_chunks = basic_fixit[ 'fixits' ][ 0 ][ 'chunks' ] + +    multi_fixit = { +      'fixits': [ { +        'chunks': [ { +          'dummy chunk contents': True +        } ] +      }, { +        'additional fixits are ignored currently': True +      } ] +    } +    multi_fixit_first_chunks = multi_fixit[ 'fixits' ][ 0 ][ 'chunks' ] + +    tests = [ +      [ 'AnythingYouLike',        basic_fixit, basic_fixit_chunks ], +      [ 'GoToEvenWorks',          basic_fixit, basic_fixit_chunks ], +      [ 'FixItWorks',             basic_fixit, basic_fixit_chunks ], +      [ 'and8434fd andy garbag!', basic_fixit, basic_fixit_chunks ], +      [ 'additional fixits ignored', multi_fixit, multi_fixit_first_chunks ], +    ] + +    for test in tests: +      yield FixItTest, test[ 0 ], test[ 1 ], test[ 2 ] + + +  def Message_Response_test( self ): +    # Ensures we correctly recognise and handle responses with a message to show +    # to the user + +    def MessageTest( command, message ): +      with patch( 'ycm.vimsupport.EchoText' ) as echo_text: +        request = CommandRequest( [ command ] ) +        request._response = { 'message': message } +        request.RunPostCommandActionsIfNeeded() +        echo_text.assert_called_with( message ) + +    tests = [ +      [ '___________', 'This is a message' ], +      [ '',            'this is also a message' ], +      [ 'GetType',     'std::string' ], +    ] + +    for test in tests: +      yield MessageTest, test[ 0 ], test[ 1 ] + + +  def Detailed_Info_test( self ): +    # Ensures we correctly detect and handle detailed_info responses which are +    # used to display information in the preview window + +    def DetailedInfoTest( command, info ): +      with patch( 'ycm.vimsupport.WriteToPreviewWindow' ) as write_to_preview: +        request = CommandRequest( [ command ] ) +        request._response = { 'detailed_info': info } +        request.RunPostCommandActionsIfNeeded() +        write_to_preview.assert_called_with( info ) + +    tests = [ +      [ '___________', 'This is a message' ], +      [ '',            'this is also a message' ], +      [ 'GetDoc',      'std::string\netc\netc' ], +    ] + +    for test in tests: +      yield DetailedInfoTest, test[ 0 ], test[ 1 ] + + +  def GoTo_Single_test( self ): +    # Ensures we handle any unknown type of response as a GoTo response + +    def GoToTest( command, response ): +      with patch( 'ycm.vimsupport.JumpToLocation' ) as jump_to_location: +        request = CommandRequest( [ command ] ) +        request._response = response +        request.RunPostCommandActionsIfNeeded() +        jump_to_location.assert_called_with( +            response[ 'filepath' ], +            response[ 'line_num' ], +            response[ 'column_num' ] ) + +    def GoToListTest( command, response ): +      # Note: the detail of these called are tested by +      # GoToResponse_QuickFix_test, so here we just check that the right call is +      # made +      with patch( 'ycm.vimsupport.SetQuickFixList' ) as set_qf_list: +        request = CommandRequest( [ command ] ) +        request._response = response +        request.RunPostCommandActionsIfNeeded() +        ok_( set_qf_list.called ) + +    basic_goto = { +      'filepath': 'test', +      'line_num': 10, +      'column_num': 100, +    } + +    tests = [ +      [ GoToTest,     'AnythingYouLike', basic_goto ], +      [ GoToTest,     'GoTo',            basic_goto ], +      [ GoToTest,     'FindAThing',      basic_goto ], +      [ GoToTest,     'FixItGoto',       basic_goto ], +      [ GoToListTest, 'AnythingYouLike', [ basic_goto ] ], +      [ GoToListTest, 'GoTo',            []  ], +      [ GoToListTest, 'FixItGoto',       [ basic_goto, basic_goto ] ], +    ] + +    for test in tests: +      yield test[ 0 ], test[ 1 ], test[ 2 ] diff --git a/vim/bundle/YouCompleteMe/python/ycm/client/tests/completion_request_test.py b/vim/bundle/YouCompleteMe/python/ycm/client/tests/completion_request_test.py new file mode 100644 index 0000000..acf0252 --- /dev/null +++ b/vim/bundle/YouCompleteMe/python/ycm/client/tests/completion_request_test.py @@ -0,0 +1,186 @@ +# Copyright (C) 2015 YouCompleteMe Contributors +# +# This file is part of YouCompleteMe. +# +# YouCompleteMe is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# YouCompleteMe is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with YouCompleteMe.  If not, see <http://www.gnu.org/licenses/>. + +from __future__ import unicode_literals +from __future__ import print_function +from __future__ import division +from __future__ import absolute_import +from future import standard_library +standard_library.install_aliases() +from builtins import *  # noqa + +from nose.tools import eq_ +from ycm.test_utils import MockVimModule +vim_mock = MockVimModule() + +from .. import completion_request + + +class ConvertCompletionResponseToVimDatas_test( object ): +  """ This class tests the +      completion_request._ConvertCompletionResponseToVimDatas method """ + +  def _Check( self, completion_data, expected_vim_data ): +    vim_data = completion_request.ConvertCompletionDataToVimData( +        completion_data ) + +    try: +      eq_( expected_vim_data, vim_data ) +    except: +      print( "Expected:\n'{0}'\nwhen parsing:\n'{1}'\nBut found:\n'{2}'".format( +          expected_vim_data, +          completion_data, +          vim_data ) ) +      raise + + +  def All_Fields_test( self ): +    self._Check( { +      'insertion_text':  'INSERTION TEXT', +      'menu_text':       'MENU TEXT', +      'extra_menu_info': 'EXTRA MENU INFO', +      'kind':            'K', +      'detailed_info':   'DETAILED INFO', +      'extra_data': { +        'doc_string':    'DOC STRING', +      }, +    }, { +      'word' : 'INSERTION TEXT', +      'abbr' : 'MENU TEXT', +      'menu' : 'EXTRA MENU INFO', +      'kind' : 'k', +      'info' : 'DETAILED INFO\nDOC STRING', +      'dup'  : 1, +      'empty': 1, +    } ) + + +  def Just_Detailed_Info_test( self ): +    self._Check( { +      'insertion_text':  'INSERTION TEXT', +      'menu_text':       'MENU TEXT', +      'extra_menu_info': 'EXTRA MENU INFO', +      'kind':            'K', +      'detailed_info':   'DETAILED INFO', +    }, { +      'word' : 'INSERTION TEXT', +      'abbr' : 'MENU TEXT', +      'menu' : 'EXTRA MENU INFO', +      'kind' : 'k', +      'info' : 'DETAILED INFO', +      'dup'  : 1, +      'empty': 1, +    } ) + + +  def Just_Doc_String_test( self ): +    self._Check( { +      'insertion_text':  'INSERTION TEXT', +      'menu_text':       'MENU TEXT', +      'extra_menu_info': 'EXTRA MENU INFO', +      'kind':            'K', +      'extra_data': { +        'doc_string':    'DOC STRING', +      }, +    }, { +      'word' : 'INSERTION TEXT', +      'abbr' : 'MENU TEXT', +      'menu' : 'EXTRA MENU INFO', +      'kind' : 'k', +      'info' : 'DOC STRING', +      'dup'  : 1, +      'empty': 1, +    } ) + + +  def Extra_Info_No_Doc_String_test( self ): +    self._Check( { +      'insertion_text':  'INSERTION TEXT', +      'menu_text':       'MENU TEXT', +      'extra_menu_info': 'EXTRA MENU INFO', +      'kind':            'K', +      'extra_data': { +      }, +    }, { +      'word' : 'INSERTION TEXT', +      'abbr' : 'MENU TEXT', +      'menu' : 'EXTRA MENU INFO', +      'kind' : 'k', +      'dup'  : 1, +      'empty': 1, +    } ) + + +  def Extra_Info_No_Doc_String_With_Detailed_Info_test( self ): +    self._Check( { +      'insertion_text':  'INSERTION TEXT', +      'menu_text':       'MENU TEXT', +      'extra_menu_info': 'EXTRA MENU INFO', +      'kind':            'K', +      'detailed_info':   'DETAILED INFO', +      'extra_data': { +      }, +    }, { +      'word' : 'INSERTION TEXT', +      'abbr' : 'MENU TEXT', +      'menu' : 'EXTRA MENU INFO', +      'kind' : 'k', +      'info' : 'DETAILED INFO', +      'dup'  : 1, +      'empty': 1, +    } ) + + +  def Empty_Insertion_Text_test( self ): +    self._Check( { +      'insertion_text':  '', +      'menu_text':       'MENU TEXT', +      'extra_menu_info': 'EXTRA MENU INFO', +      'kind':            'K', +      'detailed_info':   'DETAILED INFO', +      'extra_data': { +        'doc_string':    'DOC STRING', +      }, +    }, { +      'word' : '', +      'abbr' : 'MENU TEXT', +      'menu' : 'EXTRA MENU INFO', +      'kind' : 'k', +      'info' : 'DETAILED INFO\nDOC STRING', +      'dup'  : 1, +      'empty': 1, +    } ) + + +  def No_Insertion_Text_test( self ): +    self._Check( { +      'menu_text':       'MENU TEXT', +      'extra_menu_info': 'EXTRA MENU INFO', +      'kind':            'K', +      'detailed_info':   'DETAILED INFO', +      'extra_data': { +        'doc_string':    'DOC STRING', +      }, +    }, { +      'word' : '', +      'abbr' : 'MENU TEXT', +      'menu' : 'EXTRA MENU INFO', +      'kind' : 'k', +      'info' : 'DETAILED INFO\nDOC STRING', +      'dup'  : 1, +      'empty': 1, +    } ) diff --git a/vim/bundle/YouCompleteMe/python/ycm/client/tests/omni_completion_request_tests.py b/vim/bundle/YouCompleteMe/python/ycm/client/tests/omni_completion_request_tests.py new file mode 100644 index 0000000..02fb2e9 --- /dev/null +++ b/vim/bundle/YouCompleteMe/python/ycm/client/tests/omni_completion_request_tests.py @@ -0,0 +1,81 @@ +# Copyright (C) 2016 YouCompleteMe contributors +# +# This file is part of YouCompleteMe. +# +# YouCompleteMe is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# YouCompleteMe is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with YouCompleteMe.  If not, see <http://www.gnu.org/licenses/>. + +from __future__ import unicode_literals +from __future__ import print_function +from __future__ import division +from __future__ import absolute_import +from future import standard_library +standard_library.install_aliases() +from builtins import *  # noqa + +from mock import MagicMock +from nose.tools import eq_ +from hamcrest import assert_that, has_entries + +from ycm.client.omni_completion_request import OmniCompletionRequest + + +def BuildOmnicompletionRequest( results ): +  omni_completer = MagicMock() +  omni_completer.ComputeCandidates = MagicMock( return_value = results ) + +  request = OmniCompletionRequest( omni_completer, None ) +  request.Start() + +  return request + + +def Done_AlwaysTrue_test(): +  request = BuildOmnicompletionRequest( [] ) + +  eq_( request.Done(), True ) + + +def Response_FromOmniCompleter_test(): +  results = [ { "word": "test" } ] +  request = BuildOmnicompletionRequest( results ) + +  eq_( request.Response(), results ) + + +def RawResponse_ConvertedFromOmniCompleter_test(): +  vim_results = [ +    { "word": "WORD", "abbr": "ABBR", "menu": "MENU", +      "kind": "KIND", "info": "INFO" }, +    { "word": "WORD2", "abbr": "ABBR2", "menu": "MENU2", +      "kind": "KIND2", "info": "INFO" }, +    { "word": "WORD", "abbr": "ABBR",  }, +    {  }, +  ] +  expected_results = [ +    has_entries( { "insertion_text": "WORD", "menu_text": "ABBR", +                   "extra_menu_info": "MENU", "kind": [ "KIND" ], +                   "detailed_info": "INFO" } ), +    has_entries( { "insertion_text": "WORD2", "menu_text": "ABBR2", +                   "extra_menu_info": "MENU2", "kind": [ "KIND2" ], +                   "detailed_info": "INFO" } ), +    has_entries( { "insertion_text": "WORD", "menu_text": "ABBR",  } ), +    has_entries( {  } ), +  ] +  request = BuildOmnicompletionRequest( vim_results ) + +  results = request.RawResponse() + +  eq_( len( results ), len( expected_results ) ) +  for result, expected_result in zip( results, expected_results ): +    assert_that( result, expected_result ) diff --git a/vim/bundle/YouCompleteMe/python/ycm/client/ycmd_keepalive.py b/vim/bundle/YouCompleteMe/python/ycm/client/ycmd_keepalive.py new file mode 100644 index 0000000..fca59f4 --- /dev/null +++ b/vim/bundle/YouCompleteMe/python/ycm/client/ycmd_keepalive.py @@ -0,0 +1,54 @@ +# Copyright (C) 2013  Google Inc. +# +# This file is part of YouCompleteMe. +# +# YouCompleteMe is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# YouCompleteMe is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with YouCompleteMe.  If not, see <http://www.gnu.org/licenses/>. + +from __future__ import unicode_literals +from __future__ import print_function +from __future__ import division +from __future__ import absolute_import +from future import standard_library +standard_library.install_aliases() +from builtins import *  # noqa + +import time +from threading import Thread +from ycm.client.base_request import BaseRequest + + +# This class can be used to keep the ycmd server alive for the duration of the +# life of the client. By default, ycmd shuts down if it doesn't see a request in +# a while. +class YcmdKeepalive( object ): +  def __init__( self, ping_interval_seconds = 60 * 10 ): +    self._keepalive_thread = Thread( target = self._ThreadMain ) +    self._keepalive_thread.daemon = True +    self._ping_interval_seconds = ping_interval_seconds + + +  def Start( self ): +    self._keepalive_thread.start() + + +  def _ThreadMain( self ): +    while True: +      time.sleep( self._ping_interval_seconds ) + +      # We don't care if there's an intermittent problem in contacting the +      # server; it's fine to just skip this ping. +      try: +        BaseRequest.GetDataFromHandler( 'healthy' ) +      except: +        pass | 
