Merge pull request #355 from puremourning/terminate-debugee

Ask the user about terminating the debuggee
This commit is contained in:
mergify[bot] 2021-02-24 18:27:16 +00:00 • committed by GitHub
commit f9c5a33301
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 87 additions and 33 deletions

View file

@ -128,7 +128,7 @@ And a couple of brief demos:
- call stack display and navigation - call stack display and navigation
- hierarchical variable value display popup (see `<Plug>VimspectorBalloonEval`) - hierarchical variable value display popup (see `<Plug>VimspectorBalloonEval`)
- interactive debug console with autocompletion - interactive debug console with autocompletion
- launch debugee within Vim's embedded terminal - launch debuggee within Vim's embedded terminal
- logging/stdout display - logging/stdout display
- simple stable API for custom tooling (e.g. integrate with language server) - simple stable API for custom tooling (e.g. integrate with language server)
@ -698,7 +698,7 @@ let g:vimspector_enable_mappings = 'VISUAL_STUDIO'
| `F5` | When debugging, continue. Otherwise start debugging. | `vimspector#Continue()` | | `F5` | When debugging, continue. Otherwise start debugging. | `vimspector#Continue()` |
| `Shift F5` | Stop debugging. | `vimspector#Stop()` | | `Shift F5` | Stop debugging. | `vimspector#Stop()` |
| `Ctrl Shift F5` | Restart debugging with the same configuration. | `vimspector#Restart()` | | `Ctrl Shift F5` | Restart debugging with the same configuration. | `vimspector#Restart()` |
| `F6` | Pause debugee. | `vimspector#Pause()` | | `F6` | Pause debuggee. | `vimspector#Pause()` |
| `F9` | Toggle line breakpoint on the current line. | `vimspector#ToggleBreakpoint()` | | `F9` | Toggle line breakpoint on the current line. | `vimspector#ToggleBreakpoint()` |
| `Shift F9` | Add a function breakpoint for the expression under cursor | `vimspector#AddFunctionBreakpoint( '<cexpr>' )` | | `Shift F9` | Add a function breakpoint for the expression under cursor | `vimspector#AddFunctionBreakpoint( '<cexpr>' )` |
| `F10` | Step Over | `vimspector#StepOver()` | | `F10` | Step Over | `vimspector#StepOver()` |
@ -723,7 +723,7 @@ let g:vimspector_enable_mappings = 'HUMAN'
| `F5` | When debugging, continue. Otherwise start debugging. | `vimspector#Continue()` | | `F5` | When debugging, continue. Otherwise start debugging. | `vimspector#Continue()` |
| `F3` | Stop debugging. | `vimspector#Stop()` | | `F3` | Stop debugging. | `vimspector#Stop()` |
| `F4` | Restart debugging with the same configuration. | `vimspector#Restart()` | | `F4` | Restart debugging with the same configuration. | `vimspector#Restart()` |
| `F6` | Pause debugee. | `vimspector#Pause()` | | `F6` | Pause debuggee. | `vimspector#Pause()` |
| `F9` | Toggle line breakpoint on the current line. | `vimspector#ToggleBreakpoint()` | | `F9` | Toggle line breakpoint on the current line. | `vimspector#ToggleBreakpoint()` |
| `<leader>F9` | Toggle conditional line breakpoint on the current line. | `vimspector#ToggleBreakpoint( { trigger expr, hit count expr } )` | | `<leader>F9` | Toggle conditional line breakpoint on the current line. | `vimspector#ToggleBreakpoint( { trigger expr, hit count expr } )` |
| `F8` | Add a function breakpoint for the expression under cursor | `vimspector#AddFunctionBreakpoint( '<cexpr>' )` | | `F8` | Add a function breakpoint for the expression under cursor | `vimspector#AddFunctionBreakpoint( '<cexpr>' )` |
@ -1002,7 +1002,7 @@ The stack trace is represented by the buffer `vimspector.StackTrace`.
* In the outputs window, use the WinBar to select the output channel. * In the outputs window, use the WinBar to select the output channel.
* Alternatively, use `:VimspectorShowOutput <category>`. Use command-line * Alternatively, use `:VimspectorShowOutput <category>`. Use command-line
completion to see the categories. completion to see the categories.
* The debugee prints to the stdout channel. * The debuggee prints to the stdout channel.
* Other channels may be useful for debugging. * Other channels may be useful for debugging.
![output window](https://puremourning.github.io/vimspector-web/img/vimspector-output-window.png) ![output window](https://puremourning.github.io/vimspector-web/img/vimspector-output-window.png)
@ -1057,10 +1057,29 @@ which will tail it in a little window (doesn't work on Windows).
To close the debugger, use: To close the debugger, use:
* `Reset` WinBar button (`set mouse=a`) * `Reset` WinBar button
* `:VimspectorReset` when the WinBar is not available. * `:VimspectorReset` when the WinBar is not available.
* `call vimspector#Reset()` * `call vimspector#Reset()`
## Terminate debuggee
If the debuggee is still running when stopping or resetting, then some debug
adapters allow you to specify what should happen to it when finishing debugging.
Typically, the default behaviour is sensible, and this is what happens most of
the time. These are the defaults according to DAP:
* If the request was 'launch': terminate the debuggee
* If the request was 'attach': don't terminate the debuggee
Some debug adapters allow you to choose what to do when disconnecting. If you
wish to control this behaviour, use `:VimspectorReset` or call
`vimspector#Reset( { 'interactive': v:true } )`. If the debug adapter offers a
choice as to whether or not to terminate the debuggee, you will be prompted to
choose. The same applies for `vimspector#Stop()` which can take an argument:
`vimspector#Stop( { 'interactive': v:true } )`.
# Debug profile configuration # Debug profile configuration
For an introduction to the configuration of `.vimspector.json`, take a look at For an introduction to the configuration of `.vimspector.json`, take a look at
@ -1903,14 +1922,14 @@ You can even customise the WinBar buttons by simply running the usual `menu`
By default, Vimspector uses something a bit like this: By default, Vimspector uses something a bit like this:
```viml ```viml
nnoremenu WinBar.■\ Stop :call vimspector#Stop()<CR> nnoremenu WinBar.■\ Stop :call vimspector#Stop( { 'interactive': v:false } )<CR>
nnoremenu WinBar.▶\ Cont :call vimspector#Continue()<CR> nnoremenu WinBar.▶\ Cont :call vimspector#Continue()<CR>
nnoremenu WinBar.▷\ Pause :call vimspector#Pause()<CR> nnoremenu WinBar.▷\ Pause :call vimspector#Pause()<CR>
nnoremenu WinBar.↷\ Next :call vimspector#StepOver()<CR> nnoremenu WinBar.↷\ Next :call vimspector#StepOver()<CR>
nnoremenu WinBar.→\ Step :call vimspector#StepInto()<CR> nnoremenu WinBar.→\ Step :call vimspector#StepInto()<CR>
nnoremenu WinBar.←\ Out :call vimspector#StepOut()<CR> nnoremenu WinBar.←\ Out :call vimspector#StepOut()<CR>
nnoremenu WinBar.⟲: :call vimspector#Restart()<CR> nnoremenu WinBar.⟲: :call vimspector#Restart()<CR>
nnoremenu WinBar.✕ :call vimspector#Reset()<CR> nnoremenu WinBar.✕ :call vimspector#Reset( { 'interactive': v:false } )<CR>
``` ```
If you prefer a different layout or if the unicode symbols don't render If you prefer a different layout or if the unicode symbols don't render
@ -1923,7 +1942,7 @@ func! CustomiseUI()
" Clear the existing WinBar created by Vimspector " Clear the existing WinBar created by Vimspector
nunmenu WinBar nunmenu WinBar
" Cretae our own WinBar " Cretae our own WinBar
nnoremenu WinBar.Kill :call vimspector#Stop()<CR> nnoremenu WinBar.Kill :call vimspector#Stop( { 'interactive': v:true } )<CR>
nnoremenu WinBar.Continue :call vimspector#Continue()<CR> nnoremenu WinBar.Continue :call vimspector#Continue()<CR>
nnoremenu WinBar.Pause :call vimspector#Pause()<CR> nnoremenu WinBar.Pause :call vimspector#Pause()<CR>
nnoremenu WinBar.Step\ Over :call vimspector#StepOver()<CR> nnoremenu WinBar.Step\ Over :call vimspector#StepOver()<CR>

View file

@ -55,11 +55,16 @@ function! vimspector#LaunchWithSettings( settings ) abort
py3 _vimspector_session.Start( launch_variables = vim.eval( 'a:settings' ) ) py3 _vimspector_session.Start( launch_variables = vim.eval( 'a:settings' ) )
endfunction endfunction
function! vimspector#Reset() abort function! vimspector#Reset( ... ) abort
if !s:Enabled() if !s:Enabled()
return return
endif endif
py3 _vimspector_session.Reset() if a:0 == 0
let options = {}
else
let options = a:1
endif
py3 _vimspector_session.Reset( **vim.eval( 'options' ) )
endfunction endfunction
function! vimspector#Restart() abort function! vimspector#Restart() abort
@ -185,11 +190,16 @@ function! vimspector#SetCurrentThread() abort
py3 _vimspector_session.SetCurrentThread() py3 _vimspector_session.SetCurrentThread()
endfunction endfunction
function! vimspector#Stop() abort function! vimspector#Stop( ... ) abort
if !s:Enabled() if !s:Enabled()
return return
endif endif
py3 _vimspector_session.Stop() if a:0 == 0
options = {}
else
options = a:1
endif
py3 _vimspector_session.Stop( **vim.eval( 'options' ) )
endfunction endfunction
function! vimspector#ExpandVariable() abort function! vimspector#ExpandVariable() abort

View file

@ -105,7 +105,7 @@ command! -bar -nargs=1 -complete=custom,vimspector#CompleteExpr
\ call vimspector#Evaluate( <f-args> ) \ call vimspector#Evaluate( <f-args> )
command! -bar command! -bar
\ VimspectorReset \ VimspectorReset
\ call vimspector#Reset() \ call vimspector#Reset( { 'interactive': v:true } )
" Installer commands " Installer commands
command! -bar -bang -nargs=* -complete=custom,vimspector#CompleteInstall command! -bar -bang -nargs=* -complete=custom,vimspector#CompleteInstall

View file

@ -320,7 +320,7 @@ class DebugSession( object ):
if self._connection: if self._connection:
self._logger.debug( "_StopDebugAdapter with callback: start" ) self._logger.debug( "_StopDebugAdapter with callback: start" )
self._StopDebugAdapter( start ) self._StopDebugAdapter( interactive = False, callback = start )
return return
start() start()
@ -385,14 +385,15 @@ class DebugSession( object ):
self._connection = None self._connection = None
@IfConnected() @IfConnected()
def Stop( self ): def Stop( self, interactive = False ):
self._logger.debug( "Stop debug adapter with no callback" ) self._logger.debug( "Stop debug adapter with no callback" )
self._StopDebugAdapter() self._StopDebugAdapter( interactive = interactive )
def Reset( self ): def Reset( self, interactive = False ):
if self._connection: if self._connection:
self._logger.debug( "Stop debug adapter with callback : self._Reset()" ) self._logger.debug( "Stop debug adapter with callback : self._Reset()" )
self._StopDebugAdapter( lambda: self._Reset() ) self._StopDebugAdapter( interactive = interactive,
callback = lambda: self._Reset() )
else: else:
self._Reset() self._Reset()
@ -772,7 +773,7 @@ class DebugSession( object ):
self._logger.info( 'Debug Adapter Started' ) self._logger.info( 'Debug Adapter Started' )
def _StopDebugAdapter( self, callback = None ): def _StopDebugAdapter( self, interactive = False, callback = None ):
self._splash_screen = utils.DisplaySplash( self._splash_screen = utils.DisplaySplash(
self._api_prefix, self._api_prefix,
self._splash_screen, self._splash_screen,
@ -791,9 +792,14 @@ class DebugSession( object ):
self._connection_type ) ) self._connection_type ) )
arguments = {} arguments = {}
if self._server_capabilities.get( 'supportTerminateDebuggee' ): if ( interactive and
# If we attached, we should _not_ terminate the debuggee self._server_capabilities.get( 'supportTerminateDebuggee' ) ):
arguments[ 'terminateDebuggee' ] = False if self._stackTraceView.AnyThreadsRunning():
choice = utils.AskForInput( "Terminate debuggee [Y/N/default]? ", "" )
if choice == "Y" or choice == "y":
arguments[ 'terminateDebuggee' ] = True
elif choice == "N" or choice == 'n':
arguments[ 'terminateDebuggee' ] = False
self._connection.DoRequest( handler, { self._connection.DoRequest( handler, {
'command': 'disconnect', 'command': 'disconnect',
@ -1023,14 +1029,14 @@ class DebugSession( object ):
self._splash_screen = utils.DisplaySplash( self._splash_screen = utils.DisplaySplash(
self._api_prefix, self._api_prefix,
self._splash_screen, self._splash_screen,
"Attaching to debugee..." ) "Attaching to debuggee..." )
self._PrepareAttach( self._adapter, self._launch_config ) self._PrepareAttach( self._adapter, self._launch_config )
elif request == "launch": elif request == "launch":
self._splash_screen = utils.DisplaySplash( self._splash_screen = utils.DisplaySplash(
self._api_prefix, self._api_prefix,
self._splash_screen, self._splash_screen,
"Launching debugee..." ) "Launching debuggee..." )
# FIXME: This cmdLine hack is not fun. # FIXME: This cmdLine hack is not fun.
self._PrepareLaunch( self._configuration.get( 'remote-cmdLine', [] ), self._PrepareLaunch( self._configuration.get( 'remote-cmdLine', [] ),
@ -1164,13 +1170,25 @@ class DebugSession( object ):
self._connection.DoResponse( message, None, response ) self._connection.DoResponse( message, None, response )
def OnEvent_exited( self, message ): def OnEvent_terminated( self, message ):
utils.UserMessage( 'The debugee exited with status code: {}'.format( # The debugging _session_ has terminated. This does not mean that the
message[ 'body' ][ 'exitCode' ] ) ) # debuggee has terminated (that's the exited event).
#
# We will handle this when the server actually exists.
#
# FIXME we should always wait for this event before disconnecting closing
# any socket connection
self.SetCurrentFrame( None ) self.SetCurrentFrame( None )
def OnEvent_exited( self, message ):
utils.UserMessage( 'The debuggee exited with status code: {}'.format(
message[ 'body' ][ 'exitCode' ] ) )
self._stackTraceView.OnExited( message )
self._codeView.SetCurrentFrame( None )
def OnEvent_process( self, message ): def OnEvent_process( self, message ):
utils.UserMessage( 'The debugee was started: {}'.format( utils.UserMessage( 'The debuggee was started: {}'.format(
message[ 'body' ][ 'name' ] ) ) message[ 'body' ][ 'name' ] ) )
def OnEvent_module( self, message ): def OnEvent_module( self, message ):
@ -1210,11 +1228,6 @@ class DebugSession( object ):
else: else:
self._logger.debug( "No server exit handler" ) self._logger.debug( "No server exit handler" )
def OnEvent_terminated( self, message ):
# We will handle this when the server actually exists
utils.UserMessage( "Debugging was terminated by the server." )
self.SetCurrentFrame( None )
def OnEvent_output( self, message ): def OnEvent_output( self, message ):
if self._outputView: if self._outputView:
self._outputView.OnOutput( message[ 'body' ] ) self._outputView.OnOutput( message[ 'body' ] )

View file

@ -364,6 +364,14 @@ class StackTraceView( object ):
self._JumpToFrame( frame ) self._JumpToFrame( frame )
def AnyThreadsRunning( self ):
for thread in self._threads:
if thread.state != Thread.TERMINATED:
return True
return False
def _JumpToFrame( self, frame, reason = '' ): def _JumpToFrame( self, frame, reason = '' ):
def do_jump(): def do_jump():
if 'line' in frame and frame[ 'line' ] > 0: if 'line' in frame and frame[ 'line' ] > 0:
@ -459,6 +467,10 @@ class StackTraceView( object ):
self.LoadThreads( infer_current_frame ) self.LoadThreads( infer_current_frame )
def OnExited( self, event ):
for thread in self._threads:
thread.Exited()
def _DrawStackTrace( self, thread: Thread ): def _DrawStackTrace( self, thread: Thread ):
if not thread.IsExpanded(): if not thread.IsExpanded():
return return