diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index eb12e9a..2632948 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -277,8 +277,16 @@ class DebugSession( object ): # Variables vim.command( 'spl' ) 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, - vim.current.buffer ) + vars_win, + watch_win ) with utils.TemporaryVimOption( 'splitbelow', True ): @@ -520,7 +528,8 @@ class DebugSession( object ): file_name ) ) def OnEvent_output( self, message ): - self._outputView.OnOutput( message[ 'body' ] ) + if self._outputView: + self._outputView.OnOutput( message[ 'body' ] ) def OnEvent_stopped( self, message ): event = message[ 'body' ] diff --git a/python3/vimspector/stack_trace.py b/python3/vimspector/stack_trace.py index 4358e94..ae65db4 100644 --- a/python3/vimspector/stack_trace.py +++ b/python3/vimspector/stack_trace.py @@ -64,7 +64,9 @@ class StackTraceView( object ): if not message[ 'body' ][ 'threads' ]: # 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' ]: self._threads.append( thread ) diff --git a/python3/vimspector/utils.py b/python3/vimspector/utils.py index e1ba9af..c627875 100644 --- a/python3/vimspector/utils.py +++ b/python3/vimspector/utils.py @@ -76,6 +76,7 @@ def RestoreCursorPosition(): @contextlib.contextmanager def RestorCurrentWindow(): + # TODO: Don't trigger autoccommands when shifting windows old_window = vim.current.window try: yield @@ -85,6 +86,7 @@ def RestorCurrentWindow(): @contextlib.contextmanager def RestoreCurrentBuffer( window ): + # TODO: Don't trigger autoccommands when shifting buffers old_buffer = window.buffer try: yield diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index c83bf44..cb95d67 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -14,20 +14,21 @@ # limitations under the License. import vim +from collections import namedtuple from functools import partial from vimspector import utils +View = namedtuple( 'View', [ 'win', 'lines', 'draw' ] ) class VariablesView( object ): - def __init__( self, connection, buf ): - vim.current.buffer = buf - - self._buf = buf + def __init__( self, connection, variables_win, watches_win ): + self._vars = View( variables_win, {}, self._DrawScopes ) + self._watch = View( watches_win, {}, self._DrawWatches ) self._connection = connection # Allows us to hit to expand/collapse variables - self._line_to_variable = {} + vim.current.window = self._vars.win vim.command( 'nnoremap :call vimspector#ExpandVariable()' ) @@ -45,11 +46,15 @@ class VariablesView( object ): # above. It also has a special _line key which is where we printed it (last) self._watches = [] - # Allows us to delete manual watches + # Allows us to hit to expand/collapse variables + vim.current.window = self._watch.win + vim.command( + 'nnoremap :call vimspector#ExpandVariable()' ) vim.command( 'nnoremap :call vimspector#DeleteWatch()' ) - 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_term = int( vim.eval( "has( 'balloon_eval_term' )" ) ) @@ -73,8 +78,10 @@ class VariablesView( object ): def Clear( self ): - with utils.ModifiableScratchBuffer( self._buf ): - self._buf[:] = None + with utils.ModifiableScratchBuffer( self._vars.win.buffer ): + self._vars.win.buffer[:] = None + with utils.ModifiableScratchBuffer( self._watch.win.buffer ): + self._watch.win.buffer[:] = None def ConnectionClosed( self ): self.Clear() @@ -91,14 +98,16 @@ class VariablesView( object ): self._scopes = [] for scope in message[ 'body' ][ 'scopes' ]: self._scopes.append( scope ) - self._connection.DoRequest( partial( self._ConsumeVariables, scope ), { + self._connection.DoRequest( partial( self._ConsumeVariables, + self._DrawScopes, + scope ), { 'command': 'variables', 'arguments': { 'variablesReference': scope[ 'variablesReference' ] }, } ) - self._DrawScopesAndWatches() + self._DrawScopes() self._connection.DoRequest( scopes_consumer, { 'command': 'scopes', @@ -117,7 +126,8 @@ class VariablesView( object ): self.EvaluateWatches() def DeleteWatch( self ): - if vim.current.window.buffer != self._buf: + if vim.current.window != self._watch.win: + utils.UserMessage( 'Not a watch window' ) return current_line = vim.current.window.cursor[ 0 ] @@ -125,9 +135,12 @@ class VariablesView( object ): for index, watch in enumerate( self._watches ): if '_line' in watch and watch[ '_line' ] == current_line: del self._watches[ index ] - self._DrawScopesAndWatches() + utils.UserMessage( 'Deleted' ) + self._DrawWatches() return + utils.UserMessage( 'No watch found' ) + def EvaluateWatches( self ): for watch in self._watches: self._connection.DoRequest( partial( self._UpdateWatchExpression, @@ -138,22 +151,26 @@ class VariablesView( object ): def _UpdateWatchExpression( self, watch, message ): watch[ '_result' ] = message[ 'body' ] - self._DrawScopesAndWatches() + self._DrawWatches() 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 current_line = vim.current.window.cursor[ 0 ] - if current_line not in self._line_to_variable: + if current_line not in view.lines: return - variable = self._line_to_variable[ current_line ] + variable = view.lines[ current_line ] if '_variables' in variable: # Collapse del variable[ '_variables' ] - self._DrawScopesAndWatches() + view.draw() return # Expand. (only if there is anything to expand) @@ -162,17 +179,19 @@ class VariablesView( object ): if variable[ 'variablesReference' ] <= 0: return - self._connection.DoRequest( partial( self._ConsumeVariables, variable ), { + self._connection.DoRequest( partial( self._ConsumeVariables, + view.draw, + variable ), { 'command': 'variables', 'arguments': { 'variablesReference': variable[ 'variablesReference' ] }, } ) - def _DrawVariables( self, variables, indent ): + def _DrawVariables( self, view, variables, indent ): for variable in variables: - self._line_to_variable[ len( self._buf ) + 1 ] = variable - self._buf.append( + view.lines[ len( view.win.buffer ) + 1 ] = variable + view.win.buffer.append( '{indent}{icon} {name} ({type_}): {value}'.format( indent = ' ' * indent, icon = '+' if ( variable[ 'variablesReference' ] > 0 and @@ -182,41 +201,48 @@ class VariablesView( object ): value = variable.get( 'value', '' ) ).split( '\n' ) ) if '_variables' in variable: - self._DrawVariables( variable[ '_variables' ], indent + 2 ) + self._DrawVariables( view, variable[ '_variables' ], indent + 2 ) - def _DrawScopesAndWatches( self ): - self._line_to_variable = {} + def _DrawScopes( self ): + self._vars.lines.clear() with utils.RestoreCursorPosition(): - with utils.ModifiableScratchBuffer( self._buf ): - self._buf[:] = None + with utils.ModifiableScratchBuffer( self._vars.win.buffer ): + self._vars.win.buffer[:] = None for scope in self._scopes: 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: - self._buf.append( 'Expression: ' + watch[ 'expression' ] ) - watch[ '_line' ] = len( self._buf ) + self._watch.win.buffer.append( + 'Expression: ' + watch[ 'expression' ] ) + watch[ '_line' ] = len( self._watch.win.buffer ) self._DrawWatchResult( 2, watch ) def _DrawScope( self, indent, scope ): icon = '+' if ( scope[ 'variablesReference' ] > 0 and '_variables' not in scope ) else '-' - self._line_to_variable[ len( self._buf ) + 1 ] = scope - self._buf.append( '{0}{1} Scope: {2}'.format( ' ' * indent, - icon, - scope[ 'name' ] ) ) + self._vars.lines[ len( self._vars.win.buffer ) + 1 ] = scope + self._vars.win.buffer.append( '{0}{1} Scope: {2}'.format( + ' ' * indent, + icon, + scope[ 'name' ] ) ) if '_variables' in scope: indent += 2 - self._DrawVariables( scope[ '_variables' ], indent ) + self._DrawVariables( self._vars, scope[ '_variables' ], indent ) def _DrawWatchResult( self, indent, watch ): if '_result' not in watch: return 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 '_variables' not in result ) else '-' @@ -224,20 +250,20 @@ class VariablesView( object ): line = '{0}{1} Result: {2} '.format( ' ' * indent, icon, result[ 'result' ] ) - self._buf.append( line.split( '\n' ) ) + self._watch.win.buffer.append( line.split( '\n' ) ) if '_variables' in result: 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' ]: if '_variables' not in parent: parent[ '_variables' ] = [] parent[ '_variables' ].append( variable ) - self._DrawScopesAndWatches() + draw() def ShowBalloon( self, frame, expression ): if not self._connection: