def __init__(self, user_options): super(ClangCompleter, self).__init__(user_options) self._max_diagnostics_to_display = user_options[ 'max_diagnostics_to_display'] self._completer = ycm_core.ClangCompleter() self._flags = Flags() self._diagnostic_store = None
def __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 __init__(self): super(ClangCompleter, self).__init__() self.completer = ycm_core.ClangCompleter() self.completer.EnableThreading() self.contents_holder = [] self.filename_holder = [] self.last_prepared_diagnostics = [] self.parse_future = None self.flags = Flags() self.diagnostic_store = None # We set this flag when a compilation request comes in while one is already # in progress. We use this to trigger the pending request after the previous # one completes (from GetDiagnosticsForCurrentFile because that's the only # method that knows when the compilation has finished). self.extra_parse_desired = False
def __init__( self ): super( FilenameCompleter, self ).__init__() self._flags = Flags() self._path_regex = re.compile( """ # 1 or more 'D:/'-like token or '/' or '~' or './' or '../' (?:[A-z]+:/|[/~]|\./|\.+/)+ # any alphanumeric symbal and space literal (?:[ /a-zA-Z0-9()$+_~.\x80-\xff-\[\]]| # skip any special symbols [^\x20-\x7E]| # backslash and 1 char after it. + matches 1 or more of whole group \\.)*$ """, re.X ) include_regex_common = '^\s*#(?:include|import)\s*(?:"|<)' self._include_start_regex = re.compile( include_regex_common + '$' ) self._include_regex = re.compile( include_regex_common )
class ClangCompleter(Completer): def __init__(self, user_options): super(ClangCompleter, self).__init__(user_options) self._max_diagnostics_to_display = user_options[ 'max_diagnostics_to_display'] self._completer = ycm_core.ClangCompleter() self._flags = Flags() self._diagnostic_store = None def SupportedFiletypes(self): return CLANG_FILETYPES def GetUnsavedFilesVector(self, request_data): files = ycm_core.UnsavedFileVec() for filename, file_data in request_data['file_data'].iteritems(): if not ClangAvailableForFiletypes(file_data['filetypes']): continue contents = file_data['contents'] if not contents or not filename: continue unsaved_file = ycm_core.UnsavedFile() utf8_contents = ToUtf8IfNeeded(contents) unsaved_file.contents_ = utf8_contents unsaved_file.length_ = len(utf8_contents) unsaved_file.filename_ = ToUtf8IfNeeded(filename) files.append(unsaved_file) return files def ComputeCandidatesInner(self, request_data): filename = request_data['filepath'] if not filename: return if self._completer.UpdatingTranslationUnit(ToUtf8IfNeeded(filename)): raise RuntimeError(PARSING_FILE_MESSAGE) flags = self._FlagsForRequest(request_data) if not flags: raise RuntimeError(NO_COMPILE_FLAGS_MESSAGE) files = self.GetUnsavedFilesVector(request_data) line = request_data['line_num'] + 1 column = request_data['start_column'] + 1 results = self._completer.CandidatesForLocationInFile( ToUtf8IfNeeded(filename), line, column, files, flags) if not results: raise RuntimeError(NO_COMPLETIONS_MESSAGE) return [ConvertCompletionData(x) for x in results] def DefinedSubcommands(self): return [ 'GoToDefinition', 'GoToDeclaration', 'GoToDefinitionElseDeclaration', 'ClearCompilationFlagCache' ] def OnUserCommand(self, arguments, request_data): if not arguments: raise ValueError(self.UserCommandsHelpMessage()) command = arguments[0] if command == 'GoToDefinition': return self._GoToDefinition(request_data) elif command == 'GoToDeclaration': return self._GoToDeclaration(request_data) elif command == 'GoToDefinitionElseDeclaration': return self._GoToDefinitionElseDeclaration(request_data) elif command == 'ClearCompilationFlagCache': return self._ClearCompilationFlagCache() raise ValueError(self.UserCommandsHelpMessage()) def _LocationForGoTo(self, goto_function, request_data): filename = request_data['filepath'] if not filename: raise ValueError(INVALID_FILE_MESSAGE) flags = self._FlagsForRequest(request_data) if not flags: raise ValueError(NO_COMPILE_FLAGS_MESSAGE) files = self.GetUnsavedFilesVector(request_data) line = request_data['line_num'] + 1 column = request_data['column_num'] + 1 return getattr(self._completer, goto_function)(ToUtf8IfNeeded(filename), line, column, files, flags) def _GoToDefinition(self, request_data): location = self._LocationForGoTo('GetDefinitionLocation', request_data) if not location or not location.IsValid(): raise RuntimeError('Can\'t jump to definition.') return responses.BuildGoToResponse(location.filename_, location.line_number_ - 1, location.column_number_ - 1) def _GoToDeclaration(self, request_data): location = self._LocationForGoTo('GetDeclarationLocation', request_data) if not location or not location.IsValid(): raise RuntimeError('Can\'t jump to declaration.') return responses.BuildGoToResponse(location.filename_, location.line_number_ - 1, location.column_number_ - 1) def _GoToDefinitionElseDeclaration(self, request_data): location = self._LocationForGoTo('GetDefinitionLocation', request_data) if not location or not location.IsValid(): location = self._LocationForGoTo('GetDeclarationLocation', request_data) if not location or not location.IsValid(): raise RuntimeError('Can\'t jump to definition or declaration.') return responses.BuildGoToResponse(location.filename_, location.line_number_ - 1, location.column_number_ - 1) def _ClearCompilationFlagCache(self): self._flags.Clear() def OnFileReadyToParse(self, request_data): filename = request_data['filepath'] contents = request_data['file_data'][filename]['contents'] if contents.count('\n') < MIN_LINES_IN_FILE_TO_PARSE: raise ValueError(FILE_TOO_SHORT_MESSAGE) if not filename: raise ValueError(INVALID_FILE_MESSAGE) flags = self._FlagsForRequest(request_data) if not flags: raise ValueError(NO_COMPILE_FLAGS_MESSAGE) diagnostics = self._completer.UpdateTranslationUnit( ToUtf8IfNeeded(filename), self.GetUnsavedFilesVector(request_data), flags) diagnostics = _FilterDiagnostics(diagnostics) self._diagnostic_store = DiagnosticsToDiagStructure(diagnostics) return [ ConvertToDiagnosticResponse(x) for x in diagnostics[:self._max_diagnostics_to_display] ] def OnBufferUnload(self, request_data): self._completer.DeleteCachesForFile( ToUtf8IfNeeded(request_data['unloaded_buffer'])) def GetDetailedDiagnostic(self, request_data): current_line = request_data['line_num'] + 1 current_column = request_data['column_num'] + 1 current_file = request_data['filepath'] if not self._diagnostic_store: raise ValueError(NO_DIAGNOSTIC_MESSAGE) diagnostics = self._diagnostic_store[current_file][current_line] if not diagnostics: raise ValueError(NO_DIAGNOSTIC_MESSAGE) closest_diagnostic = None distance_to_closest_diagnostic = 999 for diagnostic in diagnostics: distance = abs(current_column - diagnostic.column_number_) if distance < distance_to_closest_diagnostic: distance_to_closest_diagnostic = distance closest_diagnostic = diagnostic return responses.BuildDisplayMessageResponse( closest_diagnostic.long_formatted_text_) def DebugInfo(self, request_data): filename = request_data['filepath'] if not filename: return '' flags = self._FlagsForRequest(request_data) or [] source = extra_conf_store.ModuleFileForSourceFile(filename) return 'Flags for {0} loaded from {1}:\n{2}'.format( filename, source, list(flags)) def _FlagsForRequest(self, request_data): filename = ToUtf8IfNeeded(request_data['filepath']) if 'compilation_flags' in request_data: return PrepareFlagsForClang(request_data['compilation_flags'], filename) client_data = request_data.get('extra_conf_data', None) return self._flags.FlagsForFile(filename, client_data=client_data)
class FilenameCompleter(Completer): """ General completer that provides filename and filepath completions. """ def __init__(self, user_options): super(FilenameCompleter, self).__init__(user_options) self._flags = Flags() self._path_regex = re.compile( """ # 1 or more 'D:/'-like token or '/' or '~' or './' or '../' (?:[A-z]+:/|[/~]|\./|\.+/)+ # any alphanumeric symbal and space literal (?:[ /a-zA-Z0-9()$+_~.\x80-\xff-\[\]]| # skip any special symbols [^\x20-\x7E]| # backslash and 1 char after it. + matches 1 or more of whole group \\.)*$ """, re.X) include_regex_common = '^\s*#(?:include|import)\s*(?:"|<)' self._include_start_regex = re.compile(include_regex_common + '$') self._include_regex = re.compile(include_regex_common) def AtIncludeStatementStart(self, request_data): start_column = request_data['start_column'] current_line = request_data['line_value'] filepath = request_data['filepath'] filetypes = request_data['file_data'][filepath]['filetypes'] return (InCFamilyFile(filetypes) and self._include_start_regex.match( current_line[:start_column])) def ShouldUseNowInner(self, request_data): start_column = request_data['start_column'] current_line = request_data['line_value'] return (start_column and (current_line[start_column - 1] == '/' or self.AtIncludeStatementStart(request_data))) def SupportedFiletypes(self): return [] def ComputeCandidatesInner(self, request_data): current_line = request_data['line_value'] start_column = request_data['start_column'] filepath = request_data['filepath'] filetypes = request_data['file_data'][filepath]['filetypes'] line = current_line[:start_column] if InCFamilyFile(filetypes): include_match = self._include_regex.search(line) if include_match: path_dir = line[include_match.end():] # We do what GCC does for <> versus "": # http://gcc.gnu.org/onlinedocs/cpp/Include-Syntax.html include_current_file_dir = '<' not in include_match.group() return _GenerateCandidatesForPaths( self.GetPathsIncludeCase(path_dir, include_current_file_dir, filepath)) path_match = self._path_regex.search(line) path_dir = os.path.expanduser(path_match.group()) if path_match else '' return _GenerateCandidatesForPaths( _GetPathsStandardCase( path_dir, self.user_options['filepath_completion_use_working_dir'], filepath)) def GetPathsIncludeCase(self, path_dir, include_current_file_dir, filepath): paths = [] include_paths = self._flags.UserIncludePaths(filepath) if include_current_file_dir: include_paths.append(os.path.dirname(filepath)) for include_path in include_paths: try: relative_paths = os.listdir( os.path.join(include_path, path_dir)) except: relative_paths = [] paths.extend( os.path.join(include_path, path_dir, relative_path) for relative_path in relative_paths) return sorted(set(paths))
class ClangCompleter(Completer): def __init__(self): super(ClangCompleter, self).__init__() self.completer = ycm_core.ClangCompleter() self.completer.EnableThreading() self.contents_holder = [] self.filename_holder = [] self.last_prepared_diagnostics = [] self.parse_future = None self.flags = Flags() self.diagnostic_store = None # We set this flag when a compilation request comes in while one is already # in progress. We use this to trigger the pending request after the previous # one completes (from GetDiagnosticsForCurrentFile because that's the only # method that knows when the compilation has finished). self.extra_parse_desired = False def SupportedFiletypes(self): return CLANG_FILETYPES def GetUnsavedFilesVector(self): # CAREFUL HERE! For UnsavedFile filename and contents we are referring # directly to Python-allocated and -managed memory since we are accepting # pointers to data members of python objects. We need to ensure that those # objects outlive our UnsavedFile objects. This is why we need the # contents_holder and filename_holder lists, to make sure the string objects # are still around when we call CandidatesForQueryAndLocationInFile. We do # this to avoid an extra copy of the entire file contents. files = ycm_core.UnsavedFileVec() self.contents_holder = [] self.filename_holder = [] for buffer in vimsupport.GetUnsavedBuffers(): if not ClangAvailableForBuffer(buffer): continue contents = '\n'.join(buffer) name = buffer.name if not contents or not name: continue self.contents_holder.append(contents) self.filename_holder.append(name) unsaved_file = ycm_core.UnsavedFile() unsaved_file.contents_ = self.contents_holder[-1] unsaved_file.length_ = len(self.contents_holder[-1]) unsaved_file.filename_ = self.filename_holder[-1] files.append(unsaved_file) return files def CandidatesForQueryAsync(self, query, start_column): filename = vim.current.buffer.name if not filename: return if self.completer.UpdatingTranslationUnit(filename): vimsupport.PostVimMessage( 'Still parsing file, no completions yet.') self.completions_future = None return flags = self.flags.FlagsForFile(filename) if not flags: vimsupport.PostVimMessage( 'Still no compile flags, no completions yet.') self.completions_future = None return # TODO: sanitize query, probably in C++ code files = ycm_core.UnsavedFileVec() if not query: files = self.GetUnsavedFilesVector() line, _ = vim.current.window.cursor column = start_column + 1 self.completions_future = ( self.completer.CandidatesForQueryAndLocationInFileAsync( query, filename, line, column, files, flags)) def CandidatesFromStoredRequest(self): if not self.completions_future: return [] results = [ CompletionDataToDict(x) for x in self.completions_future.GetResults() ] if not results: vimsupport.PostVimMessage( 'No completions found; errors in the file?') return results def DefinedSubcommands(self): return [ 'GoToDefinition', 'GoToDeclaration', 'GoToDefinitionElseDeclaration', 'ClearCompilationFlagCache' ] def OnUserCommand(self, arguments): if not arguments: self.EchoUserCommandsHelpMessage() return command = arguments[0] if command == 'GoToDefinition': self._GoToDefinition() elif command == 'GoToDeclaration': self._GoToDeclaration() elif command == 'GoToDefinitionElseDeclaration': self._GoToDefinitionElseDeclaration() elif command == 'ClearCompilationFlagCache': self._ClearCompilationFlagCache() def _LocationForGoTo(self, goto_function): filename = vim.current.buffer.name if not filename: return None flags = self.flags.FlagsForFile(filename) if not flags: vimsupport.PostVimMessage( 'Still no compile flags, can\'t compile.') return None files = self.GetUnsavedFilesVector() line, column = vimsupport.CurrentLineAndColumn() # Making the line & column 1-based instead of 0-based line += 1 column += 1 return getattr(self.completer, goto_function)(filename, line, column, files, flags) def _GoToDefinition(self): location = self._LocationForGoTo('GetDefinitionLocation') if not location or not location.IsValid(): vimsupport.PostVimMessage('Can\'t jump to definition.') return vimsupport.JumpToLocation(location.filename_, location.line_number_, location.column_number_) def _GoToDeclaration(self): location = self._LocationForGoTo('GetDeclarationLocation') if not location or not location.IsValid(): vimsupport.PostVimMessage('Can\'t jump to declaration.') return vimsupport.JumpToLocation(location.filename_, location.line_number_, location.column_number_) def _GoToDefinitionElseDeclaration(self): location = self._LocationForGoTo('GetDefinitionLocation') if not location or not location.IsValid(): location = self._LocationForGoTo('GetDeclarationLocation') if not location or not location.IsValid(): vimsupport.PostVimMessage( 'Can\'t jump to definition or declaration.') return vimsupport.JumpToLocation(location.filename_, location.line_number_, location.column_number_) def _ClearCompilationFlagCache(self): self.flags.Clear() def OnFileReadyToParse(self): if vimsupport.NumLinesInBuffer(vim.current.buffer) < 5: self.parse_future = None return filename = vim.current.buffer.name if not filename: return if self.completer.UpdatingTranslationUnit(filename): self.extra_parse_desired = True return flags = self.flags.FlagsForFile(filename) if not flags: self.parse_future = None return self.parse_future = self.completer.UpdateTranslationUnitAsync( filename, self.GetUnsavedFilesVector(), flags) self.extra_parse_desired = False def OnBufferUnload(self, deleted_buffer_file): self.completer.DeleteCachesForFileAsync(deleted_buffer_file) def DiagnosticsForCurrentFileReady(self): if not self.parse_future: return False return self.parse_future.ResultsReady() def GettingCompletions(self): return self.completer.UpdatingTranslationUnit(vim.current.buffer.name) def GetDiagnosticsForCurrentFile(self): if self.DiagnosticsForCurrentFileReady(): diagnostics = self.completer.DiagnosticsForFile( vim.current.buffer.name) self.diagnostic_store = DiagnosticsToDiagStructure(diagnostics) self.last_prepared_diagnostics = [ DiagnosticToDict(x) for x in diagnostics[:MAX_DIAGNOSTICS_TO_DISPLAY] ] self.parse_future = None if self.extra_parse_desired: self.OnFileReadyToParse() return self.last_prepared_diagnostics def ShowDetailedDiagnostic(self): current_line, current_column = vimsupport.CurrentLineAndColumn() # CurrentLineAndColumn() numbers are 0-based, clang numbers are 1-based current_line += 1 current_column += 1 current_file = vim.current.buffer.name if not self.diagnostic_store: vimsupport.PostVimMessage("No diagnostic for current line!") return diagnostics = self.diagnostic_store[current_file][current_line] if not diagnostics: vimsupport.PostVimMessage("No diagnostic for current line!") return closest_diagnostic = None distance_to_closest_diagnostic = 999 for diagnostic in diagnostics: distance = abs(current_column - diagnostic.column_number_) if distance < distance_to_closest_diagnostic: distance_to_closest_diagnostic = distance closest_diagnostic = diagnostic vimsupport.EchoText(closest_diagnostic.long_formatted_text_) def ShouldUseNow(self, start_column): # We don't want to use the Completer API cache, we use one in the C++ code. return self.ShouldUseNowInner(start_column) def DebugInfo(self): filename = vim.current.buffer.name if not filename: return '' flags = self.flags.FlagsForFile(filename) or [] source = extra_conf_store.ModuleFileForSourceFile(filename) return 'Flags for {0} loaded from {1}:\n{2}'.format( filename, source, list(flags))