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) - [YcmCompleter subcommands](#ycmcompleter-subcommands)
- [Options](#options) - [Options](#options)
- [FAQ](#faq) - [FAQ](#faq)
- [Contributor Code of Conduct](#contributor-code-of-conduct)
- [Contact](#contact) - [Contact](#contact)
- [License](#license) - [License](#license)
@ -2609,6 +2610,14 @@ os.environ['PATH'] = ';'.join(path)
EOF 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 Contact
------- -------
@ -2623,6 +2632,7 @@ The latest version of the plugin is available at
The author's homepage is <http://val.markovic.io>. The author's homepage is <http://val.markovic.io>.
License License
------- -------
@ -2684,4 +2694,4 @@ This software is licensed under the [GPL v3 license][gpl].
[rust-src]: https://www.rust-lang.org/downloads.html [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 [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 [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: install:
- git submodule update --init --recursive - git submodule update --init --recursive
- ps: $env:python = if ($env:arch -eq 32) { 'C:\Python27' } else { 'C:\Python27-x64' } - 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% - set PATH=%python%;%python%\Scripts;%PATH%
- python C:\get-pip.py - python get-pip.py
- pip install -r python\test_requirements.txt - pip install -r python\test_requirements.txt
build_script: build_script:
- python run_tests.py - python run_tests.py

View File

@ -17,6 +17,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>. # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
from ycmd.utils import ToUtf8IfNeeded
from ycm.client.completion_request import CompletionRequest from ycm.client.completion_request import CompletionRequest
@ -34,5 +35,32 @@ class OmniCompletionRequest( CompletionRequest ):
return True return True
def RawResponse( self ):
return _ConvertVimDatasToCompletionDatas( self._results )
def Response( self ): def Response( self ):
return self._results 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/>. # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
import os import os
import sys
import vim import vim
import functools import functools
import re
from ycmd import utils from ycmd import utils
DIR_OF_CURRENT_SCRIPT = os.path.dirname( os.path.abspath( __file__ ) ) DIR_OF_CURRENT_SCRIPT = os.path.dirname( os.path.abspath( __file__ ) )
WIN_PYTHON27_PATH = 'C:\python27\python.exe' WIN_PYTHON_PATH = os.path.join( sys.exec_prefix, 'python.exe' )
WIN_PYTHON26_PATH = 'C:\python26\python.exe' PYTHON_BINARY_REGEX = re.compile( r'python(2(\.[67])?)?(.exe)?$' )
def Memoize( obj ): def Memoize( obj ):
@ -42,29 +44,56 @@ def Memoize( obj ):
@Memoize @Memoize
def PathToPythonInterpreter(): 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: if python_interpreter:
return user_path_to_python if IsPythonVersionCorrect( python_interpreter ):
return python_interpreter
# We check for 'python2' before 'python' because some OS's (I'm looking at raise RuntimeError( "Path in 'g:ycm_path_to_python_interpreter' option "
# you Arch Linux) have made the... interesting decision to point "does not point to a valid Python 2.6 or 2.7." )
# /usr/bin/python to python3.
python_names = [ 'python2', 'python' ]
path_to_python = utils.PathToFirstExistingExecutable( python_names ) # On UNIX platforms, we use sys.executable as the Python interpreter path.
if path_to_python: # We cannot use sys.executable on Windows because for unknown reasons, it
return path_to_python # 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 if IsPythonVersionCorrect( python_interpreter ):
# install locations. return python_interpreter
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
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(): def PathToServerScript():

View File

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