This commit is contained in:
Chiel ten Brinke 2016-01-29 09:53:01 +01:00
commit 40db3c0865
9 changed files with 301 additions and 47 deletions

50
CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,50 @@
# Contributor Code of Conduct
As contributors and maintainers of this project, and in the interest of
fostering an open and welcoming community, we pledge to respect all people who
contribute through reporting issues, posting feature requests, updating
documentation, submitting pull requests or patches, and other activities.
We are committed to making participation in this project a harassment-free
experience for everyone, regardless of level of experience, gender, gender
identity and expression, sexual orientation, disability, personal appearance,
body size, race, ethnicity, age, religion, or nationality.
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery
* Personal attacks
* Trolling or insulting/derogatory comments
* Public or private harassment
* Publishing other's private information, such as physical or electronic
addresses, without explicit permission
* Other unethical or unprofessional conduct
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
By adopting this Code of Conduct, project maintainers commit themselves to
fairly and consistently applying these principles to every aspect of managing
this project. Project maintainers who do not follow or enforce the Code of
Conduct may be permanently removed from the project team.
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community.
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting a project maintainer at val@markovic.io. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. Maintainers are
obligated to maintain confidentiality with regard to the reporter of an
incident.
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 1.3.0, available at
[http://contributor-covenant.org/version/1/3/0/][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/3/0/

View File

@ -30,6 +30,7 @@ YouCompleteMe: a code-completion engine for Vim
- [YcmCompleter subcommands](#ycmcompleter-subcommands)
- [Options](#options)
- [FAQ](#faq)
- [Contributor Code of Conduct](#contributor-code-of-conduct)
- [Contact](#contact)
- [License](#license)
@ -2609,6 +2610,14 @@ os.environ['PATH'] = ';'.join(path)
EOF
```
Contributor Code of Conduct
---------------------------
Please note that this project is released with a [Contributor Code of
Conduct][ccoc]. By participating in this project you agree to abide by its
terms.
Contact
-------
@ -2623,6 +2632,7 @@ The latest version of the plugin is available at
The author's homepage is <http://val.markovic.io>.
License
-------
@ -2684,4 +2694,4 @@ This software is licensed under the [GPL v3 license][gpl].
[rust-src]: https://www.rust-lang.org/downloads.html
[add-msbuild-to-path]: http://stackoverflow.com/questions/6319274/how-do-i-run-msbuild-from-the-command-line-using-windows-sdk-7-1
[identify-R6034-cause]: http://stackoverflow.com/questions/14552348/runtime-error-r6034-in-embedded-python-application/34696022
[ccoc]: https://github.com/Valloric/YouCompleteMe/blob/master/CODE_OF_CONDUCT.md

View File

@ -6,9 +6,9 @@ environment:
install:
- git submodule update --init --recursive
- ps: $env:python = if ($env:arch -eq 32) { 'C:\Python27' } else { 'C:\Python27-x64' }
- ps: (new-object net.webclient).DownloadFile('https://raw.github.com/pypa/pip/master/contrib/get-pip.py', 'C:\get-pip.py')
- appveyor DownloadFile https://bootstrap.pypa.io/get-pip.py
- set PATH=%python%;%python%\Scripts;%PATH%
- python C:\get-pip.py
- python get-pip.py
- pip install -r python\test_requirements.txt
build_script:
- python run_tests.py

View File

@ -17,6 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
from ycmd.utils import ToUtf8IfNeeded
from ycm.client.completion_request import CompletionRequest
@ -34,5 +35,32 @@ class OmniCompletionRequest( CompletionRequest ):
return True
def RawResponse( self ):
return _ConvertVimDatasToCompletionDatas( self._results )
def Response( self ):
return self._results
def ConvertVimDataToCompletionData( vim_data ):
# see :h complete-items for a description of the dictionary fields
completion_data = {}
if 'word' in vim_data:
completion_data[ 'insertion_text' ] = ToUtf8IfNeeded( vim_data[ 'word' ] )
if 'abbr' in vim_data:
completion_data[ 'menu_text' ] = ToUtf8IfNeeded( vim_data[ 'abbr' ] )
if 'menu' in vim_data:
completion_data[ 'extra_menu_info' ] = ToUtf8IfNeeded( vim_data[ 'menu' ] )
if 'kind' in vim_data:
completion_data[ 'kind' ] = [ ToUtf8IfNeeded( vim_data[ 'kind' ] ) ]
if 'info' in vim_data:
completion_data[ 'detailed_info' ] = ToUtf8IfNeeded( vim_data[ 'info' ] )
return completion_data
def _ConvertVimDatasToCompletionDatas( response_data ):
return [ ConvertVimDataToCompletionData( x )
for x in response_data ]

View File

@ -18,14 +18,16 @@
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
import os
import sys
import vim
import functools
import re
from ycmd import utils
DIR_OF_CURRENT_SCRIPT = os.path.dirname( os.path.abspath( __file__ ) )
WIN_PYTHON27_PATH = 'C:\python27\python.exe'
WIN_PYTHON26_PATH = 'C:\python26\python.exe'
WIN_PYTHON_PATH = os.path.join( sys.exec_prefix, 'python.exe' )
PYTHON_BINARY_REGEX = re.compile( r'python(2(\.[67])?)?(.exe)?$' )
def Memoize( obj ):
@ -42,29 +44,56 @@ def Memoize( obj ):
@Memoize
def PathToPythonInterpreter():
user_path_to_python = vim.eval( 'g:ycm_path_to_python_interpreter' )
python_interpreter = vim.eval( 'g:ycm_path_to_python_interpreter' )
if user_path_to_python:
return user_path_to_python
if python_interpreter:
if IsPythonVersionCorrect( python_interpreter ):
return python_interpreter
# We check for 'python2' before 'python' because some OS's (I'm looking at
# you Arch Linux) have made the... interesting decision to point
# /usr/bin/python to python3.
python_names = [ 'python2', 'python' ]
raise RuntimeError( "Path in 'g:ycm_path_to_python_interpreter' option "
"does not point to a valid Python 2.6 or 2.7." )
path_to_python = utils.PathToFirstExistingExecutable( python_names )
if path_to_python:
return path_to_python
# On UNIX platforms, we use sys.executable as the Python interpreter path.
# We cannot use sys.executable on Windows because for unknown reasons, it
# returns the Vim executable. Instead, we use sys.exec_prefix to deduce the
# interpreter path.
python_interpreter = ( WIN_PYTHON_PATH if utils.OnWindows() else
sys.executable )
# On Windows, Python may not be on the PATH at all, so we check some common
# install locations.
if utils.OnWindows():
if os.path.exists( WIN_PYTHON27_PATH ):
return WIN_PYTHON27_PATH
elif os.path.exists( WIN_PYTHON26_PATH ):
return WIN_PYTHON26_PATH
if IsPythonVersionCorrect( python_interpreter ):
return python_interpreter
raise RuntimeError( 'Python 2.7/2.6 not installed!' )
# As a last resort, we search python in the PATH. We check 'python2' before
# 'python' because on some distributions (Arch Linux for example), python
# refers to python3.
python_interpreter = utils.PathToFirstExistingExecutable( [ 'python2',
'python' ] )
if IsPythonVersionCorrect( python_interpreter ):
return python_interpreter
raise RuntimeError( "Cannot find Python 2.6 or 2.7. You can set its path "
"using the 'g:ycm_path_to_python_interpreter' "
"option." )
def EndsWithPython( path ):
"""Check if given path ends with a python 2.6 or 2.7 name."""
return PYTHON_BINARY_REGEX.search( path ) is not None
def IsPythonVersionCorrect( path ):
"""Check if given path is the Python interpreter version 2.6 or 2.7."""
if not EndsWithPython( path ):
return False
command = [ path,
'-c',
"import sys;"
"major, minor = sys.version_info[ :2 ];"
"sys.exit( major != 2 or minor < 6)" ]
return utils.SafePopen( command ).wait() == 0
def PathToServerScript():

View File

@ -18,6 +18,7 @@
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
from mock import MagicMock
from hamcrest import assert_that, equal_to
import re
import sys
@ -116,3 +117,10 @@ def MockVimModule():
sys.modules[ 'vim' ] = VIM_MOCK
return VIM_MOCK
class ExtendedMock( MagicMock ):
def assert_has_exact_calls( self, calls, any_order = False ):
self.assert_has_calls( calls, any_order )
assert_that( self.call_count, equal_to( len( calls ) ) )

View File

@ -16,10 +16,11 @@
# You should have received a copy of the GNU General Public License
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
from ycm.test_utils import MockVimModule
from ycm.test_utils import MockVimModule, ExtendedMock
MockVimModule()
import contextlib, os
import contextlib
import os
from ycm.youcompleteme import YouCompleteMe
from ycmd import user_options_store
@ -36,6 +37,7 @@ DEFAULT_CLIENT_OPTIONS = {
'extra_conf_vim_data': [],
}
def PostVimMessage_Call( message ):
"""Return a mock.call object for a call to vimsupport.PostVimMesasge with the
supplied message"""
@ -74,7 +76,7 @@ def MockArbitraryBuffer( filetype, native_available = True ):
raise ValueError( 'Unexpected evaluation' )
# Arbitrary, but valid, cursor position
vim_current.window.cursor = (1,2)
vim_current.window.cursor = ( 1, 2 )
# Arbitrary, but valid, single buffer open
current_buffer = MagicMock()
@ -129,7 +131,7 @@ class EventNotification_test( object ):
def setUp( self ):
options = dict( user_options_store.DefaultOptions() )
options.update ( DEFAULT_CLIENT_OPTIONS )
options.update( DEFAULT_CLIENT_OPTIONS )
user_options_store.SetAll( options )
self.server_state = YouCompleteMe( user_options_store.GetAll() )
@ -141,7 +143,7 @@ class EventNotification_test( object ):
self.server_state.OnVimLeave()
@patch( 'vim.command' )
@patch( 'vim.command', new_callable = ExtendedMock )
def FileReadyToParse_NonDiagnostic_Error_test( self, vim_command ):
# This test validates the behaviour of YouCompleteMe.ValidateParseRequest in
# combination with YouCompleteMe.OnFileReadyToParse when the completer
@ -158,13 +160,13 @@ class EventNotification_test( object ):
self.server_state.ValidateParseRequest()
# The first call raises a warning
vim_command.assert_has_calls( [
vim_command.assert_has_exact_calls( [
PostVimMessage_Call( ERROR_TEXT ),
] )
# Subsequent calls don't re-raise the warning
self.server_state.ValidateParseRequest()
vim_command.assert_has_calls( [
vim_command.assert_has_exact_calls( [
PostVimMessage_Call( ERROR_TEXT ),
] )
@ -172,7 +174,7 @@ class EventNotification_test( object ):
self.server_state.OnFileReadyToParse()
assert self.server_state.DiagnosticsForCurrentFileReady()
self.server_state.ValidateParseRequest()
vim_command.assert_has_calls( [
vim_command.assert_has_exact_calls( [
PostVimMessage_Call( ERROR_TEXT ),
PostVimMessage_Call( ERROR_TEXT ),
] )
@ -187,8 +189,10 @@ class EventNotification_test( object ):
vim_command.assert_not_called()
@patch( 'ycm.client.event_notification._LoadExtraConfFile' )
@patch( 'ycm.client.event_notification._IgnoreExtraConfFile' )
@patch( 'ycm.client.event_notification._LoadExtraConfFile',
new_callable = ExtendedMock )
@patch( 'ycm.client.event_notification._IgnoreExtraConfFile',
new_callable = ExtendedMock )
def FileReadyToParse_NonDiagnostic_ConfirmExtraConf_test(
self,
ignore_extra_conf,
@ -211,25 +215,26 @@ class EventNotification_test( object ):
# When the user accepts the extra conf, we load it
with patch( 'ycm.vimsupport.PresentDialog',
return_value = 0 ) as present_dialog:
return_value = 0,
new_callable = ExtendedMock ) as present_dialog:
self.server_state.OnFileReadyToParse()
assert self.server_state.DiagnosticsForCurrentFileReady()
self.server_state.ValidateParseRequest()
present_dialog.assert_has_calls( [
present_dialog.assert_has_exact_calls( [
PresentDialog_Confirm_Call( MESSAGE ),
] )
load_extra_conf.assert_has_calls( [
load_extra_conf.assert_has_exact_calls( [
call( FILE_NAME ),
] )
# Subsequent calls don't re-raise the warning
self.server_state.ValidateParseRequest()
present_dialog.assert_has_calls( [
present_dialog.assert_has_exact_calls( [
PresentDialog_Confirm_Call( MESSAGE )
] )
load_extra_conf.assert_has_calls( [
load_extra_conf.assert_has_exact_calls( [
call( FILE_NAME ),
] )
@ -238,36 +243,37 @@ class EventNotification_test( object ):
assert self.server_state.DiagnosticsForCurrentFileReady()
self.server_state.ValidateParseRequest()
present_dialog.assert_has_calls( [
present_dialog.assert_has_exact_calls( [
PresentDialog_Confirm_Call( MESSAGE ),
PresentDialog_Confirm_Call( MESSAGE ),
] )
load_extra_conf.assert_has_calls( [
load_extra_conf.assert_has_exact_calls( [
call( FILE_NAME ),
call( FILE_NAME ),
] )
# When the user rejects the extra conf, we reject it
with patch( 'ycm.vimsupport.PresentDialog',
return_value = 1 ) as present_dialog:
return_value = 1,
new_callable = ExtendedMock ) as present_dialog:
self.server_state.OnFileReadyToParse()
assert self.server_state.DiagnosticsForCurrentFileReady()
self.server_state.ValidateParseRequest()
present_dialog.assert_has_calls( [
present_dialog.assert_has_exact_calls( [
PresentDialog_Confirm_Call( MESSAGE ),
] )
ignore_extra_conf.assert_has_calls( [
ignore_extra_conf.assert_has_exact_calls( [
call( FILE_NAME ),
] )
# Subsequent calls don't re-raise the warning
self.server_state.ValidateParseRequest()
present_dialog.assert_has_calls( [
present_dialog.assert_has_exact_calls( [
PresentDialog_Confirm_Call( MESSAGE )
] )
ignore_extra_conf.assert_has_calls( [
ignore_extra_conf.assert_has_exact_calls( [
call( FILE_NAME ),
] )
@ -276,11 +282,11 @@ class EventNotification_test( object ):
assert self.server_state.DiagnosticsForCurrentFileReady()
self.server_state.ValidateParseRequest()
present_dialog.assert_has_calls( [
present_dialog.assert_has_exact_calls( [
PresentDialog_Confirm_Call( MESSAGE ),
PresentDialog_Confirm_Call( MESSAGE ),
] )
ignore_extra_conf.assert_has_calls( [
ignore_extra_conf.assert_has_exact_calls( [
call( FILE_NAME ),
call( FILE_NAME ),
] )

View File

@ -0,0 +1,76 @@
#!/usr/bin/env python
#
# Copyright (C) 2016 YouCompleteMe contributors
#
# This file is part of YouCompleteMe.
#
# YouCompleteMe is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# YouCompleteMe is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
from mock import MagicMock
from nose.tools import eq_
from hamcrest import assert_that, has_entries
from ycm.client.omni_completion_request import OmniCompletionRequest
def BuildOmnicompletionRequest( results ):
omni_completer = MagicMock()
omni_completer.ComputeCandidates = MagicMock( return_value = results )
request = OmniCompletionRequest( omni_completer, None )
request.Start()
return request;
def Done_AlwaysTrue_test():
request = BuildOmnicompletionRequest( [] )
eq_( request.Done(), True )
def Response_FromOmniCompleter_test():
results = [ { "word": "test" } ]
request = BuildOmnicompletionRequest( results )
eq_( request.Response(), results )
def RawResponse_ConvertedFromOmniCompleter_test():
vim_results = [
{ "word": "WORD", "abbr": "ABBR", "menu": "MENU",
"kind": "KIND", "info": "INFO" },
{ "word": "WORD2", "abbr": "ABBR2", "menu": "MENU2",
"kind": "KIND2", "info": "INFO" },
{ "word": "WORD", "abbr": "ABBR", },
{ },
]
expected_results = [
has_entries( { "insertion_text": "WORD", "menu_text": "ABBR",
"extra_menu_info": "MENU", "kind": [ "KIND" ],
"detailed_info": "INFO" } ),
has_entries( { "insertion_text": "WORD2", "menu_text": "ABBR2",
"extra_menu_info": "MENU2", "kind": [ "KIND2" ],
"detailed_info": "INFO" } ),
has_entries( { "insertion_text": "WORD", "menu_text": "ABBR", } ),
has_entries( { } ),
]
request = BuildOmnicompletionRequest( vim_results )
results = request.RawResponse()
eq_( len( results ), len( expected_results ) )
for result, expected_result in zip( results, expected_results ):
assert_that( result, expected_result )

View File

@ -0,0 +1,47 @@
#
# Copyright (C) 2016 YouCompleteMe contributors
#
# This file is part of YouCompleteMe.
#
# YouCompleteMe is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# YouCompleteMe is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
from ycm.test_utils import MockVimModule
MockVimModule()
from nose.tools import ok_
from ycm.paths import EndsWithPython
def EndsWithPython_Python2Paths_test():
python_paths = [
'python',
'/usr/bin/python2.6',
'/home/user/.pyenv/shims/python2.7',
r'C:\Python26\python.exe'
]
for path in python_paths:
ok_( EndsWithPython( path ) )
def EndsWithPython_NotPython2Paths_test():
not_python_paths = [
'/opt/local/bin/vim',
r'C:\Program Files\Vim\vim74\gvim.exe',
'/usr/bin/python3',
'/home/user/.pyenv/shims/python3',
]
for path in not_python_paths:
ok_( not EndsWithPython( path ) )