Very basic support for launch configuration
This change refactors the way we launch the job and puts it all in an internal namespace. Having done that, we are able to launch the job from the python side. This allows us to neatly load a json file, simlar in format to .vscode's launch.json, but sufficiently different that users won't just expect the launch.json to work. This change allows selecting between 2 different adapters to debug the same c program.
This commit is contained in:
parent
66dd8ceb84
commit
269d09b73e
8 changed files with 302 additions and 189 deletions
|
|
@ -13,11 +13,8 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import logging
|
||||
import vim
|
||||
|
||||
_logger = logging.getLogger( __name__ )
|
||||
|
||||
SIGN_ID_OFFSET = 10000000
|
||||
|
||||
|
||||
|
|
@ -38,6 +35,7 @@ class CodeView( object ):
|
|||
vim.command( 'nnoremenu WinBar.Step :call vimspector#StepInto()<CR>' )
|
||||
vim.command( 'nnoremenu WinBar.Finish :call vimspector#StepOut()<CR>' )
|
||||
vim.command( 'nnoremenu WinBar.Pause :call vimspector#Pause()<CR>' )
|
||||
vim.command( 'nnoremenu WinBar.Stop :call vimspector#Stop()<CR>' )
|
||||
|
||||
vim.command( 'sign define vimspectorPC text=>> texthl=Search' )
|
||||
|
||||
|
|
|
|||
|
|
@ -15,13 +15,15 @@
|
|||
|
||||
import logging
|
||||
import json
|
||||
import vim
|
||||
|
||||
_logger = logging.getLogger( __name__ )
|
||||
from vimspector import utils
|
||||
|
||||
|
||||
class DebugAdapterConnection( object ):
|
||||
def __init__( self, handler, send_func ):
|
||||
self._logger = logging.getLogger( __name__ )
|
||||
utils.SetUpLogging( self._logger )
|
||||
|
||||
self._Write = send_func
|
||||
self._SetState( 'READ_HEADER' )
|
||||
self._buffer = bytes()
|
||||
|
|
@ -41,7 +43,7 @@ class DebugAdapterConnection( object ):
|
|||
|
||||
def OnData( self, data ):
|
||||
data = bytes( data, 'utf-8' )
|
||||
_logger.debug( 'Received ({0}/{1}): {2},'.format( type( data ),
|
||||
self._logger.debug( 'Received ({0}/{1}): {2},'.format( type( data ),
|
||||
len( data ),
|
||||
data ) )
|
||||
|
||||
|
|
@ -69,7 +71,7 @@ class DebugAdapterConnection( object ):
|
|||
msg = json.dumps( msg )
|
||||
data = 'Content-Length: {0}\r\n\r\n{1}'.format( len( msg ), msg )
|
||||
|
||||
_logger.debug( 'Sending: {0}'.format( data ) )
|
||||
self._logger.debug( 'Sending: {0}'.format( data ) )
|
||||
self._Write( data )
|
||||
|
||||
def _ReadHeaders( self ):
|
||||
|
|
@ -102,7 +104,7 @@ class DebugAdapterConnection( object ):
|
|||
|
||||
message = json.loads( payload )
|
||||
|
||||
_logger.debug( 'Message received: {0}'.format( message ) )
|
||||
self._logger.debug( 'Message received: {0}'.format( message ) )
|
||||
|
||||
self._OnMessageReceived( message )
|
||||
|
||||
|
|
@ -116,8 +118,9 @@ class DebugAdapterConnection( object ):
|
|||
if handler:
|
||||
handler( message )
|
||||
else:
|
||||
_logger.error( 'Request failed: {0}'.format( message[ 'message' ] ) )
|
||||
vim.command( "echom 'Request failed: {0}'".format(
|
||||
self._logger.error(
|
||||
'Request failed: {0}'.format( message[ 'message' ] ) )
|
||||
utils.UserMessage( 'Request failed: {0}'.format(
|
||||
message[ 'message' ] ) )
|
||||
elif message[ 'type' ] == 'event':
|
||||
method = 'OnEvent_' + message[ 'event' ]
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
import logging
|
||||
import vim
|
||||
import json
|
||||
|
||||
from vimspector import ( code,
|
||||
debug_adapter_connection,
|
||||
|
|
@ -22,16 +23,13 @@ from vimspector import ( code,
|
|||
utils,
|
||||
variables )
|
||||
|
||||
_logger = logging.getLogger( __name__ )
|
||||
|
||||
|
||||
class DebugSession( object ):
|
||||
def __init__( self, channel_send_func ):
|
||||
utils.SetUpLogging()
|
||||
def __init__( self ):
|
||||
self._logger = logging.getLogger( __name__ )
|
||||
utils.SetUpLogging( self._logger )
|
||||
|
||||
self._connection = debug_adapter_connection.DebugAdapterConnection(
|
||||
self,
|
||||
channel_send_func )
|
||||
self._connection = None
|
||||
|
||||
self._uiTab = None
|
||||
self._threadsBuffer = None
|
||||
|
|
@ -40,59 +38,38 @@ class DebugSession( object ):
|
|||
self._currentThread = None
|
||||
self._currentFrame = None
|
||||
|
||||
def Start( self, configuration = None ):
|
||||
launch_config_file = utils.PathToConfigFile( '.vimspector.json' )
|
||||
|
||||
if not launch_config_file:
|
||||
utils.UserMessage( 'Unable to find .vimspector.json. You need to tell '
|
||||
'vimspector how to launch your application' )
|
||||
return
|
||||
|
||||
with open( launch_config_file, 'r' ) as f:
|
||||
launch_config = json.load( f )
|
||||
|
||||
if not configuration:
|
||||
configuration = utils.SelectFromList( 'Which launch configuration?',
|
||||
list( launch_config.keys() ) )
|
||||
|
||||
if not configuration:
|
||||
return
|
||||
|
||||
configuration = launch_config[ configuration ]
|
||||
|
||||
self._StartDebugAdapter( configuration[ 'adapter' ] )
|
||||
self._Initialise( configuration[ 'adapter' ],
|
||||
configuration[ 'configuration' ] )
|
||||
self._SetUpUI()
|
||||
|
||||
def _SetUpUI( self ):
|
||||
vim.command( 'tabnew' )
|
||||
self._uiTab = vim.current.tabpage
|
||||
|
||||
# Code window
|
||||
self._codeView = code.CodeView( vim.current.window )
|
||||
|
||||
# Threads
|
||||
vim.command( '50vspl' )
|
||||
vim.command( 'enew' )
|
||||
self._threadsBuffer = vim.current.buffer
|
||||
utils.SetUpScratchBuffer( self._threadsBuffer )
|
||||
|
||||
with utils.TemporaryVimOption( 'eadirection', 'ver' ):
|
||||
with utils.TemporaryVimOption( 'equalalways', 1 ):
|
||||
# Call stack
|
||||
vim.command( 'spl' )
|
||||
vim.command( 'enew' )
|
||||
self._stackTraceView = stack_trace.StackTraceView( self,
|
||||
self._connection,
|
||||
vim.current.buffer )
|
||||
|
||||
# Output/logging
|
||||
vim.command( 'spl' )
|
||||
vim.command( 'enew' )
|
||||
self._outputBuffer = vim.current.buffer
|
||||
utils.SetUpScratchBuffer( self._outputBuffer )
|
||||
|
||||
# Variables
|
||||
vim.command( 'spl' )
|
||||
vim.command( 'enew' )
|
||||
self._variablesView = variables.VariablesView( self._connection,
|
||||
vim.current.buffer )
|
||||
|
||||
|
||||
def SetCurrentFrame( self, frame ):
|
||||
self._currentFrame = frame
|
||||
self._codeView.SetCurrentFrame( frame )
|
||||
self._variablesView.LoadScopes( frame )
|
||||
|
||||
|
||||
def OnChannelData( self, data ):
|
||||
self._connection.OnData( data )
|
||||
|
||||
def Start( self ):
|
||||
self._Initialise()
|
||||
|
||||
def Stop( self ):
|
||||
self._codeView.Clear()
|
||||
|
||||
self._connection.DoRequest( None, {
|
||||
self._connection.DoRequest( lambda msg: self._CleanUpUi(), {
|
||||
'command': 'disconnect',
|
||||
'arguments': {
|
||||
'terminateDebugee': True
|
||||
|
|
@ -145,34 +122,81 @@ class DebugSession( object ):
|
|||
def GoToFrame( self ):
|
||||
self._stackTraceView.GoToFrame()
|
||||
|
||||
def _Initialise( self ):
|
||||
def handler( message ) :
|
||||
self._connection.DoRequest( None, {
|
||||
'command': 'launch',
|
||||
def _SetUpUI( self ):
|
||||
vim.command( 'tabnew' )
|
||||
self._uiTab = vim.current.tabpage
|
||||
|
||||
'arguments': {
|
||||
"target": "/Users/Ben/.vim/bundle/vimspector/support/test/cpp/"
|
||||
"simple_c_program/test",
|
||||
"args": [],
|
||||
"cwd": "/Users/ben",
|
||||
"stopOnEntry": True,
|
||||
'lldbmipath':
|
||||
'/Users/ben/.vscode/extensions/ms-vscode.cpptools-0.17.1/'
|
||||
'debugAdapters/lldb/bin/lldb-mi',
|
||||
}
|
||||
} )
|
||||
# Code window
|
||||
self._codeView = code.CodeView( vim.current.window )
|
||||
|
||||
# Threads
|
||||
vim.command( '50vspl' )
|
||||
vim.command( 'enew' )
|
||||
self._threadsBuffer = vim.current.buffer
|
||||
utils.SetUpScratchBuffer( self._threadsBuffer )
|
||||
|
||||
with utils.TemporaryVimOption( 'eadirection', 'ver' ):
|
||||
with utils.TemporaryVimOption( 'equalalways', 1 ):
|
||||
# Call stack
|
||||
vim.command( 'spl' )
|
||||
vim.command( 'enew' )
|
||||
self._stackTraceView = stack_trace.StackTraceView( self,
|
||||
self._connection,
|
||||
vim.current.buffer )
|
||||
|
||||
# Output/logging
|
||||
vim.command( 'spl' )
|
||||
vim.command( 'enew' )
|
||||
self._outputBuffer = vim.current.buffer
|
||||
utils.SetUpScratchBuffer( self._outputBuffer )
|
||||
|
||||
# Variables
|
||||
vim.command( 'spl' )
|
||||
vim.command( 'enew' )
|
||||
self._variablesView = variables.VariablesView( self._connection,
|
||||
vim.current.buffer )
|
||||
|
||||
def SetCurrentFrame( self, frame ):
|
||||
self._currentFrame = frame
|
||||
self._codeView.SetCurrentFrame( frame )
|
||||
self._variablesView.LoadScopes( frame )
|
||||
|
||||
def _StartDebugAdapter( self, adapter_config ):
|
||||
self._logger.info( 'Starting debug adapter with: {0}'.format( json.dumps(
|
||||
adapter_config ) ) )
|
||||
|
||||
channel_send_func = vim.bindeval(
|
||||
"vimspector#internal#job#StartDebugSession( {0} )".format(
|
||||
json.dumps( adapter_config ) ) )
|
||||
|
||||
self._connection = debug_adapter_connection.DebugAdapterConnection(
|
||||
self,
|
||||
channel_send_func )
|
||||
|
||||
self._logger.info( 'Debug Adapter Started' )
|
||||
|
||||
|
||||
self._connection.DoRequest( handler, {
|
||||
def _Initialise( self, adapter_config, launch_config ):
|
||||
self._logger.info( 'Initialising adapter with config {0}'.format(
|
||||
json.dumps( launch_config ) ) )
|
||||
|
||||
self._connection.DoRequest( lambda msg: self._Launch( launch_config ), {
|
||||
'command': 'initialize',
|
||||
'arguments': {
|
||||
'adapterID': 'cppdbg', # Apparently only MS debugger cares
|
||||
'adapterID': adapter_config.get( 'name', 'adapter' ),
|
||||
'linesStartAt1': True,
|
||||
'columnsStartAt1': True,
|
||||
'pathFormat': 'path',
|
||||
},
|
||||
} )
|
||||
|
||||
|
||||
def _Launch( self, launch_config ):
|
||||
self._connection.DoRequest( None, {
|
||||
'command': launch_config[ 'request' ],
|
||||
'arguments': launch_config
|
||||
} )
|
||||
|
||||
def OnEvent_initialized( self, message ):
|
||||
self._connection.DoRequest( None, {
|
||||
'command': 'setFunctionBreakpoints',
|
||||
|
|
|
|||
|
|
@ -13,13 +13,10 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import logging
|
||||
import vim
|
||||
|
||||
from vimspector import utils
|
||||
|
||||
_logger = logging.getLogger( __name__ )
|
||||
|
||||
|
||||
class StackTraceView( object ):
|
||||
def __init__( self, session, connection, buf ):
|
||||
|
|
@ -59,10 +56,11 @@ class StackTraceView( object ):
|
|||
stackFrames = message[ 'body' ][ 'stackFrames' ]
|
||||
|
||||
for frame in stackFrames:
|
||||
source = frame[ 'source' ] or { 'name': '<unknown>' }
|
||||
self._buf.append(
|
||||
'{0}: {1}@{2}:{3}'.format( frame[ 'id' ],
|
||||
frame[ 'name' ],
|
||||
frame[ 'source' ][ 'name' ],
|
||||
source[ 'name' ],
|
||||
frame[ 'line' ] ) )
|
||||
self._line_to_frame[ len( self._buf ) ] = frame
|
||||
|
||||
|
|
|
|||
|
|
@ -18,16 +18,15 @@ import logging
|
|||
import os
|
||||
import contextlib
|
||||
import vim
|
||||
|
||||
_logger = logging.getLogger( __name__ )
|
||||
import json
|
||||
|
||||
|
||||
def SetUpLogging():
|
||||
def SetUpLogging( logger ):
|
||||
handler = logging.FileHandler( os.path.expanduser( '~/.vimspector.log' ) )
|
||||
_logger.setLevel( logging.DEBUG )
|
||||
logger.setLevel( logging.DEBUG )
|
||||
handler.setFormatter(
|
||||
logging.Formatter( '%(asctime)s - %(levelname)s - %(message)s' ) )
|
||||
_logger.addHandler( handler )
|
||||
logger.addHandler( handler )
|
||||
|
||||
|
||||
def SetUpScratchBuffer( buf ):
|
||||
|
|
@ -68,3 +67,42 @@ def TemporaryVimOption( opt, value ):
|
|||
yield
|
||||
finally:
|
||||
vim.options[ opt ] = old_value
|
||||
|
||||
|
||||
def PathToConfigFile( file_name ):
|
||||
p = os.getcwd()
|
||||
while True:
|
||||
candidate = os.path.join( p, file_name )
|
||||
if os.path.exists( candidate ):
|
||||
return candidate
|
||||
|
||||
parent = os.path.dirname( p )
|
||||
if parent == p:
|
||||
return None
|
||||
p = parent
|
||||
|
||||
|
||||
def Escape( msg ):
|
||||
return msg.replace( "'", "''" )
|
||||
|
||||
|
||||
def UserMessage( msg, persist=False ):
|
||||
vim.command( 'redraw' )
|
||||
cmd = 'echom' if persist else 'echo'
|
||||
for line in msg.split( '\n' ):
|
||||
vim.command( '{0} \'{1}\''.format( cmd, Escape( line ) ) )
|
||||
|
||||
|
||||
def SelectFromList( prompt, options ):
|
||||
vim.eval( 'inputsave()' )
|
||||
display_options = [ prompt ]
|
||||
display_options.extend( [ '{0}: {1}'.format( i + 1, v )
|
||||
for i, v in enumerate( options ) ] )
|
||||
try:
|
||||
selection = int( vim.eval(
|
||||
'inputlist( ' + json.dumps( display_options ) + ' )' ) ) - 1
|
||||
if selection < 0 or selection >= len( options ):
|
||||
return None
|
||||
return options[ selection ]
|
||||
finally:
|
||||
vim.eval( 'inputrestore()' )
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue