youcompleteme.py 25 KB

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