コード例 #1
0
def YouCompleteMe_OnPeriodicTick_DontRetry_test(post_data_to_handler_async,
                                                validate_response_object,
                                                filetype_completer_exists,
                                                ycm):

    current_buffer = VimBuffer('/current', filetype='ycmtest', number=1)

    # Create the request and make the first poll; we expect no response
    with MockVimBuffers([current_buffer], [current_buffer], (1, 1)):
        assert_that(ycm.OnPeriodicTick(), equal_to(True))
        post_data_to_handler_async.assert_called()

    assert ycm._message_poll_requests['ycmtest'] is not None
    post_data_to_handler_async.reset_mock()

    # OK that sent the request, now poll to check if it is complete (say it is
    # not)
    with MockVimBuffers([current_buffer], [current_buffer], (1, 1)) as v:
        mock_response = MockAsyncServerResponseInProgress()
        with patch.dict(ycm._message_poll_requests, {}):
            ycm._message_poll_requests['ycmtest'] = MessagesPoll(
                v.current.buffer)
            ycm._message_poll_requests[
                'ycmtest']._response_future = mock_response
            mock_future = ycm._message_poll_requests[
                'ycmtest']._response_future
            poll_again = ycm.OnPeriodicTick()
            mock_future.done.assert_called()
            mock_future.result.assert_not_called()
            assert_that(poll_again, equal_to(True))

    # Poll again, but return a response (telling us to stop polling)
    with MockVimBuffers([current_buffer], [current_buffer], (1, 1)) as v:
        mock_response = MockAsyncServerResponseDone(False)
        with patch.dict(ycm._message_poll_requests, {}):
            ycm._message_poll_requests['ycmtest'] = MessagesPoll(
                v.current.buffer)
            ycm._message_poll_requests[
                'ycmtest']._response_future = mock_response
            mock_future = ycm._message_poll_requests[
                'ycmtest']._response_future
            poll_again = ycm.OnPeriodicTick()
            mock_future.done.assert_called()
            mock_future.result.assert_called()
            post_data_to_handler_async.assert_not_called()
            # We reset and don't poll anymore
            assert_that(ycm._message_poll_requests['ycmtest'] is None)
            assert_that(poll_again, equal_to(False))
コード例 #2
0
ファイル: youcompleteme_test.py プロジェクト: zimingzpp/rc
def YouCompleteMe_OnPeriodicTick_ValidResponse_test(ycm, handle_poll_response,
                                                    post_data_to_handler_async,
                                                    *args):

    current_buffer = VimBuffer('/current', filetype='ycmtest', number=1)

    # Create the request and make the first poll; we expect no response
    with MockVimBuffers([current_buffer], [current_buffer], (1, 1)):
        assert_that(ycm.OnPeriodicTick(), equal_to(True))
        post_data_to_handler_async.assert_called()

    post_data_to_handler_async.reset_mock()

    # Poll again, and return a _proper_ response (finally!).
    # Note, _HandlePollResponse is tested independently (for simplicity)
    with MockVimBuffers([current_buffer], [current_buffer], (1, 1)) as v:
        mock_response = MockAsyncServerResponseDone([])
        with patch.dict(ycm._message_poll_requests, {}):
            ycm._message_poll_requests['ycmtest'] = MessagesPoll(
                v.current.buffer)
            ycm._message_poll_requests[
                'ycmtest']._response_future = mock_response
            mock_future = ycm._message_poll_requests[
                'ycmtest']._response_future
            assert_that(ycm.OnPeriodicTick(), equal_to(True))
            handle_poll_response.assert_called()
            mock_future.done.assert_called()
            mock_future.result.assert_called()
            post_data_to_handler_async.assert_called()  # Poll again!
            assert_that(ycm._message_poll_requests['ycmtest'] is not None)
コード例 #3
0
ファイル: youcompleteme_test.py プロジェクト: zimingzpp/rc
def YouCompleteMe_OnPeriodicTick_Exception_test(ycm,
                                                post_data_to_handler_async,
                                                *args):

    current_buffer = VimBuffer('/current', filetype='ycmtest', number=1)

    # Create the request and make the first poll; we expect no response
    with MockVimBuffers([current_buffer], [current_buffer], (1, 1)):
        assert_that(ycm.OnPeriodicTick(), equal_to(True))
        post_data_to_handler_async.assert_called()

    post_data_to_handler_async.reset_mock()

    # Poll again, but return an exception response
    with MockVimBuffers([current_buffer], [current_buffer], (1, 1)) as v:
        mock_response = MockAsyncServerResponseException(RuntimeError('test'))
        with patch.dict(ycm._message_poll_requests, {}):
            ycm._message_poll_requests['ycmtest'] = MessagesPoll(
                v.current.buffer)
            ycm._message_poll_requests[
                'ycmtest']._response_future = mock_response
            mock_future = ycm._message_poll_requests[
                'ycmtest']._response_future
            assert_that(ycm.OnPeriodicTick(), equal_to(False))
            mock_future.done.assert_called()
            mock_future.result.assert_called()
            post_data_to_handler_async.assert_not_called()
            # We reset and don't poll anymore
            assert_that(ycm._message_poll_requests['ycmtest'] is None)
コード例 #4
0
  def OnPeriodicTick( self ):
    if not self.IsServerAlive():
      # Server has died. We'll reset when the server is started again.
      return False
    elif not self.IsServerReady():
      # Try again in a jiffy
      return True

    if not self._message_poll_request:
      self._message_poll_request = MessagesPoll()

    if not self._message_poll_request.Poll( self ):
      # Don't poll again until some event which might change the server's mind
      # about whether to provide messages for the current buffer (e.g. buffer
      # visit, file ready to parse, etc.)
      self._message_poll_request = None
      return False

    # Poll again in a jiffy
    return True
コード例 #5
0
  def OnPeriodicTick( self ):
    if not self.IsServerAlive():
      # Server has died. We'll reset when the server is started again.
      return False
    elif not self.IsServerReady():
      # Try again in a jiffy
      return True

    for w in vim.windows:
      for filetype in vimsupport.FiletypesForBuffer( w.buffer ):
        if filetype not in self._message_poll_requests:
          self._message_poll_requests[ filetype ] = MessagesPoll( w.buffer )

        # None means don't poll this filetype
        if ( self._message_poll_requests[ filetype ] and
             not self._message_poll_requests[ filetype ].Poll( self ) ):
          self._message_poll_requests[ filetype ] = None

    return any( self._message_poll_requests.values() )
コード例 #6
0
class YouCompleteMe( object ):
  def __init__( self ):
    self._available_completers = {}
    self._user_options = None
    self._user_notified_about_crash = False
    self._omnicomp = None
    self._buffers = None
    self._latest_completion_request = None
    self._logger = logging.getLogger( 'ycm' )
    self._client_logfile = None
    self._server_stdout = None
    self._server_stderr = None
    self._server_popen = None
    self._filetypes_with_keywords_loaded = set()
    self._ycmd_keepalive = YcmdKeepalive()
    self._server_is_ready_with_cache = False
    self._SetUpLogging()
    self._SetUpServer()
    self._ycmd_keepalive.Start()


  def _SetUpServer( self ):
    self._available_completers = {}
    self._user_notified_about_crash = False
    self._filetypes_with_keywords_loaded = set()
    self._server_is_ready_with_cache = False
    self._message_poll_request = None

    self._user_options = base.GetUserOptions()
    self._omnicomp = OmniCompleter( self._user_options )
    self._buffers = BufferDict( self._user_options )

    self._SetLogLevel()

    hmac_secret = os.urandom( HMAC_SECRET_LENGTH )
    options_dict = dict( self._user_options )
    options_dict[ 'hmac_secret' ] = utils.ToUnicode(
      base64.b64encode( hmac_secret ) )
    options_dict[ 'server_keep_logfiles' ] = self._user_options[
      'keep_logfiles' ]

    # The temp options file is deleted by ycmd during startup.
    with NamedTemporaryFile( delete = False, mode = 'w+' ) as options_file:
      json.dump( options_dict, options_file )

    server_port = utils.GetUnusedLocalhostPort()

    BaseRequest.server_location = 'http://127.0.0.1:' + str( server_port )
    BaseRequest.hmac_secret = hmac_secret

    try:
      python_interpreter = paths.PathToPythonInterpreter()
    except RuntimeError as error:
      error_message = (
        "Unable to start the ycmd server. {0}. "
        "Correct the error then restart the server "
        "with ':YcmRestartServer'.".format( str( error ).rstrip( '.' ) ) )
      self._logger.exception( error_message )
      vimsupport.PostVimMessage( error_message )
      return

    args = [ python_interpreter,
             paths.PathToServerScript(),
             '--port={0}'.format( server_port ),
             '--options_file={0}'.format( options_file.name ),
             '--log={0}'.format( self._user_options[ 'log_level' ] ),
             '--idle_suicide_seconds={0}'.format(
                SERVER_IDLE_SUICIDE_SECONDS ) ]

    self._server_stdout = utils.CreateLogfile(
        SERVER_LOGFILE_FORMAT.format( port = server_port, std = 'stdout' ) )
    self._server_stderr = utils.CreateLogfile(
        SERVER_LOGFILE_FORMAT.format( port = server_port, std = 'stderr' ) )
    args.append( '--stdout={0}'.format( self._server_stdout ) )
    args.append( '--stderr={0}'.format( self._server_stderr ) )

    if self._user_options[ 'keep_logfiles' ]:
      args.append( '--keep_logfiles' )

    self._server_popen = utils.SafePopen( args, stdin_windows = PIPE,
                                          stdout = PIPE, stderr = PIPE )


  def _SetUpLogging( self ):
    def FreeFileFromOtherProcesses( file_object ):
      if utils.OnWindows():
        from ctypes import windll
        import msvcrt

        file_handle = msvcrt.get_osfhandle( file_object.fileno() )
        windll.kernel32.SetHandleInformation( file_handle,
                                              HANDLE_FLAG_INHERIT,
                                              0 )

    self._client_logfile = utils.CreateLogfile( CLIENT_LOGFILE_FORMAT )

    handler = logging.FileHandler( self._client_logfile )

    # On Windows and Python prior to 3.4, file handles are inherited by child
    # processes started with at least one replaced standard stream, which is the
    # case when we start the ycmd server (we are redirecting all standard
    # outputs into a pipe). These files cannot be removed while the child
    # processes are still up. This is not desirable for a logfile because we
    # want to remove it at Vim exit without having to wait for the ycmd server
    # to be completely shut down. We need to make the logfile handle
    # non-inheritable. See https://www.python.org/dev/peps/pep-0446 for more
    # details.
    FreeFileFromOtherProcesses( handler.stream )

    formatter = logging.Formatter( '%(asctime)s - %(levelname)s - %(message)s' )
    handler.setFormatter( formatter )

    self._logger.addHandler( handler )


  def _SetLogLevel( self ):
    log_level = self._user_options[ 'log_level' ]
    numeric_level = getattr( logging, log_level.upper(), None )
    if not isinstance( numeric_level, int ):
      raise ValueError( 'Invalid log level: {0}'.format( log_level ) )
    self._logger.setLevel( numeric_level )


  def IsServerAlive( self ):
    # When the process hasn't finished yet, poll() returns None.
    return bool( self._server_popen ) and self._server_popen.poll() is None


  def CheckIfServerIsReady( self ):
    if not self._server_is_ready_with_cache and self.IsServerAlive():
      self._server_is_ready_with_cache = BaseRequest().GetDataFromHandler(
          'ready', display_message = False )
    return self._server_is_ready_with_cache


  def IsServerReady( self ):
    return self._server_is_ready_with_cache


  def NotifyUserIfServerCrashed( self ):
    if ( not self._server_popen or self._user_notified_about_crash or
         self.IsServerAlive() ):
      return
    self._user_notified_about_crash = True

    return_code = self._server_popen.poll()
    logfile = os.path.basename( self._server_stderr )
    if return_code == server_utils.CORE_UNEXPECTED_STATUS:
      error_message = CORE_UNEXPECTED_MESSAGE.format(
          logfile = logfile )
    elif return_code == server_utils.CORE_MISSING_STATUS:
      error_message = CORE_MISSING_MESSAGE
    elif return_code == server_utils.CORE_PYTHON2_STATUS:
      error_message = CORE_PYTHON2_MESSAGE
    elif return_code == server_utils.CORE_PYTHON3_STATUS:
      error_message = CORE_PYTHON3_MESSAGE
    elif return_code == server_utils.CORE_OUTDATED_STATUS:
      error_message = CORE_OUTDATED_MESSAGE
    else:
      error_message = EXIT_CODE_UNEXPECTED_MESSAGE.format(
          code = return_code,
          logfile = logfile )

    error_message = SERVER_SHUTDOWN_MESSAGE + ' ' + error_message
    self._logger.error( error_message )
    vimsupport.PostVimMessage( error_message )


  def ServerPid( self ):
    if not self._server_popen:
      return -1
    return self._server_popen.pid


  def _ShutdownServer( self ):
    SendShutdownRequest()


  def RestartServer( self ):
    vimsupport.PostVimMessage( 'Restarting ycmd server...' )
    self._ShutdownServer()
    self._SetUpServer()


  def SendCompletionRequest( self, force_semantic = False ):
    request_data = BuildRequestData()
    request_data[ 'force_semantic' ] = force_semantic
    if not self.NativeFiletypeCompletionUsable():
      wrapped_request_data = RequestWrap( request_data )
      if self._omnicomp.ShouldUseNow( wrapped_request_data ):
        self._latest_completion_request = OmniCompletionRequest(
            self._omnicomp, wrapped_request_data )
        self._latest_completion_request.Start()
        return

    self._AddExtraConfDataIfNeeded( request_data )
    self._latest_completion_request = CompletionRequest( request_data )
    self._latest_completion_request.Start()


  def CompletionRequestReady( self ):
    return bool( self._latest_completion_request and
                 self._latest_completion_request.Done() )


  def GetCompletionResponse( self ):
    response = self._latest_completion_request.Response()
    response[ 'completions' ] = base.AdjustCandidateInsertionText(
        response[ 'completions' ] )
    return response


  def SendCommandRequest( self,
                          arguments,
                          modifiers,
                          has_range,
                          start_line,
                          end_line ):
    final_arguments = []
    for argument in arguments:
      # The ft= option which specifies the completer when running a command is
      # ignored because it has not been working for a long time. The option is
      # still parsed to not break users that rely on it.
      if argument.startswith( 'ft=' ):
        continue
      final_arguments.append( argument )

    extra_data = {
      'options': {
        'tab_size': vimsupport.GetIntValue( 'shiftwidth()' ),
        'insert_spaces': vimsupport.GetBoolValue( '&expandtab' )
      }
    }
    if has_range:
      extra_data.update( vimsupport.BuildRange( start_line, end_line ) )
    self._AddExtraConfDataIfNeeded( extra_data )

    return SendCommandRequest( final_arguments,
                               modifiers,
                               self._user_options[ 'goto_buffer_command' ],
                               extra_data )


  def GetDefinedSubcommands( self ):
    subcommands = BaseRequest().PostDataToHandler( BuildRequestData(),
                                                   'defined_subcommands' )
    return subcommands if subcommands else []


  def GetCurrentCompletionRequest( self ):
    return self._latest_completion_request


  def GetOmniCompleter( self ):
    return self._omnicomp


  def FiletypeCompleterExistsForFiletype( self, filetype ):
    try:
      return self._available_completers[ filetype ]
    except KeyError:
      pass

    exists_completer = SendCompleterAvailableRequest( filetype )
    if exists_completer is None:
      return False

    self._available_completers[ filetype ] = exists_completer
    return exists_completer


  def NativeFiletypeCompletionAvailable( self ):
    return any( self.FiletypeCompleterExistsForFiletype( x ) for x in
                vimsupport.CurrentFiletypes() )


  def NativeFiletypeCompletionUsable( self ):
    disabled_filetypes = self._user_options[
      'filetype_specific_completion_to_disable' ]
    return ( vimsupport.CurrentFiletypesEnabled( disabled_filetypes ) and
             self.NativeFiletypeCompletionAvailable() )


  def NeedsReparse( self ):
    return self.CurrentBuffer().NeedsReparse()


  def UpdateWithNewDiagnosticsForFile( self, filepath, diagnostics ):
    bufnr = vimsupport.GetBufferNumberForFilename( filepath )
    if bufnr in self._buffers and vimsupport.BufferIsVisible( bufnr ):
      # Note: We only update location lists, etc. for visible buffers, because
      # otherwise we default to using the current location list and the results
      # are that non-visible buffer errors clobber visible ones.
      self._buffers[ bufnr ].UpdateWithNewDiagnostics( diagnostics )
    else:
      # The project contains errors in file "filepath", but that file is not
      # open in any buffer. This happens for Language Server Protocol-based
      # completers, as they return diagnostics for the entire "project"
      # asynchronously (rather than per-file in the response to the parse
      # request).
      #
      # There are a number of possible approaches for
      # this, but for now we simply ignore them. Other options include:
      # - Use the QuickFix list to report project errors?
      # - Use a special buffer for project errors
      # - Put them in the location list of whatever the "current" buffer is
      # - Store them in case the buffer is opened later
      # - add a :YcmProjectDiags command
      # - Add them to errror/warning _counts_ but not any actual location list
      #   or other
      # - etc.
      #
      # However, none of those options are great, and lead to their own
      # complexities. So for now, we just ignore these diagnostics for files not
      # open in any buffer.
      pass


  def OnPeriodicTick( self ):
    if not self.IsServerAlive():
      # Server has died. We'll reset when the server is started again.
      return False
    elif not self.IsServerReady():
      # Try again in a jiffy
      return True

    if not self._message_poll_request:
      self._message_poll_request = MessagesPoll()

    if not self._message_poll_request.Poll( self ):
      # Don't poll again until some event which might change the server's mind
      # about whether to provide messages for the current buffer (e.g. buffer
      # visit, file ready to parse, etc.)
      self._message_poll_request = None
      return False

    # Poll again in a jiffy
    return True


  def OnFileReadyToParse( self ):
    if not self.IsServerAlive():
      self.NotifyUserIfServerCrashed()
      return

    if not self.IsServerReady():
      return

    extra_data = {}
    self._AddTagsFilesIfNeeded( extra_data )
    self._AddSyntaxDataIfNeeded( extra_data )
    self._AddExtraConfDataIfNeeded( extra_data )

    self.CurrentBuffer().SendParseRequest( extra_data )


  def OnBufferUnload( self, deleted_buffer_number ):
    SendEventNotificationAsync( 'BufferUnload', deleted_buffer_number )


  def UpdateMatches( self ):
    self.CurrentBuffer().UpdateMatches()


  def OnBufferVisit( self ):
    extra_data = {}
    self._AddUltiSnipsDataIfNeeded( extra_data )
    SendEventNotificationAsync( 'BufferVisit', extra_data = extra_data )


  def CurrentBuffer( self ):
    return self._buffers[ vimsupport.GetCurrentBufferNumber() ]


  def OnInsertLeave( self ):
    SendEventNotificationAsync( 'InsertLeave' )


  def OnCursorMoved( self ):
    self.CurrentBuffer().OnCursorMoved()


  def _CleanLogfile( self ):
    logging.shutdown()
    if not self._user_options[ 'keep_logfiles' ]:
      if self._client_logfile:
        utils.RemoveIfExists( self._client_logfile )


  def OnVimLeave( self ):
    self._ShutdownServer()
    self._CleanLogfile()


  def OnCurrentIdentifierFinished( self ):
    SendEventNotificationAsync( 'CurrentIdentifierFinished' )


  def OnCompleteDone( self ):
    completion_request = self.GetCurrentCompletionRequest()
    if completion_request:
      completion_request.OnCompleteDone()


  def GetErrorCount( self ):
    return self.CurrentBuffer().GetErrorCount()


  def GetWarningCount( self ):
    return self.CurrentBuffer().GetWarningCount()


  def DiagnosticUiSupportedForCurrentFiletype( self ):
    return any( x in DIAGNOSTIC_UI_FILETYPES or
                x in DIAGNOSTIC_UI_ASYNC_FILETYPES
                for x in vimsupport.CurrentFiletypes() )


  def ShouldDisplayDiagnostics( self ):
    return bool( self._user_options[ 'show_diagnostics_ui' ] and
                 self.DiagnosticUiSupportedForCurrentFiletype() )


  def _PopulateLocationListWithLatestDiagnostics( self ):
    return self.CurrentBuffer().PopulateLocationList()


  def FileParseRequestReady( self ):
    # Return True if server is not ready yet, to stop repeating check timer.
    return ( not self.IsServerReady() or
             self.CurrentBuffer().FileParseRequestReady() )


  def HandleFileParseRequest( self, block = False ):
    if not self.IsServerReady():
      return

    current_buffer = self.CurrentBuffer()
    # Order is important here:
    # FileParseRequestReady has a low cost, while
    # NativeFiletypeCompletionUsable is a blocking server request
    if ( not current_buffer.IsResponseHandled() and
         current_buffer.FileParseRequestReady( block ) and
         self.NativeFiletypeCompletionUsable() ):

      if self.ShouldDisplayDiagnostics():
        # Forcefuly update the location list, etc. from the parse request when
        # doing something like :YcmDiags
        current_buffer.UpdateDiagnostics( block is True )
      else:
        # YCM client has a hard-coded list of filetypes which are known
        # to support diagnostics, self.DiagnosticUiSupportedForCurrentFiletype()
        #
        # For filetypes which don't support diagnostics, we just want to check
        # the _latest_file_parse_request for any exception or UnknownExtraConf
        # response, to allow the server to raise configuration warnings, etc.
        # to the user. We ignore any other supplied data.
        current_buffer.GetResponse()

      # We set the file parse request as handled because we want to prevent
      # repeated issuing of the same warnings/errors/prompts. Setting this
      # makes IsRequestHandled return True until the next request is created.
      #
      # Note: it is the server's responsibility to determine the frequency of
      # error/warning/prompts when receiving a FileReadyToParse event, but
      # it is our responsibility to ensure that we only apply the
      # warning/error/prompt received once (for each event).
      current_buffer.MarkResponseHandled()


  def ShouldResendFileParseRequest( self ):
    return self.CurrentBuffer().ShouldResendParseRequest()


  def DebugInfo( self ):
    debug_info = ''
    if self._client_logfile:
      debug_info += 'Client logfile: {0}\n'.format( self._client_logfile )
    extra_data = {}
    self._AddExtraConfDataIfNeeded( extra_data )
    debug_info += FormatDebugInfoResponse( SendDebugInfoRequest( extra_data ) )
    debug_info += 'Server running at: {0}\n'.format(
      BaseRequest.server_location )
    if self._server_popen:
      debug_info += 'Server process ID: {0}\n'.format( self._server_popen.pid )
    if self._server_stdout and self._server_stderr:
      debug_info += ( 'Server logfiles:\n'
                      '  {0}\n'
                      '  {1}'.format( self._server_stdout,
                                      self._server_stderr ) )
    return debug_info


  def GetLogfiles( self ):
    logfiles_list = [ self._client_logfile,
                      self._server_stdout,
                      self._server_stderr ]

    extra_data = {}
    self._AddExtraConfDataIfNeeded( extra_data )
    debug_info = SendDebugInfoRequest( extra_data )
    if debug_info:
      completer = debug_info[ 'completer' ]
      if completer:
        for server in completer[ 'servers' ]:
          logfiles_list.extend( server[ 'logfiles' ] )

    logfiles = {}
    for logfile in logfiles_list:
      logfiles[ os.path.basename( logfile ) ] = logfile
    return logfiles


  def _OpenLogfile( self, logfile ):
    # Open log files in a horizontal window with the same behavior as the
    # preview window (same height and winfixheight enabled). Automatically
    # watch for changes. Set the cursor position at the end of the file.
    options = {
      'size': vimsupport.GetIntValue( '&previewheight' ),
      'fix': True,
      'focus': False,
      'watch': True,
      'position': 'end'
    }

    vimsupport.OpenFilename( logfile, options )


  def _CloseLogfile( self, logfile ):
    vimsupport.CloseBuffersForFilename( logfile )


  def ToggleLogs( self, *filenames ):
    logfiles = self.GetLogfiles()
    if not filenames:
      sorted_logfiles = sorted( list( logfiles ) )
      try:
        logfile_index = vimsupport.SelectFromList(
          'Which logfile do you wish to open (or close if already open)?',
          sorted_logfiles )
      except RuntimeError as e:
        vimsupport.PostVimMessage( str( e ) )
        return

      logfile = logfiles[ sorted_logfiles[ logfile_index ] ]
      if not vimsupport.BufferIsVisibleForFilename( logfile ):
        self._OpenLogfile( logfile )
      else:
        self._CloseLogfile( logfile )
      return

    for filename in set( filenames ):
      if filename not in logfiles:
        continue

      logfile = logfiles[ filename ]

      if not vimsupport.BufferIsVisibleForFilename( logfile ):
        self._OpenLogfile( logfile )
        continue

      self._CloseLogfile( logfile )


  def ShowDetailedDiagnostic( self ):
    detailed_diagnostic = BaseRequest().PostDataToHandler(
        BuildRequestData(), 'detailed_diagnostic' )

    if detailed_diagnostic and 'message' in detailed_diagnostic:
      vimsupport.PostVimMessage( detailed_diagnostic[ 'message' ],
                                 warning = False )


  def ForceCompileAndDiagnostics( self ):
    if not self.NativeFiletypeCompletionUsable():
      vimsupport.PostVimMessage(
          'Native filetype completion not supported for current file, '
          'cannot force recompilation.', warning = False )
      return False
    vimsupport.PostVimMessage(
        'Forcing compilation, this will block Vim until done.',
        warning = False )
    self.OnFileReadyToParse()
    self.HandleFileParseRequest( block = True )
    vimsupport.PostVimMessage( 'Diagnostics refreshed', warning = False )
    return True


  def ShowDiagnostics( self ):
    if not self.ForceCompileAndDiagnostics():
      return

    if not self._PopulateLocationListWithLatestDiagnostics():
      vimsupport.PostVimMessage( 'No warnings or errors detected.',
                                 warning = False )
      return

    if self._user_options[ 'open_loclist_on_ycm_diags' ]:
      vimsupport.OpenLocationList( focus = True )


  def _AddSyntaxDataIfNeeded( self, extra_data ):
    if not self._user_options[ 'seed_identifiers_with_syntax' ]:
      return
    filetype = vimsupport.CurrentFiletypes()[ 0 ]
    if filetype in self._filetypes_with_keywords_loaded:
      return

    if self.IsServerReady():
      self._filetypes_with_keywords_loaded.add( filetype )
    extra_data[ 'syntax_keywords' ] = list(
       syntax_parse.SyntaxKeywordsForCurrentBuffer() )


  def _AddTagsFilesIfNeeded( self, extra_data ):
    def GetTagFiles():
      tag_files = vim.eval( 'tagfiles()' )
      return [ os.path.join( utils.GetCurrentDirectory(), tag_file )
               for tag_file in tag_files ]

    if not self._user_options[ 'collect_identifiers_from_tags_files' ]:
      return
    extra_data[ 'tag_files' ] = GetTagFiles()


  def _AddExtraConfDataIfNeeded( self, extra_data ):
    def BuildExtraConfData( extra_conf_vim_data ):
      extra_conf_data = {}
      for expr in extra_conf_vim_data:
        try:
          extra_conf_data[ expr ] = vimsupport.VimExpressionToPythonType( expr )
        except vim.error:
          message = (
            "Error evaluating '{expr}' in the 'g:ycm_extra_conf_vim_data' "
            "option.".format( expr = expr ) )
          vimsupport.PostVimMessage( message, truncate = True )
          self._logger.exception( message )
      return extra_conf_data

    extra_conf_vim_data = self._user_options[ 'extra_conf_vim_data' ]
    if extra_conf_vim_data:
      extra_data[ 'extra_conf_data' ] = BuildExtraConfData(
        extra_conf_vim_data )


  def _AddUltiSnipsDataIfNeeded( self, extra_data ):
    # See :h UltiSnips#SnippetsInCurrentScope.
    try:
      vim.eval( 'UltiSnips#SnippetsInCurrentScope( 1 )' )
    except vim.error:
      return

    snippets = vimsupport.GetVariableValue( 'g:current_ulti_dict_info' )
    extra_data[ 'ultisnips_snippets' ] = [
      { 'trigger': trigger,
        'description': snippet[ 'description' ] }
      for trigger, snippet in iteritems( snippets )
    ]