Browse Source

Server now shuts down cleanly on VimClose

Strahinja Val Markovic 11 years ago
parent
commit
f51a687297
4 changed files with 70 additions and 15 deletions
  1. 6 0
      plugin/youcompleteme.vim
  2. 23 3
      python/ycm/server/server.py
  3. 16 0
      python/ycm/utils.py
  4. 25 12
      python/ycm/youcompleteme.py

+ 6 - 0
plugin/youcompleteme.vim

@@ -160,6 +160,12 @@ let g:ycm_auto_stop_csharp_server =
 let g:ycm_csharp_server_port =
       \ get( g:, 'ycm_csharp_server_port', 2000 )
 
+let g:ycm_server_use_vim_stdout =
+      \ get( g:, 'ycm_server_use_vim_stdout', 0 )
+
+let g:ycm_server_log_level =
+      \ get( g:, 'ycm_server_log_level', 'info' )
+
 " On-demand loading. Let's use the autoload folder and not slow down vim's
 " startup procedure.
 augroup youcompletemeStart

+ 23 - 3
python/ycm/server/server.py

@@ -19,6 +19,7 @@
 
 import sys
 import os
+import threading
 
 # We want to have the YouCompleteMe/python directory on the Python PATH because
 # all the code already assumes that it's there. This is a relic from before the
@@ -38,6 +39,7 @@ from bottle import run, request, response
 import server_state
 from ycm import extra_conf_store
 from ycm import user_options_store
+from ycm import utils
 import argparse
 
 # num bytes for the request body buffer; request.json only works if the request
@@ -66,10 +68,15 @@ def EventNotification():
     getattr( SERVER_STATE.GetFiletypeCompleter( filetypes ),
               event_handler )( request_data )
 
-  if hasattr( extra_conf_store, event_handler ):
-    getattr( extra_conf_store, event_handler )( request_data )
+  try:
+    if hasattr( extra_conf_store, event_handler ):
+      getattr( extra_conf_store, event_handler )( request_data )
+  except OSError as e:
+    LOGGER.exception( e )
+
+  if event_name == 'VimLeave':
+    _ScheduleServerShutdown()
 
-  # TODO: shut down the server on VimClose
 
 
 @app.post( '/run_completer_command' )
@@ -140,6 +147,19 @@ def _JsonResponse( data ):
   return json.dumps( data )
 
 
+def _ScheduleServerShutdown():
+  # The reason why we want to schedule a shutdown in the near future instead of
+  # just shutting down right now is because we want the current request (the one
+  # that made us want to shutdown) to complete successfully first.
+
+  def Shutdown():
+    # sys.exit() doesn't work because we're not in the main thread.
+    utils.TerminateProcess( os.getpid() )
+
+  killer_thread = threading.Timer( 2, Shutdown )
+  killer_thread.start()
+
+
 def Main():
   global LOGGER
   parser = argparse.ArgumentParser()

+ 16 - 0
python/ycm/utils.py

@@ -19,6 +19,8 @@
 
 import tempfile
 import os
+import sys
+import signal
 
 def IsIdentifierChar( char ):
   return char.isalnum() or char == '_'
@@ -36,3 +38,17 @@ def ToUtf8IfNeeded( string_or_unicode ):
 
 def PathToTempDir():
   return os.path.join( tempfile.gettempdir(), 'ycm_temp' )
+
+
+# From here: http://stackoverflow.com/a/8536476/1672783
+def TerminateProcess( pid ):
+  if sys.platform == 'win32':
+    import ctypes
+    PROCESS_TERMINATE = 1
+    handle = ctypes.windll.kernel32.OpenProcess( PROCESS_TERMINATE,
+                                                 False,
+                                                 pid )
+    ctypes.windll.kernel32.TerminateProcess( handle, -1 )
+    ctypes.windll.kernel32.CloseHandle( handle )
+  else:
+    os.kill( pid, signal.SIGTERM )

+ 25 - 12
python/ycm/youcompleteme.py

@@ -37,26 +37,39 @@ class YouCompleteMe( object ):
     self._user_options = user_options
     self._omnicomp = OmniCompleter( user_options )
     self._current_completion_request = None
+    self._server_stdout = None
+    self._server_stderr = None
+    self._SetupServer()
 
+
+  def _SetupServer( self ):
     server_port = SERVER_PORT_RANGE_START + os.getpid()
     command = ''.join( [ 'python ',
                         _PathToServerScript(),
                         ' --port=',
-                        str( server_port ) ] )
+                        str( server_port ),
+                        ' --log=',
+                        self._user_options[ 'server_log_level' ] ] )
 
     BaseRequest.server_location = 'http://localhost:' + str( server_port )
 
-    filename_format = os.path.join( utils.PathToTempDir(),
-                                   'server_{port}_{std}.log' )
-
-    self._server_stdout = filename_format.format( port=server_port,
-                                                  std='stdout' )
-    self._server_stderr = filename_format.format( port=server_port,
-                                                  std='stderr' )
-
-    with open( self._server_stderr, 'w' ) as fstderr:
-      with open( self._server_stdout, 'w' ) as fstdout:
-        subprocess.Popen( command, stdout=fstdout, stderr=fstderr, shell=True )
+    if self._user_options[ 'server_use_vim_stdout' ]:
+      subprocess.Popen( command, shell = True )
+    else:
+      filename_format = os.path.join( utils.PathToTempDir(),
+                                      'server_{port}_{std}.log' )
+
+      self._server_stdout = filename_format.format( port = server_port,
+                                                    std = 'stdout' )
+      self._server_stderr = filename_format.format( port = server_port,
+                                                    std = 'stderr' )
+
+      with open( self._server_stderr, 'w' ) as fstderr:
+        with open( self._server_stdout, 'w' ) as fstdout:
+          subprocess.Popen( command,
+                            stdout = fstdout,
+                            stderr = fstderr,
+                            shell = True )
 
 
   def CreateCompletionRequest( self ):