Пример #1
0
 def __init__(self, user_options):
     super(ClangCompleter, self).__init__(user_options)
     self._max_diagnostics_to_display = user_options[
         'max_diagnostics_to_display']
     self._completer = ycm_core.ClangCompleter()
     self._flags = Flags()
     self._diagnostic_store = None
Пример #2
0
 def __init__(self):
     super(ClangCompleter, self).__init__()
     self.completer = ycm_core.ClangCompleter()
     self.completer.EnableThreading()
     self.contents_holder = []
     self.filename_holder = []
     self.last_prepared_diagnostics = []
     self.parse_future = None
     self.flags = Flags()
     self.diagnostic_store = None
Пример #3
0
    def __init__(self):
        super(ClangCompleter, self).__init__()
        self.completer = ycm_core.ClangCompleter()
        self.completer.EnableThreading()
        self.contents_holder = []
        self.filename_holder = []
        self.last_prepared_diagnostics = []
        self.parse_future = None
        self.flags = Flags()
        self.diagnostic_store = None

        # We set this flag when a compilation request comes in while one is already
        # in progress. We use this to trigger the pending request after the previous
        # one completes (from GetDiagnosticsForCurrentFile because that's the only
        # method that knows when the compilation has finished).
        self.extra_parse_desired = False
Пример #4
0
  def __init__( self ):
    super( FilenameCompleter, self ).__init__()
    self._flags = Flags()

    self._path_regex = re.compile( """
      # 1 or more 'D:/'-like token or '/' or '~' or './' or '../'
      (?:[A-z]+:/|[/~]|\./|\.+/)+

      # any alphanumeric symbal and space literal
      (?:[ /a-zA-Z0-9()$+_~.\x80-\xff-\[\]]|

      # skip any special symbols
      [^\x20-\x7E]|

      # backslash and 1 char after it. + matches 1 or more of whole group
      \\.)*$
      """, re.X )

    include_regex_common = '^\s*#(?:include|import)\s*(?:"|<)'
    self._include_start_regex = re.compile( include_regex_common + '$' )
    self._include_regex = re.compile( include_regex_common )
Пример #5
0
class ClangCompleter(Completer):
    def __init__(self, user_options):
        super(ClangCompleter, self).__init__(user_options)
        self._max_diagnostics_to_display = user_options[
            'max_diagnostics_to_display']
        self._completer = ycm_core.ClangCompleter()
        self._flags = Flags()
        self._diagnostic_store = None

    def SupportedFiletypes(self):
        return CLANG_FILETYPES

    def GetUnsavedFilesVector(self, request_data):
        files = ycm_core.UnsavedFileVec()
        for filename, file_data in request_data['file_data'].iteritems():
            if not ClangAvailableForFiletypes(file_data['filetypes']):
                continue
            contents = file_data['contents']
            if not contents or not filename:
                continue

            unsaved_file = ycm_core.UnsavedFile()
            utf8_contents = ToUtf8IfNeeded(contents)
            unsaved_file.contents_ = utf8_contents
            unsaved_file.length_ = len(utf8_contents)
            unsaved_file.filename_ = ToUtf8IfNeeded(filename)

            files.append(unsaved_file)
        return files

    def ComputeCandidatesInner(self, request_data):
        filename = request_data['filepath']
        if not filename:
            return

        if self._completer.UpdatingTranslationUnit(ToUtf8IfNeeded(filename)):
            raise RuntimeError(PARSING_FILE_MESSAGE)

        flags = self._FlagsForRequest(request_data)
        if not flags:
            raise RuntimeError(NO_COMPILE_FLAGS_MESSAGE)

        files = self.GetUnsavedFilesVector(request_data)
        line = request_data['line_num'] + 1
        column = request_data['start_column'] + 1
        results = self._completer.CandidatesForLocationInFile(
            ToUtf8IfNeeded(filename), line, column, files, flags)

        if not results:
            raise RuntimeError(NO_COMPLETIONS_MESSAGE)

        return [ConvertCompletionData(x) for x in results]

    def DefinedSubcommands(self):
        return [
            'GoToDefinition', 'GoToDeclaration',
            'GoToDefinitionElseDeclaration', 'ClearCompilationFlagCache'
        ]

    def OnUserCommand(self, arguments, request_data):
        if not arguments:
            raise ValueError(self.UserCommandsHelpMessage())

        command = arguments[0]
        if command == 'GoToDefinition':
            return self._GoToDefinition(request_data)
        elif command == 'GoToDeclaration':
            return self._GoToDeclaration(request_data)
        elif command == 'GoToDefinitionElseDeclaration':
            return self._GoToDefinitionElseDeclaration(request_data)
        elif command == 'ClearCompilationFlagCache':
            return self._ClearCompilationFlagCache()
        raise ValueError(self.UserCommandsHelpMessage())

    def _LocationForGoTo(self, goto_function, request_data):
        filename = request_data['filepath']
        if not filename:
            raise ValueError(INVALID_FILE_MESSAGE)

        flags = self._FlagsForRequest(request_data)
        if not flags:
            raise ValueError(NO_COMPILE_FLAGS_MESSAGE)

        files = self.GetUnsavedFilesVector(request_data)
        line = request_data['line_num'] + 1
        column = request_data['column_num'] + 1
        return getattr(self._completer,
                       goto_function)(ToUtf8IfNeeded(filename), line, column,
                                      files, flags)

    def _GoToDefinition(self, request_data):
        location = self._LocationForGoTo('GetDefinitionLocation', request_data)
        if not location or not location.IsValid():
            raise RuntimeError('Can\'t jump to definition.')

        return responses.BuildGoToResponse(location.filename_,
                                           location.line_number_ - 1,
                                           location.column_number_ - 1)

    def _GoToDeclaration(self, request_data):
        location = self._LocationForGoTo('GetDeclarationLocation',
                                         request_data)
        if not location or not location.IsValid():
            raise RuntimeError('Can\'t jump to declaration.')

        return responses.BuildGoToResponse(location.filename_,
                                           location.line_number_ - 1,
                                           location.column_number_ - 1)

    def _GoToDefinitionElseDeclaration(self, request_data):
        location = self._LocationForGoTo('GetDefinitionLocation', request_data)
        if not location or not location.IsValid():
            location = self._LocationForGoTo('GetDeclarationLocation',
                                             request_data)
        if not location or not location.IsValid():
            raise RuntimeError('Can\'t jump to definition or declaration.')

        return responses.BuildGoToResponse(location.filename_,
                                           location.line_number_ - 1,
                                           location.column_number_ - 1)

    def _ClearCompilationFlagCache(self):
        self._flags.Clear()

    def OnFileReadyToParse(self, request_data):
        filename = request_data['filepath']
        contents = request_data['file_data'][filename]['contents']
        if contents.count('\n') < MIN_LINES_IN_FILE_TO_PARSE:
            raise ValueError(FILE_TOO_SHORT_MESSAGE)

        if not filename:
            raise ValueError(INVALID_FILE_MESSAGE)

        flags = self._FlagsForRequest(request_data)
        if not flags:
            raise ValueError(NO_COMPILE_FLAGS_MESSAGE)

        diagnostics = self._completer.UpdateTranslationUnit(
            ToUtf8IfNeeded(filename), self.GetUnsavedFilesVector(request_data),
            flags)

        diagnostics = _FilterDiagnostics(diagnostics)
        self._diagnostic_store = DiagnosticsToDiagStructure(diagnostics)
        return [
            ConvertToDiagnosticResponse(x)
            for x in diagnostics[:self._max_diagnostics_to_display]
        ]

    def OnBufferUnload(self, request_data):
        self._completer.DeleteCachesForFile(
            ToUtf8IfNeeded(request_data['unloaded_buffer']))

    def GetDetailedDiagnostic(self, request_data):
        current_line = request_data['line_num'] + 1
        current_column = request_data['column_num'] + 1
        current_file = request_data['filepath']

        if not self._diagnostic_store:
            raise ValueError(NO_DIAGNOSTIC_MESSAGE)

        diagnostics = self._diagnostic_store[current_file][current_line]
        if not diagnostics:
            raise ValueError(NO_DIAGNOSTIC_MESSAGE)

        closest_diagnostic = None
        distance_to_closest_diagnostic = 999

        for diagnostic in diagnostics:
            distance = abs(current_column - diagnostic.column_number_)
            if distance < distance_to_closest_diagnostic:
                distance_to_closest_diagnostic = distance
                closest_diagnostic = diagnostic

        return responses.BuildDisplayMessageResponse(
            closest_diagnostic.long_formatted_text_)

    def DebugInfo(self, request_data):
        filename = request_data['filepath']
        if not filename:
            return ''
        flags = self._FlagsForRequest(request_data) or []
        source = extra_conf_store.ModuleFileForSourceFile(filename)
        return 'Flags for {0} loaded from {1}:\n{2}'.format(
            filename, source, list(flags))

    def _FlagsForRequest(self, request_data):
        filename = ToUtf8IfNeeded(request_data['filepath'])
        if 'compilation_flags' in request_data:
            return PrepareFlagsForClang(request_data['compilation_flags'],
                                        filename)
        client_data = request_data.get('extra_conf_data', None)
        return self._flags.FlagsForFile(filename, client_data=client_data)
Пример #6
0
class FilenameCompleter(Completer):
    """
  General completer that provides filename and filepath completions.
  """
    def __init__(self, user_options):
        super(FilenameCompleter, self).__init__(user_options)
        self._flags = Flags()

        self._path_regex = re.compile(
            """
      # 1 or more 'D:/'-like token or '/' or '~' or './' or '../'
      (?:[A-z]+:/|[/~]|\./|\.+/)+

      # any alphanumeric symbal and space literal
      (?:[ /a-zA-Z0-9()$+_~.\x80-\xff-\[\]]|

      # skip any special symbols
      [^\x20-\x7E]|

      # backslash and 1 char after it. + matches 1 or more of whole group
      \\.)*$
      """, re.X)

        include_regex_common = '^\s*#(?:include|import)\s*(?:"|<)'
        self._include_start_regex = re.compile(include_regex_common + '$')
        self._include_regex = re.compile(include_regex_common)

    def AtIncludeStatementStart(self, request_data):
        start_column = request_data['start_column']
        current_line = request_data['line_value']
        filepath = request_data['filepath']
        filetypes = request_data['file_data'][filepath]['filetypes']
        return (InCFamilyFile(filetypes) and self._include_start_regex.match(
            current_line[:start_column]))

    def ShouldUseNowInner(self, request_data):
        start_column = request_data['start_column']
        current_line = request_data['line_value']
        return (start_column
                and (current_line[start_column - 1] == '/'
                     or self.AtIncludeStatementStart(request_data)))

    def SupportedFiletypes(self):
        return []

    def ComputeCandidatesInner(self, request_data):
        current_line = request_data['line_value']
        start_column = request_data['start_column']
        filepath = request_data['filepath']
        filetypes = request_data['file_data'][filepath]['filetypes']
        line = current_line[:start_column]

        if InCFamilyFile(filetypes):
            include_match = self._include_regex.search(line)
            if include_match:
                path_dir = line[include_match.end():]
                # We do what GCC does for <> versus "":
                # http://gcc.gnu.org/onlinedocs/cpp/Include-Syntax.html
                include_current_file_dir = '<' not in include_match.group()
                return _GenerateCandidatesForPaths(
                    self.GetPathsIncludeCase(path_dir,
                                             include_current_file_dir,
                                             filepath))

        path_match = self._path_regex.search(line)
        path_dir = os.path.expanduser(path_match.group()) if path_match else ''

        return _GenerateCandidatesForPaths(
            _GetPathsStandardCase(
                path_dir,
                self.user_options['filepath_completion_use_working_dir'],
                filepath))

    def GetPathsIncludeCase(self, path_dir, include_current_file_dir,
                            filepath):
        paths = []
        include_paths = self._flags.UserIncludePaths(filepath)

        if include_current_file_dir:
            include_paths.append(os.path.dirname(filepath))

        for include_path in include_paths:
            try:
                relative_paths = os.listdir(
                    os.path.join(include_path, path_dir))
            except:
                relative_paths = []

            paths.extend(
                os.path.join(include_path, path_dir, relative_path)
                for relative_path in relative_paths)

        return sorted(set(paths))
Пример #7
0
class ClangCompleter(Completer):
    def __init__(self):
        super(ClangCompleter, self).__init__()
        self.completer = ycm_core.ClangCompleter()
        self.completer.EnableThreading()
        self.contents_holder = []
        self.filename_holder = []
        self.last_prepared_diagnostics = []
        self.parse_future = None
        self.flags = Flags()
        self.diagnostic_store = None

        # We set this flag when a compilation request comes in while one is already
        # in progress. We use this to trigger the pending request after the previous
        # one completes (from GetDiagnosticsForCurrentFile because that's the only
        # method that knows when the compilation has finished).
        self.extra_parse_desired = False

    def SupportedFiletypes(self):
        return CLANG_FILETYPES

    def GetUnsavedFilesVector(self):
        # CAREFUL HERE! For UnsavedFile filename and contents we are referring
        # directly to Python-allocated and -managed memory since we are accepting
        # pointers to data members of python objects. We need to ensure that those
        # objects outlive our UnsavedFile objects. This is why we need the
        # contents_holder and filename_holder lists, to make sure the string objects
        # are still around when we call CandidatesForQueryAndLocationInFile.  We do
        # this to avoid an extra copy of the entire file contents.

        files = ycm_core.UnsavedFileVec()
        self.contents_holder = []
        self.filename_holder = []
        for buffer in vimsupport.GetUnsavedBuffers():
            if not ClangAvailableForBuffer(buffer):
                continue
            contents = '\n'.join(buffer)
            name = buffer.name
            if not contents or not name:
                continue
            self.contents_holder.append(contents)
            self.filename_holder.append(name)

            unsaved_file = ycm_core.UnsavedFile()
            unsaved_file.contents_ = self.contents_holder[-1]
            unsaved_file.length_ = len(self.contents_holder[-1])
            unsaved_file.filename_ = self.filename_holder[-1]

            files.append(unsaved_file)

        return files

    def CandidatesForQueryAsync(self, query, start_column):
        filename = vim.current.buffer.name

        if not filename:
            return

        if self.completer.UpdatingTranslationUnit(filename):
            vimsupport.PostVimMessage(
                'Still parsing file, no completions yet.')
            self.completions_future = None
            return

        flags = self.flags.FlagsForFile(filename)
        if not flags:
            vimsupport.PostVimMessage(
                'Still no compile flags, no completions yet.')
            self.completions_future = None
            return

        # TODO: sanitize query, probably in C++ code

        files = ycm_core.UnsavedFileVec()
        if not query:
            files = self.GetUnsavedFilesVector()

        line, _ = vim.current.window.cursor
        column = start_column + 1
        self.completions_future = (
            self.completer.CandidatesForQueryAndLocationInFileAsync(
                query, filename, line, column, files, flags))

    def CandidatesFromStoredRequest(self):
        if not self.completions_future:
            return []
        results = [
            CompletionDataToDict(x)
            for x in self.completions_future.GetResults()
        ]
        if not results:
            vimsupport.PostVimMessage(
                'No completions found; errors in the file?')
        return results

    def DefinedSubcommands(self):
        return [
            'GoToDefinition', 'GoToDeclaration',
            'GoToDefinitionElseDeclaration', 'ClearCompilationFlagCache'
        ]

    def OnUserCommand(self, arguments):
        if not arguments:
            self.EchoUserCommandsHelpMessage()
            return

        command = arguments[0]
        if command == 'GoToDefinition':
            self._GoToDefinition()
        elif command == 'GoToDeclaration':
            self._GoToDeclaration()
        elif command == 'GoToDefinitionElseDeclaration':
            self._GoToDefinitionElseDeclaration()
        elif command == 'ClearCompilationFlagCache':
            self._ClearCompilationFlagCache()

    def _LocationForGoTo(self, goto_function):
        filename = vim.current.buffer.name
        if not filename:
            return None

        flags = self.flags.FlagsForFile(filename)
        if not flags:
            vimsupport.PostVimMessage(
                'Still no compile flags, can\'t compile.')
            return None

        files = self.GetUnsavedFilesVector()
        line, column = vimsupport.CurrentLineAndColumn()
        # Making the line & column 1-based instead of 0-based
        line += 1
        column += 1
        return getattr(self.completer, goto_function)(filename, line, column,
                                                      files, flags)

    def _GoToDefinition(self):
        location = self._LocationForGoTo('GetDefinitionLocation')
        if not location or not location.IsValid():
            vimsupport.PostVimMessage('Can\'t jump to definition.')
            return

        vimsupport.JumpToLocation(location.filename_, location.line_number_,
                                  location.column_number_)

    def _GoToDeclaration(self):
        location = self._LocationForGoTo('GetDeclarationLocation')
        if not location or not location.IsValid():
            vimsupport.PostVimMessage('Can\'t jump to declaration.')
            return

        vimsupport.JumpToLocation(location.filename_, location.line_number_,
                                  location.column_number_)

    def _GoToDefinitionElseDeclaration(self):
        location = self._LocationForGoTo('GetDefinitionLocation')
        if not location or not location.IsValid():
            location = self._LocationForGoTo('GetDeclarationLocation')
        if not location or not location.IsValid():
            vimsupport.PostVimMessage(
                'Can\'t jump to definition or declaration.')
            return

        vimsupport.JumpToLocation(location.filename_, location.line_number_,
                                  location.column_number_)

    def _ClearCompilationFlagCache(self):
        self.flags.Clear()

    def OnFileReadyToParse(self):
        if vimsupport.NumLinesInBuffer(vim.current.buffer) < 5:
            self.parse_future = None
            return

        filename = vim.current.buffer.name
        if not filename:
            return

        if self.completer.UpdatingTranslationUnit(filename):
            self.extra_parse_desired = True
            return

        flags = self.flags.FlagsForFile(filename)
        if not flags:
            self.parse_future = None
            return

        self.parse_future = self.completer.UpdateTranslationUnitAsync(
            filename, self.GetUnsavedFilesVector(), flags)

        self.extra_parse_desired = False

    def OnBufferUnload(self, deleted_buffer_file):
        self.completer.DeleteCachesForFileAsync(deleted_buffer_file)

    def DiagnosticsForCurrentFileReady(self):
        if not self.parse_future:
            return False

        return self.parse_future.ResultsReady()

    def GettingCompletions(self):
        return self.completer.UpdatingTranslationUnit(vim.current.buffer.name)

    def GetDiagnosticsForCurrentFile(self):
        if self.DiagnosticsForCurrentFileReady():
            diagnostics = self.completer.DiagnosticsForFile(
                vim.current.buffer.name)
            self.diagnostic_store = DiagnosticsToDiagStructure(diagnostics)
            self.last_prepared_diagnostics = [
                DiagnosticToDict(x)
                for x in diagnostics[:MAX_DIAGNOSTICS_TO_DISPLAY]
            ]
            self.parse_future = None

            if self.extra_parse_desired:
                self.OnFileReadyToParse()

        return self.last_prepared_diagnostics

    def ShowDetailedDiagnostic(self):
        current_line, current_column = vimsupport.CurrentLineAndColumn()

        # CurrentLineAndColumn() numbers are 0-based, clang numbers are 1-based
        current_line += 1
        current_column += 1

        current_file = vim.current.buffer.name

        if not self.diagnostic_store:
            vimsupport.PostVimMessage("No diagnostic for current line!")
            return

        diagnostics = self.diagnostic_store[current_file][current_line]
        if not diagnostics:
            vimsupport.PostVimMessage("No diagnostic for current line!")
            return

        closest_diagnostic = None
        distance_to_closest_diagnostic = 999

        for diagnostic in diagnostics:
            distance = abs(current_column - diagnostic.column_number_)
            if distance < distance_to_closest_diagnostic:
                distance_to_closest_diagnostic = distance
                closest_diagnostic = diagnostic

        vimsupport.EchoText(closest_diagnostic.long_formatted_text_)

    def ShouldUseNow(self, start_column):
        # We don't want to use the Completer API cache, we use one in the C++ code.
        return self.ShouldUseNowInner(start_column)

    def DebugInfo(self):
        filename = vim.current.buffer.name
        if not filename:
            return ''
        flags = self.flags.FlagsForFile(filename) or []
        source = extra_conf_store.ModuleFileForSourceFile(filename)
        return 'Flags for {0} loaded from {1}:\n{2}'.format(
            filename, source, list(flags))