Rainbow
[my-vim-dotfolder.git] / plugin / libclang.py
blob2779f814820d47c0963aaca0653c0da7009655b8
1 from clang.cindex import *
2 import vim
3 import time
4 import re
5 import threading
7 def initClangComplete(clang_complete_flags):
8 global index
9 index = Index.create()
10 global translationUnits
11 translationUnits = dict()
12 global complete_flags
13 complete_flags = int(clang_complete_flags)
15 # Get a tuple (fileName, fileContent) for the file opened in the current
16 # vim buffer. The fileContent contains the unsafed buffer content.
17 def getCurrentFile():
18 file = "\n".join(vim.eval("getline(1, '$')"))
19 return (vim.current.buffer.name, file)
21 def getCurrentTranslationUnit(args, currentFile, fileName, update = False):
22 if fileName in translationUnits:
23 tu = translationUnits[fileName]
24 if update:
25 if debug:
26 start = time.time()
27 tu.reparse([currentFile])
28 if debug:
29 elapsed = (time.time() - start)
30 print "LibClang - Reparsing: " + str(elapsed)
31 return tu
33 if debug:
34 start = time.time()
35 flags = TranslationUnit.PrecompiledPreamble | TranslationUnit.CXXPrecompiledPreamble # | TranslationUnit.CacheCompletionResults
36 tu = index.parse(fileName, args, [currentFile], flags)
37 if debug:
38 elapsed = (time.time() - start)
39 print "LibClang - First parse: " + str(elapsed)
41 if tu == None:
42 print "Cannot parse this source file. The following arguments " \
43 + "are used for clang: " + " ".join(args)
44 return None
46 translationUnits[fileName] = tu
48 # Reparse to initialize the PCH cache even for auto completion
49 # This should be done by index.parse(), however it is not.
50 # So we need to reparse ourselves.
51 if debug:
52 start = time.time()
53 tu.reparse([currentFile])
54 if debug:
55 elapsed = (time.time() - start)
56 print "LibClang - First reparse (generate PCH cache): " + str(elapsed)
57 return tu
59 def splitOptions(options):
60 optsList = []
61 opt = ""
62 quoted = False
64 for char in options:
65 if char == ' ' and not quoted:
66 if opt != "":
67 optsList += [opt]
68 opt = ""
69 continue
70 elif char == '"':
71 quoted = not quoted
72 opt += char
74 if opt != "":
75 optsList += [opt]
76 return optsList
78 def getQuickFix(diagnostic):
79 # Some diagnostics have no file, e.g. "too many errors emitted, stopping now"
80 if diagnostic.location.file:
81 filename = diagnostic.location.file.name
82 else:
83 filename = ""
85 if diagnostic.severity == diagnostic.Ignored:
86 type = 'I'
87 elif diagnostic.severity == diagnostic.Note:
88 type = 'I'
89 elif diagnostic.severity == diagnostic.Warning:
90 type = 'W'
91 elif diagnostic.severity == diagnostic.Error:
92 type = 'E'
93 elif diagnostic.severity == diagnostic.Fatal:
94 type = 'E'
95 else:
96 return None
98 return dict({ 'bufnr' : int(vim.eval("bufnr('" + filename + "', 1)")),
99 'lnum' : diagnostic.location.line,
100 'col' : diagnostic.location.column,
101 'text' : diagnostic.spelling,
102 'type' : type})
104 def getQuickFixList(tu):
105 return filter (None, map (getQuickFix, tu.diagnostics))
107 def highlightRange(range, hlGroup):
108 pattern = '/\%' + str(range.start.line) + 'l' + '\%' \
109 + str(range.start.column) + 'c' + '.*' \
110 + '\%' + str(range.end.column) + 'c/'
111 command = "exe 'syntax match' . ' " + hlGroup + ' ' + pattern + "'"
112 vim.command(command)
114 def highlightDiagnostic(diagnostic):
115 if diagnostic.severity == diagnostic.Warning:
116 hlGroup = 'SpellLocal'
117 elif diagnostic.severity == diagnostic.Error:
118 hlGroup = 'SpellBad'
119 else:
120 return
122 pattern = '/\%' + str(diagnostic.location.line) + 'l\%' \
123 + str(diagnostic.location.column) + 'c./'
124 command = "exe 'syntax match' . ' " + hlGroup + ' ' + pattern + "'"
125 vim.command(command)
127 # Use this wired kind of iterator as the python clang libraries
128 # have a bug in the range iterator that stops us to use:
130 # | for range in diagnostic.ranges
132 for i in range(len(diagnostic.ranges)):
133 highlightRange(diagnostic.ranges[i], hlGroup)
135 def highlightDiagnostics(tu):
136 map (highlightDiagnostic, tu.diagnostics)
138 def highlightCurrentDiagnostics():
139 if vim.current.buffer.name in translationUnits:
140 highlightDiagnostics(translationUnits[vim.current.buffer.name])
142 def getCurrentQuickFixList():
143 if vim.current.buffer.name in translationUnits:
144 return getQuickFixList(translationUnits[vim.current.buffer.name])
145 return []
147 def updateCurrentDiagnostics():
148 global debug
149 debug = int(vim.eval("g:clang_debug")) == 1
150 userOptionsGlobal = splitOptions(vim.eval("g:clang_user_options"))
151 userOptionsLocal = splitOptions(vim.eval("b:clang_user_options"))
152 args = userOptionsGlobal + userOptionsLocal
153 getCurrentTranslationUnit(args, getCurrentFile(),
154 vim.current.buffer.name, update = True)
156 def getCurrentCompletionResults(line, column, args, currentFile, fileName):
157 tu = getCurrentTranslationUnit(args, currentFile, fileName)
158 if debug:
159 start = time.time()
160 cr = tu.codeComplete(fileName, line, column, [currentFile],
161 complete_flags)
162 if debug:
163 elapsed = (time.time() - start)
164 print "LibClang - Code completion time: " + str(elapsed)
165 return cr
167 def formatResult(result):
168 completion = dict()
170 abbr = getAbbr(result.string)
171 word = filter(lambda x: not x.isKindInformative() and not x.isKindResultType(), result.string)
173 args_pos = []
174 cur_pos = 0
175 for chunk in word:
176 chunk_len = len(chunk.spelling)
177 if chunk.isKindPlaceHolder():
178 args_pos += [[ cur_pos, cur_pos + chunk_len ]]
179 cur_pos += chunk_len
181 word = "".join(map(lambda x: x.spelling, word))
183 completion['word'] = word
184 completion['abbr'] = abbr
185 completion['menu'] = word
186 completion['info'] = word
187 completion['args_pos'] = args_pos
188 completion['dup'] = 0
190 # Replace the number that represents a specific kind with a better
191 # textual representation.
192 completion['kind'] = kinds[result.cursorKind]
194 return completion
197 class CompleteThread(threading.Thread):
198 lock = threading.Lock()
200 def __init__(self, line, column, currentFile, fileName):
201 threading.Thread.__init__(self)
202 self.line = line
203 self.column = column
204 self.currentFile = currentFile
205 self.fileName = fileName
206 self.result = None
207 userOptionsGlobal = splitOptions(vim.eval("g:clang_user_options"))
208 userOptionsLocal = splitOptions(vim.eval("b:clang_user_options"))
209 self.args = userOptionsGlobal + userOptionsLocal
211 def run(self):
212 try:
213 CompleteThread.lock.acquire()
214 if self.line == -1:
215 # Warm up the caches. For this it is sufficient to get the current
216 # translation unit. No need to retrieve completion results.
217 # This short pause is necessary to allow vim to initialize itself.
218 # Otherwise we would get: E293: block was not locked
219 # The user does not see any delay, as we just pause a background thread.
220 time.sleep(0.1)
221 getCurrentTranslationUnit(self.args, self.currentFile, self.fileName)
222 else:
223 self.result = getCurrentCompletionResults(self.line, self.column,
224 self.args, self.currentFile, self.fileName)
225 except Exception:
226 pass
227 CompleteThread.lock.release()
229 def WarmupCache():
230 global debug
231 debug = int(vim.eval("g:clang_debug")) == 1
232 t = CompleteThread(-1, -1, getCurrentFile(), vim.current.buffer.name)
233 t.start()
234 return
237 def getCurrentCompletions(base):
238 global debug
239 debug = int(vim.eval("g:clang_debug")) == 1
240 priority = vim.eval("g:clang_sort_algo") == 'priority'
241 line = int(vim.eval("line('.')"))
242 column = int(vim.eval("b:col"))
244 t = CompleteThread(line, column, getCurrentFile(), vim.current.buffer.name)
245 t.start()
246 while t.isAlive():
247 t.join(0.01)
248 cancel = int(vim.eval('complete_check()'))
249 if cancel != 0:
250 return []
251 cr = t.result
252 if cr is None:
253 return []
255 regexp = re.compile("^" + base)
256 filteredResult = filter(lambda x: regexp.match(getAbbr(x.string)), cr.results)
258 getPriority = lambda x: x.string.priority
259 getAbbrevation = lambda x: getAbbr(x.string).lower()
260 if priority:
261 key = getPriority
262 else:
263 key = getAbbrevation
264 sortedResult = sorted(filteredResult, None, key)
265 return map(formatResult, sortedResult)
267 def getAbbr(strings):
268 tmplst = filter(lambda x: x.isKindTypedText(), strings)
269 if len(tmplst) == 0:
270 return ""
271 else:
272 return tmplst[0].spelling
274 kinds = dict({ \
275 # Declarations \
276 1 : 't', # CXCursor_UnexposedDecl (A declaration whose specific kind is not \
277 # exposed via this interface) \
278 2 : 't', # CXCursor_StructDecl (A C or C++ struct) \
279 3 : 't', # CXCursor_UnionDecl (A C or C++ union) \
280 4 : 't', # CXCursor_ClassDecl (A C++ class) \
281 5 : 't', # CXCursor_EnumDecl (An enumeration) \
282 6 : 'm', # CXCursor_FieldDecl (A field (in C) or non-static data member \
283 # (in C++) in a struct, union, or C++ class) \
284 7 : 'e', # CXCursor_EnumConstantDecl (An enumerator constant) \
285 8 : 'f', # CXCursor_FunctionDecl (A function) \
286 9 : 'v', # CXCursor_VarDecl (A variable) \
287 10 : 'a', # CXCursor_ParmDecl (A function or method parameter) \
288 11 : '11', # CXCursor_ObjCInterfaceDecl (An Objective-C @interface) \
289 12 : '12', # CXCursor_ObjCCategoryDecl (An Objective-C @interface for a \
290 # category) \
291 13 : '13', # CXCursor_ObjCProtocolDecl (An Objective-C @protocol declaration) \
292 14 : '14', # CXCursor_ObjCPropertyDecl (An Objective-C @property declaration) \
293 15 : '15', # CXCursor_ObjCIvarDecl (An Objective-C instance variable) \
294 16 : '16', # CXCursor_ObjCInstanceMethodDecl (An Objective-C instance method) \
295 17 : '17', # CXCursor_ObjCClassMethodDecl (An Objective-C class method) \
296 18 : '18', # CXCursor_ObjCImplementationDec (An Objective-C @implementation) \
297 19 : '19', # CXCursor_ObjCCategoryImplDecll (An Objective-C @implementation \
298 # for a category) \
299 20 : 't', # CXCursor_TypedefDecl (A typedef) \
300 21 : 'f', # CXCursor_CXXMethod (A C++ class method) \
301 22 : 'n', # CXCursor_Namespace (A C++ namespace) \
302 23 : '23', # CXCursor_LinkageSpec (A linkage specification, e.g. 'extern "C"') \
303 24 : '+', # CXCursor_Constructor (A C++ constructor) \
304 25 : '~', # CXCursor_Destructor (A C++ destructor) \
305 26 : '26', # CXCursor_ConversionFunction (A C++ conversion function) \
306 27 : 'a', # CXCursor_TemplateTypeParameter (A C++ template type parameter) \
307 28 : 'a', # CXCursor_NonTypeTemplateParameter (A C++ non-type template \
308 # parameter) \
309 29 : 'a', # CXCursor_TemplateTemplateParameter (A C++ template template \
310 # parameter) \
311 30 : 'f', # CXCursor_FunctionTemplate (A C++ function template) \
312 31 : 'p', # CXCursor_ClassTemplate (A C++ class template) \
313 32 : '32', # CXCursor_ClassTemplatePartialSpecialization (A C++ class template \
314 # partial specialization) \
315 33 : 'n', # CXCursor_NamespaceAlias (A C++ namespace alias declaration) \
316 34 : '34', # CXCursor_UsingDirective (A C++ using directive) \
317 35 : '35', # CXCursor_UsingDeclaration (A using declaration) \
319 # References \
320 40 : '40', # CXCursor_ObjCSuperClassRef \
321 41 : '41', # CXCursor_ObjCProtocolRef \
322 42 : '42', # CXCursor_ObjCClassRef \
323 43 : '43', # CXCursor_TypeRef \
324 44 : '44', # CXCursor_CXXBaseSpecifier \
325 45 : '45', # CXCursor_TemplateRef (A reference to a class template, function \
326 # template, template template parameter, or class template partial \
327 # specialization) \
328 46 : '46', # CXCursor_NamespaceRef (A reference to a namespace or namespace \
329 # alias) \
330 47 : '47', # CXCursor_MemberRef (A reference to a member of a struct, union, \
331 # or class that occurs in some non-expression context, e.g., a \
332 # designated initializer) \
333 48 : '48', # CXCursor_LabelRef (A reference to a labeled statement) \
334 49 : '49', # CXCursor_OverloadedDeclRef (A reference to a set of overloaded \
335 # functions or function templates that has not yet been resolved to \
336 # a specific function or function template) \
338 # Error conditions \
339 #70 : '70', # CXCursor_FirstInvalid \
340 70 : '70', # CXCursor_InvalidFile \
341 71 : '71', # CXCursor_NoDeclFound \
342 72 : 'u', # CXCursor_NotImplemented \
343 73 : '73', # CXCursor_InvalidCode \
345 # Expressions \
346 100 : '100', # CXCursor_UnexposedExpr (An expression whose specific kind is \
347 # not exposed via this interface) \
348 101 : '101', # CXCursor_DeclRefExpr (An expression that refers to some value \
349 # declaration, such as a function, varible, or enumerator) \
350 102 : '102', # CXCursor_MemberRefExpr (An expression that refers to a member \
351 # of a struct, union, class, Objective-C class, etc) \
352 103 : '103', # CXCursor_CallExpr (An expression that calls a function) \
353 104 : '104', # CXCursor_ObjCMessageExpr (An expression that sends a message \
354 # to an Objective-C object or class) \
355 105 : '105', # CXCursor_BlockExpr (An expression that represents a block \
356 # literal) \
358 # Statements \
359 200 : '200', # CXCursor_UnexposedStmt (A statement whose specific kind is not \
360 # exposed via this interface) \
361 201 : '201', # CXCursor_LabelStmt (A labelled statement in a function) \
363 # Translation unit \
364 300 : '300', # CXCursor_TranslationUnit (Cursor that represents the \
365 # translation unit itself) \
367 # Attributes \
368 400 : '400', # CXCursor_UnexposedAttr (An attribute whose specific kind is \
369 # not exposed via this interface) \
370 401 : '401', # CXCursor_IBActionAttr \
371 402 : '402', # CXCursor_IBOutletAttr \
372 403 : '403', # CXCursor_IBOutletCollectionAttr \
374 # Preprocessing \
375 500 : '500', # CXCursor_PreprocessingDirective \
376 501 : 'd', # CXCursor_MacroDefinition \
377 502 : '502', # CXCursor_MacroInstantiation \
378 503 : '503' # CXCursor_InclusionDirective \
381 # vim: set ts=2 sts=2 sw=2 expandtab :