From 4adf469e0f0f98f1976054969fbc0c853e763e4f Mon Sep 17 00:00:00 2001
From: Maxim Kim <habamax@gmail.com>
Date: Fri, 9 Oct 2020 11:20:03 +0300
Subject: [PATCH] Allow completion on a buffer without filetype

Introduce "fake" filetype `ycm_nofiletype` that is used to handle all
buffers without filetype.
---
 autoload/youcompleteme.vim    |  5 ++-
 python/ycm/vimsupport.py      | 10 ++++-
 test/completion.common.vim    | 70 +++++++++++++++++++++++++++++++++++
 test/completion_info.test.vim | 10 ++---
 test/filesize.test.vim        |  3 ++
 third_party/ycmd              |  2 +-
 6 files changed, 91 insertions(+), 9 deletions(-)

diff --git a/autoload/youcompleteme.vim b/autoload/youcompleteme.vim
index b1ad66d2..4cfb08dc 100644
--- a/autoload/youcompleteme.vim
+++ b/autoload/youcompleteme.vim
@@ -478,6 +478,9 @@ function! s:AllowedToCompleteInBuffer( buffer )
   endif
 
   let filetype = getbufvar( a:buffer, '&filetype' )
+  if empty( filetype )
+    let filetype = 'ycm_nofiletype'
+  endif
 
   let whitelist_allows = type( g:ycm_filetype_whitelist ) != v:t_dict ||
         \ has_key( g:ycm_filetype_whitelist, '*' ) ||
@@ -487,7 +490,7 @@ function! s:AllowedToCompleteInBuffer( buffer )
 
   let allowed = whitelist_allows && blacklist_allows
 
-  if empty( filetype ) || !allowed || s:DisableOnLargeFile( a:buffer )
+  if !allowed || s:DisableOnLargeFile( a:buffer )
     return 0
   endif
 
diff --git a/python/ycm/vimsupport.py b/python/ycm/vimsupport.py
index 69cef186..5cab1e53 100644
--- a/python/ycm/vimsupport.py
+++ b/python/ycm/vimsupport.py
@@ -715,7 +715,10 @@ def EscapeForVim( text ):
 
 
 def CurrentFiletypes():
-  return ToUnicode( vim.eval( "&filetype" ) ).split( '.' )
+  filetypes = vim.eval( "&filetype" )
+  if not filetypes:
+    filetypes = 'ycm_nofiletype'
+  return ToUnicode( filetypes ).split( '.' )
 
 
 def CurrentFiletypesEnabled( disabled_filetypes ):
@@ -729,7 +732,10 @@ def CurrentFiletypesEnabled( disabled_filetypes ):
 
 def GetBufferFiletypes( bufnr ):
   command = f'getbufvar({ bufnr }, "&ft")'
-  return ToUnicode( vim.eval( command ) ).split( '.' )
+  filetypes = vim.eval( command )
+  if not filetypes:
+    filetypes = 'ycm_nofiletype'
+  return ToUnicode( filetypes ).split( '.' )
 
 
 def FiletypesForBuffer( buffer_object ):
diff --git a/test/completion.common.vim b/test/completion.common.vim
index dee5c201..f0b46942 100644
--- a/test/completion.common.vim
+++ b/test/completion.common.vim
@@ -203,6 +203,76 @@ function! Test_Enter_Delete_Chars_Updates_Filter()
   %bwipeout!
 endfunction
 
+function! Test_Compl_No_Filetype()
+  enew
+  call setline( '.', 'hello this is some text ' )
+
+  " Even when fileytpe is set to '', the filetype autocommand is triggered, but
+  " apparently, _not_ within this function.
+  doautocmd FileType
+  call assert_equal( 1, b:ycm_completing )
+
+  " Required to trigger TextChangedI
+  " https://github.com/vim/vim/issues/4665#event-2480928194
+  call test_override( 'char_avail', 1 )
+
+  " Must do the checks in a timer callback because we need to stay in insert
+  " mode until done.
+  function! Check( id ) closure
+    call assert_equal( getline( '2' ), 'hell' )
+    call WaitForCompletion()
+    let items = complete_info().items
+    call map( items, {index, value -> value.word} )
+    call assert_equal( [ 'hello' ], items )
+    call feedkeys( "\<ESC>" )
+  endfunction
+
+  call FeedAndCheckMain( 'ohell', funcref( 'Check' ) )
+  " Checks run in insert mode, then exit insert mode.
+  call assert_false( pumvisible(), 'pumvisible()' )
+
+  call test_override( 'ALL', 0 )
+  delfunc! Check
+  %bwipeout!
+endfunction
+
+function! SetUp_Test_Compl_No_Filetype_Blacklisted()
+  let g:ycm_filetype_blacklist = { 'ycm_nofiletype': 1 }
+endfunction
+
+function! TearDown__Test_Compl_No_Filetype_Blacklisted()
+  unlet! g:ycm_filetype_blacklist
+endfunction
+
+function! Test_Compl_No_Filetype_Blacklisted()
+  enew
+  call setline( '.', 'hello this is some text ' )
+
+  " Even when fileytpe is set to '', the filetype autocommand is triggered, but
+  " apparently, _not_ within this function.
+  doautocmd FileType
+  call assert_false( exists( 'b:ycm_completing' ) )
+
+  " Required to trigger TextChangedI
+  " https://github.com/vim/vim/issues/4665#event-2480928194
+  call test_override( 'char_avail', 1 )
+
+  " Must do the checks in a timer callback because we need to stay in insert
+  " mode until done.
+  function! Check( id ) closure
+    call assert_false( pumvisible() )
+    call feedkeys( "\<ESC>" )
+  endfunction
+
+  call FeedAndCheckMain( 'ohell', funcref( 'Check' ) )
+  " Checks run in insert mode, then exit insert mode.
+  call assert_false( pumvisible(), 'pumvisible()' )
+
+  call test_override( 'ALL', 0 )
+  delfunc! Check
+  %bwipeout!
+endfunction
+
 function! OmniFuncTester( findstart, query )
   if a:findstart
     return s:omnifunc_start_col
diff --git a/test/completion_info.test.vim b/test/completion_info.test.vim
index 3d7dc502..0b42682d 100644
--- a/test/completion_info.test.vim
+++ b/test/completion_info.test.vim
@@ -55,9 +55,9 @@ function! Test_ResolveCompletion_OnChange()
   " Only the java completer actually uses the completion resolve
   call youcompleteme#test#setup#OpenFile(
         \ '/third_party/ycmd/ycmd/tests/java/testdata/simple_eclipse_project' .
-        \ '/src/com/test/MethodsWithDocumentation.java', { 'delay': 15 } )
+        \ '/src/com/test/TestWithDocumentation.java', { 'delay': 15 } )
 
-  call setpos( '.', [ 0, 33, 20 ] )
+  call setpos( '.', [ 0, 6, 21 ] )
   " Required to trigger TextChangedI
   " https://github.com/vim/vim/issues/4665#event-2480928194
   call test_override( 'char_avail', 1 )
@@ -106,7 +106,7 @@ function! Test_ResolveCompletion_OnChange()
     endif
   endfunction
 
-  call FeedAndCheckMain( 'C.', funcref( 'Check1' ) )
+  call FeedAndCheckMain( 'cw', funcref( 'Check1' ) )
 
   call assert_false( pumvisible(), 'pumvisible()' )
   call assert_equal( 1, found_getAString )
@@ -121,9 +121,9 @@ function! Test_DontResolveCompletion_AlreadyResolved()
   " Only the java completer actually uses the completion resolve
   call youcompleteme#test#setup#OpenFile(
         \ '/third_party/ycmd/ycmd/tests/java/testdata/simple_eclipse_project' .
-        \ '/src/com/test/MethodsWithDocumentation.java', { 'delay': 15 } )
+        \ '/src/com/test/TestWithDocumentation.java', { 'delay': 15 } )
 
-  call setpos( '.', [ 0, 34, 12 ] )
+  call setpos( '.', [ 0, 7, 12 ] )
   " Required to trigger TextChangedI
   " https://github.com/vim/vim/issues/4665#event-2480928194
   call test_override( 'char_avail', 1 )
diff --git a/test/filesize.test.vim b/test/filesize.test.vim
index 60b37c64..105eef6a 100644
--- a/test/filesize.test.vim
+++ b/test/filesize.test.vim
@@ -4,6 +4,9 @@ function! SetUp()
   let g:ycm_auto_trigger = 1
   let g:ycm_keep_logfiles = 1
   let g:ycm_always_populate_location_list = 1
+  let g:ycm_filetype_blacklist = {
+        \ 'ycm_nofiletype': 1
+        \ }
 
   " diagnostics take ages
   let g:ycm_test_min_delay = 7 
-- 
2.29.1

