ycm_core now imported after extra conf preload
This commit is contained in:
parent
bc607724f0
commit
b903867cdd
210
python/ycm/server/handlers.py
Normal file
210
python/ycm/server/handlers.py
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
#!/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/>.
|
||||||
|
|
||||||
|
import atexit
|
||||||
|
import logging
|
||||||
|
import json
|
||||||
|
import bottle
|
||||||
|
import httplib
|
||||||
|
from bottle import request, response
|
||||||
|
import server_state
|
||||||
|
from ycm import user_options_store
|
||||||
|
from ycm.server.responses import BuildExceptionResponse
|
||||||
|
from ycm import extra_conf_store
|
||||||
|
|
||||||
|
# num bytes for the request body buffer; request.json only works if the request
|
||||||
|
# size is less than this
|
||||||
|
bottle.Request.MEMFILE_MAX = 300 * 1024
|
||||||
|
|
||||||
|
# TODO: rename these to _lower_case
|
||||||
|
SERVER_STATE = None
|
||||||
|
LOGGER = logging.getLogger( __name__ )
|
||||||
|
app = bottle.Bottle()
|
||||||
|
|
||||||
|
|
||||||
|
@app.post( '/event_notification' )
|
||||||
|
def EventNotification():
|
||||||
|
LOGGER.info( 'Received event notification' )
|
||||||
|
request_data = request.json
|
||||||
|
event_name = request_data[ 'event_name' ]
|
||||||
|
LOGGER.debug( 'Event name: %s', event_name )
|
||||||
|
|
||||||
|
event_handler = 'On' + event_name
|
||||||
|
getattr( SERVER_STATE.GetGeneralCompleter(), event_handler )( request_data )
|
||||||
|
|
||||||
|
filetypes = request_data[ 'filetypes' ]
|
||||||
|
response_data = None
|
||||||
|
if SERVER_STATE.FiletypeCompletionUsable( filetypes ):
|
||||||
|
response_data = getattr( SERVER_STATE.GetFiletypeCompleter( filetypes ),
|
||||||
|
event_handler )( request_data )
|
||||||
|
|
||||||
|
if response_data:
|
||||||
|
return _JsonResponse( response_data )
|
||||||
|
|
||||||
|
|
||||||
|
@app.post( '/run_completer_command' )
|
||||||
|
def RunCompleterCommand():
|
||||||
|
LOGGER.info( 'Received command request' )
|
||||||
|
request_data = request.json
|
||||||
|
completer = _GetCompleterForRequestData( request_data )
|
||||||
|
|
||||||
|
return _JsonResponse( completer.OnUserCommand(
|
||||||
|
request_data[ 'command_arguments' ],
|
||||||
|
request_data ) )
|
||||||
|
|
||||||
|
|
||||||
|
@app.post( '/completions' )
|
||||||
|
def GetCompletions():
|
||||||
|
LOGGER.info( 'Received completion request' )
|
||||||
|
request_data = request.json
|
||||||
|
do_filetype_completion = SERVER_STATE.ShouldUseFiletypeCompleter(
|
||||||
|
request_data )
|
||||||
|
LOGGER.debug( 'Using filetype completion: %s', do_filetype_completion )
|
||||||
|
filetypes = request_data[ 'filetypes' ]
|
||||||
|
completer = ( SERVER_STATE.GetFiletypeCompleter( filetypes ) if
|
||||||
|
do_filetype_completion else
|
||||||
|
SERVER_STATE.GetGeneralCompleter() )
|
||||||
|
|
||||||
|
return _JsonResponse( completer.ComputeCandidates( request_data ) )
|
||||||
|
|
||||||
|
|
||||||
|
@app.get( '/user_options' )
|
||||||
|
def GetUserOptions():
|
||||||
|
LOGGER.info( 'Received user options GET request' )
|
||||||
|
return _JsonResponse( dict( SERVER_STATE.user_options ) )
|
||||||
|
|
||||||
|
|
||||||
|
@app.get( '/healthy' )
|
||||||
|
def GetHealthy():
|
||||||
|
LOGGER.info( 'Received health request' )
|
||||||
|
return _JsonResponse( True )
|
||||||
|
|
||||||
|
|
||||||
|
@app.post( '/user_options' )
|
||||||
|
def SetUserOptions():
|
||||||
|
LOGGER.info( 'Received user options POST request' )
|
||||||
|
UpdateUserOptions( request.json )
|
||||||
|
|
||||||
|
|
||||||
|
@app.post( '/semantic_completion_available' )
|
||||||
|
def FiletypeCompletionAvailable():
|
||||||
|
LOGGER.info( 'Received filetype completion available request' )
|
||||||
|
return _JsonResponse( SERVER_STATE.FiletypeCompletionAvailable(
|
||||||
|
request.json[ 'filetypes' ] ) )
|
||||||
|
|
||||||
|
|
||||||
|
@app.post( '/defined_subcommands' )
|
||||||
|
def DefinedSubcommands():
|
||||||
|
LOGGER.info( 'Received defined subcommands request' )
|
||||||
|
completer = _GetCompleterForRequestData( request.json )
|
||||||
|
|
||||||
|
return _JsonResponse( completer.DefinedSubcommands() )
|
||||||
|
|
||||||
|
|
||||||
|
@app.post( '/detailed_diagnostic' )
|
||||||
|
def GetDetailedDiagnostic():
|
||||||
|
LOGGER.info( 'Received detailed diagnostic request' )
|
||||||
|
request_data = request.json
|
||||||
|
completer = _GetCompleterForRequestData( request_data )
|
||||||
|
|
||||||
|
return _JsonResponse( completer.GetDetailedDiagnostic( request_data ) )
|
||||||
|
|
||||||
|
|
||||||
|
@app.post( '/load_extra_conf_file' )
|
||||||
|
def LoadExtraConfFile():
|
||||||
|
LOGGER.info( 'Received extra conf load request' )
|
||||||
|
request_data = request.json
|
||||||
|
extra_conf_store.Load( request_data[ 'filepath' ], force = True )
|
||||||
|
|
||||||
|
|
||||||
|
@app.post( '/debug_info' )
|
||||||
|
def DebugInfo():
|
||||||
|
# This can't be at the top level because of possible extra conf preload
|
||||||
|
import ycm_core
|
||||||
|
LOGGER.info( 'Received debug info request' )
|
||||||
|
|
||||||
|
output = []
|
||||||
|
has_clang_support = ycm_core.HasClangSupport()
|
||||||
|
output.append( 'Server has Clang support compiled in: {0}'.format(
|
||||||
|
has_clang_support ) )
|
||||||
|
|
||||||
|
if has_clang_support:
|
||||||
|
output.append( 'Clang version: ' + ycm_core.ClangVersion() )
|
||||||
|
|
||||||
|
request_data = request.json
|
||||||
|
try:
|
||||||
|
output.append(
|
||||||
|
_GetCompleterForRequestData( request_data ).DebugInfo( request_data) )
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return _JsonResponse( '\n'.join( output ) )
|
||||||
|
|
||||||
|
|
||||||
|
# The type of the param is Bottle.HTTPError
|
||||||
|
@app.error( httplib.INTERNAL_SERVER_ERROR )
|
||||||
|
def ErrorHandler( httperror ):
|
||||||
|
return _JsonResponse( BuildExceptionResponse( httperror.exception,
|
||||||
|
httperror.traceback ) )
|
||||||
|
|
||||||
|
|
||||||
|
def _JsonResponse( data ):
|
||||||
|
response.set_header( 'Content-Type', 'application/json' )
|
||||||
|
return json.dumps( data, default = _UniversalSerialize )
|
||||||
|
|
||||||
|
|
||||||
|
def _UniversalSerialize( obj ):
|
||||||
|
serialized = obj.__dict__.copy()
|
||||||
|
serialized[ 'TYPE' ] = type( obj ).__name__
|
||||||
|
return serialized
|
||||||
|
|
||||||
|
|
||||||
|
def _GetCompleterForRequestData( request_data ):
|
||||||
|
completer_target = request_data.get( 'completer_target', None )
|
||||||
|
|
||||||
|
if completer_target == 'identifier':
|
||||||
|
return SERVER_STATE.GetGeneralCompleter().GetIdentifierCompleter()
|
||||||
|
elif completer_target == 'filetype_default' or not completer_target:
|
||||||
|
return SERVER_STATE.GetFiletypeCompleter( request_data[ 'filetypes' ] )
|
||||||
|
else:
|
||||||
|
return SERVER_STATE.GetFiletypeCompleter( [ completer_target ] )
|
||||||
|
|
||||||
|
|
||||||
|
@atexit.register
|
||||||
|
def _ServerShutdown():
|
||||||
|
if SERVER_STATE:
|
||||||
|
SERVER_STATE.Shutdown()
|
||||||
|
extra_conf_store.Shutdown()
|
||||||
|
|
||||||
|
|
||||||
|
def UpdateUserOptions( options ):
|
||||||
|
global SERVER_STATE
|
||||||
|
|
||||||
|
if not options:
|
||||||
|
return
|
||||||
|
|
||||||
|
user_options_store.SetAll( options )
|
||||||
|
SERVER_STATE = server_state.ServerState( options )
|
||||||
|
|
||||||
|
|
||||||
|
def SetServerStateToDefaults():
|
||||||
|
global SERVER_STATE, LOGGER
|
||||||
|
LOGGER = logging.getLogger( __name__ )
|
||||||
|
user_options_store.LoadDefaults()
|
||||||
|
SERVER_STATE = server_state.ServerState( user_options_store.GetAll() )
|
||||||
|
extra_conf_store.Reset()
|
@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
import imp
|
import imp
|
||||||
import os
|
import os
|
||||||
from ycm import extra_conf_store
|
|
||||||
from ycm.utils import ForceSemanticCompletion
|
from ycm.utils import ForceSemanticCompletion
|
||||||
from ycm.completers.general.general_completer_store import GeneralCompleterStore
|
from ycm.completers.general.general_completer_store import GeneralCompleterStore
|
||||||
from ycm.completers.completer_utils import PathToFiletypeCompleterPluginLoader
|
from ycm.completers.completer_utils import PathToFiletypeCompleterPluginLoader
|
||||||
@ -30,7 +29,6 @@ class ServerState( object ):
|
|||||||
self._user_options = user_options
|
self._user_options = user_options
|
||||||
self._filetype_completers = {}
|
self._filetype_completers = {}
|
||||||
self._gencomp = GeneralCompleterStore( self._user_options )
|
self._gencomp = GeneralCompleterStore( self._user_options )
|
||||||
extra_conf_store.CallGlobalExtraConfYcmCorePreloadIfExists()
|
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -44,7 +42,6 @@ class ServerState( object ):
|
|||||||
completer.Shutdown()
|
completer.Shutdown()
|
||||||
|
|
||||||
self._gencomp.Shutdown()
|
self._gencomp.Shutdown()
|
||||||
extra_conf_store.Shutdown()
|
|
||||||
|
|
||||||
|
|
||||||
def _GetFiletypeCompleterForFiletype( self, filetype ):
|
def _GetFiletypeCompleterForFiletype( self, filetype ):
|
||||||
|
34
python/ycm/server/server_utils.py
Normal file
34
python/ycm/server/server_utils.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#!/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/>.
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
def SetUpPythonPath():
|
||||||
|
# We want to have the YouCompleteMe/python directory on the Python PATH
|
||||||
|
# because all the code already assumes that it's there. This is a relic from
|
||||||
|
# before the client/server architecture.
|
||||||
|
# TODO: Fix things so that this is not needed anymore when we split ycmd into
|
||||||
|
# a separate repository.
|
||||||
|
sys.path.insert( 0, os.path.join(
|
||||||
|
os.path.dirname( os.path.abspath( __file__ ) ),
|
||||||
|
'../..' ) )
|
||||||
|
|
||||||
|
from ycm import utils
|
||||||
|
utils.AddThirdPartyFoldersToSysPath()
|
@ -20,8 +20,10 @@
|
|||||||
import os
|
import os
|
||||||
import httplib
|
import httplib
|
||||||
import time
|
import time
|
||||||
|
from ..server_utils import SetUpPythonPath
|
||||||
|
SetUpPythonPath()
|
||||||
from webtest import TestApp
|
from webtest import TestApp
|
||||||
from .. import ycmd
|
from .. import handlers
|
||||||
from ..responses import BuildCompletionData, UnknownExtraConf
|
from ..responses import BuildCompletionData, UnknownExtraConf
|
||||||
from nose.tools import ok_, eq_, with_setup
|
from nose.tools import ok_, eq_, with_setup
|
||||||
from hamcrest import ( assert_that, has_items, has_entry, contains,
|
from hamcrest import ( assert_that, has_items, has_entry, contains,
|
||||||
@ -73,7 +75,7 @@ def CompletionEntryMatcher( insertion_text ):
|
|||||||
|
|
||||||
|
|
||||||
def Setup():
|
def Setup():
|
||||||
ycmd.SetServerStateToDefaults()
|
handlers.SetServerStateToDefaults()
|
||||||
|
|
||||||
|
|
||||||
def PathToTestDataDir():
|
def PathToTestDataDir():
|
||||||
@ -87,7 +89,7 @@ def PathToTestFile( test_basename ):
|
|||||||
|
|
||||||
@with_setup( Setup )
|
@with_setup( Setup )
|
||||||
def GetCompletions_IdentifierCompleter_Works_test():
|
def GetCompletions_IdentifierCompleter_Works_test():
|
||||||
app = TestApp( ycmd.app )
|
app = TestApp( handlers.app )
|
||||||
event_data = BuildRequest( contents = 'foo foogoo ba',
|
event_data = BuildRequest( contents = 'foo foogoo ba',
|
||||||
event_name = 'FileReadyToParse' )
|
event_name = 'FileReadyToParse' )
|
||||||
|
|
||||||
@ -104,7 +106,7 @@ def GetCompletions_IdentifierCompleter_Works_test():
|
|||||||
|
|
||||||
@with_setup( Setup )
|
@with_setup( Setup )
|
||||||
def GetCompletions_CsCompleter_Works_test():
|
def GetCompletions_CsCompleter_Works_test():
|
||||||
app = TestApp( ycmd.app )
|
app = TestApp( handlers.app )
|
||||||
filepath = PathToTestFile( 'testy/Program.cs' )
|
filepath = PathToTestFile( 'testy/Program.cs' )
|
||||||
contents = open( filepath ).read()
|
contents = open( filepath ).read()
|
||||||
event_data = BuildRequest( filepath = filepath,
|
event_data = BuildRequest( filepath = filepath,
|
||||||
@ -138,7 +140,7 @@ def GetCompletions_CsCompleter_Works_test():
|
|||||||
|
|
||||||
@with_setup( Setup )
|
@with_setup( Setup )
|
||||||
def GetCompletions_ClangCompleter_WorksWithExplicitFlags_test():
|
def GetCompletions_ClangCompleter_WorksWithExplicitFlags_test():
|
||||||
app = TestApp( ycmd.app )
|
app = TestApp( handlers.app )
|
||||||
contents = """
|
contents = """
|
||||||
struct Foo {
|
struct Foo {
|
||||||
int x;
|
int x;
|
||||||
@ -170,7 +172,7 @@ int main()
|
|||||||
|
|
||||||
@with_setup( Setup )
|
@with_setup( Setup )
|
||||||
def GetCompletions_ClangCompleter_UnknownExtraConfException_test():
|
def GetCompletions_ClangCompleter_UnknownExtraConfException_test():
|
||||||
app = TestApp( ycmd.app )
|
app = TestApp( handlers.app )
|
||||||
filepath = PathToTestFile( 'basic.cpp' )
|
filepath = PathToTestFile( 'basic.cpp' )
|
||||||
completion_data = BuildRequest( filepath = filepath,
|
completion_data = BuildRequest( filepath = filepath,
|
||||||
filetype = 'cpp',
|
filetype = 'cpp',
|
||||||
@ -189,7 +191,7 @@ def GetCompletions_ClangCompleter_UnknownExtraConfException_test():
|
|||||||
|
|
||||||
@with_setup( Setup )
|
@with_setup( Setup )
|
||||||
def GetCompletions_ClangCompleter_WorksWhenExtraConfExplicitlyAllowed_test():
|
def GetCompletions_ClangCompleter_WorksWhenExtraConfExplicitlyAllowed_test():
|
||||||
app = TestApp( ycmd.app )
|
app = TestApp( handlers.app )
|
||||||
app.post_json( '/load_extra_conf_file',
|
app.post_json( '/load_extra_conf_file',
|
||||||
{ 'filepath': PathToTestFile( '.ycm_extra_conf.py' ) } )
|
{ 'filepath': PathToTestFile( '.ycm_extra_conf.py' ) } )
|
||||||
|
|
||||||
@ -209,7 +211,7 @@ def GetCompletions_ClangCompleter_WorksWhenExtraConfExplicitlyAllowed_test():
|
|||||||
|
|
||||||
@with_setup( Setup )
|
@with_setup( Setup )
|
||||||
def GetCompletions_ClangCompleter_ForceSemantic_OnlyFileteredCompletions_test():
|
def GetCompletions_ClangCompleter_ForceSemantic_OnlyFileteredCompletions_test():
|
||||||
app = TestApp( ycmd.app )
|
app = TestApp( handlers.app )
|
||||||
contents = """
|
contents = """
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
@ -241,7 +243,7 @@ int main()
|
|||||||
|
|
||||||
@with_setup( Setup )
|
@with_setup( Setup )
|
||||||
def GetCompletions_ForceSemantic_Works_test():
|
def GetCompletions_ForceSemantic_Works_test():
|
||||||
app = TestApp( ycmd.app )
|
app = TestApp( handlers.app )
|
||||||
|
|
||||||
completion_data = BuildRequest( filetype = 'python',
|
completion_data = BuildRequest( filetype = 'python',
|
||||||
force_semantic = True )
|
force_semantic = True )
|
||||||
@ -254,7 +256,7 @@ def GetCompletions_ForceSemantic_Works_test():
|
|||||||
|
|
||||||
@with_setup( Setup )
|
@with_setup( Setup )
|
||||||
def GetCompletions_IdentifierCompleter_SyntaxKeywordsAdded_test():
|
def GetCompletions_IdentifierCompleter_SyntaxKeywordsAdded_test():
|
||||||
app = TestApp( ycmd.app )
|
app = TestApp( handlers.app )
|
||||||
event_data = BuildRequest( event_name = 'FileReadyToParse',
|
event_data = BuildRequest( event_name = 'FileReadyToParse',
|
||||||
syntax_keywords = ['foo', 'bar', 'zoo'] )
|
syntax_keywords = ['foo', 'bar', 'zoo'] )
|
||||||
|
|
||||||
@ -271,7 +273,7 @@ def GetCompletions_IdentifierCompleter_SyntaxKeywordsAdded_test():
|
|||||||
|
|
||||||
@with_setup( Setup )
|
@with_setup( Setup )
|
||||||
def GetCompletions_UltiSnipsCompleter_Works_test():
|
def GetCompletions_UltiSnipsCompleter_Works_test():
|
||||||
app = TestApp( ycmd.app )
|
app = TestApp( handlers.app )
|
||||||
event_data = BuildRequest(
|
event_data = BuildRequest(
|
||||||
event_name = 'BufferVisit',
|
event_name = 'BufferVisit',
|
||||||
ultisnips_snippets = [
|
ultisnips_snippets = [
|
||||||
@ -292,7 +294,7 @@ def GetCompletions_UltiSnipsCompleter_Works_test():
|
|||||||
|
|
||||||
@with_setup( Setup )
|
@with_setup( Setup )
|
||||||
def RunCompleterCommand_GoTo_Jedi_ZeroBasedLineAndColumn_test():
|
def RunCompleterCommand_GoTo_Jedi_ZeroBasedLineAndColumn_test():
|
||||||
app = TestApp( ycmd.app )
|
app = TestApp( handlers.app )
|
||||||
contents = """
|
contents = """
|
||||||
def foo():
|
def foo():
|
||||||
pass
|
pass
|
||||||
@ -318,7 +320,7 @@ foo()
|
|||||||
|
|
||||||
@with_setup( Setup )
|
@with_setup( Setup )
|
||||||
def RunCompleterCommand_GoTo_Clang_ZeroBasedLineAndColumn_test():
|
def RunCompleterCommand_GoTo_Clang_ZeroBasedLineAndColumn_test():
|
||||||
app = TestApp( ycmd.app )
|
app = TestApp( handlers.app )
|
||||||
contents = """
|
contents = """
|
||||||
struct Foo {
|
struct Foo {
|
||||||
int x;
|
int x;
|
||||||
@ -352,7 +354,7 @@ int main()
|
|||||||
|
|
||||||
@with_setup( Setup )
|
@with_setup( Setup )
|
||||||
def DefinedSubcommands_Works_test():
|
def DefinedSubcommands_Works_test():
|
||||||
app = TestApp( ycmd.app )
|
app = TestApp( handlers.app )
|
||||||
subcommands_data = BuildRequest( completer_target = 'python' )
|
subcommands_data = BuildRequest( completer_target = 'python' )
|
||||||
|
|
||||||
eq_( [ 'GoToDefinition',
|
eq_( [ 'GoToDefinition',
|
||||||
@ -363,7 +365,7 @@ def DefinedSubcommands_Works_test():
|
|||||||
|
|
||||||
@with_setup( Setup )
|
@with_setup( Setup )
|
||||||
def DefinedSubcommands_WorksWhenNoExplicitCompleterTargetSpecified_test():
|
def DefinedSubcommands_WorksWhenNoExplicitCompleterTargetSpecified_test():
|
||||||
app = TestApp( ycmd.app )
|
app = TestApp( handlers.app )
|
||||||
subcommands_data = BuildRequest( filetype = 'python' )
|
subcommands_data = BuildRequest( filetype = 'python' )
|
||||||
|
|
||||||
eq_( [ 'GoToDefinition',
|
eq_( [ 'GoToDefinition',
|
||||||
@ -374,7 +376,7 @@ def DefinedSubcommands_WorksWhenNoExplicitCompleterTargetSpecified_test():
|
|||||||
|
|
||||||
@with_setup( Setup )
|
@with_setup( Setup )
|
||||||
def Diagnostics_ClangCompleter_ZeroBasedLineAndColumn_test():
|
def Diagnostics_ClangCompleter_ZeroBasedLineAndColumn_test():
|
||||||
app = TestApp( ycmd.app )
|
app = TestApp( handlers.app )
|
||||||
contents = """
|
contents = """
|
||||||
struct Foo {
|
struct Foo {
|
||||||
int x // semicolon missing here!
|
int x // semicolon missing here!
|
||||||
@ -399,7 +401,7 @@ struct Foo {
|
|||||||
|
|
||||||
@with_setup( Setup )
|
@with_setup( Setup )
|
||||||
def GetDetailedDiagnostic_ClangCompleter_Works_test():
|
def GetDetailedDiagnostic_ClangCompleter_Works_test():
|
||||||
app = TestApp( ycmd.app )
|
app = TestApp( handlers.app )
|
||||||
contents = """
|
contents = """
|
||||||
struct Foo {
|
struct Foo {
|
||||||
int x // semicolon missing here!
|
int x // semicolon missing here!
|
||||||
@ -427,7 +429,7 @@ struct Foo {
|
|||||||
|
|
||||||
@with_setup( Setup )
|
@with_setup( Setup )
|
||||||
def FiletypeCompletionAvailable_Works_test():
|
def FiletypeCompletionAvailable_Works_test():
|
||||||
app = TestApp( ycmd.app )
|
app = TestApp( handlers.app )
|
||||||
request_data = {
|
request_data = {
|
||||||
'filetypes': ['python']
|
'filetypes': ['python']
|
||||||
}
|
}
|
||||||
@ -438,7 +440,7 @@ def FiletypeCompletionAvailable_Works_test():
|
|||||||
|
|
||||||
@with_setup( Setup )
|
@with_setup( Setup )
|
||||||
def UserOptions_Works_test():
|
def UserOptions_Works_test():
|
||||||
app = TestApp( ycmd.app )
|
app = TestApp( handlers.app )
|
||||||
options = app.get( '/user_options' ).json
|
options = app.get( '/user_options' ).json
|
||||||
ok_( len( options ) )
|
ok_( len( options ) )
|
||||||
|
|
||||||
|
@ -17,214 +17,24 @@
|
|||||||
# 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/>.
|
||||||
|
|
||||||
|
from server_utils import SetUpPythonPath
|
||||||
|
SetUpPythonPath()
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import os
|
|
||||||
import atexit
|
|
||||||
|
|
||||||
# We want to have the YouCompleteMe/python directory on the Python PATH because
|
|
||||||
# all the code already assumes that it's there. This is a relic from before the
|
|
||||||
# client/server architecture.
|
|
||||||
# TODO: Fix things so that this is not needed anymore when we split ycmd into a
|
|
||||||
# separate repository.
|
|
||||||
sys.path.insert( 0, os.path.join(
|
|
||||||
os.path.dirname( os.path.abspath( __file__ ) ),
|
|
||||||
'../..' ) )
|
|
||||||
|
|
||||||
from ycm import utils
|
|
||||||
utils.AddThirdPartyFoldersToSysPath()
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import json
|
import json
|
||||||
import bottle
|
|
||||||
import argparse
|
import argparse
|
||||||
import httplib
|
|
||||||
import waitress
|
import waitress
|
||||||
from bottle import request, response
|
|
||||||
import server_state
|
|
||||||
from ycm import user_options_store
|
from ycm import user_options_store
|
||||||
from ycm.server.responses import BuildExceptionResponse
|
|
||||||
from ycm import extra_conf_store
|
from ycm import extra_conf_store
|
||||||
|
|
||||||
# num bytes for the request body buffer; request.json only works if the request
|
|
||||||
# size is less than this
|
|
||||||
bottle.Request.MEMFILE_MAX = 300 * 1024
|
|
||||||
|
|
||||||
# TODO: rename these to _lower_case
|
def YcmCoreSanityCheck():
|
||||||
SERVER_STATE = None
|
if 'ycm_core' in sys.modules:
|
||||||
LOGGER = None
|
raise RuntimeError( 'ycm_core already imported, ycmd has a bug!' )
|
||||||
app = bottle.Bottle()
|
|
||||||
|
|
||||||
|
|
||||||
@app.post( '/event_notification' )
|
|
||||||
def EventNotification():
|
|
||||||
LOGGER.info( 'Received event notification' )
|
|
||||||
request_data = request.json
|
|
||||||
event_name = request_data[ 'event_name' ]
|
|
||||||
LOGGER.debug( 'Event name: %s', event_name )
|
|
||||||
|
|
||||||
event_handler = 'On' + event_name
|
|
||||||
getattr( SERVER_STATE.GetGeneralCompleter(), event_handler )( request_data )
|
|
||||||
|
|
||||||
filetypes = request_data[ 'filetypes' ]
|
|
||||||
response_data = None
|
|
||||||
if SERVER_STATE.FiletypeCompletionUsable( filetypes ):
|
|
||||||
response_data = getattr( SERVER_STATE.GetFiletypeCompleter( filetypes ),
|
|
||||||
event_handler )( request_data )
|
|
||||||
|
|
||||||
if response_data:
|
|
||||||
return _JsonResponse( response_data )
|
|
||||||
|
|
||||||
|
|
||||||
@app.post( '/run_completer_command' )
|
|
||||||
def RunCompleterCommand():
|
|
||||||
LOGGER.info( 'Received command request' )
|
|
||||||
request_data = request.json
|
|
||||||
completer = _GetCompleterForRequestData( request_data )
|
|
||||||
|
|
||||||
return _JsonResponse( completer.OnUserCommand(
|
|
||||||
request_data[ 'command_arguments' ],
|
|
||||||
request_data ) )
|
|
||||||
|
|
||||||
|
|
||||||
@app.post( '/completions' )
|
|
||||||
def GetCompletions():
|
|
||||||
LOGGER.info( 'Received completion request' )
|
|
||||||
request_data = request.json
|
|
||||||
do_filetype_completion = SERVER_STATE.ShouldUseFiletypeCompleter(
|
|
||||||
request_data )
|
|
||||||
LOGGER.debug( 'Using filetype completion: %s', do_filetype_completion )
|
|
||||||
filetypes = request_data[ 'filetypes' ]
|
|
||||||
completer = ( SERVER_STATE.GetFiletypeCompleter( filetypes ) if
|
|
||||||
do_filetype_completion else
|
|
||||||
SERVER_STATE.GetGeneralCompleter() )
|
|
||||||
|
|
||||||
return _JsonResponse( completer.ComputeCandidates( request_data ) )
|
|
||||||
|
|
||||||
|
|
||||||
@app.get( '/user_options' )
|
|
||||||
def GetUserOptions():
|
|
||||||
LOGGER.info( 'Received user options GET request' )
|
|
||||||
return _JsonResponse( dict( SERVER_STATE.user_options ) )
|
|
||||||
|
|
||||||
|
|
||||||
@app.get( '/healthy' )
|
|
||||||
def GetHealthy():
|
|
||||||
LOGGER.info( 'Received health request' )
|
|
||||||
return _JsonResponse( True )
|
|
||||||
|
|
||||||
|
|
||||||
@app.post( '/user_options' )
|
|
||||||
def SetUserOptions():
|
|
||||||
LOGGER.info( 'Received user options POST request' )
|
|
||||||
_SetUserOptions( request.json )
|
|
||||||
|
|
||||||
|
|
||||||
@app.post( '/semantic_completion_available' )
|
|
||||||
def FiletypeCompletionAvailable():
|
|
||||||
LOGGER.info( 'Received filetype completion available request' )
|
|
||||||
return _JsonResponse( SERVER_STATE.FiletypeCompletionAvailable(
|
|
||||||
request.json[ 'filetypes' ] ) )
|
|
||||||
|
|
||||||
|
|
||||||
@app.post( '/defined_subcommands' )
|
|
||||||
def DefinedSubcommands():
|
|
||||||
LOGGER.info( 'Received defined subcommands request' )
|
|
||||||
completer = _GetCompleterForRequestData( request.json )
|
|
||||||
|
|
||||||
return _JsonResponse( completer.DefinedSubcommands() )
|
|
||||||
|
|
||||||
|
|
||||||
@app.post( '/detailed_diagnostic' )
|
|
||||||
def GetDetailedDiagnostic():
|
|
||||||
LOGGER.info( 'Received detailed diagnostic request' )
|
|
||||||
request_data = request.json
|
|
||||||
completer = _GetCompleterForRequestData( request_data )
|
|
||||||
|
|
||||||
return _JsonResponse( completer.GetDetailedDiagnostic( request_data ) )
|
|
||||||
|
|
||||||
|
|
||||||
@app.post( '/load_extra_conf_file' )
|
|
||||||
def LoadExtraConfFile():
|
|
||||||
LOGGER.info( 'Received extra conf load request' )
|
|
||||||
request_data = request.json
|
|
||||||
extra_conf_store.Load( request_data[ 'filepath' ], force = True )
|
|
||||||
|
|
||||||
|
|
||||||
@app.post( '/debug_info' )
|
|
||||||
def DebugInfo():
|
|
||||||
# This can't be at the top level because of possible extra conf preload
|
|
||||||
import ycm_core
|
|
||||||
LOGGER.info( 'Received debug info request' )
|
|
||||||
|
|
||||||
output = []
|
|
||||||
has_clang_support = ycm_core.HasClangSupport()
|
|
||||||
output.append( 'Server has Clang support compiled in: {0}'.format(
|
|
||||||
has_clang_support ) )
|
|
||||||
|
|
||||||
if has_clang_support:
|
|
||||||
output.append( 'Clang version: ' + ycm_core.ClangVersion() )
|
|
||||||
|
|
||||||
request_data = request.json
|
|
||||||
try:
|
|
||||||
output.append(
|
|
||||||
_GetCompleterForRequestData( request_data ).DebugInfo( request_data) )
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
return _JsonResponse( '\n'.join( output ) )
|
|
||||||
|
|
||||||
|
|
||||||
# The type of the param is Bottle.HTTPError
|
|
||||||
@app.error( httplib.INTERNAL_SERVER_ERROR )
|
|
||||||
def ErrorHandler( httperror ):
|
|
||||||
return _JsonResponse( BuildExceptionResponse( httperror.exception,
|
|
||||||
httperror.traceback ) )
|
|
||||||
|
|
||||||
|
|
||||||
def _JsonResponse( data ):
|
|
||||||
response.set_header( 'Content-Type', 'application/json' )
|
|
||||||
return json.dumps( data, default = _UniversalSerialize )
|
|
||||||
|
|
||||||
|
|
||||||
def _UniversalSerialize( obj ):
|
|
||||||
serialized = obj.__dict__.copy()
|
|
||||||
serialized[ 'TYPE' ] = type( obj ).__name__
|
|
||||||
return serialized
|
|
||||||
|
|
||||||
|
|
||||||
def _GetCompleterForRequestData( request_data ):
|
|
||||||
completer_target = request_data.get( 'completer_target', None )
|
|
||||||
|
|
||||||
if completer_target == 'identifier':
|
|
||||||
return SERVER_STATE.GetGeneralCompleter().GetIdentifierCompleter()
|
|
||||||
elif completer_target == 'filetype_default' or not completer_target:
|
|
||||||
return SERVER_STATE.GetFiletypeCompleter( request_data[ 'filetypes' ] )
|
|
||||||
else:
|
|
||||||
return SERVER_STATE.GetFiletypeCompleter( [ completer_target ] )
|
|
||||||
|
|
||||||
|
|
||||||
@atexit.register
|
|
||||||
def _ServerShutdown():
|
|
||||||
if SERVER_STATE:
|
|
||||||
SERVER_STATE.Shutdown()
|
|
||||||
|
|
||||||
|
|
||||||
def _SetUserOptions( options ):
|
|
||||||
global SERVER_STATE
|
|
||||||
|
|
||||||
user_options_store.SetAll( options )
|
|
||||||
SERVER_STATE = server_state.ServerState( options )
|
|
||||||
|
|
||||||
|
|
||||||
def SetServerStateToDefaults():
|
|
||||||
global SERVER_STATE, LOGGER
|
|
||||||
LOGGER = logging.getLogger( __name__ )
|
|
||||||
user_options_store.LoadDefaults()
|
|
||||||
SERVER_STATE = server_state.ServerState( user_options_store.GetAll() )
|
|
||||||
extra_conf_store.Reset()
|
|
||||||
|
|
||||||
|
|
||||||
def Main():
|
def Main():
|
||||||
global LOGGER
|
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument( '--host', type = str, default = 'localhost',
|
parser.add_argument( '--host', type = str, default = 'localhost',
|
||||||
help = 'server hostname')
|
help = 'server hostname')
|
||||||
@ -237,18 +47,31 @@ def Main():
|
|||||||
help = 'file with user options, in JSON format' )
|
help = 'file with user options, in JSON format' )
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
if args.options_file:
|
|
||||||
_SetUserOptions( json.load( open( args.options_file, 'r' ) ) )
|
|
||||||
|
|
||||||
numeric_level = getattr( logging, args.log.upper(), None )
|
numeric_level = getattr( logging, args.log.upper(), None )
|
||||||
if not isinstance( numeric_level, int ):
|
if not isinstance( numeric_level, int ):
|
||||||
raise ValueError( 'Invalid log level: %s' % args.log )
|
raise ValueError( 'Invalid log level: %s' % args.log )
|
||||||
|
|
||||||
|
# Has to be called before any call to logging.getLogger()
|
||||||
logging.basicConfig( format = '%(asctime)s - %(levelname)s - %(message)s',
|
logging.basicConfig( format = '%(asctime)s - %(levelname)s - %(message)s',
|
||||||
level = numeric_level )
|
level = numeric_level )
|
||||||
|
|
||||||
LOGGER = logging.getLogger( __name__ )
|
options = None
|
||||||
waitress.serve( app, host = args.host, port = args.port, threads = 10 )
|
if args.options_file:
|
||||||
|
options = json.load( open( args.options_file, 'r' ) )
|
||||||
|
user_options_store.SetAll( options )
|
||||||
|
# This ensures that ycm_core is not loaded before extra conf preload
|
||||||
|
# was run.
|
||||||
|
YcmCoreSanityCheck()
|
||||||
|
extra_conf_store.CallGlobalExtraConfYcmCorePreloadIfExists()
|
||||||
|
|
||||||
|
# This can't be a top-level import because it transitively imports ycm_core
|
||||||
|
# which we want to be imported ONLY after extra conf preload has executed.
|
||||||
|
import handlers
|
||||||
|
handlers.UpdateUserOptions( options )
|
||||||
|
waitress.serve( handlers.app,
|
||||||
|
host = args.host,
|
||||||
|
port = args.port,
|
||||||
|
threads = 10 )
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
Loading…
Reference in New Issue
Block a user