From fbe60f144f46abbefb20d8f56aa78b1fa5e97be9 Mon Sep 17 00:00:00 2001 From: Dmitri Gribenko Date: Sat, 15 Sep 2012 15:49:36 +0300 Subject: [PATCH 1/6] Display brief comments in preview window. This requires updating libclang.py to the latest version. --- plugin/clang/cindex.py | 30 ++++++++++++++++++++++++++++-- plugin/clang_complete.vim | 20 ++++++++++++++------ plugin/libclang.py | 15 +++++++++++---- 3 files changed, 53 insertions(+), 12 deletions(-) diff --git a/plugin/clang/cindex.py b/plugin/clang/cindex.py index d265f7e5..770ff0e0 100644 --- a/plugin/clang/cindex.py +++ b/plugin/clang/cindex.py @@ -1735,10 +1735,15 @@ def availability(self): res = conf.lib.clang_getCompletionAvailability(self.obj) return availabilityKinds[res] + @property + def briefComment(self): + return conf.lib.clang_getCompletionBriefComment(self.obj) + def __repr__(self): return " | ".join([str(a) for a in self]) \ + " || Priority: " + str(self.priority) \ - + " || Availability: " + str(self.availability) + + " || Availability: " + str(self.availability) \ + + " || Brief comment: " + str(self.briefComment.spelling) availabilityKinds = { 0: CompletionChunk.Kind("Available"), @@ -1877,6 +1882,10 @@ class TranslationUnit(ClangObject): # searching for declarations/definitions. PARSE_SKIP_FUNCTION_BODIES = 64 + # Used to indicate that brief documentation comments should be included + # into the set of code completions returned from this translation unit. + PARSE_INCLUDE_BRIEF_COMMENTS_IN_CODE_COMPLETION = 128 + @classmethod def from_source(cls, filename, args=None, unsaved_files=None, options=0, index=None): @@ -2149,7 +2158,9 @@ def save(self, filename): raise TranslationUnitSaveError(result, 'Error saving TranslationUnit.') - def codeComplete(self, path, line, column, unsaved_files=None, options=0): + def codeComplete(self, path, line, column, unsaved_files=None, + include_macros=False, include_code_patterns=False, + include_brief_comments=False): """ Code complete in this translation unit. @@ -2158,6 +2169,17 @@ def codeComplete(self, path, line, column, unsaved_files=None, options=0): and the second should be the contents to be substituted for the file. The contents may be passed as strings or file objects. """ + options = 0 + + if include_macros: + options += 1 + + if include_code_patterns: + options += 2 + + if include_brief_comments: + options += 4 + if unsaved_files is None: unsaved_files = [] @@ -2556,6 +2578,10 @@ def cursor(self): [c_void_p], c_int), + ("clang_getCompletionBriefComment", + [c_void_p], + _CXString), + ("clang_getCompletionChunkCompletionString", [c_void_p, c_int], c_object_p), diff --git a/plugin/clang_complete.vim b/plugin/clang_complete.vim index 363f98cf..c4be904a 100644 --- a/plugin/clang_complete.vim +++ b/plugin/clang_complete.vim @@ -84,6 +84,10 @@ function! s:ClangCompleteInit() let g:clang_complete_patterns = 0 endif + if !exists('g:clang_include_brief_comments') + let g:clang_include_brief_comments = 1 + endif + if !exists('g:clang_debug') let g:clang_debug = 0 endif @@ -136,16 +140,12 @@ function! s:ClangCompleteInit() let b:clang_parameters .= '-header' endif - let g:clang_complete_lib_flags = 0 - if g:clang_complete_macros == 1 let b:clang_parameters .= ' -code-completion-macros' - let g:clang_complete_lib_flags = 1 endif if g:clang_complete_patterns == 1 let b:clang_parameters .= ' -code-completion-patterns' - let g:clang_complete_lib_flags += 2 endif setlocal completefunc=ClangComplete @@ -239,9 +239,16 @@ function! s:initClangCompletePython() exe 'python sys.path = ["' . s:plugin_path . '"] + sys.path' exe 'pyfile ' . s:plugin_path . '/libclang.py' if exists('g:clang_library_path') - python initClangComplete(vim.eval('g:clang_complete_lib_flags'), vim.eval('g:clang_library_path')) + python 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'), + \ library_path=vim.eval('g:clang_library_path')) else - python initClangComplete(vim.eval('g:clang_complete_lib_flags')) + python 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')) endif let s:libclang_loaded = 1 endif @@ -582,6 +589,7 @@ function! ClangComplete(findstart, base) let item['word'] = b:AddSnip(item['info'], item['args_pos']) else let item['word'] = item['abbr'] + let item['info'] = item['info'] . "\n" . item['brief_comment'] endif endfor diff --git a/plugin/libclang.py b/plugin/libclang.py index 8e865db7..d79e355b 100644 --- a/plugin/libclang.py +++ b/plugin/libclang.py @@ -4,7 +4,8 @@ import re import threading -def initClangComplete(clang_complete_flags, library_path = None): +def initClangComplete(include_macros=False, include_code_patterns=False, + include_brief_comments=False, library_path=None): global index if library_path: Config.set_library_path(library_path) @@ -14,7 +15,11 @@ def initClangComplete(clang_complete_flags, library_path = None): global translationUnits translationUnits = dict() global complete_flags - complete_flags = int(clang_complete_flags) + complete_flags = { + 'include_macros': include_macros, + 'include_code_patterns': include_code_patterns, + 'include_brief_comments': include_brief_comments + } global libclangLock libclangLock = threading.Lock() @@ -87,7 +92,8 @@ def getCurrentTranslationUnit(args, currentFile, fileName, update = False): if debug: start = time.time() - flags = TranslationUnit.PARSE_PRECOMPILED_PREAMBLE + flags = TranslationUnit.PARSE_PRECOMPILED_PREAMBLE | \ + TranslationUnit.PARSE_INCLUDE_BRIEF_COMMENTS_IN_CODE_COMPLETION tu = index.parse(fileName, args, [currentFile], flags) if debug: elapsed = (time.time() - start) @@ -220,7 +226,7 @@ def getCurrentCompletionResults(line, column, args, currentFile, fileName, timer.registerEvent("Get TU") cr = tu.codeComplete(fileName, line, column, [currentFile], - complete_flags) + **complete_flags) timer.registerEvent("Code Complete") return cr @@ -263,6 +269,7 @@ def formatResult(result): completion['info'] = word completion['args_pos'] = args_pos completion['dup'] = 0 + completion['brief_comment'] = result.string.briefComment.spelling or "" # Replace the number that represents a specific kind with a better # textual representation. From af8ebf35e079b719240c08f0c318d9091cc02eab Mon Sep 17 00:00:00 2001 From: Dmitri Gribenko Date: Sat, 15 Sep 2012 20:42:27 +0300 Subject: [PATCH 2/6] Handle brief comments with quotes correctly. This is done by serializing the whole completion result data structure in a vim-compatible way instead of relying on python and vim representation being somewhat compatible. --- plugin/clang_complete.vim | 2 +- plugin/libclang.py | 46 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/plugin/clang_complete.vim b/plugin/clang_complete.vim index c4be904a..f92b7f3d 100644 --- a/plugin/clang_complete.vim +++ b/plugin/clang_complete.vim @@ -578,7 +578,7 @@ function! ClangComplete(findstart, base) if g:clang_use_library == 1 python completions, timer = getCurrentCompletions(vim.eval('a:base')) - python vim.command('let l:res = ' + completions) + python vim.command('let l:res = ' + toVimRepr(completions)) python timer.registerEvent("Load into vimscript") else let l:res = s:ClangCompleteBinary(a:base) diff --git a/plugin/libclang.py b/plugin/libclang.py index d79e355b..d070690c 100644 --- a/plugin/libclang.py +++ b/plugin/libclang.py @@ -3,6 +3,7 @@ import time import re import threading +import types def initClangComplete(include_macros=False, include_code_patterns=False, include_brief_comments=False, library_path=None): @@ -361,7 +362,50 @@ def getCurrentCompletions(base): result = map(formatResult, results) timer.registerEvent("Format") - return (str(result), timer) + return (result, timer) + +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): tmplst = filter(lambda x: x.isKindTypedText(), strings) From 07a9a6bb914c71732a306263d83b00fd7f67e7f4 Mon Sep 17 00:00:00 2001 From: Dmitri Gribenko Date: Sat, 22 Sep 2012 19:22:21 +0300 Subject: [PATCH 3/6] Don't retain brief comments while parsing if the user doesn't want to see them. --- plugin/libclang.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugin/libclang.py b/plugin/libclang.py index d070690c..3e563161 100644 --- a/plugin/libclang.py +++ b/plugin/libclang.py @@ -93,8 +93,9 @@ def getCurrentTranslationUnit(args, currentFile, fileName, update = False): if debug: start = time.time() - flags = TranslationUnit.PARSE_PRECOMPILED_PREAMBLE | \ - TranslationUnit.PARSE_INCLUDE_BRIEF_COMMENTS_IN_CODE_COMPLETION + flags = TranslationUnit.PARSE_PRECOMPILED_PREAMBLE + if complete_flags['include_brief_comments']: + flags |= TranslationUnit.PARSE_INCLUDE_BRIEF_COMMENTS_IN_CODE_COMPLETION tu = index.parse(fileName, args, [currentFile], flags) if debug: elapsed = (time.time() - start) From 8803f94637aa340eb5eba898c21467c88e8d6a7e Mon Sep 17 00:00:00 2001 From: Dmitri Gribenko Date: Sat, 22 Sep 2012 20:49:49 +0300 Subject: [PATCH 4/6] Optimize python-to-vim datastructure conversion. Two approaches: * optimize string concatenation; * do a special conversion only if it is needed. --- plugin/libclang.py | 101 +++++++++++++++++++++++++++++---------------- 1 file changed, 65 insertions(+), 36 deletions(-) diff --git a/plugin/libclang.py b/plugin/libclang.py index 3e563161..2278e161 100644 --- a/plugin/libclang.py +++ b/plugin/libclang.py @@ -365,48 +365,77 @@ def getCurrentCompletions(base): timer.registerEvent("Format") return (result, timer) -def toVimRepr(v): +class VimReprHelper: + def __init__(self): + self._result = [] + + def getResult(self): + return ''.join(self._result) + + def append(self, v): + t = type(v) + if t in [types.IntType, types.LongType, types.FloatType]: + self._result.append(repr(v)) + if t in [types.StringType, types.UnicodeType]: + self.appendString(v) + if t is types.ListType: + self.appendList(v) + if t is types.DictType: + self.appendDict(v) + + def appendString(self, s): + if '\'' in s: + self._result.append('\'') + self._result.append(s.replace('\'', '\'\'')) + self._result.append('\'') + else: + self._result.append(repr(s)) + + def appendList(self, l): + self._result.append('[') + for i in xrange(len(l)): + self.append(l[i]) + if i != len(l) - 1: + self._result.append(',') + self._result.append(']') + + def appendDict(self, d): + self._result.append('{') + keys = d.keys() + for i in xrange(len(keys)): + k = keys[i] + self.append(k) + self._result.append(':') + self.append(d[k]) + if i != len(keys) - 1: + self._result.append(',') + self._result.append('}') + +def needsSpecialConversion(v): t = type(v) if t in [types.IntType, types.LongType, types.FloatType]: - return repr(v) + return False if t in [types.StringType, types.UnicodeType]: - return stringToVimRepr(v) + return '\'' in v if t is types.ListType: - return listToVimRepr(v) + for e in v: + if needsSpecialConversion(e): + return True if t is types.DictType: - return dictToVimRepr(v) + for k, v in enumerate(v): + if needsSpecialConversion(k): + return True + if needsSpecialConversion(v): + return True + return False -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 toVimRepr(v): + if needsSpecialConversion(v): + helper = VimReprHelper() + helper.append(v) + return helper.getResult() + else: + return repr(v) def getAbbr(strings): tmplst = filter(lambda x: x.isKindTypedText(), strings) From 9e2322efa2d2cb41737dcad48bafcf886d064fda Mon Sep 17 00:00:00 2001 From: Dmitri Gribenko Date: Sat, 22 Sep 2012 20:54:09 +0300 Subject: [PATCH 5/6] Fix cindex.py compatibility with older libclang.so The issue is that we were calling clang_getCompletionBriefComment unconditionally. New we check if this function is available before calling it. --- plugin/clang/cindex.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/plugin/clang/cindex.py b/plugin/clang/cindex.py index 770ff0e0..edd3e707 100644 --- a/plugin/clang/cindex.py +++ b/plugin/clang/cindex.py @@ -1737,7 +1737,9 @@ def availability(self): @property def briefComment(self): - return conf.lib.clang_getCompletionBriefComment(self.obj) + if conf.function_exists("clang_getCompletionBriefComment"): + return conf.lib.clang_getCompletionBriefComment(self.obj) + return _CXString() def __repr__(self): return " | ".join([str(a) for a in self]) \ @@ -3097,6 +3099,13 @@ def get_cindex_library(self): return library + def function_exists(self, name): + try: + getattr(self.lib, name) + except AttributeError: + return False + + return True def register_enumerations(): for name, value in clang.enumerations.TokenKinds: From ad432d9abf4aaeb5ffbda4159e7a8c6662930864 Mon Sep 17 00:00:00 2001 From: Dmitri Gribenko Date: Sat, 22 Sep 2012 22:29:38 +0300 Subject: [PATCH 6/6] Actually check dictionary key,value pairs instead of key,index pairs. --- plugin/libclang.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin/libclang.py b/plugin/libclang.py index 2278e161..fee1cdc7 100644 --- a/plugin/libclang.py +++ b/plugin/libclang.py @@ -422,10 +422,10 @@ def needsSpecialConversion(v): if needsSpecialConversion(e): return True if t is types.DictType: - for k, v in enumerate(v): + for k, val in v.items(): if needsSpecialConversion(k): return True - if needsSpecialConversion(v): + if needsSpecialConversion(val): return True return False