Example #1
0
  def SendSignatureHelpRequest( self ):
    """Send a signature help request, if we're ready to. Return whether or not a
    request was sent (and should be checked later)"""
    if not self.NativeFiletypeCompletionUsable():
      return False

    for filetype in vimsupport.CurrentFiletypes():
      if not self.SignatureHelpAvailableRequestComplete( filetype ):
        continue

      sig_help_available = self._signature_help_available_requests[
          filetype ].Response()
      if sig_help_available == 'NO':
        continue

      if sig_help_available == 'PENDING':
        # Send another /signature_help_available request
        self._signature_help_available_requests[ filetype ].Start( filetype )
        continue

      if not self._latest_completion_request:
        return False

      request_data = self._latest_completion_request.request_data.copy()
      request_data[ 'signature_help_state' ] = self._signature_help_state.state

      self._AddExtraConfDataIfNeeded( request_data )

      self._latest_signature_help_request = SignatureHelpRequest( request_data )
      self._latest_signature_help_request.Start()
      return True

    return False
Example #2
0
  def SendSignatureHelpRequest( self ):
    filetype = vimsupport.CurrentFiletypes()[ 0 ]
    if not self._signature_help_available_requests[ filetype ].Done():
      return

    sig_help_available = self._signature_help_available_requests[
        filetype ].Response()
    if sig_help_available == 'NO':
      return

    if sig_help_available == 'PENDING':
      # Send another /signature_help_available request
      self._signature_help_available_requests[ filetype ].Start( filetype )
      return

    if not self.NativeFiletypeCompletionUsable():
      return

    if not self._latest_completion_request:
      return

    request_data = self._latest_completion_request.request_data.copy()
    request_data[ 'signature_help_state' ] = self._signature_help_state.state

    self._AddExtraConfDataIfNeeded( request_data )

    self._latest_signature_help_request = SignatureHelpRequest(
      request_data )
    self._latest_signature_help_request.Start()
Example #3
0
class YouCompleteMe:
    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._latest_signature_help_request = None
        self._signature_help_available_requests = SigHelpAvailableByFileType()
        self._signature_help_state = signature_help.SignatureHelpState()
        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_requests = {}

        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)
        # See https://github.com/Valloric/ycmd#exit-codes for the list of exit
        # codes.
        if return_code == 3:
            error_message = CORE_UNEXPECTED_MESSAGE.format(logfile=logfile)
        elif return_code == 4:
            error_message = CORE_MISSING_MESSAGE
        elif return_code == 7:
            error_message = CORE_OUTDATED_MESSAGE
        elif return_code == 8:
            error_message = NO_PYTHON2_SUPPORT_MESSAGE
        else:
            error_message = EXIT_CODE_UNEXPECTED_MESSAGE.format(
                code=return_code, logfile=logfile)

        if return_code != 8:
            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 SignatureHelpAvailableRequestComplete(self, filetype, send_new=True):
        """Triggers or polls signature help available request. Returns whether or
    not the request is complete. When send_new is False, won't send a new
    request, only return the current status (This is used by the tests)"""
        if not send_new and filetype not in self._signature_help_available_requests:
            return False

        return self._signature_help_available_requests[filetype].Done()

    def SendSignatureHelpRequest(self):
        """Send a signature help request, if we're ready to. Return whether or not a
    request was sent (and should be checked later)"""
        if not self.NativeFiletypeCompletionUsable():
            return False

        for filetype in vimsupport.CurrentFiletypes():
            if not self.SignatureHelpAvailableRequestComplete(filetype):
                continue

            sig_help_available = self._signature_help_available_requests[
                filetype].Response()
            if sig_help_available == 'NO':
                continue

            if sig_help_available == 'PENDING':
                # Send another /signature_help_available request
                self._signature_help_available_requests[filetype].Start(
                    filetype)
                continue

            if not self._latest_completion_request:
                return False

            request_data = self._latest_completion_request.request_data.copy()
            request_data[
                'signature_help_state'] = self._signature_help_state.state

            self._AddExtraConfDataIfNeeded(request_data)

            self._latest_signature_help_request = SignatureHelpRequest(
                request_data)
            self._latest_signature_help_request.Start()
            return True

        return False

    def SignatureHelpRequestReady(self):
        return bool(self._latest_signature_help_request
                    and self._latest_signature_help_request.Done())

    def GetSignatureHelpResponse(self):
        return self._latest_signature_help_request.Response()

    def ClearSignatureHelp(self):
        self.UpdateSignatureHelp({})
        if self._latest_signature_help_request:
            self._latest_signature_help_request.Reset()

    def UpdateSignatureHelp(self, signature_info):
        self._signature_help_state = signature_help.UpdateSignatureHelp(
            self._signature_help_state, signature_info)

    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):
        if not self._user_options['show_diagnostics_ui']:
            return

        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

        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())

    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 OnFileTypeSet(self):
        buffer_number = vimsupport.GetCurrentBufferNumber()
        filetypes = vimsupport.CurrentFiletypes()
        self._buffers[buffer_number].UpdateFromFileTypes(filetypes)
        self.OnBufferVisit()

    def OnBufferVisit(self):
        for filetype in vimsupport.CurrentFiletypes():
            # Send the signature help available request for these filetypes if we need
            # to (as a side effect of checking if it is complete)
            self.SignatureHelpAvailableRequestComplete(filetype, True)

        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 _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._user_options['show_diagnostics_ui']:
                # Forcefuly update the location list, etc. from the parse request when
                # doing something like :YcmDiags
                current_buffer.UpdateDiagnostics(block)
            else:
                # If the user disabled 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, size, mods, 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.
        if not size:
            size = vimsupport.GetIntValue('&previewheight')

        options = {
            'size': size,
            'fix': True,
            'focus': False,
            'watch': True,
            'position': 'end',
            'mods': mods
        }

        vimsupport.OpenFilename(logfile, options)

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

    def ToggleLogs(self, size, mods, *filenames):
        logfiles = self.GetLogfiles()
        if not filenames:
            sorted_logfiles = sorted(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(size, mods, 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(size, mods, 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 snippets.items()]