Fix applying fixit twice after completion

This commit is contained in:
micbou 2018-03-27 03:22:31 +02:00
parent c1815a9b2d
commit 3b115831a0
No known key found for this signature in database
GPG Key ID: C7E8FD1F3BDA1E05
2 changed files with 39 additions and 122 deletions

View File

@ -1,4 +1,4 @@
# Copyright (C) 2013 Google Inc. # Copyright (C) 2013-2018 YouCompleteMe contributors
# #
# This file is part of YouCompleteMe. # This file is part of YouCompleteMe.
# #
@ -93,34 +93,21 @@ class CompletionRequest( BaseRequest ):
def _GetCompletionsUserMayHaveCompleted( self ): def _GetCompletionsUserMayHaveCompleted( self ):
completed_item = vimsupport.GetVariableValue( 'v:completed_item' ) completed_item = vimsupport.GetVariableValue( 'v:completed_item' )
completions = self.RawResponse()[ 'completions' ]
if 'user_data' in completed_item and completed_item[ 'user_data' ]: # If Vim supports user_data (8.0.1493 or later), we actually know the
# Vim supports user_data (8.0.1493) or later, so we actually know the # _exact_ element that was selected, having put its index in the
# _exact_ element that was selected, having put its index in the # user_data field. Otherwise, we have to guess by matching the values in the
# user_data field. # completed item and the list of completions. Sometimes this returns
# multiple possibilities, which is essentially unresolvable.
if 'user_data' not in completed_item:
completions = self.RawResponse()[ 'completions' ]
return _FilterToMatchingCompletions( completed_item, completions )
if completed_item[ 'user_data' ]:
completions = self.RawResponse()[ 'completions' ]
return [ completions[ int( completed_item[ 'user_data' ] ) ] ] return [ completions[ int( completed_item[ 'user_data' ] ) ] ]
# Otherwise, we have to guess by matching the values in the completed item return []
# and the list of completions. Sometimes this returns multiple
# possibilities, which is essentially unresolvable.
result = _FilterToMatchingCompletions( completed_item, completions, True )
result = list( result )
if result:
return result
if _HasCompletionsThatCouldBeCompletedWithMoreText( completed_item,
completions ):
# Since the way that YCM works leads to CompleteDone called on every
# character, return blank if the completion might not be done. This won't
# match if the completion is ended with typing a non-keyword character.
return []
result = _FilterToMatchingCompletions( completed_item, completions, False )
return list( result )
def _OnCompleteDone_Csharp( self ): def _OnCompleteDone_Csharp( self ):
@ -178,13 +165,10 @@ def _GetFixItCompletion( completion ):
return completion[ 'extra_data' ][ 'fixits' ] return completion[ 'extra_data' ][ 'fixits' ]
def _FilterToMatchingCompletions( completed_item, def _FilterToMatchingCompletions( completed_item, completions ):
completions,
full_match_only ):
"""Filter to completions matching the item Vim said was completed""" """Filter to completions matching the item Vim said was completed"""
match_keys = ( [ 'word', 'abbr', 'menu', 'info' ] if full_match_only match_keys = [ 'word', 'abbr', 'menu', 'info' ]
else [ 'word' ] ) matched_completions = []
for index, completion in enumerate( completions ): for index, completion in enumerate( completions ):
item = _ConvertCompletionDataToVimData( index, completion ) item = _ConvertCompletionDataToVimData( index, completion )
@ -193,34 +177,8 @@ def _FilterToMatchingCompletions( completed_item,
ToUnicode( item.get( key, "" ) ) ) ToUnicode( item.get( key, "" ) ) )
if all( [ matcher( i ) for i in match_keys ] ): if all( [ matcher( i ) for i in match_keys ] ):
yield completion matched_completions.append( completion )
return matched_completions
def _HasCompletionsThatCouldBeCompletedWithMoreText( completed_item,
completions ):
if not completed_item:
return False
completed_word = ToUnicode( completed_item[ 'word' ] )
if not completed_word:
return False
# Sometimes CompleteDone is called after the next character is inserted.
# If so, use inserted character to filter possible completions further.
text = vimsupport.TextBeforeCursor()
reject_exact_match = True
if text and text[ -1 ] != completed_word[ -1 ]:
reject_exact_match = False
completed_word += text[ -1 ]
for index, completion in enumerate( completions ):
word = ToUnicode(
_ConvertCompletionDataToVimData( index, completion )[ 'word' ] )
if reject_exact_match and word == completed_word:
continue
if word.startswith( completed_word ):
return True
return False
def _ConvertCompletionDataToVimData( completion_identifier, completion_data ): def _ConvertCompletionDataToVimData( completion_identifier, completion_data ):

View File

@ -33,11 +33,9 @@ from nose.tools import eq_, ok_
from ycm import vimsupport from ycm import vimsupport
from ycmd.utils import ToBytes from ycmd.utils import ToBytes
from ycm.client.completion_request import ( from ycm.client.completion_request import ( CompletionRequest,
CompletionRequest, _FilterToMatchingCompletions,
_FilterToMatchingCompletions, _GetRequiredNamespaceImport )
_GetRequiredNamespaceImport,
_HasCompletionsThatCouldBeCompletedWithMoreText )
def CompleteItemIs( word, abbr = None, menu = None, def CompleteItemIs( word, abbr = None, menu = None,
@ -185,88 +183,36 @@ def OnCompleteDone_NoActionIfNotDone_test( *args ):
def FilterToCompletedCompletions_MatchIsReturned_test(): def FilterToCompletedCompletions_MatchIsReturned_test():
completions = [ BuildCompletion( insertion_text = 'Test' ) ] completions = [ BuildCompletion( insertion_text = 'Test' ) ]
result = _FilterToMatchingCompletions( CompleteItemIs( 'Test' ), result = _FilterToMatchingCompletions( CompleteItemIs( 'Test' ), completions )
completions,
False )
eq_( list( result ), completions ) eq_( list( result ), completions )
def FilterToCompletedCompletions_ShortTextDoesntRaise_test(): def FilterToCompletedCompletions_ShortTextDoesntRaise_test():
completions = [ BuildCompletion( insertion_text = 'AAA' ) ] completions = [ BuildCompletion( insertion_text = 'AAA' ) ]
result = _FilterToMatchingCompletions( CompleteItemIs( 'A' ), result = _FilterToMatchingCompletions( CompleteItemIs( 'A' ), completions )
completions,
False )
eq_( list( result ), [] ) eq_( list( result ), [] )
def FilterToCompletedCompletions_ExactMatchIsReturned_test(): def FilterToCompletedCompletions_ExactMatchIsReturned_test():
completions = [ BuildCompletion( insertion_text = 'Test' ) ] completions = [ BuildCompletion( insertion_text = 'Test' ) ]
result = _FilterToMatchingCompletions( CompleteItemIs( 'Test' ), result = _FilterToMatchingCompletions( CompleteItemIs( 'Test' ), completions )
completions,
False )
eq_( list( result ), completions ) eq_( list( result ), completions )
def FilterToCompletedCompletions_NonMatchIsntReturned_test(): def FilterToCompletedCompletions_NonMatchIsntReturned_test():
completions = [ BuildCompletion( insertion_text = 'A' ) ] completions = [ BuildCompletion( insertion_text = 'A' ) ]
result = _FilterToMatchingCompletions( CompleteItemIs( ' Quote' ), result = _FilterToMatchingCompletions( CompleteItemIs( ' Quote' ),
completions, completions )
False )
eq_( list( result ), [] ) eq_( list( result ), [] )
def FilterToCompletedCompletions_Unicode_test(): def FilterToCompletedCompletions_Unicode_test():
completions = [ BuildCompletion( insertion_text = '†es†' ) ] completions = [ BuildCompletion( insertion_text = '†es†' ) ]
result = _FilterToMatchingCompletions( CompleteItemIs( '†es†' ), result = _FilterToMatchingCompletions( CompleteItemIs( '†es†' ),
completions, completions )
False )
eq_( list( result ), completions ) eq_( list( result ), completions )
@patch( 'ycm.vimsupport.TextBeforeCursor', return_value = ' Quote' )
def HasCompletionsThatCouldBeCompletedWithMoreText_MatchIsReturned_test(
*args ):
completions = [ BuildCompletion( insertion_text = 'Test' ) ]
ok_( _HasCompletionsThatCouldBeCompletedWithMoreText( CompleteItemIs( 'Te' ),
completions ) )
@patch( 'ycm.vimsupport.TextBeforeCursor', return_value = ' Quote' )
def HasCompletionsThatCouldBeCompletedWithMoreText_ShortTextDoesntRaise_test(
*args ):
completions = [ BuildCompletion( insertion_text = 'AAA' ) ]
ok_( not _HasCompletionsThatCouldBeCompletedWithMoreText(
CompleteItemIs( 'X' ),
completions ) )
@patch( 'ycm.vimsupport.TextBeforeCursor', return_value = ' Quote' )
def HasCompletionsThatCouldBeCompletedWithMoreText_ExactMatchIsntReturned_test(
*args ):
completions = [ BuildCompletion( insertion_text = 'Test' ) ]
ok_( not _HasCompletionsThatCouldBeCompletedWithMoreText(
CompleteItemIs( 'Test' ),
completions ) )
@patch( 'ycm.vimsupport.TextBeforeCursor', return_value = ' Quote' )
def HasCompletionsThatCouldBeCompletedWithMoreText_NonMatchIsntReturned_test(
*args ):
completions = [ BuildCompletion( insertion_text = "A" ) ]
ok_( not _HasCompletionsThatCouldBeCompletedWithMoreText(
CompleteItemIs( ' Quote' ),
completions ) )
@patch( 'ycm.vimsupport.TextBeforeCursor', return_value = 'Uniç' )
def HasCompletionsThatCouldBeCompletedWithMoreText_Unicode_test(
*args ):
completions = [ BuildCompletion( insertion_text = 'Uniçø∂¢' ) ]
ok_( _HasCompletionsThatCouldBeCompletedWithMoreText(
CompleteItemIs( 'Uniç' ),
completions ) )
def GetRequiredNamespaceImport_ReturnNoneForNoExtraData_test(): def GetRequiredNamespaceImport_ReturnNoneForNoExtraData_test():
eq_( _GetRequiredNamespaceImport( {} ), None ) eq_( _GetRequiredNamespaceImport( {} ), None )
@ -351,6 +297,19 @@ def GetCompletionsUserMayHaveCompleted_UseUserData1_test( *args ):
[ BuildCompletionNamespace( 'namespace2' ) ] ) [ BuildCompletionNamespace( 'namespace2' ) ] )
@patch( 'ycm.vimsupport.GetVariableValue',
GetVariableValue_CompleteItemIs( 'Test', user_data='' ) )
def GetCompletionsUserMayHaveCompleted_EmptyUserData_test( *args ):
# Identical completions but none is selected.
completions = [
BuildCompletionNamespace( 'namespace1' ),
BuildCompletionNamespace( 'namespace2' )
]
with _SetupForCsharpCompletionDone( completions ) as request:
eq_( request._GetCompletionsUserMayHaveCompleted(), [] )
@patch( 'ycm.vimsupport.GetVariableValue', @patch( 'ycm.vimsupport.GetVariableValue',
GetVariableValue_CompleteItemIs( 'Test' ) ) GetVariableValue_CompleteItemIs( 'Test' ) )
def PostCompleteCsharp_EmptyDoesntInsertNamespace_test( *args ): def PostCompleteCsharp_EmptyDoesntInsertNamespace_test( *args ):