213 lines
6.8 KiB
Python
213 lines
6.8 KiB
Python
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 ''
|