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 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 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'] column = request_data['start_column'] with self._files_being_compiled.GetExclusive(filename): 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 GetSubcommandsMap(self): return { 'GoToDefinition': (lambda self, request_data: self._GoToDefinition(request_data)), 'GoToDeclaration': (lambda self, request_data: self._GoToDeclaration(request_data)), 'GoTo': (lambda self, request_data: self._GoTo(request_data)), 'GoToImprecise': (lambda self, request_data: self._GoToImprecise(request_data)), 'GoToInclude': (lambda self, request_data: self._GoToInclude(request_data)), 'ClearCompilationFlagCache': (lambda self, request_data: self._ClearCompilationFlagCache()), 'GetType': (lambda self, request_data: self._GetSemanticInfo( request_data, func='GetTypeAtLocation')), 'GetParent': (lambda self, request_data: self._GetSemanticInfo( request_data, func='GetEnclosingFunctionAtLocation')), 'FixIt': (lambda self, request_data: self._FixIt(request_data)), 'GetDoc': (lambda self, request_data: self._GetSemanticInfo( request_data, reparse=True, func='GetDocsForLocationInFile', response_builder=_BuildGetDocResponse)), 'GetDocQuick': (lambda self, request_data: self._GetSemanticInfo( request_data, reparse=False, func='GetDocsForLocationInFile', response_builder=_BuildGetDocResponse)), } def _LocationForGoTo(self, goto_function, request_data, reparse=True): 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'] column = request_data['column_num'] return getattr(self._completer, goto_function)(ToUtf8IfNeeded(filename), 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('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 _ResponseForLocation(location) def _GoToImprecise(self, request_data): include_response = self._ResponseForInclude(request_data) if include_response: return include_response location = self._LocationForGoTo('GetDefinitionLocation', request_data, reparse=False) if not location or not location.IsValid(): location = self._LocationForGoTo('GetDeclarationLocation', 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 = GetIncludeStatementValue( current_line) if not include_file_name: return None current_file_path = ToUtf8IfNeeded(request_data['filepath']) client_data = request_data.get('extra_conf_data', None) quoted_include_paths, include_paths = (self._flags.UserIncludePaths( current_file_path, client_data)) if quoted_include: include_file_path = _GetAbsolutePath(include_file_name, quoted_include_paths) if include_file_path: return responses.BuildGoToResponse(include_file_path, line_num=1, column_num=1) include_file_path = _GetAbsolutePath(include_file_name, include_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): 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'] column = request_data['column_num'] message = getattr(self._completer, func)(ToUtf8IfNeeded(filename), 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): 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'] column = request_data['column_num'] fixits = getattr(self._completer, "GetFixItsForLocationInFile")( ToUtf8IfNeeded(filename), 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): 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) with self._files_being_compiled.GetExclusive(filename): diagnostics = self._completer.UpdateTranslationUnit( ToUtf8IfNeeded(filename), self.GetUnsavedFilesVector(request_data), flags) diagnostics = _FilterDiagnostics(diagnostics) self._diagnostic_store = DiagnosticsToDiagStructure(diagnostics) return [ responses.BuildDiagnosticData(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'] 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 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): 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)
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)
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 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 ComputeCandidatesInner( self, request_data ): filename = request_data[ 'filepath' ] if not filename: return if self._completer.UpdatingTranslationUnit( ToCppStringCompatible( 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' ] column = request_data[ 'start_column' ] with self._files_being_compiled.GetExclusive( filename ): results = self._completer.CandidatesForLocationInFile( ToCppStringCompatible( filename ), 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 ): 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' ] column = request_data[ 'column_num' ] return getattr( self._completer, goto_function )( ToCppStringCompatible( filename ), 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( '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 _ResponseForLocation( location ) def _GoToImprecise( self, request_data ): include_response = self._ResponseForInclude( request_data ) if include_response: return include_response location = self._LocationForGoTo( 'GetDefinitionLocation', request_data, reparse = False ) if not location or not location.IsValid(): location = self._LocationForGoTo( 'GetDeclarationLocation', 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 = GetIncludeStatementValue( current_line ) if not include_file_name: return None current_file_path = request_data[ 'filepath' ] client_data = request_data.get( 'extra_conf_data', None ) quoted_include_paths, include_paths = ( self._flags.UserIncludePaths( current_file_path, client_data ) ) if quoted_include: include_file_path = _GetAbsolutePath( include_file_name, quoted_include_paths ) if include_file_path: return responses.BuildGoToResponse( include_file_path, line_num = 1, column_num = 1 ) include_file_path = _GetAbsolutePath( include_file_name, include_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 ): 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' ] column = request_data[ 'column_num' ] message = getattr( self._completer, func )( ToCppStringCompatible( filename ), 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 ): 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' ] column = request_data[ 'column_num' ] fixits = getattr( self._completer, "GetFixItsForLocationInFile" )( ToCppStringCompatible( filename ), 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 ): 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 ) 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.BuildDiagnosticData( x ) for x in diagnostics[ : self._max_diagnostics_to_display ] ] def OnBufferUnload( self, request_data ): 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 ): filename = request_data[ 'filepath' ] try: extra_conf = extra_conf_store.ModuleFileForSourceFile( filename ) except UnknownExtraConf as error: return ( 'C-family completer debug information:\n' ' Configuration file found but not loaded\n' ' Configuration path: {0}'.format( error.extra_conf_file ) ) try: # Note that it only raises NoExtraConfDetected: # - when extra_conf is None and, # - there is no compilation database flags = self._FlagsForRequest( request_data ) except NoExtraConfDetected: # No flags return ( 'C-family completer debug information:\n' ' No configuration file found\n' ' No compilation database found' ) # If _FlagsForRequest returns None or raises, we use an empty list in # practice. flags = flags or [] if extra_conf: # We got the flags from the extra conf file return ( 'C-family completer debug information:\n' ' Configuration file found and loaded\n' ' Configuration path: {0}\n' ' Flags: {1}'.format( extra_conf, list( flags ) ) ) try: database = self._flags.FindCompilationDatabase( os.path.dirname( filename ) ) except NoCompilationDatabase: # No flags return ( 'C-family completer debug information:\n' ' No configuration file found\n' ' No compilation database found' ) # We got the flags from the compilation database return ( 'C-family completer debug information:\n' ' No configuration file found\n' ' Using compilation database from: {0}\n' ' Flags: {1}'.format( database.database_directory, list( flags ) ) ) def _FlagsForRequest( self, request_data ): filename = 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 )
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 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 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 compfilename = filename filename = self._ParentForRequest(filename) 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'] column = request_data['start_column'] with self._files_being_compiled.GetExclusive(filename): results = self._completer.CandidatesForLocationInFile( ToUtf8IfNeeded(filename), ToUtf8IfNeeded(compfilename), 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', 'GoTo', 'GoToImprecise', 'ClearCompilationFlagCache', 'GetType', 'GetParent' ] def OnUserCommand(self, arguments, request_data): if not arguments: raise ValueError(self.UserCommandsHelpMessage()) # command_map maps: command -> { method, args } # # where: # "command" is the completer command entered by the user # (e.g. GoToDefinition) # "method" is a method to call for that command # (e.g. self._GoToDefinition) # "args" is a dictionary of # "method_argument" : "value" ... # which defines the kwargs (via the ** double splat) # when calling "method" command_map = { 'GoToDefinition': { 'method': self._GoToDefinition, 'args': { 'request_data': request_data } }, 'GoToDeclaration': { 'method': self._GoToDeclaration, 'args': { 'request_data': request_data } }, 'GoTo': { 'method': self._GoTo, 'args': { 'request_data': request_data } }, 'GoToImprecise': { 'method': self._GoToImprecise, 'args': { 'request_data': request_data } }, 'ClearCompilationFlagCache': { 'method': self._ClearCompilationFlagCache, 'args': {} }, 'GetType': { 'method': self._GetSemanticInfo, 'args': { 'request_data': request_data, 'func': 'GetTypeAtLocation' } }, 'GetParent': { 'method': self._GetSemanticInfo, 'args': { 'request_data': request_data, 'func': 'GetEnclosingFunctionAtLocation' } }, } try: command_def = command_map[arguments[0]] return command_def['method'](**(command_def['args'])) except KeyError: raise ValueError(self.UserCommandsHelpMessage()) def _LocationForGoTo(self, goto_function, request_data, reparse=True): filename = request_data['filepath'] if not filename: raise ValueError(INVALID_FILE_MESSAGE) gotofilename = filename filename = self._ParentForRequest(filename) flags = self._FlagsForRequest(request_data) if not flags: raise ValueError(NO_COMPILE_FLAGS_MESSAGE) files = self.GetUnsavedFilesVector(request_data) line = request_data['line_num'] column = request_data['column_num'] return getattr(self._completer, goto_function)(ToUtf8IfNeeded(filename), ToUtf8IfNeeded(gotofilename), 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): 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 _ResponseForLocation(location) def _GoToImprecise(self, request_data): location = self._LocationForGoTo('GetDefinitionLocation', request_data, reparse=False) if not location or not location.IsValid(): location = self._LocationForGoTo('GetDeclarationLocation', request_data, reparse=False) if not location or not location.IsValid(): raise RuntimeError('Can\'t jump to definition or declaration.') return _ResponseForLocation(location) def _GetSemanticInfo(self, request_data, func, reparse=True): filename = request_data['filepath'] if not filename: raise ValueError(INVALID_FILE_MESSAGE) gotofilename = filename filename = self._ParentForRequest(filename) flags = self._FlagsForRequest(request_data) if not flags: raise ValueError(NO_COMPILE_FLAGS_MESSAGE) files = self.GetUnsavedFilesVector(request_data) line = request_data['line_num'] column = request_data['column_num'] message = getattr(self._completer, func)(ToUtf8IfNeeded(filename), ToUtf8IfNeeded(gotofilename), line, column, files, flags, reparse) if not message: message = "No semantic information available" return responses.BuildDisplayMessageResponse(message) 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) with self._files_being_compiled.GetExclusive(filename): diagnostics = self._completer.UpdateTranslationUnit( ToUtf8IfNeeded(filename), self.GetUnsavedFilesVector(request_data), flags) diagnostics = _FilterDiagnostics(diagnostics) self._diagnostic_store = DiagnosticsToDiagStructure(diagnostics) return [ responses.BuildDiagnosticData(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'] 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 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): 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) def _ParentForRequest(self, filename): return self._flags.ParentForFile(ToUtf8IfNeeded(filename))