示例#1
0
  def test_IncludeCache_Cached_NoNewMtime( self ):
    include_cache = IncludeCache()
    assert_that( include_cache._cache, equal_to( {} ) )
    old_includes = include_cache.GetIncludes( PathToTestFile( 'cache_test' ) )
    old_mtime = os.path.getmtime( PathToTestFile( 'cache_test' ) )

    assert_that( old_includes, contains_exactly( 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_exactly( has_properties( {
                                                      'name': 'foo.h',
                                                      'entry_type': 1
                                                    } ) ) } ) ) )

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

    assert_that( new_mtime, equal_to( old_mtime ) )
    assert_that( new_includes, contains_exactly( 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_exactly( has_properties( {
                                                      'name': 'foo.h',
                                                      'entry_type': 1
                                                    } ) ) } ) ) )
def IncludeCache_NotCached_DirAccessible_test():
    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
                    }))
            })))
示例#3
0
 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()
示例#4
0
  def test_IncludeCache_Cached_NewMtime( self ):
    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
                                  } ) )
                              } ) ) )
示例#5
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._include_cache = IncludeCache()
   self._diagnostic_store = None
   self._files_being_compiled = EphemeralValuesSet()
   self._logger = logging.getLogger( __name__ )
示例#6
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
                }))
            })))
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({}))
示例#8
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)
示例#9
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, {})