Separate watches and scopes

They are related (and share code) but make more sense separately
This commit is contained in:
Ben Jackson 2018-06-03 00:20:07 +01:00
commit fa775f4499
4 changed files with 82 additions and 43 deletions

View file

@ -277,8 +277,16 @@ class DebugSession( object ):
# Variables # Variables
vim.command( 'spl' ) vim.command( 'spl' )
vim.command( 'enew' ) vim.command( 'enew' )
vars_win = vim.current.window
# Watches
vim.command( 'spl' )
vim.command( 'enew' )
watch_win = vim.current.window
self._variablesView = variables.VariablesView( self._connection, self._variablesView = variables.VariablesView( self._connection,
vim.current.buffer ) vars_win,
watch_win )
with utils.TemporaryVimOption( 'splitbelow', True ): with utils.TemporaryVimOption( 'splitbelow', True ):
@ -520,7 +528,8 @@ class DebugSession( object ):
file_name ) ) file_name ) )
def OnEvent_output( self, message ): def OnEvent_output( self, message ):
self._outputView.OnOutput( message[ 'body' ] ) if self._outputView:
self._outputView.OnOutput( message[ 'body' ] )
def OnEvent_stopped( self, message ): def OnEvent_stopped( self, message ):
event = message[ 'body' ] event = message[ 'body' ]

View file

@ -64,7 +64,9 @@ class StackTraceView( object ):
if not message[ 'body' ][ 'threads' ]: if not message[ 'body' ][ 'threads' ]:
# This is a protocol error. It is required to return at least one! # This is a protocol error. It is required to return at least one!
raise ValueError( 'Server returned no threads. Is it running?' ) utils.UserMessage( 'Server returned no threads. Is it running?',
persist = True )
return
for thread in message[ 'body' ][ 'threads' ]: for thread in message[ 'body' ][ 'threads' ]:
self._threads.append( thread ) self._threads.append( thread )

View file

@ -76,6 +76,7 @@ def RestoreCursorPosition():
@contextlib.contextmanager @contextlib.contextmanager
def RestorCurrentWindow(): def RestorCurrentWindow():
# TODO: Don't trigger autoccommands when shifting windows
old_window = vim.current.window old_window = vim.current.window
try: try:
yield yield
@ -85,6 +86,7 @@ def RestorCurrentWindow():
@contextlib.contextmanager @contextlib.contextmanager
def RestoreCurrentBuffer( window ): def RestoreCurrentBuffer( window ):
# TODO: Don't trigger autoccommands when shifting buffers
old_buffer = window.buffer old_buffer = window.buffer
try: try:
yield yield

View file

@ -14,20 +14,21 @@
# limitations under the License. # limitations under the License.
import vim import vim
from collections import namedtuple
from functools import partial from functools import partial
from vimspector import utils from vimspector import utils
View = namedtuple( 'View', [ 'win', 'lines', 'draw' ] )
class VariablesView( object ): class VariablesView( object ):
def __init__( self, connection, buf ): def __init__( self, connection, variables_win, watches_win ):
vim.current.buffer = buf self._vars = View( variables_win, {}, self._DrawScopes )
self._watch = View( watches_win, {}, self._DrawWatches )
self._buf = buf
self._connection = connection self._connection = connection
# Allows us to hit <CR> to expand/collapse variables # Allows us to hit <CR> to expand/collapse variables
self._line_to_variable = {} vim.current.window = self._vars.win
vim.command( vim.command(
'nnoremap <buffer> <CR> :call vimspector#ExpandVariable()<CR>' ) 'nnoremap <buffer> <CR> :call vimspector#ExpandVariable()<CR>' )
@ -45,11 +46,15 @@ class VariablesView( object ):
# above. It also has a special _line key which is where we printed it (last) # above. It also has a special _line key which is where we printed it (last)
self._watches = [] self._watches = []
# Allows us to delete manual watches # Allows us to hit <CR> to expand/collapse variables
vim.current.window = self._watch.win
vim.command(
'nnoremap <buffer> <CR> :call vimspector#ExpandVariable()<CR>' )
vim.command( vim.command(
'nnoremap <buffer> <DEL> :call vimspector#DeleteWatch()<CR>' ) 'nnoremap <buffer> <DEL> :call vimspector#DeleteWatch()<CR>' )
utils.SetUpScratchBuffer( self._buf, 'vimspector.Variables' ) utils.SetUpScratchBuffer( self._vars.win.buffer, 'vimspector.Variables' )
utils.SetUpScratchBuffer( self._watch.win.buffer, 'vimspector.Watches' )
has_balloon = int( vim.eval( "has( 'balloon_eval' )" ) ) has_balloon = int( vim.eval( "has( 'balloon_eval' )" ) )
has_balloon_term = int( vim.eval( "has( 'balloon_eval_term' )" ) ) has_balloon_term = int( vim.eval( "has( 'balloon_eval_term' )" ) )
@ -73,8 +78,10 @@ class VariablesView( object ):
def Clear( self ): def Clear( self ):
with utils.ModifiableScratchBuffer( self._buf ): with utils.ModifiableScratchBuffer( self._vars.win.buffer ):
self._buf[:] = None self._vars.win.buffer[:] = None
with utils.ModifiableScratchBuffer( self._watch.win.buffer ):
self._watch.win.buffer[:] = None
def ConnectionClosed( self ): def ConnectionClosed( self ):
self.Clear() self.Clear()
@ -91,14 +98,16 @@ class VariablesView( object ):
self._scopes = [] self._scopes = []
for scope in message[ 'body' ][ 'scopes' ]: for scope in message[ 'body' ][ 'scopes' ]:
self._scopes.append( scope ) self._scopes.append( scope )
self._connection.DoRequest( partial( self._ConsumeVariables, scope ), { self._connection.DoRequest( partial( self._ConsumeVariables,
self._DrawScopes,
scope ), {
'command': 'variables', 'command': 'variables',
'arguments': { 'arguments': {
'variablesReference': scope[ 'variablesReference' ] 'variablesReference': scope[ 'variablesReference' ]
}, },
} ) } )
self._DrawScopesAndWatches() self._DrawScopes()
self._connection.DoRequest( scopes_consumer, { self._connection.DoRequest( scopes_consumer, {
'command': 'scopes', 'command': 'scopes',
@ -117,7 +126,8 @@ class VariablesView( object ):
self.EvaluateWatches() self.EvaluateWatches()
def DeleteWatch( self ): def DeleteWatch( self ):
if vim.current.window.buffer != self._buf: if vim.current.window != self._watch.win:
utils.UserMessage( 'Not a watch window' )
return return
current_line = vim.current.window.cursor[ 0 ] current_line = vim.current.window.cursor[ 0 ]
@ -125,9 +135,12 @@ class VariablesView( object ):
for index, watch in enumerate( self._watches ): for index, watch in enumerate( self._watches ):
if '_line' in watch and watch[ '_line' ] == current_line: if '_line' in watch and watch[ '_line' ] == current_line:
del self._watches[ index ] del self._watches[ index ]
self._DrawScopesAndWatches() utils.UserMessage( 'Deleted' )
self._DrawWatches()
return return
utils.UserMessage( 'No watch found' )
def EvaluateWatches( self ): def EvaluateWatches( self ):
for watch in self._watches: for watch in self._watches:
self._connection.DoRequest( partial( self._UpdateWatchExpression, self._connection.DoRequest( partial( self._UpdateWatchExpression,
@ -138,22 +151,26 @@ class VariablesView( object ):
def _UpdateWatchExpression( self, watch, message ): def _UpdateWatchExpression( self, watch, message ):
watch[ '_result' ] = message[ 'body' ] watch[ '_result' ] = message[ 'body' ]
self._DrawScopesAndWatches() self._DrawWatches()
def ExpandVariable( self ): def ExpandVariable( self ):
if vim.current.window.buffer != self._buf: if vim.current.window == self._vars.win:
view = self._vars
elif vim.current.window == self._watch.win:
view = self._watch
else:
return return
current_line = vim.current.window.cursor[ 0 ] current_line = vim.current.window.cursor[ 0 ]
if current_line not in self._line_to_variable: if current_line not in view.lines:
return return
variable = self._line_to_variable[ current_line ] variable = view.lines[ current_line ]
if '_variables' in variable: if '_variables' in variable:
# Collapse # Collapse
del variable[ '_variables' ] del variable[ '_variables' ]
self._DrawScopesAndWatches() view.draw()
return return
# Expand. (only if there is anything to expand) # Expand. (only if there is anything to expand)
@ -162,17 +179,19 @@ class VariablesView( object ):
if variable[ 'variablesReference' ] <= 0: if variable[ 'variablesReference' ] <= 0:
return return
self._connection.DoRequest( partial( self._ConsumeVariables, variable ), { self._connection.DoRequest( partial( self._ConsumeVariables,
view.draw,
variable ), {
'command': 'variables', 'command': 'variables',
'arguments': { 'arguments': {
'variablesReference': variable[ 'variablesReference' ] 'variablesReference': variable[ 'variablesReference' ]
}, },
} ) } )
def _DrawVariables( self, variables, indent ): def _DrawVariables( self, view, variables, indent ):
for variable in variables: for variable in variables:
self._line_to_variable[ len( self._buf ) + 1 ] = variable view.lines[ len( view.win.buffer ) + 1 ] = variable
self._buf.append( view.win.buffer.append(
'{indent}{icon} {name} ({type_}): {value}'.format( '{indent}{icon} {name} ({type_}): {value}'.format(
indent = ' ' * indent, indent = ' ' * indent,
icon = '+' if ( variable[ 'variablesReference' ] > 0 and icon = '+' if ( variable[ 'variablesReference' ] > 0 and
@ -182,41 +201,48 @@ class VariablesView( object ):
value = variable.get( 'value', '<unknown value>' ) ).split( '\n' ) ) value = variable.get( 'value', '<unknown value>' ) ).split( '\n' ) )
if '_variables' in variable: if '_variables' in variable:
self._DrawVariables( variable[ '_variables' ], indent + 2 ) self._DrawVariables( view, variable[ '_variables' ], indent + 2 )
def _DrawScopesAndWatches( self ): def _DrawScopes( self ):
self._line_to_variable = {} self._vars.lines.clear()
with utils.RestoreCursorPosition(): with utils.RestoreCursorPosition():
with utils.ModifiableScratchBuffer( self._buf ): with utils.ModifiableScratchBuffer( self._vars.win.buffer ):
self._buf[:] = None self._vars.win.buffer[:] = None
for scope in self._scopes: for scope in self._scopes:
self._DrawScope( 0, scope ) self._DrawScope( 0, scope )
self._buf.append( 'Watches: ----' ) def _DrawWatches( self ):
self._watch.lines.clear()
with utils.RestoreCursorPosition():
with utils.ModifiableScratchBuffer( self._watch.win.buffer ):
self._watch.win.buffer[:] = None
self._watch.win.buffer.append( 'Watches: ----' )
for watch in self._watches: for watch in self._watches:
self._buf.append( 'Expression: ' + watch[ 'expression' ] ) self._watch.win.buffer.append(
watch[ '_line' ] = len( self._buf ) 'Expression: ' + watch[ 'expression' ] )
watch[ '_line' ] = len( self._watch.win.buffer )
self._DrawWatchResult( 2, watch ) self._DrawWatchResult( 2, watch )
def _DrawScope( self, indent, scope ): def _DrawScope( self, indent, scope ):
icon = '+' if ( scope[ 'variablesReference' ] > 0 and icon = '+' if ( scope[ 'variablesReference' ] > 0 and
'_variables' not in scope ) else '-' '_variables' not in scope ) else '-'
self._line_to_variable[ len( self._buf ) + 1 ] = scope self._vars.lines[ len( self._vars.win.buffer ) + 1 ] = scope
self._buf.append( '{0}{1} Scope: {2}'.format( ' ' * indent, self._vars.win.buffer.append( '{0}{1} Scope: {2}'.format(
icon, ' ' * indent,
scope[ 'name' ] ) ) icon,
scope[ 'name' ] ) )
if '_variables' in scope: if '_variables' in scope:
indent += 2 indent += 2
self._DrawVariables( scope[ '_variables' ], indent ) self._DrawVariables( self._vars, scope[ '_variables' ], indent )
def _DrawWatchResult( self, indent, watch ): def _DrawWatchResult( self, indent, watch ):
if '_result' not in watch: if '_result' not in watch:
return return
result = watch[ '_result' ] result = watch[ '_result' ]
self._line_to_variable[ len( self._buf ) + 1 ] = result self._watch.lines[ len( self._watch.win.buffer ) + 1 ] = result
icon = '+' if ( result[ 'variablesReference' ] > 0 and icon = '+' if ( result[ 'variablesReference' ] > 0 and
'_variables' not in result ) else '-' '_variables' not in result ) else '-'
@ -224,20 +250,20 @@ class VariablesView( object ):
line = '{0}{1} Result: {2} '.format( ' ' * indent, line = '{0}{1} Result: {2} '.format( ' ' * indent,
icon, icon,
result[ 'result' ] ) result[ 'result' ] )
self._buf.append( line.split( '\n' ) ) self._watch.win.buffer.append( line.split( '\n' ) )
if '_variables' in result: if '_variables' in result:
indent = 4 indent = 4
self._DrawVariables( result[ '_variables' ], indent ) self._DrawVariables( self._watch, result[ '_variables' ], indent )
def _ConsumeVariables( self, parent, message ): def _ConsumeVariables( self, draw, parent, message ):
for variable in message[ 'body' ][ 'variables' ]: for variable in message[ 'body' ][ 'variables' ]:
if '_variables' not in parent: if '_variables' not in parent:
parent[ '_variables' ] = [] parent[ '_variables' ] = []
parent[ '_variables' ].append( variable ) parent[ '_variables' ].append( variable )
self._DrawScopesAndWatches() draw()
def ShowBalloon( self, frame, expression ): def ShowBalloon( self, frame, expression ):
if not self._connection: if not self._connection: