Browse Source

fixup! Add CompleteDone hook, with namespace insertion for C#

Spencer G. Jones 9 years ago
parent
commit
9f568be39a

+ 12 - 0
README.md

@@ -1365,6 +1365,18 @@ Default: `0`
 
 
     let g:ycm_csharp_server_port = 0
     let g:ycm_csharp_server_port = 0
 
 
+### The `g:ycm_csharp_insert_namespace_expr` option
+
+When YCM inserts a namespace, by default, it will insert it under the nearest
+using statement. When this option is set, YCM will instead set the global
+variable `g:ycm_namespace_to_insert` to the namespace to insert, and then
+evaluate this option's value as an expression. The expression is responsible
+for inserting the namespace.
+
+Default: `''`
+
+    let g:ycm_csharp_insert_namespace_expr = ''
+
 ### The `g:ycm_add_preview_to_completeopt` option
 ### The `g:ycm_add_preview_to_completeopt` option
 
 
 When this option is set to `1`, YCM will add the `preview` string to Vim's
 When this option is set to `1`, YCM will add the `preview` string to Vim's

+ 1 - 3
autoload/youcompleteme.vim

@@ -84,9 +84,7 @@ function! youcompleteme#Enable()
     autocmd InsertLeave * call s:OnInsertLeave()
     autocmd InsertLeave * call s:OnInsertLeave()
     autocmd InsertEnter * call s:OnInsertEnter()
     autocmd InsertEnter * call s:OnInsertEnter()
     autocmd VimLeave * call s:OnVimLeave()
     autocmd VimLeave * call s:OnVimLeave()
-    if pyeval( 'vimsupport.VimVersionAtLeast("7.3.598")' )
-      autocmd CompleteDone * call s:OnCompleteDone()
-    endif
+    autocmd CompleteDone * call s:OnCompleteDone()
   augroup END
   augroup END
 
 
   " Calling these once solves the problem of BufReadPre/BufRead/BufEnter not
   " Calling these once solves the problem of BufReadPre/BufRead/BufEnter not

+ 31 - 17
doc/youcompleteme.txt

@@ -73,23 +73,24 @@ Contents ~
   26. The |g:ycm_auto_start_csharp_server| option
   26. The |g:ycm_auto_start_csharp_server| option
   27. The |g:ycm_auto_stop_csharp_server| option
   27. The |g:ycm_auto_stop_csharp_server| option
   28. The |g:ycm_csharp_server_port| option
   28. The |g:ycm_csharp_server_port| option
-  29. The |g:ycm_add_preview_to_completeopt| option
-  30. The |g:ycm_autoclose_preview_window_after_completion| option
-  31. The |g:ycm_autoclose_preview_window_after_insertion| option
-  32. The |g:ycm_max_diagnostics_to_display| option
-  33. The |g:ycm_key_list_select_completion| option
-  34. The |g:ycm_key_list_previous_completion| option
-  35. The |g:ycm_key_invoke_completion| option
-  36. The |g:ycm_key_detailed_diagnostics| option
-  37. The |g:ycm_global_ycm_extra_conf| option
-  38. The |g:ycm_confirm_extra_conf| option
-  39. The |g:ycm_extra_conf_globlist| option
-  40. The |g:ycm_filepath_completion_use_working_dir| option
-  41. The |g:ycm_semantic_triggers| option
-  42. The |g:ycm_cache_omnifunc| option
-  43. The |g:ycm_use_ultisnips_completer| option
-  44. The |g:ycm_goto_buffer_command| option
-  45. The |g:ycm_disable_for_files_larger_than_kb| option
+  29. The |g:ycm_csharp_insert_namespace_expr| option
+  30. The |g:ycm_add_preview_to_completeopt| option
+  31. The |g:ycm_autoclose_preview_window_after_completion| option
+  32. The |g:ycm_autoclose_preview_window_after_insertion| option
+  33. The |g:ycm_max_diagnostics_to_display| option
+  34. The |g:ycm_key_list_select_completion| option
+  35. The |g:ycm_key_list_previous_completion| option
+  36. The |g:ycm_key_invoke_completion| option
+  37. The |g:ycm_key_detailed_diagnostics| option
+  38. The |g:ycm_global_ycm_extra_conf| option
+  39. The |g:ycm_confirm_extra_conf| option
+  40. The |g:ycm_extra_conf_globlist| option
+  41. The |g:ycm_filepath_completion_use_working_dir| option
+  42. The |g:ycm_semantic_triggers| option
+  43. The |g:ycm_cache_omnifunc| option
+  44. The |g:ycm_use_ultisnips_completer| option
+  45. The |g:ycm_goto_buffer_command| option
+  46. The |g:ycm_disable_for_files_larger_than_kb| option
  8. FAQ                                                     |youcompleteme-faq|
  8. FAQ                                                     |youcompleteme-faq|
   1. I used to be able to 'import vim' in '.ycm_extra_conf.py', but now can't |import-vim|
   1. I used to be able to 'import vim' in '.ycm_extra_conf.py', but now can't |import-vim|
   2. On very rare occasions Vim crashes when I tab through the completion menu |youcompleteme-on-very-rare-occasions-vim-crashes-when-i-tab-through-completion-menu|
   2. On very rare occasions Vim crashes when I tab through the completion menu |youcompleteme-on-very-rare-occasions-vim-crashes-when-i-tab-through-completion-menu|
@@ -1581,6 +1582,19 @@ Default: '0'
   let g:ycm_csharp_server_port = 0
   let g:ycm_csharp_server_port = 0
 <
 <
 -------------------------------------------------------------------------------
 -------------------------------------------------------------------------------
+The *g:ycm_csharp_insert_namespace_expr* option
+
+When YCM inserts a namespace, by default, it will insert it under the nearest
+using statement. When this option is set, YCM will instead set the global
+variable 'g:ycm_namespace_to_insert' to the namespace to insert, and then
+evaluate this option's value as an expression. The expression is responsible
+for inserting the namespace.
+
+Default: "''"
+>
+  let g:ycm_csharp_insert_namespace_expr = ''
+<
+-------------------------------------------------------------------------------
 The *g:ycm_add_preview_to_completeopt* option
 The *g:ycm_add_preview_to_completeopt* option
 
 
 When this option is set to '1', YCM will add the 'preview' string to Vim's
 When this option is set to '1', YCM will add the 'preview' string to Vim's

+ 2 - 2
plugin/youcompleteme.vim

@@ -27,9 +27,9 @@ endfunction
 if exists( "g:loaded_youcompleteme" )
 if exists( "g:loaded_youcompleteme" )
   call s:restore_cpo()
   call s:restore_cpo()
   finish
   finish
-elseif v:version < 703 || (v:version == 703 && !has('patch584'))
+elseif v:version < 703 || (v:version == 703 && !has('patch598'))
   echohl WarningMsg |
   echohl WarningMsg |
-        \ echomsg "YouCompleteMe unavailable: requires Vim 7.3.584+" |
+        \ echomsg "YouCompleteMe unavailable: requires Vim 7.3.598+" |
         \ echohl None
         \ echohl None
   call s:restore_cpo()
   call s:restore_cpo()
   finish
   finish

+ 117 - 37
python/ycm/tests/postcomplete_tests.py

@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 #
 #
-# Copyright (C) 2013  Google Inc.
+# Copyright (C) 2015 YouCompleteMe contributors
 #
 #
 # This file is part of YouCompleteMe.
 # This file is part of YouCompleteMe.
 #
 #
@@ -23,113 +23,193 @@ from hamcrest import assert_that, empty
 from ycm import vimsupport
 from ycm import vimsupport
 from ycm.youcompleteme import YouCompleteMe
 from ycm.youcompleteme import YouCompleteMe
 
 
-def HasPostCompletionAction_TrueOnCsharp_test():
+def GetCompleteDoneHooks_ResultOnCsharp_test():
   vimsupport.CurrentFiletypes = MagicMock( return_value = [ "cs" ] )
   vimsupport.CurrentFiletypes = MagicMock( return_value = [ "cs" ] )
   ycm_state = YouCompleteMe( MagicMock( spec_set = dict ) )
   ycm_state = YouCompleteMe( MagicMock( spec_set = dict ) )
-  eq_( True, ycm_state.HasPostCompletionAction() )
+  result = ycm_state.GetCompleteDoneHooks()
+  eq_( 1, len( list( result ) ) )
 
 
 
 
-def HasPostCompletionAction_FalseOnOtherFiletype_test():
+def GetCompleteDoneHooks_EmptyOnOtherFiletype_test():
   vimsupport.CurrentFiletypes = MagicMock( return_value = [ "txt" ] )
   vimsupport.CurrentFiletypes = MagicMock( return_value = [ "txt" ] )
   ycm_state = YouCompleteMe( MagicMock( spec_set = dict ) )
   ycm_state = YouCompleteMe( MagicMock( spec_set = dict ) )
-  eq_( False, ycm_state.HasPostCompletionAction() )
+  result = ycm_state.GetCompleteDoneHooks()
+  eq_( 0, len( list( result ) ) )
 
 
 
 
-def GetRequiredNamespaceImport_ReturnEmptyForNoExtraData_test():
+def OnCompleteDone_WithActionCallsIt_test():
+  vimsupport.CurrentFiletypes = MagicMock( return_value = [ "txt" ] )
   ycm_state = YouCompleteMe( MagicMock( spec_set = dict ) )
   ycm_state = YouCompleteMe( MagicMock( spec_set = dict ) )
+  action = MagicMock()
+  ycm_state._complete_done_hooks[ "txt" ] = action
+  ycm_state.OnCompleteDone()
 
 
-  eq_( "", ycm_state.GetRequiredNamespaceImport( {} ) )
+  assert action.called
 
 
 
 
-def GetRequiredNamespaceImport_ReturnNamespaceFromExtraData_test():
-  namespace = "A_NAMESPACE"
+def OnCompleteDone_NoActionNoError_test():
+  vimsupport.CurrentFiletypes = MagicMock( return_value = [ "txt" ] )
   ycm_state = YouCompleteMe( MagicMock( spec_set = dict ) )
   ycm_state = YouCompleteMe( MagicMock( spec_set = dict ) )
 
 
-  eq_( namespace, ycm_state.GetRequiredNamespaceImport(
-    _BuildCompletion( namespace )
-  ))
+  ycm_state.OnCompleteDone()
 
 
 
 
-def FilterMatchingCompletions_MatchIsReturned_test():
+def FilterToCompletionsMatchingOnCursor_MatchIsReturned_test():
   ycm_state = YouCompleteMe( MagicMock( spec_set = dict ) )
   ycm_state = YouCompleteMe( MagicMock( spec_set = dict ) )
   vimsupport.TextBeforeCursor = MagicMock( return_value = "   Test" )
   vimsupport.TextBeforeCursor = MagicMock( return_value = "   Test" )
-  completions = [ _BuildCompletion( "A" ) ]
+  completions = [ _BuildCompletion( "Test" ) ]
 
 
-  result = ycm_state.FilterMatchingCompletions( completions )
+  result = ycm_state.FilterToCompletionsMatchingOnCursor( completions )
 
 
   eq_( list( result ), completions )
   eq_( list( result ), completions )
 
 
 
 
-def FilterMatchingCompletions_ShortTextDoesntRaise_test():
+def FilterToCompletionsMatchingOnCursor_ShortTextDoesntRaise_test():
   ycm_state = YouCompleteMe( MagicMock( spec_set = dict ) )
   ycm_state = YouCompleteMe( MagicMock( spec_set = dict ) )
   vimsupport.TextBeforeCursor = MagicMock( return_value = "X" )
   vimsupport.TextBeforeCursor = MagicMock( return_value = "X" )
-  completions = [ _BuildCompletion( "A" ) ]
+  completions = [ _BuildCompletion( "AAA" ) ]
 
 
-  ycm_state.FilterMatchingCompletions( completions )
+  ycm_state.FilterToCompletionsMatchingOnCursor( completions )
 
 
 
 
-def FilterMatchingCompletions_ExactMatchIsReturned_test():
+def FilterToCompletionsMatchingOnCursor_ExactMatchIsReturned_test():
   ycm_state = YouCompleteMe( MagicMock( spec_set = dict ) )
   ycm_state = YouCompleteMe( MagicMock( spec_set = dict ) )
   vimsupport.TextBeforeCursor = MagicMock( return_value = "Test" )
   vimsupport.TextBeforeCursor = MagicMock( return_value = "Test" )
-  completions = [ _BuildCompletion( "A" ) ]
+  completions = [ _BuildCompletion( "Test" ) ]
 
 
-  result = ycm_state.FilterMatchingCompletions( completions )
+  result = ycm_state.FilterToCompletionsMatchingOnCursor( completions )
 
 
   eq_( list( result ), completions )
   eq_( list( result ), completions )
 
 
 
 
-def FilterMatchingCompletions_NonMatchIsntReturned_test():
+def FilterToCompletionsMatchingOnCursor_NonMatchIsntReturned_test():
   ycm_state = YouCompleteMe( MagicMock( spec_set = dict ) )
   ycm_state = YouCompleteMe( MagicMock( spec_set = dict ) )
   vimsupport.TextBeforeCursor = MagicMock( return_value = "   Quote" )
   vimsupport.TextBeforeCursor = MagicMock( return_value = "   Quote" )
   completions = [ _BuildCompletion( "A" ) ]
   completions = [ _BuildCompletion( "A" ) ]
 
 
-  result = ycm_state.FilterMatchingCompletions( completions )
+  result = ycm_state.FilterToCompletionsMatchingOnCursor( completions )
 
 
   assert_that( list( result ), empty() )
   assert_that( list( result ), empty() )
 
 
 
 
-def PostComplete_EmptyDoesntInsertNamespace_test():
-  ycm_state = _SetupForCompletionDone( [] )
+def HasCompletionsThatCouldMatchOnCursorWithMoreText_MatchIsReturned_test():
+  ycm_state = YouCompleteMe( MagicMock( spec_set = dict ) )
+  vimsupport.TextBeforeCursor = MagicMock( return_value = "   Te" )
+  completions = [ _BuildCompletion( "Test" ) ]
+
+  result = ycm_state.HasCompletionsThatCouldMatchOnCursorWithMoreText( completions )
 
 
-  ycm_state.OnCompleteDone()
+  eq_( result, True )
+
+
+def HasCompletionsThatCouldMatchOnCursorWithMoreText_ShortTextDoesntRaise_test():
+  ycm_state = YouCompleteMe( MagicMock( spec_set = dict ) )
+  vimsupport.TextBeforeCursor = MagicMock( return_value = "X" )
+  completions = [ _BuildCompletion( "AAA" ) ]
+
+  ycm_state.HasCompletionsThatCouldMatchOnCursorWithMoreText( completions )
+
+
+def HasCompletionsThatCouldMatchOnCursorWithMoreText_ExactMatchIsntReturned_test():
+  ycm_state = YouCompleteMe( MagicMock( spec_set = dict ) )
+  vimsupport.TextBeforeCursor = MagicMock( return_value = "Test" )
+  completions = [ _BuildCompletion( "Test" ) ]
+
+  result = ycm_state.HasCompletionsThatCouldMatchOnCursorWithMoreText( completions )
+
+  eq_( result, False )
+
+
+def HasCompletionsThatCouldMatchOnCursorWithMoreText_NonMatchIsntReturned_test():
+  ycm_state = YouCompleteMe( MagicMock( spec_set = dict ) )
+  vimsupport.TextBeforeCursor = MagicMock( return_value = "   Quote" )
+  completions = [ _BuildCompletion( "A" ) ]
+
+  result = ycm_state.HasCompletionsThatCouldMatchOnCursorWithMoreText( completions )
+
+  eq_( result, False )
+
+
+def GetRequiredNamespaceImport_ReturnNoneForNoExtraData_test():
+  ycm_state = YouCompleteMe( MagicMock( spec_set = dict ) )
+
+  eq_( None, ycm_state.GetRequiredNamespaceImport( {} ) )
+
+
+def GetRequiredNamespaceImport_ReturnNamespaceFromExtraData_test():
+  namespace = "A_NAMESPACE"
+  ycm_state = YouCompleteMe( MagicMock( spec_set = dict ) )
+
+  eq_( namespace, ycm_state.GetRequiredNamespaceImport(
+    _BuildCompletion( namespace )
+  ))
+
+
+def GetMatchingCompletionsOnCursor_ReturnEmptyIfNotDone_test():
+  ycm_state = _SetupForCsharpCompletionDone( [] )
+  ycm_state._latest_completion_request.Done = MagicMock( return_value = False )
+
+  eq_( [], ycm_state.GetMatchingCompletionsOnCursor() )
+  
+
+def GetMatchingCompletionsOnCursor_ReturnEmptyIfPendingMatches_test():
+  completions = [ _BuildCompletion( None ) ]
+  ycm_state = _SetupForCsharpCompletionDone( completions )
+  vimsupport.TextBeforeCursor = MagicMock( return_value = "   Te" )
+
+  eq_( [], ycm_state.GetMatchingCompletionsOnCursor() )
+
+
+def GetMatchingCompletionsOnCursor_ReturnMatchIfMatches_test():
+  completions = [ _BuildCompletion( None ) ]
+  ycm_state = _SetupForCsharpCompletionDone( completions )
+  vimsupport.TextBeforeCursor = MagicMock( return_value = "   Test" )
+
+  eq_( completions, ycm_state.GetMatchingCompletionsOnCursor() )
+
+
+def PostCompleteCsharp_EmptyDoesntInsertNamespace_test():
+  ycm_state = _SetupForCsharpCompletionDone( [] )
+
+  ycm_state.OnCompleteDone_Csharp()
 
 
   assert not vimsupport.InsertNamespace.called
   assert not vimsupport.InsertNamespace.called
 
 
-def PostComplete_ExistingWithoutNamespaceDoesntInsertNamespace_test():
+
+def PostCompleteCsharp_ExistingWithoutNamespaceDoesntInsertNamespace_test():
   completions = [ _BuildCompletion( None ) ]
   completions = [ _BuildCompletion( None ) ]
-  ycm_state = _SetupForCompletionDone( completions )
+  ycm_state = _SetupForCsharpCompletionDone( completions )
 
 
-  ycm_state.OnCompleteDone()
+  ycm_state.OnCompleteDone_Csharp()
 
 
   assert not vimsupport.InsertNamespace.called
   assert not vimsupport.InsertNamespace.called
 
 
 
 
-def PostComplete_ValueDoesInsertNamespace_test():
+def PostCompleteCsharp_ValueDoesInsertNamespace_test():
   namespace = "A_NAMESPACE"
   namespace = "A_NAMESPACE"
   completions = [ _BuildCompletion( namespace ) ]
   completions = [ _BuildCompletion( namespace ) ]
-  ycm_state = _SetupForCompletionDone( completions )
+  ycm_state = _SetupForCsharpCompletionDone( completions )
 
 
-  ycm_state.OnCompleteDone()
+  ycm_state.OnCompleteDone_Csharp()
 
 
   vimsupport.InsertNamespace.assert_called_once_with( namespace )
   vimsupport.InsertNamespace.assert_called_once_with( namespace )
 
 
-def PostComplete_InsertSecondNamespaceIfSelected_test():
+def PostCompleteCsharp_InsertSecondNamespaceIfSelected_test():
   namespace = "A_NAMESPACE"
   namespace = "A_NAMESPACE"
   namespace2 = "ANOTHER_NAMESPACE"
   namespace2 = "ANOTHER_NAMESPACE"
   completions = [
   completions = [
     _BuildCompletion( namespace ),
     _BuildCompletion( namespace ),
     _BuildCompletion( namespace2 ),
     _BuildCompletion( namespace2 ),
   ]
   ]
-  ycm_state = _SetupForCompletionDone( completions )
+  ycm_state = _SetupForCsharpCompletionDone( completions )
   vimsupport.PresentDialog = MagicMock( return_value = 1 )
   vimsupport.PresentDialog = MagicMock( return_value = 1 )
 
 
-  ycm_state.OnCompleteDone()
+  ycm_state.OnCompleteDone_Csharp()
 
 
   vimsupport.InsertNamespace.assert_called_once_with( namespace2 )
   vimsupport.InsertNamespace.assert_called_once_with( namespace2 )
 
 
 
 
-def _SetupForCompletionDone( completions ):
-  vimsupport.CurrentFiletypes = MagicMock( return_value = [ "cs" ] )
+def _SetupForCsharpCompletionDone( completions ):
   ycm_state = YouCompleteMe( MagicMock( spec_set = dict ) )
   ycm_state = YouCompleteMe( MagicMock( spec_set = dict ) )
   request = MagicMock();
   request = MagicMock();
   request.Done = MagicMock( return_value = True )
   request.Done = MagicMock( return_value = True )

+ 15 - 13
python/ycm/vimsupport.py

@@ -521,19 +521,21 @@ def ReplaceChunk( start, end, replacement_text, line_delta, char_delta,
 
 
 
 
 def InsertNamespace( namespace ):
 def InsertNamespace( namespace ):
-  if VariableExists( 'g:ycm_cs_insert_namespace_function' ):
-    function = GetVariableValue( 'g:ycm_cs_insert_namespace_function' )
-    SetVariableValue( "g:ycm_namespace", namespace )
-    vim.eval( function )
-  else:
-    pattern = '^\s*using\(\s\+[a-zA-Z0-9]\+\s\+=\)\?\s\+[a-zA-Z0-9.]\+\s*;\s*'
-    line = SearchInCurrentBuffer( pattern )
-    existing_line = LineTextInCurrentBuffer( line )
-    existing_indent = re.sub( r"\S.*", "", existing_line )
-    new_line = "{0}using {1};\n\n".format( existing_indent, namespace )
-    replace_pos = { 'line_num': line + 1, 'column_num': 1 }
-    ReplaceChunk( replace_pos, replace_pos, new_line, 0, 0 )
-    PostVimMessage( "Add namespace: {0}".format( namespace ) )
+  if VariableExists( 'g:ycm_csharp_insert_namespace_expr' ):
+    expr = GetVariableValue( 'g:ycm_csharp_insert_namespace_expr' )
+    if expr:
+      SetVariableValue( "g:ycm_namespace_to_insert", namespace )
+      vim.eval( expr )
+      return
+
+  pattern = '^\s*using\(\s\+[a-zA-Z0-9]\+\s\+=\)\?\s\+[a-zA-Z0-9.]\+\s*;\s*'
+  line = SearchInCurrentBuffer( pattern )
+  existing_line = LineTextInCurrentBuffer( line )
+  existing_indent = re.sub( r"\S.*", "", existing_line )
+  new_line = "{0}using {1};\n\n".format( existing_indent, namespace )
+  replace_pos = { 'line_num': line + 1, 'column_num': 1 }
+  ReplaceChunk( replace_pos, replace_pos, new_line, 0, 0 )
+  PostVimMessage( "Add namespace: {0}".format( namespace ) )
 
 
 
 
 def SearchInCurrentBuffer( pattern ):
 def SearchInCurrentBuffer( pattern ):

+ 53 - 25
python/ycm/youcompleteme.py

@@ -21,6 +21,7 @@ import os
 import vim
 import vim
 import tempfile
 import tempfile
 import json
 import json
+import re
 import signal
 import signal
 import base64
 import base64
 from subprocess import PIPE
 from subprocess import PIPE
@@ -96,6 +97,9 @@ class YouCompleteMe( object ):
     self._ycmd_keepalive = YcmdKeepalive()
     self._ycmd_keepalive = YcmdKeepalive()
     self._SetupServer()
     self._SetupServer()
     self._ycmd_keepalive.Start()
     self._ycmd_keepalive.Start()
+    self._complete_done_hooks = {
+      'cs': lambda( self ): self.OnCompleteDone_Csharp()
+    }
 
 
   def _SetupServer( self ):
   def _SetupServer( self ):
     self._available_completers = {}
     self._available_completers = {}
@@ -293,18 +297,58 @@ class YouCompleteMe( object ):
 
 
 
 
   def OnCompleteDone( self ):
   def OnCompleteDone( self ):
-    if not self.HasPostCompletionAction():
-      return
+    complete_done_actions = self.GetCompleteDoneHooks()
+    for action in complete_done_actions:
+      action(self)
+
+
+  def GetCompleteDoneHooks( self ):
+    filetypes = vimsupport.CurrentFiletypes()
+    for key, value in self._complete_done_hooks.iteritems():
+      if key in filetypes:
+        yield value
 
 
+
+  def GetMatchingCompletionsOnCursor( self ):
     latest_completion_request = self.GetCurrentCompletionRequest()
     latest_completion_request = self.GetCurrentCompletionRequest()
     if not latest_completion_request.Done():
     if not latest_completion_request.Done():
-      return
+      return []
 
 
     completions = latest_completion_request.RawResponse()
     completions = latest_completion_request.RawResponse()
-    completions = list( self.FilterMatchingCompletions( completions ) )
-    if not completions:
-      return
+    if self.HasCompletionsThatCouldMatchOnCursorWithMoreText( 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 = self.FilterToCompletionsMatchingOnCursor( completions )
+
+    return list( result )
+
+
+  def FilterToCompletionsMatchingOnCursor( self, completions ):
+    text = vimsupport.TextBeforeCursor() # No support for multiple line completions
+    for completion in completions:
+      word = completion[ "insertion_text" ]
+      # Trim complete-ending character if needed
+      text = re.sub( r"[^a-zA-Z0-9_]$", "", text )
+      buffer_text = text[ -1 * len( word ) : ]
+      if buffer_text == word:
+        yield completion
+
+
+  def HasCompletionsThatCouldMatchOnCursorWithMoreText( self, completions ):
+    text = vimsupport.TextBeforeCursor() # No support for multiple line completions
+    for completion in completions:
+      word = completion[ "insertion_text" ]
+      for i in range( 1, len( word ) - 1 ): # Excluding full word
+        if text[ -1 * i  : ] == word[ : i ]:
+          return True
+    return False
 
 
+
+  def OnCompleteDone_Csharp( self ):
+    completions = self.GetMatchingCompletionsOnCursor()
     namespaces = [ self.GetRequiredNamespaceImport( c )
     namespaces = [ self.GetRequiredNamespaceImport( c )
                    for c in completions ]
                    for c in completions ]
     namespaces = [ n for n in namespaces if n ]
     namespaces = [ n for n in namespaces if n ]
@@ -312,10 +356,9 @@ class YouCompleteMe( object ):
       return
       return
 
 
     if len( namespaces ) > 1:
     if len( namespaces ) > 1:
-      choices = [ "{0}: {1}".format( i + 1, n )
+      choices = [ "{0} {1}".format( i + 1, n )
                   for i,n in enumerate( namespaces ) ]
                   for i,n in enumerate( namespaces ) ]
-      choice = vimsupport.PresentDialog(
-        "Insert which namespace:", choices )
+      choice = vimsupport.PresentDialog( "Insert which namespace:", choices )
       if choice < 0:
       if choice < 0:
         return
         return
       namespace = namespaces[ choice ]
       namespace = namespaces[ choice ]
@@ -325,25 +368,10 @@ class YouCompleteMe( object ):
     vimsupport.InsertNamespace( namespace )
     vimsupport.InsertNamespace( namespace )
 
 
 
 
-  def HasPostCompletionAction( self ):
-    filetype = vimsupport.CurrentFiletypes()[ 0 ]
-    return filetype == 'cs'
-
-
-  def FilterMatchingCompletions( self, completions ):
-    text = vimsupport.TextBeforeCursor() # No support for multiple line completions
-    for completion in completions:
-      word = completion[ "insertion_text" ]
-      for i in [ None, -1 ]:
-        if text[ -1 * len( word ) + ( i or 0 ) : i ] == word:
-          yield completion
-          break
-
-
   def GetRequiredNamespaceImport( self, completion ):
   def GetRequiredNamespaceImport( self, completion ):
     if ( "extra_data" not in completion
     if ( "extra_data" not in completion
          or "required_namespace_import" not in completion[ "extra_data" ] ):
          or "required_namespace_import" not in completion[ "extra_data" ] ):
-      return ""
+      return None
     return completion[ "extra_data" ][ "required_namespace_import" ]
     return completion[ "extra_data" ][ "required_namespace_import" ]