Exemple #1
0
def IncludeCache_Cached_NoNewMtime_test():
    include_cache = IncludeCache()
    eq_(include_cache._cache, {})
    old_includes = include_cache.GetIncludes(PathToTestFile('cache_test'))
    old_mtime = os.path.getmtime(PathToTestFile('cache_test'))

    assert_that(old_includes,
                contains(has_properties({
                    'name': 'foo.h',
                    'entry_type': 1
                })))
    assert_that(
        include_cache._cache,
        has_entry(
            PathToTestFile('cache_test'),
            has_entries({
                'mtime':
                old_mtime,
                'includes':
                contains(has_properties({
                    'name': 'foo.h',
                    'entry_type': 1
                }))
            })))

    new_includes = include_cache.GetIncludes(PathToTestFile('cache_test'))
    new_mtime = os.path.getmtime(PathToTestFile('cache_test'))

    eq_(new_mtime, old_mtime)
    assert_that(new_includes,
                contains(has_properties({
                    'name': 'foo.h',
                    'entry_type': 1
                })))
    assert_that(
        include_cache._cache,
        has_entry(
            PathToTestFile('cache_test'),
            has_entries({
                'mtime':
                new_mtime,
                'includes':
                contains(has_properties({
                    'name': 'foo.h',
                    'entry_type': 1
                }))
            })))
Exemple #2
0
 def test_IncludeCache_NotCached_DirAccessible( self ):
   include_cache = IncludeCache()
   assert_that( include_cache._cache, equal_to( {} ) )
   includes = include_cache.GetIncludes( PathToTestFile( 'cache_test' ) )
   mtime = os.path.getmtime( PathToTestFile( 'cache_test' ) )
   assert_that( includes, contains_exactly( has_properties( {
                                      'name': 'foo.h',
                                      'entry_type': 1
                                    } ) ) )
   assert_that( include_cache._cache,
                has_entry( PathToTestFile( 'cache_test' ),
                           has_entries( { 'mtime': mtime,
                             'includes': contains_exactly( has_properties( {
                                                     'name': 'foo.h',
                                                     'entry_type': 1
                                                   } ) ) } ) ) )
def IncludeCache_NotCached_DirInaccessible_test():
    include_cache = IncludeCache()
    assert_that(include_cache._cache, equal_to({}))
    includes = include_cache.GetIncludes(PathToTestFile('unknown_dir'))
    assert_that(includes, equal_to([]))
    assert_that(include_cache._cache, equal_to({}))
def IncludeCache_Cached_NewMtime_test():
    with TemporaryTestDir() as tmp_dir:
        include_cache = IncludeCache()
        assert_that(include_cache._cache, equal_to({}))
        foo_path = os.path.join(tmp_dir, 'foo')
        with open(foo_path, 'w') as foo_file:
            foo_file.write('foo')

        old_includes = include_cache.GetIncludes(tmp_dir)
        old_mtime = os.path.getmtime(tmp_dir)
        assert_that(
            old_includes,
            contains_exactly(has_properties({
                'name': 'foo',
                'entry_type': 1
            })))
        assert_that(
            include_cache._cache,
            has_entry(
                tmp_dir,
                has_entries({
                    'mtime':
                    old_mtime,
                    'includes':
                    contains_exactly(
                        has_properties({
                            'name': 'foo',
                            'entry_type': 1
                        }))
                })))

        sleep(2)

        bar_path = os.path.join(tmp_dir, 'bar')
        with open(bar_path, 'w') as bar_file:
            bar_file.write('bar')

        new_includes = include_cache.GetIncludes(tmp_dir)
        new_mtime = os.path.getmtime(tmp_dir)
        assert_that(old_mtime, not_(equal_to(new_mtime)))
        assert_that(
            new_includes,
            contains_inanyorder(
                has_properties({
                    'name': 'foo',
                    'entry_type': 1
                }), has_properties({
                    'name': 'bar',
                    'entry_type': 1
                })))
        assert_that(
            include_cache._cache,
            has_entry(
                tmp_dir,
                has_entries({
                    'mtime':
                    new_mtime,
                    'includes':
                    contains_inanyorder(
                        has_properties({
                            'name': 'foo',
                            'entry_type': 1
                        }), has_properties({
                            'name': 'bar',
                            'entry_type': 1
                        }))
                })))
Exemple #5
0
class ClangCompleter(Completer):
    def __init__(self, user_options):
        super(ClangCompleter, self).__init__(user_options)
        self._completer = ycm_core.ClangCompleter()
        self._flags = Flags()
        self._include_cache = IncludeCache()
        self._diagnostic_store = None
        self._files_being_compiled = EphemeralValuesSet()

    def SupportedFiletypes(self):
        return CLANG_FILETYPES

    def GetUnsavedFilesVector(self, request_data):
        files = ycm_core.UnsavedFileVector()
        for filename, file_data in iteritems(request_data['file_data']):
            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 = ToCppStringCompatible(contents)
            unsaved_file.contents_ = utf8_contents
            unsaved_file.length_ = len(utf8_contents)
            unsaved_file.filename_ = ToCppStringCompatible(filename)

            files.append(unsaved_file)
        return files

    def ShouldCompleteIncludeStatement(self, request_data):
        column_codepoint = request_data['column_codepoint'] - 1
        current_line = request_data['line_value']
        return INCLUDE_REGEX.match(current_line[:column_codepoint])

    def ShouldUseNowInner(self, request_data):
        if self.ShouldCompleteIncludeStatement(request_data):
            return True
        return super(ClangCompleter, self).ShouldUseNowInner(request_data)

    def GetIncludePaths(self, request_data):
        column_codepoint = request_data['column_codepoint'] - 1
        current_line = request_data['line_value']
        line = current_line[:column_codepoint]
        path_dir, quoted_include, start_codepoint = (
            GetIncompleteIncludeValue(line))
        if start_codepoint is None:
            return None

        request_data['start_codepoint'] = start_codepoint

        # We do what GCC does for <> versus "":
        # http://gcc.gnu.org/onlinedocs/cpp/Include-Syntax.html
        flags, filepath = self._FlagsForRequest(request_data)
        (quoted_include_paths, include_paths,
         framework_paths) = UserIncludePaths(flags, filepath)
        if quoted_include:
            include_paths.extend(quoted_include_paths)

        includes = IncludeList()

        for include_path in include_paths:
            unicode_path = ToUnicode(os.path.join(include_path, path_dir))
            includes.AddIncludes(self._include_cache.GetIncludes(unicode_path))

        if framework_paths:
            if path_dir:
                head, tail = PathLeftSplit(path_dir)
                path_dir = os.path.join(head + '.framework', 'Headers', tail)
            for framework_path in framework_paths:
                unicode_path = ToUnicode(os.path.join(framework_path,
                                                      path_dir))
                includes.AddIncludes(
                    self._include_cache.GetIncludes(unicode_path,
                                                    is_framework=not path_dir))

        return includes.GetIncludes()

    def ComputeCandidatesInner(self, request_data):
        flags, filename = self._FlagsForRequest(request_data)
        if not flags:
            raise RuntimeError(NO_COMPILE_FLAGS_MESSAGE)

        includes = self.GetIncludePaths(request_data)
        if includes is not None:
            return includes

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

        files = self.GetUnsavedFilesVector(request_data)
        line = request_data['line_num']
        column = request_data['start_column']
        with self._files_being_compiled.GetExclusive(filename):
            results = self._completer.CandidatesForLocationInFile(
                ToCppStringCompatible(filename),
                ToCppStringCompatible(request_data['filepath']), line, column,
                files, flags)

        if not results:
            raise RuntimeError(NO_COMPLETIONS_MESSAGE)

        return [ConvertCompletionData(x) for x in results]

    def GetSubcommandsMap(self):
        return {
            'GoToDefinition': (lambda self, request_data, args: self.
                               _GoToDefinition(request_data)),
            'GoToDeclaration': (lambda self, request_data, args: self.
                                _GoToDeclaration(request_data)),
            'GoTo':
            (lambda self, request_data, args: self._GoTo(request_data)),
            'GoToImprecise':
            (lambda self, request_data, args: self._GoToImprecise(request_data)
             ),
            'GoToInclude':
            (lambda self, request_data, args: self._GoToInclude(request_data)),
            'ClearCompilationFlagCache': (lambda self, request_data, args: self
                                          ._ClearCompilationFlagCache()),
            'GetType': (lambda self, request_data, args: self._GetSemanticInfo(
                request_data, func='GetTypeAtLocation')),
            'GetTypeImprecise':
            (lambda self, request_data, args: self._GetSemanticInfo(
                request_data, func='GetTypeAtLocation', reparse=False)),
            'GetParent':
            (lambda self, request_data, args: self._GetSemanticInfo(
                request_data, func='GetEnclosingFunctionAtLocation')),
            'FixIt':
            (lambda self, request_data, args: self._FixIt(request_data)),
            'GetDoc': (lambda self, request_data, args: self._GetSemanticInfo(
                request_data,
                reparse=True,
                func='GetDocsForLocationInFile',
                response_builder=_BuildGetDocResponse)),
            'GetDocImprecise':
            (lambda self, request_data, args: self._GetSemanticInfo(
                request_data,
                reparse=False,
                func='GetDocsForLocationInFile',
                response_builder=_BuildGetDocResponse)),
        }

    def _LocationForGoTo(self, goto_function, request_data, reparse=True):
        flags, filename = self._FlagsForRequest(request_data)
        if not flags:
            raise ValueError(NO_COMPILE_FLAGS_MESSAGE)

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

        files = self.GetUnsavedFilesVector(request_data)
        line = request_data['line_num']
        column = request_data['column_num']
        return getattr(self._completer, goto_function)(
            ToCppStringCompatible(filename),
            ToCppStringCompatible(request_data['filepath']), line, column,
            files, flags, reparse)

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

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

    def _GoTo(self, request_data):
        include_response = self._ResponseForInclude(request_data)
        if include_response:
            return include_response

        location = self._LocationForGoTo('GetDefinitionOrDeclarationLocation',
                                         request_data)
        if not location or not location.IsValid():
            raise RuntimeError('Can\'t jump to definition or declaration.')
        return _ResponseForLocation(location)

    def _GoToImprecise(self, request_data):
        include_response = self._ResponseForInclude(request_data)
        if include_response:
            return include_response

        location = self._LocationForGoTo('GetDefinitionOrDeclarationLocation',
                                         request_data,
                                         reparse=False)
        if not location or not location.IsValid():
            raise RuntimeError('Can\'t jump to definition or declaration.')
        return _ResponseForLocation(location)

    def _ResponseForInclude(self, request_data):
        """Returns response for include file location if cursor is on the
    include statement, None otherwise.
    Throws RuntimeError if cursor is on include statement and corresponding
    include file not found."""
        current_line = request_data['line_value']
        include_file_name, quoted_include = GetFullIncludeValue(current_line)
        if not include_file_name:
            return None

        flags, current_file_path = self._FlagsForRequest(request_data)
        (quoted_include_paths, include_paths,
         framework_paths) = UserIncludePaths(flags, current_file_path)

        include_file_path = None
        if quoted_include:
            include_file_path = _GetAbsolutePath(include_file_name,
                                                 quoted_include_paths)

        if not include_file_path:
            include_file_path = _GetAbsolutePath(include_file_name,
                                                 include_paths)

        if not include_file_path and framework_paths:
            head, tail = PathLeftSplit(include_file_name)
            include_file_name = os.path.join(head + '.framework', 'Headers',
                                             tail)
            include_file_path = _GetAbsolutePath(include_file_name,
                                                 framework_paths)

        if include_file_path:
            return responses.BuildGoToResponse(include_file_path,
                                               line_num=1,
                                               column_num=1)

        raise RuntimeError('Include file not found.')

    def _GoToInclude(self, request_data):
        include_response = self._ResponseForInclude(request_data)
        if not include_response:
            raise RuntimeError('Not an include/import line.')
        return include_response

    def _GetSemanticInfo(
            self,
            request_data,
            func,
            response_builder=responses.BuildDisplayMessageResponse,
            reparse=True):
        flags, filename = self._FlagsForRequest(request_data)
        if not flags:
            raise ValueError(NO_COMPILE_FLAGS_MESSAGE)

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

        files = self.GetUnsavedFilesVector(request_data)
        line = request_data['line_num']
        column = request_data['column_num']

        message = getattr(self._completer, func)(
            ToCppStringCompatible(filename),
            ToCppStringCompatible(request_data['filepath']), line, column,
            files, flags, reparse)

        if not message:
            message = "No semantic information available"

        return response_builder(message)

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

    def _FixIt(self, request_data):
        flags, filename = self._FlagsForRequest(request_data)
        if not flags:
            raise ValueError(NO_COMPILE_FLAGS_MESSAGE)

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

        files = self.GetUnsavedFilesVector(request_data)
        line = request_data['line_num']
        column = request_data['column_num']

        fixits = getattr(self._completer, "GetFixItsForLocationInFile")(
            ToCppStringCompatible(filename),
            ToCppStringCompatible(request_data['filepath']), line, column,
            files, flags, True)

        # don't raise an error if not fixits: - leave that to the client to respond
        # in a nice way

        return responses.BuildFixItResponse(fixits)

    def OnFileReadyToParse(self, request_data):
        flags, filename = self._FlagsForRequest(request_data)
        if not flags:
            raise ValueError(NO_COMPILE_FLAGS_MESSAGE)

        with self._files_being_compiled.GetExclusive(filename):
            diagnostics = self._completer.UpdateTranslationUnit(
                ToCppStringCompatible(filename),
                self.GetUnsavedFilesVector(request_data), flags)

        diagnostics = _FilterDiagnostics(diagnostics)
        self._diagnostic_store = DiagnosticsToDiagStructure(diagnostics)
        return responses.BuildDiagnosticResponse(
            diagnostics, request_data['filepath'],
            self.max_diagnostics_to_display)

    def OnBufferUnload(self, request_data):
        # FIXME: The filepath here is (possibly) wrong when overriding the
        # translation unit filename. If the buffer that the user closed is not the
        # "translation unit" filename, then we won't close the unit. It would
        # require the user to open the translation unit file, and close that.
        # Incidentally, doing so would flush the unit for any _other_ open files
        # which use that translation unit.
        #
        # Solving this would require remembering the graph of files to translation
        # units and only closing a unit when there are no files open which use it.
        self._completer.DeleteCachesForFile(
            ToCppStringCompatible(request_data['filepath']))

    def GetDetailedDiagnostic(self, request_data):
        current_line = request_data['line_num']
        current_column = request_data['column_num']
        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

        # FIXME: all of these calculations are currently working with byte
        # offsets, which are technically incorrect. We should be working with
        # codepoint offsets, as we want the nearest character-wise diagnostic
        for diagnostic in diagnostics:
            distance = abs(current_column -
                           diagnostic.location_.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):
        try:
            # Note that it only raises NoExtraConfDetected:
            #  - when extra_conf is None and,
            #  - there is no compilation database
            flags, filename = self._FlagsForRequest(request_data) or []
        except (NoExtraConfDetected, UnknownExtraConf):
            # If _FlagsForRequest returns None or raises, we use an empty list in
            # practice.
            flags = []
            filename = request_data['filepath']

        database = self._flags.LoadCompilationDatabase(filename)
        database_directory = database.database_directory if database else None

        database_item = responses.DebugInfoItem(
            key='compilation database path',
            value='{0}'.format(database_directory))
        flags_item = responses.DebugInfoItem(key='flags',
                                             value='{0}'.format(list(flags)))
        filename_item = responses.DebugInfoItem(key='translation unit',
                                                value=filename)

        return responses.BuildDebugInfoResponse(
            name='C-family', items=[database_item, flags_item, filename_item])

    def _FlagsForRequest(self, request_data):
        filename = request_data['filepath']

        if 'compilation_flags' in request_data:
            # Not supporting specifying the translation unit using this method as it
            # is only used by the tests.
            return (PrepareFlagsForClang(request_data['compilation_flags'],
                                         filename), filename)

        client_data = request_data['extra_conf_data']
        return self._flags.FlagsForFile(filename, client_data=client_data)
Exemple #6
0
def IncludeCache_NotCached_DirInaccessible_test():
    include_cache = IncludeCache()
    eq_(include_cache._cache, {})
    includes = include_cache.GetIncludes(PathToTestFile('unknown_dir'))
    eq_(includes, [])
    eq_(include_cache._cache, {})