Skip to content

Commit

Permalink
Auto merge of #264 - micbou:subcommands-completer-api, r=puremourning
Browse files Browse the repository at this point in the history
Move subcommands logic to Completer class

Currently, each semantic completer reimplements the `OnUserCommand` and `DefinedSubcommands` in about the same way. To avoid duplication of code, this PR moves the logic of these methods to the `Completer` class. Completers only need to implement a new function `GetSubcommandsMap` that return a map between the subcommands name and the methods to call. The map structure is explained in the method documentation.

Minor improvements:
 - `DefinedSubcommands` now returns an alphabetically sorted list (tests updated to reflect this);
 - Fix issue in PR #263.

Important: if this PR is merged, the `OmniCompleter` class will need to be updated in YCM by inheriting from `GeneralCompleter` instead of `Completer` or implementing the `GetSubcommandsMap` method.

<!-- Reviewable:start -->
[<img src="https://reviewable.io/review_button.png" height=40 alt="Review on Reviewable"/>](https://reviewable.io/reviews/valloric/ycmd/264)
<!-- Reviewable:end -->
  • Loading branch information
homu committed Nov 29, 2015
2 parents b601c14 + f45c847 commit 5960cc2
Show file tree
Hide file tree
Showing 7 changed files with 170 additions and 212 deletions.
33 changes: 28 additions & 5 deletions ycmd/completers/completer.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,10 @@ class Completer( object ):
command :YcmCompleter and is passed all extra arguments used on command
invocation (e.g. OnUserCommand(['first argument', 'second'])). This can be
used for completer-specific commands such as reloading external configuration.
When the command is called with no arguments you should print a short summary
of the supported commands or point the user to the help section where this
information can be found.
Do not override this function. Instead, you need to implement the
GetSubcommandsMap method. It should return a map between the user commands
and the methods of your completer. See the documentation of this method for
more informations on how to implement it.
Override the Shutdown() member function if your Completer subclass needs to do
custom cleanup logic on server shutdown."""
Expand Down Expand Up @@ -190,7 +191,19 @@ def ComputeCandidatesInner( self, request_data ):


def DefinedSubcommands( self ):
return []
return sorted( self.GetSubcommandsMap().keys() )


def GetSubcommandsMap( self ):
"""This method should return a dictionary where each key represents the
completer command name and its value is a lambda function of this form:
( self, request_data ) -> method
where "method" is the call to the completer method with corresponding
parameters. See the already implemented completers for examples.
"""
return {}


def UserCommandsHelpMessage( self ):
Expand Down Expand Up @@ -242,7 +255,17 @@ def OnInsertLeave( self, request_data ):


def OnUserCommand( self, arguments, request_data ):
raise NotImplementedError( NO_USER_COMMANDS )
if not arguments:
raise ValueError( self.UserCommandsHelpMessage() )

command_map = self.GetSubcommandsMap()

try:
command = command_map[ arguments[ 0 ] ]
except KeyError:
raise ValueError( self.UserCommandsHelpMessage() )

return command( self, request_data )


def OnCurrentIdentifierFinished( self, request_data ):
Expand Down
123 changes: 33 additions & 90 deletions ycmd/completers/cpp/clang_completer.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,96 +106,39 @@ def ComputeCandidatesInner( self, request_data ):
return [ ConvertCompletionData( x ) for x in results ]


def DefinedSubcommands( self ):
return [ 'GoToDefinition',
'GoToDeclaration',
'GoTo',
'GoToImprecise',
'GoToInclude',
'ClearCompilationFlagCache',
'GetType',
'GetParent',
'FixIt',
'GetDoc',
'GetDocQuick' ]


def OnUserCommand( self, arguments, request_data ):
if not arguments:
raise ValueError( self.UserCommandsHelpMessage() )

# command_map maps: command -> { method, args }
#
# where:
# "command" is the completer command entered by the user
# (e.g. GoToDefinition)
# "method" is a method to call for that command
# (e.g. self._GoToDefinition)
# "args" is a dictionary of
# "method_argument" : "value" ...
# which defines the kwargs (via the ** double splat)
# when calling "method"
command_map = {
'GoToDefinition' : {
'method' : self._GoToDefinition,
'args' : { 'request_data' : request_data }
},
'GoToDeclaration' : {
'method' : self._GoToDeclaration,
'args' : { 'request_data' : request_data }
},
'GoTo' : {
'method' : self._GoTo,
'args' : { 'request_data' : request_data }
},
'GoToImprecise' : {
'method' : self._GoToImprecise,
'args' : { 'request_data' : request_data }
},
'GoToInclude' : {
'method' : self._GoToInclude,
'args' : { 'request_data' : request_data }
},
'ClearCompilationFlagCache' : {
'method' : self._ClearCompilationFlagCache,
'args' : { }
},
'GetType' : {
'method' : self._GetSemanticInfo,
'args' : { 'request_data' : request_data,
'func' : 'GetTypeAtLocation' }
},
'GetParent' : {
'method' : self._GetSemanticInfo,
'args' : { 'request_data' : request_data,
'func' : 'GetEnclosingFunctionAtLocation' }
},
'FixIt' : {
'method' : self._FixIt,
'args' : { 'request_data' : request_data }
},
'GetDoc' : {
'method' : self._GetSemanticInfo,
'args' : { 'request_data' : request_data,
'reparse' : True,
'func' : 'GetDocsForLocationInFile',
'response_buider' : _BuildGetDocResponse, }
},
'GetDocQuick' : {
'method' : self._GetSemanticInfo,
'args' : { 'request_data' : request_data,
'reparse' : False,
'func' : 'GetDocsForLocationInFile',
'response_buider' : _BuildGetDocResponse, }
},
def GetSubcommandsMap( self ):
return {
'GoToDefinition' : ( lambda self, request_data:
self._GoToDefinition( request_data ) ),
'GoToDeclaration' : ( lambda self, request_data:
self._GoToDeclaration( request_data ) ),
'GoTo' : ( lambda self, request_data:
self._GoTo( request_data ) ),
'GoToImprecise' : ( lambda self, request_data:
self._GoToImprecise( request_data ) ),
'GoToInclude' : ( lambda self, request_data:
self._GoToInclude( request_data ) ),
'ClearCompilationFlagCache': ( lambda self, request_data:
self._ClearCompilationFlagCache() ),
'GetType' : ( lambda self, request_data:
self._GetSemanticInfo( request_data, func = 'GetTypeAtLocation' ) ),
'GetParent' : ( lambda self, request_data:
self._GetSemanticInfo( request_data,
func = 'GetEnclosingFunctionAtLocation' ) ),
'FixIt' : ( lambda self, request_data:
self._FixIt( request_data ) ),
'GetDoc' : ( lambda self, request_data:
self._GetSemanticInfo( request_data,
reparse = True,
func = 'GetDocsForLocationInFile',
response_builder = _BuildGetDocResponse ) ),
'GetDocQuick' : ( lambda self, request_data:
self._GetSemanticInfo( request_data,
reparse = False,
func = 'GetDocsForLocationInFile',
response_builder = _BuildGetDocResponse ) ),
}

try:
command_def = command_map[ arguments[ 0 ] ]
except KeyError:
raise ValueError( self.UserCommandsHelpMessage() )

return command_def[ 'method' ]( **( command_def[ 'args' ] ) )

def _LocationForGoTo( self, goto_function, request_data, reparse = True ):
filename = request_data[ 'filepath' ]
Expand Down Expand Up @@ -302,7 +245,7 @@ def _GoToInclude( self, request_data ):
def _GetSemanticInfo( self,
request_data,
func,
response_buider = responses.BuildDisplayMessageResponse,
response_builder = responses.BuildDisplayMessageResponse,
reparse = True ):
filename = request_data[ 'filepath' ]
if not filename:
Expand All @@ -327,7 +270,7 @@ def _GetSemanticInfo( self,
if not message:
message = "No semantic information available"

return response_buider( message )
return response_builder( message )

def _ClearCompilationFlagCache( self ):
self._flags.Clear()
Expand Down
124 changes: 73 additions & 51 deletions ycmd/completers/cs/cs_completer.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,79 @@ def FilterAndSortCandidates( self, candidates, query ):
return result


def DefinedSubcommands( self ):
return CsharpSolutionCompleter.subcommands.keys()
def GetSubcommandsMap( self ):
return {
'StartServer' : ( lambda self, request_data:
self._SolutionSubcommand( request_data,
method = '_StartServer',
no_request_data = True ) ),
'StopServer' : ( lambda self, request_data:
self._SolutionSubcommand( request_data,
method = '_StopServer',
no_request_data = True ) ),
'RestartServer' : ( lambda self, request_data:
self._SolutionSubcommand( request_data,
method = '_RestartServer',
no_request_data = True ) ),
'ReloadSolution' : ( lambda self, request_data:
self._SolutionSubcommand( request_data,
method = '_ReloadSolution',
no_request_data = True ) ),
'SolutionFile' : ( lambda self, request_data:
self._SolutionSubcommand( request_data,
method = '_SolutionFile',
no_request_data = True ) ),
'GoToDefinition' : ( lambda self, request_data:
self._SolutionSubcommand( request_data,
method = '_GoToDefinition' ) ),
'GoToDeclaration' : ( lambda self, request_data:
self._SolutionSubcommand( request_data,
method = '_GoToDefinition' ) ),
'GoTo' : ( lambda self, request_data:
self._SolutionSubcommand( request_data,
method = '_GoToImplementation',
fallback_to_declaration = True ) ),
'GoToDefinitionElseDeclaration' : ( lambda self, request_data:
self._SolutionSubcommand( request_data,
method = '_GoToDefinition' ) ),
'GoToImplementation' : ( lambda self, request_data:
self._SolutionSubcommand( request_data,
method = '_GoToImplementation',
fallback_to_declaration = False ) ),
'GoToImplementationElseDeclaration': ( lambda self, request_data:
self._SolutionSubcommand( request_data,
method = '_GoToImplementation',
fallback_to_declaration = True ) ),
'GetType' : ( lambda self, request_data:
self._SolutionSubcommand( request_data,
method = '_GetType' ) ),
'FixIt' : ( lambda self, request_data:
self._SolutionSubcommand( request_data,
method = '_FixIt' ) ),
'GetDoc' : ( lambda self, request_data:
self._SolutionSubcommand( request_data,
method = '_GetDoc' ) ),
'ServerRunning' : ( lambda self, request_data:
self._SolutionSubcommand( request_data,
method = 'ServerIsRunning',
no_request_data = True ) ),
'ServerReady' : ( lambda self, request_data:
self._SolutionSubcommand( request_data,
method = 'ServerIsReady',
no_request_data = True ) ),
'ServerTerminated' : ( lambda self, request_data:
self._SolutionSubcommand( request_data,
method = 'ServerTerminated',
no_request_data = True ) )
}


def _SolutionSubcommand( self, request_data, method,
no_request_data = False, **kwargs ):
solutioncompleter = self._GetSolutionCompleter( request_data )
if not no_request_data:
kwargs[ 'request_data' ] = request_data
return getattr( solutioncompleter, method )( **kwargs )


def OnFileReadyToParse( self, request_data ):
Expand Down Expand Up @@ -210,18 +281,6 @@ def GetDetailedDiagnostic( self, request_data ):
closest_diagnostic.text_ )


def OnUserCommand( self, arguments, request_data ):
if not arguments:
raise ValueError( self.UserCommandsHelpMessage() )

command = arguments[ 0 ]
if command in CsharpSolutionCompleter.subcommands:
solutioncompleter = self._GetSolutionCompleter( request_data )
return solutioncompleter.Subcommand( command, arguments, request_data )
else:
raise ValueError( self.UserCommandsHelpMessage() )


def DebugInfo( self, request_data ):
solutioncompleter = self._GetSolutionCompleter( request_data )
if solutioncompleter.ServerIsRunning():
Expand Down Expand Up @@ -275,34 +334,6 @@ def _GetSolutionFile( self, filepath ):


class CsharpSolutionCompleter:
subcommands = {
'StartServer': ( lambda self, request_data: self._StartServer() ),
'StopServer': ( lambda self, request_data: self._StopServer() ),
'RestartServer': ( lambda self, request_data: self._RestartServer() ),
'ReloadSolution': ( lambda self, request_data: self._ReloadSolution() ),
'SolutionFile': ( lambda self, request_data: self._SolutionFile() ),
'GoToDefinition': ( lambda self, request_data: self._GoToDefinition(
request_data ) ),
'GoToDeclaration': ( lambda self, request_data: self._GoToDefinition(
request_data ) ),
'GoTo': ( lambda self, request_data: self._GoToImplementation(
request_data, True ) ),
'GoToDefinitionElseDeclaration': ( lambda self, request_data:
self._GoToDefinition( request_data ) ),
'GoToImplementation': ( lambda self, request_data:
self._GoToImplementation( request_data, False ) ),
'GoToImplementationElseDeclaration': ( lambda self, request_data:
self._GoToImplementation( request_data, True ) ),
'GetType': ( lambda self, request_data: self._GetType(
request_data ) ),
'FixIt': ( lambda self, request_data: self._FixIt( request_data ) ),
'GetDoc': ( lambda self, request_data: self._GetDoc( request_data ) ),
'ServerRunning': ( lambda self, request_data: self.ServerIsRunning() ),
'ServerReady': ( lambda self, request_data: self.ServerIsReady() ),
'ServerTerminated': ( lambda self, request_data: self.ServerTerminated() ),
}


def __init__( self, solution_path, keep_logfiles, desired_omnisharp_port ):
self._logger = logging.getLogger( __name__ )
self._solution_path = solution_path
Expand All @@ -314,15 +345,6 @@ def __init__( self, solution_path, keep_logfiles, desired_omnisharp_port ):
self._desired_omnisharp_port = desired_omnisharp_port;


def Subcommand( self, command, arguments, request_data ):
command_lamba = CsharpSolutionCompleter.subcommands[ command ]
return command_lamba( self, request_data )


def DefinedSubcommands( self ):
return CsharpSolutionCompleter.subcommands.keys()


def CodeCheck( self, request_data ):
filename = request_data[ 'filepath' ]
if not filename:
Expand Down
Loading

0 comments on commit 5960cc2

Please sign in to comment.