Hierarchical variables and scopes

Maintain the variables in a simple tree and allow the user to
expand/collapse in the dumbest way possible. This means we don't
recursively consume all of the possible variable scopes.
This commit is contained in:
Ben Jackson 2018-05-20 19:07:44 +01:00
commit a535d65b26
2 changed files with 59 additions and 32 deletions

View file

@ -72,6 +72,7 @@ class DebugSession( object ):
self._currentThread = None
self._currentFrame = None
self._scopes = []
self._line_to_variable = {}
self._SetUpUI()
@ -99,6 +100,8 @@ class DebugSession( object ):
self._outputBuffer = vim.current.buffer
vim.command( 'spl' )
vim.command( 'enew' )
vim.command(
'nnoremap <buffer> <CR> :call vimspector#ExpandVariable()<CR>' )
self._localsBuffer = vim.current.buffer
SetUpScratchBuffer( self._threadsBuffer )
@ -120,58 +123,56 @@ class DebugSession( object ):
self._codeWindow.cursor = ( frame[ 'line' ], frame[ 'column' ] )
self._LoadScopes( frame )
def _LoadScopes( self, frame ):
def draw_variables( variables, indent ):
for variable in variables:
self._localsBuffer.append( '{indent}{name} ({type_}): {value}'.format(
def _DrawVariables( self, variables, indent ):
for variable in variables:
self._localsBuffer.append(
'{indent}{icon} {name} ({type_}): {value}'.format(
indent = ' ' * indent,
icon = '+' if ( variable[ 'variablesReference' ] > 0 and
'_variables' not in variable ) else '-',
name = variable[ 'name' ],
type_ = variable.get( 'type', '<unknown type>' ),
value = variable[ 'value' ] ) )
self._line_to_variable[ len( self._localsBuffer ) ] = variable
if '_variables' in variable:
draw_variables( variable[ '_variables' ], indent + 2 )
if '_variables' in variable:
self._DrawVariables( variable[ '_variables' ], indent + 2 )
def draw_scopes():
with ModifiableScratchBuffer( self._localsBuffer ):
self._localsBuffer[:] = None
for scope in self._scopes:
self._localsBuffer.append( scope[ 'name' ] )
if '_variables' in scope:
indent = 2
draw_variables( scope[ '_variables' ], indent )
def _DrawScopes( self ):
current_pos = vim.current.window.cursor
with ModifiableScratchBuffer( self._localsBuffer ):
self._localsBuffer[:] = None
for scope in self._scopes:
self._localsBuffer.append( 'Scope: ' + scope[ 'name' ] )
if '_variables' in scope:
indent = 2
self._DrawVariables( scope[ '_variables' ], indent )
def variables_consumer( parent, message ):
_logger.debug( 'consuming variables with parent {0}'.format( parent ) )
for variable in message[ 'body' ][ 'variables' ]:
if '_variables' not in parent:
parent[ '_variables' ] = []
vim.current.window.cursor = current_pos
parent[ '_variables' ].append( variable )
if variable[ 'variablesReference' ] > 0:
self._DoRequest( partial( variables_consumer, variable ), {
'command': 'variables',
'arguments': {
'variablesReference': variable[ 'variablesReference' ]
},
} )
draw_scopes()
def _ConsumeVariables( self, parent, message ):
for variable in message[ 'body' ][ 'variables' ]:
if '_variables' not in parent:
parent[ '_variables' ] = []
parent[ '_variables' ].append( variable )
self._DrawScopes()
def _LoadScopes( self, frame ):
def scopes_consumer( message ):
self._scopes = []
for scope in message[ 'body' ][ 'scopes' ]:
self._scopes.append( scope )
self._DoRequest( partial( variables_consumer, scope ), {
self._DoRequest( partial( self._ConsumeVariables, scope ), {
'command': 'variables',
'arguments': {
'variablesReference': scope[ 'variablesReference' ]
},
} )
draw_scopes()
self._DrawScopes()
self._DoRequest( scopes_consumer, {
'command': 'scopes',
@ -235,6 +236,28 @@ class DebugSession( object ):
},
} )
def ExpandVariable( self ):
current_line = vim.current.window.cursor[ 0 ]
_logger.debug( 'ExpandVariable: {0} with {1}'.format(
current_line,
self._line_to_variable ) )
if current_line not in self._line_to_variable:
vim.command( 'echo "No variable found on that line"' )
return
variable = self._line_to_variable[ current_line ]
if '_variables' in variable:
del variable[ '_variables' ]
self._DrawScopes()
else:
self._DoRequest( partial( self._ConsumeVariables, variable ), {
'command': 'variables',
'arguments': {
'variablesReference': variable[ 'variablesReference' ]
},
} )
def _DoRequest( self, handler, msg ):
this_id = self._next_message_id