Bladeren bron

Decoupling completers from Vim; still WIP & broken

Note to self: squash this commit before merging into master.
Strahinja Val Markovic 11 jaren geleden
bovenliggende
commit
29bb90a6b4

+ 17 - 18
autoload/youcompleteme.vim

@@ -41,6 +41,7 @@ function! youcompleteme#Enable()
   py import vim
   py import vim
   exe 'python sys.path.insert( 0, "' . s:script_folder_path . '/../python" )'
   exe 'python sys.path.insert( 0, "' . s:script_folder_path . '/../python" )'
   py from ycm import base
   py from ycm import base
+  py from ycm import vimsupport
   py from ycm import user_options_store
   py from ycm import user_options_store
   py user_options_store.SetAll( base.BuildServerConf() )
   py user_options_store.SetAll( base.BuildServerConf() )
   py from ycm import extra_conf_store
   py from ycm import extra_conf_store
@@ -260,7 +261,7 @@ function! s:OnCursorHold()
   call s:SetUpCompleteopt()
   call s:SetUpCompleteopt()
   " Order is important here; we need to extract any done diagnostics before
   " Order is important here; we need to extract any done diagnostics before
   " reparsing the file again
   " reparsing the file again
-  call s:UpdateDiagnosticNotifications()
+  " call s:UpdateDiagnosticNotifications()
   call s:OnFileReadyToParse()
   call s:OnFileReadyToParse()
 endfunction
 endfunction
 
 
@@ -327,7 +328,7 @@ function! s:OnCursorMovedNormalMode()
     return
     return
   endif
   endif
 
 
-  call s:UpdateDiagnosticNotifications()
+  " call s:UpdateDiagnosticNotifications()
   call s:OnFileReadyToParse()
   call s:OnFileReadyToParse()
 endfunction
 endfunction
 
 
@@ -338,7 +339,7 @@ function! s:OnInsertLeave()
   endif
   endif
 
 
   let s:omnifunc_mode = 0
   let s:omnifunc_mode = 0
-  call s:UpdateDiagnosticNotifications()
+  " call s:UpdateDiagnosticNotifications()
   call s:OnFileReadyToParse()
   call s:OnFileReadyToParse()
   py ycm_state.OnInsertLeave()
   py ycm_state.OnInsertLeave()
   if g:ycm_autoclose_preview_window_after_completion ||
   if g:ycm_autoclose_preview_window_after_completion ||
@@ -572,7 +573,9 @@ command! YcmShowDetailedDiagnostic call s:ShowDetailedDiagnostic()
 " required (currently that's on buffer save) OR when the SyntasticCheck command
 " required (currently that's on buffer save) OR when the SyntasticCheck command
 " is invoked
 " is invoked
 function! youcompleteme#CurrentFileDiagnostics()
 function! youcompleteme#CurrentFileDiagnostics()
-  return pyeval( 'ycm_state.GetDiagnosticsForCurrentFile()' )
+  " TODO: Make this work again.
+  " return pyeval( 'ycm_state.GetDiagnosticsForCurrentFile()' )
+  return []
 endfunction
 endfunction
 
 
 
 
@@ -595,28 +598,24 @@ function! s:CompleterCommand(...)
   " to select the omni completer or "ft=ycm:ident" to select the identifier
   " to select the omni completer or "ft=ycm:ident" to select the identifier
   " completer.  The remaining arguments will passed to the completer.
   " completer.  The remaining arguments will passed to the completer.
   let arguments = copy(a:000)
   let arguments = copy(a:000)
+  let completer = ''
 
 
   if a:0 > 0 && strpart(a:1, 0, 3) == 'ft='
   if a:0 > 0 && strpart(a:1, 0, 3) == 'ft='
     if a:1 == 'ft=ycm:omni'
     if a:1 == 'ft=ycm:omni'
-      py completer = ycm_state.GetOmniCompleter()
+      let completer = 'omni'
     elseif a:1 == 'ft=ycm:ident'
     elseif a:1 == 'ft=ycm:ident'
-      py completer = ycm_state.GetGeneralCompleter()
+      let completer = 'identifier'
-    else
-      py completer = ycm_state.GetFiletypeCompleterForFiletype(
-                   \ vim.eval('a:1').lstrip('ft=') )
     endif
     endif
     let arguments = arguments[1:]
     let arguments = arguments[1:]
-  elseif pyeval( 'ycm_state.NativeFiletypeCompletionAvailable()' )
-    py completer = ycm_state.GetFiletypeCompleter()
-  else
-    echohl WarningMsg |
-      \ echomsg "No native completer found for current buffer." |
-      \ echomsg  "Use ft=... as the first argument to specify a completer." |
-      \ echohl None
-    return
   endif
   endif
 
 
-  py completer.OnUserCommand( vim.eval( 'l:arguments' ) )
+py << EOF
+response = ycm_state.SendCommandRequest( vim.eval( 'l:arguments' ),
+                                         vim.eval( 'l:completer' ) )
+if not response.Valid():
+  vimsupport.PostVimMessage( 'No native completer found for current buffer. ' +
+     'Use ft=... as the first argument to specify a completer.')
+EOF
 endfunction
 endfunction
 
 
 
 

+ 23 - 19
cpp/ycm/ClangCompleter/ClangCompleter.cpp

@@ -103,7 +103,7 @@ void ClangCompleter::EnableThreading() {
 
 
 
 
 std::vector< Diagnostic > ClangCompleter::DiagnosticsForFile(
 std::vector< Diagnostic > ClangCompleter::DiagnosticsForFile(
-  const std::string &filename ) {
+  std::string filename ) {
   shared_ptr< TranslationUnit > unit = translation_unit_store_.Get( filename );
   shared_ptr< TranslationUnit > unit = translation_unit_store_.Get( filename );
 
 
   if ( !unit )
   if ( !unit )
@@ -127,9 +127,9 @@ bool ClangCompleter::UpdatingTranslationUnit( const std::string &filename ) {
 
 
 
 
 void ClangCompleter::UpdateTranslationUnit(
 void ClangCompleter::UpdateTranslationUnit(
-  const std::string &filename,
+  std::string filename,
-  const std::vector< UnsavedFile > &unsaved_files,
+  std::vector< UnsavedFile > unsaved_files,
-  const std::vector< std::string > &flags ) {
+  std::vector< std::string > flags ) {
   bool translation_unit_created;
   bool translation_unit_created;
   shared_ptr< TranslationUnit > unit = translation_unit_store_.GetOrCreate(
   shared_ptr< TranslationUnit > unit = translation_unit_store_.GetOrCreate(
       filename,
       filename,
@@ -182,11 +182,11 @@ Future< void > ClangCompleter::UpdateTranslationUnitAsync(
 
 
 std::vector< CompletionData >
 std::vector< CompletionData >
 ClangCompleter::CandidatesForLocationInFile(
 ClangCompleter::CandidatesForLocationInFile(
-  const std::string &filename,
+  std::string filename,
   int line,
   int line,
   int column,
   int column,
-  const std::vector< UnsavedFile > &unsaved_files,
+  std::vector< UnsavedFile > unsaved_files,
-  const std::vector< std::string > &flags ) {
+  std::vector< std::string > flags ) {
   shared_ptr< TranslationUnit > unit =
   shared_ptr< TranslationUnit > unit =
       translation_unit_store_.GetOrCreate( filename, unsaved_files, flags );
       translation_unit_store_.GetOrCreate( filename, unsaved_files, flags );
 
 
@@ -201,12 +201,12 @@ ClangCompleter::CandidatesForLocationInFile(
 
 
 Future< AsyncCompletions >
 Future< AsyncCompletions >
 ClangCompleter::CandidatesForQueryAndLocationInFileAsync(
 ClangCompleter::CandidatesForQueryAndLocationInFileAsync(
-  const std::string &query,
+  std::string query,
-  const std::string &filename,
+  std::string filename,
   int line,
   int line,
   int column,
   int column,
-  const std::vector< UnsavedFile > &unsaved_files,
+  std::vector< UnsavedFile > unsaved_files,
-  const std::vector< std::string > &flags ) {
+  std::vector< std::string > flags ) {
   // TODO: throw exception when threading is not enabled and this is called
   // TODO: throw exception when threading is not enabled and this is called
   if ( !threading_enabled_ )
   if ( !threading_enabled_ )
     return Future< AsyncCompletions >();
     return Future< AsyncCompletions >();
@@ -238,7 +238,11 @@ ClangCompleter::CandidatesForQueryAndLocationInFileAsync(
   CreateSortingTask( query, future );
   CreateSortingTask( query, future );
 
 
   if ( skip_clang_result_cache ) {
   if ( skip_clang_result_cache ) {
-    CreateClangTask( filename, line, column, unsaved_files, flags );
+    CreateClangTask( boost::move( filename ),
+                     line,
+                     column,
+                     boost::move( unsaved_files ),
+                     boost::move( flags ) );
   }
   }
 
 
   return Future< AsyncCompletions >( boost::move( future ) );
   return Future< AsyncCompletions >( boost::move( future ) );
@@ -246,11 +250,11 @@ ClangCompleter::CandidatesForQueryAndLocationInFileAsync(
 
 
 
 
 Location ClangCompleter::GetDeclarationLocation(
 Location ClangCompleter::GetDeclarationLocation(
-  const std::string &filename,
+  std::string filename,
   int line,
   int line,
   int column,
   int column,
-  const std::vector< UnsavedFile > &unsaved_files,
+  std::vector< UnsavedFile > unsaved_files,
-  const std::vector< std::string > &flags ) {
+  std::vector< std::string > flags ) {
   shared_ptr< TranslationUnit > unit =
   shared_ptr< TranslationUnit > unit =
       translation_unit_store_.GetOrCreate( filename, unsaved_files, flags );
       translation_unit_store_.GetOrCreate( filename, unsaved_files, flags );
 
 
@@ -263,11 +267,11 @@ Location ClangCompleter::GetDeclarationLocation(
 
 
 
 
 Location ClangCompleter::GetDefinitionLocation(
 Location ClangCompleter::GetDefinitionLocation(
-  const std::string &filename,
+  std::string filename,
   int line,
   int line,
   int column,
   int column,
-  const std::vector< UnsavedFile > &unsaved_files,
+  std::vector< UnsavedFile > unsaved_files,
-  const std::vector< std::string > &flags ) {
+  std::vector< std::string > flags ) {
   shared_ptr< TranslationUnit > unit =
   shared_ptr< TranslationUnit > unit =
       translation_unit_store_.GetOrCreate( filename, unsaved_files, flags );
       translation_unit_store_.GetOrCreate( filename, unsaved_files, flags );
 
 
@@ -279,7 +283,7 @@ Location ClangCompleter::GetDefinitionLocation(
 }
 }
 
 
 
 
-void ClangCompleter::DeleteCachesForFileAsync( const std::string &filename ) {
+void ClangCompleter::DeleteCachesForFileAsync( std::string filename ) {
   file_cache_delete_stack_.Push( filename );
   file_cache_delete_stack_.Push( filename );
 }
 }
 
 

+ 25 - 20
cpp/ycm/ClangCompleter/ClangCompleter.h

@@ -59,18 +59,23 @@ public:
 
 
   void EnableThreading();
   void EnableThreading();
 
 
-  std::vector< Diagnostic > DiagnosticsForFile( const std::string &filename );
+  std::vector< Diagnostic > DiagnosticsForFile( std::string filename );
 
 
   bool UpdatingTranslationUnit( const std::string &filename );
   bool UpdatingTranslationUnit( const std::string &filename );
 
 
+  // NOTE: params are taken by value on purpose! With a C++11 compiler we can
+  // avoid internal copies if params are taken by value (move ctors FTW), and we
+  // need to ensure we own the memory.
+  // TODO: Change some of these params back to const ref where possible after we
+  // get the server up.
+  // TODO: Remove the async methods and the threads when the server is ready.
+
   // Public because of unit tests (gtest is not very thread-friendly)
   // Public because of unit tests (gtest is not very thread-friendly)
   void UpdateTranslationUnit(
   void UpdateTranslationUnit(
-    const std::string &filename,
+    std::string filename,
-    const std::vector< UnsavedFile > &unsaved_files,
+    std::vector< UnsavedFile > unsaved_files,
-    const std::vector< std::string > &flags );
+    std::vector< std::string > flags );
 
 
-  // NOTE: params are taken by value on purpose! With a C++11 compiler we can
-  // avoid internal copies if params are taken by value (move ctors FTW)
   Future< void > UpdateTranslationUnitAsync(
   Future< void > UpdateTranslationUnitAsync(
     std::string filename,
     std::string filename,
     std::vector< UnsavedFile > unsaved_files,
     std::vector< UnsavedFile > unsaved_files,
@@ -78,35 +83,35 @@ public:
 
 
   // Public because of unit tests (gtest is not very thread-friendly)
   // Public because of unit tests (gtest is not very thread-friendly)
   std::vector< CompletionData > CandidatesForLocationInFile(
   std::vector< CompletionData > CandidatesForLocationInFile(
-    const std::string &filename,
+    std::string filename,
     int line,
     int line,
     int column,
     int column,
-    const std::vector< UnsavedFile > &unsaved_files,
+    std::vector< UnsavedFile > unsaved_files,
-    const std::vector< std::string > &flags );
+    std::vector< std::string > flags );
 
 
   Future< AsyncCompletions > CandidatesForQueryAndLocationInFileAsync(
   Future< AsyncCompletions > CandidatesForQueryAndLocationInFileAsync(
-    const std::string &query,
+    std::string query,
-    const std::string &filename,
+    std::string filename,
     int line,
     int line,
     int column,
     int column,
-    const std::vector< UnsavedFile > &unsaved_files,
+    std::vector< UnsavedFile > unsaved_files,
-    const std::vector< std::string > &flags );
+    std::vector< std::string > flags );
 
 
   Location GetDeclarationLocation(
   Location GetDeclarationLocation(
-    const std::string &filename,
+    std::string filename,
     int line,
     int line,
     int column,
     int column,
-    const std::vector< UnsavedFile > &unsaved_files,
+    std::vector< UnsavedFile > unsaved_files,
-    const std::vector< std::string > &flags );
+    std::vector< std::string > flags );
 
 
   Location GetDefinitionLocation(
   Location GetDefinitionLocation(
-    const std::string &filename,
+    std::string filename,
     int line,
     int line,
     int column,
     int column,
-    const std::vector< UnsavedFile > &unsaved_files,
+    std::vector< UnsavedFile > unsaved_files,
-    const std::vector< std::string > &flags );
+    std::vector< std::string > flags );
 
 
-  void DeleteCachesForFileAsync( const std::string &filename );
+  void DeleteCachesForFileAsync( std::string filename );
 
 
 private:
 private:
 
 

+ 2 - 2
plugin/youcompleteme.vim

@@ -143,8 +143,8 @@ let g:ycm_extra_conf_globlist =
 let g:ycm_filepath_completion_use_working_dir =
 let g:ycm_filepath_completion_use_working_dir =
       \ get( g:, 'ycm_filepath_completion_use_working_dir', 0 )
       \ get( g:, 'ycm_filepath_completion_use_working_dir', 0 )
 
 
-" Default semantic triggers are in python/ycm/completers/completer.py, these
+" Default semantic triggers are in python/ycm/completers/completer_utils.py
-" just append new triggers to the default dict.
+" these just append new triggers to the default dict.
 let g:ycm_semantic_triggers =
 let g:ycm_semantic_triggers =
       \ get( g:, 'ycm_semantic_triggers', {} )
       \ get( g:, 'ycm_semantic_triggers', {} )
 
 

+ 18 - 22
python/ycm/completers/all/identifier_completer.py

@@ -25,6 +25,7 @@ from ycm.completers.general_completer import GeneralCompleter
 from ycm.completers.general import syntax_parse
 from ycm.completers.general import syntax_parse
 from ycm import vimsupport
 from ycm import vimsupport
 from ycm import utils
 from ycm import utils
+from ycm import server_responses
 
 
 MAX_IDENTIFIER_COMPLETIONS_RETURNED = 10
 MAX_IDENTIFIER_COMPLETIONS_RETURNED = 10
 SYNTAX_FILENAME = 'YCM_PLACEHOLDER_FOR_SYNTAX'
 SYNTAX_FILENAME = 'YCM_PLACEHOLDER_FOR_SYNTAX'
@@ -39,15 +40,14 @@ class IdentifierCompleter( GeneralCompleter ):
     self.filetypes_with_keywords_loaded = set()
     self.filetypes_with_keywords_loaded = set()
 
 
 
 
-  def ShouldUseNow( self, start_column, unused_current_line ):
+  def ShouldUseNow( self, request_data ):
-    return self.QueryLengthAboveMinThreshold( start_column )
+    return self.QueryLengthAboveMinThreshold( request_data )
 
 
 
 
-  def CandidatesForQueryAsync( self, query, unused_start_column ):
+  def CandidatesForQueryAsync( self, request_data ):
-    filetype = vim.eval( "&filetype" )
     self.completions_future = self.completer.CandidatesForQueryAndTypeAsync(
     self.completions_future = self.completer.CandidatesForQueryAndTypeAsync(
-      utils.SanitizeQuery( query ),
+      utils.SanitizeQuery( request_data[ 'query' ] ),
-      filetype )
+      request_data[ 'filetypes' ][ 0 ] )
 
 
 
 
   def AddIdentifier( self, identifier ):
   def AddIdentifier( self, identifier ):
@@ -83,17 +83,16 @@ class IdentifierCompleter( GeneralCompleter ):
     self.AddIdentifier( stripped_cursor_identifier )
     self.AddIdentifier( stripped_cursor_identifier )
 
 
 
 
-  def AddBufferIdentifiers( self ):
+  def AddBufferIdentifiers( self, request_data ):
-    # TODO: use vimsupport.GetFiletypes; also elsewhere in file
+    filetype = request_data[ 'filetypes' ][ 0 ]
-    filetype = vim.eval( "&filetype" )
+    filepath = request_data[ 'filepath' ]
-    filepath = vim.eval( "expand('%:p')" )
     collect_from_comments_and_strings = bool( self.user_options[
     collect_from_comments_and_strings = bool( self.user_options[
       'collect_identifiers_from_comments_and_strings' ] )
       'collect_identifiers_from_comments_and_strings' ] )
 
 
     if not filetype or not filepath:
     if not filetype or not filepath:
       return
       return
 
 
-    text = "\n".join( vim.current.buffer )
+    text = request_data[ 'file_data' ][ filepath ][ 'contents' ]
     self.completer.AddIdentifiersToDatabaseFromBufferAsync(
     self.completer.AddIdentifiersToDatabaseFromBufferAsync(
       text,
       text,
       filetype,
       filetype,
@@ -147,14 +146,15 @@ class IdentifierCompleter( GeneralCompleter ):
                                              filepath )
                                              filepath )
 
 
 
 
-  def OnFileReadyToParse( self ):
+  def OnFileReadyToParse( self, request_data ):
-    self.AddBufferIdentifiers()
+    self.AddBufferIdentifiers( request_data )
 
 
-    if self.user_options[ 'collect_identifiers_from_tags_files' ]:
+    # TODO: make these work again
-      self.AddIdentifiersFromTagFiles()
+    # if self.user_options[ 'collect_identifiers_from_tags_files' ]:
+    #   self.AddIdentifiersFromTagFiles()
 
 
-    if self.user_options[ 'seed_identifiers_with_syntax' ]:
+    # if self.user_options[ 'seed_identifiers_with_syntax' ]:
-      self.AddIdentifiersFromSyntax()
+    #   self.AddIdentifiersFromSyntax()
 
 
 
 
   def OnInsertLeave( self ):
   def OnInsertLeave( self ):
@@ -174,11 +174,7 @@ class IdentifierCompleter( GeneralCompleter ):
     completions = _RemoveSmallCandidates(
     completions = _RemoveSmallCandidates(
       completions, self.user_options[ 'min_num_identifier_candidate_chars' ] )
       completions, self.user_options[ 'min_num_identifier_candidate_chars' ] )
 
 
-    # We will never have duplicates in completions so with 'dup':1 we tell Vim
+    return [ server_responses.BuildCompletionData( x ) for x in completions ]
-    # to add this candidate even if it's a duplicate of an existing one (which
-    # will never happen). This saves us some expensive string matching
-    # operations in Vim.
-    return [ { 'word': x, 'dup': 1 } for x in completions ]
 
 
 
 
 def _PreviousIdentifier( min_num_completion_start_chars ):
 def _PreviousIdentifier( min_num_completion_start_chars ):

+ 11 - 13
python/ycm/completers/all/omni_completer.py

@@ -40,29 +40,27 @@ class OmniCompleter( Completer ):
     return bool( self.user_options[ 'cache_omnifunc' ] )
     return bool( self.user_options[ 'cache_omnifunc' ] )
 
 
 
 
-  def ShouldUseNow( self, start_column, current_line ):
+  def ShouldUseNow( self, request_data ):
     if self.ShouldUseCache():
     if self.ShouldUseCache():
-      return super( OmniCompleter, self ).ShouldUseNow( start_column,
+      return super( OmniCompleter, self ).ShouldUseNow( request_data )
-                                                        current_line )
+    return self.ShouldUseNowInner( request_data )
-    return self.ShouldUseNowInner( start_column, current_line )
 
 
 
 
-  def ShouldUseNowInner( self, start_column, current_line ):
+  def ShouldUseNowInner( self, request_data ):
     if not self.omnifunc:
     if not self.omnifunc:
       return False
       return False
-    return super( OmniCompleter, self ).ShouldUseNowInner( start_column,
+    return super( OmniCompleter, self ).ShouldUseNowInner( request_data )
-                                                           current_line )
 
 
 
 
-  def CandidatesForQueryAsync( self, query, unused_start_column ):
+  def CandidatesForQueryAsync( self, request_data ):
     if self.ShouldUseCache():
     if self.ShouldUseCache():
       return super( OmniCompleter, self ).CandidatesForQueryAsync(
       return super( OmniCompleter, self ).CandidatesForQueryAsync(
-          query, unused_start_column )
+        request_data )
     else:
     else:
-      return self.CandidatesForQueryAsyncInner( query, unused_start_column )
+      return self.CandidatesForQueryAsyncInner( request_data )
 
 
 
 
-  def CandidatesForQueryAsyncInner( self, query, unused_start_column ):
+  def CandidatesForQueryAsyncInner( self, request_data ):
     if not self.omnifunc:
     if not self.omnifunc:
       self.stored_candidates = None
       self.stored_candidates = None
       return
       return
@@ -75,7 +73,7 @@ class OmniCompleter( Completer ):
 
 
       omnifunc_call = [ self.omnifunc,
       omnifunc_call = [ self.omnifunc,
                         "(0,'",
                         "(0,'",
-                        vimsupport.EscapeForVim( query ),
+                        vimsupport.EscapeForVim( request_data[ 'query' ] ),
                         "')" ]
                         "')" ]
 
 
       items = vim.eval( ''.join( omnifunc_call ) )
       items = vim.eval( ''.join( omnifunc_call ) )
@@ -98,7 +96,7 @@ class OmniCompleter( Completer ):
     return True
     return True
 
 
 
 
-  def OnFileReadyToParse( self ):
+  def OnFileReadyToParse( self, request_data ):
     self.omnifunc = vim.eval( '&omnifunc' )
     self.omnifunc = vim.eval( '&omnifunc' )
 
 
 
 

+ 45 - 37
python/ycm/completers/completer.py

@@ -19,7 +19,6 @@
 
 
 import abc
 import abc
 import ycm_core
 import ycm_core
-from ycm import vimsupport
 from ycm.completers.completer_utils import TriggersForFiletype
 from ycm.completers.completer_utils import TriggersForFiletype
 
 
 NO_USER_COMMANDS = 'This completer does not define any commands.'
 NO_USER_COMMANDS = 'This completer does not define any commands.'
@@ -116,32 +115,35 @@ class Completer( object ):
   def __init__( self, user_options ):
   def __init__( self, user_options ):
     self.user_options = user_options
     self.user_options = user_options
     self.min_num_chars = user_options[ 'min_num_of_chars_for_completion' ]
     self.min_num_chars = user_options[ 'min_num_of_chars_for_completion' ]
-    self.triggers_for_filetype = TriggersForFiletype()
+    self.triggers_for_filetype = TriggersForFiletype(
+      user_options[ 'semantic_triggers' ] )
     self.completions_future = None
     self.completions_future = None
     self.completions_cache = None
     self.completions_cache = None
-    self.completion_start_column = None
 
 
 
 
   # It's highly likely you DON'T want to override this function but the *Inner
   # It's highly likely you DON'T want to override this function but the *Inner
   # version of it.
   # version of it.
-  def ShouldUseNow( self, start_column, current_line ):
+  def ShouldUseNow( self, request_data ):
-    inner_says_yes = self.ShouldUseNowInner( start_column, current_line )
+    inner_says_yes = self.ShouldUseNowInner( request_data )
     if not inner_says_yes:
     if not inner_says_yes:
       self.completions_cache = None
       self.completions_cache = None
 
 
     previous_results_were_empty = ( self.completions_cache and
     previous_results_were_empty = ( self.completions_cache and
                                     self.completions_cache.CacheValid(
                                     self.completions_cache.CacheValid(
-                                      start_column ) and
+                                      request_data[ 'line_num' ],
+                                      request_data[ 'start_column' ] ) and
                                     not self.completions_cache.raw_completions )
                                     not self.completions_cache.raw_completions )
     return inner_says_yes and not previous_results_were_empty
     return inner_says_yes and not previous_results_were_empty
 
 
 
 
-  def ShouldUseNowInner( self, start_column, current_line ):
+  def ShouldUseNowInner( self, request_data ):
+    current_line = request_data[ 'line_value' ]
+    start_column = request_data[ 'start_column' ]
     line_length = len( current_line )
     line_length = len( current_line )
     if not line_length or start_column - 1 >= line_length:
     if not line_length or start_column - 1 >= line_length:
       return False
       return False
 
 
-    filetype = self._CurrentFiletype()
+    filetype = self._CurrentFiletype( request_data[ 'filetypes' ] )
     triggers = self.triggers_for_filetype[ filetype ]
     triggers = self.triggers_for_filetype[ filetype ]
 
 
     for trigger in triggers:
     for trigger in triggers:
@@ -158,52 +160,61 @@ class Completer( object ):
     return False
     return False
 
 
 
 
-  def QueryLengthAboveMinThreshold( self, start_column ):
+  def QueryLengthAboveMinThreshold( self, request_data ):
-    query_length = vimsupport.CurrentColumn() - start_column
+    query_length = request_data[ 'column_num' ] - request_data[ 'start_column' ]
     return query_length >= self.min_num_chars
     return query_length >= self.min_num_chars
 
 
 
 
   # It's highly likely you DON'T want to override this function but the *Inner
   # It's highly likely you DON'T want to override this function but the *Inner
   # version of it.
   # version of it.
-  def CandidatesForQueryAsync( self, query, start_column ):
+  def CandidatesForQueryAsync( self, request_data ):
-    self.completion_start_column = start_column
+    self.request_data = request_data
 
 
-    if query and self.completions_cache and self.completions_cache.CacheValid(
+    if ( request_data[ 'query' ] and
-      start_column ):
+         self.completions_cache and
+         self.completions_cache.CacheValid( request_data[ 'line_num' ],
+                                            request_data[ 'start_column' ] ) ):
       self.completions_cache.filtered_completions = (
       self.completions_cache.filtered_completions = (
         self.FilterAndSortCandidates(
         self.FilterAndSortCandidates(
           self.completions_cache.raw_completions,
           self.completions_cache.raw_completions,
-          query ) )
+          request_data[ 'query' ] ) )
     else:
     else:
       self.completions_cache = None
       self.completions_cache = None
-      self.CandidatesForQueryAsyncInner( query, start_column )
+      self.CandidatesForQueryAsyncInner( request_data )
 
 
 
 
   def DefinedSubcommands( self ):
   def DefinedSubcommands( self ):
     return []
     return []
 
 
 
 
-  def EchoUserCommandsHelpMessage( self ):
+  def UserCommandsHelpMessage( self ):
     subcommands = self.DefinedSubcommands()
     subcommands = self.DefinedSubcommands()
     if subcommands:
     if subcommands:
-      vimsupport.EchoText( 'Supported commands are:\n' +
+      return ( 'Supported commands are:\n' +
-                           '\n'.join( subcommands ) +
+               '\n'.join( subcommands ) +
-                           '\nSee the docs for information on what they do.' )
+               '\nSee the docs for information on what they do.' )
     else:
     else:
-      vimsupport.EchoText( 'No supported subcommands' )
+      return 'No supported subcommands'
 
 
 
 
   def FilterAndSortCandidates( self, candidates, query ):
   def FilterAndSortCandidates( self, candidates, query ):
     if not candidates:
     if not candidates:
       return []
       return []
 
 
-    if hasattr( candidates, 'words' ):
+    # We need to handle both an omni_completer style completer and a server
-      candidates = candidates.words
+    # style completer
-    items_are_objects = 'word' in candidates[ 0 ]
+    if 'words' in candidates:
+      candidates = candidates[ 'words' ]
+
+    sort_property = ''
+    if 'word' in candidates[ 0 ]:
+      sort_property = 'word'
+    elif 'insertion_text' in candidates[ 0 ]:
+      sort_property = 'insertion_text'
 
 
     matches = ycm_core.FilterAndSortCandidates(
     matches = ycm_core.FilterAndSortCandidates(
       candidates,
       candidates,
-      'word' if items_are_objects else '',
+      sort_property,
       query )
       query )
 
 
     return matches
     return matches
@@ -238,8 +249,8 @@ class Completer( object ):
     else:
     else:
       self.completions_cache = CompletionsCache()
       self.completions_cache = CompletionsCache()
       self.completions_cache.raw_completions = self.CandidatesFromStoredRequestInner()
       self.completions_cache.raw_completions = self.CandidatesFromStoredRequestInner()
-      self.completions_cache.line, _ = vimsupport.CurrentLineAndColumn()
+      self.completions_cache.line = self.request_data[ 'line_num' ]
-      self.completions_cache.column = self.completion_start_column
+      self.completions_cache.column = self.request_data[ 'start_column' ]
       return self.completions_cache.raw_completions
       return self.completions_cache.raw_completions
 
 
 
 
@@ -249,7 +260,7 @@ class Completer( object ):
     return self.completions_future.GetResults()
     return self.completions_future.GetResults()
 
 
 
 
-  def OnFileReadyToParse( self ):
+  def OnFileReadyToParse( self, request_data ):
     pass
     pass
 
 
 
 
@@ -269,8 +280,8 @@ class Completer( object ):
     pass
     pass
 
 
 
 
-  def OnUserCommand( self, arguments ):
+  def OnUserCommand( self, arguments, request_data ):
-    vimsupport.PostVimMessage( NO_USER_COMMANDS )
+    raise NotImplementedError( NO_USER_COMMANDS )
 
 
 
 
   def OnCurrentIdentifierFinished( self ):
   def OnCurrentIdentifierFinished( self ):
@@ -285,7 +296,7 @@ class Completer( object ):
     return []
     return []
 
 
 
 
-  def ShowDetailedDiagnostic( self ):
+  def GetDetailedDiagnostic( self ):
     pass
     pass
 
 
 
 
@@ -293,8 +304,7 @@ class Completer( object ):
     return False
     return False
 
 
 
 
-  def _CurrentFiletype( self ):
+  def _CurrentFiletype( self, filetypes ):
-    filetypes = vimsupport.CurrentFiletypes()
     supported = self.SupportedFiletypes()
     supported = self.SupportedFiletypes()
 
 
     for filetype in filetypes:
     for filetype in filetypes:
@@ -321,9 +331,7 @@ class CompletionsCache( object ):
     self.filtered_completions = []
     self.filtered_completions = []
 
 
 
 
-  def CacheValid( self, start_column ):
+  def CacheValid( self, current_line, start_column ):
-    completion_line, _ = vimsupport.CurrentLineAndColumn()
+    return current_line == self.line and start_column == self.column
-    completion_column = start_column
-    return completion_line == self.line and completion_column == self.column
 
 
 
 

+ 2 - 6
python/ycm/completers/completer_utils.py

@@ -19,7 +19,6 @@
 
 
 from collections import defaultdict
 from collections import defaultdict
 from copy import deepcopy
 from copy import deepcopy
-import vim
 
 
 DEFAULT_FILETYPE_TRIGGERS = {
 DEFAULT_FILETYPE_TRIGGERS = {
   'c' : ['->', '.'],
   'c' : ['->', '.'],
@@ -58,12 +57,9 @@ def _FiletypeDictUnion( dict_one, dict_two ):
   return final_dict
   return final_dict
 
 
 
 
-def TriggersForFiletype():
+def TriggersForFiletype( user_triggers ):
-  user_triggers = _FiletypeTriggerDictFromSpec(
-    vim.eval( 'g:ycm_semantic_triggers' ) )
-
   default_triggers = _FiletypeTriggerDictFromSpec(
   default_triggers = _FiletypeTriggerDictFromSpec(
     DEFAULT_FILETYPE_TRIGGERS )
     DEFAULT_FILETYPE_TRIGGERS )
 
 
-  return _FiletypeDictUnion( default_triggers, user_triggers )
+  return _FiletypeDictUnion( default_triggers, dict( user_triggers ) )
 
 

+ 142 - 134
python/ycm/completers/cpp/clang_completer.py

@@ -18,14 +18,23 @@
 # along with YouCompleteMe.  If not, see <http://www.gnu.org/licenses/>.
 # along with YouCompleteMe.  If not, see <http://www.gnu.org/licenses/>.
 
 
 from collections import defaultdict
 from collections import defaultdict
-import vim
 import ycm_core
 import ycm_core
-from ycm import vimsupport
+import logging
+from ycm import server_responses
 from ycm import extra_conf_store
 from ycm import extra_conf_store
 from ycm.completers.completer import Completer
 from ycm.completers.completer import Completer
 from ycm.completers.cpp.flags import Flags
 from ycm.completers.cpp.flags import Flags
 
 
 CLANG_FILETYPES = set( [ 'c', 'cpp', 'objc', 'objcpp' ] )
 CLANG_FILETYPES = set( [ 'c', 'cpp', 'objc', 'objcpp' ] )
+MIN_LINES_IN_FILE_TO_PARSE = 5
+PARSING_FILE_MESSAGE = 'Still parsing file, no completions yet.'
+NO_COMPILE_FLAGS_MESSAGE = 'Still no compile flags, no completions yet.'
+NO_COMPLETIONS_MESSAGE = 'No completions found; errors in the file?'
+INVALID_FILE_MESSAGE = 'File is invalid.'
+FILE_TOO_SHORT_MESSAGE = (
+  'File is less than {} lines long; not compiling.'.format(
+    MIN_LINES_IN_FILE_TO_PARSE ) )
+NO_DIAGNOSTIC_MESSAGE = 'No diagnostic for current line!'
 
 
 
 
 class ClangCompleter( Completer ):
 class ClangCompleter( Completer ):
@@ -35,12 +44,11 @@ class ClangCompleter( Completer ):
       'max_diagnostics_to_display' ]
       'max_diagnostics_to_display' ]
     self.completer = ycm_core.ClangCompleter()
     self.completer = ycm_core.ClangCompleter()
     self.completer.EnableThreading()
     self.completer.EnableThreading()
-    self.contents_holder = []
-    self.filename_holder = []
     self.last_prepared_diagnostics = []
     self.last_prepared_diagnostics = []
     self.parse_future = None
     self.parse_future = None
     self.flags = Flags()
     self.flags = Flags()
     self.diagnostic_store = None
     self.diagnostic_store = None
+    self._logger = logging.getLogger( __name__ )
 
 
     # We set this flag when a compilation request comes in while one is already
     # We set this flag when a compilation request comes in while one is already
     # in progress. We use this to trigger the pending request after the previous
     # in progress. We use this to trigger the pending request after the previous
@@ -53,63 +61,52 @@ class ClangCompleter( Completer ):
     return CLANG_FILETYPES
     return CLANG_FILETYPES
 
 
 
 
-  def GetUnsavedFilesVector( self ):
+  def GetUnsavedFilesVector( self, request_data ):
-    # CAREFUL HERE! For UnsavedFile filename and contents we are referring
-    # directly to Python-allocated and -managed memory since we are accepting
-    # pointers to data members of python objects. We need to ensure that those
-    # objects outlive our UnsavedFile objects. This is why we need the
-    # contents_holder and filename_holder lists, to make sure the string objects
-    # are still around when we call CandidatesForQueryAndLocationInFile.  We do
-    # this to avoid an extra copy of the entire file contents.
-
     files = ycm_core.UnsavedFileVec()
     files = ycm_core.UnsavedFileVec()
-    self.contents_holder = []
+    for filename, file_data in request_data[ 'file_data' ].iteritems():
-    self.filename_holder = []
+      if not ClangAvailableForFiletypes( file_data[ 'filetypes' ] ):
-    for buffer in vimsupport.GetUnsavedBuffers():
-      if not ClangAvailableForBuffer( buffer ):
         continue
         continue
-      contents = '\n'.join( buffer )
+      contents = file_data[ 'contents' ]
-      name = buffer.name
+      if not contents or not filename:
-      if not contents or not name:
         continue
         continue
-      self.contents_holder.append( contents )
-      self.filename_holder.append( name )
 
 
       unsaved_file = ycm_core.UnsavedFile()
       unsaved_file = ycm_core.UnsavedFile()
-      unsaved_file.contents_ = self.contents_holder[ -1 ]
+      unsaved_file.contents_ = contents
-      unsaved_file.length_ = len( self.contents_holder[ -1 ] )
+      unsaved_file.length_ = len( contents )
-      unsaved_file.filename_ = self.filename_holder[ -1 ]
+      unsaved_file.filename_ = filename
 
 
       files.append( unsaved_file )
       files.append( unsaved_file )
-
     return files
     return files
 
 
 
 
-  def CandidatesForQueryAsync( self, query, start_column ):
+  def CandidatesForQueryAsync( self, request_data ):
-    filename = vim.current.buffer.name
+    filename = request_data[ 'filepath' ]
 
 
     if not filename:
     if not filename:
       return
       return
 
 
     if self.completer.UpdatingTranslationUnit( filename ):
     if self.completer.UpdatingTranslationUnit( filename ):
-      vimsupport.PostVimMessage( 'Still parsing file, no completions yet.' )
       self.completions_future = None
       self.completions_future = None
-      return
+      self._logger.info( PARSING_FILE_MESSAGE )
+      return server_responses.BuildDisplayMessageResponse(
+        PARSING_FILE_MESSAGE )
 
 
     flags = self.flags.FlagsForFile( filename )
     flags = self.flags.FlagsForFile( filename )
     if not flags:
     if not flags:
-      vimsupport.PostVimMessage( 'Still no compile flags, no completions yet.' )
       self.completions_future = None
       self.completions_future = None
-      return
+      self._logger.info( NO_COMPILE_FLAGS_MESSAGE )
+      return server_responses.BuildDisplayMessageResponse(
+        NO_COMPILE_FLAGS_MESSAGE )
 
 
     # TODO: sanitize query, probably in C++ code
     # TODO: sanitize query, probably in C++ code
 
 
     files = ycm_core.UnsavedFileVec()
     files = ycm_core.UnsavedFileVec()
+    query = request_data[ 'query' ]
     if not query:
     if not query:
-      files = self.GetUnsavedFilesVector()
+      files = self.GetUnsavedFilesVector( request_data )
 
 
-    line, _ = vim.current.window.cursor
+    line = request_data[ 'line_num' ] + 1
-    column = start_column + 1
+    column = request_data[ 'start_column' ] + 1
     self.completions_future = (
     self.completions_future = (
       self.completer.CandidatesForQueryAndLocationInFileAsync(
       self.completer.CandidatesForQueryAndLocationInFileAsync(
         query,
         query,
@@ -123,10 +120,11 @@ class ClangCompleter( Completer ):
   def CandidatesFromStoredRequest( self ):
   def CandidatesFromStoredRequest( self ):
     if not self.completions_future:
     if not self.completions_future:
       return []
       return []
-    results = [ CompletionDataToDict( x ) for x in
+    results = [ ConvertCompletionData( x ) for x in
                 self.completions_future.GetResults() ]
                 self.completions_future.GetResults() ]
     if not results:
     if not results:
-      vimsupport.PostVimMessage( 'No completions found; errors in the file?' )
+      self._logger.warning( NO_COMPLETIONS_MESSAGE )
+      raise RuntimeError( NO_COMPLETIONS_MESSAGE )
     return results
     return results
 
 
 
 
@@ -137,37 +135,37 @@ class ClangCompleter( Completer ):
              'ClearCompilationFlagCache']
              'ClearCompilationFlagCache']
 
 
 
 
-  def OnUserCommand( self, arguments ):
+  def OnUserCommand( self, arguments, request_data ):
     if not arguments:
     if not arguments:
-      self.EchoUserCommandsHelpMessage()
+      raise ValueError( self.UserCommandsHelpMessage() )
-      return
 
 
     command = arguments[ 0 ]
     command = arguments[ 0 ]
     if command == 'GoToDefinition':
     if command == 'GoToDefinition':
-      self._GoToDefinition()
+      self._GoToDefinition( request_data )
     elif command == 'GoToDeclaration':
     elif command == 'GoToDeclaration':
-      self._GoToDeclaration()
+      self._GoToDeclaration( request_data )
     elif command == 'GoToDefinitionElseDeclaration':
     elif command == 'GoToDefinitionElseDeclaration':
-      self._GoToDefinitionElseDeclaration()
+      self._GoToDefinitionElseDeclaration( request_data )
     elif command == 'ClearCompilationFlagCache':
     elif command == 'ClearCompilationFlagCache':
-      self._ClearCompilationFlagCache()
+      self._ClearCompilationFlagCache( request_data )
 
 
 
 
-  def _LocationForGoTo( self, goto_function ):
+  def _LocationForGoTo( self, goto_function, request_data ):
-    filename = vim.current.buffer.name
+    filename = request_data[ 'filepath' ]
     if not filename:
     if not filename:
-      return None
+      self._logger.warning( INVALID_FILE_MESSAGE )
+      return server_responses.BuildDisplayMessageResponse(
+        INVALID_FILE_MESSAGE )
 
 
     flags = self.flags.FlagsForFile( filename )
     flags = self.flags.FlagsForFile( filename )
     if not flags:
     if not flags:
-      vimsupport.PostVimMessage( 'Still no compile flags, can\'t compile.' )
+      self._logger.info( NO_COMPILE_FLAGS_MESSAGE )
-      return None
+      return server_responses.BuildDisplayMessageResponse(
+        NO_COMPILE_FLAGS_MESSAGE )
 
 
     files = self.GetUnsavedFilesVector()
     files = self.GetUnsavedFilesVector()
-    line, column = vimsupport.CurrentLineAndColumn()
+    line = request_data[ 'line_num' ] + 1
-    # Making the line & column 1-based instead of 0-based
+    column = request_data[ 'start_column' ] + 1
-    line += 1
-    column += 1
     return getattr( self.completer, goto_function )(
     return getattr( self.completer, goto_function )(
         filename,
         filename,
         line,
         line,
@@ -176,39 +174,37 @@ class ClangCompleter( Completer ):
         flags )
         flags )
 
 
 
 
-  def _GoToDefinition( self ):
+  def _GoToDefinition( self, request_data ):
     location = self._LocationForGoTo( 'GetDefinitionLocation' )
     location = self._LocationForGoTo( 'GetDefinitionLocation' )
     if not location or not location.IsValid():
     if not location or not location.IsValid():
-      vimsupport.PostVimMessage( 'Can\'t jump to definition.' )
+      raise RuntimeError( 'Can\'t jump to definition.' )
-      return
 
 
-    vimsupport.JumpToLocation( location.filename_,
+    return server_responses.BuildGoToResponse( location.filename_,
-                               location.line_number_,
+                                               location.line_number_,
-                               location.column_number_ )
+                                               location.column_number_ )
 
 
 
 
-  def _GoToDeclaration( self ):
+  def _GoToDeclaration( self, request_data ):
     location = self._LocationForGoTo( 'GetDeclarationLocation' )
     location = self._LocationForGoTo( 'GetDeclarationLocation' )
     if not location or not location.IsValid():
     if not location or not location.IsValid():
-      vimsupport.PostVimMessage( 'Can\'t jump to declaration.' )
+      raise RuntimeError( 'Can\'t jump to declaration.' )
-      return
 
 
-    vimsupport.JumpToLocation( location.filename_,
+    return server_responses.BuildGoToResponse( location.filename_,
-                               location.line_number_,
+                                               location.line_number_,
-                               location.column_number_ )
+                                               location.column_number_ )
 
 
 
 
-  def _GoToDefinitionElseDeclaration( self ):
+  def _GoToDefinitionElseDeclaration( self, request_data ):
     location = self._LocationForGoTo( 'GetDefinitionLocation' )
     location = self._LocationForGoTo( 'GetDefinitionLocation' )
     if not location or not location.IsValid():
     if not location or not location.IsValid():
       location = self._LocationForGoTo( 'GetDeclarationLocation' )
       location = self._LocationForGoTo( 'GetDeclarationLocation' )
     if not location or not location.IsValid():
     if not location or not location.IsValid():
-      vimsupport.PostVimMessage( 'Can\'t jump to definition or declaration.' )
+      raise RuntimeError( 'Can\'t jump to definition or declaration.' )
-      return
+
+    return server_responses.BuildGoToResponse( location.filename_,
+                                               location.line_number_,
+                                               location.column_number_ )
 
 
-    vimsupport.JumpToLocation( location.filename_,
-                               location.line_number_,
-                               location.column_number_ )
 
 
 
 
   def _ClearCompilationFlagCache( self ):
   def _ClearCompilationFlagCache( self ):
@@ -216,14 +212,18 @@ class ClangCompleter( Completer ):
 
 
 
 
 
 
-  def OnFileReadyToParse( self ):
+  def OnFileReadyToParse( self, request_data ):
-    if vimsupport.NumLinesInBuffer( vim.current.buffer ) < 5:
+    filename = request_data[ 'filepath' ]
+    contents = request_data[ 'file_data' ][ filename ][ 'contents' ]
+    if contents.count( '\n' ) < MIN_LINES_IN_FILE_TO_PARSE:
       self.parse_future = None
       self.parse_future = None
-      return
+      self._logger.warning( FILE_TOO_SHORT_MESSAGE )
+      raise ValueError( FILE_TOO_SHORT_MESSAGE )
 
 
-    filename = vim.current.buffer.name
     if not filename:
     if not filename:
-      return
+      self._logger.warning( INVALID_FILE_MESSAGE )
+      return server_responses.BuildDisplayMessageResponse(
+        INVALID_FILE_MESSAGE )
 
 
     if self.completer.UpdatingTranslationUnit( filename ):
     if self.completer.UpdatingTranslationUnit( filename ):
       self.extra_parse_desired = True
       self.extra_parse_desired = True
@@ -232,11 +232,13 @@ class ClangCompleter( Completer ):
     flags = self.flags.FlagsForFile( filename )
     flags = self.flags.FlagsForFile( filename )
     if not flags:
     if not flags:
       self.parse_future = None
       self.parse_future = None
-      return
+      self._logger.info( NO_COMPILE_FLAGS_MESSAGE )
+      return server_responses.BuildDisplayMessageResponse(
+        NO_COMPILE_FLAGS_MESSAGE )
 
 
     self.parse_future = self.completer.UpdateTranslationUnitAsync(
     self.parse_future = self.completer.UpdateTranslationUnitAsync(
       filename,
       filename,
-      self.GetUnsavedFilesVector(),
+      self.GetUnsavedFilesVector( request_data ),
       flags )
       flags )
 
 
     self.extra_parse_desired = False
     self.extra_parse_desired = False
@@ -253,16 +255,18 @@ class ClangCompleter( Completer ):
     return self.parse_future.ResultsReady()
     return self.parse_future.ResultsReady()
 
 
 
 
-  def GettingCompletions( self ):
+  def GettingCompletions( self, request_data ):
-    return self.completer.UpdatingTranslationUnit( vim.current.buffer.name )
+    return self.completer.UpdatingTranslationUnit( request_data[ 'filepath' ] )
 
 
 
 
-  def GetDiagnosticsForCurrentFile( self ):
+  def GetDiagnosticsForCurrentFile( self, request_data ):
+    filename = request_data[ 'filepath' ]
     if self.DiagnosticsForCurrentFileReady():
     if self.DiagnosticsForCurrentFileReady():
-      diagnostics = self.completer.DiagnosticsForFile( vim.current.buffer.name )
+      diagnostics = self.completer.DiagnosticsForFile( filename )
       self.diagnostic_store = DiagnosticsToDiagStructure( diagnostics )
       self.diagnostic_store = DiagnosticsToDiagStructure( diagnostics )
-      self.last_prepared_diagnostics = [ DiagnosticToDict( x ) for x in
+      self.last_prepared_diagnostics = [
-          diagnostics[ : self.max_diagnostics_to_display ] ]
+        server_responses.BuildDiagnosticData( x ) for x in
+        diagnostics[ : self.max_diagnostics_to_display ] ]
       self.parse_future = None
       self.parse_future = None
 
 
       if self.extra_parse_desired:
       if self.extra_parse_desired:
@@ -271,23 +275,19 @@ class ClangCompleter( Completer ):
     return self.last_prepared_diagnostics
     return self.last_prepared_diagnostics
 
 
 
 
-  def ShowDetailedDiagnostic( self ):
+  def GetDetailedDiagnostic( self, request_data ):
-    current_line, current_column = vimsupport.CurrentLineAndColumn()
+    current_line = request_data[ 'line_num' ] + 1
-
+    current_column = request_data[ 'column_num' ] + 1
-    # CurrentLineAndColumn() numbers are 0-based, clang numbers are 1-based
+    current_file = request_data[ 'filepath' ]
-    current_line += 1
-    current_column += 1
-
-    current_file = vim.current.buffer.name
 
 
     if not self.diagnostic_store:
     if not self.diagnostic_store:
-      vimsupport.PostVimMessage( "No diagnostic for current line!" )
+      return server_responses.BuildDisplayMessageResponse(
-      return
+        NO_DIAGNOSTIC_MESSAGE )
 
 
     diagnostics = self.diagnostic_store[ current_file ][ current_line ]
     diagnostics = self.diagnostic_store[ current_file ][ current_line ]
     if not diagnostics:
     if not diagnostics:
-      vimsupport.PostVimMessage( "No diagnostic for current line!" )
+      return server_responses.BuildDisplayMessageResponse(
-      return
+        NO_DIAGNOSTIC_MESSAGE )
 
 
     closest_diagnostic = None
     closest_diagnostic = None
     distance_to_closest_diagnostic = 999
     distance_to_closest_diagnostic = 999
@@ -298,50 +298,61 @@ class ClangCompleter( Completer ):
         distance_to_closest_diagnostic = distance
         distance_to_closest_diagnostic = distance
         closest_diagnostic = diagnostic
         closest_diagnostic = diagnostic
 
 
-    vimsupport.EchoText( closest_diagnostic.long_formatted_text_ )
+    return server_responses.BuildDisplayMessageResponse(
+      closest_diagnostic.long_formatted_text_ )
 
 
 
 
-  def ShouldUseNow( self, start_column, current_line ):
+  def ShouldUseNow( self, request_data ):
     # We don't want to use the Completer API cache, we use one in the C++ code.
     # We don't want to use the Completer API cache, we use one in the C++ code.
-    return self.ShouldUseNowInner( start_column, current_line )
+    return self.ShouldUseNowInner( request_data )
 
 
 
 
-  def DebugInfo( self ):
+  def DebugInfo( self, request_data ):
-    filename = vim.current.buffer.name
+    filename = request_data[ 'filepath' ]
     if not filename:
     if not filename:
       return ''
       return ''
     flags = self.flags.FlagsForFile( filename ) or []
     flags = self.flags.FlagsForFile( filename ) or []
     source = extra_conf_store.ModuleFileForSourceFile( filename )
     source = extra_conf_store.ModuleFileForSourceFile( filename )
-    return 'Flags for {0} loaded from {1}:\n{2}'.format( filename,
+    return server_responses.BuildDisplayMessageResponse(
-                                                         source,
+      'Flags for {0} loaded from {1}:\n{2}'.format( filename,
-                                                         list( flags ) )
+                                                    source,
+                                                    list( flags ) ) )
 
 
 
 
 # TODO: make these functions module-local
 # TODO: make these functions module-local
-def CompletionDataToDict( completion_data ):
+# def CompletionDataToDict( completion_data ):
-  # see :h complete-items for a description of the dictionary fields
+#   # see :h complete-items for a description of the dictionary fields
-  return {
+#   return {
-    'word' : completion_data.TextToInsertInBuffer(),
+#     'word' : completion_data.TextToInsertInBuffer(),
-    'abbr' : completion_data.MainCompletionText(),
+#     'abbr' : completion_data.MainCompletionText(),
-    'menu' : completion_data.ExtraMenuInfo(),
+#     'menu' : completion_data.ExtraMenuInfo(),
-    'kind' : completion_data.kind_,
+#     'kind' : completion_data.kind_,
-    'info' : completion_data.DetailedInfoForPreviewWindow(),
+#     'info' : completion_data.DetailedInfoForPreviewWindow(),
-    'dup'  : 1,
+#     'dup'  : 1,
-  }
+#   }
-
+
-
+
-def DiagnosticToDict( diagnostic ):
+# def DiagnosticToDict( diagnostic ):
-  # see :h getqflist for a description of the dictionary fields
+#   # see :h getqflist for a description of the dictionary fields
-  return {
+#   return {
-    # TODO: wrap the bufnr generation into a function
+#     # TODO: wrap the bufnr generation into a function
-    'bufnr' : int( vim.eval( "bufnr('{0}', 1)".format(
+#     'bufnr' : int( vim.eval( "bufnr('{0}', 1)".format(
-      diagnostic.filename_ ) ) ),
+#       diagnostic.filename_ ) ) ),
-    'lnum'  : diagnostic.line_number_,
+#     'lnum'  : diagnostic.line_number_,
-    'col'   : diagnostic.column_number_,
+#     'col'   : diagnostic.column_number_,
-    'text'  : diagnostic.text_,
+#     'text'  : diagnostic.text_,
-    'type'  : diagnostic.kind_,
+#     'type'  : diagnostic.kind_,
-    'valid' : 1
+#     'valid' : 1
-  }
+#   }
+
+
+def ConvertCompletionData( completion_data ):
+  return server_responses.BuildCompletionData(
+    insertion_text = completion_data.TextToInsertInBuffer(),
+    menu_text = completion_data.MainCompletionText(),
+    extra_menu_info = completion_data.ExtraMenuInfo(),
+    kind = completion_data.kind_,
+    detailed_info = completion_data.DetailedInfoForPreviewWindow() )
 
 
 
 
 def DiagnosticsToDiagStructure( diagnostics ):
 def DiagnosticsToDiagStructure( diagnostics ):
@@ -352,12 +363,9 @@ def DiagnosticsToDiagStructure( diagnostics ):
   return structure
   return structure
 
 
 
 
-def ClangAvailableForBuffer( buffer_object ):
+def ClangAvailableForFiletypes( filetypes ):
-  filetypes = vimsupport.FiletypesForBuffer( buffer_object )
   return any( [ filetype in CLANG_FILETYPES for filetype in filetypes ] )
   return any( [ filetype in CLANG_FILETYPES for filetype in filetypes ] )
 
 
 
 
-def InCFamilyFile():
+def InCFamilyFile( filetypes ):
-  return any( [ filetype in CLANG_FILETYPES for filetype in
+  return ClangAvailableForFiletypes( filetypes )
-                vimsupport.CurrentFiletypes() ] )
-

+ 3 - 8
python/ycm/completers/cpp/flags.py

@@ -19,12 +19,11 @@
 
 
 import ycm_core
 import ycm_core
 import os
 import os
-from ycm import vimsupport
 from ycm import extra_conf_store
 from ycm import extra_conf_store
 
 
-NO_EXTRA_CONF_FILENAME_MESSAGE = ('No {0} file detected, so no compile flags '
+NO_EXTRA_CONF_FILENAME_MESSAGE = ( 'No {0} file detected, so no compile flags '
   'are available. Thus no semantic support for C/C++/ObjC/ObjC++. Go READ THE '
   'are available. Thus no semantic support for C/C++/ObjC/ObjC++. Go READ THE '
-  'DOCS *NOW*, DON\'T file a bug report.').format(
+  'DOCS *NOW*, DON\'T file a bug report.' ).format(
     extra_conf_store.YCM_EXTRA_CONF_FILENAME )
     extra_conf_store.YCM_EXTRA_CONF_FILENAME )
 
 
 
 
@@ -37,7 +36,6 @@ class Flags( object ):
     # It's caches all the way down...
     # It's caches all the way down...
     self.flags_for_file = {}
     self.flags_for_file = {}
     self.special_clang_flags = _SpecialClangIncludes()
     self.special_clang_flags = _SpecialClangIncludes()
-    self.no_extra_conf_file_warning_posted = False
 
 
 
 
   def FlagsForFile( self, filename, add_special_clang_flags = True ):
   def FlagsForFile( self, filename, add_special_clang_flags = True ):
@@ -46,10 +44,7 @@ class Flags( object ):
     except KeyError:
     except KeyError:
       module = extra_conf_store.ModuleForSourceFile( filename )
       module = extra_conf_store.ModuleForSourceFile( filename )
       if not module:
       if not module:
-        if not self.no_extra_conf_file_warning_posted:
+        raise RuntimeError( NO_EXTRA_CONF_FILENAME_MESSAGE )
-          vimsupport.PostVimMessage( NO_EXTRA_CONF_FILENAME_MESSAGE )
-          self.no_extra_conf_file_warning_posted = True
-        return None
 
 
       results = module.FlagsForFile( filename )
       results = module.FlagsForFile( filename )
 
 

+ 48 - 55
python/ycm/completers/cs/cs_completer.py

@@ -18,18 +18,18 @@
 # You should have received a copy of the GNU General Public License
 # You should have received a copy of the GNU General Public License
 # along with YouCompleteMe.  If not, see <http://www.gnu.org/licenses/>.
 # along with YouCompleteMe.  If not, see <http://www.gnu.org/licenses/>.
 
 
-import vim
 import os
 import os
 from sys import platform
 from sys import platform
 import glob
 import glob
 from ycm.completers.threaded_completer import ThreadedCompleter
 from ycm.completers.threaded_completer import ThreadedCompleter
-from ycm import vimsupport
+from ycm import server_responses
 import urllib2
 import urllib2
 import urllib
 import urllib
 import urlparse
 import urlparse
 import json
 import json
 import subprocess
 import subprocess
 import tempfile
 import tempfile
+import logging
 
 
 
 
 SERVER_NOT_FOUND_MSG = ( 'OmniSharp server binary not found at {0}. ' +
 SERVER_NOT_FOUND_MSG = ( 'OmniSharp server binary not found at {0}. ' +
@@ -44,9 +44,10 @@ class CsharpCompleter( ThreadedCompleter ):
   def __init__( self, user_options ):
   def __init__( self, user_options ):
     super( CsharpCompleter, self ).__init__( user_options )
     super( CsharpCompleter, self ).__init__( user_options )
     self._omnisharp_port = None
     self._omnisharp_port = None
+    self._logger = logging.getLogger(__name__)
 
 
-    if self.user_options[ 'auto_start_csharp_server' ]:
+    # if self.user_options[ 'auto_start_csharp_server' ]:
-      self._StartServer()
+    #   self._StartServer()
 
 
 
 
   def OnVimLeave( self ):
   def OnVimLeave( self ):
@@ -60,11 +61,12 @@ class CsharpCompleter( ThreadedCompleter ):
     return [ 'cs' ]
     return [ 'cs' ]
 
 
 
 
-  def ComputeCandidates( self, unused_query, unused_start_column ):
+  def ComputeCandidates( self, request_data ):
-    return [ { 'word': str( completion[ 'CompletionText' ] ),
+    return [ server_responses.BuildCompletionData(
-               'menu': str( completion[ 'DisplayText' ] ),
+                completion[ 'CompletionText' ],
-               'info': str( completion[ 'Description' ] ) }
+                completion[ 'DisplayText' ],
-             for completion in self._GetCompletions() ]
+                completion[ 'Description' ] )
+             for completion in self._GetCompletions( request_data ) ]
 
 
 
 
   def DefinedSubcommands( self ):
   def DefinedSubcommands( self ):
@@ -76,24 +78,23 @@ class CsharpCompleter( ThreadedCompleter ):
              'GoToDefinitionElseDeclaration' ]
              'GoToDefinitionElseDeclaration' ]
 
 
 
 
-  def OnUserCommand( self, arguments ):
+  def OnUserCommand( self, arguments, request_data ):
     if not arguments:
     if not arguments:
-      self.EchoUserCommandsHelpMessage()
+      raise ValueError( self.UserCommandsHelpMessage() )
-      return
 
 
     command = arguments[ 0 ]
     command = arguments[ 0 ]
     if command == 'StartServer':
     if command == 'StartServer':
-      self._StartServer()
+      self._StartServer( request_data )
     elif command == 'StopServer':
     elif command == 'StopServer':
       self._StopServer()
       self._StopServer()
     elif command == 'RestartServer':
     elif command == 'RestartServer':
       if self._ServerIsRunning():
       if self._ServerIsRunning():
         self._StopServer()
         self._StopServer()
-      self._StartServer()
+      self._StartServer( request_data )
     elif command in [ 'GoToDefinition',
     elif command in [ 'GoToDefinition',
                       'GoToDeclaration',
                       'GoToDeclaration',
                       'GoToDefinitionElseDeclaration' ]:
                       'GoToDefinitionElseDeclaration' ]:
-      self._GoToDefinition()
+      return self._GoToDefinition( request_data )
 
 
 
 
   def DebugInfo( self ):
   def DebugInfo( self ):
@@ -104,35 +105,27 @@ class CsharpCompleter( ThreadedCompleter ):
       return 'Server is not running'
       return 'Server is not running'
 
 
 
 
-  def _StartServer( self ):
+  def _StartServer( self, request_data ):
     """ Start the OmniSharp server """
     """ Start the OmniSharp server """
     self._omnisharp_port = self._FindFreePort()
     self._omnisharp_port = self._FindFreePort()
-    solutionfiles, folder = _FindSolutionFiles()
+    solutionfiles, folder = _FindSolutionFiles( request_data[ 'filepath' ] )
 
 
     if len( solutionfiles ) == 0:
     if len( solutionfiles ) == 0:
-      vimsupport.PostVimMessage(
+      raise RuntimeError(
-              'Error starting OmniSharp server: no solutionfile found' )
+        'Error starting OmniSharp server: no solutionfile found' )
-      return
     elif len( solutionfiles ) == 1:
     elif len( solutionfiles ) == 1:
       solutionfile = solutionfiles[ 0 ]
       solutionfile = solutionfiles[ 0 ]
     else:
     else:
-      choice = vimsupport.PresentDialog(
+      raise RuntimeError(
-              'Which solutionfile should be loaded?',
+        'Found multiple solution files instead of one!\n{}'.format(
-              [ str( i ) + " " + solution for i, solution in
+          solutionfiles ) )
-                enumerate( solutionfiles ) ] )
-      if choice == -1:
-        vimsupport.PostVimMessage( 'OmniSharp not started' )
-        return
-      else:
-        solutionfile = solutionfiles[ choice ]
 
 
     omnisharp = os.path.join(
     omnisharp = os.path.join(
       os.path.abspath( os.path.dirname( __file__ ) ),
       os.path.abspath( os.path.dirname( __file__ ) ),
       'OmniSharpServer/OmniSharp/bin/Debug/OmniSharp.exe' )
       'OmniSharpServer/OmniSharp/bin/Debug/OmniSharp.exe' )
 
 
     if not os.path.isfile( omnisharp ):
     if not os.path.isfile( omnisharp ):
-      vimsupport.PostVimMessage( SERVER_NOT_FOUND_MSG.format( omnisharp ) )
+      raise RuntimeError( SERVER_NOT_FOUND_MSG.format( omnisharp ) )
-      return
 
 
     if not platform.startswith( 'win' ):
     if not platform.startswith( 'win' ):
       omnisharp = 'mono ' + omnisharp
       omnisharp = 'mono ' + omnisharp
@@ -154,40 +147,44 @@ class CsharpCompleter( ThreadedCompleter ):
       with open( self._filename_stdout, 'w' ) as fstdout:
       with open( self._filename_stdout, 'w' ) as fstdout:
         subprocess.Popen( command, stdout=fstdout, stderr=fstderr, shell=True )
         subprocess.Popen( command, stdout=fstdout, stderr=fstderr, shell=True )
 
 
-    vimsupport.PostVimMessage( 'Starting OmniSharp server' )
+    self._logger.info( 'Starting OmniSharp server' )
 
 
 
 
   def _StopServer( self ):
   def _StopServer( self ):
     """ Stop the OmniSharp server """
     """ Stop the OmniSharp server """
     self._GetResponse( '/stopserver' )
     self._GetResponse( '/stopserver' )
     self._omnisharp_port = None
     self._omnisharp_port = None
-    vimsupport.PostVimMessage( 'Stopping OmniSharp server' )
+    self._logger.info( 'Stopping OmniSharp server' )
 
 
 
 
-  def _GetCompletions( self ):
+  def _GetCompletions( self, request_data ):
     """ Ask server for completions """
     """ Ask server for completions """
-    completions = self._GetResponse( '/autocomplete', self._DefaultParameters() )
+    completions = self._GetResponse( '/autocomplete',
+                                     self._DefaultParameters( request_data ) )
     return completions if completions != None else []
     return completions if completions != None else []
 
 
 
 
-  def _GoToDefinition( self ):
+  def _GoToDefinition( self, request_data ):
     """ Jump to definition of identifier under cursor """
     """ Jump to definition of identifier under cursor """
-    definition = self._GetResponse( '/gotodefinition', self._DefaultParameters() )
+    definition = self._GetResponse( '/gotodefinition',
+                                    self._DefaultParameters( request_data ) )
     if definition[ 'FileName' ] != None:
     if definition[ 'FileName' ] != None:
-      vimsupport.JumpToLocation( definition[ 'FileName' ],
+      return server_responses.BuildGoToResponse( definition[ 'FileName' ],
-                                 definition[ 'Line' ],
+                                                 definition[ 'Line' ],
-                                 definition[ 'Column' ] )
+                                                 definition[ 'Column' ] )
     else:
     else:
-      vimsupport.PostVimMessage( 'Can\'t jump to definition' )
+      raise RuntimeError( 'Can\'t jump to definition' )
 
 
 
 
-  def _DefaultParameters( self ):
+  def _DefaultParameters( self, request_data ):
     """ Some very common request parameters """
     """ Some very common request parameters """
-    line, column = vimsupport.CurrentLineAndColumn()
     parameters = {}
     parameters = {}
-    parameters[ 'line' ], parameters[ 'column' ] = line + 1, column + 1
+    parameters[ 'line' ] = request_data[ 'line_num' ] + 1
-    parameters[ 'buffer' ] = '\n'.join( vim.current.buffer )
+    parameters[ 'column' ] = request_data[ 'column_num' ] + 1
-    parameters[ 'filename' ] = vim.current.buffer.name
+    filepath = request_data[ 'filepath' ]
+    parameters[ 'buffer' ] = request_data[ 'file_data' ][ filepath ][
+      'contents' ]
+    parameters[ 'filename' ] = filepath
     return parameters
     return parameters
 
 
 
 
@@ -215,20 +212,16 @@ class CsharpCompleter( ThreadedCompleter ):
 
 
   def _GetResponse( self, endPoint, parameters={}, silent=False, port=None ):
   def _GetResponse( self, endPoint, parameters={}, silent=False, port=None ):
     """ Handle communication with server """
     """ Handle communication with server """
+    # TODO: Replace usage of urllib with Requests
     target = urlparse.urljoin( self._PortToHost( port ), endPoint )
     target = urlparse.urljoin( self._PortToHost( port ), endPoint )
     parameters = urllib.urlencode( parameters )
     parameters = urllib.urlencode( parameters )
-    try:
+    response = urllib2.urlopen( target, parameters )
-      response = urllib2.urlopen( target, parameters )
+    return json.loads( response.read() )
-      return json.loads( response.read() )
-    except Exception:
-      # TODO: Add logging for this case. We can't post a Vim message because Vim
-      # crashes when that's done from a no-GUI thread.
-      return None
 
 
 
 
-def _FindSolutionFiles():
+def _FindSolutionFiles( filepath ):
   """ Find solution files by searching upwards in the file tree """
   """ Find solution files by searching upwards in the file tree """
-  folder = os.path.dirname( vim.current.buffer.name )
+  folder = os.path.dirname( filepath )
   solutionfiles = glob.glob1( folder, '*.sln' )
   solutionfiles = glob.glob1( folder, '*.sln' )
   while not solutionfiles:
   while not solutionfiles:
     lastfolder = folder
     lastfolder = folder

+ 32 - 19
python/ycm/completers/general/filename_completer.py

@@ -16,13 +16,13 @@
 # You should have received a copy of the GNU General Public License
 # You should have received a copy of the GNU General Public License
 # along with YouCompleteMe.  If not, see <http://www.gnu.org/licenses/>.
 # along with YouCompleteMe.  If not, see <http://www.gnu.org/licenses/>.
 
 
-import vim
 import os
 import os
 import re
 import re
 
 
 from ycm.completers.threaded_completer import ThreadedCompleter
 from ycm.completers.threaded_completer import ThreadedCompleter
 from ycm.completers.cpp.clang_completer import InCFamilyFile
 from ycm.completers.cpp.clang_completer import InCFamilyFile
 from ycm.completers.cpp.flags import Flags
 from ycm.completers.cpp.flags import Flags
+from ycm import server_responses
 
 
 class FilenameCompleter( ThreadedCompleter ):
 class FilenameCompleter( ThreadedCompleter ):
   """
   """
@@ -52,23 +52,32 @@ class FilenameCompleter( ThreadedCompleter ):
     self._include_regex = re.compile( include_regex_common )
     self._include_regex = re.compile( include_regex_common )
 
 
 
 
-  def AtIncludeStatementStart( self, start_column ):
+  def AtIncludeStatementStart( self, request_data ):
-    return ( InCFamilyFile() and
+    start_column = request_data[ 'start_column' ]
+    current_line = request_data[ 'line_value' ]
+    filepath = request_data[ 'filepath' ]
+    filetypes = request_data[ 'file_data' ][ filepath ][ 'filetypes' ]
+    return ( InCFamilyFile( filetypes ) and
              self._include_start_regex.match(
              self._include_start_regex.match(
-               vim.current.line[ :start_column ] ) )
+               current_line[ :start_column ] ) )
 
 
 
 
-  def ShouldUseNowInner( self, start_column, current_line ):
+  def ShouldUseNowInner( self, request_data ):
+    start_column = request_data[ 'start_column' ]
+    current_line = request_data[ 'line_value' ]
     return ( start_column and ( current_line[ start_column - 1 ] == '/' or
     return ( start_column and ( current_line[ start_column - 1 ] == '/' or
-             self.AtIncludeStatementStart( start_column ) ) )
+             self.AtIncludeStatementStart( request_data ) ) )
 
 
 
 
   def SupportedFiletypes( self ):
   def SupportedFiletypes( self ):
     return []
     return []
 
 
 
 
-  def ComputeCandidates( self, unused_query, start_column ):
+  def ComputeCandidates( self, request_data ):
-    line = vim.current.line[ :start_column ]
+    current_line = request_data[ 'line_value' ]
+    start_column = request_data[ 'start_column' ]
+    filepath = request_data[ 'filepath' ]
+    line = current_line[ :start_column ]
 
 
     if InCFamilyFile():
     if InCFamilyFile():
       include_match = self._include_regex.search( line )
       include_match = self._include_regex.search( line )
@@ -78,22 +87,26 @@ class FilenameCompleter( ThreadedCompleter ):
         # http://gcc.gnu.org/onlinedocs/cpp/Include-Syntax.html
         # http://gcc.gnu.org/onlinedocs/cpp/Include-Syntax.html
         include_current_file_dir = '<' not in include_match.group()
         include_current_file_dir = '<' not in include_match.group()
         return _GenerateCandidatesForPaths(
         return _GenerateCandidatesForPaths(
-          self.GetPathsIncludeCase( path_dir, include_current_file_dir ) )
+          self.GetPathsIncludeCase( path_dir,
+                                    include_current_file_dir,
+                                    filepath ) )
 
 
     path_match = self._path_regex.search( line )
     path_match = self._path_regex.search( line )
     path_dir = os.path.expanduser( path_match.group() ) if path_match else ''
     path_dir = os.path.expanduser( path_match.group() ) if path_match else ''
 
 
     return _GenerateCandidatesForPaths(
     return _GenerateCandidatesForPaths(
-      _GetPathsStandardCase( path_dir, self.user_options[
+      _GetPathsStandardCase(
-        'filepath_completion_use_working_dir' ] ) )
+        path_dir,
+        self.user_options[ 'filepath_completion_use_working_dir' ],
+        filepath ) )
 
 
 
 
-  def GetPathsIncludeCase( self, path_dir, include_current_file_dir ):
+  def GetPathsIncludeCase( self, path_dir, include_current_file_dir, filepath ):
     paths = []
     paths = []
-    include_paths = self._flags.UserIncludePaths( vim.current.buffer.name )
+    include_paths = self._flags.UserIncludePaths( filepath )
 
 
     if include_current_file_dir:
     if include_current_file_dir:
-      include_paths.append( os.path.dirname( vim.current.buffer.name ) )
+      include_paths.append( os.path.dirname( filepath ) )
 
 
     for include_path in include_paths:
     for include_path in include_paths:
       try:
       try:
@@ -107,9 +120,9 @@ class FilenameCompleter( ThreadedCompleter ):
     return sorted( set( paths ) )
     return sorted( set( paths ) )
 
 
 
 
-def _GetPathsStandardCase( path_dir, use_working_dir ):
+def _GetPathsStandardCase( path_dir, use_working_dir, filepath ):
   if not use_working_dir and not path_dir.startswith( '/' ):
   if not use_working_dir and not path_dir.startswith( '/' ):
-    path_dir = os.path.join( os.path.dirname( vim.current.buffer.name ),
+    path_dir = os.path.join( os.path.dirname( filepath ),
                              path_dir )
                              path_dir )
 
 
   try:
   try:
@@ -132,8 +145,8 @@ def _GenerateCandidatesForPaths( absolute_paths ):
     seen_basenames.add( basename )
     seen_basenames.add( basename )
 
 
     is_dir = os.path.isdir( absolute_path )
     is_dir = os.path.isdir( absolute_path )
-    completion_dicts.append( { 'word': basename,
+    completion_dicts.append(
-                               'dup': 1,
+      server_responses.BuildCompletionData( basename,
-                               'menu': '[Dir]' if is_dir else '[File]' } )
+                                            '[Dir]' if is_dir else '[File]' ) )
 
 
   return completion_dicts
   return completion_dicts

+ 7 - 8
python/ycm/completers/general/general_completer_store.py

@@ -58,18 +58,17 @@ class GeneralCompleterStore( Completer ):
     return set()
     return set()
 
 
 
 
-  def ShouldUseNow( self, start_column, current_line ):
+  def ShouldUseNow( self, request_data ):
     self._current_query_completers = []
     self._current_query_completers = []
 
 
-    if self._filename_completer.ShouldUseNow( start_column, current_line ):
+    if self._filename_completer.ShouldUseNow( request_data ):
       self._current_query_completers = [ self._filename_completer ]
       self._current_query_completers = [ self._filename_completer ]
       return True
       return True
 
 
     should_use_now = False
     should_use_now = False
 
 
     for completer in self._non_filename_completers:
     for completer in self._non_filename_completers:
-      should_use_this_completer = completer.ShouldUseNow( start_column,
+      should_use_this_completer = completer.ShouldUseNow( request_data )
-                                                          current_line )
       should_use_now = should_use_now or should_use_this_completer
       should_use_now = should_use_now or should_use_this_completer
 
 
       if should_use_this_completer:
       if should_use_this_completer:
@@ -78,9 +77,9 @@ class GeneralCompleterStore( Completer ):
     return should_use_now
     return should_use_now
 
 
 
 
-  def CandidatesForQueryAsync( self, query, start_column ):
+  def CandidatesForQueryAsync( self, request_data ):
     for completer in self._current_query_completers:
     for completer in self._current_query_completers:
-      completer.CandidatesForQueryAsync( query, start_column )
+      completer.CandidatesForQueryAsync( request_data )
 
 
 
 
   def AsyncCandidateRequestReady( self ):
   def AsyncCandidateRequestReady( self ):
@@ -96,9 +95,9 @@ class GeneralCompleterStore( Completer ):
     return candidates
     return candidates
 
 
 
 
-  def OnFileReadyToParse( self ):
+  def OnFileReadyToParse( self, request_data ):
     for completer in self._all_completers:
     for completer in self._all_completers:
-      completer.OnFileReadyToParse()
+      completer.OnFileReadyToParse( request_data )
 
 
 
 
   def OnBufferVisit( self ):
   def OnBufferVisit( self ):

+ 10 - 8
python/ycm/completers/general/ultisnips_completer.py

@@ -20,6 +20,7 @@
 
 
 from ycm.completers.general_completer import GeneralCompleter
 from ycm.completers.general_completer import GeneralCompleter
 from UltiSnips import UltiSnips_Manager
 from UltiSnips import UltiSnips_Manager
+from ycm import server_responses
 
 
 
 
 class UltiSnipsCompleter( GeneralCompleter ):
 class UltiSnipsCompleter( GeneralCompleter ):
@@ -33,13 +34,13 @@ class UltiSnipsCompleter( GeneralCompleter ):
     self._filtered_candidates = None
     self._filtered_candidates = None
 
 
 
 
-  def ShouldUseNowInner( self, start_column, unused_current_line ):
+  def ShouldUseNowInner( self, request_data ):
-    return self.QueryLengthAboveMinThreshold( start_column )
+    return self.QueryLengthAboveMinThreshold( request_data )
 
 
 
 
-  def CandidatesForQueryAsync( self, query, unused_start_column ):
+  def CandidatesForQueryAsync( self, request_data ):
-    self._filtered_candidates = self.FilterAndSortCandidates( self._candidates,
+    self._filtered_candidates = self.FilterAndSortCandidates(
-                                                              query )
+      self._candidates, request_data[ 'query' ] )
 
 
 
 
   def AsyncCandidateRequestReady( self ):
   def AsyncCandidateRequestReady( self ):
@@ -61,8 +62,9 @@ def _GetCandidates():
     # UltiSnips_Manager._snips() returns a class instance where:
     # UltiSnips_Manager._snips() returns a class instance where:
     # class.trigger - name of snippet trigger word ( e.g. defn or testcase )
     # class.trigger - name of snippet trigger word ( e.g. defn or testcase )
     # class.description - description of the snippet
     # class.description - description of the snippet
-    return  [ { 'word': str( snip.trigger ),
+    return [ server_responses.BuildCompletionData(
-                'menu': str( '<snip> ' + snip.description.encode('utf-8') ) }
+              str( snip.trigger ),
-              for snip in rawsnips ]
+              str( '<snip> ' + snip.description.encode( 'utf-8' ) ) )
+            for snip in rawsnips ]
   except:
   except:
     return []
     return []

+ 49 - 55
python/ycm/completers/python/jedi_completer.py

@@ -19,9 +19,8 @@
 # You should have received a copy of the GNU General Public License
 # You should have received a copy of the GNU General Public License
 # along with YouCompleteMe.  If not, see <http://www.gnu.org/licenses/>.
 # along with YouCompleteMe.  If not, see <http://www.gnu.org/licenses/>.
 
 
-import vim
 from ycm.completers.threaded_completer import ThreadedCompleter
 from ycm.completers.threaded_completer import ThreadedCompleter
-from ycm import vimsupport
+from ycm import server_responses
 
 
 import sys
 import sys
 from os.path import join, abspath, dirname
 from os.path import join, abspath, dirname
@@ -33,7 +32,7 @@ sys.path.insert( 0, join( abspath( dirname( __file__ ) ), 'jedi' ) )
 try:
 try:
   import jedi
   import jedi
 except ImportError:
 except ImportError:
-  vimsupport.PostVimMessage(
+  raise ImportError(
     'Error importing jedi. Make sure the jedi submodule has been checked out. '
     'Error importing jedi. Make sure the jedi submodule has been checked out. '
     'In the YouCompleteMe folder, run "git submodule update --init --recursive"')
     'In the YouCompleteMe folder, run "git submodule update --init --recursive"')
 sys.path.pop( 0 )
 sys.path.pop( 0 )
@@ -54,113 +53,108 @@ class JediCompleter( ThreadedCompleter ):
     return [ 'python' ]
     return [ 'python' ]
 
 
 
 
-  def _GetJediScript( self ):
+  def _GetJediScript( self, request_data ):
-      contents = '\n'.join( vim.current.buffer )
+      filename = request_data[ 'filepath' ]
-      line, column = vimsupport.CurrentLineAndColumn()
+      contents = request_data[ 'file_data' ][ filename ][ 'contents' ]
       # Jedi expects lines to start at 1, not 0
       # Jedi expects lines to start at 1, not 0
-      line += 1
+      line = request_data[ 'line_num' ] + 1
-      filename = vim.current.buffer.name
+      column = request_data[ 'column_num' ]
+      print contents
 
 
       return jedi.Script( contents, line, column, filename )
       return jedi.Script( contents, line, column, filename )
 
 
 
 
-  def ComputeCandidates( self, unused_query, unused_start_column ):
+  def ComputeCandidates( self, request_data ):
-    script = self._GetJediScript()
+    script = self._GetJediScript( request_data )
-
+    return [ server_responses.BuildCompletionData( completion.name,
-    return [ { 'word': str( completion.name ),
+                                                   completion.description,
-               'menu': str( completion.description ),
+                                                   completion.doc )
-               'info': str( completion.doc ) }
              for completion in script.completions() ]
              for completion in script.completions() ]
 
 
-
   def DefinedSubcommands( self ):
   def DefinedSubcommands( self ):
     return [ "GoToDefinition",
     return [ "GoToDefinition",
              "GoToDeclaration",
              "GoToDeclaration",
              "GoToDefinitionElseDeclaration" ]
              "GoToDefinitionElseDeclaration" ]
 
 
 
 
-  def OnUserCommand( self, arguments ):
+  def OnUserCommand( self, arguments, request_data ):
     if not arguments:
     if not arguments:
-      self.EchoUserCommandsHelpMessage()
+      raise ValueError( self.UserCommandsHelpMessage() )
-      return
 
 
     command = arguments[ 0 ]
     command = arguments[ 0 ]
     if command == 'GoToDefinition':
     if command == 'GoToDefinition':
-      self._GoToDefinition()
+      return self._GoToDefinition( request_data )
     elif command == 'GoToDeclaration':
     elif command == 'GoToDeclaration':
-      self._GoToDeclaration()
+      return self._GoToDeclaration( request_data )
     elif command == 'GoToDefinitionElseDeclaration':
     elif command == 'GoToDefinitionElseDeclaration':
-      self._GoToDefinitionElseDeclaration()
+      return self._GoToDefinitionElseDeclaration( request_data )
 
 
 
 
-  def _GoToDefinition( self ):
+  def _GoToDefinition( self, request_data ):
-    definitions = self._GetDefinitionsList()
+    definitions = self._GetDefinitionsList( request_data )
     if definitions:
     if definitions:
-      self._JumpToLocation( definitions )
+      return self._BuildGoToResponse( definitions )
     else:
     else:
-      vimsupport.PostVimMessage( 'Can\'t jump to definition.' )
+      raise RuntimeError( 'Can\'t jump to definition.' )
 
 
 
 
-  def _GoToDeclaration( self ):
+  def _GoToDeclaration( self, request_data ):
-    definitions = self._GetDefinitionsList( declaration = True )
+    definitions = self._GetDefinitionsList( request_data, declaration = True )
     if definitions:
     if definitions:
-      self._JumpToLocation( definitions )
+      return self._BuildGoToResponse( definitions )
     else:
     else:
-      vimsupport.PostVimMessage( 'Can\'t jump to declaration.' )
+      raise RuntimeError( 'Can\'t jump to declaration.' )
 
 
 
 
-  def _GoToDefinitionElseDeclaration( self ):
+  def _GoToDefinitionElseDeclaration( self, request_data ):
     definitions = self._GetDefinitionsList() or \
     definitions = self._GetDefinitionsList() or \
-        self._GetDefinitionsList( declaration = True )
+        self._GetDefinitionsList( request_data, declaration = True )
     if definitions:
     if definitions:
-      self._JumpToLocation( definitions )
+      return self._BuildGoToResponse( definitions )
     else:
     else:
-      vimsupport.PostVimMessage( 'Can\'t jump to definition or declaration.' )
+      raise RuntimeError( 'Can\'t jump to definition or declaration.' )
 
 
 
 
-  def _GetDefinitionsList( self, declaration = False ):
+  def _GetDefinitionsList( self, request_data, declaration = False ):
     definitions = []
     definitions = []
-    script = self._GetJediScript()
+    script = self._GetJediScript( request_data )
     try:
     try:
       if declaration:
       if declaration:
         definitions = script.goto_definitions()
         definitions = script.goto_definitions()
       else:
       else:
         definitions = script.goto_assignments()
         definitions = script.goto_assignments()
     except jedi.NotFoundError:
     except jedi.NotFoundError:
-      vimsupport.PostVimMessage(
+      raise RuntimeError(
-                  "Cannot follow nothing. Put your cursor on a valid name." )
+                  'Cannot follow nothing. Put your cursor on a valid name.' )
-    except Exception as e:
-      vimsupport.PostVimMessage(
-                  "Caught exception, aborting. Full error: " + str( e ) )
 
 
     return definitions
     return definitions
 
 
 
 
-  def _JumpToLocation( self, definition_list ):
+  def _BuildGoToResponse( self, definition_list ):
     if len( definition_list ) == 1:
     if len( definition_list ) == 1:
       definition = definition_list[ 0 ]
       definition = definition_list[ 0 ]
       if definition.in_builtin_module():
       if definition.in_builtin_module():
         if definition.is_keyword:
         if definition.is_keyword:
-          vimsupport.PostVimMessage(
+          raise RuntimeError(
-                  "Cannot get the definition of Python keywords." )
+                  'Cannot get the definition of Python keywords.' )
         else:
         else:
-          vimsupport.PostVimMessage( "Builtin modules cannot be displayed." )
+          raise RuntimeError( 'Builtin modules cannot be displayed.' )
       else:
       else:
-        vimsupport.JumpToLocation( definition.module_path,
+        return server_responses.BuildGoToResponse( definition.module_path,
-                                   definition.line,
+                                                   definition.line -1,
-                                   definition.column + 1 )
+                                                   definition.column )
     else:
     else:
       # multiple definitions
       # multiple definitions
       defs = []
       defs = []
       for definition in definition_list:
       for definition in definition_list:
         if definition.in_builtin_module():
         if definition.in_builtin_module():
-          defs.append( {'text': 'Builtin ' + \
+          defs.append( server_responses.BuildDescriptionOnlyGoToResponse(
-                       definition.description.encode( 'utf-8' ) } )
+                       'Builting ' + definition.description ) )
         else:
         else:
-          defs.append( {'filename': definition.module_path.encode( 'utf-8' ),
+          defs.append(
-                        'lnum': definition.line,
+            server_responses.BuildGoToResponse( definition.module_path,
-                        'col': definition.column + 1,
+                                                definition.line -1,
-                        'text': definition.description.encode( 'utf-8' ) } )
+                                                definition.column,
+                                                definition.description ) )
+      return defs
 
 
-      vim.eval( 'setqflist( %s )' % repr( defs ) )
-      vim.eval( 'youcompleteme#OpenGoToList()' )

+ 4 - 6
python/ycm/completers/threaded_completer.py

@@ -52,11 +52,10 @@ class ThreadedCompleter( Completer ):
     self._completion_thread.start()
     self._completion_thread.start()
 
 
 
 
-  def CandidatesForQueryAsyncInner( self, query, start_column ):
+  def CandidatesForQueryAsyncInner( self, request_data ):
     self._candidates = None
     self._candidates = None
     self._candidates_ready.clear()
     self._candidates_ready.clear()
-    self._query = query
+    self._request_data = request_data
-    self._start_column = start_column
     self._query_ready.set()
     self._query_ready.set()
 
 
 
 
@@ -69,7 +68,7 @@ class ThreadedCompleter( Completer ):
 
 
 
 
   @abc.abstractmethod
   @abc.abstractmethod
-  def ComputeCandidates( self, query, start_column ):
+  def ComputeCandidates( self, request_data ):
     """This function should compute the candidates to show to the user.
     """This function should compute the candidates to show to the user.
     The return value should be of the same type as that for
     The return value should be of the same type as that for
     CandidatesFromStoredRequest()."""
     CandidatesFromStoredRequest()."""
@@ -80,8 +79,7 @@ class ThreadedCompleter( Completer ):
     while True:
     while True:
       try:
       try:
         WaitAndClearIfSet( self._query_ready )
         WaitAndClearIfSet( self._query_ready )
-        self._candidates = self.ComputeCandidates( self._query,
+        self._candidates = self.ComputeCandidates( self._request_data )
-                                                   self._start_column )
       except:
       except:
         self._query_ready.clear()
         self._query_ready.clear()
         self._candidates = []
         self._candidates = []

+ 1 - 0
python/ycm/extra_conf_store.py

@@ -68,6 +68,7 @@ def CallExtraConfVimCloseIfExists():
 def _CallExtraConfMethod( function_name ):
 def _CallExtraConfMethod( function_name ):
   vim_current_working_directory = vim.eval( 'getcwd()' )
   vim_current_working_directory = vim.eval( 'getcwd()' )
   path_to_dummy = os.path.join( vim_current_working_directory, 'DUMMY_FILE' )
   path_to_dummy = os.path.join( vim_current_working_directory, 'DUMMY_FILE' )
+  # The dummy file in the Vim CWD ensures we find the correct extra conf file
   module = ModuleForSourceFile( path_to_dummy )
   module = ModuleForSourceFile( path_to_dummy )
   if not module or not hasattr( module, function_name ):
   if not module or not hasattr( module, function_name ):
     return
     return

+ 76 - 0
python/ycm/server_responses.py

@@ -0,0 +1,76 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2013  Strahinja Val Markovic <val@markovic.io>
+#
+# This file is part of YouCompleteMe.
+#
+# YouCompleteMe is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# YouCompleteMe is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with YouCompleteMe.  If not, see <http://www.gnu.org/licenses/>.
+
+
+def BuildGoToResponse( filepath, line_num, column_num, description = None ):
+  response = {
+    'filepath': filepath,
+    'line_num': line_num,
+    'column_num': column_num
+  }
+
+  if description:
+    response[ 'description' ] = description
+  return response
+
+
+def BuildDescriptionOnlyGoToResponse( text ):
+  return {
+    'description': text,
+  }
+
+
+def BuildDisplayMessageResponse( text ):
+  return {
+    'message': text
+  }
+
+
+def BuildCompletionData( insertion_text,
+                         extra_menu_info = None,
+                         detailed_info = None,
+                         menu_text = None,
+                         kind = None ):
+  completion_data = {
+    'insertion_text': insertion_text
+  }
+
+  if extra_menu_info:
+    completion_data[ 'extra_menu_info' ] = extra_menu_info
+  if menu_text:
+    completion_data[ 'menu_text' ] = menu_text
+  if detailed_info:
+    completion_data[ 'detailed_info' ] = detailed_info
+  if kind:
+    completion_data[ 'kind' ] = kind
+  return completion_data
+
+
+def BuildDiagnosticData( filepath,
+                         line_num,
+                         column_num,
+                         text,
+                         kind ):
+  return {
+    'filepath': filepath,
+    'line_num': line_num,
+    'column_num': column_num,
+    'text': text,
+    'kind': kind
+  }

+ 30 - 11
python/ycm/vimsupport.py

@@ -46,12 +46,33 @@ def TextAfterCursor():
   return vim.current.line[ CurrentColumn(): ]
   return vim.current.line[ CurrentColumn(): ]
 
 
 
 
-def GetUnsavedBuffers():
+# Note the difference between buffer OPTIONS and VARIABLES; the two are not
-  def BufferModified( buffer_number ):
+# the same.
-    to_eval = 'getbufvar({0}, "&mod")'.format( buffer_number )
+def GetBufferOption( buffer_object, option ):
-    return GetBoolValue( to_eval )
+  # The 'options' property is only available in recent (7.4+) Vim builds
+  if hasattr( buffer_object, 'options' ):
+    return buffer_object.options[ option ]
 
 
-  return ( x for x in vim.buffers if BufferModified( x.number ) )
+  to_eval = 'getbufvar({0}, "&{1}")'.format( buffer.number, option )
+  return GetVariableValue( to_eval )
+
+
+def GetUnsavedAndCurrentBufferData():
+  def BufferModified( buffer_object ):
+    return bool( int( GetBufferOption( buffer_object, 'mod' ) ) )
+
+  buffers_data = {}
+  for buffer_object in vim.buffers:
+    if not ( BufferModified( buffer_object ) or
+             buffer_object == vim.current.buffer ):
+      continue
+
+    buffers_data[ buffer_object.name ] = {
+      'contents': '\n'.join( buffer_object ),
+      'filetypes': FiletypesForBuffer( buffer_object )
+    }
+
+  return buffers_data
 
 
 
 
 # Both |line| and |column| need to be 1-based
 # Both |line| and |column| need to be 1-based
@@ -73,9 +94,9 @@ def JumpToLocation( filename, line, column ):
   vim.command( 'normal! zz' )
   vim.command( 'normal! zz' )
 
 
 
 
-def NumLinesInBuffer( buffer ):
+def NumLinesInBuffer( buffer_object ):
   # This is actually less than obvious, that's why it's wrapped in a function
   # This is actually less than obvious, that's why it's wrapped in a function
-  return len( buffer )
+  return len( buffer_object )
 
 
 
 
 def PostVimMessage( message ):
 def PostVimMessage( message ):
@@ -128,15 +149,13 @@ def EscapeForVim( text ):
 
 
 
 
 def CurrentFiletypes():
 def CurrentFiletypes():
-  ft_string = vim.eval( "&filetype" )
+  return vim.eval( "&filetype" ).split( '.' )
-  return ft_string.split( '.' )
 
 
 
 
 def FiletypesForBuffer( buffer_object ):
 def FiletypesForBuffer( buffer_object ):
   # NOTE: Getting &ft for other buffers only works when the buffer has been
   # NOTE: Getting &ft for other buffers only works when the buffer has been
   # visited by the user at least once, which is true for modified buffers
   # visited by the user at least once, which is true for modified buffers
-  ft_string = vim.eval( 'getbufvar({0}, "&ft")'.format( buffer_object.number ) )
+  return GetBufferOption( buffer_object, 'ft' ).split( '.' )
-  return ft_string.split( '.' )
 
 
 
 
 def GetVariableValue( variable ):
 def GetVariableValue( variable ):

+ 122 - 15
python/ycm/youcompleteme.py

@@ -19,20 +19,25 @@
 
 
 import imp
 import imp
 import os
 import os
+import time
 import vim
 import vim
 import ycm_core
 import ycm_core
+import logging
+import tempfile
 from ycm import vimsupport
 from ycm import vimsupport
 from ycm import base
 from ycm import base
 from ycm.completers.all.omni_completer import OmniCompleter
 from ycm.completers.all.omni_completer import OmniCompleter
 from ycm.completers.general.general_completer_store import GeneralCompleterStore
 from ycm.completers.general.general_completer_store import GeneralCompleterStore
 
 
 
 
+# TODO: Put the Request classes in separate files
 class CompletionRequest( object ):
 class CompletionRequest( object ):
   def __init__( self, ycm_state ):
   def __init__( self, ycm_state ):
     self._completion_start_column = base.CompletionStartColumn()
     self._completion_start_column = base.CompletionStartColumn()
     self._ycm_state = ycm_state
     self._ycm_state = ycm_state
+    self._request_data = _BuildRequestData( self._completion_start_column )
     self._do_filetype_completion = self._ycm_state.ShouldUseFiletypeCompleter(
     self._do_filetype_completion = self._ycm_state.ShouldUseFiletypeCompleter(
-      self._completion_start_column )
+      self._request_data )
     self._completer = ( self._ycm_state.GetFiletypeCompleter() if
     self._completer = ( self._ycm_state.GetFiletypeCompleter() if
                         self._do_filetype_completion else
                         self._do_filetype_completion else
                         self._ycm_state.GetGeneralCompleter() )
                         self._ycm_state.GetGeneralCompleter() )
@@ -40,8 +45,7 @@ class CompletionRequest( object ):
 
 
   def ShouldComplete( self ):
   def ShouldComplete( self ):
     return ( self._do_filetype_completion or
     return ( self._do_filetype_completion or
-             self._ycm_state.ShouldUseGeneralCompleter(
+             self._ycm_state.ShouldUseGeneralCompleter( self._request_data ) )
-               self._completion_start_column ) )
 
 
 
 
   def CompletionStartColumn( self ):
   def CompletionStartColumn( self ):
@@ -49,20 +53,80 @@ class CompletionRequest( object ):
 
 
 
 
   def Start( self, query ):
   def Start( self, query ):
-    self._completer.CandidatesForQueryAsync( query,
+    self._request_data[ 'query' ] = query
-                                             self._completion_start_column )
+    self._completer.CandidatesForQueryAsync( self._request_data )
 
 
   def Done( self ):
   def Done( self ):
     return self._completer.AsyncCandidateRequestReady()
     return self._completer.AsyncCandidateRequestReady()
 
 
 
 
   def Results( self ):
   def Results( self ):
-    return self._completer.CandidatesFromStoredRequest()
+    try:
+      return [ _ConvertCompletionDataToVimData( x )
+              for x in self._completer.CandidatesFromStoredRequest() ]
+    except Exception as e:
+      vimsupport.PostVimMessage( str( e ) )
+      return []
+
+
+
+class CommandRequest( object ):
+  class ServerResponse( object ):
+    def __init__( self ):
+      pass
+
+    def Valid( self ):
+      return True
+
+  def __init__( self, ycm_state, arguments, completer_target = None ):
+    if not completer_target:
+      completer_target = 'filetpe_default'
+
+    if completer_target == 'omni':
+      self._completer = ycm_state.GetOmniCompleter()
+    elif completer_target == 'identifier':
+      self._completer = ycm_state.GetGeneralCompleter()
+    else:
+      self._completer = ycm_state.GetFiletypeCompleter()
+    self._arguments = arguments
+
+
+  def Start( self ):
+    self._completer.OnUserCommand( self._arguments,
+                                   _BuildRequestData() )
+
+  def Done( self ):
+    return True
+
+
+  def Response( self ):
+    # TODO: Call vimsupport.JumpToLocation if the user called a GoTo command...
+    # we may want to have specific subclasses of CommandRequest so that a
+    # GoToRequest knows it needs to jump after the data comes back.
+    #
+    # Also need to run the following on GoTo data:
+    # CAREFUL about line/column number 0-based/1-based confusion!
+    #
+    # defs = []
+    # defs.append( {'filename': definition.module_path.encode( 'utf-8' ),
+    #               'lnum': definition.line,
+    #               'col': definition.column + 1,
+    #               'text': definition.description.encode( 'utf-8' ) } )
+    # vim.eval( 'setqflist( %s )' % repr( defs ) )
+    # vim.eval( 'youcompleteme#OpenGoToList()' )
+    return self.ServerResponse()
 
 
 
 
 
 
 class YouCompleteMe( object ):
 class YouCompleteMe( object ):
   def __init__( self, user_options ):
   def __init__( self, user_options ):
+    # TODO: This should go into the server
+    # TODO: Use more logging like we do in cs_completer
+    self._logfile = tempfile.NamedTemporaryFile()
+    logging.basicConfig( format='%(asctime)s - %(levelname)s - %(message)s',
+                         filename=self._logfile.name,
+                         level=logging.DEBUG )
+
     self._user_options = user_options
     self._user_options = user_options
     self._gencomp = GeneralCompleterStore( user_options )
     self._gencomp = GeneralCompleterStore( user_options )
     self._omnicomp = OmniCompleter( user_options )
     self._omnicomp = OmniCompleter( user_options )
@@ -78,6 +142,16 @@ class YouCompleteMe( object ):
     return self._current_completion_request
     return self._current_completion_request
 
 
 
 
+  def SendCommandRequest( self, arguments, completer ):
+    # TODO: This should be inside a method in a command_request module
+    request = CommandRequest( self, arguments, completer )
+    request.Start()
+    while not request.Done():
+      time.sleep( 0.1 )
+
+    return request.Response()
+
+
   def GetCurrentCompletionRequest( self ):
   def GetCurrentCompletionRequest( self ):
     return self._current_completion_request
     return self._current_completion_request
 
 
@@ -131,14 +205,13 @@ class YouCompleteMe( object ):
     return completer
     return completer
 
 
 
 
-  def ShouldUseGeneralCompleter( self, start_column ):
+  def ShouldUseGeneralCompleter( self, request_data ):
-    return self._gencomp.ShouldUseNow( start_column, vim.current.line )
+    return self._gencomp.ShouldUseNow( request_data )
 
 
 
 
-  def ShouldUseFiletypeCompleter( self, start_column ):
+  def ShouldUseFiletypeCompleter( self, request_data ):
     if self.FiletypeCompletionUsable():
     if self.FiletypeCompletionUsable():
-      return self.GetFiletypeCompleter().ShouldUseNow(
+      return self.GetFiletypeCompleter().ShouldUseNow( request_data )
-        start_column, vim.current.line )
     return False
     return False
 
 
 
 
@@ -162,10 +235,10 @@ class YouCompleteMe( object ):
 
 
 
 
   def OnFileReadyToParse( self ):
   def OnFileReadyToParse( self ):
-    self._gencomp.OnFileReadyToParse()
+    self._gencomp.OnFileReadyToParse( _BuildRequestData() )
 
 
     if self.FiletypeCompletionUsable():
     if self.FiletypeCompletionUsable():
-      self.GetFiletypeCompleter().OnFileReadyToParse()
+      self.GetFiletypeCompleter().OnFileReadyToParse( _BuildRequestData() )
 
 
 
 
   def OnBufferUnload( self, deleted_buffer_file ):
   def OnBufferUnload( self, deleted_buffer_file ):
@@ -208,9 +281,9 @@ class YouCompleteMe( object ):
     return []
     return []
 
 
 
 
-  def ShowDetailedDiagnostic( self ):
+  def GetDetailedDiagnostic( self ):
     if self.FiletypeCompletionUsable():
     if self.FiletypeCompletionUsable():
-      return self.GetFiletypeCompleter().ShowDetailedDiagnostic()
+      return self.GetFiletypeCompleter().GetDetailedDiagnostic()
 
 
 
 
   def GettingCompletions( self ):
   def GettingCompletions( self ):
@@ -263,3 +336,37 @@ def _PathToFiletypeCompleterPluginLoader( filetype ):
   return os.path.join( _PathToCompletersFolder(), filetype, 'hook.py' )
   return os.path.join( _PathToCompletersFolder(), filetype, 'hook.py' )
 
 
 
 
+def _BuildRequestData( start_column = None, query = None ):
+  line, column = vimsupport.CurrentLineAndColumn()
+  request_data = {
+    'filetypes': vimsupport.CurrentFiletypes(),
+    'line_num': line,
+    'column_num': column,
+    'start_column': start_column,
+    'line_value': vim.current.line,
+    'filepath': vim.current.buffer.name,
+    'file_data': vimsupport.GetUnsavedAndCurrentBufferData()
+  }
+
+  if query:
+    request_data[ 'query' ] = query
+
+  return request_data
+
+def _ConvertCompletionDataToVimData( completion_data ):
+  # see :h complete-items for a description of the dictionary fields
+  vim_data = {
+    'word' : completion_data[ 'insertion_text' ],
+    'dup'  : 1,
+  }
+
+  if 'menu_text' in completion_data:
+    vim_data[ 'abbr' ] = completion_data[ 'menu_text' ]
+  if 'extra_menu_info' in completion_data:
+    vim_data[ 'menu' ] = completion_data[ 'extra_menu_info' ]
+  if 'kind' in completion_data:
+    vim_data[ 'kind' ] = completion_data[ 'kind' ]
+  if 'detailed_info' in completion_data:
+    vim_data[ 'info' ] = completion_data[ 'detailed_info' ]
+
+  return vim_data