Compare commits
4 commits
master
...
multiple-s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f05a7f66a | ||
|
|
0cdab6be4e | ||
|
|
099431ba55 | ||
|
|
fb2bad216f |
21 changed files with 692 additions and 225 deletions
|
|
@ -19,30 +19,36 @@ let s:save_cpo = &cpoptions
|
|||
set cpoptions&vim
|
||||
" }}}
|
||||
|
||||
function! s:_OnServerData( channel, data ) abort
|
||||
if !exists( 's:ch' ) || s:ch isnot a:channel
|
||||
let s:channels = {}
|
||||
let s:jobs = {}
|
||||
|
||||
function! s:_OnServerData( session_id, channel, data ) abort
|
||||
if !has_key( s:channels, a:session_id ) ||
|
||||
\ s:channels[ a:session_id ] isnot a:channel
|
||||
return
|
||||
endif
|
||||
|
||||
py3 << EOF
|
||||
_vimspector_session.OnChannelData( vim.eval( 'a:data' ) )
|
||||
EOF
|
||||
py3 _VimspectorSession( vim.eval( 'a:session_id' ) ).OnChannelData(
|
||||
\ vim.eval( 'a:data' ) )
|
||||
endfunction
|
||||
|
||||
function! s:_OnClose( channel ) abort
|
||||
if !exists( 's:ch' ) || s:ch isnot a:channel
|
||||
function! s:_OnClose( session_id, channel ) abort
|
||||
if !has_key( s:channels, a:session_id ) ||
|
||||
\ s:channels[ a:session_id ] isnot a:channel
|
||||
return
|
||||
endif
|
||||
|
||||
echom 'Channel closed'
|
||||
redraw
|
||||
unlet s:ch
|
||||
py3 _vimspector_session.OnServerExit( 0 )
|
||||
unlet s:channels[ a:session_id ]
|
||||
py3 _VimspectorSession( vim.eval( 'a:session_id' ) ).OnServerExit( 0 )
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#channel#StartDebugSession( config ) abort
|
||||
function! vimspector#internal#channel#StartDebugSession(
|
||||
\ session_id,
|
||||
\ config ) abort
|
||||
|
||||
if exists( 's:ch' )
|
||||
if has_key( s:channels, a:session_id )
|
||||
echo 'Channel is already running'
|
||||
return v:false
|
||||
endif
|
||||
|
|
@ -50,7 +56,7 @@ function! vimspector#internal#channel#StartDebugSession( config ) abort
|
|||
" If we _also_ have a command line, then start the actual job. This allows for
|
||||
" servers which start up and listen on some port
|
||||
if has_key( a:config, 'command' )
|
||||
let s:job = job_start( a:config[ 'command' ],
|
||||
let s:jobs[ a:session_id ] = job_start( a:config[ 'command' ],
|
||||
\ {
|
||||
\ 'in_mode': 'raw',
|
||||
\ 'out_mode': 'raw',
|
||||
|
|
@ -65,16 +71,19 @@ function! vimspector#internal#channel#StartDebugSession( config ) abort
|
|||
let l:addr = get( a:config, 'host', '127.0.0.1' ) . ':' . a:config[ 'port' ]
|
||||
|
||||
echo 'Connecting to ' . l:addr . '... (waiting fo up to 10 seconds)'
|
||||
let s:ch = ch_open( l:addr,
|
||||
" FIXME: This _always_ waits 10s; the neochannel version is quicker
|
||||
let s:channels[ a:session_id ] = ch_open( l:addr,
|
||||
\ {
|
||||
\ 'mode': 'raw',
|
||||
\ 'callback': funcref( 's:_OnServerData' ),
|
||||
\ 'close_cb': funcref( 's:_OnClose' ),
|
||||
\ 'callback': funcref( 's:_OnServerData',
|
||||
\ [ a:session_id ] ),
|
||||
\ 'close_cb': funcref( 's:_OnClose',
|
||||
\ [ a:session_id ] ),
|
||||
\ 'waittime': 10000,
|
||||
\ }
|
||||
\ )
|
||||
|
||||
if ch_status( s:ch ) !=# 'open'
|
||||
if ch_status( s:channels[ a:session_id ] ) !=# 'open'
|
||||
echom 'Unable to connect to' l:addr
|
||||
redraw
|
||||
return v:false
|
||||
|
|
@ -83,62 +92,67 @@ function! vimspector#internal#channel#StartDebugSession( config ) abort
|
|||
return v:true
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#channel#Send( msg ) abort
|
||||
call ch_sendraw( s:ch, a:msg )
|
||||
function! vimspector#internal#channel#Send( session_id, msg ) abort
|
||||
call ch_sendraw( s:channels[ a:session_id ], a:msg )
|
||||
return 1
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#channel#Timeout( id ) abort
|
||||
py3 << EOF
|
||||
_vimspector_session.OnRequestTimeout( vim.eval( 'a:id' ) )
|
||||
EOF
|
||||
function! vimspector#internal#channel#Timeout( session_id, id ) abort
|
||||
py3 _VimspectorSession( vim.eval( 'a:session_id' ) ).OnRequestTimeout(
|
||||
\ vim.eval( 'a:id' ) )
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#channel#StopDebugSession() abort
|
||||
function! s:_ChannelExists( session_id ) abort
|
||||
return has_key( s:channels, a:session_id ) &&
|
||||
\ count( [ 'closed', 'fail' ],
|
||||
\ ch_status( s:channels[ a:session_id ] ) ) == 0
|
||||
endfunction
|
||||
|
||||
if exists( 's:job' )
|
||||
function! vimspector#internal#channel#StopDebugSession( session_id ) abort
|
||||
|
||||
if has_key( s:jobs, a:session_id )
|
||||
" We started the job, so we need to kill it and wait to read all the data
|
||||
" from the socket
|
||||
|
||||
if job_status( s:job ) ==# 'run'
|
||||
call job_stop( s:job, 'term' )
|
||||
let job = s:jobs[ a:session_id ]
|
||||
if job_status( job ) ==# 'run'
|
||||
call job_stop( job, 'term' )
|
||||
endif
|
||||
|
||||
while job_status( s:job ) ==# 'run'
|
||||
call job_stop( s:job, 'kill' )
|
||||
while job_status( job ) ==# 'run'
|
||||
call job_stop( job, 'kill' )
|
||||
endwhile
|
||||
|
||||
unlet s:job
|
||||
call remove( s:jobs, a:session_id )
|
||||
|
||||
if exists( 's:ch' ) && count( [ 'closed', 'fail' ], ch_status( s:ch ) ) == 0
|
||||
if s:_ChannelExists( a:session_id )
|
||||
" We're going to block on this channel reading, then manually call the
|
||||
" close callback, so remove the automatic close callback to avoid tricky
|
||||
" re-entrancy
|
||||
call ch_setoptions( s:ch, { 'close_cb': '' } )
|
||||
call ch_setoptions( s:channels[ a:session_id ], { 'close_cb': '' } )
|
||||
endif
|
||||
|
||||
elseif exists( 's:ch' ) &&
|
||||
\ count( [ 'closed', 'fail' ], ch_status( s:ch ) ) == 0
|
||||
|
||||
elseif s:_ChannelExists( a:session_id )
|
||||
" channel is open, close it and trigger the callback. The callback is _not_
|
||||
" triggered when manually calling ch_close. if we get here and the channel
|
||||
" is not open, then we there is a _OnClose callback waiting for us, so do
|
||||
" nothing.
|
||||
call ch_close( s:ch )
|
||||
call ch_close( s:channels[ a:session_id ] )
|
||||
endif
|
||||
|
||||
" block until we've read all data from the socket and handled it.
|
||||
while count( [ 'open', 'buffered' ], ch_status( s:ch ) ) == 1
|
||||
let data = ch_read( s:ch, { 'timeout': 10 } )
|
||||
call s:_OnServerData( s:ch, data )
|
||||
while has_key( s:channels, a:session_id ) &&
|
||||
\ count( [ 'open', 'buffered' ],
|
||||
\ ch_status( s:channels[ a:session_id ] ) ) == 1
|
||||
let data = ch_read( s:channels[ a:session_id ], { 'timeout': 10 } )
|
||||
call s:_OnServerData( s:channels[ a:session_id ], data )
|
||||
endwhile
|
||||
call s:_OnClose( s:ch )
|
||||
if has_key( s:channels, a:session_id )
|
||||
call s:_OnClose( a:session_id, s:channels[ a:session_id ] )
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#channel#Reset() abort
|
||||
if exists( 's:ch' ) || exists( 's:job' )
|
||||
call vimspector#internal#channel#StopDebugSession()
|
||||
endif
|
||||
function! vimspector#internal#channel#Reset( session_id ) abort
|
||||
call vimspector#internal#channel#StopDebugSession( a:session_id )
|
||||
endfunction
|
||||
|
||||
" Boilerplate {{{
|
||||
|
|
|
|||
|
|
@ -19,44 +19,54 @@ let s:save_cpo = &cpoptions
|
|||
set cpoptions&vim
|
||||
" }}}
|
||||
|
||||
function! s:_OnServerData( channel, data ) abort
|
||||
if !exists( 's:job' ) || ch_getjob( a:channel ) isnot s:job
|
||||
let s:jobs = {}
|
||||
let s:commands = {}
|
||||
|
||||
function! s:_OnServerData( session_id, channel, data ) abort
|
||||
if !has_key( s:jobs, a:session_id ) ||
|
||||
\ ch_getjob( a:channel ) isnot s:jobs[ a:session_id ]
|
||||
call ch_log( 'Get data after process exit' )
|
||||
return
|
||||
endif
|
||||
|
||||
py3 _vimspector_session.OnChannelData( vim.eval( 'a:data' ) )
|
||||
py3 _VimspectorSession( vim.eval( 'a:session_id' ) ).OnChannelData(
|
||||
\ vim.eval( 'a:data' ) )
|
||||
endfunction
|
||||
|
||||
function! s:_OnServerError( channel, data ) abort
|
||||
if !exists( 's:job' ) || ch_getjob( a:channel ) isnot s:job
|
||||
function! s:_OnServerError( session_id, channel, data ) abort
|
||||
if !has_key( s:jobs, a:session_id ) ||
|
||||
\ ch_getjob( a:channel ) isnot s:jobs[ a:session_id ]
|
||||
call ch_log( 'Get data after process exit' )
|
||||
return
|
||||
endif
|
||||
|
||||
py3 _vimspector_session.OnServerStderr( vim.eval( 'a:data' ) )
|
||||
py3 _VimspectorSession( vim.eval( 'a:session_id' ) ).OnServerStderr(
|
||||
\ vim.eval( 'a:data' ) )
|
||||
endfunction
|
||||
|
||||
|
||||
" FIXME: We should wait until both the exit_cb _and_ the channel closed callback
|
||||
" have been received before OnServerExit?
|
||||
|
||||
function! s:_OnExit( channel, status ) abort
|
||||
if !exists( 's:job' ) || ch_getjob( a:channel ) isnot s:job
|
||||
function! s:_OnExit( session_id, channel, status ) abort
|
||||
if !has_key( s:jobs, a:session_id ) ||
|
||||
\ ch_getjob( a:channel ) isnot s:jobs[ a:session_id ]
|
||||
call ch_log( 'Unexpected exit callback' )
|
||||
return
|
||||
endif
|
||||
|
||||
echom 'Channel exit with status ' . a:status
|
||||
redraw
|
||||
if exists( 's:job' )
|
||||
unlet s:job
|
||||
if has_key( s:jobs, a:session_id )
|
||||
unlet s:jobs[ a:session_id ]
|
||||
endif
|
||||
py3 _vimspector_session.OnServerExit( vim.eval( 'a:status' ) )
|
||||
py3 _VimspectorSession( vim.eval( 'a:session_id' ) ).OnServerExit(
|
||||
\ vim.eval( 'a:status' ) )
|
||||
endfunction
|
||||
|
||||
function! s:_OnClose( channel ) abort
|
||||
if !exists( 's:job' ) || job_getchannel( s:job ) != a:channel
|
||||
function! s:_OnClose( session_id, channel ) abort
|
||||
if !has_key( s:jobs, a:session_id ) ||
|
||||
\ ch_getjob( a:channel ) isnot s:jobs[ a:session_id ]
|
||||
call ch_log( 'Channel closed after exit' )
|
||||
return
|
||||
endif
|
||||
|
|
@ -65,34 +75,38 @@ function! s:_OnClose( channel ) abort
|
|||
redraw
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#job#StartDebugSession( config ) abort
|
||||
if exists( 's:job' )
|
||||
function! vimspector#internal#job#StartDebugSession( session_id, config ) abort
|
||||
if has_key( s:jobs, a:session_id )
|
||||
echom 'Not starting: Job is already running'
|
||||
redraw
|
||||
return v:false
|
||||
endif
|
||||
|
||||
let s:job = job_start( a:config[ 'command' ],
|
||||
let s:jobs[ a:session_id ] = job_start( a:config[ 'command' ],
|
||||
\ {
|
||||
\ 'in_mode': 'raw',
|
||||
\ 'out_mode': 'raw',
|
||||
\ 'err_mode': 'raw',
|
||||
\ 'exit_cb': funcref( 's:_OnExit' ),
|
||||
\ 'close_cb': funcref( 's:_OnClose' ),
|
||||
\ 'out_cb': funcref( 's:_OnServerData' ),
|
||||
\ 'err_cb': funcref( 's:_OnServerError' ),
|
||||
\ 'exit_cb': funcref( 's:_OnExit',
|
||||
\ [ a:session_id ] ),
|
||||
\ 'close_cb': funcref( 's:_OnClose',
|
||||
\ [ a:session_id ] ),
|
||||
\ 'out_cb': funcref( 's:_OnServerData',
|
||||
\ [ a:session_id ] ),
|
||||
\ 'err_cb': funcref( 's:_OnServerError',
|
||||
\ [ a:session_id ] ),
|
||||
\ 'stoponexit': 'term',
|
||||
\ 'env': a:config[ 'env' ],
|
||||
\ 'cwd': a:config[ 'cwd' ],
|
||||
\ }
|
||||
\ )
|
||||
|
||||
if !exists( 's:job' )
|
||||
if !has_key( s:jobs, a:session_id )
|
||||
" The job died immediately after starting and we cleaned up
|
||||
return v:false
|
||||
endif
|
||||
|
||||
let status = job_status( s:job )
|
||||
let status = job_status( s:jobs[ a:session_id ] )
|
||||
|
||||
echom 'Started job, status is: ' . status
|
||||
redraw
|
||||
|
|
@ -104,20 +118,22 @@ function! vimspector#internal#job#StartDebugSession( config ) abort
|
|||
return v:true
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#job#Send( msg ) abort
|
||||
if ! exists( 's:job' )
|
||||
function! vimspector#internal#job#Send( session_id, msg ) abort
|
||||
if ! has_key( s:jobs, a:session_id )
|
||||
echom "Can't send message: Job was not initialised correctly"
|
||||
redraw
|
||||
return 0
|
||||
endif
|
||||
|
||||
if job_status( s:job ) !=# 'run'
|
||||
let job = s:jobs[ a:session_id ]
|
||||
|
||||
if job_status( job ) !=# 'run'
|
||||
echom "Can't send message: Job is not running"
|
||||
redraw
|
||||
return 0
|
||||
endif
|
||||
|
||||
let ch = job_getchannel( s:job )
|
||||
let ch = job_getchannel( job )
|
||||
if ch ==# 'channel fail'
|
||||
echom 'Channel was closed unexpectedly!'
|
||||
redraw
|
||||
|
|
@ -128,45 +144,55 @@ function! vimspector#internal#job#Send( msg ) abort
|
|||
return 1
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#job#StopDebugSession() abort
|
||||
if !exists( 's:job' )
|
||||
function! vimspector#internal#job#StopDebugSession( session_id ) abort
|
||||
if ! has_key( s:jobs, a:session_id )
|
||||
echom "Not stopping session: Job doesn't exist"
|
||||
redraw
|
||||
return
|
||||
endif
|
||||
|
||||
if job_status( s:job ) ==# 'run'
|
||||
let job = s:jobs[ a:session_id ]
|
||||
|
||||
if job_status( job ) ==# 'run'
|
||||
echom 'Terminating job'
|
||||
redraw
|
||||
call job_stop( s:job, 'kill' )
|
||||
call job_stop( job, 'kill' )
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#job#Reset() abort
|
||||
call vimspector#internal#job#StopDebugSession()
|
||||
function! vimspector#internal#job#Reset( session_id ) abort
|
||||
call vimspector#internal#job#StopDebugSession( a:session_id )
|
||||
endfunction
|
||||
|
||||
function! s:_OnCommandExit( category, ch, code ) abort
|
||||
function! s:_OnCommandExit( session_id, category, ch, code ) abort
|
||||
py3 __import__( "vimspector",
|
||||
\ fromlist = [ "utils" ] ).utils.OnCommandWithLogComplete(
|
||||
\ vim.eval( 'a:session_id' ),
|
||||
\ vim.eval( 'a:category' ),
|
||||
\ int( vim.eval( 'a:code' ) ) )
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#job#StartCommandWithLog( cmd, category ) abort
|
||||
function! vimspector#internal#job#StartCommandWithLog(
|
||||
\ session_id,
|
||||
\ cmd,
|
||||
\ category ) abort
|
||||
if ! exists( 's:commands' )
|
||||
let s:commands = {}
|
||||
endif
|
||||
|
||||
if ! has_key( s:commands, a:category )
|
||||
let s:commands[ a:category ] = []
|
||||
if ! has_key( s:commands, a:session_id )
|
||||
let s:commands[ a:session_id ] = {}
|
||||
endif
|
||||
|
||||
let l:index = len( s:commands[ a:category ] )
|
||||
if ! has_key( s:commands[ a:session_id ], a:category )
|
||||
let s:commands[ a:session_id ][ a:category ] = []
|
||||
endif
|
||||
|
||||
let buf = '_vimspector_log_' . a:category
|
||||
let l:index = len( s:commands[ a:session_id ][ a:category ] )
|
||||
|
||||
call add( s:commands[ a:category ], job_start(
|
||||
let buf = '_vimspector_log_' . a:session_id . '_' . a:category
|
||||
|
||||
call add( s:commands[ a:session_id ][ a:category ], job_start(
|
||||
\ a:cmd,
|
||||
\ {
|
||||
\ 'out_io': 'buffer',
|
||||
|
|
@ -175,13 +201,14 @@ function! vimspector#internal#job#StartCommandWithLog( cmd, category ) abort
|
|||
\ 'err_msg': 0,
|
||||
\ 'out_name': buf,
|
||||
\ 'err_name': buf,
|
||||
\ 'exit_cb': funcref( 's:_OnCommandExit', [ a:category ] ),
|
||||
\ 'exit_cb': funcref( 's:_OnCommandExit',
|
||||
\ [ a:session_id, a:category ] ),
|
||||
\ 'out_modifiable': 0,
|
||||
\ 'err_modifiable': 0,
|
||||
\ 'stoponexit': 'kill'
|
||||
\ } ) )
|
||||
|
||||
if job_status( s:commands[ a:category ][ index ] ) !=# 'run'
|
||||
if job_status( s:commands[ a:session_id ][ a:category ][ index ] ) !=# 'run'
|
||||
echom 'Unable to start job for ' . string( a:cmd )
|
||||
redraw
|
||||
return v:none
|
||||
|
|
@ -191,19 +218,27 @@ function! vimspector#internal#job#StartCommandWithLog( cmd, category ) abort
|
|||
endfunction
|
||||
|
||||
|
||||
function! vimspector#internal#job#CleanUpCommand( category ) abort
|
||||
function! vimspector#internal#job#CleanUpCommand( session_id, category ) abort
|
||||
if ! exists( 's:commands' )
|
||||
let s:commands = {}
|
||||
endif
|
||||
|
||||
if ! has_key( s:commands, a:category )
|
||||
if ! has_key( s:commands, a:session_id )
|
||||
let s:commands[ a:session_id ] = {}
|
||||
endif
|
||||
|
||||
if ! has_key( s:commands[ a:session_id ], a:category )
|
||||
return
|
||||
endif
|
||||
for j in s:commands[ a:category ]
|
||||
for j in s:commands[ a:session_id ][ a:category ]
|
||||
call job_stop( j, 'kill' )
|
||||
endfor
|
||||
|
||||
unlet s:commands[ a:category ]
|
||||
unlet s:commands[ a:session_id ][ a:category ]
|
||||
|
||||
if len( s:commands[ a:session_id ] ) == 0
|
||||
unlet s:commands[ a:session_id ]
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" Boilerplate {{{
|
||||
|
|
|
|||
|
|
@ -20,28 +20,34 @@ set cpoptions&vim
|
|||
" }}}
|
||||
|
||||
|
||||
let s:channels = {}
|
||||
let s:jobs = {}
|
||||
|
||||
function! s:_OnEvent( chan_id, data, event ) abort
|
||||
|
||||
function! s:_OnEvent( session_id, chan_id, data, event ) abort
|
||||
if v:exiting isnot# v:null
|
||||
return
|
||||
endif
|
||||
|
||||
if !exists( 's:ch' ) || a:chan_id != s:ch
|
||||
if !has_key( s:channels, a:session_id ) ||
|
||||
\ a:chan_id != s:channels[ a:session_id ]
|
||||
return
|
||||
endif
|
||||
|
||||
if a:data == ['']
|
||||
echom 'Channel closed'
|
||||
redraw
|
||||
unlet s:ch
|
||||
unlet s:channels[ a:session_id ]
|
||||
py3 _vimspector_session.OnServerExit( 0 )
|
||||
else
|
||||
py3 _vimspector_session.OnChannelData( '\n'.join( vim.eval( 'a:data' ) ) )
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#neochannel#StartDebugSession( config ) abort
|
||||
if exists( 's:ch' )
|
||||
function! vimspector#internal#neochannel#StartDebugSession(
|
||||
\ session_id,
|
||||
\ config ) abort
|
||||
if has_key( s:channels, a:session_id )
|
||||
echom 'Not starting: Channel is already running'
|
||||
redraw
|
||||
return v:false
|
||||
|
|
@ -54,12 +60,12 @@ function! vimspector#internal#neochannel#StartDebugSession( config ) abort
|
|||
try
|
||||
let old_env = vimspector#internal#neoterm#PrepareEnvironment(
|
||||
\ a:config[ 'env' ] )
|
||||
let s:job = jobstart( a:config[ 'command' ],
|
||||
\ {
|
||||
\ 'cwd': a:config[ 'cwd' ],
|
||||
\ 'env': a:config[ 'env' ],
|
||||
\ }
|
||||
\ )
|
||||
let s:jobs[ a:session_id ] = jobstart( a:config[ 'command' ],
|
||||
\ {
|
||||
\ 'cwd': a:config[ 'cwd' ],
|
||||
\ 'env': a:config[ 'env' ],
|
||||
\ }
|
||||
\ )
|
||||
finally
|
||||
call vimspector#internal#neoterm#ResetEnvironment( a:config[ 'env' ],
|
||||
\ old_env )
|
||||
|
|
@ -72,9 +78,10 @@ function! vimspector#internal#neochannel#StartDebugSession( config ) abort
|
|||
while attempt <= 10
|
||||
echo 'Connecting to ' . l:addr . '... (attempt' attempt 'of 10)'
|
||||
try
|
||||
let s:ch = sockconnect( 'tcp',
|
||||
\ addr,
|
||||
\ { 'on_data': funcref( 's:_OnEvent' ) } )
|
||||
let s:channels[ a:session_id ] = sockconnect(
|
||||
\ 'tcp',
|
||||
\ addr,
|
||||
\ { 'on_data': funcref( 's:_OnEvent', [ a:session_id ] ) } )
|
||||
redraw
|
||||
return v:true
|
||||
catch /connection refused/
|
||||
|
|
@ -88,30 +95,30 @@ function! vimspector#internal#neochannel#StartDebugSession( config ) abort
|
|||
return v:false
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#neochannel#Send( msg ) abort
|
||||
if ! exists( 's:ch' )
|
||||
function! vimspector#internal#neochannel#Send( session_id, msg ) abort
|
||||
if ! has_key( s:channels, a:session_id )
|
||||
echom "Can't send message: Channel was not initialised correctly"
|
||||
redraw
|
||||
return 0
|
||||
endif
|
||||
|
||||
call chansend( s:ch, a:msg )
|
||||
call chansend( s:channels[ a:session_id ], a:msg )
|
||||
return 1
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#neochannel#StopDebugSession() abort
|
||||
if exists( 's:ch' )
|
||||
call chanclose( s:ch )
|
||||
function! vimspector#internal#neochannel#StopDebugSession( session_id ) abort
|
||||
if has_key( s:channels, a:session_id )
|
||||
call chanclose( s:channels[ a:session_id ] )
|
||||
" It doesn't look like we get a callback after chanclos. Who knows if we
|
||||
" will subsequently receive data callbacks.
|
||||
call s:_OnEvent( s:ch, [ '' ], 'data' )
|
||||
call s:_OnEvent( a:session_id, s:channels[ a:session_id ], [ '' ], 'data' )
|
||||
endif
|
||||
|
||||
if exists( 's:job' )
|
||||
if vimspector#internal#neojob#JobIsRunning( s:job )
|
||||
call jobstop( s:job )
|
||||
if has_key( s:jobs, a:session_id )
|
||||
if vimspector#internal#neojob#JobIsRunning( s:jobs[ a:session_id ] )
|
||||
call jobstop( s:jobs[ a:session_id ] )
|
||||
endif
|
||||
unlet s:job
|
||||
unlet s:jobs[ a:session_id ]
|
||||
endif
|
||||
endfunction
|
||||
|
||||
|
|
|
|||
|
|
@ -19,14 +19,17 @@ let s:save_cpo = &cpoptions
|
|||
set cpoptions&vim
|
||||
" }}}
|
||||
|
||||
let s:jobs = {}
|
||||
let s:commands = {}
|
||||
|
||||
|
||||
function! s:_OnEvent( chan_id, data, event ) abort
|
||||
|
||||
function! s:_OnEvent( session_id, chan_id, data, event ) abort
|
||||
if v:exiting isnot# v:null
|
||||
return
|
||||
endif
|
||||
|
||||
if !exists( 's:job' ) || a:chan_id != s:job
|
||||
if !has_key( s:jobs, a:session_id ) || a:chan_id != s:jobs[ a:session_id ]
|
||||
return
|
||||
endif
|
||||
|
||||
|
|
@ -38,13 +41,15 @@ function! s:_OnEvent( chan_id, data, event ) abort
|
|||
elseif a:event ==# 'exit'
|
||||
echom 'Channel exit with status ' . a:data
|
||||
redraw
|
||||
unlet s:job
|
||||
unlet s:jobs[ a:session_id ]
|
||||
py3 _vimspector_session.OnServerExit( vim.eval( 'a:data' ) )
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#neojob#StartDebugSession( config ) abort
|
||||
if exists( 's:job' )
|
||||
function! vimspector#internal#neojob#StartDebugSession(
|
||||
\ session_id,
|
||||
\ config ) abort
|
||||
if has_key( s:jobs, a:session_id )
|
||||
echom 'Not starging: Job is already running'
|
||||
redraw
|
||||
return v:false
|
||||
|
|
@ -57,11 +62,14 @@ function! vimspector#internal#neojob#StartDebugSession( config ) abort
|
|||
try
|
||||
let old_env = vimspector#internal#neoterm#PrepareEnvironment(
|
||||
\ a:config[ 'env' ] )
|
||||
let s:job = jobstart( a:config[ 'command' ],
|
||||
let s:jobs[ a:session_id ] = jobstart( a:config[ 'command' ],
|
||||
\ {
|
||||
\ 'on_stdout': funcref( 's:_OnEvent' ),
|
||||
\ 'on_stderr': funcref( 's:_OnEvent' ),
|
||||
\ 'on_exit': funcref( 's:_OnEvent' ),
|
||||
\ 'on_stdout': funcref( 's:_OnEvent',
|
||||
\ [ a:session_id ] ),
|
||||
\ 'on_stderr': funcref( 's:_OnEvent',
|
||||
\ [ a:session_id ] ),
|
||||
\ 'on_exit': funcref( 's:_OnEvent',
|
||||
\ [ a:session_id ] ),
|
||||
\ 'cwd': a:config[ 'cwd' ],
|
||||
\ 'env': a:config[ 'env' ],
|
||||
\ }
|
||||
|
|
@ -78,40 +86,40 @@ function! vimspector#internal#neojob#JobIsRunning( job ) abort
|
|||
return jobwait( [ a:job ], 0 )[ 0 ] == -1
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#neojob#Send( msg ) abort
|
||||
if ! exists( 's:job' )
|
||||
function! vimspector#internal#neojob#Send( session_id, msg ) abort
|
||||
if ! has_key( s:jobs, a:session_id )
|
||||
echom "Can't send message: Job was not initialised correctly"
|
||||
redraw
|
||||
return 0
|
||||
endif
|
||||
|
||||
if !vimspector#internal#neojob#JobIsRunning( s:job )
|
||||
if !vimspector#internal#neojob#JobIsRunning( s:jobs[ a:session_id ] )
|
||||
echom "Can't send message: Job is not running"
|
||||
redraw
|
||||
return 0
|
||||
endif
|
||||
|
||||
call chansend( s:job, a:msg )
|
||||
call chansend( s:jobs[ a:session_id ], a:msg )
|
||||
return 1
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#neojob#StopDebugSession() abort
|
||||
if !exists( 's:job' )
|
||||
function! vimspector#internal#neojob#StopDebugSession( session_id ) abort
|
||||
if !has_key( s:jobs, a:session_id )
|
||||
return
|
||||
endif
|
||||
|
||||
if vimspector#internal#neojob#JobIsRunning( s:job )
|
||||
if vimspector#internal#neojob#JobIsRunning( s:jobs[ a:session_id ] )
|
||||
echom 'Terminating job'
|
||||
redraw
|
||||
call jobstop( s:job )
|
||||
call jobstop( s:jobs[ a:session_id ] )
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#neojob#Reset() abort
|
||||
call vimspector#internal#neojob#StopDebugSession()
|
||||
function! vimspector#internal#neojob#Reset( session_id ) abort
|
||||
call vimspector#internal#neojob#StopDebugSession( a:session_id )
|
||||
endfunction
|
||||
|
||||
function! s:_OnCommandEvent( category, id, data, event ) abort
|
||||
function! s:_OnCommandEvent( session_id, category, id, data, event ) abort
|
||||
if v:exiting isnot# v:null
|
||||
return
|
||||
endif
|
||||
|
|
@ -121,18 +129,22 @@ function! s:_OnCommandEvent( category, id, data, event ) abort
|
|||
return
|
||||
endif
|
||||
|
||||
if !has_key( s:commands, a:category )
|
||||
if ! has_key( s:commands, a:session_id )
|
||||
return
|
||||
endif
|
||||
|
||||
if !has_key( s:commands[ a:category ], a:id )
|
||||
if !has_key( s:commands[ a:session_id ], a:category )
|
||||
return
|
||||
endif
|
||||
|
||||
if !has_key( s:commands[ a:session_id ][ a:category ], a:id )
|
||||
return
|
||||
endif
|
||||
|
||||
if a:event ==# 'stdout'
|
||||
let buffer = s:commands[ a:category ][ a:id ].stdout
|
||||
let buffer = s:commands[ a:session_id ][ a:category ][ a:id ].stdout
|
||||
elseif a:event ==# 'stderr'
|
||||
let buffer = s:commands[ a:category ][ a:id ].stderr
|
||||
let buffer = s:commands[ a:session_id ][ a:category ][ a:id ].stderr
|
||||
endif
|
||||
|
||||
try
|
||||
|
|
@ -173,6 +185,7 @@ function! s:_OnCommandEvent( category, id, data, event ) abort
|
|||
elseif a:event ==# 'exit'
|
||||
py3 __import__( "vimspector",
|
||||
\ fromlist = [ "utils" ] ).utils.OnCommandWithLogComplete(
|
||||
\ vim.eval( 'a:session_id' ),
|
||||
\ vim.eval( 'a:category' ),
|
||||
\ int( vim.eval( 'a:data' ) ) )
|
||||
endif
|
||||
|
|
@ -198,11 +211,16 @@ function! s:MakeBufferWritable( buffer ) abort
|
|||
endfunction
|
||||
|
||||
|
||||
let s:commands = {}
|
||||
function! vimspector#internal#neojob#StartCommandWithLog(
|
||||
\ session_id,
|
||||
\ cmd,
|
||||
\ category ) abort
|
||||
if ! has_key( s:commands, a:session_id )
|
||||
let s:commands[ a:session_id ] = {}
|
||||
endif
|
||||
|
||||
function! vimspector#internal#neojob#StartCommandWithLog( cmd, category ) abort
|
||||
if ! has_key( s:commands, a:category )
|
||||
let s:commands[ a:category ] = {}
|
||||
if ! has_key( s:commands[ a:session_id ], a:category )
|
||||
let s:commands[ a:session_id ][ a:category ] = {}
|
||||
endif
|
||||
|
||||
let buf = bufnr( '_vimspector_log_' . a:category, v:true )
|
||||
|
|
@ -215,14 +233,14 @@ function! vimspector#internal#neojob#StartCommandWithLog( cmd, category ) abort
|
|||
let id = jobstart(a:cmd,
|
||||
\ {
|
||||
\ 'on_stdout': funcref( 's:_OnCommandEvent',
|
||||
\ [ a:category ] ),
|
||||
\ [ a:session_id, a:category ] ),
|
||||
\ 'on_stderr': funcref( 's:_OnCommandEvent',
|
||||
\ [ a:category ] ),
|
||||
\ [ a:session_id, a:category ] ),
|
||||
\ 'on_exit': funcref( 's:_OnCommandEvent',
|
||||
\ [ a:category ] ),
|
||||
\ [ a:session_id, a:category ] ),
|
||||
\ } )
|
||||
|
||||
let s:commands[ a:category ][ id ] = {
|
||||
let s:commands[ a:session_id ][ a:category ][ id ] = {
|
||||
\ 'stdout': buf,
|
||||
\ 'stderr': buf
|
||||
\ }
|
||||
|
|
@ -230,19 +248,25 @@ function! vimspector#internal#neojob#StartCommandWithLog( cmd, category ) abort
|
|||
return buf
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#neojob#CleanUpCommand( category ) abort
|
||||
if ! has_key( s:commands, a:category )
|
||||
function! vimspector#internal#neojob#CleanUpCommand(
|
||||
\ session_id,
|
||||
\ category ) abort
|
||||
if ! has_key( s:commands, a:session_id )
|
||||
return
|
||||
endif
|
||||
|
||||
for id in keys( s:commands[ a:category ] )
|
||||
if ! has_key( s:commands[ a:session_id ], a:category )
|
||||
return
|
||||
endif
|
||||
|
||||
for id in keys( s:commands[ a:session_id ][ a:category ] )
|
||||
let id = str2nr( id )
|
||||
if jobwait( [ id ], 0 )[ 0 ] == -1
|
||||
call jobstop( id )
|
||||
endif
|
||||
call jobwait( [ id ], -1 )
|
||||
endfor
|
||||
unlet! s:commands[ a:category ]
|
||||
unlet! s:commands[ a:session_id ][ a:category ]
|
||||
endfunction
|
||||
|
||||
" Boilerplate {{{
|
||||
|
|
|
|||
|
|
@ -26,11 +26,22 @@ endif
|
|||
|
||||
function! vimspector#internal#state#Reset() abort
|
||||
try
|
||||
py3 import vim
|
||||
py3 _vimspector_session = __import__(
|
||||
\ "vimspector",
|
||||
\ fromlist=[ "debug_session" ] ).debug_session.DebugSession(
|
||||
\ vim.eval( 's:prefix' ) )
|
||||
py3 <<EOF
|
||||
|
||||
import vim
|
||||
|
||||
_vimspector_session_man = __import__(
|
||||
"vimspector",
|
||||
fromlist=[ "session_manager" ] ).session_manager.Get()
|
||||
|
||||
# Deprecated
|
||||
_vimspector_session = _vimspector_session_man.NewSession(
|
||||
vim.eval( 's:prefix' ) )
|
||||
|
||||
def _VimspectorSession( session_id ):
|
||||
return _vimspector_session_man.GetSession( int( session_id ) )
|
||||
|
||||
EOF
|
||||
catch /.*/
|
||||
echohl WarningMsg
|
||||
echom 'Exception while loading vimspector:' v:exception
|
||||
|
|
@ -47,6 +58,22 @@ function! vimspector#internal#state#GetAPIPrefix() abort
|
|||
return s:prefix
|
||||
endfunction
|
||||
|
||||
function! vimspector#internal#state#SwitchToSession( id ) abort
|
||||
py3 _vimspector_session = _VimspectorSession( vim.eval( 'a:id' ) )
|
||||
endfunction
|
||||
|
||||
|
||||
function! vimspector#internal#state#OnTabEnter() abort
|
||||
py3 <<EOF
|
||||
session = _vimspector_session_man.SessionForTab(
|
||||
int( vim.eval( 'tabpagenr()' ) ) )
|
||||
|
||||
if session is not None:
|
||||
_vimspector_session = session
|
||||
EOF
|
||||
endfunction
|
||||
|
||||
|
||||
" Boilerplate {{{
|
||||
let &cpoptions=s:save_cpo
|
||||
unlet s:save_cpo
|
||||
|
|
|
|||
|
|
@ -141,6 +141,7 @@ augroup END
|
|||
augroup Vimspector
|
||||
autocmd!
|
||||
autocmd BufNew * call vimspector#OnBufferCreated( expand( '<afile>' ) )
|
||||
autocmd TabEnter * call vimspector#internal#state#OnTabEnter()
|
||||
augroup END
|
||||
|
||||
" boilerplate {{{
|
||||
|
|
|
|||
|
|
@ -34,13 +34,24 @@ class ServerBreakpointHandler( object ):
|
|||
pass
|
||||
|
||||
|
||||
# FIXME: THis really should be project scope and not associated with a debug
|
||||
# session. Breakpoints set by the user should be independent and breakpoints for
|
||||
# the current active session should be associated with the session when they are
|
||||
# in use.
|
||||
#
|
||||
# Questions include:
|
||||
# 1. what happens if we set/chnage a breakpiont in session #2 while session #1
|
||||
# is active ? Maybe we re-send the breakpoints to _all_ active sessions?
|
||||
#
|
||||
# More...
|
||||
class ProjectBreakpoints( object ):
|
||||
def __init__( self ):
|
||||
def __init__( self, session_id ):
|
||||
self._connection = None
|
||||
self._logger = logging.getLogger( __name__ )
|
||||
utils.SetUpLogging( self._logger )
|
||||
self._logger = logging.getLogger( __name__ + '.' + str( session_id ) )
|
||||
utils.SetUpLogging( self._logger, session_id )
|
||||
|
||||
# These are the user-entered breakpoints.
|
||||
# These are the user-entered breakpoints. NOTE: if updating this, also
|
||||
# update Copy()
|
||||
self._line_breakpoints = defaultdict( list )
|
||||
self._func_breakpoints = []
|
||||
self._exception_breakpoints = None
|
||||
|
|
@ -91,6 +102,12 @@ class ProjectBreakpoints( object ):
|
|||
# FIXME: If the adapter type changes, we should probably forget this ?
|
||||
|
||||
|
||||
def Copy( self, other: 'ProjectBreakpoints' ):
|
||||
self._line_breakpoints = dict( other._line_breakpoints )
|
||||
self._func_breakpoints = list( other._func_breakpoints )
|
||||
if other._exception_breakpoints is not None:
|
||||
self._exception_breakpoints = dict( other._exception_breakpoints )
|
||||
|
||||
def BreakpointsAsQuickFix( self ):
|
||||
# FIXME: Handling of breakpoints is a mess, split between _codeView and this
|
||||
# object. This makes no sense and should be centralised so that we don't
|
||||
|
|
|
|||
|
|
@ -20,20 +20,20 @@ from collections import defaultdict
|
|||
|
||||
from vimspector import utils, terminal, signs
|
||||
|
||||
NEXT_SIGN_ID = 1
|
||||
|
||||
|
||||
class CodeView( object ):
|
||||
def __init__( self, window, api_prefix ):
|
||||
def __init__( self, session_id, window, api_prefix ):
|
||||
self._window = window
|
||||
self._api_prefix = api_prefix
|
||||
|
||||
self._terminal = None
|
||||
self.current_syntax = None
|
||||
|
||||
self._logger = logging.getLogger( __name__ )
|
||||
self._logger = logging.getLogger( __name__ + '.' + str( session_id ) )
|
||||
utils.SetUpLogging( self._logger )
|
||||
|
||||
# FIXME: This ID is by group, so should be module scope
|
||||
self._next_sign_id = 1
|
||||
self._breakpoints = defaultdict( list )
|
||||
self._signs = {
|
||||
'vimspectorPC': None,
|
||||
|
|
@ -92,8 +92,9 @@ class CodeView( object ):
|
|||
self._UndisplayPC( clear_pc = False )
|
||||
|
||||
# FIXME: Do we relly need to keep using up IDs ?
|
||||
self._signs[ 'vimspectorPC' ] = self._next_sign_id
|
||||
self._next_sign_id += 1
|
||||
global NEXT_SIGN_ID
|
||||
self._signs[ 'vimspectorPC' ] = NEXT_SIGN_ID
|
||||
NEXT_SIGN_ID += 1
|
||||
|
||||
sign = 'vimspectorPC'
|
||||
# If there's also a breakpoint on this line, use vimspectorPCBP
|
||||
|
|
@ -247,8 +248,9 @@ class CodeView( object ):
|
|||
if 'line' not in breakpoint:
|
||||
continue
|
||||
|
||||
sign_id = self._next_sign_id
|
||||
self._next_sign_id += 1
|
||||
global NEXT_SIGN_ID
|
||||
sign_id = NEXT_SIGN_ID
|
||||
NEXT_SIGN_ID += 1
|
||||
self._signs[ 'breakpoints' ].append( sign_id )
|
||||
if utils.BufferExists( file_name ):
|
||||
signs.PlaceSign( sign_id,
|
||||
|
|
|
|||
77
python3/vimspector/custom/python.py
Normal file
77
python3/vimspector/custom/python.py
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
# vimspector - A multi-language debugging system for Vim
|
||||
# Copyright 2021 Ben Jackson
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from vimspector.debug_session import DebugSession
|
||||
from vimspector import session_manager, gadgets, utils
|
||||
|
||||
from typing import Sequence
|
||||
|
||||
|
||||
class Debugpy( object ):
|
||||
parent: DebugSession
|
||||
sessions: Sequence[ DebugSession ]
|
||||
|
||||
def __init__( self, debug_session: DebugSession ):
|
||||
self.parent = debug_session
|
||||
self.queue = []
|
||||
|
||||
def LaunchSubprocessDebugSession( self, result ):
|
||||
launch_arguments = self.queue.pop( 0 )
|
||||
|
||||
if result == 1:
|
||||
session = session_manager.Get().NewSession( self.parent._api_prefix )
|
||||
|
||||
# Inject the launch config (HACK!). This will actually mean that the
|
||||
# configuration passed below is ignored.
|
||||
session._launch_config = launch_arguments
|
||||
|
||||
# FIXME: We probably do need to add a StartWithLauncArguments and somehow
|
||||
# tell the new session that it shoud not support "Restart" requests ?
|
||||
#
|
||||
# In fact, what even would Reset do... ?
|
||||
session._breakpoints.Copy( self.parent._breakpoints )
|
||||
session._StartWithConfiguration( { 'configuration': launch_arguments },
|
||||
launch_arguments[ 'connect' ] )
|
||||
|
||||
self.HandleNext()
|
||||
|
||||
|
||||
def OnEvent_debugpyAttach( self, message ):
|
||||
# Debugpy sends us the contents of a launch request that we should use. We
|
||||
# probaly just jave to guess the rest
|
||||
launch_arguments = message[ 'body' ]
|
||||
self.queue.append( launch_arguments )
|
||||
|
||||
# We use a queue because the confirm mechanism is quasi-modal and we can't
|
||||
# do multiple 'confirm' dialogs at once. It's not uncommon for
|
||||
# multiprocessing to create multiple subprocesses all at the same time.
|
||||
if len( self.queue ) == 1:
|
||||
self.HandleNext()
|
||||
|
||||
def HandleNext( self ):
|
||||
if not self.queue:
|
||||
return
|
||||
|
||||
launch_argyments = self.queue[ 0 ]
|
||||
pid = launch_argyments[ 'subProcessId' ]
|
||||
|
||||
utils.Confirm(
|
||||
self.parent._api_prefix,
|
||||
f"Subprocess {pid} was launched.\nAttach to it in a new tab?",
|
||||
self.LaunchSubprocessDebugSession,
|
||||
default_value = 1,
|
||||
options = [ 'Yes', 'No' ],
|
||||
keys = [ 'y', 'n' ] )
|
||||
|
||||
|
|
@ -29,14 +29,15 @@ class PendingRequest( object ):
|
|||
|
||||
|
||||
class DebugAdapterConnection( object ):
|
||||
def __init__( self, handlers, send_func ):
|
||||
self._logger = logging.getLogger( __name__ )
|
||||
utils.SetUpLogging( self._logger )
|
||||
def __init__( self, handlers, session_id, send_func ):
|
||||
self._logger = logging.getLogger( __name__ + '.' + str( session_id ) )
|
||||
utils.SetUpLogging( self._logger, session_id )
|
||||
|
||||
self._Write = send_func
|
||||
self._SetState( 'READ_HEADER' )
|
||||
self._buffer = bytes()
|
||||
self._handlers = handlers
|
||||
self._session_id = session_id
|
||||
self._next_message_id = 0
|
||||
self._outstanding_requests = {}
|
||||
|
||||
|
|
@ -51,10 +52,12 @@ class DebugAdapterConnection( object ):
|
|||
msg[ 'seq' ] = this_id
|
||||
msg[ 'type' ] = 'request'
|
||||
|
||||
# TODO/FIXME: This is so messy
|
||||
expiry_id = vim.eval(
|
||||
'timer_start( {}, "vimspector#internal#channel#Timeout" )'.format(
|
||||
timeout ) )
|
||||
'timer_start( {}, '
|
||||
' function( "vimspector#internal#channel#Timeout", '
|
||||
' [ {} ] ) )'.format(
|
||||
timeout,
|
||||
self._session_id ) )
|
||||
|
||||
request = PendingRequest( msg,
|
||||
handler,
|
||||
|
|
|
|||
|
|
@ -42,15 +42,17 @@ VIMSPECTOR_HOME = utils.GetVimspectorBase()
|
|||
# cache of what the user entered for any option we ask them
|
||||
USER_CHOICES = {}
|
||||
|
||||
|
||||
class DebugSession( object ):
|
||||
def __init__( self, api_prefix ):
|
||||
self._logger = logging.getLogger( __name__ )
|
||||
utils.SetUpLogging( self._logger )
|
||||
def __init__( self, session_id, session_manager, api_prefix ):
|
||||
self.session_id = session_id
|
||||
self.manager = session_manager
|
||||
self._logger = logging.getLogger( __name__ + '.' + str( session_id ) )
|
||||
utils.SetUpLogging( self._logger, session_id )
|
||||
|
||||
self._api_prefix = api_prefix
|
||||
|
||||
self._logger.info( "**** INITIALISING NEW VIMSPECTOR SESSION ****" )
|
||||
self._logger.info( "**** INITIALISING NEW VIMSPECTOR SESSION FOR ID "
|
||||
f"{session_id } ****" )
|
||||
self._logger.info( "API is: {}".format( api_prefix ) )
|
||||
self._logger.info( 'VIMSPECTOR_HOME = %s', VIMSPECTOR_HOME )
|
||||
self._logger.info( 'gadgetDir = %s',
|
||||
|
|
@ -61,7 +63,7 @@ class DebugSession( object ):
|
|||
self._stackTraceView = None
|
||||
self._variablesView = None
|
||||
self._outputView = None
|
||||
self._breakpoints = breakpoints.ProjectBreakpoints()
|
||||
self._breakpoints = breakpoints.ProjectBreakpoints( session_id )
|
||||
self._splash_screen = None
|
||||
self._remote_term = None
|
||||
|
||||
|
|
@ -73,6 +75,11 @@ class DebugSession( object ):
|
|||
|
||||
self._ResetServerState()
|
||||
|
||||
|
||||
def __del__( self ):
|
||||
self.manager.DestroySession( self )
|
||||
|
||||
|
||||
def _ResetServerState( self ):
|
||||
self._connection = None
|
||||
self._init_complete = False
|
||||
|
|
@ -408,7 +415,13 @@ class DebugSession( object ):
|
|||
if self._uiTab:
|
||||
self._logger.debug( "Clearing down UI" )
|
||||
|
||||
del vim.vars[ 'vimspector_session_windows' ]
|
||||
try:
|
||||
# FIXME: vimspector_session_windows is totally buseted with multiple
|
||||
# sessions
|
||||
del vim.vars[ 'vimspector_session_windows' ]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
vim.current.tabpage = self._uiTab
|
||||
|
||||
self._splash_screen = utils.HideSplash( self._api_prefix,
|
||||
|
|
@ -667,6 +680,15 @@ class DebugSession( object ):
|
|||
|
||||
def _SetUpUI( self ):
|
||||
vim.command( 'tab split' )
|
||||
|
||||
# Switch to this session now that we've made it visible. Note that the
|
||||
# TabEnter autocmd does trigger when the above is run, but that's before the
|
||||
# following line assigns the tab to this session, so when we try to find
|
||||
# this session by tab number, it's not found. So we have to manually switch
|
||||
# to it when creating a new tab.
|
||||
utils.Call( 'vimspector#internal#state#SwitchToSession',
|
||||
self.session_id )
|
||||
|
||||
self._uiTab = vim.current.tabpage
|
||||
|
||||
mode = settings.Get( 'ui_mode' )
|
||||
|
|
@ -710,7 +732,9 @@ class DebugSession( object ):
|
|||
def _SetUpUIHorizontal( self ):
|
||||
# Code window
|
||||
code_window = vim.current.window
|
||||
self._codeView = code.CodeView( code_window, self._api_prefix )
|
||||
self._codeView = code.CodeView( self.session_id,
|
||||
code_window,
|
||||
self._api_prefix )
|
||||
|
||||
# Call stack
|
||||
vim.command(
|
||||
|
|
@ -735,7 +759,8 @@ class DebugSession( object ):
|
|||
with utils.LetCurrentWindow( stack_trace_window ):
|
||||
vim.command( f'{ one_third }wincmd _' )
|
||||
|
||||
self._variablesView = variables.VariablesView( vars_window,
|
||||
self._variablesView = variables.VariablesView( self,
|
||||
vars_window,
|
||||
watch_window )
|
||||
|
||||
# Output/logging
|
||||
|
|
@ -743,7 +768,8 @@ class DebugSession( object ):
|
|||
vim.command( f'rightbelow { settings.Int( "bottombar_height" ) }new' )
|
||||
output_window = vim.current.window
|
||||
self._outputView = output.DAPOutputView( output_window,
|
||||
self._api_prefix )
|
||||
self._api_prefix,
|
||||
session_id = self.session_id )
|
||||
|
||||
# TODO: If/when we support multiple sessions, we'll need some way to
|
||||
# indicate which tab was created and store all the tabs
|
||||
|
|
@ -766,7 +792,9 @@ class DebugSession( object ):
|
|||
def _SetUpUIVertical( self ):
|
||||
# Code window
|
||||
code_window = vim.current.window
|
||||
self._codeView = code.CodeView( code_window, self._api_prefix )
|
||||
self._codeView = code.CodeView( self.session_id,
|
||||
code_window,
|
||||
self._api_prefix )
|
||||
|
||||
# Call stack
|
||||
vim.command(
|
||||
|
|
@ -793,7 +821,8 @@ class DebugSession( object ):
|
|||
with utils.LetCurrentWindow( stack_trace_window ):
|
||||
vim.command( f'{ one_third }wincmd |' )
|
||||
|
||||
self._variablesView = variables.VariablesView( vars_window,
|
||||
self._variablesView = variables.VariablesView( self,
|
||||
vars_window,
|
||||
watch_window )
|
||||
|
||||
|
||||
|
|
@ -802,7 +831,8 @@ class DebugSession( object ):
|
|||
vim.command( f'rightbelow { settings.Int( "bottombar_height" ) }new' )
|
||||
output_window = vim.current.window
|
||||
self._outputView = output.DAPOutputView( output_window,
|
||||
self._api_prefix )
|
||||
self._api_prefix,
|
||||
session_id = self.session_id )
|
||||
|
||||
# TODO: If/when we support multiple sessions, we'll need some way to
|
||||
# indicate which tab was created and store all the tabs
|
||||
|
|
@ -887,8 +917,10 @@ class DebugSession( object ):
|
|||
|
||||
vim.vars[ '_vimspector_adapter_spec' ] = self._adapter
|
||||
if not vim.eval( "vimspector#internal#{}#StartDebugSession( "
|
||||
" {},"
|
||||
" g:_vimspector_adapter_spec "
|
||||
")".format( self._connection_type ) ):
|
||||
")".format( self._connection_type,
|
||||
self.session_id ) ):
|
||||
self._logger.error( "Unable to start debug server" )
|
||||
self._splash_screen = utils.DisplaySplash( self._api_prefix,
|
||||
self._splash_screen,
|
||||
|
|
@ -908,9 +940,11 @@ class DebugSession( object ):
|
|||
handlers = [ self ]
|
||||
|
||||
self._connection = debug_adapter_connection.DebugAdapterConnection(
|
||||
handlers,
|
||||
lambda msg: utils.Call(
|
||||
handlers = handlers,
|
||||
session_id = self.session_id,
|
||||
send_func = lambda msg: utils.Call(
|
||||
"vimspector#internal#{}#Send".format( self._connection_type ),
|
||||
self.session_id,
|
||||
msg ) )
|
||||
|
||||
self._logger.info( 'Debug Adapter Started' )
|
||||
|
|
@ -933,8 +967,9 @@ class DebugSession( object ):
|
|||
assert not self._run_on_server_exit
|
||||
self._run_on_server_exit = callback
|
||||
|
||||
vim.eval( 'vimspector#internal#{}#StopDebugSession()'.format(
|
||||
self._connection_type ) )
|
||||
vim.eval( 'vimspector#internal#{}#StopDebugSession( {} )'.format(
|
||||
self._connection_type,
|
||||
self.session_id ) )
|
||||
|
||||
self._connection.DoRequest( handler, {
|
||||
'command': 'disconnect',
|
||||
|
|
@ -1179,9 +1214,10 @@ class DebugSession( object ):
|
|||
self._on_init_complete_handlers = []
|
||||
|
||||
self._logger.debug( "LAUNCH!" )
|
||||
self._launch_config = {}
|
||||
self._launch_config.update( self._adapter.get( 'configuration', {} ) )
|
||||
self._launch_config.update( self._configuration[ 'configuration' ] )
|
||||
if self._launch_config is None:
|
||||
self._launch_config = {}
|
||||
self._launch_config.update( self._adapter.get( 'configuration', {} ) )
|
||||
self._launch_config.update( self._configuration[ 'configuration' ] )
|
||||
|
||||
request = self._configuration.get(
|
||||
'remote-request',
|
||||
|
|
|
|||
|
|
@ -136,7 +136,8 @@ GADGETS = {
|
|||
# doesn't support the custom messages)
|
||||
# https://github.com/puremourning/vimspector/issues/141
|
||||
"subProcess": False,
|
||||
}
|
||||
},
|
||||
'custom_handler': 'vimspector.custom.python.Debugpy'
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -58,12 +58,18 @@ class OutputView( object ):
|
|||
files or the output of commands."""
|
||||
_buffers: typing.Dict[ str, TabBuffer ]
|
||||
|
||||
def __init__( self, window, api_prefix ):
|
||||
def __init__( self, window, api_prefix, session_id = None ):
|
||||
self._window = window
|
||||
self._buffers = {}
|
||||
self._api_prefix = api_prefix
|
||||
VIEWS.add( self )
|
||||
|
||||
if session_id is None:
|
||||
# FIXME: hack?
|
||||
self._session_id = hash( self )
|
||||
else:
|
||||
self._session_id = session_id
|
||||
|
||||
def Print( self, categroy, text ):
|
||||
self._Print( 'server', text.splitlines() )
|
||||
|
||||
|
|
@ -105,7 +111,7 @@ class OutputView( object ):
|
|||
def Clear( self ):
|
||||
for category, tab_buffer in self._buffers.items():
|
||||
if tab_buffer.is_job:
|
||||
utils.CleanUpCommand( category, self._api_prefix )
|
||||
utils.CleanUpCommand( self._session_id, category, self._api_prefix )
|
||||
utils.CleanUpHiddenBuffer( tab_buffer.buf )
|
||||
|
||||
# FIXME: nunmenu the WinBar ?
|
||||
|
|
@ -174,8 +180,9 @@ class OutputView( object ):
|
|||
|
||||
if cmd is not None:
|
||||
out = utils.SetUpCommandBuffer(
|
||||
self._session_id,
|
||||
cmd,
|
||||
category,
|
||||
utils.BufferNameForSession( category, self._session_id ),
|
||||
self._api_prefix,
|
||||
completion_handler = completion_handler )
|
||||
|
||||
|
|
@ -188,6 +195,8 @@ class OutputView( object ):
|
|||
else:
|
||||
name = 'vimspector.Output:{0}'.format( category )
|
||||
|
||||
name = utils.BufferNameForSession( name, self._session_id )
|
||||
|
||||
tab_buffer = TabBuffer( utils.NewEmptyBuffer(), len( self._buffers ) )
|
||||
|
||||
self._buffers[ category ] = tab_buffer
|
||||
|
|
@ -250,8 +259,8 @@ class OutputView( object ):
|
|||
|
||||
class DAPOutputView( OutputView ):
|
||||
"""Specialised OutputView which adds the DAP Console (REPL)"""
|
||||
def __init__( self, *args ):
|
||||
super().__init__( *args )
|
||||
def __init__( self, *args, **kwargs ):
|
||||
super().__init__( *args, **kwargs )
|
||||
|
||||
self._connection = None
|
||||
for b in set( BUFFER_MAP.values() ):
|
||||
|
|
|
|||
58
python3/vimspector/session_manager.py
Normal file
58
python3/vimspector/session_manager.py
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
# vimspector - A multi-language debugging system for Vim
|
||||
# Copyright 2020 Ben Jackson
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from vimspector.debug_session import DebugSession
|
||||
|
||||
# Singleton
|
||||
_session_manager = None
|
||||
|
||||
|
||||
class SessionManager:
|
||||
next_session_id = 0
|
||||
sessions = {}
|
||||
|
||||
|
||||
def NewSession( self, *args, **kwargs ):
|
||||
session_id = self.next_session_id
|
||||
self.next_session_id += 1
|
||||
session = DebugSession( session_id, self, *args, **kwargs )
|
||||
self.sessions[ session_id ] = session
|
||||
|
||||
return session
|
||||
|
||||
|
||||
def DestroySession( self, session: DebugSession ):
|
||||
# TODO: Call this!
|
||||
del self.sessions[ session.session_id ]
|
||||
|
||||
|
||||
def GetSession( self, session_id ):
|
||||
return self.sessions.get( session_id )
|
||||
|
||||
|
||||
def SessionForTab( self, tabnr ):
|
||||
for _, session in self.sessions.items():
|
||||
if session._HasUI() and session._uiTab.number == int( tabnr ):
|
||||
return session
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def Get():
|
||||
global _session_manager
|
||||
if _session_manager is None:
|
||||
_session_manager = SessionManager()
|
||||
|
||||
return _session_manager
|
||||
|
|
@ -86,8 +86,8 @@ class StackTraceView( object ):
|
|||
_line_to_thread = typing.Dict[ int, Thread ]
|
||||
|
||||
def __init__( self, session, win ):
|
||||
self._logger = logging.getLogger( __name__ )
|
||||
utils.SetUpLogging( self._logger )
|
||||
self._logger = logging.getLogger( __name__ + '.' + str( session.session_id ) )
|
||||
utils.SetUpLogging( self._logger, session.session_id )
|
||||
|
||||
self._buf = win.buffer
|
||||
self._session = session
|
||||
|
|
@ -104,7 +104,10 @@ class StackTraceView( object ):
|
|||
# FIXME: This ID is by group, so should be module scope
|
||||
self._next_sign_id = 1
|
||||
|
||||
utils.SetUpHiddenBuffer( self._buf, 'vimspector.StackTrace' )
|
||||
utils.SetUpHiddenBuffer(
|
||||
self._buf,
|
||||
utils.BufferNameForSession( 'vimspector.StackTrace',
|
||||
self._session.session_id ) )
|
||||
utils.SetUpUIWindow( win )
|
||||
|
||||
mappings = settings.Dict( 'mappings' )[ 'stack_trace' ]
|
||||
|
|
@ -562,7 +565,10 @@ class StackTraceView( object ):
|
|||
|
||||
buf = utils.BufferForFile( buf_name )
|
||||
self._scratch_buffers.append( buf )
|
||||
utils.SetUpHiddenBuffer( buf, buf_name )
|
||||
utils.SetUpHiddenBuffer( buf,
|
||||
utils.BufferNameForSession(
|
||||
buf_name,
|
||||
self._session.session_id ) )
|
||||
source[ 'path' ] = buf_name
|
||||
with utils.ModifiableScratchBuffer( buf ):
|
||||
utils.SetBufferContents( buf, msg[ 'body' ][ 'content' ] )
|
||||
|
|
|
|||
|
|
@ -32,13 +32,30 @@ LOG_FILE = os.path.expanduser( os.path.join( '~', '.vimspector.log' ) )
|
|||
_log_handler = logging.FileHandler( LOG_FILE, mode = 'w' )
|
||||
|
||||
_log_handler.setFormatter(
|
||||
logging.Formatter( '%(asctime)s - %(levelname)s - %(message)s' ) )
|
||||
logging.Formatter( '%(asctime)s - %(levelname)s - %(filename)s:%(lineno)s - '
|
||||
'%(context)s - %(message)s' ) )
|
||||
|
||||
|
||||
def SetUpLogging( logger ):
|
||||
class ContextLogFilter( logging.Filter ):
|
||||
context: str
|
||||
|
||||
def __init__( self, context ):
|
||||
self.context = str( context )
|
||||
|
||||
def filter( self, record: logging.LogRecord ):
|
||||
if self.context is None:
|
||||
record.context = 'UNKNOWN'
|
||||
else:
|
||||
record.context = self.context
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def SetUpLogging( logger, context = None ):
|
||||
logger.setLevel( logging.DEBUG )
|
||||
if _log_handler not in logger.handlers:
|
||||
logger.addHandler( _log_handler )
|
||||
logger.addFilter( ContextLogFilter( context ) )
|
||||
|
||||
|
||||
_logger = logging.getLogger( __name__ )
|
||||
|
|
@ -87,16 +104,21 @@ def OpenFileInCurrentWindow( file_name ):
|
|||
COMMAND_HANDLERS = {}
|
||||
|
||||
|
||||
def OnCommandWithLogComplete( name, exit_code ):
|
||||
cb = COMMAND_HANDLERS.get( name )
|
||||
def OnCommandWithLogComplete( session_id, name, exit_code ):
|
||||
cb = COMMAND_HANDLERS.get( str( session_id ) + '.' + name )
|
||||
if cb:
|
||||
cb( exit_code )
|
||||
|
||||
|
||||
def SetUpCommandBuffer( cmd, name, api_prefix, completion_handler = None ):
|
||||
COMMAND_HANDLERS[ name ] = completion_handler
|
||||
def SetUpCommandBuffer( session_id,
|
||||
cmd,
|
||||
name,
|
||||
api_prefix,
|
||||
completion_handler = None ):
|
||||
COMMAND_HANDLERS[ str( session_id ) + '.' + name ] = completion_handler
|
||||
|
||||
buf = Call( f'vimspector#internal#{api_prefix}job#StartCommandWithLog',
|
||||
session_id,
|
||||
cmd,
|
||||
name )
|
||||
|
||||
|
|
@ -110,10 +132,12 @@ def SetUpCommandBuffer( cmd, name, api_prefix, completion_handler = None ):
|
|||
return vim.buffers[ int( buf ) ]
|
||||
|
||||
|
||||
def CleanUpCommand( name, api_prefix ):
|
||||
return vim.eval( 'vimspector#internal#{}job#CleanUpCommand( "{}" )'.format(
|
||||
api_prefix,
|
||||
name ) )
|
||||
def CleanUpCommand( session_id, name, api_prefix ):
|
||||
return vim.eval(
|
||||
'vimspector#internal#{}job#CleanUpCommand( {}, "{}" )'.format(
|
||||
api_prefix,
|
||||
session_id,
|
||||
name ) )
|
||||
|
||||
|
||||
def CleanUpHiddenBuffer( buf ):
|
||||
|
|
@ -397,6 +421,8 @@ def Confirm( api_prefix,
|
|||
default_value = 2,
|
||||
options: list = None,
|
||||
keys: list = None ):
|
||||
# TODO: Implement a queue here? If calling code calls Confirm (async) multiple
|
||||
# times, we... well what happens?!
|
||||
if not options:
|
||||
options = [ '(Y)es', '(N)o' ]
|
||||
if not keys:
|
||||
|
|
@ -864,3 +890,12 @@ def UseWinBar():
|
|||
# Buggy neovim doesn't render correctly when the WinBar is defined:
|
||||
# https://github.com/neovim/neovim/issues/12689
|
||||
return not int( Call( 'has', 'nvim' ) )
|
||||
|
||||
|
||||
def BufferNameForSession( name, session_id ):
|
||||
if session_id == 0:
|
||||
# Hack for backward compat - don't suffix with the ID for the "first"
|
||||
# session
|
||||
return name
|
||||
|
||||
return f'{name}[{session_id}]'
|
||||
|
|
|
|||
|
|
@ -166,10 +166,11 @@ def AddExpandMappings( mappings = None ):
|
|||
|
||||
|
||||
class VariablesView( object ):
|
||||
def __init__( self, variables_win, watches_win ):
|
||||
self._logger = logging.getLogger( __name__ )
|
||||
utils.SetUpLogging( self._logger )
|
||||
def __init__( self, session, variables_win, watches_win ):
|
||||
self._logger = logging.getLogger( __name__ + '.' + str( session.session_id ) )
|
||||
utils.SetUpLogging( self._logger, session.session_id )
|
||||
|
||||
self._session = session
|
||||
self._connection = None
|
||||
self._current_syntax = ''
|
||||
self._server_capabilities = None
|
||||
|
|
@ -182,7 +183,10 @@ class VariablesView( object ):
|
|||
# Set up the "Variables" buffer in the variables_win
|
||||
self._scopes: typing.List[ Scope ] = []
|
||||
self._vars = View( variables_win, {}, self._DrawScopes )
|
||||
utils.SetUpHiddenBuffer( self._vars.buf, 'vimspector.Variables' )
|
||||
utils.SetUpHiddenBuffer(
|
||||
self._vars.buf,
|
||||
utils.BufferNameForSession( 'vimspector.Variables',
|
||||
self._session.session_id ) )
|
||||
with utils.LetCurrentWindow( variables_win ):
|
||||
if utils.UseWinBar():
|
||||
vim.command( 'nnoremenu <silent> 1.1 WinBar.Set '
|
||||
|
|
@ -193,11 +197,14 @@ class VariablesView( object ):
|
|||
# there)
|
||||
self._watches: typing.List[ Watch ] = []
|
||||
self._watch = View( watches_win, {}, self._DrawWatches )
|
||||
utils.SetUpPromptBuffer( self._watch.buf,
|
||||
'vimspector.Watches',
|
||||
'Expression: ',
|
||||
'vimspector#AddWatchPrompt',
|
||||
'vimspector#OmniFuncWatch' )
|
||||
utils.SetUpPromptBuffer(
|
||||
self._watch.buf,
|
||||
utils.BufferNameForSession( 'vimspector.Watches',
|
||||
self._session.session_id ),
|
||||
'Expression: ',
|
||||
'vimspector#AddWatchPrompt',
|
||||
'vimspector#OmniFuncWatch' )
|
||||
|
||||
with utils.LetCurrentWindow( watches_win ):
|
||||
AddExpandMappings( mappings )
|
||||
for mapping in utils.GetVimList( mappings, 'delete' ):
|
||||
|
|
@ -224,9 +231,12 @@ class VariablesView( object ):
|
|||
'balloonexpr': vim.options[ 'balloonexpr' ],
|
||||
'balloondelay': vim.options[ 'balloondelay' ],
|
||||
}
|
||||
# TODO: How can we make this work. I think we can set ballooneval as a
|
||||
# buffer-local or maybe window-local variable ? We could pass session_id
|
||||
# to the expression here, but still how would it work with 2 concurrent
|
||||
# sessions?
|
||||
vim.options[ 'balloonexpr' ] = ( "vimspector#internal#"
|
||||
"balloon#HoverTooltip()" )
|
||||
|
||||
vim.options[ 'balloondelay' ] = 250
|
||||
|
||||
if has_balloon:
|
||||
|
|
|
|||
43
support/test/python/multiprocessing/.vimspector.json
Normal file
43
support/test/python/multiprocessing/.vimspector.json
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
{
|
||||
"$schema": "https://puremourning.github.io/vimspector/schema/vimspector.schema.json",
|
||||
"configurations": {
|
||||
"run": {
|
||||
"adapter": "debugpy",
|
||||
"configuration": {
|
||||
"request": "launch",
|
||||
"type": "python",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"program": "${file}",
|
||||
"stopOnEntry": true,
|
||||
"console": "integratedTerminal",
|
||||
"subProcess": true
|
||||
},
|
||||
"breakpoints": {
|
||||
"exception": {
|
||||
"raised": "N",
|
||||
"uncaught": "Y",
|
||||
"userUnhandled": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"attach": {
|
||||
"adapter": "multi-session",
|
||||
"configuration": {
|
||||
"request": "attach",
|
||||
"type": "python",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"program": "${file}",
|
||||
"stopOnEntry": true,
|
||||
"console": "integratedTerminal",
|
||||
"subProcess": true
|
||||
},
|
||||
"breakpoints": {
|
||||
"exception": {
|
||||
"raised": "N",
|
||||
"uncaught": "Y",
|
||||
"userUnhandled": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
18
support/test/python/multiprocessing/multiprocessing_test.py
Normal file
18
support/test/python/multiprocessing/multiprocessing_test.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import time
|
||||
import multiprocessing as mp
|
||||
|
||||
|
||||
def First():
|
||||
for i in range( 10 ):
|
||||
print( f"in first x {i}" )
|
||||
time.sleep( 0.1 )
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print( "main" )
|
||||
p1 = mp.Process( target=First )
|
||||
|
||||
p1.start()
|
||||
p1.join()
|
||||
|
||||
print( "Done" )
|
||||
|
|
@ -6,8 +6,9 @@ import os
|
|||
def Main():
|
||||
print( os.environ.get( 'Something', 'ERROR' ) )
|
||||
print( os.environ.get( 'SomethingElse', 'ERROR' ) )
|
||||
print( os.environ.get( 'PATH', 'ERROR' ) )
|
||||
|
||||
for k, v in os.environ:
|
||||
for k, v in os.environ.items():
|
||||
print( f'{ k } = "{ v }"' )
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -107,3 +107,46 @@ function! Test_Python_Remote_Attach()
|
|||
lcd -
|
||||
%bwipeout!
|
||||
endfunction
|
||||
|
||||
function! SetUp_Test_Python_Remote_Attach_With_Run()
|
||||
let g:vimspector_enable_mappings = 'HUMAN'
|
||||
endfunction
|
||||
|
||||
function! Test_Python_Remote_Attach_With_Run()
|
||||
lcd ../support/test/python/simple_python
|
||||
let fn='main.py'
|
||||
exe 'edit ' . fn
|
||||
|
||||
call setpos( '.', [ 0, 6, 1 ] )
|
||||
|
||||
call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 6, 1 )
|
||||
call vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorBP', 6 )
|
||||
|
||||
" Add the breakpoint
|
||||
call feedkeys( "\<F9>", 'xt' )
|
||||
call vimspector#test#signs#AssertSignGroupSingletonAtLine( 'VimspectorBP',
|
||||
\ 6,
|
||||
\ 'vimspectorBP',
|
||||
\ 9 )
|
||||
|
||||
call setpos( '.', [ 0, 1, 1 ] )
|
||||
|
||||
" Here we go. Start Debugging (note will wait up to 10s for the script to do
|
||||
" its virtualenv thing)
|
||||
call vimspector#LaunchWithSettings( {
|
||||
\ 'configuration': 'attach-run',
|
||||
\ } )
|
||||
call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 6, 1 )
|
||||
|
||||
" Step
|
||||
call feedkeys( "\<F10>", 'xt' )
|
||||
|
||||
call vimspector#test#signs#AssertCursorIsAtLineInBuffer( fn, 7, 1 )
|
||||
call WaitForAssert( {->
|
||||
\ vimspector#test#signs#AssertPCIsAtLineInBuffer( fn, 7 )
|
||||
\ } )
|
||||
|
||||
call vimspector#test#setup#Reset()
|
||||
lcd -
|
||||
%bwipeout!
|
||||
endfunction
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue