Auto merge of #2970 - micbou:duplicate-fixits, r=puremourning

[READY] Fix applying fixit twice after completion

Fixes the issue mentioned in PR https://github.com/Valloric/YouCompleteMe/pull/2963 for Vim 8.0.1493 and later. I don't think it's worth the trouble to fix the issue for older versions.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/valloric/youcompleteme/2970)
<!-- Reviewable:end -->
This commit is contained in:
zzbot 2018-04-12 14:37:20 -07:00 committed by GitHub
commit 45502e5108
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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.
#
@ -93,34 +93,21 @@ class CompletionRequest( BaseRequest ):
def _GetCompletionsUserMayHaveCompleted( self ):
completed_item = vimsupport.GetVariableValue( 'v:completed_item' )
completions = self.RawResponse()[ 'completions' ]
if 'user_data' in completed_item and completed_item[ 'user_data' ]:
# 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
# user_data field.
# If Vim supports user_data (8.0.1493 or later), we actually know 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
# 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' ] ) ] ]
# Otherwise, we have to guess by matching the values in the completed item
# 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 )
return []
def _OnCompleteDone_Csharp( self ):
@ -178,13 +165,10 @@ def _GetFixItCompletion( completion ):
return completion[ 'extra_data' ][ 'fixits' ]
def _FilterToMatchingCompletions( completed_item,
completions,
full_match_only ):
def _FilterToMatchingCompletions( completed_item, completions ):
"""Filter to completions matching the item Vim said was completed"""
match_keys = ( [ 'word', 'abbr', 'menu', 'info' ] if full_match_only
else [ 'word' ] )
match_keys = [ 'word', 'abbr', 'menu', 'info' ]
matched_completions = []
for index, completion in enumerate( completions ):
item = _ConvertCompletionDataToVimData( index, completion )
@ -193,34 +177,8 @@ def _FilterToMatchingCompletions( completed_item,
ToUnicode( item.get( key, "" ) ) )
if all( [ matcher( i ) for i in match_keys ] ):
yield completion
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
matched_completions.append( completion )
return matched_completions
def _ConvertCompletionDataToVimData( completion_identifier, completion_data ):

View File

@ -33,11 +33,9 @@ from nose.tools import eq_, ok_
from ycm import vimsupport
from ycmd.utils import ToBytes
from ycm.client.completion_request import (
CompletionRequest,
_FilterToMatchingCompletions,
_GetRequiredNamespaceImport,
_HasCompletionsThatCouldBeCompletedWithMoreText )
from ycm.client.completion_request import ( CompletionRequest,
_FilterToMatchingCompletions,
_GetRequiredNamespaceImport )
def CompleteItemIs( word, abbr = None, menu = None,
@ -185,88 +183,36 @@ def OnCompleteDone_NoActionIfNotDone_test( *args ):
def FilterToCompletedCompletions_MatchIsReturned_test():
completions = [ BuildCompletion( insertion_text = 'Test' ) ]
result = _FilterToMatchingCompletions( CompleteItemIs( 'Test' ),
completions,
False )
result = _FilterToMatchingCompletions( CompleteItemIs( 'Test' ), completions )
eq_( list( result ), completions )
def FilterToCompletedCompletions_ShortTextDoesntRaise_test():
completions = [ BuildCompletion( insertion_text = 'AAA' ) ]
result = _FilterToMatchingCompletions( CompleteItemIs( 'A' ),
completions,
False )
result = _FilterToMatchingCompletions( CompleteItemIs( 'A' ), completions )
eq_( list( result ), [] )
def FilterToCompletedCompletions_ExactMatchIsReturned_test():
completions = [ BuildCompletion( insertion_text = 'Test' ) ]
result = _FilterToMatchingCompletions( CompleteItemIs( 'Test' ),
completions,
False )
result = _FilterToMatchingCompletions( CompleteItemIs( 'Test' ), completions )
eq_( list( result ), completions )
def FilterToCompletedCompletions_NonMatchIsntReturned_test():
completions = [ BuildCompletion( insertion_text = 'A' ) ]
result = _FilterToMatchingCompletions( CompleteItemIs( ' Quote' ),
completions,
False )
completions )
eq_( list( result ), [] )
def FilterToCompletedCompletions_Unicode_test():
completions = [ BuildCompletion( insertion_text = '†es†' ) ]
result = _FilterToMatchingCompletions( CompleteItemIs( '†es†' ),
completions,
False )
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():
eq_( _GetRequiredNamespaceImport( {} ), None )
@ -351,6 +297,19 @@ def GetCompletionsUserMayHaveCompleted_UseUserData1_test( *args ):
[ 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',
GetVariableValue_CompleteItemIs( 'Test' ) )
def PostCompleteCsharp_EmptyDoesntInsertNamespace_test( *args ):