From cb83e992457b91e2102353a4f399c4969daf9a66 Mon Sep 17 00:00:00 2001 From: micbou Date: Sat, 4 Feb 2017 21:46:54 +0100 Subject: [PATCH] Rewrite completion system --- autoload/youcompleteme.vim | 209 +++++++++--------- python/ycm/base.py | 8 - python/ycm/client/completion_request.py | 23 +- python/ycm/client/omni_completion_request.py | 10 +- python/ycm/omni_completer.py | 2 + .../client/omni_completion_request_tests.py | 14 +- python/ycm/tests/completion_test.py | 191 +++++++++------- python/ycm/tests/event_notification_test.py | 14 +- python/ycm/tests/test_utils.py | 3 - python/ycm/youcompleteme.py | 30 ++- 10 files changed, 269 insertions(+), 235 deletions(-) diff --git a/autoload/youcompleteme.vim b/autoload/youcompleteme.vim index b09c4d383e..056c60a364 100644 --- a/autoload/youcompleteme.vim +++ b/autoload/youcompleteme.vim @@ -21,12 +21,18 @@ set cpo&vim " This needs to be called outside of a function let s:script_folder_path = escape( expand( ':p:h' ), '\' ) -let s:omnifunc_mode = 0 - -let s:old_cursor_position = [] -let s:cursor_moved = 0 +let s:force_semantic = 0 +let s:default_completion = { + \ 'start_column': -1, + \ 'candidates': [] + \ } +let s:completion = s:default_completion let s:previous_allowed_buffer_number = 0 let s:pollers = { + \ 'completion': { + \ 'id': -1, + \ 'wait_milliseconds': 10 + \ }, \ 'file_parse_response': { \ 'id': -1, \ 'wait_milliseconds': 100 @@ -100,7 +106,6 @@ function! youcompleteme#Enable() autocmd BufEnter * call s:OnBufferEnter() autocmd BufUnload * call s:OnBufferUnload() autocmd InsertLeave * call s:OnInsertLeave() - autocmd InsertEnter * call s:OnInsertEnter() autocmd VimLeave * call s:OnVimLeave() autocmd CompleteDone * call s:OnCompleteDone() augroup END @@ -119,6 +124,7 @@ function! youcompleteme#EnableCursorMovedAutocommands() autocmd CursorMoved * call s:OnCursorMovedNormalMode() autocmd TextChanged * call s:OnTextChangedNormalMode() autocmd TextChangedI * call s:OnTextChangedInsertMode() + autocmd InsertCharPre * call s:OnInsertCharPre() augroup END endfunction @@ -221,15 +227,20 @@ function! s:SetUpKeyMappings() imap endif - " trigger omni completion, deselects the first completion - " candidate that vim selects by default - silent! exe 'inoremap ' . invoke_key . ' ' + silent! exe 'inoremap ' . invoke_key . + \ ' =InvokeSemanticCompletion()' endif if !empty( g:ycm_key_detailed_diagnostics ) silent! exe 'nnoremap ' . g:ycm_key_detailed_diagnostics . - \ ' :YcmShowDetailedDiagnostic' + \ ' :YcmShowDetailedDiagnostic' endif + + " Update completions when deleting a character in insert mode. + for key in [ "", "" ] + silent! exe 'inoremap ' . key . + \ ' OnDeleteChar( "\' . key . '" )' + endfor endfunction @@ -406,6 +417,11 @@ function! s:SetUpCompleteopt() endfunction +function! s:SetCompleteFunc() + let &completefunc = 'youcompleteme#CompleteFunc' +endfunction + + function! s:OnVimLeave() exec s:python_command "ycm_state.OnVimLeave()" endfunction @@ -423,7 +439,6 @@ function! s:OnFileTypeSet() call s:SetUpCompleteopt() call s:SetCompleteFunc() - call s:SetOmnicompleteFunc() exec s:python_command "ycm_state.OnBufferVisit()" call s:OnFileReadyToParse( 1 ) @@ -437,7 +452,6 @@ function! s:OnBufferEnter() call s:SetUpCompleteopt() call s:SetCompleteFunc() - call s:SetOmnicompleteFunc() exec s:python_command "ycm_state.OnBufferVisit()" " Last parse may be outdated because of changes from other buffers. Force a @@ -504,24 +518,10 @@ function! s:PollFileParseResponse( ... ) endfunction -function! s:SetCompleteFunc() - let &completefunc = 'youcompleteme#Complete' - let &l:completefunc = 'youcompleteme#Complete' -endfunction - - -function! s:SetOmnicompleteFunc() - if s:Pyeval( 'ycm_state.NativeFiletypeCompletionUsable()' ) - let &omnifunc = 'youcompleteme#OmniComplete' - let &l:omnifunc = 'youcompleteme#OmniComplete' - - " If we don't have native filetype support but the omnifunc is set to YCM's - " omnifunc because the previous file the user was editing DID have native - " support, we remove our omnifunc. - elseif &omnifunc == 'youcompleteme#OmniComplete' - let &omnifunc = '' - let &l:omnifunc = '' - endif +function! s:OnInsertCharPre() + " The TextChangedI event is not triggered when the completion menu is open. We + " solve this by closing the completion menu just before inserting a character. + call s:StopCompletion() endfunction @@ -548,24 +548,27 @@ function! s:OnTextChangedInsertMode() return endif + call s:IdentifierFinishedOperations() + + " We have to make sure we correctly leave semantic mode even when the user + " inserts something like a "operator[]" candidate string which fails + " CurrentIdentifierFinished check. + if s:force_semantic && !s:Pyeval( 'base.LastEnteredCharIsIdentifierChar()' ) + let s:force_semantic = 0 + endif + + " Immediately call previous completion to avoid flickers. + call s:Complete() + exec s:python_command "ycm_state.OnCursorMoved()" - call s:UpdateCursorMoved() - call s:IdentifierFinishedOperations() if g:ycm_autoclose_preview_window_after_completion call s:ClosePreviewWindowIfNeeded() endif - if g:ycm_auto_trigger || s:omnifunc_mode + if g:ycm_auto_trigger || s:force_semantic call s:InvokeCompletion() endif - - " We have to make sure we correctly leave omnifunc mode even when the user - " inserts something like a "operator[]" candidate string which fails - " CurrentIdentifierFinished check. - if s:omnifunc_mode && !s:Pyeval( 'base.LastEnteredCharIsIdentifierChar()') - let s:omnifunc_mode = 0 - endif endfunction @@ -574,7 +577,8 @@ function! s:OnInsertLeave() return endif - let s:omnifunc_mode = 0 + let s:force_semantic = 0 + let s:completion = s:default_completion call s:OnFileReadyToParse() exec s:python_command "ycm_state.OnInsertLeave()" if g:ycm_autoclose_preview_window_after_completion || @@ -584,22 +588,6 @@ function! s:OnInsertLeave() endfunction -function! s:OnInsertEnter() - if !s:AllowedToCompleteInCurrentBuffer() - return - endif - - let s:old_cursor_position = [] -endfunction - - -function! s:UpdateCursorMoved() - let current_position = getpos('.') - let s:cursor_moved = current_position != s:old_cursor_position - let s:old_cursor_position = current_position -endfunction - - function! s:ClosePreviewWindowIfNeeded() let current_buffer_name = bufname('') @@ -621,7 +609,8 @@ function! s:IdentifierFinishedOperations() return endif exec s:python_command "ycm_state.OnCurrentIdentifierFinished()" - let s:omnifunc_mode = 0 + let s:force_semantic = 0 + let s:completion = s:default_completion endfunction @@ -664,28 +653,68 @@ endfunction function! s:InvokeCompletion() - if &completefunc != "youcompleteme#Complete" + if s:InsideCommentOrStringAndShouldStop() || s:OnBlankLine() + call s:StopCompletion() return endif - if s:InsideCommentOrStringAndShouldStop() || s:OnBlankLine() - return + exec s:python_command "ycm_state.SendCompletionRequest(" . + \ "vimsupport.GetBoolValue( 's:force_semantic' ) )" + + call s:PollCompletion() +endfunction + + +function! s:InvokeSemanticCompletion() + let s:force_semantic = 1 + exec s:python_command "ycm_state.SendCompletionRequest( True )" + + call s:PollCompletion() + " Since this function is called in a mapping through the expression register + " =, its return value is inserted (see :h c_CTRL-R_=). + return '' +endfunction + + +function! s:StopCompletion() + call timer_stop( s:pollers.completion.id ) + if pumvisible() + call feedkeys( "\", 'n' ) endif +endfunction - " This is tricky. First, having 'refresh' set to 'always' in the dictionary - " that our completion function returns makes sure that our completion function - " is called on every keystroke. Second, when the sequence of characters the - " user typed produces no results in our search an infinite loop can occur. The - " problem is that our feedkeys call triggers the OnCursorMovedI event which we - " are tied to. We prevent this infinite loop from starting by making sure that - " the user has moved the cursor since the last time we provided completion - " results. - if !s:cursor_moved + +function! s:OnDeleteChar( char ) + " We can't use s:StopCompletion here as we don't want to throw away the + " completed text before deleting the character. + call timer_stop( s:pollers.completion.id ) + if pumvisible() + return "\" . a:char + endif + return a:char +endfunction + + +function! s:PollCompletion( ... ) + if !s:Pyeval( 'ycm_state.CompletionRequestReady()' ) + let s:pollers.completion.id = timer_start( + \ s:pollers.completion.wait_milliseconds, + \ function( 's:PollCompletion' ) ) return endif + let response = s:Pyeval( 'ycm_state.GetCompletionResponse()' ) + let s:completion = { + \ 'start_column': response.completion_start_column, + \ 'candidates': response.completions + \ } + call s:Complete() +endfunction + + +function! s:Complete() " invokes the user's completion function (which we have set to - " youcompleteme#Complete), and tells Vim to select the previous + " youcompleteme#CompleteFunc), and tells Vim to select the previous " completion candidate. This is necessary because by default, Vim selects the " first candidate when completion is invoked, and selecting a candidate " automatically replaces the current text with it. Calling forces Vim to @@ -695,43 +724,17 @@ function! s:InvokeCompletion() endfunction -" This is our main entry point. This is what vim calls to get completions. -function! youcompleteme#Complete( findstart, base ) - " After the user types one character after the call to the omnifunc, the - " completefunc will be called because of our mapping that calls the - " completefunc on every keystroke. Therefore we need to delegate the call we - " 'stole' back to the omnifunc - if s:omnifunc_mode - return youcompleteme#OmniComplete( a:findstart, a:base ) - endif - +function! youcompleteme#CompleteFunc( findstart, base ) if a:findstart - " InvokeCompletion has this check but we also need it here because of random - " Vim bugs and unfortunate interactions with the autocommands of other - " plugins - if !s:cursor_moved - " for vim, -2 means not found but don't trigger an error message - " see :h complete-functions + if s:completion.start_column > col( '.' ) || + \ empty( s:completion.candidates ) + " For vim, -2 means not found but don't trigger an error message. + " See :h complete-functions. return -2 endif - - exec s:python_command "ycm_state.CreateCompletionRequest()" - return s:Pyeval( 'base.CompletionStartColumn()' ) - else - return s:Pyeval( 'ycm_state.GetCompletions()' ) - endif -endfunction - - -function! youcompleteme#OmniComplete( findstart, base ) - if a:findstart - let s:omnifunc_mode = 1 - exec s:python_command "ycm_state.CreateCompletionRequest(" . - \ "force_semantic = True )" - return s:Pyeval( 'base.CompletionStartColumn()' ) - else - return s:Pyeval( 'ycm_state.GetCompletions()' ) + return s:completion.start_column - 1 endif + return s:completion.candidates endfunction diff --git a/python/ycm/base.py b/python/ycm/base.py index 4bfb36630f..7dd7eade51 100644 --- a/python/ycm/base.py +++ b/python/ycm/base.py @@ -25,7 +25,6 @@ from future.utils import iteritems from ycm import vimsupport from ycmd import user_options_store -from ycmd import request_wrap from ycmd import identifier_utils YCM_VAR_PREFIX = 'ycm_' @@ -57,13 +56,6 @@ def LoadJsonDefaultsIntoVim(): vimsupport.SetVariableValue( new_key, value ) -def CompletionStartColumn(): - return ( request_wrap.CompletionStartColumn( - vimsupport.CurrentLineContents(), - vimsupport.CurrentColumn() + 1, - vimsupport.CurrentFiletypes()[ 0 ] ) - 1 ) - - def CurrentIdentifierFinished(): current_column = vimsupport.CurrentColumn() previous_char_index = current_column - 1 diff --git a/python/ycm/client/completion_request.py b/python/ycm/client/completion_request.py index 76c969a0b1..1b5308239b 100644 --- a/python/ycm/client/completion_request.py +++ b/python/ycm/client/completion_request.py @@ -27,20 +27,18 @@ HandleServerException, MakeServerException ) -TIMEOUT_SECONDS = 0.5 - class CompletionRequest( BaseRequest ): def __init__( self, request_data ): super( CompletionRequest, self ).__init__() self.request_data = request_data self._response_future = None + self._response = { 'completions': [], 'completion_start_column': -1 } def Start( self ): self._response_future = self.PostDataToHandlerAsync( self.request_data, - 'completions', - TIMEOUT_SECONDS ) + 'completions' ) def Done( self ): @@ -49,21 +47,26 @@ def Done( self ): def RawResponse( self ): if not self._response_future: - return [] + return self._response + with HandleServerException( truncate = True ): - response = JsonFromFuture( self._response_future ) + self._response = JsonFromFuture( self._response_future ) - errors = response[ 'errors' ] if 'errors' in response else [] + # Vim may not be able to convert the 'errors' entry to its internal format + # so we remove it from the response. + errors = self._response.pop( 'errors', [] ) for e in errors: with HandleServerException( truncate = True ): raise MakeServerException( e ) - return response[ 'completions' ] - return [] + return self._response def Response( self ): - return _ConvertCompletionDatasToVimDatas( self.RawResponse() ) + response = self.RawResponse() + response[ 'completions' ] = _ConvertCompletionDatasToVimDatas( + response[ 'completions' ] ) + return response def ConvertCompletionDataToVimData( completion_data ): diff --git a/python/ycm/client/omni_completion_request.py b/python/ycm/client/omni_completion_request.py index 7fc76594c7..35b639cad1 100644 --- a/python/ycm/client/omni_completion_request.py +++ b/python/ycm/client/omni_completion_request.py @@ -40,11 +40,17 @@ def Done( self ): def RawResponse( self ): - return _ConvertVimDatasToCompletionDatas( self._results ) + return { + 'completions': _ConvertVimDatasToCompletionDatas( self._results ), + 'completion_start_column': self.request_data[ 'start_column' ] + } def Response( self ): - return self._results + return { + 'completions': self._results, + 'completion_start_column': self.request_data[ 'start_column' ] + } def ConvertVimDataToCompletionData( vim_data ): diff --git a/python/ycm/omni_completer.py b/python/ycm/omni_completer.py index 2587e088c6..1ea274b5bb 100644 --- a/python/ycm/omni_completer.py +++ b/python/ycm/omni_completer.py @@ -59,6 +59,8 @@ def ShouldUseNow( self, request_data ): def ShouldUseNowInner( self, request_data ): if not self._omnifunc: return False + if request_data.get( 'force_semantic', False ): + return True return super( OmniCompleter, self ).ShouldUseNowInner( request_data ) diff --git a/python/ycm/tests/client/omni_completion_request_tests.py b/python/ycm/tests/client/omni_completion_request_tests.py index 6b69962249..db2cf49873 100644 --- a/python/ycm/tests/client/omni_completion_request_tests.py +++ b/python/ycm/tests/client/omni_completion_request_tests.py @@ -29,11 +29,14 @@ from ycm.client.omni_completion_request import OmniCompletionRequest -def BuildOmnicompletionRequest( results ): +def BuildOmnicompletionRequest( results, start_column = 1 ): omni_completer = MagicMock() omni_completer.ComputeCandidates = MagicMock( return_value = results ) - request = OmniCompletionRequest( omni_completer, None ) + request_data = { + 'start_column': start_column + } + request = OmniCompletionRequest( omni_completer, request_data ) request.Start() return request @@ -49,7 +52,10 @@ def Response_FromOmniCompleter_test(): results = [ { "word": "test" } ] request = BuildOmnicompletionRequest( results ) - eq_( request.Response(), results ) + eq_( request.Response(), { + 'completions': results, + 'completion_start_column': 1 + } ) def RawResponse_ConvertedFromOmniCompleter_test(): @@ -73,7 +79,7 @@ def RawResponse_ConvertedFromOmniCompleter_test(): ] request = BuildOmnicompletionRequest( vim_results ) - results = request.RawResponse() + results = request.RawResponse()[ 'completions' ] eq_( len( results ), len( expected_results ) ) for result, expected_result in zip( results, expected_results ): diff --git a/python/ycm/tests/completion_test.py b/python/ycm/tests/completion_test.py index e16de3fa6a..831e034b73 100644 --- a/python/ycm/tests/completion_test.py +++ b/python/ycm/tests/completion_test.py @@ -28,111 +28,138 @@ MockVimModule, MockVimBuffers, VimBuffer ) MockVimModule() +import contextlib from hamcrest import assert_that, contains, empty, has_entries -from mock import call, patch +from mock import call, MagicMock, patch +from nose.tools import ok_ from ycm.tests import PathToTestFile, YouCompleteMeInstance from ycmd.responses import ServerError +@contextlib.contextmanager +def MockCompletionRequest( response_method ): + """Mock out the CompletionRequest, replacing the response handler + JsonFromFuture with the |response_method| parameter.""" + + # We don't want the event to actually be sent to the server, just have it + # return success. + with patch( 'ycm.client.completion_request.CompletionRequest.' + 'PostDataToHandlerAsync', + return_value = MagicMock( return_value=True ) ): + + # We set up a fake response (as called by CompletionRequest.RawResponse) + # which calls the supplied callback method. + # + # Note: JsonFromFuture is actually part of ycm.client.base_request, but we + # must patch where an object is looked up, not where it is defined. + # See https://docs.python.org/dev/library/unittest.mock.html#where-to-patch + # for details. + with patch( 'ycm.client.completion_request.JsonFromFuture', + side_effect = response_method ): + yield + + @YouCompleteMeInstance() -def CreateCompletionRequest_UnicodeWorkingDirectory_test( ycm ): +def SendCompletionRequest_UnicodeWorkingDirectory_test( ycm ): unicode_dir = PathToTestFile( 'uni¢𐍈d€' ) current_buffer = VimBuffer( PathToTestFile( 'uni¢𐍈d€', 'current_buffer' ) ) + def ServerResponse( *args ): + return { 'completions': [], 'completion_start_column': 1 } + with CurrentWorkingDirectory( unicode_dir ): with MockVimBuffers( [ current_buffer ], current_buffer ): - ycm.CreateCompletionRequest(), - - results = ycm.GetCompletions() - - assert_that( - results, - has_entries( { - 'words': empty(), - 'refresh': 'always' - } ) - ) + with MockCompletionRequest( ServerResponse ): + ycm.SendCompletionRequest() + ok_( ycm.CompletionRequestReady() ) + assert_that( + ycm.GetCompletionResponse(), + has_entries( { + 'completions': empty(), + 'completion_start_column': 1 + } ) + ) @YouCompleteMeInstance() @patch( 'ycm.client.base_request._logger', autospec = True ) @patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) -def CreateCompletionRequest_ResponseContainingError_test( ycm, - post_vim_message, - logger ): +def SendCompletionRequest_ResponseContainingError_test( ycm, + post_vim_message, + logger ): current_buffer = VimBuffer( 'buffer' ) + + def ServerResponse( *args ): + return { + 'completions': [ { + 'insertion_text': 'insertion_text', + 'menu_text': 'menu_text', + 'extra_menu_info': 'extra_menu_info', + 'detailed_info': 'detailed_info', + 'kind': 'kind', + 'extra_data': { + 'doc_string': 'doc_string' + } + } ], + 'completion_start_column': 3, + 'errors': [ { + 'exception': { + 'TYPE': 'Exception' + }, + 'message': 'message', + 'traceback': 'traceback' + } ] + } + with MockVimBuffers( [ current_buffer ], current_buffer ): - ycm.CreateCompletionRequest(), - - response = { - 'completions': [ { - 'insertion_text': 'insertion_text', - 'menu_text': 'menu_text', - 'extra_menu_info': 'extra_menu_info', - 'detailed_info': 'detailed_info', - 'kind': 'kind', - 'extra_data': { - 'doc_string': 'doc_string' - } - } ], - 'completion_start_column': 3, - 'errors': [ { - 'exception': { - 'TYPE': 'Exception' - }, - 'message': 'message', - 'traceback': 'traceback' - } ] - } - - with patch( 'ycm.client.completion_request.JsonFromFuture', - return_value = response ): - results = ycm.GetCompletions() - - logger.exception.assert_called_with( 'Error while handling server response' ) - post_vim_message.assert_has_exact_calls( [ - call( 'Exception: message', truncate = True ) - ] ) - assert_that( - results, - has_entries( { - 'words': contains( has_entries( { - 'word': 'insertion_text', - 'abbr': 'menu_text', - 'menu': 'extra_menu_info', - 'info': 'detailed_info\ndoc_string', - 'kind': 'k', - 'dup': 1, - 'empty': 1 - } ) ), - 'refresh': 'always' - } ) - ) + with MockCompletionRequest( ServerResponse ): + ycm.SendCompletionRequest() + ok_( ycm.CompletionRequestReady() ) + response = ycm.GetCompletionResponse() + logger.exception.assert_called_with( 'Error while handling server ' + 'response' ) + post_vim_message.assert_has_exact_calls( [ + call( 'Exception: message', truncate = True ) + ] ) + assert_that( + response, + has_entries( { + 'completions': contains( has_entries( { + 'word': 'insertion_text', + 'abbr': 'menu_text', + 'menu': 'extra_menu_info', + 'info': 'detailed_info\ndoc_string', + 'kind': 'k', + 'dup': 1, + 'empty': 1 + } ) ), + 'completion_start_column': 3 + } ) + ) @YouCompleteMeInstance() @patch( 'ycm.client.base_request._logger', autospec = True ) @patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock ) -def CreateCompletionRequest_ErrorFromServer_test( ycm, - post_vim_message, - logger ): +def SendCompletionRequest_ErrorFromServer_test( ycm, + post_vim_message, + logger ): current_buffer = VimBuffer( 'buffer' ) with MockVimBuffers( [ current_buffer ], current_buffer ): - ycm.CreateCompletionRequest(), - - with patch( 'ycm.client.completion_request.JsonFromFuture', - side_effect = ServerError( 'Server error' ) ): - results = ycm.GetCompletions() - - logger.exception.assert_called_with( 'Error while handling server response' ) - post_vim_message.assert_has_exact_calls( [ - call( 'Server error', truncate = True ) - ] ) - assert_that( - results, - has_entries( { - 'words': empty(), - 'refresh': 'always' - } ) - ) + with MockCompletionRequest( ServerError( 'Server error' ) ): + ycm.SendCompletionRequest() + ok_( ycm.CompletionRequestReady() ) + response = ycm.GetCompletionResponse() + logger.exception.assert_called_with( 'Error while handling server ' + 'response' ) + post_vim_message.assert_has_exact_calls( [ + call( 'Server error', truncate = True ) + ] ) + assert_that( + response, + has_entries( { + 'completions': empty(), + 'completion_start_column': -1 + } ) + ) diff --git a/python/ycm/tests/event_notification_test.py b/python/ycm/tests/event_notification_test.py index fa4e082a6d..1c68ce3dfd 100644 --- a/python/ycm/tests/event_notification_test.py +++ b/python/ycm/tests/event_notification_test.py @@ -86,15 +86,15 @@ def MockEventNotification( response_method, native_filetype_completer = True ): 'PostDataToHandlerAsync', return_value = MagicMock( return_value=True ) ): - # We set up a fake a Response (as called by EventNotification.Response) - # which calls the supplied callback method. Generally this callback just - # raises an apropriate exception, otherwise it would have to return a mock - # future object. + # We set up a fake response (as called by EventNotification.Response) which + # calls the supplied callback method. Generally this callback just raises an + # apropriate exception, otherwise it would have to return a mock future + # object. # # Note: JsonFromFuture is actually part of ycm.client.base_request, but we - # must patch where an object is looked up, not where it is defined. - # See https://docs.python.org/dev/library/unittest.mock.html#where-to-patch - # for details. + # must patch where an object is looked up, not where it is defined. See + # https://docs.python.org/dev/library/unittest.mock.html#where-to-patch for + # details. with patch( 'ycm.client.event_notification.JsonFromFuture', side_effect = response_method ): diff --git a/python/ycm/tests/test_utils.py b/python/ycm/tests/test_utils.py index 36ce2478cc..ff14fa1507 100644 --- a/python/ycm/tests/test_utils.py +++ b/python/ycm/tests/test_utils.py @@ -169,9 +169,6 @@ def _MockVimEval( value ): if value == 'tempname()': return '_TEMP_FILE_' - if value == 'complete_check()': - return 0 - if value == 'tagfiles()': return [ 'tags' ] diff --git a/python/ycm/youcompleteme.py b/python/ycm/youcompleteme.py index 254ce20078..0b6e3dd04c 100644 --- a/python/ycm/youcompleteme.py +++ b/python/ycm/youcompleteme.py @@ -275,37 +275,35 @@ def RestartServer( self ): self._SetupServer() - def CreateCompletionRequest( self, force_semantic = False ): + def SendCompletionRequest( self, force_semantic = False ): request_data = BuildRequestData() + request_data[ 'force_semantic' ] = force_semantic if ( not self.NativeFiletypeCompletionAvailable() and self.CurrentFiletypeCompletionEnabled() ): wrapped_request_data = RequestWrap( request_data ) if self._omnicomp.ShouldUseNow( wrapped_request_data ): self._latest_completion_request = OmniCompletionRequest( self._omnicomp, wrapped_request_data ) - return self._latest_completion_request + self._latest_completion_request.Start() + return request_data[ 'working_dir' ] = utils.GetCurrentDirectory() self._AddExtraConfDataIfNeeded( request_data ) - if force_semantic: - request_data[ 'force_semantic' ] = True self._latest_completion_request = CompletionRequest( request_data ) - return self._latest_completion_request + self._latest_completion_request.Start() + + def CompletionRequestReady( self ): + return bool( self._latest_completion_request and + self._latest_completion_request.Done() ) - def GetCompletions( self ): - request = self.GetCurrentCompletionRequest() - request.Start() - while not request.Done(): - try: - if vimsupport.GetBoolValue( 'complete_check()' ): - return { 'words' : [], 'refresh' : 'always' } - except KeyboardInterrupt: - return { 'words' : [], 'refresh' : 'always' } - results = base.AdjustCandidateInsertionText( request.Response() ) - return { 'words' : results, 'refresh' : 'always' } + def GetCompletionResponse( self ): + response = self._latest_completion_request.Response() + response[ 'completions' ] = base.AdjustCandidateInsertionText( + response[ 'completions' ] ) + return response def SendCommandRequest( self, arguments, completer ):