Candidate overlap with buffer text improved

Now, "foobar.h" will be changed to insert "foo" if the text after the cursor is
"bar.h". This already worked for "foobar" and "bar", but the overlap search
would stop before a non-word character. This has now been resolved.
This commit is contained in:
Strahinja Val Markovic 2014-01-05 11:48:05 -08:00
parent df161cff52
commit a18807d31e
2 changed files with 87 additions and 14 deletions

View File

@ -17,7 +17,6 @@
# You should have received a copy of the GNU General Public License
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
import re
import vim
from ycm import vimsupport
from ycm import utils
@ -124,18 +123,14 @@ def AdjustCandidateInsertionText( candidates ):
to implement and is probably not worth doing.
"""
def NewCandidateInsertionText( to_insert, word_after_cursor ):
if to_insert.endswith( word_after_cursor ):
return to_insert[ : - len( word_after_cursor ) ]
def NewCandidateInsertionText( to_insert, text_after_cursor ):
overlap_len = OverlapLength( to_insert, text_after_cursor )
if overlap_len:
return to_insert[ :-overlap_len ]
return to_insert
match = re.search( r'^(\w+)', vimsupport.TextAfterCursor() )
if not match:
return candidates
new_candidates = []
word_after_cursor = match.group( 1 )
text_after_cursor = vimsupport.TextAfterCursor()
for candidate in candidates:
if type( candidate ) is dict:
new_candidate = candidate.copy()
@ -145,17 +140,51 @@ def AdjustCandidateInsertionText( candidates ):
new_candidate[ 'word' ] = NewCandidateInsertionText(
new_candidate[ 'word' ],
word_after_cursor )
text_after_cursor )
new_candidates.append( new_candidate )
elif type( candidate ) is str:
new_candidates.append(
{ 'abbr': candidate,
'word': NewCandidateInsertionText( candidate, word_after_cursor ) } )
'word': NewCandidateInsertionText( candidate, text_after_cursor ) } )
return new_candidates
def OverlapLength( left_string, right_string ):
"""Returns the length of the overlap between two strings.
Example: "foo baro" and "baro zoo" -> 4
"""
left_string_length = len( left_string )
right_string_length = len( right_string )
if not left_string_length or not right_string_length:
return 0
# Truncate the longer string.
if left_string_length > right_string_length:
left_string = left_string[ -right_string_length: ]
elif left_string_length < right_string_length:
right_string = right_string[ :left_string_length ]
if left_string == right_string:
return min( left_string_length, right_string_length )
# Start by looking for a single character match
# and increase length until no match is found.
best = 0
length = 1
while True:
pattern = left_string[ -length: ]
found = right_string.find( pattern )
if found < 0:
return best
length += found
if left_string[ -length: ] == right_string[ :length ]:
best = length
length += 1
COMPATIBLE_WITH_CORE_VERSION = 7
def CompatibleWithYcmCore():

View File

@ -49,6 +49,16 @@ def AdjustCandidateInsertionText_WhitespaceInTextAfterCursor_test():
base.AdjustCandidateInsertionText( [ 'foobar' ] ) )
def AdjustCandidateInsertionText_MoreThanWordMatchingAfterCursor_test():
vimsupport.TextAfterCursor = MagicMock( return_value = 'bar.h' )
eq_( [ { 'abbr': 'foobar.h', 'word': 'foo' } ],
base.AdjustCandidateInsertionText( [ 'foobar.h' ] ) )
vimsupport.TextAfterCursor = MagicMock( return_value = 'bar(zoo' )
eq_( [ { 'abbr': 'foobar(zoo', 'word': 'foo' } ],
base.AdjustCandidateInsertionText( [ 'foobar(zoo' ] ) )
def AdjustCandidateInsertionText_NotSuffix_test():
vimsupport.TextAfterCursor = MagicMock( return_value = 'bar' )
eq_( [ { 'abbr': 'foofoo', 'word': 'foofoo' } ],
@ -57,8 +67,8 @@ def AdjustCandidateInsertionText_NotSuffix_test():
def AdjustCandidateInsertionText_NothingAfterCursor_test():
vimsupport.TextAfterCursor = MagicMock( return_value = '' )
eq_( [ 'foofoo',
'zobar' ],
eq_( [ { 'abbr': 'foofoo', 'word': 'foofoo' },
{ 'abbr': 'zobar', 'word': 'zobar' }, ],
base.AdjustCandidateInsertionText( [ 'foofoo',
'zobar' ] ) )
@ -88,3 +98,37 @@ def AdjustCandidateInsertionText_DontTouchAbbr_test():
eq_( [ { 'abbr': '1234', 'word': 'foo' } ],
base.AdjustCandidateInsertionText(
[ { 'abbr': '1234', 'word': 'foobar' } ] ) )
def OverlapLength_Basic_test():
eq_( 3, base.OverlapLength( 'foo bar', 'bar zoo' ) )
eq_( 3, base.OverlapLength( 'foobar', 'barzoo' ) )
def OverlapLength_OneCharOverlap_test():
eq_( 1, base.OverlapLength( 'foo b', 'b zoo' ) )
def OverlapLength_SameStrings_test():
eq_( 6, base.OverlapLength( 'foobar', 'foobar' ) )
def OverlapLength_Substring_test():
eq_( 6, base.OverlapLength( 'foobar', 'foobarzoo' ) )
eq_( 6, base.OverlapLength( 'zoofoobar', 'foobar' ) )
def OverlapLength_LongestOverlap_test():
eq_( 7, base.OverlapLength( 'bar foo foo', 'foo foo bar' ) )
def OverlapLength_EmptyInput_test():
eq_( 0, base.OverlapLength( '', 'goobar' ) )
eq_( 0, base.OverlapLength( 'foobar', '' ) )
eq_( 0, base.OverlapLength( '', '' ) )
def OverlapLength_NoOverlap_test():
eq_( 0, base.OverlapLength( 'foobar', 'goobar' ) )
eq_( 0, base.OverlapLength( 'foobar', '(^($@#$#@' ) )
eq_( 0, base.OverlapLength( 'foo bar zoo', 'foo zoo bar' ) )