import os import re from clang_index import Clang_Index class cgo(object): def get_inline_source(buffer): # TODO(zchee): very slow. about 100ms if 'import "C"' not in buffer: return (0, '') pos_import_c = list(buffer).index('import "C"') c_inline = buffer[:pos_import_c] if c_inline[len(c_inline) - 1] == '*/': comment_start = \ next(i for i, v in zip(range(len(c_inline) - 1, 0, -1), reversed(c_inline)) if v == '/*') c_inline = c_inline[comment_start + 1:len(c_inline) - 1] return (len(c_inline), '\n'.join(c_inline)) def get_pkgconfig(packages): out = [] pkgconfig = cgo.find_binary_path('pkg-config') if pkgconfig != '': for pkg in packages: flag = \ os.popen(pkgconfig + " " + pkg + " --cflags --libs").read() out += flag.rstrip().split(' ') return out def parse_candidates(result): completion = {'dup': 1, 'word': ''} _type = "" word = "" placeholder = "" sep = ' ' for chunk in [x for x in result.string if x.spelling]: chunk_spelling = chunk.spelling # ignore inline fake main(void), and '_' prefix function if chunk_spelling == 'main' or chunk_spelling.find('_') is 0: return completion if chunk.isKindTypedText(): word += chunk_spelling placeholder += chunk_spelling elif chunk.isKindResultType(): _type += chunk_spelling else: placeholder += chunk_spelling completion['word'] = word completion['abbr'] = completion['info'] = placeholder + sep + _type completion['kind'] = \ ' '.join([(Clang_Index.kinds[result.cursorKind] if (result.cursorKind in Clang_Index.kinds) else str(result.cursorKind))]) return completion def complete(index, cache, cgo_options, line_count, source): cgo_pattern = r'#cgo (\S+): (.+)' flags = set() for key, value in re.findall(cgo_pattern, source): if key == 'pkg-config': for flag in cgo.get_pkgconfig(value.split()): flags.add(flag) else: if '${SRCDIR}' in key: key = key.replace('${SRCDIR}', './') flags.add('%s=%s' % (key, value)) cgo_flags = ['-std', cgo_options['std']] + list(flags) fname = 'cgo_inline.c' main = """ int main(void) { } """ template = source + main files = [(fname, template)] # clang.TranslationUnit # PARSE_NONE = 0 # PARSE_DETAILED_PROCESSING_RECORD = 1 # PARSE_INCOMPLETE = 2 # PARSE_PRECOMPILED_PREAMBLE = 4 # PARSE_CACHE_COMPLETION_RESULTS = 8 # PARSE_SKIP_FUNCTION_BODIES = 64 # PARSE_INCLUDE_BRIEF_COMMENTS_IN_CODE_COMPLETION = 128 options = 15 # Index.parse(path, args=None, unsaved_files=None, options = 0) tu = index.parse( fname, cgo_flags, unsaved_files=files, options=options ) # TranslationUnit.codeComplete(path, line, column, ...) cr = tu.codeComplete( fname, (line_count + 2), 1, unsaved_files=files, include_macros=True, include_code_patterns=True, include_brief_comments=False ) if cgo_options['sort_algo'] == 'priority': results = sorted(cr.results, key=cgo.get_priority) elif cgo_options['sort_algo'] == 'alphabetical': results = sorted(cr.results, key=cgo.get_abbrevation) else: results = cr.results # Go string to C string # The C string is allocated in the C heap using malloc. # It is the caller's responsibility to arrange for it to be # freed, such as by calling C.free (be sure to include stdlib.h # if C.free is needed). # func C.CString(string) *C.char # # Go []byte slice to C array # The C array is allocated in the C heap using malloc. # It is the caller's responsibility to arrange for it to be # freed, such as by calling C.free (be sure to include stdlib.h # if C.free is needed). # func C.CBytes([]byte) unsafe.Pointer # # C string to Go string # func C.GoString(*C.char) string # # C data with explicit length to Go string # func C.GoStringN(*C.char, C.int) string # # C data with explicit length to Go []byte # func C.GoBytes(unsafe.Pointer, C.int) []byte cache[source] = [ { 'word': 'CString', 'abbr': 'CString(string) *C.char', 'info': 'CString(string) *C.char', 'kind': 'function', 'dup': 1 }, { 'word': 'CBytes', 'abbr': 'CBytes([]byte) unsafe.Pointer', 'info': 'CBytes([]byte) unsafe.Pointer', 'kind': 'function', 'dup': 1 }, { 'word': 'GoString', 'abbr': 'GoString(*C.char) string', 'info': 'GoString(*C.char) string', 'kind': 'function', 'dup': 1 }, { 'word': 'GoStringN', 'abbr': 'GoStringN(*C.char, C.int) string', 'info': 'GoStringN(*C.char, C.int) string', 'kind': 'function', 'dup': 1 }, { 'word': 'GoBytes', 'abbr': 'GoBytes(unsafe.Pointer, C.int) []byte', 'info': 'GoBytes(unsafe.Pointer, C.int) []byte', 'kind': 'function', 'dup': 1 }, ] cache[source] += \ list(map(cgo.parse_candidates, results)) return cache[source] def get_priority(x): return x.string.priority def get_abbr(strings): for chunks in strings: if chunks.isKindTypedText(): return chunks.spelling return "" def get_abbrevation(x): return cgo.get_abbr(x.string).lower() def find_binary_path(cmd): def is_exec(fpath): return os.path.isfile(fpath) and os.access(fpath, os.X_OK) fpath, fname = os.path.split(cmd) if fpath: if is_exec(cmd): return cmd else: for path in os.environ["PATH"].split(os.pathsep): path = path.strip('"') binary = os.path.join(path, cmd) if is_exec(binary): return binary return ''