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 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 OnUserCommand(self, arguments): if not arguments: vimsupport.EchoText(USER_COMMANDS_HELP_MESSAGE) return command = arguments[0] if command == 'GoToDefinition': self._GoToDefinition() elif command == 'GoToDeclaration': self._GoToDeclaration() elif command == 'GoToDefinitionElseDeclaration': self._GoToDefinitionElseDeclaration() 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 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): return flags = self.flags.FlagsForFile(filename) if not flags: self.parse_future = None return self.parse_future = self.completer.UpdateTranslationUnitAsync( filename, self.GetUnsavedFilesVector(), flags) def OnBufferDelete(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 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 flags = self.flags.FlagsForFile(filename) or [] source = self.flags.ModuleForFile(filename) return 'Flags for {0} loaded from {1}:\n{2}'.format( filename, source, list(flags))
class ClangCompleter( Completer ): def __init__( self ): 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 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 ): 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 = int( vim.eval( "s:completion_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 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 ): return flags = self.flags.FlagsForFile( filename ) if not flags: self.parse_future = None return self.parse_future = self.completer.UpdateTranslationUnitAsync( filename, self.GetUnsavedFilesVector(), flags ) 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 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 ): return ShouldUseClang( start_column ) def DebugInfo( self ): filename = vim.current.buffer.name flags = self.flags.FlagsForFile( filename ) or [] return 'Flags for {0}:\n{1}'.format( filename, list( flags ) )