WIP: First sort-of untested attempt at readmemory request

This commit is contained in:
Ben Jackson 2021-06-04 19:40:00 +01:00
commit ba9cb2f6d3
5 changed files with 88 additions and 4 deletions

View file

@ -223,6 +223,13 @@ function! vimspector#SetVariableValue( ... ) abort
endif endif
endfunction endfunction
function! vimspector#ReadMemory() abort
if !s:Enabled()
return
endif
py3 _vimspector_session.ReadMemory()
endfunction
function! vimspector#DeleteWatch() abort function! vimspector#DeleteWatch() abort
if !s:Enabled() if !s:Enabled()
return return

View file

@ -16,6 +16,7 @@
import vim import vim
import logging import logging
import json import json
import os
from collections import defaultdict from collections import defaultdict
from vimspector import utils, terminal, signs from vimspector import utils, terminal, signs
@ -40,6 +41,7 @@ class CodeView( object ):
'breakpoints': [] 'breakpoints': []
} }
self._current_frame = None self._current_frame = None
self._scratch_buffers = []
with utils.LetCurrentWindow( self._window ): with utils.LetCurrentWindow( self._window ):
if utils.UseWinBar(): if utils.UseWinBar():
@ -173,6 +175,10 @@ class CodeView( object ):
self.ClearBreakpoints() self.ClearBreakpoints()
self.Clear() self.Clear()
for b in self._scratch_buffers:
utils.CleanUpHiddenBuffer( b )
self._scratch_buffers = []
def AddBreakpoints( self, source, breakpoints ): def AddBreakpoints( self, source, breakpoints ):
for breakpoint in breakpoints: for breakpoint in breakpoints:
source = breakpoint.get( 'source' ) or source 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 # FIXME: Change this tor return the PID rather than having debug_session
# work that out # work that out
return self._terminal.buffer_number 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' ) )

View file

@ -535,6 +535,32 @@ class DebugSession( object ):
def SetVariableValue( self, new_value = None, buf = None, line_num = None ): def SetVariableValue( self, new_value = None, buf = None, line_num = None ):
self._variablesView.SetVariableValue( new_value, buf, line_num ) 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() @IfConnected()
def AddWatch( self, expression ): def AddWatch( self, expression ):
self._variablesView.AddWatch( self._stackTraceView.GetCurrentFrame(), self._variablesView.AddWatch( self._stackTraceView.GetCurrentFrame(),
@ -1163,7 +1189,8 @@ class DebugSession( object ):
'pathFormat': 'path', 'pathFormat': 'path',
'supportsVariableType': True, 'supportsVariableType': True,
'supportsVariablePaging': False, 'supportsVariablePaging': False,
'supportsRunInTerminalRequest': True 'supportsRunInTerminalRequest': True,
'supportsMemoryReferences': True
}, },
} ) } )

View file

@ -54,7 +54,8 @@ DEFAULTS = {
'variables': { 'variables': {
'expand_collapse': [ '<CR>', '<2-LeftMouse>' ], 'expand_collapse': [ '<CR>', '<2-LeftMouse>' ],
'delete': [ '<Del>' ], 'delete': [ '<Del>' ],
'set_value': [ '<C-CR>', '<leader><CR>' ] 'set_value': [ '<C-CR>', '<leader><CR>' ],
'read_memory': [ '<leader>m' ],
}, },
'stack_trace': { 'stack_trace': {
'expand_or_jump': [ '<CR>', '<2-LeftMouse>' ], 'expand_or_jump': [ '<CR>', '<2-LeftMouse>' ],

View file

@ -56,6 +56,11 @@ class Expandable:
def VariablesReference( self ): def VariablesReference( self ):
assert False assert False
@abc.abstractmethod
def MemoryReference( self ):
assert None
class Scope( Expandable ): class Scope( Expandable ):
"""Holds an expandable scope (a DAP scope dict), with expand/collapse state""" """Holds an expandable scope (a DAP scope dict), with expand/collapse state"""
@ -66,6 +71,9 @@ class Scope( Expandable ):
def VariablesReference( self ): def VariablesReference( self ):
return self.scope.get( 'variablesReference', 0 ) return self.scope.get( 'variablesReference', 0 )
def MemoryReference( self ):
return None
def Update( self, scope ): def Update( self, scope ):
self.scope = scope self.scope = scope
@ -81,6 +89,9 @@ class WatchResult( Expandable ):
def VariablesReference( self ): def VariablesReference( self ):
return self.result.get( 'variablesReference', 0 ) return self.result.get( 'variablesReference', 0 )
def MemoryReference( self ):
return self.result.get( 'memoryReference' )
def Update( self, result ): def Update( self, result ):
self.changed = False self.changed = False
if self.result[ 'result' ] != result[ 'result' ]: if self.result[ 'result' ] != result[ 'result' ]:
@ -105,6 +116,9 @@ class Variable( Expandable ):
def VariablesReference( self ): def VariablesReference( self ):
return self.variable.get( 'variablesReference', 0 ) return self.variable.get( 'variablesReference', 0 )
def MemoryReference( self ):
return self.variable.get( 'memoryReference' )
def Update( self, variable ): def Update( self, variable ):
self.changed = False self.changed = False
if self.variable[ 'value' ] != variable[ 'value' ]: if self.variable[ 'value' ] != variable[ 'value' ]:
@ -163,6 +177,10 @@ def AddExpandMappings( mappings = None ):
for mapping in utils.GetVimList( mappings, 'set_value' ): for mapping in utils.GetVimList( mappings, 'set_value' ):
vim.command( f'nnoremap <silent> <buffer> { mapping } ' vim.command( f'nnoremap <silent> <buffer> { mapping } '
':<C-u>call vimspector#SetVariableValue()<CR>' ) ':<C-u>call vimspector#SetVariableValue()<CR>' )
for mapping in utils.GetVimList( mappings, 'read_memory' ):
vim.command( f'nnoremap <silent> <buffer> { mapping } '
':<C-u>call vimspector#ReadMemory()<CR>' )
class VariablesView( object ): class VariablesView( object ):
@ -187,6 +205,8 @@ class VariablesView( object ):
if utils.UseWinBar(): if utils.UseWinBar():
vim.command( 'nnoremenu <silent> 1.1 WinBar.Set ' vim.command( 'nnoremenu <silent> 1.1 WinBar.Set '
':call vimspector#SetVariableValue()<CR>' ) ':call vimspector#SetVariableValue()<CR>' )
vim.command( 'nnoremenu <silent> 1.2 WinBar.Memory '
':call vimspector#ReadMemory()<CR>' )
AddExpandMappings( mappings ) AddExpandMappings( mappings )
# Set up the "Watches" buffer in the watches_win (and create a WinBar in # Set up the "Watches" buffer in the watches_win (and create a WinBar in
@ -211,8 +231,10 @@ class VariablesView( object ):
':call vimspector#ExpandVariable()<CR>' ) ':call vimspector#ExpandVariable()<CR>' )
vim.command( 'nnoremenu <silent> 1.3 WinBar.Delete ' vim.command( 'nnoremenu <silent> 1.3 WinBar.Delete '
':call vimspector#DeleteWatch()<CR>' ) ':call vimspector#DeleteWatch()<CR>' )
vim.command( 'nnoremenu <silent> 1.1 WinBar.Set ' vim.command( 'nnoremenu <silent> 1.4 WinBar.Set '
':call vimspector#SetVariableValue()<CR>' ) ':call vimspector#SetVariableValue()<CR>' )
vim.command( 'nnoremenu <silent> 1.5 WinBar.Memory '
':call vimspector#ReadMemory()<CR>' )
# Set the (global!) balloon expr if supported # Set the (global!) balloon expr if supported
has_balloon = int( vim.eval( "has( 'balloon_eval' )" ) ) has_balloon = int( vim.eval( "has( 'balloon_eval' )" ) )
@ -580,6 +602,14 @@ class VariablesView( object ):
}, failure_handler = failure_handler ) }, 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 ): def _DrawVariables( self, view, variables, indent, is_short = False ):
assert indent > 0 assert indent > 0
@ -595,10 +625,12 @@ class VariablesView( object ):
value = variable.variable.get( 'value', '<unknown>' ) value = variable.variable.get( 'value', '<unknown>' )
) )
else: else:
marker = 'm' if variable.MemoryReference() is not None else ' '
marker += '*' if variable.changed else ' '
text = '{indent}{marker}{icon} {name} ({type_}): {value}'.format( text = '{indent}{marker}{icon} {name} ({type_}): {value}'.format(
# We borrow 1 space of indent to draw the change marker # We borrow 1 space of indent to draw the change marker
indent = ' ' * ( indent - 1 ), indent = ' ' * ( indent - 1 ),
marker = '*' if variable.changed else ' ', marker = marker,
icon = '+' if ( variable.IsExpandable() icon = '+' if ( variable.IsExpandable()
and not variable.IsExpanded() ) else '-', and not variable.IsExpanded() ) else '-',
name = variable.variable.get( 'name', '' ), name = variable.variable.get( 'name', '' ),