youcompleteme.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648
  1. # Copyright (C) 2011, 2012 Google Inc.
  2. #
  3. # This file is part of YouCompleteMe.
  4. #
  5. # YouCompleteMe is free software: you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation, either version 3 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # YouCompleteMe is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
  17. import os
  18. import vim
  19. import tempfile
  20. import json
  21. import re
  22. import signal
  23. import base64
  24. from subprocess import PIPE
  25. from ycm import paths, vimsupport
  26. from ycmd import utils
  27. from ycmd.request_wrap import RequestWrap
  28. from ycm.diagnostic_interface import DiagnosticInterface
  29. from ycm.omni_completer import OmniCompleter
  30. from ycm import syntax_parse
  31. from ycm.client.ycmd_keepalive import YcmdKeepalive
  32. from ycm.client.base_request import BaseRequest, BuildRequestData
  33. from ycm.client.completer_available_request import SendCompleterAvailableRequest
  34. from ycm.client.command_request import SendCommandRequest
  35. from ycm.client.completion_request import ( CompletionRequest,
  36. ConvertCompletionDataToVimData )
  37. from ycm.client.omni_completion_request import OmniCompletionRequest
  38. from ycm.client.event_notification import ( SendEventNotificationAsync,
  39. EventNotification )
  40. from ycmd.responses import ServerError
  41. try:
  42. from UltiSnips import UltiSnips_Manager
  43. USE_ULTISNIPS_DATA = True
  44. except ImportError:
  45. USE_ULTISNIPS_DATA = False
  46. def PatchNoProxy():
  47. current_value = os.environ.get('no_proxy', '')
  48. additions = '127.0.0.1,localhost'
  49. os.environ['no_proxy'] = ( additions if not current_value
  50. else current_value + ',' + additions )
  51. # We need this so that Requests doesn't end up using the local HTTP proxy when
  52. # talking to ycmd. Users should actually be setting this themselves when
  53. # configuring a proxy server on their machine, but most don't know they need to
  54. # or how to do it, so we do it for them.
  55. # Relevant issues:
  56. # https://github.com/Valloric/YouCompleteMe/issues/641
  57. # https://github.com/kennethreitz/requests/issues/879
  58. PatchNoProxy()
  59. # Force the Python interpreter embedded in Vim (in which we are running) to
  60. # ignore the SIGINT signal. This helps reduce the fallout of a user pressing
  61. # Ctrl-C in Vim.
  62. signal.signal( signal.SIGINT, signal.SIG_IGN )
  63. HMAC_SECRET_LENGTH = 16
  64. SERVER_CRASH_MESSAGE_STDERR_FILE = (
  65. "The ycmd server SHUT DOWN (restart with ':YcmRestartServer'). "
  66. "Run ':YcmToggleLogs stderr' to check the logs." )
  67. SERVER_CRASH_MESSAGE_STDERR_FILE_DELETED = (
  68. "The ycmd server SHUT DOWN (restart with ':YcmRestartServer'). "
  69. "Logfile was deleted; set 'g:ycm_server_keep_logfiles' to see errors "
  70. "in the future." )
  71. SERVER_IDLE_SUICIDE_SECONDS = 10800 # 3 hours
  72. class YouCompleteMe( object ):
  73. def __init__( self, user_options ):
  74. self._user_options = user_options
  75. self._user_notified_about_crash = False
  76. self._diag_interface = DiagnosticInterface( user_options )
  77. self._omnicomp = OmniCompleter( user_options )
  78. self._latest_file_parse_request = None
  79. self._latest_completion_request = None
  80. self._server_stdout = None
  81. self._server_stderr = None
  82. self._server_popen = None
  83. self._filetypes_with_keywords_loaded = set()
  84. self._ycmd_keepalive = YcmdKeepalive()
  85. self._SetupServer()
  86. self._ycmd_keepalive.Start()
  87. self._complete_done_hooks = {
  88. 'cs': lambda( self ): self._OnCompleteDone_Csharp()
  89. }
  90. def _SetupServer( self ):
  91. self._available_completers = {}
  92. server_port = utils.GetUnusedLocalhostPort()
  93. # The temp options file is deleted by ycmd during startup
  94. with tempfile.NamedTemporaryFile( delete = False ) as options_file:
  95. hmac_secret = os.urandom( HMAC_SECRET_LENGTH )
  96. options_dict = dict( self._user_options )
  97. options_dict[ 'hmac_secret' ] = base64.b64encode( hmac_secret )
  98. json.dump( options_dict, options_file )
  99. options_file.flush()
  100. args = [ paths.PathToPythonInterpreter(),
  101. paths.PathToServerScript(),
  102. '--port={0}'.format( server_port ),
  103. '--options_file={0}'.format( options_file.name ),
  104. '--log={0}'.format( self._user_options[ 'server_log_level' ] ),
  105. '--idle_suicide_seconds={0}'.format(
  106. SERVER_IDLE_SUICIDE_SECONDS )]
  107. filename_format = os.path.join( utils.PathToTempDir(),
  108. 'server_{port}_{std}.log' )
  109. self._server_stdout = filename_format.format( port = server_port,
  110. std = 'stdout' )
  111. self._server_stderr = filename_format.format( port = server_port,
  112. std = 'stderr' )
  113. args.append( '--stdout={0}'.format( self._server_stdout ) )
  114. args.append( '--stderr={0}'.format( self._server_stderr ) )
  115. if self._user_options[ 'server_keep_logfiles' ]:
  116. args.append( '--keep_logfiles' )
  117. self._server_popen = utils.SafePopen( args, stdin_windows = PIPE,
  118. stdout = PIPE, stderr = PIPE)
  119. BaseRequest.server_location = 'http://127.0.0.1:' + str( server_port )
  120. BaseRequest.hmac_secret = hmac_secret
  121. self._NotifyUserIfServerCrashed()
  122. def IsServerAlive( self ):
  123. returncode = self._server_popen.poll()
  124. # When the process hasn't finished yet, poll() returns None.
  125. return returncode is None
  126. def _NotifyUserIfServerCrashed( self ):
  127. if self._user_notified_about_crash or self.IsServerAlive():
  128. return
  129. self._user_notified_about_crash = True
  130. try:
  131. vimsupport.CheckFilename( self._server_stderr )
  132. vimsupport.PostVimMessage( SERVER_CRASH_MESSAGE_STDERR_FILE )
  133. except RuntimeError:
  134. vimsupport.PostVimMessage( SERVER_CRASH_MESSAGE_STDERR_FILE_DELETED )
  135. def ServerPid( self ):
  136. if not self._server_popen:
  137. return -1
  138. return self._server_popen.pid
  139. def _ServerCleanup( self ):
  140. if self.IsServerAlive():
  141. self._server_popen.terminate()
  142. def RestartServer( self ):
  143. self._CloseLogs()
  144. vimsupport.PostVimMessage( 'Restarting ycmd server...' )
  145. self._user_notified_about_crash = False
  146. self._ServerCleanup()
  147. self._SetupServer()
  148. def CreateCompletionRequest( self, force_semantic = False ):
  149. request_data = BuildRequestData()
  150. if ( not self.NativeFiletypeCompletionAvailable() and
  151. self.CurrentFiletypeCompletionEnabled() ):
  152. wrapped_request_data = RequestWrap( request_data )
  153. if self._omnicomp.ShouldUseNow( wrapped_request_data ):
  154. self._latest_completion_request = OmniCompletionRequest(
  155. self._omnicomp, wrapped_request_data )
  156. return self._latest_completion_request
  157. request_data[ 'working_dir' ] = os.getcwd()
  158. self._AddExtraConfDataIfNeeded( request_data )
  159. if force_semantic:
  160. request_data[ 'force_semantic' ] = True
  161. self._latest_completion_request = CompletionRequest( request_data )
  162. return self._latest_completion_request
  163. def SendCommandRequest( self, arguments, completer ):
  164. if self.IsServerAlive():
  165. return SendCommandRequest( arguments, completer )
  166. def GetDefinedSubcommands( self ):
  167. if self.IsServerAlive():
  168. try:
  169. return BaseRequest.PostDataToHandler( BuildRequestData(),
  170. 'defined_subcommands' )
  171. except ServerError:
  172. return []
  173. else:
  174. return []
  175. def GetCurrentCompletionRequest( self ):
  176. return self._latest_completion_request
  177. def GetOmniCompleter( self ):
  178. return self._omnicomp
  179. def FiletypeCompleterExistsForFiletype( self, filetype ):
  180. try:
  181. return self._available_completers[ filetype ]
  182. except KeyError:
  183. pass
  184. if not self.IsServerAlive():
  185. return False
  186. exists_completer = SendCompleterAvailableRequest( filetype )
  187. if exists_completer is None:
  188. return False
  189. self._available_completers[ filetype ] = exists_completer
  190. return exists_completer
  191. def NativeFiletypeCompletionAvailable( self ):
  192. return any( [ self.FiletypeCompleterExistsForFiletype( x ) for x in
  193. vimsupport.CurrentFiletypes() ] )
  194. def NativeFiletypeCompletionUsable( self ):
  195. return ( self.CurrentFiletypeCompletionEnabled() and
  196. self.NativeFiletypeCompletionAvailable() )
  197. def OnFileReadyToParse( self ):
  198. self._omnicomp.OnFileReadyToParse( None )
  199. if not self.IsServerAlive():
  200. self._NotifyUserIfServerCrashed()
  201. extra_data = {}
  202. self._AddTagsFilesIfNeeded( extra_data )
  203. self._AddSyntaxDataIfNeeded( extra_data )
  204. self._AddExtraConfDataIfNeeded( extra_data )
  205. self._latest_file_parse_request = EventNotification( 'FileReadyToParse',
  206. extra_data )
  207. self._latest_file_parse_request.Start()
  208. def OnBufferUnload( self, deleted_buffer_file ):
  209. if not self.IsServerAlive():
  210. return
  211. SendEventNotificationAsync( 'BufferUnload',
  212. { 'unloaded_buffer': deleted_buffer_file } )
  213. def OnBufferVisit( self ):
  214. if not self.IsServerAlive():
  215. return
  216. extra_data = {}
  217. _AddUltiSnipsDataIfNeeded( extra_data )
  218. SendEventNotificationAsync( 'BufferVisit', extra_data )
  219. def OnInsertLeave( self ):
  220. if not self.IsServerAlive():
  221. return
  222. SendEventNotificationAsync( 'InsertLeave' )
  223. def OnCursorMoved( self ):
  224. self._diag_interface.OnCursorMoved()
  225. def OnVimLeave( self ):
  226. self._ServerCleanup()
  227. def OnCurrentIdentifierFinished( self ):
  228. if not self.IsServerAlive():
  229. return
  230. SendEventNotificationAsync( 'CurrentIdentifierFinished' )
  231. def OnCompleteDone( self ):
  232. complete_done_actions = self.GetCompleteDoneHooks()
  233. for action in complete_done_actions:
  234. action(self)
  235. def GetCompleteDoneHooks( self ):
  236. filetypes = vimsupport.CurrentFiletypes()
  237. for key, value in self._complete_done_hooks.iteritems():
  238. if key in filetypes:
  239. yield value
  240. def GetCompletionsUserMayHaveCompleted( self ):
  241. latest_completion_request = self.GetCurrentCompletionRequest()
  242. if not latest_completion_request or not latest_completion_request.Done():
  243. return []
  244. completions = latest_completion_request.RawResponse()
  245. result = self._FilterToMatchingCompletions( completions, True )
  246. result = list( result )
  247. if result:
  248. return result
  249. if self._HasCompletionsThatCouldBeCompletedWithMoreText( completions ):
  250. # Since the way that YCM works leads to CompleteDone called on every
  251. # character, return blank if the completion might not be done. This won't
  252. # match if the completion is ended with typing a non-keyword character.
  253. return []
  254. result = self._FilterToMatchingCompletions( completions, False )
  255. return list( result )
  256. def _FilterToMatchingCompletions( self, completions, full_match_only ):
  257. self._PatchBasedOnVimVersion()
  258. return self._FilterToMatchingCompletions( completions, full_match_only)
  259. def _HasCompletionsThatCouldBeCompletedWithMoreText( self, completions ):
  260. self._PatchBasedOnVimVersion()
  261. return self._HasCompletionsThatCouldBeCompletedWithMoreText( completions )
  262. def _PatchBasedOnVimVersion( self ):
  263. if vimsupport.VimVersionAtLeast( "7.4.774" ):
  264. self._HasCompletionsThatCouldBeCompletedWithMoreText = \
  265. self._HasCompletionsThatCouldBeCompletedWithMoreText_NewerVim
  266. self._FilterToMatchingCompletions = \
  267. self._FilterToMatchingCompletions_NewerVim
  268. else:
  269. self._FilterToMatchingCompletions = \
  270. self._FilterToMatchingCompletions_OlderVim
  271. self._HasCompletionsThatCouldBeCompletedWithMoreText = \
  272. self._HasCompletionsThatCouldBeCompletedWithMoreText_OlderVim
  273. def _FilterToMatchingCompletions_NewerVim( self, completions,
  274. full_match_only ):
  275. """ Filter to completions matching the item Vim said was completed """
  276. completed = vimsupport.GetVariableValue( 'v:completed_item' )
  277. for completion in completions:
  278. item = ConvertCompletionDataToVimData( completion )
  279. match_keys = ( [ "word", "abbr", "menu", "info" ] if full_match_only
  280. else [ 'word' ] )
  281. matcher = lambda key: completed.get( key, "" ) == item.get( key, "" )
  282. if all( [ matcher( i ) for i in match_keys ] ):
  283. yield completion
  284. def _FilterToMatchingCompletions_OlderVim( self, completions,
  285. full_match_only ):
  286. """ Filter to completions matching the buffer text """
  287. if full_match_only:
  288. return # Only supported in 7.4.774+
  289. # No support for multiple line completions
  290. text = vimsupport.TextBeforeCursor()
  291. for completion in completions:
  292. word = completion[ "insertion_text" ]
  293. # Trim complete-ending character if needed
  294. text = re.sub( r"[^a-zA-Z0-9_]$", "", text )
  295. buffer_text = text[ -1 * len( word ) : ]
  296. if buffer_text == word:
  297. yield completion
  298. def _HasCompletionsThatCouldBeCompletedWithMoreText_NewerVim( self,
  299. completions ):
  300. completed_item = vimsupport.GetVariableValue( 'v:completed_item' )
  301. if not completed_item:
  302. return False
  303. completed_word = completed_item[ 'word' ]
  304. if not completed_word:
  305. return False
  306. # Sometime CompleteDone is called after the next character is inserted
  307. # If so, use inserted character to filter possible completions further
  308. text = vimsupport.TextBeforeCursor()
  309. reject_exact_match = True
  310. if text and text[ -1 ] != completed_word[ -1 ]:
  311. reject_exact_match = False
  312. completed_word += text[ -1 ]
  313. for completion in completions:
  314. word = ConvertCompletionDataToVimData( completion )[ 'word' ]
  315. if reject_exact_match and word == completed_word:
  316. continue
  317. if word.startswith( completed_word ):
  318. return True
  319. return False
  320. def _HasCompletionsThatCouldBeCompletedWithMoreText_OlderVim( self,
  321. completions ):
  322. # No support for multiple line completions
  323. text = vimsupport.TextBeforeCursor()
  324. for completion in completions:
  325. word = ConvertCompletionDataToVimData( completion )[ 'word' ]
  326. for i in range( 1, len( word ) - 1 ): # Excluding full word
  327. if text[ -1 * i : ] == word[ : i ]:
  328. return True
  329. return False
  330. def _OnCompleteDone_Csharp( self ):
  331. completions = self.GetCompletionsUserMayHaveCompleted()
  332. namespaces = [ self._GetRequiredNamespaceImport( c )
  333. for c in completions ]
  334. namespaces = [ n for n in namespaces if n ]
  335. if not namespaces:
  336. return
  337. if len( namespaces ) > 1:
  338. choices = [ "{0} {1}".format( i + 1, n )
  339. for i,n in enumerate( namespaces ) ]
  340. choice = vimsupport.PresentDialog( "Insert which namespace:", choices )
  341. if choice < 0:
  342. return
  343. namespace = namespaces[ choice ]
  344. else:
  345. namespace = namespaces[ 0 ]
  346. vimsupport.InsertNamespace( namespace )
  347. def _GetRequiredNamespaceImport( self, completion ):
  348. if ( "extra_data" not in completion
  349. or "required_namespace_import" not in completion[ "extra_data" ] ):
  350. return None
  351. return completion[ "extra_data" ][ "required_namespace_import" ]
  352. def GetErrorCount( self ):
  353. return self._diag_interface.GetErrorCount()
  354. def GetWarningCount( self ):
  355. return self._diag_interface.GetWarningCount()
  356. def DiagnosticsForCurrentFileReady( self ):
  357. return bool( self._latest_file_parse_request and
  358. self._latest_file_parse_request.Done() )
  359. def GetDiagnosticsFromStoredRequest( self, qflist_format = False ):
  360. if self.DiagnosticsForCurrentFileReady():
  361. diagnostics = self._latest_file_parse_request.Response()
  362. # We set the diagnostics request to None because we want to prevent
  363. # repeated refreshing of the buffer with the same diags. Setting this to
  364. # None makes DiagnosticsForCurrentFileReady return False until the next
  365. # request is created.
  366. self._latest_file_parse_request = None
  367. if qflist_format:
  368. return vimsupport.ConvertDiagnosticsToQfList( diagnostics )
  369. else:
  370. return diagnostics
  371. return []
  372. def UpdateDiagnosticInterface( self ):
  373. if ( self.DiagnosticsForCurrentFileReady() and
  374. self.NativeFiletypeCompletionUsable() ):
  375. self._diag_interface.UpdateWithNewDiagnostics(
  376. self.GetDiagnosticsFromStoredRequest() )
  377. def ValidateParseRequest( self ):
  378. if ( self.DiagnosticsForCurrentFileReady() and
  379. self.NativeFiletypeCompletionUsable() ):
  380. # YCM client has a hard-coded list of filetypes which are known to support
  381. # diagnostics. These are found in autoload/youcompleteme.vim in
  382. # s:diagnostic_ui_filetypes.
  383. #
  384. # For filetypes which don't support diagnostics, we just want to check the
  385. # _latest_file_parse_request for any exception or UnknownExtraConf
  386. # response, to allow the server to raise configuration warnings, etc.
  387. # to the user. We ignore any other supplied data.
  388. self._latest_file_parse_request.Response()
  389. # We set the diagnostics request to None because we want to prevent
  390. # repeated issuing of the same warnings/errors/prompts. Setting this to
  391. # None makes DiagnosticsForCurrentFileReady return False until the next
  392. # request is created.
  393. #
  394. # Note: it is the server's responsibility to determine the frequency of
  395. # error/warning/prompts when receiving a FileReadyToParse event, but
  396. # it our responsibility to ensure that we only apply the
  397. # warning/error/prompt received once (for each event).
  398. self._latest_file_parse_request = None
  399. def ShowDetailedDiagnostic( self ):
  400. if not self.IsServerAlive():
  401. return
  402. try:
  403. debug_info = BaseRequest.PostDataToHandler( BuildRequestData(),
  404. 'detailed_diagnostic' )
  405. if 'message' in debug_info:
  406. vimsupport.EchoText( debug_info[ 'message' ] )
  407. except ServerError as e:
  408. vimsupport.PostVimMessage( str( e ) )
  409. def DebugInfo( self ):
  410. if self.IsServerAlive():
  411. debug_info = BaseRequest.PostDataToHandler( BuildRequestData(),
  412. 'debug_info' )
  413. else:
  414. debug_info = 'Server crashed, no debug info from server'
  415. debug_info += '\nServer running at: {0}'.format(
  416. BaseRequest.server_location )
  417. debug_info += '\nServer process ID: {0}'.format( self._server_popen.pid )
  418. if self._server_stderr or self._server_stdout:
  419. debug_info += '\nServer logfiles:\n {0}\n {1}'.format(
  420. self._server_stdout,
  421. self._server_stderr )
  422. return debug_info
  423. def _OpenLogs( self, stdout = True, stderr = True ):
  424. # Open log files in a horizontal window with the same behavior as the
  425. # preview window (same height and winfixheight enabled). Automatically
  426. # watch for changes. Set the cursor position at the end of the file.
  427. options = {
  428. 'size': vimsupport.GetIntValue( '&previewheight' ),
  429. 'fix': True,
  430. 'watch': True,
  431. 'position': 'end'
  432. }
  433. if stdout:
  434. vimsupport.OpenFilename( self._server_stdout, options )
  435. if stderr:
  436. vimsupport.OpenFilename( self._server_stderr, options )
  437. def _CloseLogs( self, stdout = True, stderr = True ):
  438. if stdout:
  439. vimsupport.CloseBuffersForFilename( self._server_stdout )
  440. if stderr:
  441. vimsupport.CloseBuffersForFilename( self._server_stderr )
  442. def ToggleLogs( self, stdout = True, stderr = True ):
  443. if ( stdout and
  444. vimsupport.BufferIsVisibleForFilename( self._server_stdout ) or
  445. stderr and
  446. vimsupport.BufferIsVisibleForFilename( self._server_stderr ) ):
  447. return self._CloseLogs( stdout = stdout, stderr = stderr )
  448. # Close hidden logfile buffers if any to keep a clean state
  449. self._CloseLogs( stdout = stdout, stderr = stderr )
  450. try:
  451. self._OpenLogs( stdout = stdout, stderr = stderr )
  452. except RuntimeError as error:
  453. vimsupport.PostVimMessage( 'YouCompleteMe encountered an error when '
  454. 'opening logs: {0}.'.format( error ) )
  455. def CurrentFiletypeCompletionEnabled( self ):
  456. filetypes = vimsupport.CurrentFiletypes()
  457. filetype_to_disable = self._user_options[
  458. 'filetype_specific_completion_to_disable' ]
  459. if '*' in filetype_to_disable:
  460. return False
  461. else:
  462. return not any([ x in filetype_to_disable for x in filetypes ])
  463. def _AddSyntaxDataIfNeeded( self, extra_data ):
  464. if not self._user_options[ 'seed_identifiers_with_syntax' ]:
  465. return
  466. filetype = vimsupport.CurrentFiletypes()[ 0 ]
  467. if filetype in self._filetypes_with_keywords_loaded:
  468. return
  469. self._filetypes_with_keywords_loaded.add( filetype )
  470. extra_data[ 'syntax_keywords' ] = list(
  471. syntax_parse.SyntaxKeywordsForCurrentBuffer() )
  472. def _AddTagsFilesIfNeeded( self, extra_data ):
  473. def GetTagFiles():
  474. tag_files = vim.eval( 'tagfiles()' )
  475. # getcwd() throws an exception when the CWD has been deleted.
  476. try:
  477. current_working_directory = os.getcwd()
  478. except OSError:
  479. return []
  480. return [ os.path.join( current_working_directory, x ) for x in tag_files ]
  481. if not self._user_options[ 'collect_identifiers_from_tags_files' ]:
  482. return
  483. extra_data[ 'tag_files' ] = GetTagFiles()
  484. def _AddExtraConfDataIfNeeded( self, extra_data ):
  485. def BuildExtraConfData( extra_conf_vim_data ):
  486. return dict( ( expr, vimsupport.VimExpressionToPythonType( expr ) )
  487. for expr in extra_conf_vim_data )
  488. extra_conf_vim_data = self._user_options[ 'extra_conf_vim_data' ]
  489. if extra_conf_vim_data:
  490. extra_data[ 'extra_conf_data' ] = BuildExtraConfData(
  491. extra_conf_vim_data )
  492. def _AddUltiSnipsDataIfNeeded( extra_data ):
  493. if not USE_ULTISNIPS_DATA:
  494. return
  495. try:
  496. # Since UltiSnips may run in a different python interpreter (python 3) than
  497. # YCM, UltiSnips_Manager singleton is not necessary the same as the one
  498. # used by YCM. In particular, it means that we cannot rely on UltiSnips to
  499. # set the current filetypes to the singleton. We need to do it ourself.
  500. UltiSnips_Manager.reset_buffer_filetypes()
  501. UltiSnips_Manager.add_buffer_filetypes(
  502. vimsupport.GetVariableValue( '&filetype' ) )
  503. rawsnips = UltiSnips_Manager._snips( '', True )
  504. except:
  505. return
  506. # UltiSnips_Manager._snips() returns a class instance where:
  507. # class.trigger - name of snippet trigger word ( e.g. defn or testcase )
  508. # class.description - description of the snippet
  509. extra_data[ 'ultisnips_snippets' ] = [ { 'trigger': x.trigger,
  510. 'description': x.description
  511. } for x in rawsnips ]