From 533b2aa0d5dc82660f83b10d5dbae86da0e64f9f Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Sun, 27 May 2018 16:59:06 +0100 Subject: [PATCH] Very basic support for watch expressions You can currently only add them, not remoove them and you have to pass the expression in the function call, but once added the variable breakdown works nicely. --- autoload/vimspector.vim | 4 +++ python3/vimspector/debug_session.py | 4 +++ python3/vimspector/variables.py | 53 ++++++++++++++++++++++++++--- 3 files changed, 56 insertions(+), 5 deletions(-) diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index 942128a..de0c6c8 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -69,6 +69,10 @@ function! vimspector#GoToFrame() abort py3 _vimspector_session.GoToFrame() endfunction +function! vimspector#AddWatch( expr ) abort + py3 _vimspector_session.AddWatch( vim.eval( 'a:expr' ) ) +endfunction + " Boilerplate {{{ let &cpo=s:save_cpo unlet s:save_cpo diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 9a9f1e2..9674412 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -180,6 +180,9 @@ class DebugSession( object ): def ExpandVariable( self ): self._variablesView.ExpandVariable() + def AddWatch( self, expression ): + self._variablesView.AddWatch( self._currentFrame, expression ) + def GoToFrame( self ): self._stackTraceView.GoToFrame() @@ -221,6 +224,7 @@ class DebugSession( object ): self._currentFrame = frame self._codeView.SetCurrentFrame( frame ) self._variablesView.LoadScopes( frame ) + self._variablesView.EvaluateWatches() def _StartDebugAdapter( self ): self._logger.info( 'Starting debug adapter with: {0}'.format( json.dumps( diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 831e0ab..daa0163 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -33,6 +33,9 @@ class VariablesView( object ): # children. Otherwise, we haven't or shouldn't. self._scopes = [] + # This is basically the same as scopes, but the top level is an "expression" + self._watches = [] + vim.current.buffer = buf vim.command( 'nnoremap :call vimspector#ExpandVariable()' ) @@ -55,7 +58,7 @@ class VariablesView( object ): }, } ) - self._DrawScopes() + self._DrawScopesAndWatches() self._connection.DoRequest( scopes_consumer, { 'command': 'scopes', @@ -64,6 +67,36 @@ class VariablesView( object ): }, } ) + def AddWatch( self, frame, expression ): + watch = { + 'expression': expression, + 'frameId': frame[ 'id' ], + 'context': 'watch', + } + self._watches.append( watch ) + self.EvaluateWatches() + + def EvaluateWatches( self ): + for watch in self._watches: + self._connection.DoRequest( partial( self._UpdateWatchExpression, + watch ), { + 'command': 'evaluate', + 'arguments': watch, + } ) + + def _UpdateWatchExpression( self, watch, message ): + watch[ 'result' ] = message[ 'body' ] + if 'variablesReference' in watch[ 'result' ]: + self._connection.DoRequest( + partial( self._ConsumeVariables, watch[ 'result' ] ), { + 'command': 'variables', + 'arguments': { + 'variablesReference': watch[ 'result' ][ 'variablesReference' ] + }, + } ) + + self._DrawScopesAndWatches() + def ExpandVariable( self ): if vim.current.window.buffer != self._buf: return @@ -75,7 +108,7 @@ class VariablesView( object ): variable = self._line_to_variable[ current_line ] if '_variables' in variable: del variable[ '_variables' ] - self._DrawScopes() + self._DrawScopesAndWatches() else: self._connection.DoRequest( partial( self._ConsumeVariables, variable ), { 'command': 'variables', @@ -93,13 +126,13 @@ class VariablesView( object ): '_variables' not in variable ) else '-', name = variable[ 'name' ], type_ = variable.get( 'type', '' ), - value = variable[ 'value' ] ) ) + value = variable[ 'value' ] ).split( '\n' ) ) self._line_to_variable[ len( self._buf ) ] = variable if '_variables' in variable: self._DrawVariables( variable[ '_variables' ], indent + 2 ) - def _DrawScopes( self ): + def _DrawScopesAndWatches( self ): self._line_to_variable = {} with utils.RestoreCursorPosition(): with utils.ModifiableScratchBuffer( self._buf ): @@ -110,6 +143,16 @@ class VariablesView( object ): indent = 2 self._DrawVariables( scope[ '_variables' ], indent ) + self._buf.append( 'Watches: ----' ) + for watch in self._watches: + self._buf.append( 'Expression: ' + watch[ 'expression' ] ) + if 'result' in watch: + result = watch[ 'result' ] + line = ' Result: ' + result[ 'result' ] + self._buf.append( line.split( '\n' ) ) + if '_variables' in result: + indent = 4 + self._DrawVariables( result[ '_variables' ], indent ) def _ConsumeVariables( self, parent, message ): for variable in message[ 'body' ][ 'variables' ]: @@ -118,4 +161,4 @@ class VariablesView( object ): parent[ '_variables' ].append( variable ) - self._DrawScopes() + self._DrawScopesAndWatches()