1
0
Эх сурвалжийг харах

Avoid evaluating Vim globals in Python

On Python 3, evaluating a Vim expression will raise a unicode exception
if it contains an invalid sequence of bytes for the current encoding.
We can't really do anything about it because this is the way Vim and
Python 3 interact. However, we can prevent this situation to occur by
not evaluating Vim data that we have no control over: in particular,
the Vim globals. This is done by:
 - adding one by one the YCM default options instead of extending the
   Vim globals with them;
 - only evaluating the Vim global variable names (and not their values)
   when building the YCM options for the ycmd server.
micbou 9 жил өмнө
parent
commit
b923431d7d

+ 9 - 11
python/ycm/base.py

@@ -35,17 +35,16 @@ YCM_VAR_PREFIX = 'ycm_'
 def BuildServerConf():
   """Builds a dictionary mapping YCM Vim user options to values. Option names
   don't have the 'ycm_' prefix."""
-
-  vim_globals = vimsupport.GetReadOnlyVimGlobals( force_python_objects = True )
+  # We only evaluate the keys of the vim globals and not the whole dictionary
+  # to avoid unicode issues.
+  # See https://github.com/Valloric/YouCompleteMe/pull/2151 for details.
+  keys = vimsupport.GetVimGlobalsKeys()
   server_conf = {}
-  for key, value in iteritems( vim_globals ):
+  for key in keys:
     if not key.startswith( YCM_VAR_PREFIX ):
       continue
-    try:
-      new_value = int( value )
-    except:
-      new_value = value
     new_key = key[ len( YCM_VAR_PREFIX ): ]
+    new_value = vimsupport.VimExpressionToPythonType( 'g:' + key )
     server_conf[ new_key ] = new_value
 
   return server_conf
@@ -53,11 +52,10 @@ def BuildServerConf():
 
 def LoadJsonDefaultsIntoVim():
   defaults = user_options_store.DefaultOptions()
-  vim_defaults = {}
   for key, value in iteritems( defaults ):
-    vim_defaults[ 'ycm_' + key ] = value
-
-  vimsupport.LoadDictIntoVimGlobals( vim_defaults, overwrite = False )
+    new_key = 'g:ycm_' + key
+    if not vimsupport.VariableExists( new_key ):
+      vimsupport.SetVariableValue( new_key, value )
 
 
 def CompletionStartColumn():

+ 5 - 24
python/ycm/vimsupport.py

@@ -294,27 +294,8 @@ def ConvertDiagnosticsToQfList( diagnostics ):
   return [ ConvertDiagnosticToQfFormat( x ) for x in diagnostics ]
 
 
-# Given a dict like {'a': 1}, loads it into Vim as if you ran 'let g:a = 1'
-# When |overwrite| is True, overwrites the existing value in Vim.
-def LoadDictIntoVimGlobals( new_globals, overwrite = True ):
-  extend_option = '"force"' if overwrite else '"keep"'
-
-  # We need to use json.dumps because that won't use the 'u' prefix on strings
-  # which Vim would bork on.
-  vim.eval( 'extend( g:, {0}, {1})'.format( json.dumps( new_globals ),
-                                            extend_option ) )
-
-
-# Changing the returned dict will NOT change the value in Vim.
-def GetReadOnlyVimGlobals( force_python_objects = False ):
-  if force_python_objects:
-    return vim.eval( 'g:' )
-
-  try:
-    # vim.vars is fairly new so it might not exist
-    return vim.vars
-  except:
-    return vim.eval( 'g:' )
+def GetVimGlobalsKeys():
+  return vim.eval( 'keys( g: )' )
 
 
 def VimExpressionToPythonType( vim_expression ):
@@ -491,8 +472,8 @@ def EchoTextVimWidth( text ):
 
   EchoText( truncated_text, False )
 
-  vim.command( 'let &ruler = {0}'.format( old_ruler ) )
-  vim.command( 'let &showcmd = {0}'.format( old_showcmd ) )
+  SetVariableValue( '&ruler', old_ruler )
+  SetVariableValue( '&showcmd', old_showcmd )
 
 
 def EscapeForVim( text ):
@@ -514,7 +495,7 @@ def VariableExists( variable ):
 
 
 def SetVariableValue( variable, value ):
-  vim.command( "let {0} = '{1}'".format( variable, EscapeForVim( value ) ) )
+  vim.command( "let {0} = {1}".format( variable, json.dumps( value ) ) )
 
 
 def GetVariableValue( variable ):