WIP: Guided config for c++

This adds some templates to the gadget config, organised by "category"
(human-readable) and presented in a menu. The configuration thus created
is just run normally through the variable replacements.

Also:
- Fix default value replacements which come from the calculus
- Add default-value _lists_. This uses select-from-list UI. Useful for
  enumerated values. Allow specifying a default.
This commit is contained in:
Ben Jackson 2020-08-23 14:49:36 +01:00
commit acd0f31573
5 changed files with 186 additions and 34 deletions

View file

@ -76,10 +76,10 @@ class DebugSession( object ):
self._server_capabilities = {}
self.ClearTemporaryBreakpoints()
def GetConfigurations( self, adapters ):
def GetConfigurations( self ):
current_file = utils.GetBufferFilepath( vim.current.buffer )
filetypes = utils.GetBufferFiletypes( vim.current.buffer )
return launch.GetConfigurations( adapters, current_file, filetypes )
return launch.GetConfigurations( {}, current_file, filetypes )[ 1 ]
def Start( self, launch_variables = None ):
@ -96,33 +96,40 @@ class DebugSession( object ):
current_file = utils.GetBufferFilepath( vim.current.buffer )
filetypes = utils.GetBufferFiletypes( vim.current.buffer )
adapters = launch.GetAdapters( current_file, filetypes )
adapters = launch.GetAdapters( current_file )
launch_config_file, configurations = launch.GetConfigurations( adapters,
current_file,
filetypes )
if not configurations:
utils.UserMessage( 'Unable to find any debug configurations. '
'You need to tell vimspector how to launch your '
'application.' )
return
if launch_config_file:
self._workspace_root = os.path.dirname( launch_config_file )
else:
self._workspace_root = os.path.dirname( current_file )
configuration_name, configuration = launch.SelectConfiguration(
launch_variables,
configurations )
if not configurations:
configuration_name, configuration = launch.SuggestConfiguration(
filetypes )
else:
configuration_name, configuration = launch.SelectConfiguration(
launch_variables,
configurations )
if not configuration:
utils.UserMessage( 'Unable to find any debug configurations. '
'You need to tell vimspector how to launch your '
'application.' )
return
adapter = launch.SelectAdapter( self._api_prefix,
self,
configuration_name,
configuration,
adapters,
launch_variables,
self )
launch_variables )
if not adapter:
return
try:
launch.ResolveConfiguration( adapter,
configuration,

View file

@ -49,6 +49,40 @@ GADGETS = {
}
},
},
'templates': [
{
'description': 'gdb/lldb local debugging with vscode-cpptools',
'filetypes': ( 'c', 'cpp', 'objc', 'rust' ),
'configurations': [
{
'description': 'Launch local process',
'launch_configuration': {
'adapter': 'vscode-cpptools',
'configuration': {
'request': 'launch',
'program': '${Binary:${fileBasenameNoExtension\\}}',
'args': [ '*${CommandLineArguments}' ],
'cwd': '${WorkingDir:${fileDirname\\}}',
'externalConsole#json': '${UseExternalConsole:true\nfalse}',
'stopAtEntry#json': '${StopAtEntry:true\nfalse}',
'MIMode': '${Debugger:gdb\nlldb}',
}
}
},
{
'description': 'Attach to local process by PID',
'launch_configuration': {
'adapter': 'vscode-cpptools',
'configuration': {
'request': 'attach',
'program': '${Binary:${fileBasenameNoExtension\\}}',
'MIMode': '${Debugger:gdb\nlldb}',
}
}
}
]
}
]
},
'linux': {
'file_name': 'cpptools-linux.vsix',
@ -452,6 +486,42 @@ GADGETS = {
},
'all': {
'version': 'v1.5.3',
'templates': [
{
'description': 'LLDB local debugging with CodeLLDB',
'filetypes': ( 'c', 'cpp', 'objc', 'rust' ),
'configurations': [
{
'description': 'Launch local process',
'launch_configuration': {
'adapter': 'CodeLLDB',
'configuration': {
'request': 'launch',
'program': '${Binary:${fileBasenameNoExtension\\}}',
'args': [ '*${CommandLineArguments}' ],
'cwd': '${WorkingDir:${fileDirname\\}}',
'terminal': '${Console:none\nintegrated\nexternal}',
'stopOnEntry#json': '${StopOnEntry:true\nfalse}',
'expressions': '${ExpressionType:native\nsimple\npython}'
}
}
},
{
'description': 'Attach to local process by PID',
'launch_configuration': {
'adapter': 'CodeLLDB',
'configuration': {
'request': 'attach',
'program': '${Binary:${fileBasenameNoExtension\\}}',
'pid#json': "${pid}",
'stopOnEntry#json': '${StopOnEntry:true\nfalse}',
'expressions': '${ExpressionType:native\nsimple\npython}'
}
}
}
]
}
]
},
'macos': {
'file_name': 'codelldb-x86_64-darwin.vsix',
@ -486,6 +556,7 @@ GADGETS = {
'name': 'CodeLLDB',
'type': 'CodeLLDB',
"command": [
# FIXME: This probably doesn't work on windows.
"${gadgetDir}/CodeLLDB/adapter/codelldb",
"--port", "${unusedLocalPort}"
],

View file

@ -19,7 +19,7 @@ import json
import glob
import shlex
from vimspector import install, installer, utils
from vimspector import install, installer, utils, gadgets
from vimspector.vendor.json_minify import minify
_logger = logging.getLogger( __name__ )
@ -33,7 +33,7 @@ USER_CHOICES = {}
def GetAdapters( current_file, filetypes ):
def GetAdapters( current_file ):
adapters = {}
for gadget_config_file in PathsToAllGadgetConfigs( VIMSPECTOR_HOME,
current_file ):
@ -120,13 +120,57 @@ def SelectConfiguration( launch_variables, configurations ):
return configuration_name, configuration
def SuggestConfiguration( filetypes ):
nothing = None, None
templates = []
filetypes = set( filetypes )
for gadget_name, gadget in gadgets.GADGETS.items():
spec = {}
spec.update( gadget.get( 'all', {} ) )
spec.update( gadget.get( install.GetOS(), {} ) )
for template in spec.get( 'templates', [] ):
if filetypes.intersection( template.get( 'filetypes', set() ) ):
templates.append( template )
if not templates:
return nothing
template_idx = utils.SelectFromList(
'No debug configurations were found for this project, '
'Would you like to use one of the following templates?',
[ t[ 'description' ] for t in templates ],
ret = 'index' )
if template_idx is None:
return nothing
template = templates[ template_idx ]
config_index = utils.SelectFromList(
'Which configuration?',
[ c[ 'description' ] for c in template[ 'configurations' ] ],
ret = 'index' )
if config_index is None:
return nothing
configuration = template[ 'configurations' ][ config_index ]
configuration_name = utils.AskForInput( 'Give the config a name: ',
configuration[ 'description' ] )
return configuration_name, configuration[ 'launch_configuration' ]
def SelectAdapter( api_prefix,
debug_session,
configuration_name,
configuration,
adapters,
launch_variables,
debug_session ):
adapter = configuration.get( 'adapter' )
launch_variables ):
adapter = configuration.get( 'adapter' )
if isinstance( adapter, str ):
adapter_dict = adapters.get( adapter )
@ -136,7 +180,7 @@ def SelectAdapter( api_prefix,
if suggested_gadgets:
response = utils.AskForInput(
f"The specified adapter '{adapter}' is not "
"installed. Would you like to install the following gadgets? ",
"installed. Would you like to install the following gadgets? ",
' '.join( suggested_gadgets ) )
if response:
new_launch_variables = dict( launch_variables )
@ -147,7 +191,7 @@ def SelectAdapter( api_prefix,
False, # Don't leave open
*shlex.split( response ),
then = debug_session.Start( new_launch_variables ) )
return
return None
elif response is None:
return None

View file

@ -333,25 +333,41 @@ def UserMessage( msg, persist=False, error=False ):
@contextlib.contextmanager
def InputSave():
def AskingForUserInput():
vim.eval( 'inputsave()' )
try:
yield
finally:
vim.eval( 'inputrestore()' )
# Clear the command line so that subsequent
vim.command( 'redraw' )
def SelectFromList( prompt, options ):
with InputSave():
def SelectFromList( prompt,
options,
ret = 'text',
default_index = None ):
with AskingForUserInput():
display_options = [ prompt ]
display_options.extend( [ '{0}: {1}'.format( i + 1, v )
for i, v in enumerate( options ) ] )
display_options.extend( [
'{0}{1}: {2}'.format( '*' if i == default_index else ' ',
i + 1,
v )
for i, v in enumerate( options )
] )
try:
selection = int( vim.eval(
'inputlist( ' + json.dumps( display_options ) + ' )' ) ) - 1
if selection < 0 or selection >= len( options ):
return None
return options[ selection ]
if default_index is not None:
selection = default_index
else:
return None
if ret == 'text':
return options[ selection ]
else:
return selection
except ( KeyboardInterrupt, vim.error ):
return None
@ -362,7 +378,7 @@ def AskForInput( prompt, default_value = None ):
else:
default_option = ", '{}'".format( Escape( default_value ) )
with InputSave():
with AskingForUserInput():
try:
return vim.eval( "input( '{}' {} )".format( Escape( prompt ),
default_option ) )
@ -531,11 +547,25 @@ def ExpandReferencesInString( orig_s,
if default_value is None and e.default_value is not None:
try:
default_value = _Substitute( e.default_value, mapping )
except MissingSubstitution:
default_value = e.default_value
except MissingSubstitution as f:
if f.name in calculus:
default_value = calculus[ f.name ]()
else:
default_value = e.default_value
mapping[ key ] = AskForInput( 'Enter value for {}: '.format( key ),
default_value )
value_list = None
if default_value:
default_value_list = default_value.split( '\n' )
if len( default_value_list ) > 1:
value_list = default_value_list
if value_list:
mapping[ key ] = SelectFromList( f'Select value for { key }: ',
default_value_list,
default_index = 0 )
else:
mapping[ key ] = AskForInput( 'Enter value for {}: '.format( key ),
default_value )
if mapping[ key ] is None:
raise KeyboardInterrupt