From cfca7972d45178aa9e6bb4c7ee1baf8974fc1bfc Mon Sep 17 00:00:00 2001 From: Tobias Grosser Date: Thu, 6 Mar 2014 09:57:45 +0100 Subject: [PATCH 1/3] Import latest cindex.py file --- plugin/clang/cindex.py | 232 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 228 insertions(+), 4 deletions(-) diff --git a/plugin/clang/cindex.py b/plugin/clang/cindex.py index 70f4f36a..9b65bef2 100644 --- a/plugin/clang/cindex.py +++ b/plugin/clang/cindex.py @@ -266,6 +266,29 @@ def __eq__(self, other): def __ne__(self, other): return not self.__eq__(other) + def __contains__(self, other): + """Useful to detect the Token/Lexer bug""" + if not isinstance(other, SourceLocation): + return False + if other.file is None and self.start.file is None: + pass + elif ( self.start.file.name != other.file.name or + other.file.name != self.end.file.name): + # same file name + return False + # same file, in between lines + if self.start.line < other.line < self.end.line: + return True + elif self.start.line == other.line: + # same file first line + if self.start.column <= other.column: + return True + elif other.line == self.end.line: + # same file last line + if other.column <= self.end.column: + return True + return False + def __repr__(self): return "" % (self.start, self.end) @@ -508,7 +531,7 @@ def name(self): @staticmethod def from_id(id): if id >= len(CursorKind._kinds) or CursorKind._kinds[id] is None: - raise ValueError,'Unknown cursor kind' + raise ValueError,'Unknown cursor kind %d' % id return CursorKind._kinds[id] @staticmethod @@ -721,10 +744,14 @@ def __repr__(self): # A reference to a labeled statement. CursorKind.LABEL_REF = CursorKind(48) -# A reference toa a set of overloaded functions or function templates +# A reference to a set of overloaded functions or function templates # that has not yet been resolved to a specific function or function template. CursorKind.OVERLOADED_DECL_REF = CursorKind(49) +# A reference to a variable that occurs in some non-expression +# context, e.g., a C++ lambda capture list. +CursorKind.VARIABLE_REF = CursorKind(50) + ### # Invalid/Error Kinds @@ -908,6 +935,26 @@ def __repr__(self): # pack. CursorKind.SIZE_OF_PACK_EXPR = CursorKind(143) +# Represents a C++ lambda expression that produces a local function +# object. +# +# \code +# void abssort(float *x, unsigned N) { +# std::sort(x, x + N, +# [](float a, float b) { +# return std::abs(a) < std::abs(b); +# }); +# } +# \endcode +CursorKind.LAMBDA_EXPR = CursorKind(144) + +# Objective-c Boolean Literal. +CursorKind.OBJ_BOOL_LITERAL_EXPR = CursorKind(145) + +# Represents the "self" expression in a ObjC method. +CursorKind.OBJ_SELF_EXPR = CursorKind(146) + + # A statement whose specific kind is not exposed via this interface. # # Unexposed statements have the same operations as any other kind of statement; @@ -999,6 +1046,9 @@ def __repr__(self): # Windows Structured Exception Handling's finally statement. CursorKind.SEH_FINALLY_STMT = CursorKind(228) +# A MS inline assembly statement extension. +CursorKind.MS_ASM_STMT = CursorKind(229) + # The null statement. CursorKind.NULL_STMT = CursorKind(230) @@ -1028,6 +1078,7 @@ def __repr__(self): CursorKind.CXX_OVERRIDE_ATTR = CursorKind(405) CursorKind.ANNOTATE_ATTR = CursorKind(406) CursorKind.ASM_LABEL_ATTR = CursorKind(407) +CursorKind.PACKED_ATTR = CursorKind(408) ### # Preprocessing @@ -1036,6 +1087,12 @@ def __repr__(self): CursorKind.MACRO_INSTANTIATION = CursorKind(502) CursorKind.INCLUSION_DIRECTIVE = CursorKind(503) +### +# Extra declaration + +# A module import declaration. +CursorKind.MODULE_IMPORT_DECL = CursorKind(600) + ### Cursors ### class Cursor(Structure): @@ -1282,6 +1339,16 @@ def referenced(self): return self._referenced + @property + def brief_comment(self): + """Returns the brief comment text associated with that Cursor""" + return conf.lib.clang_Cursor_getBriefCommentText(self) + + @property + def raw_comment(self): + """Returns the raw comment text associated with that Cursor""" + return conf.lib.clang_Cursor_getRawCommentText(self) + def get_arguments(self): """Return an iterator for accessing the arguments of this cursor.""" num_args = conf.lib.clang_Cursor_getNumArguments(self) @@ -1314,6 +1381,18 @@ def get_tokens(self): """ return TokenGroup.get_tokens(self._tu, self.extent) + def is_bitfield(self): + """ + Check if the field is a bitfield. + """ + return conf.lib.clang_Cursor_isBitField(self) + + def get_bitfield_width(self): + """ + Retrieve the width of a bitfield. + """ + return conf.lib.clang_getFieldDeclBitWidth(self) + @staticmethod def from_result(res, fn, args): assert isinstance(res, Cursor) @@ -1438,6 +1517,54 @@ def __repr__(self): TypeKind.FUNCTIONPROTO = TypeKind(111) TypeKind.CONSTANTARRAY = TypeKind(112) TypeKind.VECTOR = TypeKind(113) +TypeKind.INCOMPLETEARRAY = TypeKind(114) +TypeKind.VARIABLEARRAY = TypeKind(115) +TypeKind.DEPENDENTSIZEDARRAY = TypeKind(116) +TypeKind.MEMBERPOINTER = TypeKind(117) + +class RefQualifierKind(object): + """Describes a specific ref-qualifier of a type.""" + + # The unique kind objects, indexed by id. + _kinds = [] + _name_map = None + + def __init__(self, value): + if value >= len(RefQualifierKind._kinds): + num_kinds = value - len(RefQualifierKind._kinds) + 1 + RefQualifierKind._kinds += [None] * num_kinds + if RefQualifierKind._kinds[value] is not None: + raise ValueError, 'RefQualifierKind already loaded' + self.value = value + RefQualifierKind._kinds[value] = self + RefQualifierKind._name_map = None + + def from_param(self): + return self.value + + @property + def name(self): + """Get the enumeration name of this kind.""" + if self._name_map is None: + self._name_map = {} + for key, value in RefQualifierKind.__dict__.items(): + if isinstance(value, RefQualifierKind): + self._name_map[value] = key + return self._name_map[self] + + @staticmethod + def from_id(id): + if (id >= len(RefQualifierKind._kinds) or + RefQualifierKind._kinds[id] is None): + raise ValueError, 'Unknown type kind %d' % id + return RefQualifierKind._kinds[id] + + def __repr__(self): + return 'RefQualifierKind.%s' % (self.name,) + +RefQualifierKind.NONE = RefQualifierKind(0) +RefQualifierKind.LVALUE = RefQualifierKind(1) +RefQualifierKind.RVALUE = RefQualifierKind(2) class Type(Structure): """ @@ -1613,6 +1740,42 @@ def get_array_size(self): """ return conf.lib.clang_getArraySize(self) + def get_class_type(self): + """ + Retrieve the class type of the member pointer type. + """ + return conf.lib.clang_Type_getClassType(self) + + def get_align(self): + """ + Retrieve the alignment of the record. + """ + return conf.lib.clang_Type_getAlignOf(self) + + def get_size(self): + """ + Retrieve the size of the record. + """ + return conf.lib.clang_Type_getSizeOf(self) + + def get_offset(self, fieldname): + """ + Retrieve the offset of a field in the record. + """ + return conf.lib.clang_Type_getOffsetOf(self, c_char_p(fieldname)) + + def get_ref_qualifier(self): + """ + Retrieve the ref-qualifier of the type. + """ + return RefQualifierKind.from_id( + conf.lib.clang_Type_getCXXRefQualifier(self)) + + @property + def spelling(self): + """Retrieve the spelling of this Type.""" + return conf.lib.clang_getTypeSpelling(self) + def __eq__(self, other): if type(other) != type(self): return False @@ -1888,7 +2051,7 @@ def __del__(self): def read(self, path): """Load a TranslationUnit from the given AST file.""" - return TranslationUnit.from_ast(path, self) + return TranslationUnit.from_ast_file(path, self) def parse(self, path, args=None, unsaved_files=None, options = 0): """Load the translation unit from the given source code file by running @@ -2338,7 +2501,7 @@ class CompilationDatabaseError(Exception): constants in this class. """ - # An unknown error occured + # An unknown error occurred ERROR_UNKNOWN = 0 # The database could not be loaded @@ -2444,6 +2607,14 @@ def getCompileCommands(self, filename): return conf.lib.clang_CompilationDatabase_getCompileCommands(self, filename) + def getAllCompileCommands(self): + """ + Get an iterable object providing all the CompileCommands available from + the database. + """ + return conf.lib.clang_CompilationDatabase_getAllCompileCommands(self) + + class Token(Structure): """Represents a single token from the preprocessor. @@ -2510,6 +2681,11 @@ def cursor(self): c_object_p, CompilationDatabase.from_result), + ("clang_CompilationDatabase_getAllCompileCommands", + [c_object_p], + c_object_p, + CompileCommands.from_result), + ("clang_CompilationDatabase_getCompileCommands", [c_object_p, c_char_p], c_object_p, @@ -2560,6 +2736,10 @@ def cursor(self): [Index, c_char_p], c_object_p), + ("clang_CXXMethod_isPureVirtual", + [Cursor], + bool), + ("clang_CXXMethod_isStatic", [Cursor], bool), @@ -2623,6 +2803,10 @@ def cursor(self): [Type], c_longlong), + ("clang_getFieldDeclBitWidth", + [Cursor], + c_int), + ("clang_getCanonicalCursor", [Cursor], Cursor, @@ -2939,6 +3123,11 @@ def cursor(self): _CXString, _CXString.from_result), + ("clang_getTypeSpelling", + [Type], + _CXString, + _CXString.from_result), + ("clang_hashCursor", [Cursor], c_uint), @@ -3038,6 +3227,41 @@ def cursor(self): [Cursor, c_uint], Cursor, Cursor.from_result), + + ("clang_Cursor_isBitField", + [Cursor], + bool), + + ("clang_Cursor_getBriefCommentText", + [Cursor], + _CXString, + _CXString.from_result), + + ("clang_Cursor_getRawCommentText", + [Cursor], + _CXString, + _CXString.from_result), + + ("clang_Type_getAlignOf", + [Type], + c_longlong), + + ("clang_Type_getClassType", + [Type], + Type, + Type.from_result), + + ("clang_Type_getOffsetOf", + [Type, c_char_p], + c_longlong), + + ("clang_Type_getSizeOf", + [Type], + c_longlong), + + ("clang_Type_getCXXRefQualifier", + [Type], + c_uint), ] class LibclangError(Exception): From d7e2cda78cb8c30930d95d1cca037ec493b757c6 Mon Sep 17 00:00:00 2001 From: Tobias Grosser Date: Thu, 6 Mar 2014 10:17:15 +0100 Subject: [PATCH 2/3] Use complete flags properly Our complete flag handling has been completely broken. In ancient times, we had to calculate the bits in the complete flags ourselves, but nowadays cindex.py has a nice interface based on named parameters. However, we have still been passing the ancient complete flags to this interface, which meant we always set complete_macros=True and all other flags to false independently of the actual complete options set. This patch fixes this. It ensures that we pass complete flags as named parameters which makes the individual flags work again. Based on patches from Dmitri Gribenko --- plugin/clang_complete.vim | 12 +++++++----- plugin/libclang.py | 20 +++++++++++++++----- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/plugin/clang_complete.vim b/plugin/clang_complete.vim index 2e496781..e0367021 100644 --- a/plugin/clang_complete.vim +++ b/plugin/clang_complete.vim @@ -138,14 +138,12 @@ function! s:ClangCompleteInit() let b:clang_parameters .= '-header' endif - let g:clang_complete_lib_flags = 0 - if g:clang_complete_macros == 1 - let g:clang_complete_lib_flags = 1 + let g:clang_parameters = ' -code-completion-macros' endif if g:clang_complete_patterns == 1 - let g:clang_complete_lib_flags += 2 + let g:clang_parameters .= ' -code-completion-patterns' endif if s:initClangCompletePython() != 1 @@ -290,7 +288,11 @@ function! s:initClangCompletePython() return 0 endif - py vim.command('let l:res = ' + str(initClangComplete(vim.eval('g:clang_complete_lib_flags'), vim.eval('g:clang_compilation_database'), vim.eval('g:clang_library_path')))) + py vim.command('let l:res = ' + str(initClangComplete( + \ include_macros=vim.eval('g:clang_complete_macros'), + \ include_code_patterns=vim.eval('g:clang_complete_patterns'), + \ clang_compilation_database=vim.eval('g:clang_compilation_database'), + \ library_path=vim.eval('g:clang_library_path')))) if l:res == 0 return 0 endif diff --git a/plugin/libclang.py b/plugin/libclang.py index 45ff92e4..7cee70e9 100644 --- a/plugin/libclang.py +++ b/plugin/libclang.py @@ -52,9 +52,21 @@ def getBuiltinHeaderPath(library_path): return None -def initClangComplete(clang_complete_flags, clang_compilation_database, \ - library_path): +def initClangComplete(include_macros=False, include_code_patterns=False, + clang_compilation_database=None, + library_path=None): global index + global complete_flags + + if (include_macros == "0"): + include_macros = False + if (include_code_patterns == "0"): + include_code_patterns = False + + complete_flags = { + 'include_macros': include_macros, + 'include_code_patterns': include_code_patterns, + } debug = int(vim.eval("g:clang_debug")) == 1 @@ -93,8 +105,6 @@ def initClangComplete(clang_complete_flags, clang_compilation_database, \ global translationUnits translationUnits = dict() - global complete_flags - complete_flags = int(clang_complete_flags) global compilation_database if clang_compilation_database != '': compilation_database = CompilationDatabase.fromDirectory(clang_compilation_database) @@ -356,7 +366,7 @@ def getCurrentCompletionResults(line, column, args, currentFile, fileName, return None cr = tu.codeComplete(fileName, line, column, [currentFile], - complete_flags) + **complete_flags) timer.registerEvent("Code Complete") return cr From 812abbad390b19e2520f996257ff02aa48033215 Mon Sep 17 00:00:00 2001 From: Tobias Grosser Date: Thu, 6 Mar 2014 11:30:36 +0100 Subject: [PATCH 3/3] Show brief comments in the completion results We add a new option g:clang_include_brief_comments which includes brief comments in the completion results This change also introduces routines to translate python to vim data structures. In an earlier review I saw performance regressions, but I could not reproduce them before. I also did not include further optimizing patches as they caused problems in the presence of '\n'. Based on patches from Dmitri Gribenko --- doc/clang_complete.txt | 5 +++ plugin/clang_complete.vim | 1 + plugin/libclang.py | 66 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 69 insertions(+), 3 deletions(-) diff --git a/doc/clang_complete.txt b/doc/clang_complete.txt index b31ae454..1d0a076f 100644 --- a/doc/clang_complete.txt +++ b/doc/clang_complete.txt @@ -236,6 +236,11 @@ Default: 0 *clang_complete-complete_patterns* *g:clang_complete_patterns* If clang should complete code patterns, i.e loop constructs etc. +Defaut: 0 + + *clang_complete-brief-comments + *g:clang_include_brief_comments +If clang should show brief comments in the completion results. Defaut: 0 *clang_complete-jumpto_declaration_key* diff --git a/plugin/clang_complete.vim b/plugin/clang_complete.vim index e0367021..3a942c32 100644 --- a/plugin/clang_complete.vim +++ b/plugin/clang_complete.vim @@ -291,6 +291,7 @@ function! s:initClangCompletePython() py vim.command('let l:res = ' + str(initClangComplete( \ include_macros=vim.eval('g:clang_complete_macros'), \ include_code_patterns=vim.eval('g:clang_complete_patterns'), + \ include_brief_comments=vim.eval('g:clang_include_brief_comments'), \ clang_compilation_database=vim.eval('g:clang_compilation_database'), \ library_path=vim.eval('g:clang_library_path')))) if l:res == 0 diff --git a/plugin/libclang.py b/plugin/libclang.py index 7cee70e9..79aabca2 100644 --- a/plugin/libclang.py +++ b/plugin/libclang.py @@ -53,6 +53,7 @@ def getBuiltinHeaderPath(library_path): return None def initClangComplete(include_macros=False, include_code_patterns=False, + include_brief_comments = False, clang_compilation_database=None, library_path=None): global index @@ -62,10 +63,13 @@ def initClangComplete(include_macros=False, include_code_patterns=False, include_macros = False if (include_code_patterns == "0"): include_code_patterns = False + if (include_brief_comments == "0"): + include_brief_comments = False complete_flags = { 'include_macros': include_macros, 'include_code_patterns': include_code_patterns, + 'include_brief_comments': include_brief_comments, } debug = int(vim.eval("g:clang_debug")) == 1 @@ -182,7 +186,8 @@ def getCurrentTranslationUnit(args, currentFile, fileName, timer, return tu flags = TranslationUnit.PARSE_PRECOMPILED_PREAMBLE | \ - TranslationUnit.PARSE_DETAILED_PROCESSING_RECORD + TranslationUnit.PARSE_DETAILED_PROCESSING_RECORD | \ + TranslationUnit.PARSE_INCLUDE_BRIEF_COMMENTS_IN_CODE_COMPLETION try: tu = index.parse(fileName, args, [currentFile], flags) timer.registerEvent("First parse") @@ -376,6 +381,11 @@ def formatResult(result): abbr = "" word = "" info = "" + comment = "" + + commentstr = result.string.briefComment.spelling + if (commentstr and commentstr != ""): + comment = "// " + commentstr + "\n" for chunk in result.string: @@ -406,7 +416,7 @@ def formatResult(result): completion['word'] = snippetsAddSnippet(info, word, abbr) completion['abbr'] = abbr completion['menu'] = menu - completion['info'] = info + completion['info'] = comment + info completion['dup'] = 1 # Replace the number that represents a specific kind with a better @@ -501,7 +511,57 @@ def getCurrentCompletions(base): result = map(formatResult, results) timer.registerEvent("Format") - return (str(result), timer) + return (toVimRepr(result), timer) +import types + +# Convert python data structures to a vim-compatible string +# +# python's default str() conversion is very close to be parsable by vim, +# but certain patterns cause trouble. This conversion routines are written +# to be 100% vim compatible. This allows us to properly pass on newlines as +# well as strings that contain " or '. +def toVimRepr(v): + t = type(v) + if t in [types.IntType, types.LongType, types.FloatType]: + return repr(v) + if t in [types.StringType, types.UnicodeType]: + return stringToVimRepr(v) + if t is types.ListType: + return listToVimRepr(v) + if t is types.DictType: + return dictToVimRepr(v) + +def stringToVimRepr(s): + result = '\'' + for c in s: + if c != '\'': + result += c + else: + result += '\'\'' + result += '\'' + return result + +def listToVimRepr(l): + result = '[' + for i in xrange(len(l)): + result += toVimRepr(l[i]) + if i != len(l) - 1: + result += ', ' + result += ']' + return result + +def dictToVimRepr(d): + result = '{' + keys = d.keys() + for i in xrange(len(keys)): + k = keys[i] + result += toVimRepr(k) + result += ': ' + result += toVimRepr(d[k]) + if i != len(keys) - 1: + result += ', ' + result += '}' + return result def getAbbr(strings): for chunks in strings: