diff --git a/autoload/vimspector.vim b/autoload/vimspector.vim index 78c7c1b..0eb394f 100644 --- a/autoload/vimspector.vim +++ b/autoload/vimspector.vim @@ -223,6 +223,13 @@ function! vimspector#SetVariableValue( ... ) abort endif endfunction +function! vimspector#ReadMemory() abort + if !s:Enabled() + return + endif + py3 _vimspector_session.ReadMemory() +endfunction + function! vimspector#DeleteWatch() abort if !s:Enabled() return diff --git a/python3/vimspector/code.py b/python3/vimspector/code.py index 98aeca5..37fe23b 100644 --- a/python3/vimspector/code.py +++ b/python3/vimspector/code.py @@ -16,6 +16,7 @@ import vim import logging import json +import os from collections import defaultdict from vimspector import utils, terminal, signs @@ -40,6 +41,7 @@ class CodeView( object ): 'breakpoints': [] } self._current_frame = None + self._scratch_buffers = [] with utils.LetCurrentWindow( self._window ): if utils.UseWinBar(): @@ -173,6 +175,10 @@ class CodeView( object ): self.ClearBreakpoints() self.Clear() + for b in self._scratch_buffers: + utils.CleanUpHiddenBuffer( b ) + self._scratch_buffers = [] + def AddBreakpoints( self, source, breakpoints ): for breakpoint in breakpoints: source = breakpoint.get( 'source' ) or source @@ -287,3 +293,14 @@ class CodeView( object ): # FIXME: Change this tor return the PID rather than having debug_session # work that out return self._terminal.buffer_number + + + def ShowMemory( self, memoryReference, msg ): + buf_name = os.path.join( '_vimspector_mem', memoryReference ) + buf = utils.BufferForFile( buf_name ) + self._scratch_buffers.append( buf ) + utils.SetUpHiddenBuffer( buf, buf_name ) + with utils.ModifiableScratchBuffer( buf ): + utils.SetBufferContents( + buf, + msg.get( 'body', {} ).get( 'data', 'Cannot read memory' ) ) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 36ad62b..3fc983a 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -535,6 +535,32 @@ class DebugSession( object ): def SetVariableValue( self, new_value = None, buf = None, line_num = None ): self._variablesView.SetVariableValue( new_value, buf, line_num ) + @IfConnected() + def ReadMemory( self, buf = None, line_num = None ): + if not self._server_capabilities.get( 'supportsReadMemoryRequest' ): + utils.UserMessage( "Server does not support memory request", + error = True ) + return + + memoryReference = self._variablesView.GetMemoryReference( buf, line_num ) + + if memoryReference is None: + utils.UserMessage( "Cannot find memory reference for that", + error = True ) + return + + def handler( msg ): + self._codeView.ShowMemory( msg ) + + self._connection.DoRequest( handler, { + 'command': 'readMemory', + 'arguments': { + 'memoryReference': memoryReference, + 'count': 10 # TODO: What + } + } ) + + @IfConnected() def AddWatch( self, expression ): self._variablesView.AddWatch( self._stackTraceView.GetCurrentFrame(), @@ -1163,7 +1189,8 @@ class DebugSession( object ): 'pathFormat': 'path', 'supportsVariableType': True, 'supportsVariablePaging': False, - 'supportsRunInTerminalRequest': True + 'supportsRunInTerminalRequest': True, + 'supportsMemoryReferences': True }, } ) diff --git a/python3/vimspector/settings.py b/python3/vimspector/settings.py index 89378af..60e8341 100644 --- a/python3/vimspector/settings.py +++ b/python3/vimspector/settings.py @@ -54,7 +54,8 @@ DEFAULTS = { 'variables': { 'expand_collapse': [ '', '<2-LeftMouse>' ], 'delete': [ '' ], - 'set_value': [ '', '' ] + 'set_value': [ '', '' ], + 'read_memory': [ 'm' ], }, 'stack_trace': { 'expand_or_jump': [ '', '<2-LeftMouse>' ], diff --git a/python3/vimspector/variables.py b/python3/vimspector/variables.py index 8dcb493..8524d67 100644 --- a/python3/vimspector/variables.py +++ b/python3/vimspector/variables.py @@ -56,6 +56,11 @@ class Expandable: def VariablesReference( self ): assert False + @abc.abstractmethod + def MemoryReference( self ): + assert None + + class Scope( Expandable ): """Holds an expandable scope (a DAP scope dict), with expand/collapse state""" @@ -66,6 +71,9 @@ class Scope( Expandable ): def VariablesReference( self ): return self.scope.get( 'variablesReference', 0 ) + def MemoryReference( self ): + return None + def Update( self, scope ): self.scope = scope @@ -81,6 +89,9 @@ class WatchResult( Expandable ): def VariablesReference( self ): return self.result.get( 'variablesReference', 0 ) + def MemoryReference( self ): + return self.result.get( 'memoryReference' ) + def Update( self, result ): self.changed = False if self.result[ 'result' ] != result[ 'result' ]: @@ -105,6 +116,9 @@ class Variable( Expandable ): def VariablesReference( self ): return self.variable.get( 'variablesReference', 0 ) + def MemoryReference( self ): + return self.variable.get( 'memoryReference' ) + def Update( self, variable ): self.changed = False if self.variable[ 'value' ] != variable[ 'value' ]: @@ -163,6 +177,10 @@ def AddExpandMappings( mappings = None ): for mapping in utils.GetVimList( mappings, 'set_value' ): vim.command( f'nnoremap { mapping } ' ':call vimspector#SetVariableValue()' ) + for mapping in utils.GetVimList( mappings, 'read_memory' ): + vim.command( f'nnoremap { mapping } ' + ':call vimspector#ReadMemory()' ) + class VariablesView( object ): @@ -187,6 +205,8 @@ class VariablesView( object ): if utils.UseWinBar(): vim.command( 'nnoremenu 1.1 WinBar.Set ' ':call vimspector#SetVariableValue()' ) + vim.command( 'nnoremenu 1.2 WinBar.Memory ' + ':call vimspector#ReadMemory()' ) AddExpandMappings( mappings ) # Set up the "Watches" buffer in the watches_win (and create a WinBar in @@ -211,8 +231,10 @@ class VariablesView( object ): ':call vimspector#ExpandVariable()' ) vim.command( 'nnoremenu 1.3 WinBar.Delete ' ':call vimspector#DeleteWatch()' ) - vim.command( 'nnoremenu 1.1 WinBar.Set ' + vim.command( 'nnoremenu 1.4 WinBar.Set ' ':call vimspector#SetVariableValue()' ) + vim.command( 'nnoremenu 1.5 WinBar.Memory ' + ':call vimspector#ReadMemory()' ) # Set the (global!) balloon expr if supported has_balloon = int( vim.eval( "has( 'balloon_eval' )" ) ) @@ -580,6 +602,14 @@ class VariablesView( object ): }, failure_handler = failure_handler ) + def GetMemoryReference( self, buf = None, line_num = None ): + # Get a memoryReference for use in a ReadMemory request + variable, _ = self._GetVariable( buf, line_num ) + if variable is None: + return None + + return variable.MemoryReference() + def _DrawVariables( self, view, variables, indent, is_short = False ): assert indent > 0 @@ -595,10 +625,12 @@ class VariablesView( object ): value = variable.variable.get( 'value', '' ) ) else: + marker = 'm' if variable.MemoryReference() is not None else ' ' + marker += '*' if variable.changed else ' ' text = '{indent}{marker}{icon} {name} ({type_}): {value}'.format( # We borrow 1 space of indent to draw the change marker indent = ' ' * ( indent - 1 ), - marker = '*' if variable.changed else ' ', + marker = marker, icon = '+' if ( variable.IsExpandable() and not variable.IsExpanded() ) else '-', name = variable.variable.get( 'name', '' ),