diff --git a/autoload/youcompleteme.vim b/autoload/youcompleteme.vim index bb6b46aa..3ce1fbd2 100644 --- a/autoload/youcompleteme.vim +++ b/autoload/youcompleteme.vim @@ -44,6 +44,7 @@ function! youcompleteme#Enable() py from ycm import vimsupport py from ycm import user_options_store py user_options_store.SetAll( base.BuildServerConf() ) + " TODO: Remove the call to YcmPreload py from ycm import extra_conf_store py extra_conf_store.CallExtraConfYcmCorePreloadIfExists() @@ -549,6 +550,7 @@ endfunction function! youcompleteme#OmniComplete( findstart, base ) if a:findstart let s:omnifunc_mode = 1 + " TODO: Force semantic mode here ( needs to work) return pyeval( 'ycm_state.CreateCompletionRequest().CompletionStartColumn()' ) else return s:CompletionsForQuery( a:base ) diff --git a/cpp/ycm/Result.h b/cpp/ycm/Result.h index a9c6cab4..2aaf8c4d 100644 --- a/cpp/ycm/Result.h +++ b/cpp/ycm/Result.h @@ -88,6 +88,7 @@ private: template< class T > struct ResultAnd { + // TODO: Swap the order of these parameters ResultAnd( T extra_object, const Result &result ) : extra_object_( extra_object ), result_( result ) {} diff --git a/python/ycm/completers/cpp/clang_completer.py b/python/ycm/completers/cpp/clang_completer.py index 4fbed782..d2fd8b17 100644 --- a/python/ycm/completers/cpp/clang_completer.py +++ b/python/ycm/completers/cpp/clang_completer.py @@ -55,6 +55,9 @@ class ClangCompleter( Completer ): # in progress. We use this to trigger the pending request after the previous # one completes (from GetDiagnosticsForCurrentFile because that's the only # method that knows when the compilation has finished). + # TODO: Remove this now that we have multiple threads in the server; the + # subsequent requests that want to parse will just block until the current + # parse is done and will then proceed. self.extra_parse_desired = False @@ -213,7 +216,6 @@ class ClangCompleter( Completer ): self.flags.Clear() - def OnFileReadyToParse( self, request_data ): filename = request_data[ 'filepath' ] contents = request_data[ 'file_data' ][ filename ][ 'contents' ] diff --git a/python/ycm/completers/general/general_completer_store.py b/python/ycm/completers/general/general_completer_store.py index 4effcdd4..3c80a7b7 100644 --- a/python/ycm/completers/general/general_completer_store.py +++ b/python/ycm/completers/general/general_completer_store.py @@ -21,13 +21,7 @@ from ycm.completers.completer import Completer from ycm.completers.all.identifier_completer import IdentifierCompleter from ycm.completers.general.filename_completer import FilenameCompleter - -try: - from ycm.completers.general.ultisnips_completer import UltiSnipsCompleter - USE_ULTISNIPS_COMPLETER = True -except ImportError: - USE_ULTISNIPS_COMPLETER = False - +from ycm.completers.general.ultisnips_completer import UltiSnipsCompleter class GeneralCompleterStore( Completer ): @@ -42,8 +36,7 @@ class GeneralCompleterStore( Completer ): super( GeneralCompleterStore, self ).__init__( user_options ) self._identifier_completer = IdentifierCompleter( user_options ) self._filename_completer = FilenameCompleter( user_options ) - self._ultisnips_completer = ( UltiSnipsCompleter( user_options ) - if USE_ULTISNIPS_COMPLETER else None ) + self._ultisnips_completer = UltiSnipsCompleter( user_options ) self._non_filename_completers = filter( lambda x: x, [ self._ultisnips_completer, self._identifier_completer ] ) diff --git a/python/ycm/completers/general/ultisnips_completer.py b/python/ycm/completers/general/ultisnips_completer.py index dfc37cd1..15858585 100644 --- a/python/ycm/completers/general/ultisnips_completer.py +++ b/python/ycm/completers/general/ultisnips_completer.py @@ -19,7 +19,6 @@ # along with YouCompleteMe. If not, see . from ycm.completers.general_completer import GeneralCompleter -from UltiSnips import UltiSnips_Manager from ycm.server import responses @@ -52,21 +51,10 @@ class UltiSnipsCompleter( GeneralCompleter ): def OnBufferVisit( self, request_data ): - # TODO: _GetCandidates should be called on the client and it should send - # the snippets to the server - self._candidates = _GetCandidates() + raw_candidates = request_data[ 'ultisnips_snippets' ] + self._candidates = [ + responses.BuildCompletionData( + str( snip[ 'trigger' ] ), + str( ' ' + snip[ 'description' ].encode( 'utf-8' ) ) ) + for snip in raw_candidates ] - -def _GetCandidates(): - try: - rawsnips = UltiSnips_Manager._snips( '', 1 ) - - # UltiSnips_Manager._snips() returns a class instance where: - # class.trigger - name of snippet trigger word ( e.g. defn or testcase ) - # class.description - description of the snippet - return [ responses.BuildCompletionData( - str( snip.trigger ), - str( ' ' + snip.description.encode( 'utf-8' ) ) ) - for snip in rawsnips ] - except: - return [] diff --git a/python/ycm/server/tests/basic_test.py b/python/ycm/server/tests/basic_test.py index e012c856..db0bbfed 100644 --- a/python/ycm/server/tests/basic_test.py +++ b/python/ycm/server/tests/basic_test.py @@ -20,7 +20,7 @@ from webtest import TestApp from .. import ycmd from ..responses import BuildCompletionData -from nose.tools import ok_, eq_ +from nose.tools import ok_, eq_, with_setup import bottle bottle.debug( True ) @@ -40,7 +40,12 @@ def RequestDataForFileWithContents( filename, contents ): } -def GetCompletions_IdentifierCompleterWorks_test(): +def Setup(): + ycmd.SetServerStateToDefaults() + + +@with_setup( Setup ) +def GetCompletions_IdentifierCompleter_Works_test(): app = TestApp( ycmd.app ) event_data = RequestDataForFileWithContents( '/foo/bar', 'foo foogoo ba' ) event_data.update( { @@ -63,6 +68,7 @@ def GetCompletions_IdentifierCompleterWorks_test(): app.post_json( '/get_completions', completion_data ).json ) +@with_setup( Setup ) def GetCompletions_IdentifierCompleter_SyntaxKeywordsAdded_test(): app = TestApp( ycmd.app ) event_data = RequestDataForFileWithContents( '/foo/bar', '' ) @@ -87,6 +93,34 @@ def GetCompletions_IdentifierCompleter_SyntaxKeywordsAdded_test(): app.post_json( '/get_completions', completion_data ).json ) +@with_setup( Setup ) +def GetCompletions_UltiSnipsCompleter_Works_test(): + app = TestApp( ycmd.app ) + event_data = RequestDataForFileWithContents( '/foo/bar', '' ) + event_data.update( { + 'event_name': 'BufferVisit', + 'ultisnips_snippets': [ + {'trigger': 'foo', 'description': 'bar'}, + {'trigger': 'zoo', 'description': 'goo'}, + ] + } ) + + app.post_json( '/event_notification', event_data ) + + completion_data = RequestDataForFileWithContents( '/foo/bar', 'oo ' ) + completion_data.update( { + 'query': 'oo', + 'line_num': 0, + 'column_num': 2, + 'start_column': 0, + } ) + + eq_( [ BuildCompletionData( 'foo', ' bar' ), + BuildCompletionData( 'zoo', ' goo' ) ], + app.post_json( '/get_completions', completion_data ).json ) + + +@with_setup( Setup ) def FiletypeCompletionAvailable_Works_test(): app = TestApp( ycmd.app ) request_data = { @@ -97,6 +131,7 @@ def FiletypeCompletionAvailable_Works_test(): request_data ).json ) +@with_setup( Setup ) def UserOptions_Works_test(): app = TestApp( ycmd.app ) options = app.get( '/user_options' ).json diff --git a/python/ycm/server/ycmd.py b/python/ycm/server/ycmd.py index 85520ca6..ae2a314f 100755 --- a/python/ycm/server/ycmd.py +++ b/python/ycm/server/ycmd.py @@ -43,9 +43,8 @@ import argparse # size is less than this bottle.Request.MEMFILE_MAX = 300 * 1024 -user_options_store.LoadDefaults() -SERVER_STATE = server_state.ServerState( user_options_store.GetAll() ) - +SERVER_STATE = None +# TODO: is init needed here? LOGGER = logging.getLogger( __name__ ) app = bottle.Bottle() @@ -132,14 +131,21 @@ def _JsonResponse( data ): @atexit.register def _ServerShutdown(): - SERVER_STATE.Shutdown() + if SERVER_STATE: + SERVER_STATE.Shutdown() def _SetUserOptions( options ): global SERVER_STATE - SERVER_STATE = server_state.ServerState( options ) user_options_store.SetAll( options ) + SERVER_STATE = server_state.ServerState( options ) + + +def SetServerStateToDefaults(): + global SERVER_STATE + user_options_store.LoadDefaults() + SERVER_STATE = server_state.ServerState( user_options_store.GetAll() ) def Main(): diff --git a/python/ycm/youcompleteme.py b/python/ycm/youcompleteme.py index 7a8b58d2..a6bdc490 100644 --- a/python/ycm/youcompleteme.py +++ b/python/ycm/youcompleteme.py @@ -32,6 +32,12 @@ from ycm.client.command_request import SendCommandRequest from ycm.client.completion_request import CompletionRequest from ycm.client.event_notification import SendEventNotificationAsync +try: + from UltiSnips import UltiSnips_Manager + USE_ULTISNIPS_DATA = True +except ImportError: + USE_ULTISNIPS_DATA = False + SERVER_PORT_RANGE_START = 10000 class YouCompleteMe( object ): @@ -145,7 +151,9 @@ class YouCompleteMe( object ): def OnBufferVisit( self ): - SendEventNotificationAsync( 'BufferVisit' ) + extra_data = {} + _AddUltiSnipsDataIfNeeded( extra_data ) + SendEventNotificationAsync( 'BufferVisit', extra_data ) def OnInsertLeave( self ): @@ -237,3 +245,21 @@ def _PathToServerScript(): dir_of_current_script = os.path.dirname( os.path.abspath( __file__ ) ) return os.path.join( dir_of_current_script, 'server/ycmd.py' ) + +def _AddUltiSnipsDataIfNeeded( extra_data ): + if not USE_ULTISNIPS_DATA: + return + + try: + rawsnips = UltiSnips_Manager._snips( '', 1 ) + except: + return + + # UltiSnips_Manager._snips() returns a class instance where: + # class.trigger - name of snippet trigger word ( e.g. defn or testcase ) + # class.description - description of the snippet + extra_data[ 'ultisnips_snippets' ] = [ { 'trigger': x.trigger, + 'description': x.description + } for x in rawsnips ] + +