Auto merge of #2151 - micbou:avoid-eval-vim-globals, r=Valloric

[READY] Avoid evaluating Vim globals in Python

### Problem

See the commit message and issue #2127.

### How to reproduce

Make Vim to use Python 3 for YCM by either using Vim with only Python 3 support or by editing the `s:UsingPython2` function in `autoload/youcompleteme.vim` to always return 0.
Create the following `vimrc`:
```
set nocompatible

set runtimepath+=~/.vim/bundle/YouCompleteMe

set encoding=utf8

filetype plugin indent on

let g:dummy_variable = '€'[0]
```
and start Vim with it. The following error will occur:
```
YouCompleteMe unavailable: 'utf-8' codec can't decode byte 0xe2 in position 0: unexpected end of data
```
with the traceback in `:messages`:
```python
Traceback (most recent call last):
  File "<string>", line 24, in <module>
  File "C:\\Users\\micbou\\.vim\\bundle\\YouCompleteMe\\autoload\..\python\ycm\setup.py", line 49, in SetUpYCM
    base.LoadJsonDefaultsIntoVim()
  File "C:\\Users\\micbou\\.vim\\bundle\\YouCompleteMe\\autoload\..\python\ycm\base.py", line 60, in LoadJsonDefaultsIntoVim
    vimsupport.LoadDictIntoVimGlobals( vim_defaults, overwrite = False )
  File "C:\\Users\\micbou\\.vim\\bundle\\YouCompleteMe\\autoload\..\python\ycm\vimsupport.py", line 305, in LoadDictIntoVimGlobals
    extend_option ) )
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe2 in position 0: unexpected end of data
```

### Solution

Do not evaluate the Vim globals when loading the YCM default options into Vim and when building the options for the ycmd server.

Depending on the number of global variables and custom YCM options, this may be slower or faster than the current code but by a negligible margin (~1ms).

Fixes #2127 and #2150.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="35" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/valloric/youcompleteme/2151)
<!-- Reviewable:end -->
This commit is contained in:
Homu 2016-05-07 19:10:32 +09:00
commit 73584b2978
2 changed files with 14 additions and 35 deletions

View File

@ -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():

View File

@ -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 ):